Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
7162e84914
commit
01fbd09ea9
|
@ -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.
|
||||
|
|
|
@ -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 |
|
||||
| -------- | -------- | ------ | ----- | ------- | -------- | ----- |
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
||||
<!-- start_remove The following content will be removed on remove_date: '2023-08-22' -->
|
||||
|
||||
### 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).
|
||||
|
||||
<!-- end_remove -->
|
||||
|
||||
### Test coverage examples
|
||||
|
||||
Use this regex for commonly used test tools.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 <kbd>F1</kbd>,
|
||||
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
|
||||
|
||||
|
|
|
@ -1337,6 +1337,7 @@ It renders on the GitLab documentation site as:
|
|||
> - Second item in the list
|
||||
|
||||
## Tabs
|
||||
<!-- markdownlint-disable tabs-blank-lines -->
|
||||
|
||||
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
|
||||
```
|
||||
<!-- markdownlint-enable tabs-blank-lines -->
|
||||
|
||||
This code renders on the GitLab documentation site as:
|
||||
|
||||
|
|
|
@ -23,13 +23,13 @@ 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)). |
|
||||
| `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`. |
|
||||
|
|
|
@ -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. |
|
||||
| `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
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
||||
<!-- start_remove The following content will be removed on remove_date: '2022-08-22' -->
|
||||
## 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.
|
||||
<!-- end_remove -->
|
||||
|
||||
## Disable user profile name changes **(PREMIUM SELF)**
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/24605) in GitLab 12.7.
|
||||
|
|
|
@ -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:
|
||||
```
|
||||
|
||||
|
|
|
@ -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. |
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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() {
|
||||
|
|
|
@ -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
|
||||
},
|
||||
];
|
||||
|
|
|
@ -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('<textarea></textarea>');
|
||||
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 = '<script>alert(1)</script>';
|
||||
const escapedPayload = '<script>alert(1)</script>';
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
Loading…
Reference in New Issue