Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitlab-ci.yml1
-rw-r--r--.rubocop_manual_todo.yml5
-rw-r--r--Dangerfile8
-rw-r--r--Gemfile2
-rw-r--r--Gemfile.lock4
-rw-r--r--app/assets/javascripts/admin/users/components/actions/unblock.vue4
-rw-r--r--app/assets/javascripts/admin/users/components/user_actions.vue4
-rw-r--r--app/assets/javascripts/admin/users/components/users_table.vue4
-rw-r--r--app/assets/javascripts/ide/components/branches/item.vue2
-rw-r--r--app/assets/javascripts/issue_show/components/app.vue1
-rw-r--r--app/assets/javascripts/issue_show/components/form.vue64
-rw-r--r--app/assets/javascripts/issues_list/components/issues_list_app.vue13
-rw-r--r--app/assets/javascripts/issues_list/index.js11
-rw-r--r--app/assets/javascripts/jobs/components/table/jobs_table_app.vue4
-rw-r--r--app/assets/javascripts/pages/groups/issues/index.js8
-rw-r--r--app/assets/javascripts/pages/projects/issues/index/index.js42
-rw-r--r--app/assets/javascripts/pages/projects/issues/service_desk/index.js4
-rw-r--r--app/assets/javascripts/projects/pipelines/charts/components/app.vue5
-rw-r--r--app/assets/javascripts/projects/pipelines/charts/components/pipeline_charts.vue2
-rw-r--r--app/assets/javascripts/vue_shared/components/ci_cd_analytics/ci_cd_analytics_area_chart.vue (renamed from app/assets/javascripts/projects/pipelines/charts/components/ci_cd_analytics_area_chart.vue)2
-rw-r--r--app/assets/javascripts/vue_shared/components/ci_cd_analytics/ci_cd_analytics_charts.vue (renamed from app/assets/javascripts/projects/pipelines/charts/components/ci_cd_analytics_charts.vue)0
-rw-r--r--app/assets/javascripts/vue_shared/components/ci_cd_analytics/constants.js1
-rw-r--r--app/assets/stylesheets/framework/common.scss3
-rw-r--r--app/models/concerns/relative_positioning.rb7
-rw-r--r--app/models/cycle_analytics/project_level_stage_adapter.rb4
-rw-r--r--app/presenters/ci/pipeline_presenter.rb18
-rw-r--r--app/services/ci/prometheus_metrics/observe_histograms_service.rb6
-rw-r--r--app/views/admin/users/_user.html.haml8
-rw-r--r--changelogs/unreleased/300770-feature-flag-rollout-of-ci_accept_frontend_prometheus_metrics.yml5
-rw-r--r--changelogs/unreleased/324783-remove-use-deprecated-sizes-from-ide-branches-item.yml5
-rw-r--r--changelogs/unreleased/326156-merge-request-version-selector-overlaps-sidebar-on-mobile.yml5
-rw-r--r--changelogs/unreleased/328402-remove-optimize-shifting-positions-feature-flag.yml5
-rw-r--r--changelogs/unreleased/34817-issue-description-reverts-to-an-older-version-when-saving-without-p.yml5
-rw-r--r--changelogs/unreleased/issue-220040-fix-rails-savebang-project-models.yml5
-rw-r--r--changelogs/unreleased/rubocop-performance-openstruct-project-level-stage-adapter.yml5
-rw-r--r--changelogs/unreleased/tw-ui-text-active-voice-pipeline-presenter.yml5
-rw-r--r--config/feature_flags/development/ci_accept_frontend_prometheus_metrics.yml8
-rw-r--r--config/feature_flags/development/optimize_shifting_relative_positions.yml8
-rw-r--r--danger/changes_size/Dangerfile19
-rw-r--r--danger/database/Dangerfile2
-rw-r--r--danger/feature_flag/Dangerfile2
-rw-r--r--doc/README.md2
-rw-r--r--doc/administration/consul.md26
-rw-r--r--doc/administration/geo/index.md2
-rw-r--r--doc/administration/geo/replication/location_aware_git_url.md30
-rw-r--r--doc/ci/yaml/script.md2
-rw-r--r--doc/development/approval_rules.md2
-rw-r--r--doc/development/go_guide/index.md6
-rw-r--r--doc/development/multi_version_compatibility.md2
-rw-r--r--doc/development/snowplow/index.md2
-rw-r--r--doc/install/google_cloud_platform/index.md2
-rw-r--r--doc/integration/README.md105
-rw-r--r--doc/integration/index.md105
-rw-r--r--doc/integration/jira/img/jira_merge_request_close.pngbin21130 -> 0 bytes
-rw-r--r--doc/integration/jira/issues.md219
-rw-r--r--doc/user/application_security/index.md2
-rw-r--r--doc/user/index.md2
-rw-r--r--doc/user/project/merge_requests/approvals/index.md7
-rw-r--r--doc/user/project/merge_requests/approvals/rules.md305
-rw-r--r--doc/user/project/merge_requests/approvals/settings.md2
-rw-r--r--lib/gitlab/patch/prependable.rb7
-rw-r--r--locale/gitlab.pot3
-rw-r--r--spec/features/admin/users/user_spec.rb177
-rw-r--r--spec/features/admin/users/users_spec.rb559
-rw-r--r--spec/features/projects/user_changes_project_visibility_spec.rb4
-rw-r--r--spec/frontend/admin/users/components/user_actions_spec.js12
-rw-r--r--spec/frontend/issue_show/components/form_spec.js54
-rw-r--r--spec/frontend/issues_list/components/issues_list_app_spec.js4
-rw-r--r--spec/frontend/jobs/components/table/job_table_app_spec.js88
-rw-r--r--spec/frontend/jobs/mock_data.js93
-rw-r--r--spec/frontend/projects/pipelines/charts/components/ci_cd_analytics_area_chart_spec.js2
-rw-r--r--spec/frontend/projects/pipelines/charts/components/ci_cd_analytics_charts_spec.js6
-rw-r--r--spec/frontend/projects/pipelines/charts/components/pipeline_charts_spec.js2
-rw-r--r--spec/models/project_auto_devops_spec.rb16
-rw-r--r--spec/models/project_feature_spec.rb2
-rw-r--r--spec/models/project_spec.rb38
-rw-r--r--spec/models/project_team_spec.rb8
-rw-r--r--spec/requests/projects/ci/promeheus_metrics/histograms_controller_spec.rb14
-rw-r--r--spec/serializers/ci/pipeline_entity_spec.rb2
-rw-r--r--spec/services/ci/prometheus_metrics/observe_histograms_service_spec.rb26
-rw-r--r--spec/support/shared_examples/models/relative_positioning_shared_examples.rb22
-rw-r--r--spec/tooling/danger/project_helper_spec.rb2
-rw-r--r--tooling/danger/project_helper.rb15
83 files changed, 1285 insertions, 1010 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index c722f0a597d..f344e61639b 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -68,6 +68,7 @@ variables:
ELASTIC_URL: "http://elastic:changeme@elasticsearch:9200"
DOCKER_VERSION: "20.10.1"
CACHE_CLASSES: "true"
+ FF_USE_FASTZIP: "true"
# Preparing custom clone path to reduce space used by all random forks
# on GitLab.com's Shared Runners. Our main forks - especially the security
diff --git a/.rubocop_manual_todo.yml b/.rubocop_manual_todo.yml
index c8e4e85eb3b..48a495a0ead 100644
--- a/.rubocop_manual_todo.yml
+++ b/.rubocop_manual_todo.yml
@@ -199,10 +199,6 @@ Rails/SaveBang:
- 'spec/models/operations/feature_flags/strategy_spec.rb'
- 'spec/models/operations/feature_flags/user_list_spec.rb'
- 'spec/models/pages_domain_spec.rb'
- - 'spec/models/project_auto_devops_spec.rb'
- - 'spec/models/project_feature_spec.rb'
- - 'spec/models/project_spec.rb'
- - 'spec/models/project_team_spec.rb'
- 'spec/models/protectable_dropdown_spec.rb'
- 'spec/models/redirect_route_spec.rb'
- 'spec/models/release_spec.rb'
@@ -2726,7 +2722,6 @@ Performance/OpenStruct:
- 'Guardfile'
- 'app/finders/snippets_finder.rb'
- 'app/helpers/application_settings_helper.rb'
- - 'app/models/cycle_analytics/project_level_stage_adapter.rb'
- 'ee/lib/gitlab/graphql/aggregations/epics/epic_node.rb'
- 'ee/lib/gitlab/graphql/aggregations/epics/epic_node.rb'
- 'lib/api/wikis.rb'
diff --git a/Dangerfile b/Dangerfile
index 699be613f2d..37a45674e16 100644
--- a/Dangerfile
+++ b/Dangerfile
@@ -2,11 +2,15 @@
require 'gitlab-dangerfiles'
-Gitlab::Dangerfiles.import_plugins(danger)
-danger.import_plugin('danger/plugins/*.rb')
+gitlab_dangerfiles = Gitlab::Dangerfiles::Engine.new(self)
+gitlab_dangerfiles.import_plugins
return if helper.release_automation?
+danger.import_plugin('danger/plugins/*.rb')
+
+gitlab_dangerfiles.import_dangerfiles
+
project_helper.rule_names.each do |rule|
danger.import_dangerfile(path: File.join('danger', rule))
end
diff --git a/Gemfile b/Gemfile
index fcf5aae2585..4aecc60600e 100644
--- a/Gemfile
+++ b/Gemfile
@@ -403,7 +403,7 @@ group :development, :test do
end
group :development, :test, :danger do
- gem 'gitlab-dangerfiles', '~> 1.1.1', require: false
+ gem 'gitlab-dangerfiles', '~> 2.0.0', require: false
end
group :development, :test, :coverage do
diff --git a/Gemfile.lock b/Gemfile.lock
index 1054af774c7..4d39fc99de6 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -447,7 +447,7 @@ GEM
terminal-table (~> 1.5, >= 1.5.1)
gitlab-chronic (0.10.5)
numerizer (~> 0.2)
- gitlab-dangerfiles (1.1.1)
+ gitlab-dangerfiles (2.0.0)
danger-gitlab
gitlab-experiment (0.5.3)
activesupport (>= 3.0)
@@ -1449,7 +1449,7 @@ DEPENDENCIES
gitaly (~> 13.11.0.pre.rc1)
github-markup (~> 1.7.0)
gitlab-chronic (~> 0.10.5)
- gitlab-dangerfiles (~> 1.1.1)
+ gitlab-dangerfiles (~> 2.0.0)
gitlab-experiment (~> 0.5.3)
gitlab-fog-azure-rm (~> 1.0.1)
gitlab-fog-google (~> 1.13)
diff --git a/app/assets/javascripts/admin/users/components/actions/unblock.vue b/app/assets/javascripts/admin/users/components/actions/unblock.vue
index f2b501caf09..d4c0f900c94 100644
--- a/app/assets/javascripts/admin/users/components/actions/unblock.vue
+++ b/app/assets/javascripts/admin/users/components/actions/unblock.vue
@@ -23,9 +23,7 @@ export default {
'data-method': 'put',
'data-modal-attributes': JSON.stringify({
title: sprintf(s__('AdminUsers|Unblock user %{username}?'), { username: this.username }),
- message: s__(
- 'AdminUsers|You can always unblock their account, their data will remain intact.',
- ),
+ message: s__('AdminUsers|You can always block their account again if needed.'),
okVariant: 'confirm',
okTitle: s__('AdminUsers|Unblock'),
}),
diff --git a/app/assets/javascripts/admin/users/components/user_actions.vue b/app/assets/javascripts/admin/users/components/user_actions.vue
index f8fafb0d539..b782526e6be 100644
--- a/app/assets/javascripts/admin/users/components/user_actions.vue
+++ b/app/assets/javascripts/admin/users/components/user_actions.vue
@@ -70,14 +70,14 @@ export default {
</script>
<template>
- <div class="gl-display-flex gl-justify-content-end">
+ <div class="gl-display-flex gl-justify-content-end" :data-testid="`user-actions-${user.id}`">
<gl-button v-if="hasEditAction" data-testid="edit" :href="userPaths.edit">{{
$options.i18n.edit
}}</gl-button>
<gl-dropdown
v-if="hasDropdownActions"
- data-testid="actions"
+ data-testid="dropdown-toggle"
right
class="gl-ml-2"
icon="settings"
diff --git a/app/assets/javascripts/admin/users/components/users_table.vue b/app/assets/javascripts/admin/users/components/users_table.vue
index 8b41a063abc..4b5d12b3a5f 100644
--- a/app/assets/javascripts/admin/users/components/users_table.vue
+++ b/app/assets/javascripts/admin/users/components/users_table.vue
@@ -77,6 +77,10 @@ export default {
<user-date :date="lastActivityOn" show-never />
</template>
+ <template #cell(projectsCount)="{ item: { id, projectsCount } }">
+ <div :data-testid="`user-project-count-${id}`">{{ projectsCount }}</div>
+ </template>
+
<template #cell(settings)="{ item: user }">
<user-actions :user="user" :paths="paths" />
</template>
diff --git a/app/assets/javascripts/ide/components/branches/item.vue b/app/assets/javascripts/ide/components/branches/item.vue
index 35e2f99cb6a..bdfcff3136b 100644
--- a/app/assets/javascripts/ide/components/branches/item.vue
+++ b/app/assets/javascripts/ide/components/branches/item.vue
@@ -34,7 +34,7 @@ export default {
<template>
<a :href="branchHref" class="btn-link d-flex align-items-center">
<span class="d-flex gl-mr-3 ide-search-list-current-icon">
- <gl-icon v-if="isActive" :size="18" name="mobile-issue-close" use-deprecated-sizes />
+ <gl-icon v-if="isActive" :size="16" name="mobile-issue-close" />
</span>
<span>
<strong> {{ item.name }} </strong>
diff --git a/app/assets/javascripts/issue_show/components/app.vue b/app/assets/javascripts/issue_show/components/app.vue
index d153ff21a35..01b4e81a11a 100644
--- a/app/assets/javascripts/issue_show/components/app.vue
+++ b/app/assets/javascripts/issue_show/components/app.vue
@@ -418,6 +418,7 @@ export default {
<div v-if="canUpdate && showForm">
<form-component
:form-state="formState"
+ :initial-description-text="initialDescriptionText"
:can-destroy="canDestroy"
:issuable-templates="issuableTemplates"
:markdown-docs-path="markdownDocsPath"
diff --git a/app/assets/javascripts/issue_show/components/form.vue b/app/assets/javascripts/issue_show/components/form.vue
index 76ea489fb86..7ea8ef6dfcd 100644
--- a/app/assets/javascripts/issue_show/components/form.vue
+++ b/app/assets/javascripts/issue_show/components/form.vue
@@ -1,4 +1,5 @@
<script>
+import { GlAlert } from '@gitlab/ui';
import $ from 'jquery';
import Autosave from '~/autosave';
import eventHub from '../event_hub';
@@ -15,6 +16,7 @@ export default {
descriptionField,
descriptionTemplate,
editActions,
+ GlAlert,
},
props: {
canDestroy: {
@@ -69,6 +71,16 @@ export default {
required: false,
default: true,
},
+ initialDescriptionText: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ },
+ data() {
+ return {
+ showOutdatedDescriptionWarning: false,
+ };
},
computed: {
hasIssuableTemplates() {
@@ -102,11 +114,17 @@ export default {
},
} = this.$refs;
- this.autosaveDescription = new Autosave($(textarea), [
- document.location.pathname,
- document.location.search,
- 'description',
- ]);
+ this.autosaveDescription = new Autosave(
+ $(textarea),
+ [document.location.pathname, document.location.search, 'description'],
+ null,
+ this.formState.lock_version,
+ );
+
+ const savedLockVersion = this.autosaveDescription.getSavedLockVersion();
+
+ this.showOutdatedDescriptionWarning =
+ savedLockVersion && String(this.formState.lock_version) !== savedLockVersion;
this.autosaveTitle = new Autosave($(input), [
document.location.pathname,
@@ -118,6 +136,27 @@ export default {
this.autosaveDescription.reset();
this.autosaveTitle.reset();
},
+ keepAutosave() {
+ const {
+ description: {
+ $refs: { textarea },
+ },
+ } = this.$refs;
+
+ textarea.focus();
+ this.showOutdatedDescriptionWarning = false;
+ },
+ discardAutosave() {
+ const {
+ description: {
+ $refs: { textarea },
+ },
+ } = this.$refs;
+
+ textarea.value = this.initialDescriptionText;
+ textarea.focus();
+ this.showOutdatedDescriptionWarning = false;
+ },
},
};
</script>
@@ -125,6 +164,21 @@ export default {
<template>
<form>
<locked-warning v-if="showLockedWarning" />
+ <gl-alert
+ v-if="showOutdatedDescriptionWarning"
+ class="gl-mb-5"
+ variant="warning"
+ primary-button-text="__('Keep')"
+ secondary-button-text="__('Discard')"
+ :dismissible="false"
+ @primaryAction="keepAutosave"
+ @secondaryAction="discardAutosave"
+ >{{
+ __(
+ 'The comment you are editing has been changed by another user. Would you like to keep your changes and overwrite the new description or discard your changes?',
+ )
+ }}</gl-alert
+ >
<div class="row">
<div v-if="hasIssuableTemplates" class="col-sm-4 col-lg-3">
<description-template
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 560f691eb4e..621e08e9bfc 100644
--- a/app/assets/javascripts/issues_list/components/issues_list_app.vue
+++ b/app/assets/javascripts/issues_list/components/issues_list_app.vue
@@ -353,7 +353,18 @@ export default {
eventHub.$emit('issuables:updateBulkEdit');
});
},
- handleBulkUpdateClick() {
+ async handleBulkUpdateClick() {
+ if (!this.hasInitBulkEdit) {
+ const initBulkUpdateSidebar = await import('~/issuable_init_bulk_update_sidebar');
+ initBulkUpdateSidebar.default.init('issuable_');
+
+ const usersSelect = await import('~/users_select');
+ const UsersSelect = usersSelect.default;
+ new UsersSelect(); // eslint-disable-line no-new
+
+ this.hasInitBulkEdit = true;
+ }
+
eventHub.$emit('issuables:enableBulkEdit');
},
handleClickTab(state) {
diff --git a/app/assets/javascripts/issues_list/index.js b/app/assets/javascripts/issues_list/index.js
index 0318c2c2484..d543643b003 100644
--- a/app/assets/javascripts/issues_list/index.js
+++ b/app/assets/javascripts/issues_list/index.js
@@ -7,7 +7,7 @@ import { convertObjectPropsToCamelCase, parseBoolean } from '~/lib/utils/common_
import IssuablesListApp from './components/issuables_list_app.vue';
import JiraIssuesImportStatusRoot from './components/jira_issues_import_status_app.vue';
-function mountJiraIssuesListApp() {
+export function mountJiraIssuesListApp() {
const el = document.querySelector('.js-jira-issues-import-status');
if (!el) {
@@ -37,7 +37,7 @@ function mountJiraIssuesListApp() {
});
}
-function mountIssuablesListApp() {
+export function mountIssuablesListApp() {
if (!gon.features?.vueIssuablesList) {
return;
}
@@ -66,7 +66,7 @@ function mountIssuablesListApp() {
});
}
-export function initIssuesListApp() {
+export function mountIssuesListApp() {
const el = document.querySelector('.js-issues-list');
if (!el) {
@@ -157,8 +157,3 @@ export function initIssuesListApp() {
render: (createComponent) => createComponent(IssuesListApp),
});
}
-
-export default function initIssuablesList() {
- mountJiraIssuesListApp();
- mountIssuablesListApp();
-}
diff --git a/app/assets/javascripts/jobs/components/table/jobs_table_app.vue b/app/assets/javascripts/jobs/components/table/jobs_table_app.vue
index 55954e31654..b153f53a179 100644
--- a/app/assets/javascripts/jobs/components/table/jobs_table_app.vue
+++ b/app/assets/javascripts/jobs/components/table/jobs_table_app.vue
@@ -29,7 +29,7 @@ export default {
};
},
update({ project }) {
- return project?.jobs;
+ return project?.jobs?.nodes || [];
},
error() {
this.hasError = true;
@@ -80,6 +80,6 @@ export default {
/>
</div>
- <jobs-table v-else :jobs="jobs.nodes" />
+ <jobs-table v-else :jobs="jobs" />
</div>
</template>
diff --git a/app/assets/javascripts/pages/groups/issues/index.js b/app/assets/javascripts/pages/groups/issues/index.js
index b60607e8857..76db578f6f9 100644
--- a/app/assets/javascripts/pages/groups/issues/index.js
+++ b/app/assets/javascripts/pages/groups/issues/index.js
@@ -1,6 +1,6 @@
import IssuableFilteredSearchTokenKeys from 'ee_else_ce/filtered_search/issuable_filtered_search_token_keys';
import issuableInitBulkUpdateSidebar from '~/issuable_init_bulk_update_sidebar';
-import initIssuablesList from '~/issues_list';
+import { mountIssuablesListApp } from '~/issues_list';
import initManualOrdering from '~/manual_ordering';
import { FILTERED_SEARCH } from '~/pages/constants';
import initFilteredSearch from '~/pages/search/init_filtered_search';
@@ -12,8 +12,6 @@ IssuableFilteredSearchTokenKeys.addExtraTokensForIssues();
IssuableFilteredSearchTokenKeys.removeTokensForKeys('release');
issuableInitBulkUpdateSidebar.init(ISSUE_BULK_UPDATE_PREFIX);
-initIssuablesList();
-
initFilteredSearch({
page: FILTERED_SEARCH.ISSUES,
isGroupDecendent: true,
@@ -22,3 +20,7 @@ initFilteredSearch({
});
projectSelect();
initManualOrdering();
+
+if (gon.features?.vueIssuablesList) {
+ mountIssuablesListApp();
+}
diff --git a/app/assets/javascripts/pages/projects/issues/index/index.js b/app/assets/javascripts/pages/projects/issues/index/index.js
index 85489ae8687..8cd703133f5 100644
--- a/app/assets/javascripts/pages/projects/issues/index/index.js
+++ b/app/assets/javascripts/pages/projects/issues/index/index.js
@@ -1,36 +1,38 @@
-/* eslint-disable no-new */
-
import IssuableFilteredSearchTokenKeys from 'ee_else_ce/filtered_search/issuable_filtered_search_token_keys';
import ShortcutsNavigation from '~/behaviors/shortcuts/shortcuts_navigation';
import initCsvImportExportButtons from '~/issuable/init_csv_import_export_buttons';
import initIssuableByEmail from '~/issuable/init_issuable_by_email';
import IssuableIndex from '~/issuable_index';
-import initIssuablesList, { initIssuesListApp } from '~/issues_list';
+import { mountIssuablesListApp, mountIssuesListApp, mountJiraIssuesListApp } from '~/issues_list';
import initManualOrdering from '~/manual_ordering';
import { FILTERED_SEARCH } from '~/pages/constants';
import { ISSUABLE_INDEX } from '~/pages/projects/constants';
import initFilteredSearch from '~/pages/search/init_filtered_search';
import UsersSelect from '~/users_select';
-IssuableFilteredSearchTokenKeys.addExtraTokensForIssues();
-
-initFilteredSearch({
- page: FILTERED_SEARCH.ISSUES,
- filteredSearchTokenKeys: IssuableFilteredSearchTokenKeys,
- useDefaultState: true,
-});
-
if (gon.features?.vueIssuesList) {
- new IssuableIndex();
+ mountIssuesListApp();
} else {
- new IssuableIndex(ISSUABLE_INDEX.ISSUE);
+ IssuableFilteredSearchTokenKeys.addExtraTokensForIssues();
+
+ initFilteredSearch({
+ page: FILTERED_SEARCH.ISSUES,
+ filteredSearchTokenKeys: IssuableFilteredSearchTokenKeys,
+ useDefaultState: true,
+ });
+
+ new IssuableIndex(ISSUABLE_INDEX.ISSUE); // eslint-disable-line no-new
+ new UsersSelect(); // eslint-disable-line no-new
+
+ initCsvImportExportButtons();
+ initIssuableByEmail();
+ initManualOrdering();
+
+ if (gon.features?.vueIssuablesList) {
+ mountIssuablesListApp();
+ }
}
-new ShortcutsNavigation();
-new UsersSelect();
+new ShortcutsNavigation(); // eslint-disable-line no-new
-initManualOrdering();
-initIssuablesList();
-initIssuableByEmail();
-initCsvImportExportButtons();
-initIssuesListApp();
+mountJiraIssuesListApp();
diff --git a/app/assets/javascripts/pages/projects/issues/service_desk/index.js b/app/assets/javascripts/pages/projects/issues/service_desk/index.js
index 5be9f6117dc..d906c579697 100644
--- a/app/assets/javascripts/pages/projects/issues/service_desk/index.js
+++ b/app/assets/javascripts/pages/projects/issues/service_desk/index.js
@@ -1,4 +1,4 @@
-import initIssuablesList from '~/issues_list';
+import { mountIssuablesListApp } from '~/issues_list';
import FilteredSearchServiceDesk from './filtered_search';
const supportBotData = JSON.parse(
@@ -11,5 +11,5 @@ if (document.querySelector('.filtered-search')) {
}
if (gon.features?.vueIssuablesList) {
- initIssuablesList();
+ mountIssuablesListApp();
}
diff --git a/app/assets/javascripts/projects/pipelines/charts/components/app.vue b/app/assets/javascripts/projects/pipelines/charts/components/app.vue
index 4a235e98fe4..25bacc1cc4a 100644
--- a/app/assets/javascripts/projects/pipelines/charts/components/app.vue
+++ b/app/assets/javascripts/projects/pipelines/charts/components/app.vue
@@ -9,9 +9,8 @@ export default {
GlTab,
PipelineCharts,
DeploymentFrequencyCharts: () =>
- import('ee_component/projects/pipelines/charts/components/deployment_frequency_charts.vue'),
- LeadTimeCharts: () =>
- import('ee_component/projects/pipelines/charts/components/lead_time_charts.vue'),
+ import('ee_component/dora/components/deployment_frequency_charts.vue'),
+ LeadTimeCharts: () => import('ee_component/dora/components/lead_time_charts.vue'),
},
inject: {
shouldRenderDoraCharts: {
diff --git a/app/assets/javascripts/projects/pipelines/charts/components/pipeline_charts.vue b/app/assets/javascripts/projects/pipelines/charts/components/pipeline_charts.vue
index 6a963616224..b7b33701ab1 100644
--- a/app/assets/javascripts/projects/pipelines/charts/components/pipeline_charts.vue
+++ b/app/assets/javascripts/projects/pipelines/charts/components/pipeline_charts.vue
@@ -4,6 +4,7 @@ import { GlColumnChart } from '@gitlab/ui/dist/charts';
import dateFormat from 'dateformat';
import { getDateInPast } from '~/lib/utils/datetime_utility';
import { __, s__, sprintf } from '~/locale';
+import CiCdAnalyticsCharts from '~/vue_shared/components/ci_cd_analytics/ci_cd_analytics_charts.vue';
import {
DEFAULT,
CHART_CONTAINER_HEIGHT,
@@ -21,7 +22,6 @@ import {
} from '../constants';
import getPipelineCountByStatus from '../graphql/queries/get_pipeline_count_by_status.query.graphql';
import getProjectPipelineStatistics from '../graphql/queries/get_project_pipeline_statistics.query.graphql';
-import CiCdAnalyticsCharts from './ci_cd_analytics_charts.vue';
import StatisticsList from './statistics_list.vue';
const defaultAnalyticsValues = {
diff --git a/app/assets/javascripts/projects/pipelines/charts/components/ci_cd_analytics_area_chart.vue b/app/assets/javascripts/vue_shared/components/ci_cd_analytics/ci_cd_analytics_area_chart.vue
index ad3e6713e45..2552236a073 100644
--- a/app/assets/javascripts/projects/pipelines/charts/components/ci_cd_analytics_area_chart.vue
+++ b/app/assets/javascripts/vue_shared/components/ci_cd_analytics/ci_cd_analytics_area_chart.vue
@@ -1,7 +1,7 @@
<script>
import { GlAreaChart } from '@gitlab/ui/dist/charts';
import ResizableChartContainer from '~/vue_shared/components/resizable_chart/resizable_chart_container.vue';
-import { CHART_CONTAINER_HEIGHT } from '../constants';
+import { CHART_CONTAINER_HEIGHT } from './constants';
export default {
name: 'CiCdAnalyticsAreaChart',
diff --git a/app/assets/javascripts/projects/pipelines/charts/components/ci_cd_analytics_charts.vue b/app/assets/javascripts/vue_shared/components/ci_cd_analytics/ci_cd_analytics_charts.vue
index f4fd57e4cdc..f4fd57e4cdc 100644
--- a/app/assets/javascripts/projects/pipelines/charts/components/ci_cd_analytics_charts.vue
+++ b/app/assets/javascripts/vue_shared/components/ci_cd_analytics/ci_cd_analytics_charts.vue
diff --git a/app/assets/javascripts/vue_shared/components/ci_cd_analytics/constants.js b/app/assets/javascripts/vue_shared/components/ci_cd_analytics/constants.js
new file mode 100644
index 00000000000..1561674c0ad
--- /dev/null
+++ b/app/assets/javascripts/vue_shared/components/ci_cd_analytics/constants.js
@@ -0,0 +1 @@
+export const CHART_CONTAINER_HEIGHT = 300;
diff --git a/app/assets/stylesheets/framework/common.scss b/app/assets/stylesheets/framework/common.scss
index 652ffd79ab3..5d14e54f02e 100644
--- a/app/assets/stylesheets/framework/common.scss
+++ b/app/assets/stylesheets/framework/common.scss
@@ -93,6 +93,9 @@
.tab-content {
overflow: visible;
+ @include media-breakpoint-down(sm) {
+ isolation: isolate;
+ }
}
pre {
diff --git a/app/models/concerns/relative_positioning.rb b/app/models/concerns/relative_positioning.rb
index 9ae0ce95bc7..36c7db8456c 100644
--- a/app/models/concerns/relative_positioning.rb
+++ b/app/models/concerns/relative_positioning.rb
@@ -53,12 +53,7 @@ module RelativePositioning
return [size, starting_from] if size >= MIN_GAP
- terminus =
- if Feature.enabled?(:optimize_shifting_relative_positions, default_enabled: :yaml)
- context.at_position(starting_from)
- else
- at_end ? context.max_sibling : context.min_sibling
- end
+ terminus = context.at_position(starting_from)
if at_end
terminus.shift_left
diff --git a/app/models/cycle_analytics/project_level_stage_adapter.rb b/app/models/cycle_analytics/project_level_stage_adapter.rb
index dd4afa9b809..5538e93a39e 100644
--- a/app/models/cycle_analytics/project_level_stage_adapter.rb
+++ b/app/models/cycle_analytics/project_level_stage_adapter.rb
@@ -4,6 +4,8 @@
# compatible with the old value stream controller actions.
module CycleAnalytics
class ProjectLevelStageAdapter
+ ProjectLevelStage = Struct.new(:title, :description, :legend, :name, :project_median, keyword_init: true )
+
def initialize(stage, options)
@stage = stage
@options = options
@@ -13,7 +15,7 @@ module CycleAnalytics
def as_json(serializer: AnalyticsStageSerializer)
presenter = Analytics::CycleAnalytics::StagePresenter.new(stage)
- serializer.new.represent(OpenStruct.new(
+ serializer.new.represent(ProjectLevelStage.new(
title: presenter.title,
description: presenter.description,
legend: presenter.legend,
diff --git a/app/presenters/ci/pipeline_presenter.rb b/app/presenters/ci/pipeline_presenter.rb
index a2cdabb912f..8e0482721ae 100644
--- a/app/presenters/ci/pipeline_presenter.rb
+++ b/app/presenters/ci/pipeline_presenter.rb
@@ -8,15 +8,15 @@ module Ci
# We use a class method here instead of a constant, allowing EE to redefine
# the returned `Hash` more easily.
def self.failure_reasons
- { unknown_failure: 'Unknown pipeline failure!',
- config_error: 'CI/CD YAML configuration error!',
- external_validation_failure: 'External pipeline validation failed!',
- activity_limit_exceeded: 'Pipeline activity limit exceeded!',
- size_limit_exceeded: 'Pipeline size limit exceeded!',
- job_activity_limit_exceeded: 'Pipeline job activity limit exceeded!',
- deployments_limit_exceeded: 'Pipeline deployments limit exceeded!',
- project_deleted: 'The associated project was deleted',
- user_blocked: 'The user who created this pipeline is blocked' }
+ { unknown_failure: 'The reason for the pipeline failure is unknown.',
+ config_error: 'The pipeline failed due to an error on the CI/CD configuration file.',
+ external_validation_failure: 'The external pipeline validation failed.',
+ activity_limit_exceeded: 'The pipeline activity limit was exceeded.',
+ size_limit_exceeded: 'The pipeline size limit was exceeded.',
+ job_activity_limit_exceeded: 'The pipeline job activity limit was exceeded.',
+ deployments_limit_exceeded: 'The pipeline deployments limit was exceeded.',
+ project_deleted: 'The project associated with this pipeline was deleted.',
+ user_blocked: 'The user who created this pipeline is blocked.' }
end
presents :pipeline
diff --git a/app/services/ci/prometheus_metrics/observe_histograms_service.rb b/app/services/ci/prometheus_metrics/observe_histograms_service.rb
index 527d87f19c2..6bd3d2121ba 100644
--- a/app/services/ci/prometheus_metrics/observe_histograms_service.rb
+++ b/app/services/ci/prometheus_metrics/observe_histograms_service.rb
@@ -25,8 +25,6 @@ module Ci
end
def execute
- return ServiceResponse.success(http_status: :accepted) unless enabled?
-
params
.fetch(:histograms, [])
.each(&method(:observe))
@@ -48,10 +46,6 @@ module Ci
.fetch(name) { raise ActiveRecord::RecordNotFound }
.call
end
-
- def enabled?
- ::Feature.enabled?(:ci_accept_frontend_prometheus_metrics, project, default_enabled: :yaml)
- end
end
end
end
diff --git a/app/views/admin/users/_user.html.haml b/app/views/admin/users/_user.html.haml
index f2920579057..2816a1061b9 100644
--- a/app/views/admin/users/_user.html.haml
+++ b/app/views/admin/users/_user.html.haml
@@ -21,13 +21,13 @@
= user.last_activity_on.nil? ? _('Never') : l(user.last_activity_on, format: :admin)
- unless user.internal?
.table-section.section-20.table-button-footer
- .table-action-buttons
- = link_to _('Edit'), edit_admin_user_path(user), id: "edit_#{dom_id(user)}", class: 'btn gl-button btn-default'
+ .table-action-buttons{ data: { testid: "user-actions-#{user.id}" } }
+ = link_to _('Edit'), edit_admin_user_path(user), class: 'btn gl-button btn-default'
- unless user == current_user
- %button.dropdown-new.btn.gl-button.btn-default{ type: 'button', data: { testid: "user-action-button-#{user.id}", toggle: 'dropdown' } }
+ %button.dropdown-new.btn.gl-button.btn-default{ type: 'button', data: { testid: "dropdown-toggle", toggle: 'dropdown' } }
= sprite_icon('settings')
= sprite_icon('chevron-down')
- %ul.dropdown-menu.dropdown-menu-right{ data: { testid: "user-action-dropdown-#{user.id}" } }
+ %ul.dropdown-menu.dropdown-menu-right
%li.dropdown-header
= _('Settings')
%li
diff --git a/changelogs/unreleased/300770-feature-flag-rollout-of-ci_accept_frontend_prometheus_metrics.yml b/changelogs/unreleased/300770-feature-flag-rollout-of-ci_accept_frontend_prometheus_metrics.yml
new file mode 100644
index 00000000000..c0d310d0995
--- /dev/null
+++ b/changelogs/unreleased/300770-feature-flag-rollout-of-ci_accept_frontend_prometheus_metrics.yml
@@ -0,0 +1,5 @@
+---
+title: Allow frontend to observe Prometheus metrics
+merge_request: 60633
+author:
+type: added
diff --git a/changelogs/unreleased/324783-remove-use-deprecated-sizes-from-ide-branches-item.yml b/changelogs/unreleased/324783-remove-use-deprecated-sizes-from-ide-branches-item.yml
new file mode 100644
index 00000000000..1b2f08bab55
--- /dev/null
+++ b/changelogs/unreleased/324783-remove-use-deprecated-sizes-from-ide-branches-item.yml
@@ -0,0 +1,5 @@
+---
+title: Update active branch icon in IDE to conform to the Pajamas design guide
+merge_request: 60168
+author: Andreas Resch @reschandreas
+type: other
diff --git a/changelogs/unreleased/326156-merge-request-version-selector-overlaps-sidebar-on-mobile.yml b/changelogs/unreleased/326156-merge-request-version-selector-overlaps-sidebar-on-mobile.yml
new file mode 100644
index 00000000000..6477b1062e3
--- /dev/null
+++ b/changelogs/unreleased/326156-merge-request-version-selector-overlaps-sidebar-on-mobile.yml
@@ -0,0 +1,5 @@
+---
+title: Fix MR sidebar rendering on mobile
+merge_request: 60552
+author:
+type: fixed
diff --git a/changelogs/unreleased/328402-remove-optimize-shifting-positions-feature-flag.yml b/changelogs/unreleased/328402-remove-optimize-shifting-positions-feature-flag.yml
new file mode 100644
index 00000000000..821ef1fd444
--- /dev/null
+++ b/changelogs/unreleased/328402-remove-optimize-shifting-positions-feature-flag.yml
@@ -0,0 +1,5 @@
+---
+title: Remove optimize_shifting_relative_positions feature flag
+merge_request: 60792
+author:
+type: other
diff --git a/changelogs/unreleased/34817-issue-description-reverts-to-an-older-version-when-saving-without-p.yml b/changelogs/unreleased/34817-issue-description-reverts-to-an-older-version-when-saving-without-p.yml
new file mode 100644
index 00000000000..4f60aeb8736
--- /dev/null
+++ b/changelogs/unreleased/34817-issue-description-reverts-to-an-older-version-when-saving-without-p.yml
@@ -0,0 +1,5 @@
+---
+title: Add warning when locally stored description is out of date.
+merge_request: 29438
+author:
+type: fixed
diff --git a/changelogs/unreleased/issue-220040-fix-rails-savebang-project-models.yml b/changelogs/unreleased/issue-220040-fix-rails-savebang-project-models.yml
new file mode 100644
index 00000000000..64ca54d12af
--- /dev/null
+++ b/changelogs/unreleased/issue-220040-fix-rails-savebang-project-models.yml
@@ -0,0 +1,5 @@
+---
+title: Fix Rails/SaveBang Rubocop offenses for project related models
+merge_request: 57983
+author: Huzaifa Iftikhar @huzaifaiftikhar
+type: fixed
diff --git a/changelogs/unreleased/rubocop-performance-openstruct-project-level-stage-adapter.yml b/changelogs/unreleased/rubocop-performance-openstruct-project-level-stage-adapter.yml
new file mode 100644
index 00000000000..41c13c379ea
--- /dev/null
+++ b/changelogs/unreleased/rubocop-performance-openstruct-project-level-stage-adapter.yml
@@ -0,0 +1,5 @@
+---
+title: Replace OpenStruct with Struct in project_level_stage_adapter.rb
+merge_request: 59956
+author: Amit Patel @amit.savani
+type: performance
diff --git a/changelogs/unreleased/tw-ui-text-active-voice-pipeline-presenter.yml b/changelogs/unreleased/tw-ui-text-active-voice-pipeline-presenter.yml
new file mode 100644
index 00000000000..0225b854c13
--- /dev/null
+++ b/changelogs/unreleased/tw-ui-text-active-voice-pipeline-presenter.yml
@@ -0,0 +1,5 @@
+---
+title: Review UI text for pipeline error messages
+merge_request: 60018
+author:
+type: other
diff --git a/config/feature_flags/development/ci_accept_frontend_prometheus_metrics.yml b/config/feature_flags/development/ci_accept_frontend_prometheus_metrics.yml
deleted file mode 100644
index b795b2505d8..00000000000
--- a/config/feature_flags/development/ci_accept_frontend_prometheus_metrics.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: ci_accept_frontend_prometheus_metrics
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/52820
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/300770
-milestone: '13.9'
-type: development
-group: group::pipeline authoring
-default_enabled: false
diff --git a/config/feature_flags/development/optimize_shifting_relative_positions.yml b/config/feature_flags/development/optimize_shifting_relative_positions.yml
deleted file mode 100644
index 25392bbe7a7..00000000000
--- a/config/feature_flags/development/optimize_shifting_relative_positions.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: optimize_shifting_relative_positions
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/59745
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/328402
-milestone: '13.12'
-type: development
-group: group::project management
-default_enabled: false
diff --git a/danger/changes_size/Dangerfile b/danger/changes_size/Dangerfile
deleted file mode 100644
index 52e6cb65d04..00000000000
--- a/danger/changes_size/Dangerfile
+++ /dev/null
@@ -1,19 +0,0 @@
-# frozen_string_literal: true
-
-# FIXME: git.info_for_file raises the following error
-# /usr/local/bundle/gems/git-1.4.0/lib/git/lib.rb:956:in `command': (Danger::DSLError)
-# [!] Invalid `Dangerfile` file:
-# [!] Invalid `Dangerfile` file: git '--git-dir=/builds/gitlab-org/gitlab/.git' '--work-tree=/builds/gitlab-org/gitlab' cat-file '-t' '' 2>&1:fatal: Not a valid object name
-# This seems to be the same as https://github.com/danger/danger/issues/535.
-
-# locale_files_updated = git.modified_files.select { |path| path.start_with?('locale') }
-# locale_files_updated.each do |locale_file_updated|
-# git_stats = git.info_for_file(locale_file_updated)
-# message "Git stats for #{locale_file_updated}: #{git_stats[:insertions]} insertions, #{git_stats[:deletions]} insertions"
-# end
-
-if git.lines_of_code > 2_000
- warn "This merge request is definitely too big (#{git.lines_of_code} lines changed), please split it into multiple merge requests."
-elsif git.lines_of_code > 500
- warn "This merge request is quite big (#{git.lines_of_code} lines changed), please consider splitting it into multiple merge requests."
-end
diff --git a/danger/database/Dangerfile b/danger/database/Dangerfile
index af4d6ed513d..cd56ea8dd22 100644
--- a/danger/database/Dangerfile
+++ b/danger/database/Dangerfile
@@ -55,7 +55,7 @@ if gitlab.mr_labels.include?('database') || db_paths_to_review.any?
markdown(DB_MESSAGE)
markdown(DB_FILES_MESSAGE + helper.markdown_list(db_paths_to_review)) if db_paths_to_review.any?
- unless helper.has_database_scoped_labels?(gitlab.mr_labels)
+ unless helper.has_database_scoped_labels?
gitlab.api.update_merge_request(gitlab.mr_json['project_id'],
gitlab.mr_json['iid'],
add_labels: 'database::review pending')
diff --git a/danger/feature_flag/Dangerfile b/danger/feature_flag/Dangerfile
index d14dd97380f..88ce6393b64 100644
--- a/danger/feature_flag/Dangerfile
+++ b/danger/feature_flag/Dangerfile
@@ -13,7 +13,7 @@ group: "%<group>s"
SUGGEST_COMMENT
def check_feature_flag_yaml(feature_flag)
- mr_group_label = helper.group_label(gitlab.mr_labels)
+ mr_group_label = helper.group_label
if feature_flag.group.nil?
message_for_feature_flag_missing_group!(feature_flag: feature_flag, mr_group_label: mr_group_label)
diff --git a/doc/README.md b/doc/README.md
index 1cc309bd957..a56f17e3bf0 100644
--- a/doc/README.md
+++ b/doc/README.md
@@ -110,7 +110,7 @@ There are many ways to integrate with GitLab, including:
|:-------------------------------------------|:------------|
| [GitLab REST API](api/README.md) | Integrate with GitLab using our REST API. |
| [GitLab GraphQL API](api/graphql/index.md) | Integrate with GitLab using our GraphQL API. |
-| [Integrations](integration/README.md) | Integrations with third-party products. |
+| [Integrations](integration/index.md) | Integrations with third-party products. |
## Contributing to GitLab
diff --git a/doc/administration/consul.md b/doc/administration/consul.md
index 926267a414a..a748259aff0 100644
--- a/doc/administration/consul.md
+++ b/doc/administration/consul.md
@@ -83,7 +83,7 @@ curl "http://127.0.0.1:8500/v1/health/state/critical"
Consul nodes communicate using the raft protocol. If the current leader goes
offline, there needs to be a leader election. A leader node must exist to facilitate
synchronization across the cluster. If too many nodes go offline at the same time,
-the cluster will lose quorum and not elect a leader due to
+the cluster loses quorum and doesn't elect a leader due to
[broken consensus](https://www.consul.io/docs/architecture/consensus).
Consult the [troubleshooting section](#troubleshooting-consul) if the cluster is not
@@ -122,19 +122,19 @@ db-a XX.XX.X.Y:8301 alive client 0.9.0 2 gitlab_co
db-b XX.XX.X.Y:8301 alive client 0.9.0 2 gitlab_consul
```
-Ideally all nodes will have a `Status` of `alive`.
+Ideally all nodes have a `Status` of `alive`.
### Restart Consul
If it is necessary to restart Consul, it is important to do this in
a controlled manner to maintain quorum. If quorum is lost, to recover the cluster,
-you will need to follow the Consul [outage recovery](#outage-recovery) process.
+you follow the Consul [outage recovery](#outage-recovery) process.
To be safe, it's recommended that you only restart Consul in one node at a time to
ensure the cluster remains intact. For larger clusters, it is possible to restart
multiple nodes at a time. See the
[Consul consensus document](https://www.consul.io/docs/architecture/consensus#deployment-table)
-for how many failures it can tolerate. This will be the number of simultaneous
+for the number of failures it can tolerate. This will be the number of simultaneous
restarts it can sustain.
To restart Consul:
@@ -145,13 +145,13 @@ sudo gitlab-ctl restart consul
### Consul nodes unable to communicate
-By default, Consul will attempt to
+By default, Consul attempts to
[bind](https://www.consul.io/docs/agent/options#_bind) to `0.0.0.0`, but
-it will advertise the first private IP address on the node for other Consul nodes
+it advertises the first private IP address on the node for other Consul nodes
to communicate with it. If the other nodes cannot communicate with a node on
-this address, then the cluster will have a failed status.
+this address, then the cluster has a failed status.
-If you are running into this issue, you will see messages like the following in `gitlab-ctl tail consul` output:
+If you run into this issue, then messages like the following are output in `gitlab-ctl tail consul`:
```plaintext
2017-09-25_19:53:39.90821 2017/09/25 19:53:39 [WARN] raft: no known peers, aborting election
@@ -181,10 +181,10 @@ If you still see the errors, you may have to
### Consul does not start - multiple private IPs
-In case that a node has multiple private IPs, Consul will be confused as to
-which of the private addresses to advertise, and then immediately exit on start.
+If a node has multiple private IPs, Consul doesn't know about
+which of the private addresses to advertise, and then it immediately exits on start.
-You will see messages like the following in `gitlab-ctl tail consul` output:
+Messages like the following are output in `gitlab-ctl tail consul`:
```plaintext
2017-11-09_17:41:45.52876 ==> Starting Consul agent...
@@ -211,8 +211,8 @@ To fix this:
### Outage recovery
-If you lost enough Consul nodes in the cluster to break quorum, then the cluster
-is considered failed, and it will not function without manual intervention.
+If you have lost enough Consul nodes in the cluster to break quorum, then the cluster
+is considered to have failed and cannot function without manual intervention.
In that case, you can either recreate the nodes from scratch or attempt a
recover.
diff --git a/doc/administration/geo/index.md b/doc/administration/geo/index.md
index f1884f297e8..8026842248d 100644
--- a/doc/administration/geo/index.md
+++ b/doc/administration/geo/index.md
@@ -27,7 +27,7 @@ to clone and fetch large repositories, speeding up development.
For a video introduction to Geo, see [Introduction to GitLab Geo - GitLab Features](https://www.youtube.com/watch?v=-HDLxSjEh6w).
-To make sure you're using the right version of the documentation, navigate to [the source version of this page on GitLab.com](https://gitlab.com/gitlab-org/gitlab/blob/master/doc/administration/geo/index.md) and choose the appropriate release from the **Switch branch/tag** dropdown. For example, [`v11.2.3-ee`](https://gitlab.com/gitlab-org/gitlab/blob/v11.2.3-ee/doc/administration/geo/index.md).
+To make sure you're using the right version of the documentation, navigate to [this page on GitLab.com](https://gitlab.com/gitlab-org/gitlab/blob/master/doc/administration/geo/index.md) and choose the appropriate release from the **Switch branch/tag** dropdown. For example, [`v11.2.3-ee`](https://gitlab.com/gitlab-org/gitlab/blob/v11.2.3-ee/doc/administration/geo/index.md).
## Use cases
diff --git a/doc/administration/geo/replication/location_aware_git_url.md b/doc/administration/geo/replication/location_aware_git_url.md
index 272b746015b..014ca59e571 100644
--- a/doc/administration/geo/replication/location_aware_git_url.md
+++ b/doc/administration/geo/replication/location_aware_git_url.md
@@ -8,11 +8,11 @@ type: howto
# Location-aware Git remote URL with AWS Route53 **(PREMIUM SELF)**
You can provide GitLab users with a single remote URL that automatically uses
-the Geo node closest to them. This means users don't need to update their Git
-configuration to take advantage of closer Geo nodes as they move.
+the Geo site closest to them. This means users don't need to update their Git
+configuration to take advantage of closer Geo sites as they move.
This is possible because, Git push requests can be automatically redirected
-(HTTP) or proxied (SSH) from **secondary** nodes to the **primary** node.
+(HTTP) or proxied (SSH) from **secondary** sites to the **primary** site.
Though these instructions use [AWS Route53](https://aws.amazon.com/route53/),
other services such as [Cloudflare](https://www.cloudflare.com/) could be used
@@ -20,30 +20,30 @@ as well.
NOTE:
You can also use a load balancer to distribute web UI or API traffic to
-[multiple Geo **secondary** nodes](../../../user/admin_area/geo_nodes.md#multiple-secondary-nodes-behind-a-load-balancer).
-Importantly, the **primary** node cannot yet be included. See the feature request
+[multiple Geo **secondary** sites](../../../user/admin_area/geo_nodes.md#multiple-secondary-nodes-behind-a-load-balancer).
+Importantly, the **primary** site cannot yet be included. See the feature request
[Support putting the **primary** behind a Geo node load balancer](https://gitlab.com/gitlab-org/gitlab/-/issues/10888).
## Prerequisites
In this example, we have already set up:
-- `primary.example.com` as a Geo **primary** node.
-- `secondary.example.com` as a Geo **secondary** node.
+- `primary.example.com` as a Geo **primary** site.
+- `secondary.example.com` as a Geo **secondary** site.
We will create a `git.example.com` subdomain that will automatically direct
requests:
-- From Europe to the **secondary** node.
-- From all other locations to the **primary** node.
+- From Europe to the **secondary** site.
+- From all other locations to the **primary** site.
In any case, you require:
-- A working GitLab **primary** node that is accessible at its own address.
-- A working GitLab **secondary** node.
+- A working GitLab **primary** site that is accessible at its own address.
+- A working GitLab **secondary** site.
- A Route53 Hosted Zone managing your domain.
-If you haven't yet set up a Geo _primary_ node and _secondary_ node, see the
+If you haven't yet set up a Geo _primary_ site and _secondary_ site, see the
[Geo setup instructions](../index.md#setup-instructions).
## Create a traffic policy
@@ -89,7 +89,7 @@ routing configurations.
![Created policy record](img/single_git_created_policy_record.png)
You have successfully set up a single host, e.g. `git.example.com` which
-distributes traffic to your Geo nodes by geolocation!
+distributes traffic to your Geo sites by geolocation!
## Configure Git clone URLs to use the special Git URL
@@ -114,10 +114,10 @@ You can customize the:
After following the configuration steps above, handling for Git requests is now location aware.
For requests:
-- Outside Europe, all requests are directed to the **primary** node.
+- Outside Europe, all requests are directed to the **primary** site.
- Within Europe, over:
- HTTP:
- - `git clone http://git.example.com/foo/bar.git` is directed to the **secondary** node.
+ - `git clone http://git.example.com/foo/bar.git` is directed to the **secondary** site.
- `git push` is initially directed to the **secondary**, which automatically
redirects to `primary.example.com`.
- SSH:
diff --git a/doc/ci/yaml/script.md b/doc/ci/yaml/script.md
index 4fc52995fc1..f4e099c3128 100644
--- a/doc/ci/yaml/script.md
+++ b/doc/ci/yaml/script.md
@@ -86,7 +86,7 @@ Second command line.
When you omit the `>` or `|` block scalar indicators, GitLab concatenates non-empty
lines to form the command. Make sure the lines can run when concatenated.
-[Shell here documents](https://en.wikipedia.org/wiki/Here_document) work with the
+[These documents](https://en.wikipedia.org/wiki/Here_document) work with the
`|` and `>` operators as well. The example below transliterates lower case letters
to upper case:
diff --git a/doc/development/approval_rules.md b/doc/development/approval_rules.md
index 27ba2e79deb..922a0cd46a2 100644
--- a/doc/development/approval_rules.md
+++ b/doc/development/approval_rules.md
@@ -87,7 +87,7 @@ The `ApprovalState` model get these records when approval rules are not
overwritten.
The `protected_branches` attribute is set and used when a rule is scoped to
-protected branches. See [Scoped to Protected Branch doc](../user/project/merge_requests/approvals/rules.md#scoped-to-protected-branch)
+protected branches. See [Approvals for protected branches](../user/project/merge_requests/approvals/rules.md#approvals-for-protected-branches)
for more information about the feature.
### `ApprovalMergeRequestRule`
diff --git a/doc/development/go_guide/index.md b/doc/development/go_guide/index.md
index e8c0c751af9..a4ecf216101 100644
--- a/doc/development/go_guide/index.md
+++ b/doc/development/go_guide/index.md
@@ -449,12 +449,12 @@ Once you've picked a new Go version to use, the steps to update Omnibus and CNG
are:
- [Create a merge request in the CNG project](https://gitlab.com/gitlab-org/build/CNG/-/edit/master/ci_files/variables.yml?branch_name=update-go-version),
- updating the `GO_VERSION` in `ci_files/variables.yml`.
+ update the `GO_VERSION` in `ci_files/variables.yml`.
- [Create a merge request in the `gitlab-omnibus-builder` project](https://gitlab.com/gitlab-org/gitlab-omnibus-builder/-/edit/master/docker/VERSIONS?branch_name=update-go-version),
- updating the `GO_VERSION` in `docker/VERSIONS`.
+ update the `GO_VERSION` in `docker/VERSIONS`.
- Tag a new release of `gitlab-omnibus-builder` containing the change.
- [Create a merge request in the `omnibus-gitlab` project](https://gitlab.com/gitlab-org/omnibus-gitlab/edit/master/.gitlab-ci.yml?branch_name=update-gitlab-omnibus-builder-version),
- updating the `BUILDER_IMAGE_REVISION` to match the newly-created tag.
+ update the `BUILDER_IMAGE_REVISION` to match the newly-created tag.
To reduce unnecessary differences between two distribution methods, Omnibus and
CNG **should always use the same Go version**.
diff --git a/doc/development/multi_version_compatibility.md b/doc/development/multi_version_compatibility.md
index ff831bfa348..8a8026d6e09 100644
--- a/doc/development/multi_version_compatibility.md
+++ b/doc/development/multi_version_compatibility.md
@@ -65,7 +65,7 @@ Let's see how we can handle them safely.
### Route changes
When changing routing we should pay attention to make sure a route generated from the new version can be served by the old one and vice versa.
-As you can see in [an example later on this page](#some-links-to-issues-and-mrs-were-broken), not doing it can lead to an outage.
+As you can see in [this page](#some-links-to-issues-and-mrs-were-broken), not doing it can lead to an outage.
This type of change may look like an immediate switch between the two implementations. However,
especially with the canary stage, there is an extended period of time where both version of the code
coexists in production.
diff --git a/doc/development/snowplow/index.md b/doc/development/snowplow/index.md
index ae0cbd44629..b4bdd88e654 100644
--- a/doc/development/snowplow/index.md
+++ b/doc/development/snowplow/index.md
@@ -131,8 +131,8 @@ _** Property is usually the best place for variable strings._
```sql
SELECT
+ session_id,
event_id,
- v_tracker,
event_label,
event_action,
event_property,
diff --git a/doc/install/google_cloud_platform/index.md b/doc/install/google_cloud_platform/index.md
index 06adde16f80..1b232c361ee 100644
--- a/doc/install/google_cloud_platform/index.md
+++ b/doc/install/google_cloud_platform/index.md
@@ -130,7 +130,7 @@ GitLab can be configured to authenticate with other OAuth providers, LDAP, SAML,
Kerberos, etc. Here are some documents you might be interested in reading:
- [Omnibus GitLab documentation](https://docs.gitlab.com/omnibus/)
-- [Integration documentation](../../integration/README.md)
+- [Integration documentation](../../integration/index.md)
- [GitLab Pages configuration](../../administration/pages/index.md)
- [GitLab Container Registry configuration](../../administration/packages/container_registry.md)
diff --git a/doc/integration/README.md b/doc/integration/README.md
index c725cc08556..c5274535d98 100644
--- a/doc/integration/README.md
+++ b/doc/integration/README.md
@@ -1,105 +1,8 @@
---
-stage: Create
-group: Ecosystem
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
-comments: false
+redirect_to: 'index.md'
---
-# GitLab integrations **(FREE)**
+This document was moved to [another location](index.md).
-GitLab can be integrated with external services for enhanced functionality.
-
-## Issue trackers
-
-You can use an [external issue tracker](external-issue-tracker.md) at the same time as the GitLab
-issue tracker, or use only the external issue tracker.
-
-## Authentication sources
-
-GitLab can be configured to authenticate access requests with the following authentication sources:
-
-- Enable the [Auth0 OmniAuth](auth0.md) provider.
-- Enable sign in with [Bitbucket](bitbucket.md) accounts.
-- Configure GitLab to sign in using [CAS](cas.md).
-- Integrate with [Kerberos](kerberos.md).
-- Enable sign in via [LDAP](../administration/auth/ldap/index.md).
-- Enable [OAuth2 provider](oauth_provider.md) application creation.
-- Use [OmniAuth](omniauth.md) to enable sign in via Twitter, GitHub, GitLab.com, Google,
- Bitbucket, Facebook, Shibboleth, SAML, Crowd, Azure, or Authentiq ID.
-- Use GitLab as an [OpenID Connect](openid_connect_provider.md) identity provider.
-- Authenticate to [Vault](vault.md) through GitLab OpenID Connect.
-- Configure GitLab as a [SAML](saml.md) 2.0 Service Provider.
-
-## Security enhancements
-
-GitLab can be integrated with the following external services to enhance security:
-
-- [Akismet](akismet.md) helps reduce spam.
-- Google [reCAPTCHA](recaptcha.md) helps verify new users.
-
-GitLab also provides features to improve the security of your own application. For more details see [GitLab Secure](../user/application_security/index.md).
-
-## Security partners
-
-GitLab has integrated with several security partners. For more information, see
-[Security partners integration](security_partners/index.md).
-
-## Continuous integration
-
-GitLab can be integrated with the following external service for continuous integration:
-
-- [Jenkins](jenkins.md) CI.
-
-## Feature enhancements
-
-GitLab can be integrated with the following enhancements:
-
-- Add GitLab actions to [Gmail actions buttons](gmail_action_buttons_for_gitlab.md).
-- Configure [PlantUML](../administration/integration/plantuml.md)
-or [Kroki](../administration/integration/kroki.md) to use diagrams in AsciiDoc and Markdown documents.
-- Attach merge requests to [Trello](trello_power_up.md) cards.
-- Enable integrated code intelligence powered by [Sourcegraph](sourcegraph.md).
-- Add [Elasticsearch](elasticsearch.md) for [Advanced Search](../user/search/advanced_search.md).
-
-## Integrations
-
-Integration with services such as Campfire, Flowdock, Jira, Pivotal Tracker, and Slack are available as [Integrations](../user/project/integrations/overview.md).
-
-## Troubleshooting
-
-### SSL certificate errors
-
-When trying to integrate GitLab with services using self-signed certificates,
-SSL certificate errors can occur in different parts of the application. Sidekiq
-is a common culprit.
-
-There are two approaches you can take to solve this:
-
-1. Add the root certificate to the trusted chain of the OS.
-1. If using Omnibus, you can add the certificate to the GitLab trusted certificates.
-
-**OS main trusted chain**
-
-This [resource](https://manuals.gfi.com/en/kerio/connect/content/server-configuration/ssl-certificates/adding-trusted-root-certificates-to-the-server-1605.html)
-has all the information you need to add a certificate to the main trusted chain.
-
-This [answer](https://superuser.com/questions/437330/how-do-you-add-a-certificate-authority-ca-to-ubuntu)
-at Super User also has relevant information.
-
-**Omnibus Trusted Chain**
-
-[Install the self signed certificate or custom certificate authorities](https://docs.gitlab.com/omnibus/common_installation_problems/README.html#using-self-signed-certificate-or-custom-certificate-authorities)
-in to Omnibus GitLab.
-
-It is enough to concatenate the certificate to the main trusted certificate
-however it may be overwritten during upgrades:
-
-```shell
-cat jira.pem >> /opt/gitlab/embedded/ssl/certs/cacert.pem
-```
-
-After that restart GitLab with:
-
-```shell
-sudo gitlab-ctl restart
-```
+<!-- This redirect file can be deleted after <2021-07-30>. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/integration/index.md b/doc/integration/index.md
new file mode 100644
index 00000000000..c725cc08556
--- /dev/null
+++ b/doc/integration/index.md
@@ -0,0 +1,105 @@
+---
+stage: Create
+group: Ecosystem
+info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
+comments: false
+---
+
+# GitLab integrations **(FREE)**
+
+GitLab can be integrated with external services for enhanced functionality.
+
+## Issue trackers
+
+You can use an [external issue tracker](external-issue-tracker.md) at the same time as the GitLab
+issue tracker, or use only the external issue tracker.
+
+## Authentication sources
+
+GitLab can be configured to authenticate access requests with the following authentication sources:
+
+- Enable the [Auth0 OmniAuth](auth0.md) provider.
+- Enable sign in with [Bitbucket](bitbucket.md) accounts.
+- Configure GitLab to sign in using [CAS](cas.md).
+- Integrate with [Kerberos](kerberos.md).
+- Enable sign in via [LDAP](../administration/auth/ldap/index.md).
+- Enable [OAuth2 provider](oauth_provider.md) application creation.
+- Use [OmniAuth](omniauth.md) to enable sign in via Twitter, GitHub, GitLab.com, Google,
+ Bitbucket, Facebook, Shibboleth, SAML, Crowd, Azure, or Authentiq ID.
+- Use GitLab as an [OpenID Connect](openid_connect_provider.md) identity provider.
+- Authenticate to [Vault](vault.md) through GitLab OpenID Connect.
+- Configure GitLab as a [SAML](saml.md) 2.0 Service Provider.
+
+## Security enhancements
+
+GitLab can be integrated with the following external services to enhance security:
+
+- [Akismet](akismet.md) helps reduce spam.
+- Google [reCAPTCHA](recaptcha.md) helps verify new users.
+
+GitLab also provides features to improve the security of your own application. For more details see [GitLab Secure](../user/application_security/index.md).
+
+## Security partners
+
+GitLab has integrated with several security partners. For more information, see
+[Security partners integration](security_partners/index.md).
+
+## Continuous integration
+
+GitLab can be integrated with the following external service for continuous integration:
+
+- [Jenkins](jenkins.md) CI.
+
+## Feature enhancements
+
+GitLab can be integrated with the following enhancements:
+
+- Add GitLab actions to [Gmail actions buttons](gmail_action_buttons_for_gitlab.md).
+- Configure [PlantUML](../administration/integration/plantuml.md)
+or [Kroki](../administration/integration/kroki.md) to use diagrams in AsciiDoc and Markdown documents.
+- Attach merge requests to [Trello](trello_power_up.md) cards.
+- Enable integrated code intelligence powered by [Sourcegraph](sourcegraph.md).
+- Add [Elasticsearch](elasticsearch.md) for [Advanced Search](../user/search/advanced_search.md).
+
+## Integrations
+
+Integration with services such as Campfire, Flowdock, Jira, Pivotal Tracker, and Slack are available as [Integrations](../user/project/integrations/overview.md).
+
+## Troubleshooting
+
+### SSL certificate errors
+
+When trying to integrate GitLab with services using self-signed certificates,
+SSL certificate errors can occur in different parts of the application. Sidekiq
+is a common culprit.
+
+There are two approaches you can take to solve this:
+
+1. Add the root certificate to the trusted chain of the OS.
+1. If using Omnibus, you can add the certificate to the GitLab trusted certificates.
+
+**OS main trusted chain**
+
+This [resource](https://manuals.gfi.com/en/kerio/connect/content/server-configuration/ssl-certificates/adding-trusted-root-certificates-to-the-server-1605.html)
+has all the information you need to add a certificate to the main trusted chain.
+
+This [answer](https://superuser.com/questions/437330/how-do-you-add-a-certificate-authority-ca-to-ubuntu)
+at Super User also has relevant information.
+
+**Omnibus Trusted Chain**
+
+[Install the self signed certificate or custom certificate authorities](https://docs.gitlab.com/omnibus/common_installation_problems/README.html#using-self-signed-certificate-or-custom-certificate-authorities)
+in to Omnibus GitLab.
+
+It is enough to concatenate the certificate to the main trusted certificate
+however it may be overwritten during upgrades:
+
+```shell
+cat jira.pem >> /opt/gitlab/embedded/ssl/certs/cacert.pem
+```
+
+After that restart GitLab with:
+
+```shell
+sudo gitlab-ctl restart
+```
diff --git a/doc/integration/jira/img/jira_merge_request_close.png b/doc/integration/jira/img/jira_merge_request_close.png
deleted file mode 100644
index 9a176daf5f4..00000000000
--- a/doc/integration/jira/img/jira_merge_request_close.png
+++ /dev/null
Binary files differ
diff --git a/doc/integration/jira/issues.md b/doc/integration/jira/issues.md
index d2cab59742b..1f2ab3d79dc 100644
--- a/doc/integration/jira/issues.md
+++ b/doc/integration/jira/issues.md
@@ -6,168 +6,157 @@ info: To determine the technical writer assigned to the Stage/Group associated w
# Jira integration issue management **(FREE)**
-By now you should have [configured Jira](development_panel.md#configuration) and enabled the
-[Jira service in GitLab](development_panel.md#configure-gitlab). If everything is set up correctly
-you should be able to reference and close Jira issues by just mentioning their
-ID in GitLab commits and merge requests.
+Integrating issue management with Jira requires you to [configure Jira](development_panel.md#configuration)
+and [enable the Jira service](development_panel.md#configure-gitlab) in GitLab.
+After you configure and enable the integration, you can reference and close Jira
+issues by mentioning the Jira ID in GitLab commits and merge requests.
-Jira issue IDs must be formatted in uppercase for the integration to work.
+The Jira integration requires to you format any Jira issue IDs in uppercase.
## Reference Jira issues
-When GitLab project has Jira issue tracker configured and enabled, mentioning
-Jira issues in GitLab automatically adds a comment in Jira issue with the
-link back to GitLab. This means that in comments in merge requests and commits
-referencing an issue, `PROJECT-7` for example, adds a comment in Jira issue in the
-format:
+With this integration, you can cross-reference Jira issues while you work in
+GitLab issues and merge requests. Mention a Jira issue in a GitLab issue,
+merge request, or comment, and GitLab adds a formatted comment to the Jira issue.
+The comment links back to your work in GitLab.
-```plaintext
-USER mentioned this issue in RESOURCE_NAME of [PROJECT_NAME|LINK_TO_COMMENT]:
-ENTITY_TITLE
+For example, this commit references the Jira issue `GIT-1`:
+
+```shell
+git commit -m "GIT-1 this is a test commit"
```
-- `USER` A user that mentioned the issue. This is the link to the user profile in GitLab.
-- `LINK_TO_THE_COMMENT` Link to the origin of mention with a name of the entity where Jira issue was mentioned.
-- `RESOURCE_NAME` Kind of resource which referenced the issue. Can be a commit or merge request.
-- `PROJECT_NAME` GitLab project name.
-- `ENTITY_TITLE` Merge request title or commit message first line.
+GitLab adds a reference to the **Issue Links** section of Jira issue `GIT-1`:
-![example of mentioning or closing the Jira issue](img/jira_issue_reference.png)
+![Example of mentioning or closing the Jira issue](img/jira_issue_reference.png)
-For example, the following commit references the Jira issue with `PROJECT-1` as its ID:
+GitLab also adds a comment to the issue, and uses this format:
-```shell
-git commit -m "PROJECT-1 Fix spelling and grammar"
+```plaintext
+USER mentioned this issue in RESOURCE_NAME of [PROJECT_NAME|COMMENTLINK]:
+ENTITY_TITLE
```
-## Close Jira issues
+- `USER`: The name of the user who mentioned the issue, linked to their GitLab user profile.
+- `COMMENTLINK`: A link to where the Jira issue was mentioned.
+- `RESOURCE_NAME`: The type of resource, such as a commit or merge request, which referenced the issue.
+- `PROJECT_NAME`: The GitLab project name.
+- `ENTITY_TITLE`: The title of the merge request, or the first line of the commit.
-Jira issues can be closed directly from GitLab by using trigger words in
-commits and merge requests. When a commit which contains the trigger word
-followed by the Jira issue ID in the commit message is pushed, GitLab
-adds a comment in the mentioned Jira issue and immediately closes it (provided
-the transition ID was set up correctly).
+You can [disable comments](#disable-comments-on-jira-issues) on issues.
-There are currently three trigger words, and you can use either one to achieve
-the same goal:
+## Close Jira issues in GitLab
-- `Resolves PROJECT-1`
-- `Closes PROJECT-1`
-- `Fixes PROJECT-1`
-
-where `PROJECT-1` is the ID of the Jira issue.
+If you have configured GitLab transition IDs, you can close a Jira issue directly
+from GitLab. Use a trigger word followed by a Jira issue ID in a commit or merge request.
+When you push a commit containing a trigger word and Jira issue ID, GitLab:
-Note the following:
+1. Comments in the mentioned Jira issue.
+1. Closes the Jira issue. If the Jira issue has a resolution, it isn't transitioned.
-- Only commits and merges into the project's default branch (usually `master`)
- close an issue in Jira. You can change your project's default branch under
- [project settings](img/jira_project_settings.png).
-- The Jira issue is not transitioned if it has a resolution.
+For example, use any of these trigger words to close the Jira issue `PROJECT-1`:
-Let's consider the following example:
-
-1. For the project named `PROJECT` in Jira, we implemented a new feature
- and created a merge request in GitLab.
-1. This feature was requested in Jira issue `PROJECT-7` and the merge request
- in GitLab contains the improvement
-1. In the merge request description we use the issue closing trigger
- `Closes PROJECT-7`.
-1. Once the merge request is merged, the Jira issue is automatically closed
- with a comment and an associated link to the commit that resolved the issue.
+- `Resolves PROJECT-1`
+- `Closes PROJECT-1`
+- `Fixes PROJECT-1`
-In the following screenshot you can see what the link references to the Jira
-issue look like.
+The commit or merge request must target your project's [default branch](../../user/project/repository/branches/default.md).
+You can change your project's default branch under [project settings](img/jira_project_settings.png).
-![A Git commit that causes the Jira issue to be closed](img/jira_merge_request_close.png)
+### Use case for closing issues
-Once this merge request is merged, the Jira issue is automatically closed
-with a link to the commit that resolved the issue.
+Consider this example:
-![The GitLab integration closes Jira issue](img/jira_service_close_issue.png)
+1. A user creates Jira issue `PROJECT-7` to request a new feature.
+1. You create a merge request in GitLab to build the requested feature.
+1. In the merge request, you add the issue closing trigger `Closes PROJECT-7`.
+1. When the merge request is merged:
+ - GitLab closes the Jira issue for you:
+ ![The GitLab integration closes Jira issue](img/jira_service_close_issue.png)
+ - GitLab adds a formatted comment to Jira, linking back to the commit that
+ resolved the issue. You can [disable comments](#disable-comments-on-jira-issues).
## View Jira issues **(PREMIUM)**
> [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/3622) in [GitLab Premium](https://about.gitlab.com/pricing/) 13.2.
You can browse, search, and view issues from a selected Jira project directly in GitLab,
-if your GitLab administrator [has configured it](development_panel.md#configure-gitlab):
-
-1. In the left navigation bar, go to **Jira > Issues list**.
-1. The issue list sorts by **Created date** by default, with the newest issues listed at the top:
-
- ![Jira issues integration enabled](img/open_jira_issues_list_v13.2.png)
+if your GitLab administrator [has configured it](development_panel.md#configure-gitlab).
-1. To display the most recently updated issues first, click **Last updated**.
-1. In GitLab versions 13.10 and later, you can view [individual Jira issues](#view-a-jira-issue).
+To do this, in GitLab, go to your project and select **Jira > Issues list**. The issue list
+sorts by **Created date** by default, with the newest issues listed at the top:
-Issues are grouped into tabs based on their [Jira status](https://confluence.atlassian.com/adminjiraserver070/defining-status-field-values-749382903.html):
+![Jira issues integration enabled](img/open_jira_issues_list_v13.2.png)
-- The **Open** tab displays all issues with a Jira status in any category other than Done.
-- The **Closed** tab displays all issues with a Jira status categorized as Done.
-- The **All** tab displays all issues of any status.
+- To display the most recently updated issues first, select **Last updated**.
+- You can [search and filter](#search-and-filter-the-issues-list) the issues list.
+- In GitLab [versions 13.10 and later](https://gitlab.com/gitlab-org/gitlab/-/issues/299832),
+ you can select an issue from the list to view it in GitLab:
+ ![Jira issue detail view](img/jira_issue_detail_view_v13.10.png)
-## View a Jira issue
+Issues are grouped into tabs based on their
+[Jira status](https://confluence.atlassian.com/adminjiraserver070/defining-status-field-values-749382903.html):
-> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/299832) in [GitLab Premium](https://about.gitlab.com/pricing/) 13.10 behind a feature flag, disabled by default.
-> - [Feature flag removed](https://gitlab.com/gitlab-org/gitlab/-/issues/299832) in [GitLab Premium](https://about.gitlab.com/pricing/) 13.11.
-
-When viewing the [Jira issues list](#view-jira-issues), select an issue from the
-list to open it in GitLab:
-
-![Jira issue detail view](img/jira_issue_detail_view_v13.10.png)
+- **Open** tab: All issues with a Jira status in any category other than Done.
+- **Closed** tab: All issues with a Jira status categorized as Done.
+- **All** tab: All issues of any status.
## Search and filter the issues list
To refine the list of issues, use the search bar to search for any text
-contained in an issue summary (title) or description.
-
-You can also filter by labels, status, reporter, and assignee using URL parameters.
-Enhancements to be able to use these through the user interface are [planned](https://gitlab.com/groups/gitlab-org/-/epics/3622).
+contained in an issue summary (title) or description. Use any combination
+of these filters:
- To filter issues by `labels`, specify one or more labels as part of the `labels[]`
-parameter in the URL. When using multiple labels, only issues that contain all specified
-labels are listed. `/-/integrations/jira/issues?labels[]=backend&labels[]=feature&labels[]=QA`
-
-- To filter issues by `status`, specify the `status` parameter in the URL.
-`/-/integrations/jira/issues?status=In Progress`
-
+ parameter in the URL. When using multiple labels, only issues that contain all specified
+ labels are listed: `/-/integrations/jira/issues?labels[]=backend&labels[]=feature&labels[]=QA`
+- To filter issues by `status`, specify the `status` parameter in the URL:
+ `/-/integrations/jira/issues?status=In Progress`
- To filter issues by `reporter`, specify a reporter's Jira display name for the
-`author_username` parameter in the URL. `/-/integrations/jira/issues?author_username=John Smith`
-
+ `author_username` parameter in the URL: `/-/integrations/jira/issues?author_username=John Smith`
- To filter issues by `assignee`, specify their Jira display name for the
-`assignee_username` parameter in the URL. `/-/integrations/jira/issues?assignee_username=John Smith`
+ `assignee_username` parameter in the URL: `/-/integrations/jira/issues?assignee_username=John Smith`
+
+Enhancements to use these filters through the user interface
+[are planned](https://gitlab.com/groups/gitlab-org/-/epics/3622).
## Automatic issue transitions
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/...) in GitLab 13.10.
-In this mode the referenced Jira issue is transitioned to the next available status with a category of "Done".
+When you configure automatic issue transitions, you can transition a referenced
+Jira issue to the next available status with a category of **Done**. To configure
+this setting:
-See the [Configure GitLab](development_panel.md#configure-gitlab) section, check the **Enable Jira transitions** setting and select the **Move to Done** option.
+1. Refer to the [Configure GitLab](development_panel.md#configure-gitlab) instructions.
+1. Select the **Enable Jira transitions** check box.
+1. Select the **Move to Done** option.
## Custom issue transitions
-For advanced workflows you can specify custom Jira transition IDs.
-
-See the [Configure GitLab](development_panel.md#configure-gitlab) section, check the **Enable Jira transitions** setting, select the **Custom transitions** option, and enter your transition IDs in the text field.
-
-If you insert multiple transition IDs separated by `,` or `;`, the issue is moved to each state, one after another, using the given order. If a transition fails the sequence is aborted.
-
-To see the transition IDs on Jira Cloud, edit a workflow in the **Text** view.
-The transition IDs display in the **Transitions** column.
-
-On Jira Server you can get the transition IDs in either of the following ways:
-
-1. By using the API, with a request like `https://yourcompany.atlassian.net/rest/api/2/issue/ISSUE-123/transitions`
- using an issue that is in the appropriate "open" state
-1. By mousing over the link for the transition you want and looking for the
- "action" parameter in the URL
-
-Note that the transition ID may vary between workflows (for example, bug vs. story),
-even if the status you are changing to is the same.
-
-## Disabling comments on Jira issues
-
-You can continue to have GitLab cross-link a source commit/MR with a Jira issue while disabling the comment added to the issue.
-
-See the [Configure GitLab](development_panel.md#configure-gitlab) section and uncheck the **Enable comments** setting.
+For advanced workflows, you can specify custom Jira transition IDs:
+
+1. Use the method based on your Jira subscription status:
+ - *(For users of Jira Cloud)* Obtain your transition IDs by editing a workflow
+ in the **Text** view. The transition IDs display in the **Transitions** column.
+ - *(For users of Jira Server)* Obtain your transition IDs in one of these ways:
+ - By using the API, with a request like `https://yourcompany.atlassian.net/rest/api/2/issue/ISSUE-123/transitions`,
+ using an issue that is in the appropriate "open" state.
+ - By mousing over the link for the transition you want and looking for the
+ **action** parameter in the URL.
+ The transition ID may vary between workflows (for example, a bug instead of a
+ story), even if the status you're changing to is the same.
+1. Refer to the [Configure GitLab](development_panel.md#configure-gitlab) instructions.
+1. Select the **Enable Jira transitions** setting.
+1. Select the **Custom transitions** option.
+1. Enter your transition IDs in the text field. If you insert multiple transition IDs
+ (separated by `,` or `;`), the issue is moved to each state, one after another, in the
+ order you specify. If a transition fails, the sequence is aborted.
+
+## Disable comments on Jira issues
+
+GitLab can cross-link source commits or merge requests with Jira issues without
+adding a comment to the Jira issue:
+
+1. Refer to the [Configure GitLab](development_panel.md#configure-gitlab) instructions.
+1. Clear the **Enable comments** check box.
diff --git a/doc/user/application_security/index.md b/doc/user/application_security/index.md
index ce12e8a6a9b..b552a3f9a1f 100644
--- a/doc/user/application_security/index.md
+++ b/doc/user/application_security/index.md
@@ -156,7 +156,7 @@ rating.
### Enabling Security Approvals within a project
-To enable the `Vulnerability-Check` or `License-Check` Security Approvals, a [project approval rule](../project/merge_requests/approvals/rules.md#adding--editing-a-default-approval-rule)
+To enable the `Vulnerability-Check` or `License-Check` Security Approvals, a [project approval rule](../project/merge_requests/approvals/rules.md#add-an-approval-rule)
must be created. A [security scanner job](#security-scanning-tools) must be enabled for
`Vulnerability-Check`, and a [license scanning](../compliance/license_compliance/index.md#configuration)
job must be enabled for `License-Check`. When the proper jobs aren't configured, the following
diff --git a/doc/user/index.md b/doc/user/index.md
index 9546b48ec0f..ab159cecdef 100644
--- a/doc/user/index.md
+++ b/doc/user/index.md
@@ -178,7 +178,7 @@ pages and accomplish tasks faster.
## Integrations
-[Integrate GitLab](../integration/README.md) with your preferred tool,
+[Integrate GitLab](../integration/index.md) with your preferred tool,
such as Trello, Jira, etc.
## Webhooks
diff --git a/doc/user/project/merge_requests/approvals/index.md b/doc/user/project/merge_requests/approvals/index.md
index 1b157f9a93f..aa668e85156 100644
--- a/doc/user/project/merge_requests/approvals/index.md
+++ b/doc/user/project/merge_requests/approvals/index.md
@@ -67,7 +67,7 @@ in your project's settings.
If you enable [approval rule overrides](settings.md#prevent-overriding-default-approvals),
merge requests created before a change to default approval rules are not affected.
-The only exceptions are changes to the [target branch](rules.md#scoped-to-protected-branch)
+The only exceptions are changes to the [target branch](rules.md#approvals-for-protected-branches)
of the rule.
## Optional approvals
@@ -117,6 +117,11 @@ You can modify your external approval rules
The lack of an external approval doesn't block the merging of a merge request.
+When [approval rule overrides](settings.md#prevent-overriding-default-approvals) are allowed,
+changes to default approval rules will **not** be applied to existing
+merge requests, except for changes to the [target branch](rules.md#approvals-for-protected-branches)
+of the rule.
+
To learn more about use cases, feature discovery, and development timelines,
see the [External API approval rules epic](https://gitlab.com/groups/gitlab-org/-/epics/3869).
diff --git a/doc/user/project/merge_requests/approvals/rules.md b/doc/user/project/merge_requests/approvals/rules.md
index 0105ff23fff..3e40936f887 100644
--- a/doc/user/project/merge_requests/approvals/rules.md
+++ b/doc/user/project/merge_requests/approvals/rules.md
@@ -7,201 +7,226 @@ type: reference, concepts
# Merge request approval rules
-Approval rules define how many approvals a merge request must receive before it can
-be merged, and optionally which users should do the approving. Approvals can be defined:
+Approval rules define how many [approvals](index.md) a merge request must receive before it can
+be merged, and which users should do the approving. You can define approval rules:
-- [As project defaults](#adding--editing-a-default-approval-rule).
-- [Per merge request](#editing--overriding-approval-rules-per-merge-request).
+- [As project defaults](#add-an-approval-rule).
+- [Per merge request](#edit-or-override-merge-request-approval-rules).
+- [At the instance level](../../../admin_area/merge_requests_approvals.md)
-If no approval rules are defined, any user can approve a merge request. However, the default
-minimum number of required approvers can still be set in the
-[settings for merge request approvals](settings.md).
+If you don't define a [default approval rule](#add-an-approval-rule),
+any user can approve a merge request. Even if you don't define a rule, you can still
+enforce a [minimum number of required approvers](settings.md) in the project's settings.
-You can opt to define one single rule to approve a merge request among the available rules
-or choose more than one with [multiple approval rules](#multiple-approval-rules).
+You can define a single rule to approve merge requests from among the available
+rules, or you can select [multiple approval rules](#add-multiple-approval-rules).
-NOTE:
-On GitLab.com, you can add a group as an approver if you're a member of that group or the
-group is public.
+Merge requests that target a different project, such as from a fork to the upstream project,
+use the default approval rules from the target (upstream) project, not the source (fork).
-## Eligible Approvers
+## Add an approval rule
-> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/10294) in GitLab 13.3, when an eligible approver comments on a merge request, it appears in the **Commented by** column of the Approvals widget.
+To add a merge request approval rule:
-The following users can approve merge requests:
+1. Go to your project and select **Settings > General**.
+1. Expand **Merge request (MR) approvals**, and then select **Add approval rule**.
+1. Add a human-readable **Rule name**.
+1. Set the number of required approvals in **Approvals required**. A value of `0` makes
+ [the rule optional](#configure-optional-approval-rules), and any number greater than `0`
+ creates a required rule.
+1. To add users or groups as approvers, search for users or groups that are
+ [eligible to approve](#eligible-approvers), and select **Add**. GitLab suggests approvers based on
+ previous authors of the files changed by the merge request.
-- Users who have been added as approvers at the project or merge request levels with
- developer or higher [permissions](../../../permissions.md).
-- [Code owners](#code-owners-as-eligible-approvers) of the files changed by the merge request
- that have developer or higher [permissions](../../../permissions.md).
+ NOTE:
+ On GitLab.com, you can add a group as an approver if you're a member of that group or the
+ group is public.
-An individual user can be added as an approver for a project if they are a member of:
+1. Select **Add approval rule**.
-- The project.
-- The project's immediate parent group.
-- A group that has access to the project via a [share](../../members/share_project_with_groups.md).
+Users of GitLab Premium and higher tiers can create [additional approval rules](#add-multiple-approval-rules).
-A group of users can also be added as approvers, though they only count as approvers if
-they have direct membership to the group. In the future, group approvers may be
-[restricted to only groups with share access to the project](https://gitlab.com/gitlab-org/gitlab/-/issues/2048).
+Your configuration for approval rule overrides determines if the new rule is applied
+to existing merge requests:
-If a user is added as an individual approver and is also part of a group approver,
-then that user is just counted once. The merge request author, and users who have committed
-to the merge request, do not count as eligible approvers,
-if [**Prevent author approval**](settings.md#allowing-merge-request-authors-to-approve-their-own-merge-requests) (enabled by default)
-and [**Prevent committers approval**](settings.md#prevent-approval-of-merge-requests-by-their-committers) (disabled by default)
-are enabled on the project settings.
+- If [approval rule overrides](settings.md#prevent-overriding-default-approvals) are allowed,
+ changes to these default rules are not applied to existing merge requests, except for
+ changes to the [target branch](#approvals-for-protected-branches) of the rule.
+- If approval rule overrides are not allowed, all changes to default rules
+ are applied to existing merge requests. Any approval rules that were previously
+ manually [overridden](#edit-or-override-merge-request-approval-rules) during the
+ period when approval rule overrides where allowed, are not modified.
-When an eligible approver comments on a merge request, it displays in the
-**Commented by** column of the Approvals widget. It indicates who participated in
-the merge request review. Authors and reviewers can also identify who they should reach out
-to if they have any questions about the content of the merge request.
+## Edit an approval rule
-### Implicit Approvers
+To edit a merge request approval rule:
-If the number of required approvals is greater than the number of assigned approvers,
-approvals from other users counts towards meeting the requirement. These would be
-users with developer [permissions](../../../permissions.md) or higher in the project who
-were not explicitly listed in the approval rules.
+1. Go to your project and select **Settings > General**.
+1. Expand **Merge request (MR) approvals**, and then select **Edit**.
+1. (Optional) Change the **Rule name**.
+1. Set the number of required approvals in **Approvals required**. The minimum value is `0`.
+1. Add or remove eligible approvers, as needed:
+ - *To add users or groups as approvers,* search for users or groups that are
+ [eligible to approve](#eligible-approvers), and select **Add**.
-### Code Owners as eligible approvers
+ NOTE:
+ On GitLab.com, you can add a group as an approver if you're a member of that group or the
+ group is public.
-> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/7933) in GitLab 11.5.
-> - Moved to GitLab Premium in 13.9.
+ - *To remove users or groups,* identify the group or user to remove, and
+ select **{remove}** **Remove**.
+1. Select **Update approval rule**.
-If you add [Code Owners](../../code_owners.md) to your repository, the owners to the
-corresponding files become eligible approvers, together with members with Developer
-or higher [permissions](../../../permissions.md).
+## Add multiple approval rules **(PREMIUM)**
-To enable this merge request approval rule:
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/1979) in GitLab Premium 11.10.
-1. Navigate to your project's **Settings > General** and expand
- **Merge request (MR) approvals**.
-1. Locate **Any eligible user** and choose the number of approvals required.
+In GitLab Premium and higher tiers, you can enforce multiple approval rules on a
+merge request, and multiple default approval rules for a project. If your tier
+supports multiple default rules:
-![MR approvals by Code Owners](img/mr_approvals_by_code_owners_v12_7.png)
+- When [adding](#add-an-approval-rule) or [editing](#edit-an-approval-rule) an approval rule
+ for a project, GitLab displays the **Add approval rule** button even after a rule is defined.
+- When editing or overriding multiple approval rules
+ [on a merge request](#edit-or-override-merge-request-approval-rules), GitLab
+ displays the **Add approval rule** button even after a rule is defined.
-Once set, merge requests can only be merged once approved by the
-number of approvals you've set. GitLab accepts approvals from
-users with Developer or higher permissions, as well as by Code Owners,
-indistinguishably.
+When an [eligible approver](#eligible-approvers) approves a merge request, it
+reduces the number of approvals left (the **Approvals** column) for all rules that the approver belongs to:
-Alternatively, you can **require**
-[Code Owner's approvals for protected branches](../../protected_branches.md#protected-branches-approval-by-code-owners). **(PREMIUM)**
+![Approvals premium merge request widget](img/approvals_premium_mr_widget_v13_3.png)
-## Merge Request approval segregation of duties
+## Eligible approvers
-> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/40491) in GitLab 13.4.
-> - Moved to Premium in 13.9.
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/10294) in GitLab 13.3, when an eligible approver comments on a merge request, it appears in the **Commented by** column of the Approvals widget.
-Managers or operators with [Reporter permissions](../../../permissions.md#project-members-permissions)
-to a project sometimes need to be required approvers of a merge request,
-before a merge to a protected branch begins. These approvers aren't allowed
-to push or merge code to any branches.
+To be eligible as an approver for a project, a user must be a member of one or
+more of these:
-To enable this access:
+- The project.
+- The project's immediate parent [group](#group-approvers).
+- A group that has access to the project via a [share](../../members/share_project_with_groups.md).
+- A [group added as approvers](#group-approvers).
-1. [Create a new group](../../../group/index.md#create-a-group), and then
- [add the user to the group](../../../group/index.md#add-users-to-a-group),
- ensuring you select the Reporter role for the user.
-1. [Share the project with your group](../../members/share_project_with_groups.md#sharing-a-project-with-a-group-of-users),
- based on the Reporter role.
-1. Navigate to your project's **Settings > General**, and in the
- **Merge request (MR) approvals** section, click **Expand**.
-1. Select **Add approval rule** or **Update approval rule**.
-1. [Add the group](../../../group/index.md#create-a-group) to the permission list.
+The following users can approve merge requests if they have Developer or
+higher [permissions](../../../permissions.md):
-![Update approval rule](img/update_approval_rule_v13_10.png)
+- Users added as approvers at the project or merge request level.
+- Users who are [Code owners](#code-owners-as-eligible-approvers) of the files
+ changed in the merge request.
-## Adding / editing a default approval rule
+To show who has participated in the merge request review, the Approvals widget in
+a merge request displays a **Commented by** column. This column lists eligible approvers
+who commented on the merge request. It helps authors and reviewers identify who to
+contact with questions about the merge request's content.
-To add or edit the default merge request approval rule:
+If the number of required approvals is greater than the number of assigned approvers,
+approvals from other users with Developer [permissions](../../../permissions.md) or higher
+in the project counts toward meeting the required number of approvals, even if the
+users were not explicitly listed in the approval rules.
-1. Navigate to your project's **Settings > General** and expand **Merge request (MR) approvals**.
+### Group approvers
-1. Click **Add approval rule**, or **Edit**.
- - Add or change the **Rule name**.
- - Set the number of required approvals in **Approvals required**. The minimum value is `0`.
- - (Optional) Search for users or groups that are [eligible to approve](#eligible-approvers)
- merge requests and click the **Add** button to add them as approvers. Before typing
- in the search field, approvers are suggested based on the previous authors of
- the files being changed by the merge request.
- - (Optional) Click the **{remove}** **Remove** button next to a group or user to delete it from
- the rule.
-1. Click **Add approval rule** or **Update approval rule**.
+You can add a group of users as approvers, but those users count as approvers only if
+they have direct membership to the group. In the future, group approvers may be
+restricted to only groups [with share access to the project](https://gitlab.com/gitlab-org/gitlab/-/issues/2048).
-When [approval rule overrides](settings.md#prevent-overriding-default-approvals) are allowed,
-changes to these default rules are not applied to existing merge
-requests, except for changes to the [target branch](#scoped-to-protected-branch) of
-the rule.
+A user's membership in an approvers group affects their individual ability to
+approve in these ways:
-When approval rule overrides are not allowed, all changes to these default rules
-are applied to existing merge requests. Any approval rules that had previously been
-manually [overridden](#editing--overriding-approval-rules-per-merge-request) during a
-period when approval rule overrides where allowed, are not modified.
+- A user already part of a group approver who is later added as an individual approver
+ counts as one approver, and not two.
+- Merge request authors do not count as eligible approvers on their own merge requests by default.
+ To change this behavior, disable the
+ [**Prevent author approval**](settings.md#allowing-merge-request-authors-to-approve-their-own-merge-requests)
+ project setting.
+- Committers to merge requests can approve a merge request. To change this behavior, enable the
+ [**Prevent committers approval**](settings.md#prevent-approval-of-merge-requests-by-their-committers)
+ project setting.
-NOTE:
-If a merge request targets a different project, such as from a fork to the upstream project,
-the default approval rules are taken from the target (upstream) project, not the
-source (fork).
+### Code owners as eligible approvers
-### Editing / overriding approval rules per merge request
+> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/7933) in GitLab 11.5.
+> - Moved to GitLab Premium in 13.9.
-> Introduced in GitLab Enterprise Edition 9.4.
+If you add [code owners](../../code_owners.md) to your repository, the owners of files
+become eligible approvers in the project. To enable this merge request approval rule:
-By default, the merge request approval rule listed in each merge request (MR) can be
-edited by the MR author or a user with sufficient [permissions](../../../permissions.md).
-This ability can be disabled in the [merge request approvals settings](settings.md#prevent-overriding-default-approvals).
+1. Go to your project and select **Settings > General**.
+1. Expand **Merge request (MR) approvals**.
+1. Locate **Any eligible user** and select the number of approvals required:
-One possible scenario would be to add more approvers than were defined in the default
-settings.
+ ![MR approvals by Code Owners](img/mr_approvals_by_code_owners_v12_7.png)
-When creating or editing a merge request, find the **Approval rules** section, then follow
-the same steps as [Adding / editing a default approval rule](#adding--editing-a-default-approval-rule).
+You can also
+[require code owner approval](../../protected_branches.md#protected-branches-approval-by-code-owners)
+for protected branches. **(PREMIUM)**
-## Set up an optional approval rule
+## Merge request approval segregation of duties
-MR approvals can be configured to be optional, which can help if you're working
-on a team where approvals are appreciated, but not required.
+> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/40491) in GitLab 13.4.
+> - Moved to GitLab Premium in 13.9.
-To configure an approval to be optional, set the number of required approvals in **Approvals required** to `0`.
+You may need to grant users with [Reporter permissions](../../../permissions.md#project-members-permissions),
+permission to approve merge requests before they can merge to a protected branch.
+Some users (like managers) may not need permission to push or merge code, but still need
+oversight on proposed work. To enable approval permissions for these users without
+granting them push access:
-You can also set an optional approval rule through the [Merge requests approvals API](../../../../api/merge_request_approvals.md#update-merge-request-level-rule), by setting the `approvals_required` attribute to `0`.
+1. [Create a new group](../../../group/index.md#create-a-group).
+1. [Add the user to the group](../../../group/index.md#add-users-to-a-group),
+ and select the Reporter role for the user.
+1. [Share the project with your group](../../members/share_project_with_groups.md#sharing-a-project-with-a-group-of-users),
+ based on the Reporter role.
+1. Go to your project and select **Settings > General**.
+1. Expand **Merge request (MR) approvals**.
+1. Select **Add approval rule** or **Update approval rule**.
+1. [Add the group](../../../group/index.md#create-a-group) to the permission list.
-## Multiple approval rules **(PREMIUM)**
+ ![Update approval rule](img/update_approval_rule_v13_10.png)
-> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/1979) in [GitLab Premium](https://about.gitlab.com/pricing/) 11.10.
+### Edit or override merge request approval rules
-In GitLab Premium, it is possible to have multiple approval rules per merge request,
-as well as multiple default approval rules per project.
+By default, the merge request author (or a user with sufficient [permissions](../../../permissions.md))
+can edit the approval rule listed in a merge request. When editing an approval rule
+on a merge request, you can either add or remove approvers:
-Adding or editing multiple default rules is identical to
-[adding or editing a single default approval rule](#adding--editing-a-default-approval-rule),
-except the **Add approval rule** button is available to add more rules, even after
-a rule is already defined.
+1. In the merge request, find the **Approval rules section**.
+1. When creating a new merge request, scroll to the **Approval Rules** section,
+ and add or remove your desired approval rules before selecting **Create merge request**.
+1. When viewing an existing merge request:
+ 1. Select **Edit**.
+ 1. Scroll to the **Approval Rules** section.
+ 1. Add or remove your desired approval rules.
+ 1. Select **Save changes**.
-Similarly, editing or overriding multiple approval rules per merge request is identical
-to [editing or overriding approval rules per merge request](#editing--overriding-approval-rules-per-merge-request),
-except the **Add approval rule** button is available to add more rules, even after
-a rule is already defined.
+Administrators can change the [merge request approvals settings](settings.md#prevent-overriding-default-approvals)
+to prevent users from overriding approval rules for merge requests.
-When an [eligible approver](#eligible-approvers) approves a merge request, it
-reduces the number of approvals left for all rules that the approver belongs to.
+## Configure optional approval rules
-![Approvals premium merge request widget](img/approvals_premium_mr_widget_v13_3.png)
+Merge request approvals can be optional for projects where approvals are
+appreciated, but not required. To make an approval rule optional:
-## Scoped to protected branch **(PREMIUM)**
+- When you [create or edit a rule](#edit-an-approval-rule), set **Approvals required** to `0`.
+- Use the [Merge requests approvals API](../../../../api/merge_request_approvals.md#update-merge-request-level-rule)
+ to set the `approvals_required` attribute to `0`.
-> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/460) in [GitLab Premium](https://about.gitlab.com/pricing/) 12.8.
+## Approvals for protected branches **(PREMIUM)**
-Approval rules are often only relevant to specific branches, like `master`.
-When configuring [**Default Approval Rules**](#adding--editing-a-default-approval-rule)
-these can be scoped to all the protected branches at once by navigating to your project's
-**Settings**, expanding **Merge request (MR) approvals**, and selecting **Any branch** from
-the **Target branch** dropdown.
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/460) in GitLab Premium 12.8.
-Alternatively, you can select a very specific protected branch from the **Target branch** dropdown:
+Approval rules are often relevant only to specific branches, like your
+[default branch](../../repository/branches/default.md). To configure an
+approval rule for certain branches:
-![Scoped to protected branch](img/scoped_to_protected_branch_v13_10.png)
+1. [Create an approval rule](#add-an-approval-rule).
+1. Go to your project and select **Settings**.
+1. Expand **Merge request (MR) approvals**.
+1. Select a **Target branch**:
+ - To protect all branches, select **Any branch**.
+ - To select a specific branch, select it from the list:
-To enable this configuration, see [Code Owner's approvals for protected branches](../../protected_branches.md#protected-branches-approval-by-code-owners).
+ ![Scoped to protected branch](img/scoped_to_protected_branch_v13_10.png)
+1. To enable this configuration, read
+ [Code Owner's approvals for protected branches](../../protected_branches.md#protected-branches-approval-by-code-owners).
diff --git a/doc/user/project/merge_requests/approvals/settings.md b/doc/user/project/merge_requests/approvals/settings.md
index 8f44e6d1991..0ca1d462fef 100644
--- a/doc/user/project/merge_requests/approvals/settings.md
+++ b/doc/user/project/merge_requests/approvals/settings.md
@@ -13,7 +13,7 @@ The settings for Merge Request Approvals are found by going to
## Prevent overriding default approvals
Regardless of the approval rules you choose for your project, users can edit them in every merge
-request, overriding the [rules you set as default](rules.md#adding--editing-a-default-approval-rule).
+request, overriding the [rules you set as default](rules.md#add-an-approval-rule).
To prevent that from happening:
1. Select the **Prevent users from modifying MR approval rules in merge requests.** checkbox.
diff --git a/lib/gitlab/patch/prependable.rb b/lib/gitlab/patch/prependable.rb
index dde78cd9178..1ed341e1c26 100644
--- a/lib/gitlab/patch/prependable.rb
+++ b/lib/gitlab/patch/prependable.rb
@@ -21,7 +21,12 @@ module Gitlab
def prepend_features(base)
return false if prepended?(base)
- super
+ # Rails 6.1 allows prepending of the modules, but it doesn't
+ # work well when both modules extend ActiveSupport::Concern
+ # https://github.com/rails/rails/pull/42067
+ #
+ # Let's keep our own implementation, until the issue is fixed
+ Module.instance_method(:prepend_features).bind(self).call(base)
if const_defined?(:ClassMethods)
klass_methods = const_get(:ClassMethods, false)
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 2f324a846d9..894d421b01b 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -31740,6 +31740,9 @@ msgstr ""
msgid "The collection of events added to the data gathered for that stage."
msgstr ""
+msgid "The comment you are editing has been changed by another user. Would you like to keep your changes and overwrite the new description or discard your changes?"
+msgstr ""
+
msgid "The commit does not exist"
msgstr ""
diff --git a/spec/features/admin/users/user_spec.rb b/spec/features/admin/users/user_spec.rb
index befa7bd338b..01341398135 100644
--- a/spec/features/admin/users/user_spec.rb
+++ b/spec/features/admin/users/user_spec.rb
@@ -4,18 +4,16 @@ require 'spec_helper'
RSpec.describe 'Admin::Users::User' do
let_it_be(:user) { create(:omniauth_user, provider: 'twitter', extern_uid: '123456') }
- let_it_be(:current_user) { create(:admin, last_activity_on: 5.days.ago) }
+ let_it_be(:current_user) { create(:admin) }
before do
sign_in(current_user)
gitlab_enable_admin_mode_sign_in(current_user)
- stub_feature_flags(vue_admin_users: false)
end
describe 'GET /admin/users/:id' do
it 'has user info', :aggregate_failures do
- visit admin_users_path
- click_link user.name
+ visit admin_user_path(user)
expect(page).to have_content(user.email)
expect(page).to have_content(user.name)
@@ -27,21 +25,6 @@ RSpec.describe 'Admin::Users::User' do
expect(page).to have_button('Delete user and contributions')
end
- context 'user pending approval' do
- it 'shows user info', :aggregate_failures do
- user = create(:user, :blocked_pending_approval)
-
- visit admin_users_path
- click_link 'Pending approval'
- click_link user.name
-
- expect(page).to have_content(user.name)
- expect(page).to have_content('Pending approval')
- expect(page).to have_link('Approve user')
- expect(page).to have_link('Reject request')
- end
- end
-
context 'when blocking/unblocking the user' do
it 'shows confirmation and allows blocking and unblocking', :js do
visit admin_user_path(user)
@@ -171,6 +154,8 @@ RSpec.describe 'Admin::Users::User' do
it 'logs in as the user when impersonate is clicked' do
subject
+ find('[data-qa-selector="user_menu"]').click
+
expect(page.find(:css, '[data-testid="user-profile-link"]')['data-user']).to eql(another_user.username)
end
@@ -205,6 +190,8 @@ RSpec.describe 'Admin::Users::User' do
it 'logs out of impersonated user back to original user' do
subject
+ find('[data-qa-selector="user_menu"]').click
+
expect(page.find(:css, '[data-testid="user-profile-link"]')['data-user']).to eq(current_user.username)
end
@@ -238,6 +225,8 @@ RSpec.describe 'Admin::Users::User' do
end
it 'shows when disabled' do
+ user.update!(otp_required_for_login: false)
+
visit admin_user_path(user)
expect_two_factor_status('Disabled')
@@ -251,7 +240,7 @@ RSpec.describe 'Admin::Users::User' do
end
describe 'Email verification status' do
- let!(:secondary_email) do
+ let_it_be(:secondary_email) do
create :email, email: 'secondary@example.com', user: user
end
@@ -274,99 +263,121 @@ RSpec.describe 'Admin::Users::User' do
expect(page).to have_content("#{secondary_email.email} Verified")
end
end
- end
-
- describe 'show user attributes' do
- it 'has expected attributes', :aggregate_failures do
- visit admin_users_path
- click_link user.name
+ describe 'show user identities' do
+ it 'shows user identities', :aggregate_failures do
+ visit admin_user_identities_path(user)
- expect(page).to have_content 'Account'
- expect(page).to have_content 'Personal projects limit'
+ expect(page).to have_content(user.name)
+ expect(page).to have_content('twitter')
+ end
end
- end
- describe 'remove users secondary email', :js do
- let!(:secondary_email) do
- create :email, email: 'secondary@example.com', user: user
+ describe 'update user identities' do
+ before do
+ allow(Gitlab::Auth::OAuth::Provider).to receive(:providers).and_return([:twitter, :twitter_updated])
+ end
+
+ it 'modifies twitter identity', :aggregate_failures do
+ visit admin_user_identities_path(user)
+
+ find('.table').find(:link, 'Edit').click
+ fill_in 'identity_extern_uid', with: '654321'
+ select 'twitter_updated', from: 'identity_provider'
+ click_button 'Save changes'
+
+ expect(page).to have_content(user.name)
+ expect(page).to have_content('twitter_updated')
+ expect(page).to have_content('654321')
+ end
end
- it do
- visit admin_user_path(user.username)
+ describe 'remove users secondary email', :js do
+ let_it_be(:secondary_email) do
+ create :email, email: 'secondary@example.com', user: user
+ end
+
+ it do
+ visit admin_user_path(user.username)
- expect(page).to have_content("Secondary email: #{secondary_email.email}")
+ expect(page).to have_content("Secondary email: #{secondary_email.email}")
- accept_confirm { find("#remove_email_#{secondary_email.id}").click }
+ accept_confirm { find("#remove_email_#{secondary_email.id}").click }
- expect(page).not_to have_content(secondary_email.email)
+ expect(page).not_to have_content(secondary_email.email)
+ end
end
- end
- describe 'show user keys', :js do
- it do
- key1 = create(:key, user: user, title: 'ssh-rsa Key1', key: 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC4FIEBXGi4bPU8kzxMefudPIJ08/gNprdNTaO9BR/ndy3+58s2HCTw2xCHcsuBmq+TsAqgEidVq4skpqoTMB+Uot5Uzp9z4764rc48dZiI661izoREoKnuRQSsRqUTHg5wrLzwxlQbl1MVfRWQpqiz/5KjBC7yLEb9AbusjnWBk8wvC1bQPQ1uLAauEA7d836tgaIsym9BrLsMVnR4P1boWD3Xp1B1T/ImJwAGHvRmP/ycIqmKdSpMdJXwxcb40efWVj0Ibbe7ii9eeoLdHACqevUZi6fwfbymdow+FeqlkPoHyGg3Cu4vD/D8+8cRc7mE/zGCWcQ15Var83Tczour Key1')
- key2 = create(:key, user: user, title: 'ssh-rsa Key2', key: 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDQSTWXhJAX/He+nG78MiRRRn7m0Pb0XbcgTxE0etArgoFoh9WtvDf36HG6tOSg/0UUNcp0dICsNAmhBKdncp6cIyPaXJTURPRAGvhI0/VDk4bi27bRnccGbJ/hDaUxZMLhhrzY0r22mjVf8PF6dvv5QUIQVm1/LeaWYsHHvLgiIjwrXirUZPnFrZw6VLREoBKG8uWvfSXw1L5eapmstqfsME8099oi+vWLR8MgEysZQmD28M73fgW4zek6LDQzKQyJx9nB+hJkKUDvcuziZjGmRFlNgSA2mguERwL1OXonD8WYUrBDGKroIvBT39zS5d9tQDnidEJZ9Y8gv5ViYP7x Key2')
+ describe 'remove user with identities' do
+ it 'removes user with twitter identity', :aggregate_failures do
+ visit admin_user_identities_path(user)
- visit admin_users_path
+ click_link 'Delete'
- click_link user.name
- click_link 'SSH keys'
+ expect(page).to have_content(user.name)
+ expect(page).not_to have_content('twitter')
+ end
+ end
- expect(page).to have_content(key1.title)
- expect(page).to have_content(key2.title)
+ describe 'show user keys', :js do
+ it do
+ key1 = create(:key, user: user, title: 'ssh-rsa Key1', key: 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC4FIEBXGi4bPU8kzxMefudPIJ08/gNprdNTaO9BR/ndy3+58s2HCTw2xCHcsuBmq+TsAqgEidVq4skpqoTMB+Uot5Uzp9z4764rc48dZiI661izoREoKnuRQSsRqUTHg5wrLzwxlQbl1MVfRWQpqiz/5KjBC7yLEb9AbusjnWBk8wvC1bQPQ1uLAauEA7d836tgaIsym9BrLsMVnR4P1boWD3Xp1B1T/ImJwAGHvRmP/ycIqmKdSpMdJXwxcb40efWVj0Ibbe7ii9eeoLdHACqevUZi6fwfbymdow+FeqlkPoHyGg3Cu4vD/D8+8cRc7mE/zGCWcQ15Var83Tczour Key1')
+ key2 = create(:key, user: user, title: 'ssh-rsa Key2', key: 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDQSTWXhJAX/He+nG78MiRRRn7m0Pb0XbcgTxE0etArgoFoh9WtvDf36HG6tOSg/0UUNcp0dICsNAmhBKdncp6cIyPaXJTURPRAGvhI0/VDk4bi27bRnccGbJ/hDaUxZMLhhrzY0r22mjVf8PF6dvv5QUIQVm1/LeaWYsHHvLgiIjwrXirUZPnFrZw6VLREoBKG8uWvfSXw1L5eapmstqfsME8099oi+vWLR8MgEysZQmD28M73fgW4zek6LDQzKQyJx9nB+hJkKUDvcuziZjGmRFlNgSA2mguERwL1OXonD8WYUrBDGKroIvBT39zS5d9tQDnidEJZ9Y8gv5ViYP7x Key2')
- click_link key2.title
+ visit admin_user_path(user)
- expect(page).to have_content(key2.title)
- expect(page).to have_content(key2.key)
+ click_link 'SSH keys'
- click_button 'Delete'
+ expect(page).to have_content(key1.title)
+ expect(page).to have_content(key2.title)
- page.within('.modal') do
- page.click_button('Delete')
- end
+ click_link key2.title
- expect(page).not_to have_content(key2.title)
- end
- end
+ expect(page).to have_content(key2.title)
+ expect(page).to have_content(key2.key)
- describe 'show user identities' do
- it 'shows user identities', :aggregate_failures do
- visit admin_user_identities_path(user)
+ click_button 'Delete'
- expect(page).to have_content(user.name)
- expect(page).to have_content('twitter')
- end
- end
+ page.within('.modal') do
+ page.click_button('Delete')
+ end
- describe 'update user identities' do
- before do
- allow(Gitlab::Auth::OAuth::Provider).to receive(:providers).and_return([:twitter, :twitter_updated])
+ expect(page).not_to have_content(key2.title)
+ end
end
- it 'modifies twitter identity', :aggregate_failures do
- visit admin_user_identities_path(user)
-
- find('.table').find(:link, 'Edit').click
- fill_in 'identity_extern_uid', with: '654321'
- select 'twitter_updated', from: 'identity_provider'
- click_button 'Save changes'
+ describe 'show user attributes' do
+ it 'has expected attributes', :aggregate_failures do
+ visit admin_user_path(user)
- expect(page).to have_content(user.name)
- expect(page).to have_content('twitter_updated')
- expect(page).to have_content('654321')
+ expect(page).to have_content 'Account'
+ expect(page).to have_content 'Personal projects limit'
+ end
end
end
- describe 'remove user with identities' do
- it 'removes user with twitter identity', :aggregate_failures do
- visit admin_user_identities_path(user)
+ [true, false].each do |vue_admin_users|
+ context "with vue_admin_users feature flag set to #{vue_admin_users}", js: vue_admin_users do
+ before do
+ stub_feature_flags(vue_admin_users: vue_admin_users)
+ end
- click_link 'Delete'
+ describe 'GET /admin/users' do
+ context 'user pending approval' do
+ it 'shows user info', :aggregate_failures do
+ user = create(:user, :blocked_pending_approval)
- expect(page).to have_content(user.name)
- expect(page).not_to have_content('twitter')
+ visit admin_users_path
+ click_link 'Pending approval'
+ click_link user.name
+
+ expect(page).to have_content(user.name)
+ expect(page).to have_content('Pending approval')
+ expect(page).to have_link('Approve user')
+ expect(page).to have_link('Reject request')
+ end
+ end
+ end
end
end
end
diff --git a/spec/features/admin/users/users_spec.rb b/spec/features/admin/users/users_spec.rb
index 9482b4f8603..e38376b0741 100644
--- a/spec/features/admin/users/users_spec.rb
+++ b/spec/features/admin/users/users_spec.rb
@@ -3,298 +3,305 @@
require 'spec_helper'
RSpec.describe 'Admin::Users' do
- include Spec::Support::Helpers::Features::ResponsiveTableHelpers
-
let_it_be(:user, reload: true) { create(:omniauth_user, provider: 'twitter', extern_uid: '123456') }
- let_it_be(:current_user) { create(:admin, last_activity_on: 5.days.ago) }
+ let_it_be(:current_user) { create(:admin) }
before do
sign_in(current_user)
gitlab_enable_admin_mode_sign_in(current_user)
end
- describe 'GET /admin/users' do
- before do
- stub_feature_flags(vue_admin_users: false)
- visit admin_users_path
- end
+ [true, false].each do |vue_admin_users|
+ context "with vue_admin_users feature flag set to #{vue_admin_users}", js: vue_admin_users do
+ before do
+ stub_feature_flags(vue_admin_users: vue_admin_users)
+ end
- it "is ok" do
- expect(current_path).to eq(admin_users_path)
- end
+ describe 'GET /admin/users' do
+ before do
+ visit admin_users_path
+ end
- it "has users list" do
- expect(page).to have_content(current_user.email)
- expect(page).to have_content(current_user.name)
- expect(page).to have_content(current_user.created_at.strftime('%e %b, %Y'))
- expect(page).to have_content(current_user.last_activity_on.strftime('%e %b, %Y'))
- expect(page).to have_content(user.email)
- expect(page).to have_content(user.name)
- expect(page).to have_content('Projects')
- expect(page).to have_button('Block')
- expect(page).to have_button('Deactivate')
- expect(page).to have_button('Delete user')
- expect(page).to have_button('Delete user and contributions')
- end
+ it "is ok" do
+ expect(current_path).to eq(admin_users_path)
+ end
- describe 'view extra user information' do
- it 'shows the user popover on hover', :js, quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/11290' do
- expect(page).not_to have_selector('#__BV_popover_1__')
+ it "has users list" do
+ current_user.reload
- first_user_link = page.first('.js-user-link')
- first_user_link.hover
+ expect(page).to have_content(current_user.email)
+ expect(page).to have_content(current_user.name)
+ expect(page).to have_content(current_user.created_at.strftime('%e %b, %Y'))
+ expect(page).to have_content(user.email)
+ expect(page).to have_content(user.name)
+ expect(page).to have_content('Projects')
- expect(page).to have_selector('#__BV_popover_1__')
- end
- end
+ click_user_dropdown_toggle(user.id)
- context 'user project count' do
- before do
- project = create(:project)
- project.add_maintainer(current_user)
- end
+ expect(page).to have_button('Block')
+ expect(page).to have_button('Deactivate')
+ expect(page).to have_button('Delete user')
+ expect(page).to have_button('Delete user and contributions')
+ end
- it 'displays count of users projects' do
- visit admin_users_path
+ it 'clicking edit user takes us to edit page', :aggregate_failures do
+ page.within("[data-testid='user-actions-#{user.id}']") do
+ click_link 'Edit'
+ end
- expect(page.find("[data-testid='user-project-count-#{current_user.id}']").text).to eq("1")
- end
- end
+ expect(page).to have_content('Name')
+ expect(page).to have_content('Password')
+ end
- describe 'tabs' do
- it 'has multiple tabs to filter users' do
- expect(page).to have_link('Active', href: admin_users_path)
- expect(page).to have_link('Admins', href: admin_users_path(filter: 'admins'))
- expect(page).to have_link('2FA Enabled', href: admin_users_path(filter: 'two_factor_enabled'))
- expect(page).to have_link('2FA Disabled', href: admin_users_path(filter: 'two_factor_disabled'))
- expect(page).to have_link('External', href: admin_users_path(filter: 'external'))
- expect(page).to have_link('Blocked', href: admin_users_path(filter: 'blocked'))
- expect(page).to have_link('Deactivated', href: admin_users_path(filter: 'deactivated'))
- expect(page).to have_link('Without projects', href: admin_users_path(filter: 'wop'))
- end
+ describe 'view extra user information' do
+ it 'shows the user popover on hover', :js, quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/11290' do
+ expect(page).not_to have_selector('#__BV_popover_1__')
- context '`Pending approval` tab' do
- before do
- visit admin_users_path
- end
+ first_user_link = page.first('.js-user-link')
+ first_user_link.hover
- it 'shows the `Pending approval` tab' do
- expect(page).to have_link('Pending approval', href: admin_users_path(filter: 'blocked_pending_approval'))
+ expect(page).to have_selector('#__BV_popover_1__')
+ end
end
- end
- end
- describe 'search and sort' do
- before_all do
- create(:user, name: 'Foo Bar', last_activity_on: 3.days.ago)
- create(:user, name: 'Foo Baz', last_activity_on: 2.days.ago)
- create(:user, name: 'Dmitriy')
- end
+ context 'user project count' do
+ before do
+ project = create(:project)
+ project.add_maintainer(current_user)
+ end
- it 'searches users by name' do
- visit admin_users_path(search_query: 'Foo')
+ it 'displays count of users projects' do
+ visit admin_users_path
- expect(page).to have_content('Foo Bar')
- expect(page).to have_content('Foo Baz')
- expect(page).not_to have_content('Dmitriy')
- end
+ expect(page.find("[data-testid='user-project-count-#{current_user.id}']").text).to eq("1")
+ end
+ end
- it 'sorts users by name' do
- visit admin_users_path
+ describe 'tabs' do
+ it 'has multiple tabs to filter users' do
+ expect(page).to have_link('Active', href: admin_users_path)
+ expect(page).to have_link('Admins', href: admin_users_path(filter: 'admins'))
+ expect(page).to have_link('2FA Enabled', href: admin_users_path(filter: 'two_factor_enabled'))
+ expect(page).to have_link('2FA Disabled', href: admin_users_path(filter: 'two_factor_disabled'))
+ expect(page).to have_link('External', href: admin_users_path(filter: 'external'))
+ expect(page).to have_link('Blocked', href: admin_users_path(filter: 'blocked'))
+ expect(page).to have_link('Deactivated', href: admin_users_path(filter: 'deactivated'))
+ expect(page).to have_link('Without projects', href: admin_users_path(filter: 'wop'))
+ end
+
+ context '`Pending approval` tab' do
+ before do
+ visit admin_users_path
+ end
+
+ it 'shows the `Pending approval` tab' do
+ expect(page).to have_link('Pending approval', href: admin_users_path(filter: 'blocked_pending_approval'))
+ end
+ end
+ end
- sort_by('Name')
+ describe 'search and sort' do
+ before_all do
+ create(:user, name: 'Foo Bar', last_activity_on: 3.days.ago)
+ create(:user, name: 'Foo Baz', last_activity_on: 2.days.ago)
+ create(:user, name: 'Dmitriy')
+ end
- expect(first_row.text).to include('Dmitriy')
- expect(second_row.text).to include('Foo Bar')
- end
+ it 'searches users by name' do
+ visit admin_users_path(search_query: 'Foo')
- it 'sorts search results only' do
- visit admin_users_path(search_query: 'Foo')
+ expect(page).to have_content('Foo Bar')
+ expect(page).to have_content('Foo Baz')
+ expect(page).not_to have_content('Dmitriy')
+ end
- sort_by('Name')
+ it 'sorts users by name' do
+ visit admin_users_path
- expect(page).not_to have_content('Dmitriy')
- expect(first_row.text).to include('Foo Bar')
- expect(second_row.text).to include('Foo Baz')
- end
+ sort_by('Name')
- it 'searches with respect of sorting' do
- visit admin_users_path(sort: 'Name')
+ expect(first_row.text).to include('Dmitriy')
+ expect(second_row.text).to include('Foo Bar')
+ end
- fill_in :search_query, with: 'Foo'
- click_button('Search users')
+ it 'sorts search results only' do
+ visit admin_users_path(search_query: 'Foo')
- expect(first_row.text).to include('Foo Bar')
- expect(second_row.text).to include('Foo Baz')
- end
+ sort_by('Name')
+ expect(page).not_to have_content('Dmitriy')
+ expect(first_row.text).to include('Foo Bar')
+ expect(second_row.text).to include('Foo Baz')
+ end
- it 'sorts users by recent last activity' do
- visit admin_users_path(search_query: 'Foo')
+ it 'searches with respect of sorting' do
+ visit admin_users_path(sort: 'Name')
- sort_by('Recent last activity')
+ fill_in :search_query, with: 'Foo'
+ click_button('Search users')
- expect(first_row.text).to include('Foo Baz')
- expect(second_row.text).to include('Foo Bar')
- end
+ expect(first_row.text).to include('Foo Bar')
+ expect(second_row.text).to include('Foo Baz')
+ end
- it 'sorts users by oldest last activity' do
- visit admin_users_path(search_query: 'Foo')
+ it 'sorts users by recent last activity' do
+ visit admin_users_path(search_query: 'Foo')
- sort_by('Oldest last activity')
+ sort_by('Recent last activity')
- expect(first_row.text).to include('Foo Bar')
- expect(second_row.text).to include('Foo Baz')
- end
- end
+ expect(first_row.text).to include('Foo Baz')
+ expect(second_row.text).to include('Foo Bar')
+ end
- describe 'Two-factor Authentication filters' do
- it 'counts users who have enabled 2FA' do
- create(:user, :two_factor)
+ it 'sorts users by oldest last activity' do
+ visit admin_users_path(search_query: 'Foo')
- visit admin_users_path
+ sort_by('Oldest last activity')
- page.within('.filter-two-factor-enabled small') do
- expect(page).to have_content('1')
+ expect(first_row.text).to include('Foo Bar')
+ expect(second_row.text).to include('Foo Baz')
+ end
end
- end
- it 'filters by users who have enabled 2FA' do
- user = create(:user, :two_factor)
+ describe 'Two-factor Authentication filters' do
+ it 'counts users who have enabled 2FA' do
+ create(:user, :two_factor)
- visit admin_users_path
- click_link '2FA Enabled'
+ visit admin_users_path
- expect(page).to have_content(user.email)
- end
+ page.within('.filter-two-factor-enabled small') do
+ expect(page).to have_content('1')
+ end
+ end
- it 'counts users who have not enabled 2FA' do
- visit admin_users_path
+ it 'filters by users who have enabled 2FA' do
+ user = create(:user, :two_factor)
- page.within('.filter-two-factor-disabled small') do
- expect(page).to have_content('2') # Including admin
- end
- end
+ visit admin_users_path
+ click_link '2FA Enabled'
- it 'filters by users who have not enabled 2FA' do
- visit admin_users_path
- click_link '2FA Disabled'
+ expect(page).to have_content(user.email)
+ end
- expect(page).to have_content(user.email)
- end
- end
+ it 'counts users who have not enabled 2FA' do
+ visit admin_users_path
- describe 'Pending approval filter' do
- it 'counts users who are pending approval' do
- create_list(:user, 2, :blocked_pending_approval)
+ page.within('.filter-two-factor-disabled small') do
+ expect(page).to have_content('2') # Including admin
+ end
+ end
- visit admin_users_path
+ it 'filters by users who have not enabled 2FA' do
+ visit admin_users_path
+ click_link '2FA Disabled'
- page.within('.filter-blocked-pending-approval small') do
- expect(page).to have_content('2')
+ expect(page).to have_content(user.email)
+ end
end
- end
- it 'filters by users who are pending approval' do
- user = create(:user, :blocked_pending_approval)
+ describe 'Pending approval filter' do
+ it 'counts users who are pending approval' do
+ create_list(:user, 2, :blocked_pending_approval)
- visit admin_users_path
- click_link 'Pending approval'
+ visit admin_users_path
- expect(page).to have_content(user.email)
- end
- end
+ page.within('.filter-blocked-pending-approval small') do
+ expect(page).to have_content('2')
+ end
+ end
- context 'when blocking/unblocking a user' do
- it 'shows confirmation and allows blocking and unblocking', :js do
- expect(page).to have_content(user.email)
+ it 'filters by users who are pending approval' do
+ user = create(:user, :blocked_pending_approval)
- click_action_in_user_dropdown(user.id, 'Block')
+ visit admin_users_path
+ click_link 'Pending approval'
- wait_for_requests
+ expect(page).to have_content(user.email)
+ end
+ end
- expect(page).to have_content('Block user')
- expect(page).to have_content('Blocking user has the following effects')
- expect(page).to have_content('User will not be able to login')
- expect(page).to have_content('Owned groups will be left')
+ context 'when blocking/unblocking a user' do
+ it 'shows confirmation and allows blocking and unblocking', :js do
+ expect(page).to have_content(user.email)
- find('.modal-footer button', text: 'Block').click
+ click_action_in_user_dropdown(user.id, 'Block')
- wait_for_requests
+ wait_for_requests
- expect(page).to have_content('Successfully blocked')
- expect(page).not_to have_content(user.email)
+ expect(page).to have_content('Block user')
+ expect(page).to have_content('Blocking user has the following effects')
+ expect(page).to have_content('User will not be able to login')
+ expect(page).to have_content('Owned groups will be left')
- click_link 'Blocked'
+ find('.modal-footer button', text: 'Block').click
- wait_for_requests
+ wait_for_requests
- expect(page).to have_content(user.email)
+ expect(page).to have_content('Successfully blocked')
+ expect(page).not_to have_content(user.email)
- click_action_in_user_dropdown(user.id, 'Unblock')
+ click_link 'Blocked'
- expect(page).to have_content('Unblock user')
- expect(page).to have_content('You can always block their account again if needed.')
+ wait_for_requests
- find('.modal-footer button', text: 'Unblock').click
+ expect(page).to have_content(user.email)
- wait_for_requests
+ click_action_in_user_dropdown(user.id, 'Unblock')
- expect(page).to have_content('Successfully unblocked')
- expect(page).not_to have_content(user.email)
- end
- end
+ expect(page).to have_content('Unblock user')
+ expect(page).to have_content('You can always block their account again if needed.')
- context 'when deactivating/re-activating a user' do
- it 'shows confirmation and allows deactivating and re-activating', :js do
- expect(page).to have_content(user.email)
+ find('.modal-footer button', text: 'Unblock').click
- click_action_in_user_dropdown(user.id, 'Deactivate')
+ wait_for_requests
- expect(page).to have_content('Deactivate user')
- expect(page).to have_content('Deactivating a user has the following effects')
- expect(page).to have_content('The user will be logged out')
- expect(page).to have_content('Personal projects, group and user history will be left intact')
+ expect(page).to have_content('Successfully unblocked')
+ expect(page).not_to have_content(user.email)
+ end
+ end
- find('.modal-footer button', text: 'Deactivate').click
+ context 'when deactivating/re-activating a user' do
+ it 'shows confirmation and allows deactivating and re-activating', :js do
+ expect(page).to have_content(user.email)
- wait_for_requests
+ click_action_in_user_dropdown(user.id, 'Deactivate')
- expect(page).to have_content('Successfully deactivated')
- expect(page).not_to have_content(user.email)
+ expect(page).to have_content('Deactivate user')
+ expect(page).to have_content('Deactivating a user has the following effects')
+ expect(page).to have_content('The user will be logged out')
+ expect(page).to have_content('Personal projects, group and user history will be left intact')
- click_link 'Deactivated'
+ find('.modal-footer button', text: 'Deactivate').click
- wait_for_requests
+ wait_for_requests
- expect(page).to have_content(user.email)
+ expect(page).to have_content('Successfully deactivated')
+ expect(page).not_to have_content(user.email)
- click_action_in_user_dropdown(user.id, 'Activate')
+ click_link 'Deactivated'
- expect(page).to have_content('Activate user')
- expect(page).to have_content('You can always deactivate their account again if needed.')
+ wait_for_requests
- find('.modal-footer button', text: 'Activate').click
+ expect(page).to have_content(user.email)
- wait_for_requests
+ click_action_in_user_dropdown(user.id, 'Activate')
- expect(page).to have_content('Successfully activated')
- expect(page).not_to have_content(user.email)
- end
- end
+ expect(page).to have_content('Activate user')
+ expect(page).to have_content('You can always deactivate their account again if needed.')
- def click_action_in_user_dropdown(user_id, action)
- find("[data-testid='user-action-button-#{user_id}']").click
+ find('.modal-footer button', text: 'Activate').click
- within find("[data-testid='user-action-dropdown-#{user_id}']") do
- find('li button', text: action).click
- end
+ wait_for_requests
- wait_for_requests
+ expect(page).to have_content('Successfully activated')
+ expect(page).not_to have_content(user.email)
+ end
+ end
+ end
end
end
describe 'GET /admin/users/new' do
- let(:user_username) { 'bang' }
+ let_it_be(:user_username) { 'bang' }
before do
visit new_admin_user_path
@@ -344,7 +351,7 @@ RSpec.describe 'Admin::Users' do
end
context 'username contains spaces' do
- let(:user_username) { 'Bing bang' }
+ let_it_be(:user_username) { 'Bing bang' }
it "doesn't create the user and shows an error message" do
expect { click_button 'Create user' }.to change {User.count}.by(0)
@@ -363,22 +370,6 @@ RSpec.describe 'Admin::Users' do
visit new_admin_user_path
end
- def expects_external_to_be_checked
- expect(find('#user_external')).to be_checked
- end
-
- def expects_external_to_be_unchecked
- expect(find('#user_external')).not_to be_checked
- end
-
- def expects_warning_to_be_hidden
- expect(find('#warning_external_automatically_set', visible: :all)[:class]).to include 'hidden'
- end
-
- def expects_warning_to_be_shown
- expect(find('#warning_external_automatically_set')[:class]).not_to include 'hidden'
- end
-
it 'automatically unchecks external for matching email' do
expects_external_to_be_checked
expects_warning_to_be_hidden
@@ -413,55 +404,22 @@ RSpec.describe 'Admin::Users' do
expect(new_user.external).to be_falsy
end
- end
- end
- end
-
- describe 'GET /admin/users/:id/edit' do
- before do
- stub_feature_flags(vue_admin_users: false)
- visit admin_users_path
- click_link "edit_user_#{user.id}"
- end
-
- it 'has user edit page' do
- expect(page).to have_content('Name')
- expect(page).to have_content('Password')
- end
-
- describe 'Update user' do
- before do
- fill_in 'user_name', with: 'Big Bang'
- fill_in 'user_email', with: 'bigbang@mail.com'
- fill_in 'user_password', with: 'AValidPassword1'
- fill_in 'user_password_confirmation', with: 'AValidPassword1'
- choose 'user_access_level_admin'
- click_button 'Save changes'
- end
- it 'shows page with new data' do
- expect(page).to have_content('bigbang@mail.com')
- expect(page).to have_content('Big Bang')
- end
-
- it 'changes user entry' do
- user.reload
- expect(user.name).to eq('Big Bang')
- expect(user.admin?).to be_truthy
- expect(user.password_expires_at).to be <= Time.now
- end
- end
+ def expects_external_to_be_checked
+ expect(find('#user_external')).to be_checked
+ end
- describe 'update username to non ascii char' do
- it do
- fill_in 'user_username', with: '\u3042\u3044'
- click_button('Save')
+ def expects_external_to_be_unchecked
+ expect(find('#user_external')).not_to be_checked
+ end
- page.within '#error_explanation' do
- expect(page).to have_content('Username')
+ def expects_warning_to_be_hidden
+ expect(find('#warning_external_automatically_set', visible: :all)[:class]).to include 'hidden'
end
- expect(page).to have_selector(%(form[action="/admin/users/#{user.username}"]))
+ def expects_warning_to_be_shown
+ expect(find('#warning_external_automatically_set')[:class]).not_to include 'hidden'
+ end
end
end
end
@@ -541,15 +499,82 @@ RSpec.describe 'Admin::Users' do
check_breadcrumb('Edit Identity')
end
+
+ def check_breadcrumb(content)
+ expect(find('.breadcrumbs-sub-title')).to have_content(content)
+ end
+ end
+
+ describe 'GET /admin/users/:id/edit' do
+ before do
+ visit edit_admin_user_path(user)
+ end
+
+ describe 'Update user' do
+ before do
+ fill_in 'user_name', with: 'Big Bang'
+ fill_in 'user_email', with: 'bigbang@mail.com'
+ fill_in 'user_password', with: 'AValidPassword1'
+ fill_in 'user_password_confirmation', with: 'AValidPassword1'
+ choose 'user_access_level_admin'
+ click_button 'Save changes'
+ end
+
+ it 'shows page with new data' do
+ expect(page).to have_content('bigbang@mail.com')
+ expect(page).to have_content('Big Bang')
+ end
+
+ it 'changes user entry' do
+ user.reload
+ expect(user.name).to eq('Big Bang')
+ expect(user.admin?).to be_truthy
+ expect(user.password_expires_at).to be <= Time.now
+ end
+ end
+
+ describe 'update username to non ascii char' do
+ it do
+ fill_in 'user_username', with: '\u3042\u3044'
+ click_button('Save')
+
+ page.within '#error_explanation' do
+ expect(page).to have_content('Username')
+ end
+
+ expect(page).to have_selector(%(form[action="/admin/users/#{user.username}"]))
+ end
+ end
end
- def check_breadcrumb(content)
- expect(find('.breadcrumbs-sub-title')).to have_content(content)
+ def click_user_dropdown_toggle(user_id)
+ page.within("[data-testid='user-actions-#{user_id}']") do
+ find("[data-testid='dropdown-toggle']").click
+ end
+ end
+
+ def first_row
+ page.all('[role="row"]')[1]
+ end
+
+ def second_row
+ page.all('[role="row"]')[2]
end
- def sort_by(text)
- page.within('.user-sort-dropdown') do
- click_link text
+ def sort_by(option)
+ page.within('.filtered-search-block') do
+ find('.dropdown-menu-toggle').click
+ click_link option
end
end
+
+ def click_action_in_user_dropdown(user_id, action)
+ click_user_dropdown_toggle(user_id)
+
+ within find("[data-testid='user-actions-#{user_id}']") do
+ find('li button', text: action).click
+ end
+
+ wait_for_requests
+ end
end
diff --git a/spec/features/projects/user_changes_project_visibility_spec.rb b/spec/features/projects/user_changes_project_visibility_spec.rb
index 6935ad4be02..39b8cddd005 100644
--- a/spec/features/projects/user_changes_project_visibility_spec.rb
+++ b/spec/features/projects/user_changes_project_visibility_spec.rb
@@ -28,7 +28,9 @@ RSpec.describe 'User changes public project visibility', :js do
click_button 'Reduce project visibility'
end
- expect(page).to have_text("Project '#{project.name}' was successfully updated")
+ wait_for_requests
+
+ expect(project.reload).to be_private
end
end
diff --git a/spec/frontend/admin/users/components/user_actions_spec.js b/spec/frontend/admin/users/components/user_actions_spec.js
index 0745d961f25..debe964e7aa 100644
--- a/spec/frontend/admin/users/components/user_actions_spec.js
+++ b/spec/frontend/admin/users/components/user_actions_spec.js
@@ -1,5 +1,5 @@
import { GlDropdownDivider } from '@gitlab/ui';
-import { shallowMount } from '@vue/test-utils';
+import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import Actions from '~/admin/users/components/actions';
import AdminUserActions from '~/admin/users/components/user_actions.vue';
import { I18N_USER_ACTIONS } from '~/admin/users/constants';
@@ -14,12 +14,14 @@ describe('AdminUserActions component', () => {
const user = users[0];
const userPaths = generateUserPaths(paths, user.username);
- const findEditButton = () => wrapper.find('[data-testid="edit"]');
- const findActionsDropdown = () => wrapper.find('[data-testid="actions"');
- const findDropdownDivider = () => wrapper.find(GlDropdownDivider);
+ const findUserActions = (id) => wrapper.findByTestId(`user-actions-${id}`);
+ const findEditButton = (id = user.id) => findUserActions(id).find('[data-testid="edit"]');
+ const findActionsDropdown = (id = user.id) =>
+ findUserActions(id).find('[data-testid="dropdown-toggle"]');
+ const findDropdownDivider = () => wrapper.findComponent(GlDropdownDivider);
const initComponent = ({ actions = [] } = {}) => {
- wrapper = shallowMount(AdminUserActions, {
+ wrapper = shallowMountExtended(AdminUserActions, {
propsData: {
user: {
...user,
diff --git a/spec/frontend/issue_show/components/form_spec.js b/spec/frontend/issue_show/components/form_spec.js
index fc2e224ad92..6d4807c4261 100644
--- a/spec/frontend/issue_show/components/form_spec.js
+++ b/spec/frontend/issue_show/components/form_spec.js
@@ -1,13 +1,15 @@
-import Vue from 'vue';
-import mountComponent from 'helpers/vue_mount_component_helper';
+import { GlAlert } from '@gitlab/ui';
+import { shallowMount } from '@vue/test-utils';
import Autosave from '~/autosave';
+import DescriptionTemplate from '~/issue_show/components/fields/description_template.vue';
import formComponent from '~/issue_show/components/form.vue';
+import LockedWarning from '~/issue_show/components/locked_warning.vue';
import eventHub from '~/issue_show/event_hub';
jest.mock('~/autosave');
describe('Inline edit form component', () => {
- let vm;
+ let wrapper;
const defaultProps = {
canDestroy: true,
formState: {
@@ -24,22 +26,26 @@ describe('Inline edit form component', () => {
};
afterEach(() => {
- vm.$destroy();
+ wrapper.destroy();
});
const createComponent = (props) => {
- const Component = Vue.extend(formComponent);
-
- vm = mountComponent(Component, {
- ...defaultProps,
- ...props,
+ wrapper = shallowMount(formComponent, {
+ propsData: {
+ ...defaultProps,
+ ...props,
+ },
});
};
+ const findDescriptionTemplate = () => wrapper.findComponent(DescriptionTemplate);
+ const findLockedWarning = () => wrapper.findComponent(LockedWarning);
+ const findAlert = () => wrapper.findComponent(GlAlert);
+
it('does not render template selector if no templates exist', () => {
createComponent();
- expect(vm.$el.querySelector('.js-issuable-selector-wrap')).toBeNull();
+ expect(findDescriptionTemplate().exists()).toBe(false);
});
it('renders template selector when templates as array exists', () => {
@@ -49,7 +55,7 @@ describe('Inline edit form component', () => {
],
});
- expect(vm.$el.querySelector('.js-issuable-selector-wrap')).not.toBeNull();
+ expect(findDescriptionTemplate().exists()).toBe(true);
});
it('renders template selector when templates as hash exists', () => {
@@ -59,19 +65,19 @@ describe('Inline edit form component', () => {
},
});
- expect(vm.$el.querySelector('.js-issuable-selector-wrap')).not.toBeNull();
+ expect(findDescriptionTemplate().exists()).toBe(true);
});
it('hides locked warning by default', () => {
createComponent();
- expect(vm.$el.querySelector('.alert')).toBeNull();
+ expect(findLockedWarning().exists()).toBe(false);
});
it('shows locked warning if formState is different', () => {
createComponent({ formState: { ...defaultProps.formState, lockedWarningVisible: true } });
- expect(vm.$el.querySelector('.alert')).not.toBeNull();
+ expect(findLockedWarning().exists()).toBe(true);
});
it('hides locked warning when currently saving', () => {
@@ -79,7 +85,7 @@ describe('Inline edit form component', () => {
formState: { ...defaultProps.formState, updateLoading: true, lockedWarningVisible: true },
});
- expect(vm.$el.querySelector('.alert')).toBeNull();
+ expect(findLockedWarning().exists()).toBe(false);
});
describe('autosave', () => {
@@ -110,5 +116,23 @@ describe('Inline edit form component', () => {
expect(spy).toHaveBeenCalledTimes(6);
});
+
+ describe('outdated description', () => {
+ it('does not show warning if lock version from server is the same as the local lock version', () => {
+ createComponent();
+ expect(findAlert().exists()).toBe(false);
+ });
+
+ it('shows warning if lock version from server differs than the local lock version', async () => {
+ Autosave.prototype.getSavedLockVersion.mockResolvedValue('lock version from local storage');
+
+ createComponent({
+ formState: { ...defaultProps.formState, lock_version: 'lock version from server' },
+ });
+
+ await wrapper.vm.$nextTick();
+ expect(findAlert().exists()).toBe(true);
+ });
+ });
});
});
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 b63940e0240..9a4c2edc01a 100644
--- a/spec/frontend/issues_list/components/issues_list_app_spec.js
+++ b/spec/frontend/issues_list/components/issues_list_app_spec.js
@@ -170,13 +170,15 @@ describe('IssuesListApp component', () => {
expect(findGlButtons().filter((button) => button.text() === 'Edit issues')).toHaveLength(0);
});
- it('emits "issuables:enableBulkEdit" event to legacy bulk edit class', () => {
+ it('emits "issuables:enableBulkEdit" event to legacy bulk edit class', async () => {
wrapper = mountComponent({ provide: { canBulkUpdate: true }, mountFn: mount });
jest.spyOn(eventHub, '$emit');
findGlButtonAt(2).vm.$emit('click');
+ await waitForPromises();
+
expect(eventHub.$emit).toHaveBeenCalledWith('issuables:enableBulkEdit');
});
});
diff --git a/spec/frontend/jobs/components/table/job_table_app_spec.js b/spec/frontend/jobs/components/table/job_table_app_spec.js
new file mode 100644
index 00000000000..3f91b44ae5a
--- /dev/null
+++ b/spec/frontend/jobs/components/table/job_table_app_spec.js
@@ -0,0 +1,88 @@
+import { GlSkeletonLoader, GlAlert } from '@gitlab/ui';
+import { createLocalVue, shallowMount } from '@vue/test-utils';
+import VueApollo from 'vue-apollo';
+import createMockApollo from 'helpers/mock_apollo_helper';
+import waitForPromises from 'helpers/wait_for_promises';
+import getJobsQuery from '~/jobs/components/table/graphql/queries/get_jobs.query.graphql';
+import JobsTable from '~/jobs/components/table/jobs_table.vue';
+import JobsTableApp from '~/jobs/components/table/jobs_table_app.vue';
+import JobsTableTabs from '~/jobs/components/table/jobs_table_tabs.vue';
+import { mockJobsQueryResponse } from '../../mock_data';
+
+const projectPath = 'gitlab-org/gitlab';
+const localVue = createLocalVue();
+localVue.use(VueApollo);
+
+describe('Job table app', () => {
+ let wrapper;
+
+ const successHandler = jest.fn().mockResolvedValue(mockJobsQueryResponse);
+ const failedHandler = jest.fn().mockRejectedValue(new Error('GraphQL error'));
+
+ const findSkeletonLoader = () => wrapper.findComponent(GlSkeletonLoader);
+ const findTable = () => wrapper.findComponent(JobsTable);
+ const findTabs = () => wrapper.findComponent(JobsTableTabs);
+ const findAlert = () => wrapper.findComponent(GlAlert);
+
+ const createMockApolloProvider = (handler) => {
+ const requestHandlers = [[getJobsQuery, handler]];
+
+ return createMockApollo(requestHandlers);
+ };
+
+ const createComponent = (handler = successHandler) => {
+ wrapper = shallowMount(JobsTableApp, {
+ provide: {
+ projectPath,
+ },
+ localVue,
+ apolloProvider: createMockApolloProvider(handler),
+ });
+ };
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ describe('loading state', () => {
+ it('should display skeleton loader when loading', () => {
+ createComponent();
+
+ expect(findSkeletonLoader().exists()).toBe(true);
+ expect(findTable().exists()).toBe(false);
+ });
+ });
+
+ describe('loaded state', () => {
+ beforeEach(async () => {
+ createComponent();
+
+ await waitForPromises();
+ });
+
+ it('should display the jobs table with data', () => {
+ expect(findTable().exists()).toBe(true);
+ expect(findSkeletonLoader().exists()).toBe(false);
+ });
+
+ it('should retfech jobs query on fetchJobsByStatus event', async () => {
+ jest.spyOn(wrapper.vm.$apollo.queries.jobs, 'refetch').mockImplementation(jest.fn());
+
+ expect(wrapper.vm.$apollo.queries.jobs.refetch).toHaveBeenCalledTimes(0);
+
+ await findTabs().vm.$emit('fetchJobsByStatus');
+
+ expect(wrapper.vm.$apollo.queries.jobs.refetch).toHaveBeenCalledTimes(1);
+ });
+ });
+
+ describe('error state', () => {
+ it('should show an alert if there is an error fetching the data', async () => {
+ createComponent(failedHandler);
+
+ await waitForPromises();
+
+ expect(findAlert().exists()).toBe(true);
+ });
+ });
+});
diff --git a/spec/frontend/jobs/mock_data.js b/spec/frontend/jobs/mock_data.js
index 360d17052a3..bdedbffff22 100644
--- a/spec/frontend/jobs/mock_data.js
+++ b/spec/frontend/jobs/mock_data.js
@@ -1408,3 +1408,96 @@ export const mockJobsInTable = [
__typename: 'CiJob',
},
];
+
+export const mockJobsQueryResponse = {
+ data: {
+ project: {
+ jobs: {
+ pageInfo: {
+ endCursor: 'eyJpZCI6IjIzMTcifQ',
+ hasNextPage: true,
+ hasPreviousPage: false,
+ startCursor: 'eyJpZCI6IjIzMzYifQ',
+ __typename: 'PageInfo',
+ },
+ nodes: [
+ {
+ artifacts: {
+ nodes: [
+ {
+ downloadPath: '/root/ci-project/-/jobs/2336/artifacts/download?file_type=trace',
+ __typename: 'CiJobArtifact',
+ },
+ {
+ downloadPath:
+ '/root/ci-project/-/jobs/2336/artifacts/download?file_type=metadata',
+ __typename: 'CiJobArtifact',
+ },
+ {
+ downloadPath: '/root/ci-project/-/jobs/2336/artifacts/download?file_type=archive',
+ __typename: 'CiJobArtifact',
+ },
+ ],
+ __typename: 'CiJobArtifactConnection',
+ },
+ allowFailure: false,
+ status: 'SUCCESS',
+ scheduledAt: null,
+ manualJob: false,
+ triggered: null,
+ createdByTag: false,
+ detailedStatus: {
+ detailsPath: '/root/ci-project/-/jobs/2336',
+ group: 'success',
+ icon: 'status_success',
+ label: 'passed',
+ text: 'passed',
+ tooltip: 'passed',
+ action: {
+ buttonTitle: 'Retry this job',
+ icon: 'retry',
+ method: 'post',
+ path: '/root/ci-project/-/jobs/2336/retry',
+ title: 'Retry',
+ __typename: 'StatusAction',
+ },
+ __typename: 'DetailedStatus',
+ },
+ id: 'gid://gitlab/Ci::Build/2336',
+ refName: 'master',
+ refPath: '/root/ci-project/-/commits/master',
+ tags: [],
+ shortSha: '4408fa2a',
+ commitPath: '/root/ci-project/-/commit/4408fa2a27aaadfdf42d8dda3d6a9c01ce6cad78',
+ pipeline: {
+ id: 'gid://gitlab/Ci::Pipeline/473',
+ path: '/root/ci-project/-/pipelines/473',
+ user: {
+ webPath: '/root',
+ avatarUrl:
+ 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon',
+ __typename: 'UserCore',
+ },
+ __typename: 'Pipeline',
+ },
+ stage: {
+ name: 'deploy',
+ __typename: 'CiStage',
+ },
+ name: 'artifact_job',
+ duration: 3,
+ finishedAt: '2021-04-29T14:19:50Z',
+ coverage: null,
+ retryable: true,
+ playable: false,
+ cancelable: false,
+ active: false,
+ __typename: 'CiJob',
+ },
+ ],
+ __typename: 'CiJobConnection',
+ },
+ __typename: 'Project',
+ },
+ },
+};
diff --git a/spec/frontend/projects/pipelines/charts/components/ci_cd_analytics_area_chart_spec.js b/spec/frontend/projects/pipelines/charts/components/ci_cd_analytics_area_chart_spec.js
index 64f80300237..2b523467379 100644
--- a/spec/frontend/projects/pipelines/charts/components/ci_cd_analytics_area_chart_spec.js
+++ b/spec/frontend/projects/pipelines/charts/components/ci_cd_analytics_area_chart_spec.js
@@ -1,5 +1,5 @@
import { mount } from '@vue/test-utils';
-import CiCdAnalyticsAreaChart from '~/projects/pipelines/charts/components/ci_cd_analytics_area_chart.vue';
+import CiCdAnalyticsAreaChart from '~/vue_shared/components/ci_cd_analytics/ci_cd_analytics_area_chart.vue';
import { transformedAreaChartData } from '../mock_data';
describe('CiCdAnalyticsAreaChart', () => {
diff --git a/spec/frontend/projects/pipelines/charts/components/ci_cd_analytics_charts_spec.js b/spec/frontend/projects/pipelines/charts/components/ci_cd_analytics_charts_spec.js
index 037530ddd48..9adc6dba51e 100644
--- a/spec/frontend/projects/pipelines/charts/components/ci_cd_analytics_charts_spec.js
+++ b/spec/frontend/projects/pipelines/charts/components/ci_cd_analytics_charts_spec.js
@@ -1,8 +1,8 @@
import { GlSegmentedControl } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import { nextTick } from 'vue';
-import CiCdAnalyticsAreaChart from '~/projects/pipelines/charts/components/ci_cd_analytics_area_chart.vue';
-import CiCdAnalyticsCharts from '~/projects/pipelines/charts/components/ci_cd_analytics_charts.vue';
+import CiCdAnalyticsAreaChart from '~/vue_shared/components/ci_cd_analytics/ci_cd_analytics_area_chart.vue';
+import CiCdAnalyticsCharts from '~/vue_shared/components/ci_cd_analytics/ci_cd_analytics_charts.vue';
import { transformedAreaChartData, chartOptions } from '../mock_data';
const DEFAULT_PROPS = {
@@ -26,7 +26,7 @@ const DEFAULT_PROPS = {
],
};
-describe('~/projects/pipelines/charts/components/ci_cd_analytics_charts.vue', () => {
+describe('~/vue_shared/components/ci_cd_analytics/ci_cd_analytics_charts.vue', () => {
let wrapper;
const createWrapper = (props = {}) =>
diff --git a/spec/frontend/projects/pipelines/charts/components/pipeline_charts_spec.js b/spec/frontend/projects/pipelines/charts/components/pipeline_charts_spec.js
index c5cfe783569..b5ee62f2042 100644
--- a/spec/frontend/projects/pipelines/charts/components/pipeline_charts_spec.js
+++ b/spec/frontend/projects/pipelines/charts/components/pipeline_charts_spec.js
@@ -2,11 +2,11 @@ import { GlColumnChart } from '@gitlab/ui/dist/charts';
import { createLocalVue, shallowMount } from '@vue/test-utils';
import VueApollo from 'vue-apollo';
import createMockApollo from 'helpers/mock_apollo_helper';
-import CiCdAnalyticsCharts from '~/projects/pipelines/charts/components/ci_cd_analytics_charts.vue';
import PipelineCharts from '~/projects/pipelines/charts/components/pipeline_charts.vue';
import StatisticsList from '~/projects/pipelines/charts/components/statistics_list.vue';
import getPipelineCountByStatus from '~/projects/pipelines/charts/graphql/queries/get_pipeline_count_by_status.query.graphql';
import getProjectPipelineStatistics from '~/projects/pipelines/charts/graphql/queries/get_project_pipeline_statistics.query.graphql';
+import CiCdAnalyticsCharts from '~/vue_shared/components/ci_cd_analytics/ci_cd_analytics_charts.vue';
import { mockPipelineCount, mockPipelineStatistics } from '../mock_data';
const projectPath = 'gitlab-org/gitlab';
diff --git a/spec/models/project_auto_devops_spec.rb b/spec/models/project_auto_devops_spec.rb
index 8313879114f..d5f0b66b210 100644
--- a/spec/models/project_auto_devops_spec.rb
+++ b/spec/models/project_auto_devops_spec.rb
@@ -70,7 +70,7 @@ RSpec.describe ProjectAutoDevops do
it 'does not create a gitlab deploy token' do
expect do
- auto_devops.save
+ auto_devops.save!
end.not_to change { DeployToken.count }
end
end
@@ -80,7 +80,7 @@ RSpec.describe ProjectAutoDevops do
it 'creates a gitlab deploy token' do
expect do
- auto_devops.save
+ auto_devops.save!
end.to change { DeployToken.count }.by(1)
end
end
@@ -90,7 +90,7 @@ RSpec.describe ProjectAutoDevops do
it 'creates a gitlab deploy token' do
expect do
- auto_devops.save
+ auto_devops.save!
end.to change { DeployToken.count }.by(1)
end
end
@@ -101,7 +101,7 @@ RSpec.describe ProjectAutoDevops do
it 'creates a deploy token' do
expect do
- auto_devops.save
+ auto_devops.save!
end.to change { DeployToken.count }.by(1)
end
end
@@ -114,7 +114,7 @@ RSpec.describe ProjectAutoDevops do
allow(Gitlab::CurrentSettings).to receive(:auto_devops_enabled?).and_return(true)
expect do
- auto_devops.save
+ auto_devops.save!
end.to change { DeployToken.count }.by(1)
end
end
@@ -125,7 +125,7 @@ RSpec.describe ProjectAutoDevops do
it 'does not create a deploy token' do
expect do
- auto_devops.save
+ auto_devops.save!
end.not_to change { DeployToken.count }
end
end
@@ -137,7 +137,7 @@ RSpec.describe ProjectAutoDevops do
it 'does not create a deploy token' do
expect do
- auto_devops.save
+ auto_devops.save!
end.not_to change { DeployToken.count }
end
end
@@ -149,7 +149,7 @@ RSpec.describe ProjectAutoDevops do
it 'does not create a deploy token' do
expect do
- auto_devops.save
+ auto_devops.save!
end.not_to change { DeployToken.count }
end
end
diff --git a/spec/models/project_feature_spec.rb b/spec/models/project_feature_spec.rb
index a56018f0fee..3fd7e57a5db 100644
--- a/spec/models/project_feature_spec.rb
+++ b/spec/models/project_feature_spec.rb
@@ -20,7 +20,7 @@ RSpec.describe ProjectFeature do
context 'repository related features' do
before do
- project.project_feature.update(
+ project.project_feature.update!(
merge_requests_access_level: ProjectFeature::DISABLED,
builds_access_level: ProjectFeature::DISABLED,
repository_access_level: ProjectFeature::PRIVATE
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index be523b97135..5e12161f47b 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -215,7 +215,7 @@ RSpec.describe Project, factory_default: :keep do
it 'does not raise an error' do
project = create(:project)
- expect { project.update(ci_cd_settings: nil) }.not_to raise_exception
+ expect { project.update!(ci_cd_settings: nil) }.not_to raise_exception
end
end
@@ -873,13 +873,13 @@ RSpec.describe Project, factory_default: :keep do
end
it 'returns the most recent timestamp' do
- project.update(updated_at: nil,
+ project.update!(updated_at: nil,
last_activity_at: timestamp,
last_repository_updated_at: timestamp - 1.hour)
expect(project.last_activity_date).to be_like_time(timestamp)
- project.update(updated_at: timestamp,
+ project.update!(updated_at: timestamp,
last_activity_at: timestamp - 1.hour,
last_repository_updated_at: nil)
@@ -2672,7 +2672,7 @@ RSpec.describe Project, factory_default: :keep do
context 'with pending pipeline' do
it 'returns empty relation' do
- pipeline.update(status: 'pending')
+ pipeline.update!(status: 'pending')
pending_build = create_build(pipeline)
expect { project.latest_successful_build_for_ref!(pending_build.name) }
@@ -2865,7 +2865,7 @@ RSpec.describe Project, factory_default: :keep do
end
it 'returns false when remote mirror is disabled' do
- project.remote_mirrors.first.update(enabled: false)
+ project.remote_mirrors.first.update!(enabled: false)
is_expected.to be_falsy
end
@@ -2896,7 +2896,7 @@ RSpec.describe Project, factory_default: :keep do
end
it 'does not sync disabled remote mirrors' do
- project.remote_mirrors.first.update(enabled: false)
+ project.remote_mirrors.first.update!(enabled: false)
expect_any_instance_of(RemoteMirror).not_to receive(:sync)
@@ -2934,7 +2934,7 @@ RSpec.describe Project, factory_default: :keep do
it 'fails stuck remote mirrors' do
project = create(:project, :repository, :remote_mirror)
- project.remote_mirrors.first.update(
+ project.remote_mirrors.first.update!(
update_status: :started,
last_update_started_at: 2.days.ago
)
@@ -3192,7 +3192,7 @@ RSpec.describe Project, factory_default: :keep do
end
it 'returns the root of the fork network when the directs source was deleted' do
- forked_project.destroy
+ forked_project.destroy!
expect(second_fork.fork_source).to eq(project)
end
@@ -3436,7 +3436,7 @@ RSpec.describe Project, factory_default: :keep do
let(:environment) { 'foo%bar/test' }
it 'matches literally for _' do
- ci_variable.update(environment_scope: 'foo%bar/*')
+ ci_variable.environment_scope = 'foo%bar/*'
is_expected.to contain_exactly(ci_variable)
end
@@ -3677,7 +3677,7 @@ RSpec.describe Project, factory_default: :keep do
it "updates the namespace_id when changed" do
namespace = create(:namespace)
- project.update(namespace: namespace)
+ project.update!(namespace: namespace)
expect(project.statistics.namespace_id).to eq namespace.id
end
@@ -3970,14 +3970,14 @@ RSpec.describe Project, factory_default: :keep do
expect(project).to receive(:visibility_level_allowed_as_fork).and_call_original
expect(project).to receive(:visibility_level_allowed_by_group).and_call_original
- project.update(visibility_level: Gitlab::VisibilityLevel::INTERNAL)
+ project.update!(visibility_level: Gitlab::VisibilityLevel::INTERNAL)
end
it 'does not validate the visibility' do
expect(project).not_to receive(:visibility_level_allowed_as_fork).and_call_original
expect(project).not_to receive(:visibility_level_allowed_by_group).and_call_original
- project.update(updated_at: Time.current)
+ project.update!(updated_at: Time.current)
end
end
@@ -4061,7 +4061,7 @@ RSpec.describe Project, factory_default: :keep do
project_2 = create(:project, :public, :merge_requests_disabled)
project_3 = create(:project, :public, :issues_disabled)
project_4 = create(:project, :public)
- project_4.project_feature.update(issues_access_level: ProjectFeature::PRIVATE, merge_requests_access_level: ProjectFeature::PRIVATE )
+ project_4.project_feature.update!(issues_access_level: ProjectFeature::PRIVATE, merge_requests_access_level: ProjectFeature::PRIVATE )
project_ids = described_class.ids_with_issuables_available_for(user).pluck(:id)
@@ -4104,7 +4104,7 @@ RSpec.describe Project, factory_default: :keep do
let(:project) { create(:project, :public) }
it 'returns projects with the project feature access level nil' do
- project.project_feature.update(merge_requests_access_level: nil)
+ project.project_feature.update!(merge_requests_access_level: nil)
is_expected.to include(project)
end
@@ -4392,7 +4392,7 @@ RSpec.describe Project, factory_default: :keep do
it 'is run when the project is destroyed' do
expect(project).to receive(:legacy_remove_pages).and_call_original
- expect { project.destroy }.not_to raise_error
+ expect { project.destroy! }.not_to raise_error
end
end
@@ -4922,7 +4922,7 @@ RSpec.describe Project, factory_default: :keep do
context 'when enabled on group' do
it 'has auto devops implicitly enabled' do
- project.update(namespace: create(:group, :auto_devops_enabled))
+ project.update!(namespace: create(:group, :auto_devops_enabled))
expect(project).to have_auto_devops_implicitly_enabled
end
@@ -4931,7 +4931,7 @@ RSpec.describe Project, factory_default: :keep do
context 'when enabled on parent group' do
it 'has auto devops implicitly enabled' do
subgroup = create(:group, parent: create(:group, :auto_devops_enabled))
- project.update(namespace: subgroup)
+ project.update!(namespace: subgroup)
expect(project).to have_auto_devops_implicitly_enabled
end
@@ -5405,7 +5405,7 @@ RSpec.describe Project, factory_default: :keep do
before do
create_list(:group_badge, 2, group: project_group)
- project_group.update(parent: parent_group)
+ project_group.update!(parent: parent_group)
end
it 'returns the project and the project nested groups badges' do
@@ -6489,7 +6489,7 @@ RSpec.describe Project, factory_default: :keep do
end
it 'removes chat names on removal' do
- expect { subject.destroy }.to change { ChatName.count }.by(-5)
+ expect { subject.destroy! }.to change { ChatName.count }.by(-5)
end
end
diff --git a/spec/models/project_team_spec.rb b/spec/models/project_team_spec.rb
index bbc056889d6..ce75e68de32 100644
--- a/spec/models/project_team_spec.rb
+++ b/spec/models/project_team_spec.rb
@@ -294,7 +294,7 @@ RSpec.describe ProjectTeam do
context 'when project is shared with group' do
before do
group = create(:group)
- project.project_group_links.create(
+ project.project_group_links.create!(
group: group,
group_access: Gitlab::Access::DEVELOPER)
@@ -309,7 +309,7 @@ RSpec.describe ProjectTeam do
context 'but share_with_group_lock is true' do
before do
- project.namespace.update(share_with_group_lock: true)
+ project.namespace.update!(share_with_group_lock: true)
end
it { expect(project.team.max_member_access(maintainer.id)).to eq(Gitlab::Access::NO_ACCESS) }
@@ -496,7 +496,7 @@ RSpec.describe ProjectTeam do
project.add_guest(promoted_guest)
project.add_guest(guest)
- project.project_group_links.create(
+ project.project_group_links.create!(
group: group,
group_access: Gitlab::Access::DEVELOPER
)
@@ -505,7 +505,7 @@ RSpec.describe ProjectTeam do
group.add_developer(group_developer)
group.add_developer(second_developer)
- project.project_group_links.create(
+ project.project_group_links.create!(
group: second_group,
group_access: Gitlab::Access::MAINTAINER
)
diff --git a/spec/requests/projects/ci/promeheus_metrics/histograms_controller_spec.rb b/spec/requests/projects/ci/promeheus_metrics/histograms_controller_spec.rb
index 5d2f3e98bb4..7d5eb1c9685 100644
--- a/spec/requests/projects/ci/promeheus_metrics/histograms_controller_spec.rb
+++ b/spec/requests/projects/ci/promeheus_metrics/histograms_controller_spec.rb
@@ -24,20 +24,6 @@ RSpec.describe 'Projects::Ci::PrometheusMetrics::HistogramsController' do
expect(response).to have_gitlab_http_status(:not_found)
end
end
-
- context 'with the feature flag disabled' do
- before do
- stub_feature_flags(ci_accept_frontend_prometheus_metrics: false)
- end
-
- it 'returns 202 Accepted' do
- post histograms_route(histograms: [
- { name: :pipeline_graph_link_calculation_duration_seconds, value: 1 }
- ])
-
- expect(response).to have_gitlab_http_status(:accepted)
- end
- end
end
def histograms_route(params = {})
diff --git a/spec/serializers/ci/pipeline_entity_spec.rb b/spec/serializers/ci/pipeline_entity_spec.rb
index 83ea0d649e8..58b28de09b1 100644
--- a/spec/serializers/ci/pipeline_entity_spec.rb
+++ b/spec/serializers/ci/pipeline_entity_spec.rb
@@ -155,7 +155,7 @@ RSpec.describe Ci::PipelineEntity do
it 'has a correct failure reason' do
expect(subject[:failure_reason])
- .to eq 'CI/CD YAML configuration error!'
+ .to eq 'The pipeline failed due to an error on the CI/CD configuration file.'
end
end
diff --git a/spec/services/ci/prometheus_metrics/observe_histograms_service_spec.rb b/spec/services/ci/prometheus_metrics/observe_histograms_service_spec.rb
index f65260d461e..0b100af5902 100644
--- a/spec/services/ci/prometheus_metrics/observe_histograms_service_spec.rb
+++ b/spec/services/ci/prometheus_metrics/observe_histograms_service_spec.rb
@@ -55,32 +55,6 @@ RSpec.describe Ci::PrometheusMetrics::ObserveHistogramsService do
end
end
- context 'with feature flag disabled' do
- before do
- stub_feature_flags(ci_accept_frontend_prometheus_metrics: false)
- end
-
- let(:params) do
- {
- histograms: [
- { name: 'pipeline_graph_link_calculation_duration_seconds', value: '4' }
- ]
- }
- end
-
- it 'does not register the metrics' do
- execute
-
- expect(histogram_data).to be_nil
- end
-
- it 'returns an empty body and status code' do
- is_expected.to be_success
- expect(subject.http_status).to eq(:accepted)
- expect(subject.payload).to eq({})
- end
- end
-
def histogram_data(name = :pipeline_graph_link_calculation_duration_seconds)
Gitlab::Metrics.registry.get(name)&.get({})
end
diff --git a/spec/support/shared_examples/models/relative_positioning_shared_examples.rb b/spec/support/shared_examples/models/relative_positioning_shared_examples.rb
index c5615ced3fd..b8d12a6da59 100644
--- a/spec/support/shared_examples/models/relative_positioning_shared_examples.rb
+++ b/spec/support/shared_examples/models/relative_positioning_shared_examples.rb
@@ -66,7 +66,7 @@ RSpec.shared_examples 'a class that supports relative positioning' do
end
end
- shared_examples '.move_nulls_to_end' do
+ describe '.move_nulls_to_end' do
let(:item3) { create_item }
let(:sibling_query) { item1.class.relative_positioning_query_base(item1) }
@@ -186,7 +186,7 @@ RSpec.shared_examples 'a class that supports relative positioning' do
end
end
- shared_examples '.move_nulls_to_start' do
+ describe '.move_nulls_to_start' do
let(:item3) { create_item }
let(:sibling_query) { item1.class.relative_positioning_query_base(item1) }
@@ -261,24 +261,6 @@ RSpec.shared_examples 'a class that supports relative positioning' do
end
end
- context 'when optimize_shifting_relative_positions is enabled' do
- before do
- stub_feature_flags(optimize_shifting_relative_positions: true)
- end
-
- it_behaves_like '.move_nulls_to_start'
- it_behaves_like '.move_nulls_to_end'
- end
-
- context 'when optimize_shifting_relative_positions is disabled' do
- before do
- stub_feature_flags(optimize_shifting_relative_positions: false)
- end
-
- it_behaves_like '.move_nulls_to_start'
- it_behaves_like '.move_nulls_to_end'
- end
-
describe '#move_before' do
let(:item3) { create(factory, default_params) }
diff --git a/spec/tooling/danger/project_helper_spec.rb b/spec/tooling/danger/project_helper_spec.rb
index 5d106f08402..6bec176b39b 100644
--- a/spec/tooling/danger/project_helper_spec.rb
+++ b/spec/tooling/danger/project_helper_spec.rb
@@ -220,7 +220,7 @@ RSpec.describe Tooling::Danger::ProjectHelper do
describe '.local_warning_message' do
it 'returns an informational message with rules that can run' do
- expect(described_class.local_warning_message).to eq('==> Only the following Danger rules can be run locally: changelog, changes_size, commit_messages, database, datateam, documentation, duplicate_yarn_dependencies, eslint, karma, pajamas, pipeline, prettier, product_intelligence, utility_css')
+ expect(described_class.local_warning_message).to eq('==> Only the following Danger rules can be run locally: changelog, commit_messages, database, datateam, documentation, duplicate_yarn_dependencies, eslint, karma, pajamas, pipeline, prettier, product_intelligence, utility_css')
end
end
diff --git a/tooling/danger/project_helper.rb b/tooling/danger/project_helper.rb
index 7aac0de0b6b..b60a3aa1adc 100644
--- a/tooling/danger/project_helper.rb
+++ b/tooling/danger/project_helper.rb
@@ -5,7 +5,6 @@ module Tooling
module ProjectHelper
LOCAL_RULES ||= %w[
changelog
- changes_size
commit_messages
database
datateam
@@ -176,26 +175,12 @@ module Tooling
ee? ? 'gitlab' : 'gitlab-foss'
end
- def missing_database_labels(current_mr_labels)
- labels = if has_database_scoped_labels?(current_mr_labels)
- ['database']
- else
- ['database', 'database::review pending']
- end
-
- labels - current_mr_labels
- end
-
private
def ee?
# Support former project name for `dev` and support local Danger run
%w[gitlab gitlab-ee].include?(ENV['CI_PROJECT_NAME']) || Dir.exist?(File.expand_path('../../../ee', __dir__))
end
-
- def has_database_scoped_labels?(current_mr_labels)
- current_mr_labels.any? { |label| label.start_with?('database::') }
- end
end
end
end