diff options
74 files changed, 494 insertions, 216 deletions
diff --git a/.gitlab/issue_templates/Audit Event Proposal.md b/.gitlab/issue_templates/Audit Event Proposal.md index 7a5408ca1f2..af32845bd9a 100644 --- a/.gitlab/issue_templates/Audit Event Proposal.md +++ b/.gitlab/issue_templates/Audit Event Proposal.md @@ -9,5 +9,5 @@ <!-- Describe the audit event you are proposing should be added, including any details of what should be captured, how, and why. --> /label ~"Category:Audit Events" -/label ~"feature" +/label ~"type::feature" /label ~"group::compliance" diff --git a/.gitlab/issue_templates/Bug.md b/.gitlab/issue_templates/Bug.md index 41b694fdf2c..b9fed3745d1 100644 --- a/.gitlab/issue_templates/Bug.md +++ b/.gitlab/issue_templates/Bug.md @@ -2,10 +2,10 @@ Please read this! Before opening a new issue, make sure to search for keywords in the issues -filtered by the "regression" or "bug" label: +filtered by the "regression" or "type::bug" label: - https://gitlab.com/gitlab-org/gitlab/issues?label_name%5B%5D=regression -- https://gitlab.com/gitlab-org/gitlab/issues?label_name%5B%5D=bug +- https://gitlab.com/gitlab-org/gitlab/issues?label_name%5B%5D=type::bug and verify the issue you're about to submit isn't a duplicate. ---> @@ -82,4 +82,4 @@ will also determine whether the bug is fixed in a more recent version. --> <!-- If you can, link to the line of code that might be responsible for the problem. --> -/label ~bug +/label ~"type::bug" diff --git a/.gitlab/issue_templates/Experiment Successful Cleanup.md b/.gitlab/issue_templates/Experiment Successful Cleanup.md index 42f26342342..e28c866c0e5 100644 --- a/.gitlab/issue_templates/Experiment Successful Cleanup.md +++ b/.gitlab/issue_templates/Experiment Successful Cleanup.md @@ -17,4 +17,4 @@ The changes need to become an official part of the product. - [ ] After the flag removal is deployed, [clean up the feature/experiment feature flags](https://docs.gitlab.com/ee/development/feature_flags/controls.html#cleaning-up) by running chatops command in `#production` channel - [ ] Ensure the corresponding [Experiment Tracking](https://gitlab.com/groups/gitlab-org/-/boards/1352542?label_name[]=devops%3A%3Agrowth&label_name[]=growth%20experiment&label_name[]=experiment%20tracking) issue is updated -/label ~"feature" ~"feature::maintenance" ~"workflow::scheduling" ~"growth experiment" ~"feature flag" +/label ~"type::maintenance" ~"workflow::scheduling" ~"growth experiment" ~"feature flag" diff --git a/.gitlab/issue_templates/Feature Proposal - basic.md b/.gitlab/issue_templates/Feature Proposal - basic.md index 0c05b7a0165..794611180eb 100644 --- a/.gitlab/issue_templates/Feature Proposal - basic.md +++ b/.gitlab/issue_templates/Feature Proposal - basic.md @@ -7,4 +7,4 @@ <!-- Consider adding related issues and epics to this issue. You can also reference the Feature Proposal Template (https://gitlab.com/gitlab-org/gitlab/-/blob/master/.gitlab/issue_templates/Feature%20proposal%20-%20detailed.md) for additional details to consider adding to this issue. Additionally, as a data oriented organization, when your feature exits planning breakdown, consider adding the `What does success look like, and how can we measure that?` section. --> -/label ~feature::addition ~"group::" ~"section::" ~"Category:" ~"GitLab Core"/~"GitLab Premium"/~"GitLab Ultimate" +/label ~"type::feature" ~feature::addition ~"group::" ~"section::" ~"Category:" ~"GitLab Core"/~"GitLab Premium"/~"GitLab Ultimate" diff --git a/.gitlab/issue_templates/Feature Proposal - lean.md b/.gitlab/issue_templates/Feature Proposal - lean.md index 9dd4bdc6b22..f653f03995d 100644 --- a/.gitlab/issue_templates/Feature Proposal - lean.md +++ b/.gitlab/issue_templates/Feature Proposal - lean.md @@ -14,7 +14,7 @@ -/label ~"feature" ~"group::" ~"section::" ~"Category::" ~"GitLab Free"/~"GitLab Premium"/~"GitLab Ultimate" +/label ~"type::feature" ~"group::" ~"section::" ~"Category::" ~"GitLab Free"/~"GitLab Premium"/~"GitLab Ultimate" <!--- Use the following resources to find the appropriate labels: diff --git a/.gitlab/issue_templates/Feature proposal - detailed.md b/.gitlab/issue_templates/Feature proposal - detailed.md index 9759bb7e2dc..6448553d1b0 100644 --- a/.gitlab/issue_templates/Feature proposal - detailed.md +++ b/.gitlab/issue_templates/Feature proposal - detailed.md @@ -112,6 +112,4 @@ Use the following resources to find the appropriate labels: --> /label ~devops:: ~group: ~Category: /label ~"GitLab Free"/~"GitLab Premium"/~"GitLab Ultimate" -/label ~feature -/label ~documentation -/label ~direction +/label ~"type::feature" ~documentation ~direction diff --git a/.gitlab/issue_templates/InfraDev.md b/.gitlab/issue_templates/InfraDev.md index bc0e65c3c22..d337e75289c 100644 --- a/.gitlab/issue_templates/InfraDev.md +++ b/.gitlab/issue_templates/InfraDev.md @@ -53,4 +53,4 @@ See also: --> /label ~"infradev" -/label ~"bug" +/label ~"type::bug" diff --git a/.gitlab/issue_templates/Refactoring.md b/.gitlab/issue_templates/Refactoring.md index d9466185ff7..df18dcf7656 100644 --- a/.gitlab/issue_templates/Refactoring.md +++ b/.gitlab/issue_templates/Refactoring.md @@ -41,9 +41,9 @@ please list them here. <!-- Please select the appropriate label from the following: ~"feature::addition" - ~"feature::maintenance" + ~"type::maintenance" ~"tooling::pipelines" ~"tooling::workflow" --> -/label ~"feature::maintenance" +/label ~"type::maintenance" diff --git a/.gitlab/issue_templates/Technical Evaluation.md b/.gitlab/issue_templates/Technical Evaluation.md index cf939725a78..680ecb7d9a6 100644 --- a/.gitlab/issue_templates/Technical Evaluation.md +++ b/.gitlab/issue_templates/Technical Evaluation.md @@ -29,5 +29,5 @@ ### Team -- [ ] Add ~"workflow::planning breakdown" ~feature and the corresponding `~devops::<stage>` and `~group::<group>` labels. +- [ ] Add ~"workflow::planning breakdown" ~"type::feature" and the corresponding `~devops::<stage>` and `~group::<group>` labels. - [ ] Ping the PM and EM. diff --git a/.gitlab/merge_request_templates/Documentation.md b/.gitlab/merge_request_templates/Documentation.md index 66c1eff412b..893ae7b93b5 100644 --- a/.gitlab/merge_request_templates/Documentation.md +++ b/.gitlab/merge_request_templates/Documentation.md @@ -20,10 +20,10 @@ If you are only adding documentation, do not add any of the following labels: -- `~"feature"` +- `~"type::feature"` - `~"frontend"` - `~"backend"` -- `~"bug"` +- `~"type::bug"` - `~"database"` These labels cause the MR to be added to code verification QA issues. diff --git a/.gitlab/merge_request_templates/New End To End Test.md b/.gitlab/merge_request_templates/New End To End Test.md index f9664c6315f..9ecf8999f66 100644 --- a/.gitlab/merge_request_templates/New End To End Test.md +++ b/.gitlab/merge_request_templates/New End To End Test.md @@ -24,5 +24,5 @@ Please link to the respective test case in the testcases project /label ~"Quality:test-gap" ~"Quality:EE test gaps" -<!-- Select the appropriate feature label, ~"feature::addition" for tests added for new features, ~"feature::maintenance" for tests added for existing features --> -/label ~"feature::addition" ~"feature::maintenance" +<!-- Select the appropriate feature label, ~"feature::addition" for tests added for new features, ~"type::maintenance" for tests added for existing features --> +/label ~"feature::addition" ~"type::maintenance" diff --git a/.gitlab/merge_request_templates/Pipeline Configuration.md b/.gitlab/merge_request_templates/Pipeline Configuration.md index 083992bff92..62210028c18 100644 --- a/.gitlab/merge_request_templates/Pipeline Configuration.md +++ b/.gitlab/merge_request_templates/Pipeline Configuration.md @@ -35,4 +35,4 @@ This will help keep track of expected cost increases to the [GitLab project aver - [ ] Consider communicating these changes to the broader team following the [communication guideline for pipeline changes](https://about.gitlab.com/handbook/engineering/quality/engineering-productivity/#pipeline-changes) -/label ~tooling ~"tooling::pipelines" ~"Engineering Productivity" +/label ~"type::tooling" ~"tooling::pipelines" ~"Engineering Productivity" diff --git a/.gitlab/merge_request_templates/Quarantine End to End Test.md b/.gitlab/merge_request_templates/Quarantine End to End Test.md index 4edfd2a8c8e..4caebb7f1bb 100644 --- a/.gitlab/merge_request_templates/Quarantine End to End Test.md +++ b/.gitlab/merge_request_templates/Quarantine End to End Test.md @@ -30,7 +30,7 @@ the noise (due to constantly failing tests, flaky tests, and so on) so that new - [ ] To ensure a faster turnaround, ask in the `#quality` Slack channel for someone to review and merge the merge request, rather than assigning it directly. <!-- Base labels. --> -/label ~"Quality" ~"QA" ~"feature" ~"feature::maintenance" +/label ~"Quality" ~"QA" ~"type::maintenance" <!-- Choose the stage that appears in the test path, e.g. ~"devops::create" for diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION index 0d5366b7e02..3c03f29e505 100644 --- a/GITALY_SERVER_VERSION +++ b/GITALY_SERVER_VERSION @@ -1 +1 @@ -ba8bc12dd0ff83fc726c6c09ee0cb0ebc311a748 +d99f9cbac9164647666f1778eb912568c261813d diff --git a/app/assets/javascripts/issues_list/components/issues_list_app.vue b/app/assets/javascripts/issues_list/components/issues_list_app.vue index 2e5b912f4de..7f2082e5b90 100644 --- a/app/assets/javascripts/issues_list/components/issues_list_app.vue +++ b/app/assets/javascripts/issues_list/components/issues_list_app.vue @@ -36,6 +36,7 @@ import { TOKEN_TYPE_LABEL, TOKEN_TYPE_MILESTONE, TOKEN_TYPE_MY_REACTION, + TOKEN_TYPE_RELEASE, TOKEN_TYPE_TYPE, TOKEN_TYPE_WEIGHT, UPDATED_DESC, @@ -65,6 +66,7 @@ import { TOKEN_TITLE_LABEL, TOKEN_TITLE_MILESTONE, TOKEN_TITLE_MY_REACTION, + TOKEN_TITLE_RELEASE, TOKEN_TITLE_TYPE, TOKEN_TITLE_WEIGHT, } from '~/vue_shared/components/filtered_search_bar/constants'; @@ -88,6 +90,8 @@ const LabelToken = () => import('~/vue_shared/components/filtered_search_bar/tokens/label_token.vue'); const MilestoneToken = () => import('~/vue_shared/components/filtered_search_bar/tokens/milestone_token.vue'); +const ReleaseToken = () => + import('~/vue_shared/components/filtered_search_bar/tokens/release_token.vue'); const WeightToken = () => import('~/vue_shared/components/filtered_search_bar/tokens/weight_token.vue'); @@ -165,6 +169,9 @@ export default { newIssuePath: { default: '', }, + releasesPath: { + default: '', + }, rssPath: { default: '', }, @@ -317,7 +324,6 @@ export default { title: TOKEN_TITLE_MILESTONE, icon: 'clock', token: MilestoneToken, - unique: true, fetchMilestones: this.fetchMilestones, }, { @@ -341,6 +347,16 @@ export default { }, ]; + if (this.isProject) { + tokens.push({ + type: TOKEN_TYPE_RELEASE, + title: TOKEN_TITLE_RELEASE, + icon: 'rocket', + token: ReleaseToken, + fetchReleases: this.fetchReleases, + }); + } + if (this.isSignedIn) { tokens.push({ type: TOKEN_TYPE_MY_REACTION, @@ -371,7 +387,6 @@ export default { title: TOKEN_TITLE_ITERATION, icon: 'iteration', token: IterationToken, - unique: true, fetchIterations: this.fetchIterations, }); } @@ -457,6 +472,9 @@ export default { fetchEmojis(search) { return this.fetchWithCache(this.autocompleteAwardEmojisPath, 'emojis', 'name', search); }, + fetchReleases(search) { + return this.fetchWithCache(this.releasesPath, 'releases', 'tag', search); + }, fetchLabels(search) { return this.$apollo .query({ diff --git a/app/assets/javascripts/issues_list/constants.js b/app/assets/javascripts/issues_list/constants.js index c1949c40373..da9b96d0e22 100644 --- a/app/assets/javascripts/issues_list/constants.js +++ b/app/assets/javascripts/issues_list/constants.js @@ -166,6 +166,7 @@ const LABEL_PRIORITY_ASC_SORT = 'label_priority_asc'; const POPULARITY_ASC_SORT = 'popularity_asc'; const WEIGHT_DESC_SORT = 'weight_desc'; const BLOCKING_ISSUES_DESC_SORT = 'blocking_issues_desc'; +const TITLE_ASC_SORT = 'title_asc'; const TITLE_DESC_SORT = 'title_desc'; export const urlSortParams = { @@ -187,7 +188,7 @@ export const urlSortParams = { [WEIGHT_ASC]: WEIGHT, [WEIGHT_DESC]: WEIGHT_DESC_SORT, [BLOCKING_ISSUES_DESC]: BLOCKING_ISSUES_DESC_SORT, - [TITLE_ASC]: TITLE, + [TITLE_ASC]: TITLE_ASC_SORT, [TITLE_DESC]: TITLE_DESC_SORT, }; @@ -211,6 +212,7 @@ export const TOKEN_TYPE_ASSIGNEE = 'assignee_username'; export const TOKEN_TYPE_MILESTONE = 'milestone'; export const TOKEN_TYPE_LABEL = 'labels'; export const TOKEN_TYPE_TYPE = 'type'; +export const TOKEN_TYPE_RELEASE = 'release'; export const TOKEN_TYPE_MY_REACTION = 'my_reaction_emoji'; export const TOKEN_TYPE_CONFIDENTIAL = 'confidential'; export const TOKEN_TYPE_ITERATION = 'iteration'; @@ -291,6 +293,21 @@ export const filters = { }, }, }, + [TOKEN_TYPE_RELEASE]: { + [API_PARAM]: { + [NORMAL_FILTER]: 'releaseTag', + [SPECIAL_FILTER]: 'releaseTagWildcardId', + }, + [URL_PARAM]: { + [OPERATOR_IS]: { + [NORMAL_FILTER]: 'release_tag', + [SPECIAL_FILTER]: 'release_tag', + }, + [OPERATOR_IS_NOT]: { + [NORMAL_FILTER]: 'not[release_tag]', + }, + }, + }, [TOKEN_TYPE_MY_REACTION]: { [API_PARAM]: { [NORMAL_FILTER]: 'myReactionEmoji', diff --git a/app/assets/javascripts/issues_list/index.js b/app/assets/javascripts/issues_list/index.js index 47af20f5271..421d6b35919 100644 --- a/app/assets/javascripts/issues_list/index.js +++ b/app/assets/javascripts/issues_list/index.js @@ -137,6 +137,7 @@ export function mountIssuesListApp() { newIssuePath, projectImportJiraPath, quickActionsHelpPath, + releasesPath, resetPath, rssPath, showNewIssueLink, @@ -164,6 +165,7 @@ export function mountIssuesListApp() { isSignedIn: parseBoolean(isSignedIn), jiraIntegrationPath, newIssuePath, + releasesPath, rssPath, showNewIssueLink: parseBoolean(showNewIssueLink), signInPath, diff --git a/app/assets/javascripts/issues_list/queries/get_issues.query.graphql b/app/assets/javascripts/issues_list/queries/get_issues.query.graphql index a7517b88c5a..9866efbcecc 100644 --- a/app/assets/javascripts/issues_list/queries/get_issues.query.graphql +++ b/app/assets/javascripts/issues_list/queries/get_issues.query.graphql @@ -16,6 +16,8 @@ query getIssues( $milestoneTitle: [String] $milestoneWildcardId: MilestoneWildcardId $myReactionEmoji: String + $releaseTag: [String!] + $releaseTagWildcardId: ReleaseTagWildcardId $types: [IssueType!] $not: NegatedIssueFilterInput $beforeCursor: String @@ -66,6 +68,8 @@ query getIssues( milestoneTitle: $milestoneTitle milestoneWildcardId: $milestoneWildcardId myReactionEmoji: $myReactionEmoji + releaseTag: $releaseTag + releaseTagWildcardId: $releaseTagWildcardId types: $types not: $not before: $beforeCursor diff --git a/app/assets/javascripts/issues_list/queries/get_issues_counts.query.graphql b/app/assets/javascripts/issues_list/queries/get_issues_counts.query.graphql index 28e62f0859e..5e755ec5870 100644 --- a/app/assets/javascripts/issues_list/queries/get_issues_counts.query.graphql +++ b/app/assets/javascripts/issues_list/queries/get_issues_counts.query.graphql @@ -10,6 +10,8 @@ query getIssuesCount( $milestoneTitle: [String] $milestoneWildcardId: MilestoneWildcardId $myReactionEmoji: String + $releaseTag: [String!] + $releaseTagWildcardId: ReleaseTagWildcardId $types: [IssueType!] $not: NegatedIssueFilterInput ) { @@ -78,6 +80,8 @@ query getIssuesCount( milestoneTitle: $milestoneTitle milestoneWildcardId: $milestoneWildcardId myReactionEmoji: $myReactionEmoji + releaseTag: $releaseTag + releaseTagWildcardId: $releaseTagWildcardId types: $types not: $not ) { @@ -94,6 +98,8 @@ query getIssuesCount( milestoneTitle: $milestoneTitle milestoneWildcardId: $milestoneWildcardId myReactionEmoji: $myReactionEmoji + releaseTag: $releaseTag + releaseTagWildcardId: $releaseTagWildcardId types: $types not: $not ) { @@ -110,6 +116,8 @@ query getIssuesCount( milestoneTitle: $milestoneTitle milestoneWildcardId: $milestoneWildcardId myReactionEmoji: $myReactionEmoji + releaseTag: $releaseTag + releaseTagWildcardId: $releaseTagWildcardId types: $types not: $not ) { diff --git a/app/assets/javascripts/issues_list/utils.js b/app/assets/javascripts/issues_list/utils.js index edb9eeba095..0e57e2bff83 100644 --- a/app/assets/javascripts/issues_list/utils.js +++ b/app/assets/javascripts/issues_list/utils.js @@ -21,10 +21,13 @@ import { RELATIVE_POSITION_ASC, SPECIAL_FILTER, SPECIAL_FILTER_VALUES, + TITLE_ASC, + TITLE_DESC, TOKEN_TYPE_ASSIGNEE, TOKEN_TYPE_CONFIDENTIAL, TOKEN_TYPE_ITERATION, TOKEN_TYPE_MILESTONE, + TOKEN_TYPE_RELEASE, TOKEN_TYPE_TYPE, UPDATED_ASC, UPDATED_DESC, @@ -114,11 +117,19 @@ export const getSortOptions = (hasIssueWeightsFeature, hasBlockedIssuesFeature) descending: RELATIVE_POSITION_ASC, }, }, + { + id: 9, + title: __('Title'), + sortDirection: { + ascending: TITLE_ASC, + descending: TITLE_DESC, + }, + }, ]; if (hasIssueWeightsFeature) { sortOptions.push({ - id: 9, + id: sortOptions.length + 1, title: __('Weight'), sortDirection: { ascending: WEIGHT_ASC, @@ -129,7 +140,7 @@ export const getSortOptions = (hasIssueWeightsFeature, hasBlockedIssuesFeature) if (hasBlockedIssuesFeature) { sortOptions.push({ - id: 10, + id: sortOptions.length + 1, title: __('Blocking'), sortDirection: { ascending: BLOCKING_ISSUES_DESC, @@ -194,9 +205,10 @@ const getFilterType = (data, tokenType = '') => ? SPECIAL_FILTER : NORMAL_FILTER; +const wildcardTokens = [TOKEN_TYPE_ITERATION, TOKEN_TYPE_MILESTONE, TOKEN_TYPE_RELEASE]; + const isWildcardValue = (tokenType, value) => - (tokenType === TOKEN_TYPE_ITERATION || tokenType === TOKEN_TYPE_MILESTONE) && - SPECIAL_FILTER_VALUES.includes(value); + wildcardTokens.includes(tokenType) && SPECIAL_FILTER_VALUES.includes(value); const requiresUpperCaseValue = (tokenType, value) => tokenType === TOKEN_TYPE_TYPE || isWildcardValue(tokenType, value); diff --git a/app/assets/javascripts/vue_merge_request_widget/components/extensions/base.vue b/app/assets/javascripts/vue_merge_request_widget/components/extensions/base.vue index 68a8dbba624..6f10f788952 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/extensions/base.vue +++ b/app/assets/javascripts/vue_merge_request_widget/components/extensions/base.vue @@ -209,17 +209,25 @@ export default { <status-icon v-if="data.icon" :icon-name="data.icon.name" :size="12" class="gl-pl-0" /> <gl-intersection-observer :options="{ rootMargin: '100px', thresholds: 0.1 }" - class="gl-flex-wrap gl-align-self-center gl-display-flex" + class="gl-flex-wrap gl-display-flex gl-w-full" @appear="appear(index)" @disappear="disappear(index)" > - <div v-safe-html="data.text" class="gl-mr-4"></div> + <div + v-safe-html="data.text" + class="gl-mr-4 gl-display-flex gl-align-items-center" + ></div> <div v-if="data.link"> <gl-link :href="data.link.href">{{ data.link.text }}</gl-link> </div> <gl-badge v-if="data.badge" :variant="data.badge.variant || 'info'"> {{ data.badge.text }} </gl-badge> + <actions + :widget="$options.label || $options.name" + :tertiary-buttons="data.actions" + class="gl-ml-auto" + /> </gl-intersection-observer> </li> </smart-virtual-list> diff --git a/app/assets/javascripts/vue_merge_request_widget/extensions/issues.js b/app/assets/javascripts/vue_merge_request_widget/extensions/issues.js index 57117b6ae1f..3298c09fe24 100644 --- a/app/assets/javascripts/vue_merge_request_widget/extensions/issues.js +++ b/app/assets/javascripts/vue_merge_request_widget/extensions/issues.js @@ -67,6 +67,7 @@ export default { // href: 'https://google.com', // Required: href for the link // text: 'Link text', // Required: Text to be used inside the link // }, + actions: [{ text: 'Full report', href: 'https://gitlab.com', target: '_blank' }], })); }); }, diff --git a/app/assets/javascripts/vue_shared/components/filtered_search_bar/constants.js b/app/assets/javascripts/vue_shared/components/filtered_search_bar/constants.js index 1df65d0a666..d9290e86bca 100644 --- a/app/assets/javascripts/vue_shared/components/filtered_search_bar/constants.js +++ b/app/assets/javascripts/vue_shared/components/filtered_search_bar/constants.js @@ -53,6 +53,7 @@ export const TOKEN_TITLE_ASSIGNEE = __('Assignee'); export const TOKEN_TITLE_MILESTONE = __('Milestone'); export const TOKEN_TITLE_LABEL = __('Label'); export const TOKEN_TITLE_TYPE = __('Type'); +export const TOKEN_TITLE_RELEASE = __('Release'); export const TOKEN_TITLE_MY_REACTION = __('My-Reaction'); export const TOKEN_TITLE_CONFIDENTIAL = __('Confidential'); export const TOKEN_TITLE_ITERATION = __('Iteration'); diff --git a/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/release_token.vue b/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/release_token.vue new file mode 100644 index 00000000000..f353cc3a765 --- /dev/null +++ b/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/release_token.vue @@ -0,0 +1,85 @@ +<script> +import { GlFilteredSearchSuggestion } from '@gitlab/ui'; +import createFlash from '~/flash'; +import { __ } from '~/locale'; +import BaseToken from '~/vue_shared/components/filtered_search_bar/tokens/base_token.vue'; +import { DEFAULT_NONE_ANY } from '../constants'; + +export default { + components: { + BaseToken, + GlFilteredSearchSuggestion, + }, + props: { + active: { + type: Boolean, + required: true, + }, + config: { + type: Object, + required: true, + }, + value: { + type: Object, + required: true, + }, + }, + data() { + return { + releases: this.config.initialReleases || [], + loading: false, + }; + }, + computed: { + defaultReleases() { + return this.config.defaultReleases || DEFAULT_NONE_ANY; + }, + }, + methods: { + getActiveRelease(releases, data) { + return releases.find((release) => release.tag.toLowerCase() === data.toLowerCase()); + }, + fetchReleases(searchTerm) { + this.loading = true; + this.config + .fetchReleases(searchTerm) + .then((response) => { + this.releases = response; + }) + .catch(() => { + createFlash({ message: __('There was a problem fetching releases.') }); + }) + .finally(() => { + this.loading = false; + }); + }, + }, +}; +</script> + +<template> + <base-token + :active="active" + :config="config" + :value="value" + :default-suggestions="defaultReleases" + :suggestions="releases" + :suggestions-loading="loading" + :get-active-token-value="getActiveRelease" + @fetch-suggestions="fetchReleases" + v-on="$listeners" + > + <template #view="{ viewTokenProps: { inputValue, activeTokenValue } }"> + {{ activeTokenValue ? activeTokenValue.tag : inputValue }} + </template> + <template #suggestions-list="{ suggestions }"> + <gl-filtered-search-suggestion + v-for="release in suggestions" + :key="release.id" + :value="release.tag" + > + {{ release.tag }} + </gl-filtered-search-suggestion> + </template> + </base-token> +</template> diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb index fd508d5f127..6c096b5715a 100644 --- a/app/controllers/projects/issues_controller.rb +++ b/app/controllers/projects/issues_controller.rb @@ -48,9 +48,7 @@ class Projects::IssuesController < Projects::ApplicationController end before_action only: :show do - real_time_enabled = Gitlab::ActionCable::Config.in_app? || Feature.enabled?(:real_time_issue_sidebar, @project) - - push_to_gon_attributes(:features, :real_time_issue_sidebar, real_time_enabled) + push_frontend_feature_flag(:real_time_issue_sidebar, @project, default_enabled: :yaml) push_frontend_feature_flag(:confidential_notes, @project, default_enabled: :yaml) push_frontend_feature_flag(:issue_assignees_widget, @project, default_enabled: :yaml) push_frontend_feature_flag(:labels_widget, @project, default_enabled: :yaml) diff --git a/app/controllers/projects/wikis_controller.rb b/app/controllers/projects/wikis_controller.rb index 9ee8847004e..158a8cc262d 100644 --- a/app/controllers/projects/wikis_controller.rb +++ b/app/controllers/projects/wikis_controller.rb @@ -3,6 +3,8 @@ class Projects::WikisController < Projects::ApplicationController include WikiActions + urgency :low + alias_method :container, :project before_action do diff --git a/app/helpers/issues_helper.rb b/app/helpers/issues_helper.rb index 49f7d9aeef1..e3a5ddcda93 100644 --- a/app/helpers/issues_helper.rb +++ b/app/helpers/issues_helper.rb @@ -233,6 +233,7 @@ module IssuesHelper new_issue_path: new_project_issue_path(project, issue: { milestone_id: finder.milestones.first.try(:id) }), project_import_jira_path: project_import_jira_path(project), quick_actions_help_path: help_page_path('user/project/quick_actions'), + releases_path: project_releases_path(project, format: :json), reset_path: new_issuable_address_project_path(project, issuable_type: 'issue'), show_new_issue_link: show_new_issue_link?(project).to_s ) diff --git a/app/models/integrations/base_chat_notification.rb b/app/models/integrations/base_chat_notification.rb index c6335782b5e..ca72de47d30 100644 --- a/app/models/integrations/base_chat_notification.rb +++ b/app/models/integrations/base_chat_notification.rb @@ -73,7 +73,12 @@ module Integrations { type: 'text', name: 'webhook', placeholder: "#{webhook_placeholder}", required: true }.freeze, { type: 'text', name: 'username', placeholder: 'GitLab-integration' }.freeze, { type: 'checkbox', name: 'notify_only_broken_pipelines', help: 'Do not send notifications for successful pipelines.' }.freeze, - { type: 'select', name: 'branches_to_be_notified', choices: branch_choices }.freeze, + { + type: 'select', + name: 'branches_to_be_notified', + title: s_('Integrations|Branches for which notifications are to be sent'), + choices: branch_choices + }.freeze, { type: 'text', name: 'labels_to_be_notified', diff --git a/app/models/integrations/discord.rb b/app/models/integrations/discord.rb index 76160a61bc3..21993dd3c43 100644 --- a/app/models/integrations/discord.rb +++ b/app/models/integrations/discord.rb @@ -41,7 +41,12 @@ module Integrations [ { type: "text", name: "webhook", placeholder: "https://discordapp.com/api/webhooks/…", help: "URL to the webhook for the Discord channel." }, { type: "checkbox", name: "notify_only_broken_pipelines" }, - { type: 'select', name: 'branches_to_be_notified', choices: branch_choices } + { + type: 'select', + name: 'branches_to_be_notified', + title: s_('Integrations|Branches for which notifications are to be sent'), + choices: branch_choices + } ] end diff --git a/app/models/integrations/emails_on_push.rb b/app/models/integrations/emails_on_push.rb index e277633664f..26c47d53918 100644 --- a/app/models/integrations/emails_on_push.rb +++ b/app/models/integrations/emails_on_push.rb @@ -76,7 +76,12 @@ module Integrations help: s_("EmailsOnPushService|Send notifications from the committer's email address if the domain matches the domain used by your GitLab instance (such as %{domains}).") % { domains: domains } }, { type: 'checkbox', name: 'disable_diffs', title: s_("EmailsOnPushService|Disable code diffs"), help: s_("EmailsOnPushService|Don't include possibly sensitive code diffs in notification body.") }, - { type: 'select', name: 'branches_to_be_notified', choices: branch_choices }, + { + type: 'select', + name: 'branches_to_be_notified', + title: s_('Integrations|Branches for which notifications are to be sent'), + choices: branch_choices + }, { type: 'textarea', name: 'recipients', diff --git a/app/models/integrations/hangouts_chat.rb b/app/models/integrations/hangouts_chat.rb index d02cfe4ec56..0d6b9fb1019 100644 --- a/app/models/integrations/hangouts_chat.rb +++ b/app/models/integrations/hangouts_chat.rb @@ -40,7 +40,12 @@ module Integrations [ { type: 'text', name: 'webhook', placeholder: "#{webhook_placeholder}" }, { type: 'checkbox', name: 'notify_only_broken_pipelines' }, - { type: 'select', name: 'branches_to_be_notified', choices: branch_choices } + { + type: 'select', + name: 'branches_to_be_notified', + title: s_('Integrations|Branches for which notifications are to be sent'), + choices: branch_choices + } ] end diff --git a/app/models/integrations/microsoft_teams.rb b/app/models/integrations/microsoft_teams.rb index 5aad25e8ddc..71cd4ddaf82 100644 --- a/app/models/integrations/microsoft_teams.rb +++ b/app/models/integrations/microsoft_teams.rb @@ -37,7 +37,12 @@ module Integrations [ { type: 'text', name: 'webhook', placeholder: "#{webhook_placeholder}" }, { type: 'checkbox', name: 'notify_only_broken_pipelines', help: 'If selected, successful pipelines do not trigger a notification event.' }, - { type: 'select', name: 'branches_to_be_notified', choices: branch_choices } + { + type: 'select', + name: 'branches_to_be_notified', + title: s_('Integrations|Branches for which notifications are to be sent'), + choices: branch_choices + } ] end diff --git a/app/models/integrations/pipelines_email.rb b/app/models/integrations/pipelines_email.rb index efba35cc2a8..e5d94bab2db 100644 --- a/app/models/integrations/pipelines_email.rb +++ b/app/models/integrations/pipelines_email.rb @@ -71,6 +71,7 @@ module Integrations name: 'notify_only_broken_pipelines' }, { type: 'select', name: 'branches_to_be_notified', + title: s_('Integrations|Branches for which notifications are to be sent'), choices: branch_choices } ] end diff --git a/app/models/integrations/unify_circuit.rb b/app/models/integrations/unify_circuit.rb index e3e180ae959..f085423d229 100644 --- a/app/models/integrations/unify_circuit.rb +++ b/app/models/integrations/unify_circuit.rb @@ -34,7 +34,12 @@ module Integrations [ { type: 'text', name: 'webhook', placeholder: "https://yourcircuit.com/rest/v2/webhooks/incoming/…", required: true }, { type: 'checkbox', name: 'notify_only_broken_pipelines' }, - { type: 'select', name: 'branches_to_be_notified', choices: branch_choices } + { + type: 'select', + name: 'branches_to_be_notified', + title: s_('Integrations|Branches for which notifications are to be sent'), + choices: branch_choices + } ] end diff --git a/app/models/integrations/webex_teams.rb b/app/models/integrations/webex_teams.rb index 6fd82a32035..7660eda6f83 100644 --- a/app/models/integrations/webex_teams.rb +++ b/app/models/integrations/webex_teams.rb @@ -36,7 +36,12 @@ module Integrations [ { type: 'text', name: 'webhook', placeholder: "https://api.ciscospark.com/v1/webhooks/incoming/...", required: true }, { type: 'checkbox', name: 'notify_only_broken_pipelines' }, - { type: 'select', name: 'branches_to_be_notified', choices: branch_choices } + { + type: 'select', + name: 'branches_to_be_notified', + title: s_('Integrations|Branches for which notifications are to be sent'), + choices: branch_choices + } ] end diff --git a/app/services/issues/update_service.rb b/app/services/issues/update_service.rb index d120b007af2..824a609dfb9 100644 --- a/app/services/issues/update_service.rb +++ b/app/services/issues/update_service.rb @@ -80,7 +80,7 @@ module Issues todo_service.reassigned_assignable(issue, current_user, old_assignees) track_incident_action(current_user, issue, :incident_assigned) - if Gitlab::ActionCable::Config.in_app? || Feature.enabled?(:broadcast_issue_updates, issue.project) + if Feature.enabled?(:broadcast_issue_updates, issue.project, default_enabled: :yaml) GraphqlTriggers.issuable_assignees_updated(issue) end end diff --git a/config/environments/test.rb b/config/environments/test.rb index 07e0c729d8c..2935e0c7636 100644 --- a/config/environments/test.rb +++ b/config/environments/test.rb @@ -59,8 +59,4 @@ Rails.application.configure do config.logger = ActiveSupport::TaggedLogging.new(Logger.new(nil)) config.log_level = :fatal end - - # Mount the ActionCable Engine in-app so that we don't have to spawn another Puma - # process for feature specs - ENV['ACTION_CABLE_IN_APP'] = 'true' end diff --git a/config/feature_flags/development/broadcast_issue_updates.yml b/config/feature_flags/development/broadcast_issue_updates.yml index b7c6ccf2353..c38263528a9 100644 --- a/config/feature_flags/development/broadcast_issue_updates.yml +++ b/config/feature_flags/development/broadcast_issue_updates.yml @@ -1,8 +1,8 @@ --- name: broadcast_issue_updates introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/30732 -rollout_issue_url: https://gitlab.com/gitlab-com/gl-infra/delivery/-/issues/1210 +rollout_issue_url: https://gitlab.com/gitlab-com/gl-infra/production/-/issues/3413 milestone: '13.0' type: development group: group::project management -default_enabled: false +default_enabled: true diff --git a/config/feature_flags/development/real_time_issue_sidebar.yml b/config/feature_flags/development/real_time_issue_sidebar.yml index 4fe8e19213a..12d3da86cad 100644 --- a/config/feature_flags/development/real_time_issue_sidebar.yml +++ b/config/feature_flags/development/real_time_issue_sidebar.yml @@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-com/gl-infra/delivery/-/issues/1210 milestone: '13.0' type: development group: group::project management -default_enabled: false +default_enabled: true diff --git a/config/initializers/7_prometheus_metrics.rb b/config/initializers/7_prometheus_metrics.rb index 587d393fd77..28f3da9b3df 100644 --- a/config/initializers/7_prometheus_metrics.rb +++ b/config/initializers/7_prometheus_metrics.rb @@ -65,7 +65,7 @@ if !Rails.env.test? && Gitlab::Metrics.prometheus_metrics_enabled? Gitlab::Metrics::Samplers::DatabaseSampler.initialize_instance.start Gitlab::Metrics::Samplers::ThreadsSampler.initialize_instance.start - if Gitlab::Runtime.action_cable? + if Gitlab::Runtime.web_server? Gitlab::Metrics::Samplers::ActionCableSampler.instance.start end diff --git a/config/initializers/action_cable.rb b/config/initializers/action_cable.rb index a7ef5cc332c..fc40d0b69c5 100644 --- a/config/initializers/action_cable.rb +++ b/config/initializers/action_cable.rb @@ -3,8 +3,7 @@ require 'action_cable/subscription_adapter/redis' Rails.application.configure do - # Mount the ActionCable engine when in-app mode is enabled - config.action_cable.mount_path = Gitlab::ActionCable::Config.in_app? ? '/-/cable' : nil + config.action_cable.mount_path = '/-/cable' config.action_cable.url = Gitlab::Utils.append_path(Gitlab.config.gitlab.relative_url_root, '/-/cable') config.action_cable.worker_pool_size = Gitlab::ActionCable::Config.worker_pool_size diff --git a/danger/metadata/Dangerfile b/danger/metadata/Dangerfile index 27dda687f6a..537f55ed649 100644 --- a/danger/metadata/Dangerfile +++ b/danger/metadata/Dangerfile @@ -4,24 +4,21 @@ DEFAULT_BRANCH = 'master' -THROUGHPUT_LABELS = [ - 'Community contribution', - 'security', - 'bug', - 'feature', +TYPE_LABELS = [ + 'type::feature', 'feature::addition', - 'feature::maintenance', - 'tooling', + 'type::maintenance', + 'type::tooling', 'tooling::pipelines', 'tooling::workflow', - 'documentation' + 'type::bug' ].freeze if gitlab.mr_body.size < 5 fail "Please provide a proper merge request description." end -if (THROUGHPUT_LABELS & gitlab.mr_labels).empty? +if (TYPE_LABELS & gitlab.mr_labels).empty? warn 'Please add a [merge request type](https://about.gitlab.com/handbook/engineering/metrics/#work-type-classification) to this merge request.' end diff --git a/danger/specialization_labels/Dangerfile b/danger/specialization_labels/Dangerfile index e86d66f5fbc..e42e17762e4 100644 --- a/danger/specialization_labels/Dangerfile +++ b/danger/specialization_labels/Dangerfile @@ -8,7 +8,7 @@ SPECIALIZATIONS = { frontend: 'frontend', docs: 'documentation', qa: 'QA', - tooling: 'tooling', + tooling: 'type::tooling', ci_template: 'ci::templates', feature_flag: 'feature flag' }.freeze diff --git a/danger/specs/Dangerfile b/danger/specs/Dangerfile index 117eaf61062..067fb62807a 100644 --- a/danger/specs/Dangerfile +++ b/danger/specs/Dangerfile @@ -1,7 +1,7 @@ # frozen_string_literal: true NO_SPECS_LABELS = [ - 'tooling', + 'type::tooling', 'tooling::pipelines', 'tooling::workflow', 'documentation', diff --git a/doc/architecture/blueprints/feature_flags_development/index.md b/doc/architecture/blueprints/feature_flags_development/index.md index a5e46d25921..94d52ea3474 100644 --- a/doc/architecture/blueprints/feature_flags_development/index.md +++ b/doc/architecture/blueprints/feature_flags_development/index.md @@ -29,7 +29,7 @@ The extensive usage of feature flags poses a few challenges We sometimes forget how our feature flags are configured or why we haven't yet removed the feature flag. - The usage of feature flags can also be confusing to people outside of - development that might not fully understand dependence of ~feature or ~bug + development that might not fully understand dependence of ~"type::feature" or ~"type::bug" fix on feature flag and how this feature flag is configured. Or if the feature should be announced as part of release post. - Maintaining feature flags poses additional challenge of having to manage diff --git a/doc/development/application_slis/rails_request_apdex.md b/doc/development/application_slis/rails_request_apdex.md index 4fd6901f574..2bb94b8406e 100644 --- a/doc/development/application_slis/rails_request_apdex.md +++ b/doc/development/application_slis/rails_request_apdex.md @@ -78,7 +78,7 @@ duration is capped at 5 seconds. ## Decreasing the urgency (setting a higher target duration) -Increasing the urgency on an existing endpoint can be done on +Decreasing the urgency on an existing endpoint can be done on a case-by-case basis. Please take the following into account: 1. Apdex is about perceived performance, if a user is actively waiting @@ -137,7 +137,7 @@ the decision. ## Increasing urgency (setting a lower target duration) -When decreasing the target duration, we need to make sure the endpoint +When increasing the urgency, we need to make sure the endpoint still meets SLO for the fleet that handles the request. You can use the information in the logs to determine this: diff --git a/doc/development/contributing/issue_workflow.md b/doc/development/contributing/issue_workflow.md index d0f107ba98a..5f3d9879110 100644 --- a/doc/development/contributing/issue_workflow.md +++ b/doc/development/contributing/issue_workflow.md @@ -45,7 +45,7 @@ scheduling into milestones. Labeling is a task for everyone. (For some projects, Most issues will have labels for at least one of the following: -- Type. For example: `~feature`, `~bug`, `~tooling`, or `~documentation`. +- Type. For example: `~"type::feature"`, `~"type::bug"`, or `~"type::tooling"`. - Stage. For example: `~"devops::plan"` or `~"devops::create"`. - Group. For example: `~"group::source code"`, `~"group::knowledge"`, or `~"group::editor"`. - Category. For example: `~"Category:Code Analytics"`, `~"Category:DevOps Reports"`, or `~"Category:Templates"`. @@ -70,12 +70,12 @@ issue should have one and only one. The current type labels are: -- `~feature` +- `~"type::feature"` - `~"feature::addition"` - `~"feature::enhancement"` - - `~"feature::maintenance"` -- `~bug` -- `~tooling` +- `~"type::maintenance"` +- `~"type::bug"` +- `~"type::tooling"` - `~"tooling::pipelines"` - `~"tooling::workflow"` - `~"support request"` @@ -342,11 +342,11 @@ To create a feature proposal, open an issue on the [issue tracker](https://gitlab.com/gitlab-org/gitlab/-/issues). In order to help track the feature proposals, we have created a -[`feature`](https://gitlab.com/gitlab-org/gitlab/-/issues?label_name=feature) label. +[`~"type::feature"`](https://gitlab.com/gitlab-org/gitlab/-/issues?label_name=type::feature) label. For the time being, users that are not members of the project cannot add labels. You can instead ask one of the [core team](https://about.gitlab.com/community/core-team/) -members to add the label `~feature` to the issue or add the following -code snippet right after your description in a new line: `~feature`. +members to add the label `~"type::feature"` to the issue or add the following +code snippet right after your description in a new line: `~"type::feature"`. Please keep feature proposals as small and simple as possible, complex ones might be edited to make them small and simple. diff --git a/doc/development/documentation/workflow.md b/doc/development/documentation/workflow.md index 90c1137e5c5..782cd3411b1 100644 --- a/doc/development/documentation/workflow.md +++ b/doc/development/documentation/workflow.md @@ -39,7 +39,7 @@ The following are also added by members of the Technical Writing team: - The `~Technical Writing` [team label](../contributing/issue_workflow.md#team-labels). Documentation changes that are not associated with the release of a new or updated feature -do not take the `~feature` label, but still need the `~documentation` label. +do not take the `~"type::feature"` label, but still need the `~documentation` label. They may include: diff --git a/doc/development/testing_guide/review_apps.md b/doc/development/testing_guide/review_apps.md index 4091f213a8f..8c66ef7f7e1 100644 --- a/doc/development/testing_guide/review_apps.md +++ b/doc/development/testing_guide/review_apps.md @@ -180,7 +180,7 @@ subgraph "CNG-mirror pipeline" **Additional notes:** - If the `review-deploy` job keeps failing (and a manual retry didn't help), - please post a message in the `#g_qe_engineering_productivity` channel and/or create a `~"Engineering Productivity"` `~"ep::review apps"` `~bug` + please post a message in the `#g_qe_engineering_productivity` channel and/or create a `~"Engineering Productivity"` `~"ep::review apps"` `~"type::bug"` issue with a link to your merge request. Note that the deployment failure can reveal an actual problem introduced in your merge request (i.e. this isn't necessarily a transient failure)! @@ -189,7 +189,7 @@ subgraph "CNG-mirror pipeline" your merge request. You can also download the artifacts to see screenshots of the page at the time the failures occurred. If you don't find the cause of the failure or if it seems unrelated to your change, please post a message in the - `#quality` channel and/or create a ~Quality ~bug issue with a link to your + `#quality` channel and/or create a ~Quality ~"type::bug" issue with a link to your merge request. - The manual `review-stop` can be used to stop a Review App manually, and is also started by GitLab once a merge diff --git a/doc/user/project/integrations/mattermost.md b/doc/user/project/integrations/mattermost.md index 92e5feefb73..119f219499c 100644 --- a/doc/user/project/integrations/mattermost.md +++ b/doc/user/project/integrations/mattermost.md @@ -54,7 +54,7 @@ Then fill in the integration configuration: To change the bot's username, provide a value. - **Notify only broken pipelines**: If you enable the **Pipeline** event, and you want notifications about failed pipelines only. -- **Branches to be notified**: The branches to send notifications for. +- **Branches for which notifications are to be sent**: The branches to send notifications for. - **Labels to be notified**: (Optional) Labels required for the issue or merge request to trigger a notification. Leave blank to notify for all issues and merge requests. - **Labels to be notified behavior**: When you use the **Labels to be notified** filter, diff --git a/doc/user/project/integrations/slack.md b/doc/user/project/integrations/slack.md index a38d2157699..d399c7f2901 100644 --- a/doc/user/project/integrations/slack.md +++ b/doc/user/project/integrations/slack.md @@ -45,7 +45,7 @@ to control GitLab from Slack. Slash commands are configured separately. 1. (Optional) In **Username**, enter the username of the Slack bot that sends the notifications. 1. Select the **Notify only broken pipelines** checkbox to notify only on failures. -1. In the **Branches to be notified** dropdown, select which types of branches +1. In the **Branches for which notifications are to be sent** dropdown, select which types of branches to send notifications for. 1. Leave the **Labels to be notified** field blank to get all notifications, or add labels that the issue or merge request must have to trigger a diff --git a/doc/user/project/integrations/unify_circuit.md b/doc/user/project/integrations/unify_circuit.md index 814c64d8140..daab24a8ab9 100644 --- a/doc/user/project/integrations/unify_circuit.md +++ b/doc/user/project/integrations/unify_circuit.md @@ -21,7 +21,7 @@ In GitLab: 1. Select the checkboxes corresponding to the GitLab events you want to receive in Unify Circuit. 1. Paste the **Webhook URL** that you copied from the Unify Circuit configuration step. 1. Select the **Notify only broken pipelines** checkbox to notify only on failures. -1. In the **Branches to be notified** dropdown, select which types of branches to send notifications for. +1. In the **Branches for which notifications are to be sent** dropdown, select which types of branches to send notifications for. 1. Select `Save changes` or optionally select **Test settings**. Your Unify Circuit conversation now starts receiving GitLab event notifications. diff --git a/doc/user/project/issues/managing_issues.md b/doc/user/project/issues/managing_issues.md index a2fa044be6b..6b3d5d6563a 100644 --- a/doc/user/project/issues/managing_issues.md +++ b/doc/user/project/issues/managing_issues.md @@ -380,12 +380,18 @@ You can also use the `/iteration` [quick action](../quick_actions.md#issues-merge-requests-and-epics) in a comment or description field. -## Real-time sidebar **(FREE SELF)** +## Real-time sidebar -> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/17589) in GitLab 13.3. +> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/17589) in GitLab 13.3. Disabled by default. +> - [Enabled on GitLab.com](https://gitlab.com/gitlab-com/gl-infra/production/-/issues/3413) in GitLab 13.9. +> - [Enabled on self-managed](https://gitlab.com/gitlab-org/gitlab/-/issues/17589) in GitLab 14.5. -Assignees in the sidebar are updated in real time. This feature is **disabled by default**. -To enable it, you need to enable [Action Cable in-app mode](https://docs.gitlab.com/omnibus/settings/actioncable.html). +FLAG: +On self-managed GitLab, by default this feature is available. To hide the feature per project or for your entire instance, ask an administrator to +[disable the feature flags](../../../administration/feature_flags.md) named `real_time_issue_sidebar` and `broadcast_issue_updates`. +On GitLab.com, this feature is available. + +Assignees in the sidebar are updated in real time. ## Similar issues diff --git a/lib/api/snippets.rb b/lib/api/snippets.rb index f1ec1024492..c4b17a62b59 100644 --- a/lib/api/snippets.rb +++ b/lib/api/snippets.rb @@ -43,7 +43,7 @@ module API params do use :pagination end - get 'public' do + get 'public', urgency: :low do authenticate! present paginate(public_snippets), with: Entities::PersonalSnippet, current_user: current_user diff --git a/lib/api/wikis.rb b/lib/api/wikis.rb index 8441aeb10ab..fdce3c5ce18 100644 --- a/lib/api/wikis.rb +++ b/lib/api/wikis.rb @@ -32,7 +32,7 @@ module API params do optional :with_content, type: Boolean, default: false, desc: "Include pages' content" end - get ':id/wikis' do + get ':id/wikis', urgency: :low do authorize! :read_wiki, container entity = params[:with_content] ? Entities::WikiPage : Entities::WikiPageBasic diff --git a/lib/gitlab/action_cable/config.rb b/lib/gitlab/action_cable/config.rb index 38e870353eb..77d4ec0733d 100644 --- a/lib/gitlab/action_cable/config.rb +++ b/lib/gitlab/action_cable/config.rb @@ -4,10 +4,6 @@ module Gitlab module ActionCable class Config class << self - def in_app? - Gitlab::Utils.to_boolean(ENV.fetch('ACTION_CABLE_IN_APP', false)) - end - def worker_pool_size ENV.fetch('ACTION_CABLE_WORKER_POOL_SIZE', 4).to_i end diff --git a/lib/gitlab/metrics/samplers/action_cable_sampler.rb b/lib/gitlab/metrics/samplers/action_cable_sampler.rb index 043d2ae84cc..adce3030d0d 100644 --- a/lib/gitlab/metrics/samplers/action_cable_sampler.rb +++ b/lib/gitlab/metrics/samplers/action_cable_sampler.rb @@ -39,23 +39,14 @@ module Gitlab def sample pool = @action_cable.worker_pool.executor - labels = { - server_mode: server_mode - } - - metrics[:active_connections].set(labels, @action_cable.connections.size) - metrics[:pool_min_size].set(labels, pool.min_length) - metrics[:pool_max_size].set(labels, pool.max_length) - metrics[:pool_current_size].set(labels, pool.length) - metrics[:pool_largest_size].set(labels, pool.largest_length) - metrics[:pool_completed_tasks].set(labels, pool.completed_task_count) - metrics[:pool_pending_tasks].set(labels, pool.queue_length) - end - - private - def server_mode - Gitlab::ActionCable::Config.in_app? ? 'in-app' : 'standalone' + metrics[:active_connections].set({}, @action_cable.connections.size) + metrics[:pool_min_size].set({}, pool.min_length) + metrics[:pool_max_size].set({}, pool.max_length) + metrics[:pool_current_size].set({}, pool.length) + metrics[:pool_largest_size].set({}, pool.largest_length) + metrics[:pool_completed_tasks].set({}, pool.completed_task_count) + metrics[:pool_pending_tasks].set({}, pool.queue_length) end end end diff --git a/lib/gitlab/runtime.rb b/lib/gitlab/runtime.rb index 56b64b748d4..5fbbfd90be1 100644 --- a/lib/gitlab/runtime.rb +++ b/lib/gitlab/runtime.rb @@ -63,12 +63,8 @@ module Gitlab puma? end - def action_cable? - web_server? && (!!defined?(ACTION_CABLE_SERVER) || Gitlab::ActionCable::Config.in_app?) - end - def multi_threaded? - puma? || sidekiq? || action_cable? + puma? || sidekiq? end def puma_in_clustered_mode? @@ -92,7 +88,7 @@ module Gitlab threads += Sidekiq.options[:concurrency] + 2 end - if action_cable? + if web_server? threads += Gitlab::ActionCable::Config.worker_pool_size end diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 4a43f510e5e..969b585b2fb 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -18350,6 +18350,9 @@ msgstr "" msgid "Integrations|An error occurred while loading projects using custom settings." msgstr "" +msgid "Integrations|Branches for which notifications are to be sent" +msgstr "" + msgid "Integrations|Browser limitations" msgstr "" @@ -34507,6 +34510,9 @@ msgstr "" msgid "There was a problem fetching recent projects." msgstr "" +msgid "There was a problem fetching releases." +msgstr "" + msgid "There was a problem fetching the job token scope value" msgstr "" diff --git a/spec/controllers/projects/issues_controller_spec.rb b/spec/controllers/projects/issues_controller_spec.rb index 0b3bd4d78ac..382ce0feda0 100644 --- a/spec/controllers/projects/issues_controller_spec.rb +++ b/spec/controllers/projects/issues_controller_spec.rb @@ -1084,28 +1084,30 @@ RSpec.describe Projects::IssuesController do end context 'real-time sidebar feature flag' do - using RSpec::Parameterized::TableSyntax - let_it_be(:project) { create(:project, :public) } let_it_be(:issue) { create(:issue, project: project) } - where(:action_cable_in_app_enabled, :feature_flag_enabled, :gon_feature_flag) do - true | true | true - true | false | true - false | true | true - false | false | false + context 'when enabled' do + before do + stub_feature_flags(real_time_issue_sidebar: true) + end + + it 'pushes the correct value to the frontend' do + go(id: issue.to_param) + + expect(Gon.features).to include('realTimeIssueSidebar' => true) + end end - with_them do + context 'when disabled' do before do - expect(Gitlab::ActionCable::Config).to receive(:in_app?).and_return(action_cable_in_app_enabled) - stub_feature_flags(real_time_issue_sidebar: feature_flag_enabled) + stub_feature_flags(real_time_issue_sidebar: false) end - it 'broadcasts to the issues channel based on ActionCable and feature flag values' do + it 'pushes the correct value to the frontend' do go(id: issue.to_param) - expect(Gon.features).to include('realTimeIssueSidebar' => gon_feature_flag) + expect(Gon.features).to include('realTimeIssueSidebar' => false) end end end diff --git a/spec/frontend/issues_list/components/issues_list_app_spec.js b/spec/frontend/issues_list/components/issues_list_app_spec.js index 6b443062f12..3f52c7b4afe 100644 --- a/spec/frontend/issues_list/components/issues_list_app_spec.js +++ b/spec/frontend/issues_list/components/issues_list_app_spec.js @@ -37,6 +37,7 @@ import { TOKEN_TYPE_LABEL, TOKEN_TYPE_MILESTONE, TOKEN_TYPE_MY_REACTION, + TOKEN_TYPE_RELEASE, TOKEN_TYPE_TYPE, TOKEN_TYPE_WEIGHT, urlSortParams, @@ -581,6 +582,7 @@ describe('IssuesListApp component', () => { { type: TOKEN_TYPE_MILESTONE }, { type: TOKEN_TYPE_LABEL }, { type: TOKEN_TYPE_TYPE }, + { type: TOKEN_TYPE_RELEASE }, { type: TOKEN_TYPE_MY_REACTION }, { type: TOKEN_TYPE_CONFIDENTIAL }, { type: TOKEN_TYPE_ITERATION }, diff --git a/spec/frontend/issues_list/mock_data.js b/spec/frontend/issues_list/mock_data.js index c62050c3563..25664cc2842 100644 --- a/spec/frontend/issues_list/mock_data.js +++ b/spec/frontend/issues_list/mock_data.js @@ -95,12 +95,18 @@ export const locationSearch = [ 'assignee_username[]=lisa', 'not[assignee_username][]=patty', 'not[assignee_username][]=selma', + 'milestone_title=season+3', 'milestone_title=season+4', 'not[milestone_title]=season+20', + 'not[milestone_title]=season+30', 'label_name[]=cartoon', 'label_name[]=tv', 'not[label_name][]=live action', 'not[label_name][]=drama', + 'release_tag=v3', + 'release_tag=v4', + 'not[release_tag]=v20', + 'not[release_tag]=v30', 'type[]=issue', 'type[]=feature', 'not[type][]=bug', @@ -109,7 +115,9 @@ export const locationSearch = [ 'not[my_reaction_emoji]=thumbsdown', 'confidential=yes', 'iteration_id=4', + 'iteration_id=12', 'not[iteration_id]=20', + 'not[iteration_id]=42', 'epic_id=12', 'not[epic_id]=34', 'weight=1', @@ -122,6 +130,7 @@ export const locationSearchWithSpecialValues = [ 'my_reaction_emoji=None', 'iteration_id=Current', 'label_name[]=None', + 'release_tag=None', 'milestone_title=Upcoming', 'epic_id=None', 'weight=None', @@ -134,12 +143,18 @@ export const filteredTokens = [ { type: 'assignee_username', value: { data: 'lisa', operator: OPERATOR_IS } }, { type: 'assignee_username', value: { data: 'patty', operator: OPERATOR_IS_NOT } }, { type: 'assignee_username', value: { data: 'selma', operator: OPERATOR_IS_NOT } }, + { type: 'milestone', value: { data: 'season 3', operator: OPERATOR_IS } }, { type: 'milestone', value: { data: 'season 4', operator: OPERATOR_IS } }, { type: 'milestone', value: { data: 'season 20', operator: OPERATOR_IS_NOT } }, + { type: 'milestone', value: { data: 'season 30', operator: OPERATOR_IS_NOT } }, { type: 'labels', value: { data: 'cartoon', operator: OPERATOR_IS } }, { type: 'labels', value: { data: 'tv', operator: OPERATOR_IS } }, { type: 'labels', value: { data: 'live action', operator: OPERATOR_IS_NOT } }, { type: 'labels', value: { data: 'drama', operator: OPERATOR_IS_NOT } }, + { type: 'release', value: { data: 'v3', operator: OPERATOR_IS } }, + { type: 'release', value: { data: 'v4', operator: OPERATOR_IS } }, + { type: 'release', value: { data: 'v20', operator: OPERATOR_IS_NOT } }, + { type: 'release', value: { data: 'v30', operator: OPERATOR_IS_NOT } }, { type: 'type', value: { data: 'issue', operator: OPERATOR_IS } }, { type: 'type', value: { data: 'feature', operator: OPERATOR_IS } }, { type: 'type', value: { data: 'bug', operator: OPERATOR_IS_NOT } }, @@ -148,7 +163,9 @@ export const filteredTokens = [ { type: 'my_reaction_emoji', value: { data: 'thumbsdown', operator: OPERATOR_IS_NOT } }, { type: 'confidential', value: { data: 'yes', operator: OPERATOR_IS } }, { type: 'iteration', value: { data: '4', operator: OPERATOR_IS } }, + { type: 'iteration', value: { data: '12', operator: OPERATOR_IS } }, { type: 'iteration', value: { data: '20', operator: OPERATOR_IS_NOT } }, + { type: 'iteration', value: { data: '42', operator: OPERATOR_IS_NOT } }, { type: 'epic_id', value: { data: '12', operator: OPERATOR_IS } }, { type: 'epic_id', value: { data: '34', operator: OPERATOR_IS_NOT } }, { type: 'weight', value: { data: '1', operator: OPERATOR_IS } }, @@ -163,6 +180,7 @@ export const filteredTokensWithSpecialValues = [ { type: 'my_reaction_emoji', value: { data: 'None', operator: OPERATOR_IS } }, { type: 'iteration', value: { data: 'Current', operator: OPERATOR_IS } }, { type: 'labels', value: { data: 'None', operator: OPERATOR_IS } }, + { type: 'release', value: { data: 'None', operator: OPERATOR_IS } }, { type: 'milestone', value: { data: 'Upcoming', operator: OPERATOR_IS } }, { type: 'epic_id', value: { data: 'None', operator: OPERATOR_IS } }, { type: 'weight', value: { data: 'None', operator: OPERATOR_IS } }, @@ -171,22 +189,24 @@ export const filteredTokensWithSpecialValues = [ export const apiParams = { authorUsername: 'homer', assigneeUsernames: ['bart', 'lisa'], - milestoneTitle: 'season 4', + milestoneTitle: ['season 3', 'season 4'], labelName: ['cartoon', 'tv'], + releaseTag: ['v3', 'v4'], types: ['ISSUE', 'FEATURE'], myReactionEmoji: 'thumbsup', confidential: true, - iterationId: '4', + iterationId: ['4', '12'], epicId: '12', weight: '1', not: { authorUsername: 'marge', assigneeUsernames: ['patty', 'selma'], - milestoneTitle: 'season 20', + milestoneTitle: ['season 20', 'season 30'], labelName: ['live action', 'drama'], + releaseTag: ['v20', 'v30'], types: ['BUG', 'INCIDENT'], myReactionEmoji: 'thumbsdown', - iterationId: '20', + iterationId: ['20', '42'], epicId: '34', weight: '3', }, @@ -197,6 +217,7 @@ export const apiParamsWithSpecialValues = { assigneeUsernames: 'bart', labelName: 'None', myReactionEmoji: 'None', + releaseTagWildcardId: 'NONE', iterationWildcardId: 'CURRENT', milestoneWildcardId: 'UPCOMING', epicId: 'None', @@ -208,17 +229,19 @@ export const urlParams = { 'not[author_username]': 'marge', 'assignee_username[]': ['bart', 'lisa'], 'not[assignee_username][]': ['patty', 'selma'], - milestone_title: 'season 4', - 'not[milestone_title]': 'season 20', + milestone_title: ['season 3', 'season 4'], + 'not[milestone_title]': ['season 20', 'season 30'], 'label_name[]': ['cartoon', 'tv'], 'not[label_name][]': ['live action', 'drama'], + release_tag: ['v3', 'v4'], + 'not[release_tag]': ['v20', 'v30'], 'type[]': ['issue', 'feature'], 'not[type][]': ['bug', 'incident'], my_reaction_emoji: 'thumbsup', 'not[my_reaction_emoji]': 'thumbsdown', confidential: 'yes', - iteration_id: '4', - 'not[iteration_id]': '20', + iteration_id: ['4', '12'], + 'not[iteration_id]': ['20', '42'], epic_id: '12', 'not[epic_id]': '34', weight: '1', @@ -229,6 +252,7 @@ export const urlParamsWithSpecialValues = { assignee_id: '123', 'assignee_username[]': 'bart', 'label_name[]': 'None', + release_tag: 'None', my_reaction_emoji: 'None', iteration_id: 'Current', milestone_title: 'Upcoming', diff --git a/spec/frontend/issues_list/utils_spec.js b/spec/frontend/issues_list/utils_spec.js index 458776d9ec5..8e1d70db92d 100644 --- a/spec/frontend/issues_list/utils_spec.js +++ b/spec/frontend/issues_list/utils_spec.js @@ -58,10 +58,10 @@ describe('getDueDateValue', () => { describe('getSortOptions', () => { describe.each` hasIssueWeightsFeature | hasBlockedIssuesFeature | length | containsWeight | containsBlocking - ${false} | ${false} | ${8} | ${false} | ${false} - ${true} | ${false} | ${9} | ${true} | ${false} - ${false} | ${true} | ${9} | ${false} | ${true} - ${true} | ${true} | ${10} | ${true} | ${true} + ${false} | ${false} | ${9} | ${false} | ${false} + ${true} | ${false} | ${10} | ${true} | ${false} + ${false} | ${true} | ${10} | ${false} | ${true} + ${true} | ${true} | ${11} | ${true} | ${true} `( 'when hasIssueWeightsFeature=$hasIssueWeightsFeature and hasBlockedIssuesFeature=$hasBlockedIssuesFeature', ({ diff --git a/spec/frontend/vue_mr_widget/mr_widget_options_spec.js b/spec/frontend/vue_mr_widget/mr_widget_options_spec.js index 19892539a8f..844e7330679 100644 --- a/spec/frontend/vue_mr_widget/mr_widget_options_spec.js +++ b/spec/frontend/vue_mr_widget/mr_widget_options_spec.js @@ -1,4 +1,4 @@ -import { GlBadge, GlLink, GlIcon, GlDropdown } from '@gitlab/ui'; +import { GlBadge, GlLink, GlIcon, GlButton, GlDropdown } from '@gitlab/ui'; import { mount } from '@vue/test-utils'; import MockAdapter from 'axios-mock-adapter'; import Vue, { nextTick } from 'vue'; @@ -947,6 +947,9 @@ describe('MrWidgetOptions', () => { // Renders a link in the row expect(collapsedSection.find(GlLink).exists()).toBe(true); expect(collapsedSection.find(GlLink).text()).toBe('GitLab.com'); + + expect(collapsedSection.find(GlButton).exists()).toBe(true); + expect(collapsedSection.find(GlButton).text()).toBe('Full report'); }); }); }); diff --git a/spec/frontend/vue_mr_widget/test_extension.js b/spec/frontend/vue_mr_widget/test_extension.js index 51bc44a7063..65c1bd8473b 100644 --- a/spec/frontend/vue_mr_widget/test_extension.js +++ b/spec/frontend/vue_mr_widget/test_extension.js @@ -31,6 +31,7 @@ export default { href: 'https://gitlab.com', text: 'GitLab.com', }, + actions: [{ text: 'Full report', href: 'https://gitlab.com', target: '_blank' }], }, ]); }, diff --git a/spec/frontend/vue_shared/components/filtered_search_bar/mock_data.js b/spec/frontend/vue_shared/components/filtered_search_bar/mock_data.js index c2462d90a63..74cf6985b99 100644 --- a/spec/frontend/vue_shared/components/filtered_search_bar/mock_data.js +++ b/spec/frontend/vue_shared/components/filtered_search_bar/mock_data.js @@ -9,6 +9,7 @@ import EpicToken from '~/vue_shared/components/filtered_search_bar/tokens/epic_t import IterationToken from '~/vue_shared/components/filtered_search_bar/tokens/iteration_token.vue'; import LabelToken from '~/vue_shared/components/filtered_search_bar/tokens/label_token.vue'; import MilestoneToken from '~/vue_shared/components/filtered_search_bar/tokens/milestone_token.vue'; +import ReleaseToken from '~/vue_shared/components/filtered_search_bar/tokens/release_token.vue'; import WeightToken from '~/vue_shared/components/filtered_search_bar/tokens/weight_token.vue'; export const mockAuthor1 = { @@ -132,6 +133,14 @@ export const mockMilestoneToken = { fetchMilestones: () => Promise.resolve({ data: mockMilestones }), }; +export const mockReleaseToken = { + type: 'release', + icon: 'rocket', + title: 'Release', + token: ReleaseToken, + fetchReleases: () => Promise.resolve(), +}; + export const mockEpicToken = { type: 'epic_iid', icon: 'clock', diff --git a/spec/frontend/vue_shared/components/filtered_search_bar/tokens/release_token_spec.js b/spec/frontend/vue_shared/components/filtered_search_bar/tokens/release_token_spec.js new file mode 100644 index 00000000000..b804ff97b82 --- /dev/null +++ b/spec/frontend/vue_shared/components/filtered_search_bar/tokens/release_token_spec.js @@ -0,0 +1,78 @@ +import { GlFilteredSearchToken, GlFilteredSearchTokenSegment } from '@gitlab/ui'; +import { mount } from '@vue/test-utils'; +import waitForPromises from 'helpers/wait_for_promises'; +import createFlash from '~/flash'; +import ReleaseToken from '~/vue_shared/components/filtered_search_bar/tokens/release_token.vue'; +import { mockReleaseToken } from '../mock_data'; + +jest.mock('~/flash'); + +describe('ReleaseToken', () => { + const id = 123; + let wrapper; + + const createComponent = ({ config = mockReleaseToken, value = { data: '' } } = {}) => + mount(ReleaseToken, { + propsData: { + active: false, + config, + value, + }, + provide: { + portalName: 'fake target', + alignSuggestions: function fakeAlignSuggestions() {}, + suggestionsListClass: () => 'custom-class', + }, + }); + + afterEach(() => { + wrapper.destroy(); + }); + + it('renders release value', async () => { + wrapper = createComponent({ value: { data: id } }); + await wrapper.vm.$nextTick(); + + const tokenSegments = wrapper.findAllComponents(GlFilteredSearchTokenSegment); + + expect(tokenSegments).toHaveLength(3); // `Release` `=` `v1` + expect(tokenSegments.at(2).text()).toBe(id.toString()); + }); + + it('fetches initial values', () => { + const fetchReleasesSpy = jest.fn().mockResolvedValue(); + + wrapper = createComponent({ + config: { ...mockReleaseToken, fetchReleases: fetchReleasesSpy }, + value: { data: id }, + }); + + expect(fetchReleasesSpy).toHaveBeenCalledWith(id); + }); + + it('fetches releases on user input', () => { + const search = 'hello'; + const fetchReleasesSpy = jest.fn().mockResolvedValue(); + + wrapper = createComponent({ + config: { ...mockReleaseToken, fetchReleases: fetchReleasesSpy }, + }); + + wrapper.findComponent(GlFilteredSearchToken).vm.$emit('input', { data: search }); + + expect(fetchReleasesSpy).toHaveBeenCalledWith(search); + }); + + it('renders error message when request fails', async () => { + const fetchReleasesSpy = jest.fn().mockRejectedValue(); + + wrapper = createComponent({ + config: { ...mockReleaseToken, fetchReleases: fetchReleasesSpy }, + }); + await waitForPromises(); + + expect(createFlash).toHaveBeenCalledWith({ + message: 'There was a problem fetching releases.', + }); + }); +}); diff --git a/spec/helpers/issues_helper_spec.rb b/spec/helpers/issues_helper_spec.rb index 850051c7875..43b27dded3b 100644 --- a/spec/helpers/issues_helper_spec.rb +++ b/spec/helpers/issues_helper_spec.rb @@ -326,6 +326,7 @@ RSpec.describe IssuesHelper do new_issue_path: new_project_issue_path(project, issue: { milestone_id: finder.milestones.first.id }), project_import_jira_path: project_import_jira_path(project), quick_actions_help_path: help_page_path('user/project/quick_actions'), + releases_path: project_releases_path(project, format: :json), reset_path: new_issuable_address_project_path(project, issuable_type: 'issue'), rss_path: '#', show_new_issue_link: 'true', diff --git a/spec/lib/gitlab/metrics/samplers/action_cable_sampler_spec.rb b/spec/lib/gitlab/metrics/samplers/action_cable_sampler_spec.rb index f751416f4ec..d834b796179 100644 --- a/spec/lib/gitlab/metrics/samplers/action_cable_sampler_spec.rb +++ b/spec/lib/gitlab/metrics/samplers/action_cable_sampler_spec.rb @@ -23,64 +23,46 @@ RSpec.describe Gitlab::Metrics::Samplers::ActionCableSampler do allow(pool).to receive(:queue_length).and_return(6) end - shared_examples 'collects metrics' do |expected_labels| - it 'includes active connections' do - expect(subject.metrics[:active_connections]).to receive(:set).with(expected_labels, 0) + it 'includes active connections' do + expect(subject.metrics[:active_connections]).to receive(:set).with({}, 0) - subject.sample - end - - it 'includes minimum worker pool size' do - expect(subject.metrics[:pool_min_size]).to receive(:set).with(expected_labels, 1) - - subject.sample - end - - it 'includes maximum worker pool size' do - expect(subject.metrics[:pool_max_size]).to receive(:set).with(expected_labels, 2) - - subject.sample - end + subject.sample + end - it 'includes current worker pool size' do - expect(subject.metrics[:pool_current_size]).to receive(:set).with(expected_labels, 3) + it 'includes minimum worker pool size' do + expect(subject.metrics[:pool_min_size]).to receive(:set).with({}, 1) - subject.sample - end + subject.sample + end - it 'includes largest worker pool size' do - expect(subject.metrics[:pool_largest_size]).to receive(:set).with(expected_labels, 4) + it 'includes maximum worker pool size' do + expect(subject.metrics[:pool_max_size]).to receive(:set).with({}, 2) - subject.sample - end + subject.sample + end - it 'includes worker pool completed task count' do - expect(subject.metrics[:pool_completed_tasks]).to receive(:set).with(expected_labels, 5) + it 'includes current worker pool size' do + expect(subject.metrics[:pool_current_size]).to receive(:set).with({}, 3) - subject.sample - end + subject.sample + end - it 'includes worker pool pending task count' do - expect(subject.metrics[:pool_pending_tasks]).to receive(:set).with(expected_labels, 6) + it 'includes largest worker pool size' do + expect(subject.metrics[:pool_largest_size]).to receive(:set).with({}, 4) - subject.sample - end + subject.sample end - context 'for in-app mode' do - before do - expect(Gitlab::ActionCable::Config).to receive(:in_app?).and_return(true) - end + it 'includes worker pool completed task count' do + expect(subject.metrics[:pool_completed_tasks]).to receive(:set).with({}, 5) - it_behaves_like 'collects metrics', server_mode: 'in-app' + subject.sample end - context 'for standalone mode' do - before do - expect(Gitlab::ActionCable::Config).to receive(:in_app?).and_return(false) - end + it 'includes worker pool pending task count' do + expect(subject.metrics[:pool_pending_tasks]).to receive(:set).with({}, 6) - it_behaves_like 'collects metrics', server_mode: 'standalone' + subject.sample end end end diff --git a/spec/lib/gitlab/runtime_spec.rb b/spec/lib/gitlab/runtime_spec.rb index 96bc5448790..4627a8db82e 100644 --- a/spec/lib/gitlab/runtime_spec.rb +++ b/spec/lib/gitlab/runtime_spec.rb @@ -48,10 +48,9 @@ RSpec.describe Gitlab::Runtime do before do stub_const('::Puma', puma_type) - stub_env('ACTION_CABLE_IN_APP', 'false') end - it_behaves_like "valid runtime", :puma, 1 + it_behaves_like "valid runtime", :puma, 1 + Gitlab::ActionCable::Config.worker_pool_size end context "puma with cli_config" do @@ -61,27 +60,16 @@ RSpec.describe Gitlab::Runtime do before do stub_const('::Puma', puma_type) allow(puma_type).to receive_message_chain(:cli_config, :options).and_return(max_threads: 2, workers: max_workers) - stub_env('ACTION_CABLE_IN_APP', 'false') end - it_behaves_like "valid runtime", :puma, 3 + it_behaves_like "valid runtime", :puma, 3 + Gitlab::ActionCable::Config.worker_pool_size - context "when ActionCable in-app mode is enabled" do + context "when ActionCable worker pool size is configured" do before do - stub_env('ACTION_CABLE_IN_APP', 'true') - stub_env('ACTION_CABLE_WORKER_POOL_SIZE', '3') + stub_env('ACTION_CABLE_WORKER_POOL_SIZE', 10) end - it_behaves_like "valid runtime", :puma, 6 - end - - context "when ActionCable standalone is run" do - before do - stub_const('ACTION_CABLE_SERVER', true) - stub_env('ACTION_CABLE_WORKER_POOL_SIZE', '8') - end - - it_behaves_like "valid runtime", :puma, 11 + it_behaves_like "valid runtime", :puma, 13 end describe ".puma_in_clustered_mode?" do diff --git a/spec/serializers/service_field_entity_spec.rb b/spec/serializers/service_field_entity_spec.rb index d5a1c53d2f5..a06fdf95159 100644 --- a/spec/serializers/service_field_entity_spec.rb +++ b/spec/serializers/service_field_entity_spec.rb @@ -86,7 +86,7 @@ RSpec.describe ServiceFieldEntity do expected_hash = { type: 'select', name: 'branches_to_be_notified', - title: nil, + title: 'Branches for which notifications are to be sent', placeholder: nil, required: nil, choices: [['All branches', 'all'], ['Default branch', 'default'], ['Protected branches', 'protected'], ['Default branch and protected branches', 'default_and_protected']], diff --git a/spec/services/issues/update_service_spec.rb b/spec/services/issues/update_service_spec.rb index 331cf291f21..44ffece7f3b 100644 --- a/spec/services/issues/update_service_spec.rb +++ b/spec/services/issues/update_service_spec.rb @@ -1248,28 +1248,38 @@ RSpec.describe Issues::UpdateService, :mailer do let(:closed_issuable) { create(:closed_issue, project: project) } end - context 'real-time updates' do - using RSpec::Parameterized::TableSyntax - + context 'broadcasting issue assignee updates' do let(:update_params) { { assignee_ids: [user2.id] } } - where(:action_cable_in_app_enabled, :feature_flag_enabled, :should_broadcast) do - true | true | true - true | false | true - false | true | true - false | false | false - end + context 'when feature flag is enabled' do + before do + stub_feature_flags(broadcast_issue_updates: true) + end + + it 'triggers the GraphQL subscription' do + expect(GraphqlTriggers).to receive(:issuable_assignees_updated).with(issue) + + update_issue(update_params) + end - with_them do - it 'broadcasts to the issues channel based on ActionCable and feature flag values' do - allow(Gitlab::ActionCable::Config).to receive(:in_app?).and_return(action_cable_in_app_enabled) - stub_feature_flags(broadcast_issue_updates: feature_flag_enabled) + context 'when assignee is not updated' do + let(:update_params) { { title: 'Some other title' } } - if should_broadcast - expect(GraphqlTriggers).to receive(:issuable_assignees_updated).with(issue) - else + it 'does not trigger the GraphQL subscription' do expect(GraphqlTriggers).not_to receive(:issuable_assignees_updated).with(issue) + + update_issue(update_params) end + end + end + + context 'when feature flag is disabled' do + before do + stub_feature_flags(broadcast_issue_updates: false) + end + + it 'does not trigger the GraphQL subscription' do + expect(GraphqlTriggers).not_to receive(:issuable_assignees_updated).with(issue) update_issue(update_params) end diff --git a/spec/tooling/danger/changelog_spec.rb b/spec/tooling/danger/changelog_spec.rb index 5777186cc28..377c3e881c9 100644 --- a/spec/tooling/danger/changelog_spec.rb +++ b/spec/tooling/danger/changelog_spec.rb @@ -228,7 +228,7 @@ RSpec.describe Tooling::Danger::Changelog do end context 'with changelog label' do - let(:mr_labels) { ['feature'] } + let(:mr_labels) { ['type::feature'] } it 'is truthy' do is_expected.to be_truthy @@ -236,7 +236,7 @@ RSpec.describe Tooling::Danger::Changelog do end context 'with no changelog label' do - let(:mr_labels) { ['tooling'] } + let(:mr_labels) { ['type::tooling'] } it 'is truthy' do is_expected.to be_falsey diff --git a/tooling/danger/changelog.rb b/tooling/danger/changelog.rb index c053d366199..fbf8ae931e2 100644 --- a/tooling/danger/changelog.rb +++ b/tooling/danger/changelog.rb @@ -6,7 +6,7 @@ module Tooling module Danger module Changelog NO_CHANGELOG_LABELS = [ - 'tooling', + 'type::tooling', 'tooling::pipelines', 'tooling::workflow', 'ci-build', |