diff --git a/.gitlab/ci/review-apps/main.gitlab-ci.yml b/.gitlab/ci/review-apps/main.gitlab-ci.yml index d3f5d014464..d996a454e33 100644 --- a/.gitlab/ci/review-apps/main.gitlab-ci.yml +++ b/.gitlab/ci/review-apps/main.gitlab-ci.yml @@ -113,11 +113,11 @@ review-deploy: - echo "QA_GITLAB_URL=${CI_ENVIRONMENT_URL}" > environment.env - *base-before_script script: - - check_kube_domain - - download_chart - - deploy || (display_deployment_debug && exit 1) - - verify_deploy || exit 1 - - disable_sign_ups || (delete_release && exit 1) + - run_timed_command "check_kube_domain" + - run_timed_command "download_chart" + - run_timed_command "deploy" || (display_deployment_debug && exit 1) + - run_timed_command "verify_deploy" || exit 1 + - run_timed_command "disable_sign_ups" || (delete_release && exit 1) after_script: # Run seed-dast-test-data.sh only when DAST_RUN is set to true. This is to pupulate review app with data for DAST scan. # Set DAST_RUN to true when jobs are manually scheduled. diff --git a/.gitlab/issue_templates/Experiment Implementation.md b/.gitlab/issue_templates/Experiment Implementation.md index fc6cfbb27fa..56202240ef5 100644 --- a/.gitlab/issue_templates/Experiment Implementation.md +++ b/.gitlab/issue_templates/Experiment Implementation.md @@ -18,7 +18,7 @@ # Tracking Details - [json schema](https://gitlab.com/gitlab-org/iglu/-/blob/master/public/schemas/com.gitlab/gitlab_experiment/jsonschema/0-3-0) used in `gitlab-experiment` tracking. -- see [taxonomy](https://docs.gitlab.com/ee/development/snowplow/index.html#structured-event-taxonomy) for a guide. +- see [event schema](https://docs.gitlab.com/ee/development/snowplow/index.html#event-schema) for a guide. | sequence | activity | category | action | label | property | value | | -------- | -------- | ------ | ----- | ------- | -------- | ----- | diff --git a/app/assets/javascripts/gfm_auto_complete.js b/app/assets/javascripts/gfm_auto_complete.js index 01d218438cf..49c47e9d778 100644 --- a/app/assets/javascripts/gfm_auto_complete.js +++ b/app/assets/javascripts/gfm_auto_complete.js @@ -20,7 +20,12 @@ const MERGEREQUESTS_ALIAS = 'mergerequests'; const LABELS_ALIAS = 'labels'; const SNIPPETS_ALIAS = 'snippets'; const CONTACTS_ALIAS = 'contacts'; + export const AT_WHO_ACTIVE_CLASS = 'at-who-active'; +export const CONTACT_STATE_ACTIVE = 'active'; +export const CONTACTS_ADD_COMMAND = '/add_contacts'; +export const CONTACTS_REMOVE_COMMAND = '/remove_contacts'; + /** * Escapes user input before we pass it to at.js, which * renders it as HTML in the autocomplete dropdown. @@ -666,6 +671,9 @@ class GfmAutoComplete { } setupContacts($input) { + const fetchData = this.fetchData.bind(this); + let command = ''; + $input.atwho({ at: '[contact:', suffix: ']', @@ -694,9 +702,44 @@ class GfmAutoComplete { firstName: m.first_name, lastName: m.last_name, search: `${m.email}`, + state: m.state, + set: m.set, }; }); }, + matcher(flag, subtext) { + const subtextNodes = subtext.split(/\n+/g).pop().split(GfmAutoComplete.regexSubtext); + + command = subtextNodes.find((node) => { + if (node === CONTACTS_ADD_COMMAND || node === CONTACTS_REMOVE_COMMAND) { + return node; + } + return null; + }); + + const match = GfmAutoComplete.defaultMatcher(flag, subtext, this.app.controllers); + return match?.length ? match[1] : null; + }, + filter(query, data, searchKey) { + if (GfmAutoComplete.isLoading(data)) { + fetchData(this.$inputor, this.at); + return data; + } + + if (data === GfmAutoComplete.defaultLoadingData) { + return $.fn.atwho.default.callbacks.filter(query, data, searchKey); + } + + if (command === CONTACTS_ADD_COMMAND) { + // Return contacts that are active and not already on the issue + return data.filter((contact) => contact.state === CONTACT_STATE_ACTIVE && !contact.set); + } else if (command === CONTACTS_REMOVE_COMMAND) { + // Return contacts already on the issue + return data.filter((contact) => contact.set); + } + + return data; + }, }, }); showAndHideHelper($input, CONTACTS_ALIAS); diff --git a/app/assets/javascripts/tracking/tracker.js b/app/assets/javascripts/tracking/tracker.js index 9ad86e76b6e..85f4979e752 100644 --- a/app/assets/javascripts/tracking/tracker.js +++ b/app/assets/javascripts/tracking/tracker.js @@ -39,8 +39,8 @@ export const Tracker = { }, /** - * Dispatches a structured event per our taxonomy: - * https://docs.gitlab.com/ee/development/snowplow/index.html#structured-event-taxonomy. + * Dispatches a structured event: + * https://docs.gitlab.com/ee/development/snowplow/index.html#event-schema. * * If the library is not initialized and events are trying to be * dispatched (data-attributes, load-events), they will be added @@ -49,7 +49,7 @@ export const Tracker = { * If there is an error when using the library, it will return ´false´ * and ´true´ otherwise. * - * @param {...any} eventData defined event taxonomy + * @param {...any} eventData defined event schema * @returns {Boolean} */ event(...eventData) { diff --git a/config/metrics/settings/20221015152126_deactivate_dormant_users_enabled.yml b/config/metrics/settings/20221015152126_deactivate_dormant_users_enabled.yml new file mode 100644 index 00000000000..0f77cde4014 --- /dev/null +++ b/config/metrics/settings/20221015152126_deactivate_dormant_users_enabled.yml @@ -0,0 +1,23 @@ +--- +key_path: deactivate_dormant_users_enabled +description: Whether Dormant User Deactivation is enabled +product_section: fulfillment +product_stage: fulfillment +product_group: utilization +product_category: subscription_cost_management +value_type: boolean +status: active +milestone: "15.6" +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/101125 +time_frame: none +data_source: database +data_category: optional +instrumentation_class: DormantUserSettingEnabledMetric +performance_indicator_type: [] +distribution: +- ce +- ee +tier: +- free +- premium +- ultimate diff --git a/config/metrics/settings/20221015161233_deactivate_dormant_users_period.yml b/config/metrics/settings/20221015161233_deactivate_dormant_users_period.yml new file mode 100644 index 00000000000..6cd7e0f0da7 --- /dev/null +++ b/config/metrics/settings/20221015161233_deactivate_dormant_users_period.yml @@ -0,0 +1,23 @@ +--- +key_path: deactivate_dormant_users_period +description: The value of the dormant users period being used +product_section: fulfillment +product_stage: fulfillment +product_group: utilization +product_category: subscription_cost_management +value_type: boolean +status: active +milestone: "15.6" +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/101125 +time_frame: none +data_source: database +data_category: optional +instrumentation_class: DormantUserPeriodSettingMetric +performance_indicator_type: [] +distribution: +- ce +- ee +tier: +- free +- premium +- ultimate diff --git a/db/post_migrate/20210818185845_backfill_projects_with_coverage.rb b/db/post_migrate/20210818185845_backfill_projects_with_coverage.rb index 003b7536767..d86d49f4393 100644 --- a/db/post_migrate/20210818185845_backfill_projects_with_coverage.rb +++ b/db/post_migrate/20210818185845_backfill_projects_with_coverage.rb @@ -1,29 +1,8 @@ # frozen_string_literal: true class BackfillProjectsWithCoverage < ActiveRecord::Migration[6.1] - include Gitlab::Database::MigrationHelpers - - MIGRATION = 'BackfillProjectsWithCoverage' - DELAY_INTERVAL = 2.minutes - BATCH_SIZE = 10_000 - SUB_BATCH_SIZE = 1_000 - - disable_ddl_transaction! - - class CiDailyBuildGroupReportResult < ActiveRecord::Base - include EachBatch - - self.table_name = 'ci_daily_build_group_report_results' - end - def up - queue_background_migration_jobs_by_range_at_intervals( - CiDailyBuildGroupReportResult, - MIGRATION, - DELAY_INTERVAL, - batch_size: BATCH_SIZE, - other_job_arguments: [SUB_BATCH_SIZE] - ) + # noop end def down diff --git a/doc/ci/pipelines/settings.md b/doc/ci/pipelines/settings.md index f04a9e549f5..d183c6e43ce 100644 --- a/doc/ci/pipelines/settings.md +++ b/doc/ci/pipelines/settings.md @@ -228,53 +228,6 @@ averaged. To add test coverage results to a merge request using the project's `.gitlab-ci.yml` file, provide a regular expression using the [`coverage`](../yaml/index.md#coverage) keyword. - - -### Add test coverage results using project settings (removed) - -> - [Deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/17633) in GitLab 14.8. Replaced by [`coverage` keyword](../yaml/index.md#coverage). -> - [Removed](https://gitlab.com/gitlab-org/gitlab/-/issues/17633) in GitLab 15.0. - -This feature is in its end-of-life process. It was [deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/17633) -in GitLab 14.8. The feature is [removed](https://gitlab.com/gitlab-org/gitlab/-/issues/17633) in GitLab 15.0. - -To migrate from a project setting to the `coverage` keyword, add the [former project setting](#locate-former-project-setting) -to a CI/CD job. For example: - -- A Go test coverage project setting: `coverage: \d+.\d+% of statements`. -- A CI/CD job with `coverage` keyword setting: - - ```yaml - unit-test: - stage: test - coverage: '/coverage: \d+.\d+% of statements/' - script: - - go test -cover - ``` - -The `.gitlab-ci.yml` job [`coverage`](../yaml/index.md#coverage) keyword must be: - -- A regular expression starts and ends with the `/` character. -- Defined as single-quoted string. - -You can verify correct syntax using the [pipeline editor](../pipeline_editor/index.md). - -#### Locate former project setting - -To migrate from the project coverage setting to the `coverage` keyword, use the -regular expression displayed in the settings. Available in GitLab 14.10 and earlier: - -1. On the top bar, select **Main menu > Projects** and find your project. -1. On the left sidebar, select **Settings > CI/CD**. -1. Expand **General pipelines**. - -The regular expression you need is in the **Test coverage parsing** field. - -If you need to retrieve the project coverage setting from many projects, you can -[use the API to programmatically retrieve the setting](https://gitlab.com/gitlab-org/gitlab/-/issues/17633#note_945941397). - - - ### Test coverage examples Use this regex for commonly used test tools. diff --git a/doc/cloud_seed/index.md b/doc/cloud_seed/index.md index bf51da88cb4..04b560f7f87 100644 --- a/doc/cloud_seed/index.md +++ b/doc/cloud_seed/index.md @@ -13,6 +13,9 @@ Cloud Seed is an open-source program led by [GitLab Incubation Engineering](https://about.gitlab.com/handbook/engineering/incubation/) in collaboration with [Google Cloud](https://cloud.google.com/). +Cloud Seed combines Heroku-like ease-of-use with hyper-cloud flexibility. We do this by using OAuth 2 to provision +services on a hyper-cloud based on a foundation of Terraform and infrastructure-as-code to enable day 2 operations. + ## Purpose We believe that it should be **trivial** to deploy web applications (and other workloads) from GitLab to major cloud diff --git a/doc/development/documentation/index.md b/doc/development/documentation/index.md index 0350171544d..e0d7ab59429 100644 --- a/doc/development/documentation/index.md +++ b/doc/development/documentation/index.md @@ -145,66 +145,41 @@ pages. You can safely remove the `type` metadata parameter and its values. ### Batch updates for TW metadata -NOTE: -This task is an MVC, and requires significant manual preparation of the output. -While the task can be time consuming, it is still faster than doing the work -entirely manually. +The [`CODEOWNERS`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/.gitlab/CODEOWNERS) +file contains a list of files and the associated technical writers. -It's important to keep the [`CODEOWNERS`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/.gitlab/CODEOWNERS) -file in the `gitlab` project up to date with the current Technical Writing team assignments. -This information is used in merge requests that contain documentation: +When a merge request contains documentation, the information in the `CODEOWNERS` file determines: -- To populate the eligible approvers section. -- By GitLab Bot to ping reviewers for community contributions. +- The list of users in the **Approvers** section. +- The technical writer that the GitLab Bot pings for community contributions. -GitLab cannot automatically associate the stage and group metadata in our documentation -pages with the technical writer assigned to that group, so we use a Rake task to -generate entries for the `CODEOWNERS` file. Declaring code owners for pages reduces -the number of times GitLab Bot pings the entire Technical Writing team. +You can use a Rake task to update the `CODEOWNERS` file. -The `tw:codeowners` Rake task, located in [`lib/tasks/gitlab/tw/codeowners.rake`](https://gitlab.com/gitlab-org/gitlab/blob/master/lib/tasks/gitlab/tw/codeowners.rake), -contains an array of groups and their assigned technical writer. This task: +#### Update the `CODEOWNERS` file -- Outputs a line for each doc with metadata that matches a group in `lib/tasks/gitlab/tw/codeowners.rake`. - Files not matching a group are skipped. -- Adds the full path to the page, and the assigned technical writer. +To update the `CODEOWNERS` file: -To prepare an update to the `CODEOWNERS` file: +1. Open a merge request to update + [the Rake task](https://gitlab.com/gitlab-org/gitlab/blob/master/lib/tasks/gitlab/tw/codeowners.rake) + with the latest [TW team assignments](https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments). +1. Assign the merge request to a backend maintainer for review and merge. +1. After the MR is merged, go to the root of the `gitlab` repository. +1. Run the Rake task and save the output in a file: -1. Update `lib/tasks/gitlab/tw/codeowners.rake` with the latest [TW team assignments](https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments). - Make this update in a standalone merge request, as it runs a long pipeline and - requires backend maintainer review. Make sure this is merged before you update - `CODEOWNERS` in another merge request. -1. Run the task from the root directory of the `gitlab` repository, and save the output in a file: - - ```ruby + ```shell bundle exec rake tw:codeowners > ~/Desktop/updates.md ``` -1. Open the file you just created (`~/Desktop/updates.md` in this example). Check - that the lines are in alphabetical (ascending) order. If you have to sort - the lines and you use VS Code, you can select everything, press F1, - type `sort`, and select **Sort lines (ascending, case insensitive)**. -1. In the output, look for changes that don't match your expectations: - - New pages, or newly moved pages, show up as added lines. - - Deleted pages, and pages that are now redirects, show up as deleted lines. - - If you see an unusual number of changes to pages that all seem related, - check the metadata for the pages. A group might have been renamed and the Rake task - must be updated to match. - - If all files in a single directory are assigned to the same - technical writer, simplify these entries. Remove all the lines for the individual - files, and leave a single entry for the directory, for example: `/doc/directory/ @tech.writer`. -1. When you are happy with the output, create a new branch to update the - [`CODEOWNERS`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/.gitlab/CODEOWNERS) - file. Replace the documentation-related lines in the `^[Documentation Pages]` - section with the output you prepared. +1. Open the file (for example, `~/Desktop/updates.md`) and copy + the lines in the `^[Documentation Pages]` section. +1. Open the [`CODEOWNERS`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/.gitlab/CODEOWNERS) + file and paste the lines into the `^[Documentation Pages]` section. WARNING: The documentation section is not the last section of the `CODEOWNERS` file. Don't delete data that isn't ours! -1. Create a merge request from this branch with your changes to the `CODEOWNERS` file. - Assign it to a technical writing manager for review. +1. Create a merge request and assign it to a technical writing manager for review. ## Move, rename, or delete a page diff --git a/doc/development/documentation/styleguide/index.md b/doc/development/documentation/styleguide/index.md index 1b6231f8c15..ba8a039507a 100644 --- a/doc/development/documentation/styleguide/index.md +++ b/doc/development/documentation/styleguide/index.md @@ -1337,6 +1337,7 @@ It renders on the GitLab documentation site as: > - Second item in the list ## Tabs + On the docs site, you can format text so it's displayed as tabs. @@ -1355,6 +1356,7 @@ Here's some other content in tab two. ::EndTabs ``` + This code renders on the GitLab documentation site as: diff --git a/doc/development/snowplow/event_dictionary_guide.md b/doc/development/snowplow/event_dictionary_guide.md index c5a21f5a081..794a9a0160c 100644 --- a/doc/development/snowplow/event_dictionary_guide.md +++ b/doc/development/snowplow/event_dictionary_guide.md @@ -22,24 +22,24 @@ All event definitions are stored in the following directories: Each event is defined in a separate YAML file consisting of the following fields: -| Field | Required | Additional information | -|------------------------|----------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `description` | yes | A description of the event. | -| `category` | yes | The event category (see [Structured event taxonomy](index.md#structured-event-taxonomy)). | -| `action` | yes | The event action (see [Structured event taxonomy](index.md#structured-event-taxonomy)). | -| `label_description` | no | A description of the event label (see [Structured event taxonomy](index.md#structured-event-taxonomy)). | -| `property_description` | no | A description of the event property (see [Structured event taxonomy](index.md#structured-event-taxonomy)). | -| `value_description` | no | A description of the event value (see [Structured event taxonomy](index.md#structured-event-taxonomy)). | -| `extra_properties` | no | The type and description of each extra property sent with the event. | -| `identifiers` | no | A list of identifiers sent with the event. Can be set to one or more of `project`, `user`, or `namespace`. | -| `iglu_schema_url` | no | The URL to the custom schema sent with the event, for example, `iglu:com.gitlab/gitlab_experiment/jsonschema/1-0-0`. | -| `product_section` | yes | The [section](https://gitlab.com/gitlab-com/www-gitlab-com/-/blob/master/data/sections.yml). | -| `product_stage` | no | The [stage](https://gitlab.com/gitlab-com/www-gitlab-com/blob/master/data/stages.yml) for the event. | -| `product_group` | yes | The [group](https://gitlab.com/gitlab-com/www-gitlab-com/blob/master/data/stages.yml) that owns the event. | -| `product_category` | no | The [product category](https://gitlab.com/gitlab-com/www-gitlab-com/blob/master/data/categories.yml) for the event. | -| `milestone` | no | The milestone when the event is introduced. | -| `introduced_by_url` | no | The URL to the merge request that introduced the event. | -| `distributions` | yes | The [distributions](https://about.gitlab.com/handbook/marketing/strategic-marketing/tiers/#definitions) where the tracked feature is available. Can be set to one or more of `ce` or `ee`. | +| Field | Required | Additional information | +|------------------------|----------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `description` | yes | A description of the event. | +| `category` | yes | The event category (see [Event schema](index.md#event-schema)). | +| `action` | yes | The event action (see [Event schema](index.md#event-schema)). | +| `label_description` | no | A description of the event label (see [Event schema](index.md#event-schema)). | +| `property_description` | no | A description of the event property (see [Event schema](index.md#event-schema)). | +| `value_description` | no | A description of the event value (see [Event schema](index.md#event-schema)). | +| `extra_properties` | no | The type and description of each extra property sent with the event. | +| `identifiers` | no | A list of identifiers sent with the event. Can be set to one or more of `project`, `user`, or `namespace`. | +| `iglu_schema_url` | no | The URL to the custom schema sent with the event, for example, `iglu:com.gitlab/gitlab_experiment/jsonschema/1-0-0`. | +| `product_section` | yes | The [section](https://gitlab.com/gitlab-com/www-gitlab-com/-/blob/master/data/sections.yml). | +| `product_stage` | no | The [stage](https://gitlab.com/gitlab-com/www-gitlab-com/blob/master/data/stages.yml) for the event. | +| `product_group` | yes | The [group](https://gitlab.com/gitlab-com/www-gitlab-com/blob/master/data/stages.yml) that owns the event. | +| `product_category` | no | The [product category](https://gitlab.com/gitlab-com/www-gitlab-com/blob/master/data/categories.yml) for the event. | +| `milestone` | no | The milestone when the event is introduced. | +| `introduced_by_url` | no | The URL to the merge request that introduced the event. | +| `distributions` | yes | The [distributions](https://about.gitlab.com/handbook/marketing/strategic-marketing/tiers/#definitions) where the tracked feature is available. Can be set to one or more of `ce` or `ee`. | | `tiers` | yes | The [tiers]( https://about.gitlab.com/handbook/marketing/strategic-marketing/tiers/) where the tracked feature is available. Can be set to one or more of `free`, `premium`, or `ultimate`. | ### Example event definition diff --git a/doc/development/snowplow/implementation.md b/doc/development/snowplow/implementation.md index f568acd4428..659165cf18a 100644 --- a/doc/development/snowplow/implementation.md +++ b/doc/development/snowplow/implementation.md @@ -42,14 +42,14 @@ and the custom `extra` object. You can modify this object for any subsequent structured event that fires, although this is not recommended. Tracking implementations must have an `action` and a `category`. You can provide additional -properties from the [structured event taxonomy](index.md#structured-event-taxonomy), in +properties from the [event schema](index.md#event-schema), in addition to an `extra` object that accepts key-value pairs. -| Property | Type | Default value | Description | -|:-----------|:-------|:---------------------------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `category` | string | `document.body.dataset.page` | Page or subsection of a page in which events are captured. | +| Property | Type | Default value | Description | +|:-----------|:-------|:---------------------------|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `category` | string | `document.body.dataset.page` | Page or subsection of a page in which events are captured. | | `action` | string | `'generic'` | Action the user is taking. Clicks must be `click` and activations must be `activate`. For example, focusing a form field is `activate_form_input`, and clicking a button is `click_button`. | -| `data` | object | `{}` | Additional data such as `label`, `property`, `value` as described in [Structured event taxonomy](index.md#structured-event-taxonomy), `context` for custom contexts, and `extra` (key-value pairs object). | +| `data` | object | `{}` | Additional data such as `label`, `property`, `value` as described in [Event schema](index.md#event-schema), `context` for custom contexts, and `extra` (key-value pairs object). | ### Usage recommendations diff --git a/doc/development/snowplow/index.md b/doc/development/snowplow/index.md index 45d501d3ed4..af253486861 100644 --- a/doc/development/snowplow/index.md +++ b/doc/development/snowplow/index.md @@ -80,7 +80,7 @@ sequenceDiagram For more details about the architecture, see [Snowplow infrastructure](infrastructure.md). -## Structured event taxonomy +## Event schema Click events must be consistent. If each feature captures events differently, it can be difficult to perform analysis. diff --git a/doc/development/snowplow/review_guidelines.md b/doc/development/snowplow/review_guidelines.md index 756dbd0ca92..ee2c1eb95df 100644 --- a/doc/development/snowplow/review_guidelines.md +++ b/doc/development/snowplow/review_guidelines.md @@ -35,7 +35,7 @@ events or touches Snowplow related files. #### The Product Intelligence **reviewer** should -- Check that the [event taxonomy](index.md#structured-event-taxonomy) is correct. +- Check that the [event schema](index.md#event-schema) is correct. - Check the [usage recommendations](implementation.md#usage-recommendations). - Check that the [Event Dictionary](event_dictionary_guide.md) is up-to-date. - If needed, check that the events are firing locally using one of the diff --git a/doc/user/admin_area/settings/account_and_limit_settings.md b/doc/user/admin_area/settings/account_and_limit_settings.md index cd1f20a2f4e..3d05aa120cf 100644 --- a/doc/user/admin_area/settings/account_and_limit_settings.md +++ b/doc/user/admin_area/settings/account_and_limit_settings.md @@ -259,18 +259,6 @@ Once a lifetime for access tokens is set, GitLab: allowed lifetime. Three hours is given to allow administrators to change the allowed lifetime, or remove it, before revocation takes place. - -## Allow expired access tokens to be used (removed) **(ULTIMATE SELF)** - -> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/214723) in GitLab 13.1. -> - [Feature flag removed](https://gitlab.com/gitlab-org/gitlab/-/issues/296881) in GitLab 13.9. -> - [Deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/351962) in GitLab 14.8. -> - [Removed](https://gitlab.com/gitlab-org/gitlab/-/issues/351962) in GitLab 15.0. - -This feature was [deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/351962) in GitLab 14.8. -This feature was [removed](https://gitlab.com/gitlab-org/gitlab/-/issues/351962) in GitLab 15.0. - - ## Disable user profile name changes **(PREMIUM SELF)** > [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/24605) in GitLab 12.7. diff --git a/doc/user/crm/index.md b/doc/user/crm/index.md index d7ab2195e2d..2d1aeaa1c07 100644 --- a/doc/user/crm/index.md +++ b/doc/user/crm/index.md @@ -79,6 +79,21 @@ To edit an existing contact: You can also [edit](../../api/graphql/reference/index.md#mutationcustomerrelationscontactupdate) contacts using the GraphQL API. +#### Change the state of a contact + +Each contact can be in one of two states: + +- **Active**: contacts in this state can be added to an issue. +- **Inactive**: contacts in this state cannot be added to an issue. + +To change the state of a contact: + +1. On the top bar, select **Main menu > Groups** and find your group. +1. On the left sidebar, select **Customer relations > Contacts**. +1. Next to the contact you wish to edit, select **Edit** (**{pencil}**). +1. Select or clear the **Active** checkbox. +1. Select **Save changes**. + ## Organizations ### View organizations @@ -153,7 +168,7 @@ API. ### Add contacts to an issue -To add contacts to an issue use the `/add_contacts [contact:address@example.com]` +To add [active](#change-the-state-of-a-contact) contacts to an issue use the `/add_contacts [contact:address@example.com]` [quick action](../project/quick_actions.md). You can also add, remove, or replace issue contacts using the @@ -175,10 +190,15 @@ API. > - [Enabled on GitLab.com and self-managed](https://gitlab.com/gitlab-org/gitlab/-/issues/352123) in GitLab 15.0. > - [Generally available](https://gitlab.com/gitlab-org/gitlab/-/issues/352123) in GitLab 15.2. [Feature flag `contacts_autocomplete`](https://gitlab.com/gitlab-org/gitlab/-/issues/352123) removed. -When you use the `/add_contacts` or `/remove_contacts` quick actions, follow them with `[contact:` and an autocomplete list appears: +When you use the `/add_contacts` quick action, follow it with `[contact:` and an autocomplete list with the [active](#change-the-state-of-a-contact) contacts appears: ```plaintext /add_contacts [contact: +``` + +When you use the `/remove_contacts` quick action, follow it with `[contact:` and an autocomplete list with the contacts added to the issue appears: + +```plaintext /remove_contacts [contact: ``` diff --git a/doc/user/project/quick_actions.md b/doc/user/project/quick_actions.md index 53b3212a164..2ff579b8f8f 100644 --- a/doc/user/project/quick_actions.md +++ b/doc/user/project/quick_actions.md @@ -52,7 +52,7 @@ threads. Some quick actions might not be available to all subscription tiers. | Command | Issue | Merge request | Epic | Action | |:-------------------------------------------------------------------------------------------------|:-----------------------|:-----------------------|:-----------------------|:----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `/add_contacts [contact:email1@example.com] [contact:email2@example.com]` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Add one or more [CRM contacts](../crm/index.md) ([introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/73413) in GitLab 14.6). | +| `/add_contacts [contact:email1@example.com] [contact:email2@example.com]` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Add one or more active [CRM contacts](../crm/index.md) ([introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/73413) in GitLab 14.6). | | `/approve` | **{dotted-circle}** No | **{check-circle}** Yes | **{dotted-circle}** No | Approve the merge request. | | `/assign @user1 @user2` | **{check-circle}** Yes | **{check-circle}** Yes | **{dotted-circle}** No | Assign one or more users. | | `/assign me` | **{check-circle}** Yes | **{check-circle}** Yes | **{dotted-circle}** No | Assign yourself. | diff --git a/lib/gitlab/background_migration/backfill_projects_with_coverage.rb b/lib/gitlab/background_migration/backfill_projects_with_coverage.rb deleted file mode 100644 index ca262c0bd59..00000000000 --- a/lib/gitlab/background_migration/backfill_projects_with_coverage.rb +++ /dev/null @@ -1,41 +0,0 @@ -# frozen_string_literal: true - -module Gitlab - module BackgroundMigration - # Backfill project_ci_feature_usages for a range of projects with coverage - class BackfillProjectsWithCoverage - class ProjectCiFeatureUsage < ActiveRecord::Base # rubocop:disable Style/Documentation - self.table_name = 'project_ci_feature_usages' - end - - COVERAGE_ENUM_VALUE = 1 - INSERT_DELAY_SECONDS = 0.1 - - def perform(start_id, end_id, sub_batch_size) - report_results = ActiveRecord::Base.connection.execute <<~SQL - SELECT DISTINCT project_id, default_branch - FROM ci_daily_build_group_report_results - WHERE id BETWEEN #{start_id} AND #{end_id} - SQL - - report_results.to_a.in_groups_of(sub_batch_size, false) do |batch| - ProjectCiFeatureUsage.insert_all(build_values(batch)) - - sleep INSERT_DELAY_SECONDS - end - end - - private - - def build_values(batch) - batch.map do |data| - { - project_id: data['project_id'], - feature: COVERAGE_ENUM_VALUE, - default_branch: data['default_branch'] - } - end - end - end - end -end diff --git a/lib/gitlab/usage/metrics/instrumentations/dormant_user_period_setting_metric.rb b/lib/gitlab/usage/metrics/instrumentations/dormant_user_period_setting_metric.rb new file mode 100644 index 00000000000..c05664aa9c8 --- /dev/null +++ b/lib/gitlab/usage/metrics/instrumentations/dormant_user_period_setting_metric.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module Gitlab + module Usage + module Metrics + module Instrumentations + class DormantUserPeriodSettingMetric < GenericMetric + value do + ::Gitlab::CurrentSettings.deactivate_dormant_users_period + end + end + end + end + end +end diff --git a/lib/gitlab/usage/metrics/instrumentations/dormant_user_setting_enabled_metric.rb b/lib/gitlab/usage/metrics/instrumentations/dormant_user_setting_enabled_metric.rb new file mode 100644 index 00000000000..82d8570276a --- /dev/null +++ b/lib/gitlab/usage/metrics/instrumentations/dormant_user_setting_enabled_metric.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module Gitlab + module Usage + module Metrics + module Instrumentations + class DormantUserSettingEnabledMetric < GenericMetric + value do + ::Gitlab::CurrentSettings.deactivate_dormant_users + end + end + end + end + end +end diff --git a/scripts/review_apps/review-apps.sh b/scripts/review_apps/review-apps.sh index 0fc245a409f..ed7b6d1e507 100755 --- a/scripts/review_apps/review-apps.sh +++ b/scripts/review_apps/review-apps.sh @@ -143,7 +143,7 @@ function run_task() { local ruby_cmd="${1}" local toolbox_pod=$(get_pod "toolbox") - kubectl exec --namespace "${namespace}" "${toolbox_pod}" -- gitlab-rails runner "${ruby_cmd}" + run_timed_command "kubectl exec --namespace \"${namespace}\" \"${toolbox_pod}\" -- gitlab-rails runner \"${ruby_cmd}\"" } function disable_sign_ups() { @@ -363,7 +363,7 @@ EOF echoinfo "Deploying with:" echoinfo "${HELM_CMD}" - eval "${HELM_CMD}" + run_timed_command "eval \"${HELM_CMD}\"" } function verify_deploy() { diff --git a/spec/frontend/gfm_auto_complete/mock_data.js b/spec/frontend/gfm_auto_complete/mock_data.js index 86795ffd0a5..9c5a9d7ef3d 100644 --- a/spec/frontend/gfm_auto_complete/mock_data.js +++ b/spec/frontend/gfm_auto_complete/mock_data.js @@ -32,3 +32,60 @@ export const eventlistenersMockDefaultMap = [ namespace: 'atwho', }, ]; + +export const crmContactsMock = [ + { + id: 1, + email: 'contact.1@email.com', + firstName: 'Contact', + lastName: 'One', + search: 'contact.1@email.com', + state: 'active', + set: false, + }, + { + id: 2, + email: 'contact.2@email.com', + firstName: 'Contact', + lastName: 'Two', + search: 'contact.2@email.com', + state: 'active', + set: false, + }, + { + id: 3, + email: 'contact.3@email.com', + firstName: 'Contact', + lastName: 'Three', + search: 'contact.3@email.com', + state: 'inactive', + set: false, + }, + { + id: 4, + email: 'contact.4@email.com', + firstName: 'Contact', + lastName: 'Four', + search: 'contact.4@email.com', + state: 'inactive', + set: true, + }, + { + id: 5, + email: 'contact.5@email.com', + firstName: 'Contact', + lastName: 'Five', + search: 'contact.5@email.com', + state: 'active', + set: true, + }, + { + id: 5, + email: 'contact.6@email.com', + firstName: 'Contact', + lastName: 'Six', + search: 'contact.6@email.com', + state: 'active', + set: undefined, // On purpose + }, +]; diff --git a/spec/frontend/gfm_auto_complete_spec.js b/spec/frontend/gfm_auto_complete_spec.js index c3dfc4570f9..68225f39c66 100644 --- a/spec/frontend/gfm_auto_complete_spec.js +++ b/spec/frontend/gfm_auto_complete_spec.js @@ -3,14 +3,23 @@ import MockAdapter from 'axios-mock-adapter'; import $ from 'jquery'; import labelsFixture from 'test_fixtures/autocomplete_sources/labels.json'; import { setHTMLFixture, resetHTMLFixture } from 'helpers/fixtures'; -import GfmAutoComplete, { membersBeforeSave, highlighter } from 'ee_else_ce/gfm_auto_complete'; +import GfmAutoComplete, { + membersBeforeSave, + highlighter, + CONTACT_STATE_ACTIVE, + CONTACTS_ADD_COMMAND, + CONTACTS_REMOVE_COMMAND, +} from 'ee_else_ce/gfm_auto_complete'; import { initEmojiMock, clearEmojiMock } from 'helpers/emoji'; import '~/lib/utils/jquery_at_who'; import { TEST_HOST } from 'helpers/test_constants'; import waitForPromises from 'helpers/wait_for_promises'; import AjaxCache from '~/lib/utils/ajax_cache'; import axios from '~/lib/utils/axios_utils'; -import { eventlistenersMockDefaultMap } from 'ee_else_ce_jest/gfm_auto_complete/mock_data'; +import { + eventlistenersMockDefaultMap, + crmContactsMock, +} from 'ee_else_ce_jest/gfm_auto_complete/mock_data'; describe('GfmAutoComplete', () => { const fetchDataMock = { fetchData: jest.fn() }; @@ -871,7 +880,87 @@ describe('GfmAutoComplete', () => { }); }); - describe('Contacts', () => { + describe('CRM Contacts', () => { + const dataSources = { + contacts: `${TEST_HOST}/autocomplete_sources/contacts`, + }; + + const allContacts = crmContactsMock; + const assignedContacts = allContacts.filter((contact) => contact.set); + const unassignedContacts = allContacts.filter( + (contact) => contact.state === CONTACT_STATE_ACTIVE && !contact.set, + ); + + let autocomplete; + let $textarea; + + beforeEach(() => { + setHTMLFixture(''); + autocomplete = new GfmAutoComplete(dataSources); + $textarea = $('textarea'); + autocomplete.setup($textarea, { contacts: true }); + }); + + afterEach(() => { + autocomplete.destroy(); + resetHTMLFixture(); + }); + + const triggerDropdown = (text) => { + $textarea.trigger('focus').val(text).caret('pos', -1); + $textarea.trigger('keyup'); + + jest.runOnlyPendingTimers(); + }; + + const getDropdownItems = () => { + const dropdown = document.getElementById('at-view-contacts'); + const items = dropdown.getElementsByTagName('li'); + return [].map.call(items, (item) => item.textContent.trim()); + }; + + const expectContacts = ({ input, output }) => { + triggerDropdown(input); + + expect(getDropdownItems()).toEqual(output.map((contact) => contact.email)); + }; + + describe('with no contacts assigned', () => { + beforeEach(() => { + autocomplete.cachedData['[contact:'] = [...unassignedContacts]; + }); + + it.each` + input | output + ${`${CONTACTS_ADD_COMMAND} [contact:`} | ${unassignedContacts} + ${`${CONTACTS_REMOVE_COMMAND} [contact:`} | ${[]} + `('$input shows $output.length contacts', expectContacts); + }); + + describe('with some contacts assigned', () => { + beforeEach(() => { + autocomplete.cachedData['[contact:'] = allContacts; + }); + + it.each` + input | output + ${`${CONTACTS_ADD_COMMAND} [contact:`} | ${unassignedContacts} + ${`${CONTACTS_REMOVE_COMMAND} [contact:`} | ${assignedContacts} + `('$input shows $output.length contacts', expectContacts); + }); + + describe('with all contacts assigned', () => { + beforeEach(() => { + autocomplete.cachedData['[contact:'] = [...assignedContacts]; + }); + + it.each` + input | output + ${`${CONTACTS_ADD_COMMAND} [contact:`} | ${[]} + ${`${CONTACTS_REMOVE_COMMAND} [contact:`} | ${assignedContacts} + `('$input shows $output.length contacts', expectContacts); + }); + it('escapes name and email correct', () => { const xssPayload = ''; const escapedPayload = '<script>alert(1)</script>'; diff --git a/spec/lib/gitlab/background_migration/backfill_projects_with_coverage_spec.rb b/spec/lib/gitlab/background_migration/backfill_projects_with_coverage_spec.rb deleted file mode 100644 index 4a65ecf8c75..00000000000 --- a/spec/lib/gitlab/background_migration/backfill_projects_with_coverage_spec.rb +++ /dev/null @@ -1,95 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe Gitlab::BackgroundMigration::BackfillProjectsWithCoverage, - :suppress_gitlab_schemas_validate_connection, schema: 20210818185845 do - let(:projects) { table(:projects) } - let(:project_ci_feature_usages) { table(:project_ci_feature_usages) } - let(:ci_pipelines) { table(:ci_pipelines) } - let(:ci_daily_build_group_report_results) { table(:ci_daily_build_group_report_results) } - let(:group) { table(:namespaces).create!(name: 'user', path: 'user') } - let(:project_1) { projects.create!(namespace_id: group.id) } - let(:project_2) { projects.create!(namespace_id: group.id) } - let(:pipeline_1) { ci_pipelines.create!(project_id: project_1.id, source: 13) } - let(:pipeline_2) { ci_pipelines.create!(project_id: project_1.id, source: 13) } - let(:pipeline_3) { ci_pipelines.create!(project_id: project_2.id, source: 13) } - let(:pipeline_4) { ci_pipelines.create!(project_id: project_2.id, source: 13) } - - subject { described_class.new } - - describe '#perform' do - before do - ci_daily_build_group_report_results.create!( - id: 1, - project_id: project_1.id, - date: 4.days.ago, - last_pipeline_id: pipeline_1.id, - ref_path: 'main', - group_name: 'rspec', - data: { coverage: 95.0 }, - default_branch: true, - group_id: group.id - ) - - ci_daily_build_group_report_results.create!( - id: 2, - project_id: project_1.id, - date: 3.days.ago, - last_pipeline_id: pipeline_2.id, - ref_path: 'main', - group_name: 'rspec', - data: { coverage: 95.0 }, - default_branch: true, - group_id: group.id - ) - - ci_daily_build_group_report_results.create!( - id: 3, - project_id: project_2.id, - date: 2.days.ago, - last_pipeline_id: pipeline_3.id, - ref_path: 'main', - group_name: 'rspec', - data: { coverage: 95.0 }, - default_branch: true, - group_id: group.id - ) - - ci_daily_build_group_report_results.create!( - id: 4, - project_id: project_2.id, - date: 1.day.ago, - last_pipeline_id: pipeline_4.id, - ref_path: 'test_branch', - group_name: 'rspec', - data: { coverage: 95.0 }, - default_branch: false, - group_id: group.id - ) - - stub_const("#{described_class}::INSERT_DELAY_SECONDS", 0) - end - - it 'creates entries per project and default_branch combination in the given range', :aggregate_failures do - subject.perform(1, 4, 2) - - entries = project_ci_feature_usages.order('project_id ASC, default_branch DESC') - - expect(entries.count).to eq(3) - expect(entries[0]).to have_attributes(project_id: project_1.id, feature: 1, default_branch: true) - expect(entries[1]).to have_attributes(project_id: project_2.id, feature: 1, default_branch: true) - expect(entries[2]).to have_attributes(project_id: project_2.id, feature: 1, default_branch: false) - end - - context 'when an entry for the project and default branch combination already exists' do - before do - subject.perform(1, 4, 2) - end - - it 'does not create a new entry' do - expect { subject.perform(1, 4, 2) }.not_to change { project_ci_feature_usages.count } - end - end - end -end diff --git a/spec/lib/gitlab/usage/metrics/instrumentations/dormant_user_period_setting_metric_spec.rb b/spec/lib/gitlab/usage/metrics/instrumentations/dormant_user_period_setting_metric_spec.rb new file mode 100644 index 00000000000..a63616aeb48 --- /dev/null +++ b/spec/lib/gitlab/usage/metrics/instrumentations/dormant_user_period_setting_metric_spec.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Gitlab::Usage::Metrics::Instrumentations::DormantUserPeriodSettingMetric do + using RSpec::Parameterized::TableSyntax + + where(:deactivate_dormant_users_period_value, :expected_value) do + 90 | 90 # default + 365 | 365 + end + + with_them do + before do + stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false') + stub_application_setting(deactivate_dormant_users_period: deactivate_dormant_users_period_value) + end + + it_behaves_like 'a correct instrumented metric value', {} + end +end diff --git a/spec/lib/gitlab/usage/metrics/instrumentations/dormant_user_setting_enabled_metric_spec.rb b/spec/lib/gitlab/usage/metrics/instrumentations/dormant_user_setting_enabled_metric_spec.rb new file mode 100644 index 00000000000..5c8ca502f82 --- /dev/null +++ b/spec/lib/gitlab/usage/metrics/instrumentations/dormant_user_setting_enabled_metric_spec.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Gitlab::Usage::Metrics::Instrumentations::DormantUserSettingEnabledMetric do + using RSpec::Parameterized::TableSyntax + + where(:deactivate_dormant_users_enabled, :expected_value) do + 1 | 1 + 0 | 0 + end + + with_them do + before do + stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false') + stub_application_setting(deactivate_dormant_users: deactivate_dormant_users_enabled) + end + + it_behaves_like 'a correct instrumented metric value', {} + end +end diff --git a/spec/migrations/20210818185845_backfill_projects_with_coverage_spec.rb b/spec/migrations/20210818185845_backfill_projects_with_coverage_spec.rb deleted file mode 100644 index 13a6aa5413e..00000000000 --- a/spec/migrations/20210818185845_backfill_projects_with_coverage_spec.rb +++ /dev/null @@ -1,71 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' -require_migration! - -RSpec.describe BackfillProjectsWithCoverage, :suppress_gitlab_schemas_validate_connection do - let(:projects) { table(:projects) } - let(:ci_pipelines) { table(:ci_pipelines) } - let(:ci_daily_build_group_report_results) { table(:ci_daily_build_group_report_results) } - let(:group) { table(:namespaces).create!(name: 'user', path: 'user') } - let(:project_1) { projects.create!(namespace_id: group.id) } - let(:project_2) { projects.create!(namespace_id: group.id) } - let(:pipeline_1) { ci_pipelines.create!(project_id: project_1.id) } - let(:pipeline_2) { ci_pipelines.create!(project_id: project_2.id) } - let(:pipeline_3) { ci_pipelines.create!(project_id: project_2.id) } - - describe '#up' do - before do - stub_const("#{described_class}::BATCH_SIZE", 2) - stub_const("#{described_class}::SUB_BATCH_SIZE", 1) - - ci_daily_build_group_report_results.create!( - id: 1, - project_id: project_1.id, - date: 3.days.ago, - last_pipeline_id: pipeline_1.id, - ref_path: 'main', - group_name: 'rspec', - data: { coverage: 95.0 }, - default_branch: true, - group_id: group.id - ) - - ci_daily_build_group_report_results.create!( - id: 2, - project_id: project_2.id, - date: 2.days.ago, - last_pipeline_id: pipeline_2.id, - ref_path: 'main', - group_name: 'rspec', - data: { coverage: 95.0 }, - default_branch: true, - group_id: group.id - ) - - ci_daily_build_group_report_results.create!( - id: 3, - project_id: project_2.id, - date: 1.day.ago, - last_pipeline_id: pipeline_3.id, - ref_path: 'test_branch', - group_name: 'rspec', - data: { coverage: 95.0 }, - default_branch: false, - group_id: group.id - ) - end - - it 'schedules BackfillProjectsWithCoverage background jobs', :aggregate_failures do - Sidekiq::Testing.fake! do - freeze_time do - migrate! - - expect(described_class::MIGRATION).to be_scheduled_delayed_migration(2.minutes, 1, 2, 1) - expect(described_class::MIGRATION).to be_scheduled_delayed_migration(4.minutes, 3, 3, 1) - expect(BackgroundMigrationWorker.jobs.size).to eq(2) - end - end - end - end -end