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:
authorGitLab Bot <gitlab-bot@gitlab.com>2021-03-23 18:09:28 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2021-03-23 18:09:28 +0300
commitc46b011d3f578d2455443dfabf24226c738c8903 (patch)
tree89107fa4ccf5340dc14a7d0d2f74a0372e56985f
parentb38fc20ae0e90d5b1c538a139aa0a7da1b7b5726 (diff)
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--app/assets/javascripts/boards/components/board_form.vue13
-rw-r--r--app/assets/javascripts/boards/components/config_toggle.vue12
-rw-r--r--app/assets/javascripts/boards/config_toggle.js5
-rw-r--r--app/assets/javascripts/boards/stores/getters.js6
-rw-r--r--app/assets/javascripts/create_merge_request_dropdown.js7
-rw-r--r--app/assets/javascripts/labels_select.js6
-rw-r--r--app/assets/javascripts/projects/commit/components/commit_options_dropdown.vue106
-rw-r--r--app/assets/javascripts/projects/commit/components/form_trigger.vue35
-rw-r--r--app/assets/javascripts/projects/commit/constants.js2
-rw-r--r--app/assets/javascripts/projects/commit/index.js6
-rw-r--r--app/assets/javascripts/projects/commit/init_cherry_pick_commit_trigger.js20
-rw-r--r--app/assets/javascripts/projects/commit/init_commit_options_dropdown.js35
-rw-r--r--app/assets/javascripts/projects/commit/init_revert_commit_trigger.js20
-rw-r--r--app/assets/javascripts/search/sidebar/components/app.vue4
-rw-r--r--app/assets/javascripts/search/topbar/components/app.vue6
-rw-r--r--app/assets/stylesheets/framework/page_header.scss19
-rw-r--r--app/assets/stylesheets/highlight/common.scss28
-rw-r--r--app/assets/stylesheets/highlight/themes/dark.scss4
-rw-r--r--app/assets/stylesheets/highlight/themes/monokai.scss4
-rw-r--r--app/assets/stylesheets/highlight/themes/none.scss4
-rw-r--r--app/assets/stylesheets/highlight/themes/solarized-dark.scss4
-rw-r--r--app/assets/stylesheets/highlight/themes/solarized-light.scss4
-rw-r--r--app/assets/stylesheets/highlight/white_base.scss4
-rw-r--r--app/assets/stylesheets/page_bundles/build.scss12
-rw-r--r--app/finders/repositories/changelog_tag_finder.rb82
-rw-r--r--app/finders/repositories/previous_tag_finder.rb57
-rw-r--r--app/graphql/types/ci/pipeline_config_source_enum.rb3
-rw-r--r--app/helpers/application_helper.rb1
-rw-r--r--app/helpers/commits_helper.rb22
-rw-r--r--app/models/ci/pipeline.rb7
-rw-r--r--app/models/commit.rb8
-rw-r--r--app/services/ci/register_job_service.rb4
-rw-r--r--app/services/members/invite_service.rb4
-rw-r--r--app/services/repositories/changelog_service.rb10
-rw-r--r--app/views/layouts/_search.html.haml2
-rw-r--r--app/views/projects/commit/_commit_box.html.haml38
-rw-r--r--app/views/projects/issues/_new_branch.html.haml3
-rw-r--r--app/views/shared/_file_highlight.html.haml4
-rw-r--r--changelogs/unreleased/273311-fy21q4-foundations-kr2-audit-and-update-buttons-on-searchcontrolle.yml5
-rw-r--r--changelogs/unreleased/287921-default-global-search-tab-inconsistent.yml5
-rw-r--r--changelogs/unreleased/299346-convert-dropdown-on-commit-page-to-vue.yml5
-rw-r--r--changelogs/unreleased/31343-remove-unnecessary-use-of-freeze4.yml5
-rw-r--r--changelogs/unreleased/325636-index-namespaces-traversal_ids.yml5
-rw-r--r--changelogs/unreleased/candrews-master-patch-98104.yml5
-rw-r--r--changelogs/unreleased/changelog-tag-regex.yml5
-rw-r--r--changelogs/unreleased/move-link-icon-to-css.yml5
-rw-r--r--changelogs/unreleased/ph-296784-createMRButtonLoadingIcon.yml5
-rw-r--r--db/migrate/20210323064751_add_namespace_traversal_ids_index.rb18
-rw-r--r--db/post_migrate/20210317104032_set_iteration_cadence_automatic_to_false.rb17
-rw-r--r--db/schema_migrations/202103171040321
-rw-r--r--db/schema_migrations/202103230647511
-rw-r--r--db/structure.sql2
-rw-r--r--doc/administration/geo/replication/disable_geo.md20
-rw-r--r--doc/administration/geo/replication/remove_geo_node.md58
-rw-r--r--doc/administration/geo/replication/remove_geo_site.md58
-rw-r--r--doc/api/graphql/reference/index.md18
-rw-r--r--doc/api/repositories.md53
-rw-r--r--doc/ci/environments/index.md19
-rw-r--r--doc/ci/variables/predefined_variables.md1
-rw-r--r--doc/ci/yaml/README.md6
-rw-r--r--lib/generators/gitlab/usage_metric_definition_generator.rb5
-rw-r--r--lib/gitlab/changelog/config.rb24
-rw-r--r--lib/gitlab/ci/queue/metrics.rb6
-rw-r--r--lib/gitlab/jira/dvcs.rb4
-rw-r--r--lib/gitlab/kubernetes/deployment.rb2
-rw-r--r--lib/gitlab/performance_bar/stats.rb14
-rw-r--r--lib/gitlab/search_context.rb5
-rw-r--r--lib/gitlab/tracking/standard_context.rb4
-rw-r--r--lib/gitlab/untrusted_regexp.rb4
-rw-r--r--lib/gitlab/usage_data_counters/ci_template_unique_counter.rb2
-rw-r--r--locale/gitlab.pot3
-rw-r--r--qa/qa/page/project/commit/show.rb5
-rw-r--r--qa/qa/runtime/api/request.rb2
-rw-r--r--qa/spec/support/helpers/stub_env.rb2
-rw-r--r--rubocop/cop/gitlab/rails_logger.rb2
-rw-r--r--rubocop/cop/graphql/json_type.rb2
-rw-r--r--rubocop/cop/include_sidekiq_worker.rb2
-rw-r--r--rubocop/cop/lint/last_keyword_argument.rb2
-rw-r--r--rubocop/cop/migration/add_column_with_default.rb2
-rw-r--r--rubocop/cop/migration/add_concurrent_foreign_key.rb2
-rw-r--r--rubocop/cop/migration/add_concurrent_index.rb2
-rw-r--r--rubocop/cop/migration/add_index.rb2
-rw-r--r--rubocop/cop/migration/add_limit_to_text_columns.rb2
-rw-r--r--rubocop/cop/migration/add_timestamps.rb2
-rw-r--r--rubocop/cop/migration/datetime.rb2
-rw-r--r--rubocop/cop/project_path_helper.rb2
-rw-r--r--rubocop/cop/safe_params.rb2
-rw-r--r--rubocop/cop/sidekiq_options_queue.rb2
-rw-r--r--spec/features/projects/blobs/blob_line_permalink_updater_spec.rb22
-rw-r--r--spec/features/projects/commit/cherry_pick_spec.rb13
-rw-r--r--spec/features/projects/commit/user_reverts_commit_spec.rb17
-rw-r--r--spec/features/projects/commits/user_browses_commits_spec.rb9
-rw-r--r--spec/finders/repositories/changelog_tag_finder_spec.rb (renamed from spec/finders/repositories/previous_tag_finder_spec.rb)13
-rw-r--r--spec/fixtures/lib/gitlab/performance_bar/peek_data.json2
-rw-r--r--spec/frontend/boards/stores/getters_spec.js27
-rw-r--r--spec/frontend/create_merge_request_dropdown_spec.js18
-rw-r--r--spec/frontend/projects/commit/components/commit_options_dropdown_spec.js123
-rw-r--r--spec/frontend/projects/commit/components/form_trigger_spec.js44
-rw-r--r--spec/helpers/commits_helper_spec.rb125
-rw-r--r--spec/lib/gitlab/changelog/config_spec.rb4
-rw-r--r--spec/lib/gitlab/search_context/builder_spec.rb29
-rw-r--r--spec/lib/gitlab/untrusted_regexp_spec.rb18
-rw-r--r--spec/models/ci/bridge_spec.rb2
-rw-r--r--spec/models/ci/build_spec.rb1
-rw-r--r--spec/models/ci/pipeline_spec.rb2
-rw-r--r--spec/services/ci/register_job_service_spec.rb3
-rw-r--r--spec/services/repositories/changelog_service_spec.rb19
-rw-r--r--spec/views/layouts/_search.html.haml_spec.rb68
-rw-r--r--spec/views/projects/commit/_commit_box.html.haml_spec.rb26
109 files changed, 1092 insertions, 531 deletions
diff --git a/app/assets/javascripts/boards/components/board_form.vue b/app/assets/javascripts/boards/components/board_form.vue
index d8504dcfb0f..8c354c61fa2 100644
--- a/app/assets/javascripts/boards/components/board_form.vue
+++ b/app/assets/javascripts/boards/components/board_form.vue
@@ -107,7 +107,7 @@ export default {
};
},
computed: {
- ...mapGetters(['isEpicBoard', 'isGroupBoard', 'isProjectBoard']),
+ ...mapGetters(['isIssueBoard', 'isGroupBoard', 'isProjectBoard']),
isNewForm() {
return this.currentPage === formType.new;
},
@@ -182,7 +182,7 @@ export default {
groupPath: this.isGroupBoard ? this.fullPath : undefined,
};
},
- boardScopeMutationVariables() {
+ issueBoardScopeMutationVariables() {
/* eslint-disable @gitlab/require-i18n-strings */
return {
weight: this.board.weight,
@@ -193,13 +193,18 @@ export default {
this.board.milestone?.id || this.board.milestone?.id === 0
? convertToGraphQLId('Milestone', this.board.milestone.id)
: null,
- labelIds: this.board.labels.map(fullLabelId),
iterationId: this.board.iteration_id
? convertToGraphQLId('Iteration', this.board.iteration_id)
: null,
};
/* eslint-enable @gitlab/require-i18n-strings */
},
+ boardScopeMutationVariables() {
+ return {
+ labelIds: this.board.labels.map(fullLabelId),
+ ...(this.isIssueBoard && this.issueBoardScopeMutationVariables),
+ };
+ },
mutationVariables() {
return {
...this.baseMutationVariables,
@@ -324,7 +329,7 @@ export default {
/>
<board-scope
- v-if="scopedIssueBoardFeatureEnabled && !isEpicBoard"
+ v-if="scopedIssueBoardFeatureEnabled"
:collapse-scope="isNewForm"
:board="board"
:can-admin-board="canAdminBoard"
diff --git a/app/assets/javascripts/boards/components/config_toggle.vue b/app/assets/javascripts/boards/components/config_toggle.vue
index 7ec99e51f5b..fdb60d0ae6a 100644
--- a/app/assets/javascripts/boards/components/config_toggle.vue
+++ b/app/assets/javascripts/boards/components/config_toggle.vue
@@ -15,7 +15,8 @@ export default {
props: {
boardsStore: {
type: Object,
- required: true,
+ required: false,
+ default: null,
},
canAdminList: {
type: Boolean,
@@ -26,11 +27,6 @@ export default {
required: true,
},
},
- data() {
- return {
- state: this.boardsStore.state,
- };
- },
computed: {
buttonText() {
return this.canAdminList ? s__('Boards|Edit board') : s__('Boards|View scope');
@@ -42,7 +38,9 @@ export default {
methods: {
showPage() {
eventHub.$emit('showBoardModal', formType.edit);
- return this.boardsStore.showPage(formType.edit);
+ if (this.boardsStore) {
+ this.boardsStore.showPage(formType.edit);
+ }
},
},
};
diff --git a/app/assets/javascripts/boards/config_toggle.js b/app/assets/javascripts/boards/config_toggle.js
index 7f327c5764d..41938d8e284 100644
--- a/app/assets/javascripts/boards/config_toggle.js
+++ b/app/assets/javascripts/boards/config_toggle.js
@@ -2,14 +2,15 @@ import Vue from 'vue';
import { parseBoolean } from '~/lib/utils/common_utils';
import ConfigToggle from './components/config_toggle.vue';
-export default (boardsStore) => {
+export default (boardsStore = undefined) => {
const el = document.querySelector('.js-board-config');
if (!el) {
return;
}
- gl.boardConfigToggle = new Vue({
+ // eslint-disable-next-line no-new
+ new Vue({
el,
render(h) {
return h(ConfigToggle, {
diff --git a/app/assets/javascripts/boards/stores/getters.js b/app/assets/javascripts/boards/stores/getters.js
index caa518f91ce..c819717dade 100644
--- a/app/assets/javascripts/boards/stores/getters.js
+++ b/app/assets/javascripts/boards/stores/getters.js
@@ -1,5 +1,5 @@
import { find } from 'lodash';
-import { BoardType, inactiveId } from '../constants';
+import { BoardType, inactiveId, issuableTypes } from '../constants';
export default {
isGroupBoard: (state) => state.boardType === BoardType.group,
@@ -44,6 +44,10 @@ export default {
return find(state.boardLists, (l) => l.title === title);
},
+ isIssueBoard: (state) => {
+ return state.issuableType === issuableTypes.issue;
+ },
+
isEpicBoard: () => {
return false;
},
diff --git a/app/assets/javascripts/create_merge_request_dropdown.js b/app/assets/javascripts/create_merge_request_dropdown.js
index dcc98f86410..50c32b9c4bf 100644
--- a/app/assets/javascripts/create_merge_request_dropdown.js
+++ b/app/assets/javascripts/create_merge_request_dropdown.js
@@ -35,6 +35,7 @@ export default class CreateMergeRequestDropdown {
this.branchInput = this.wrapperEl.querySelector('.js-branch-name');
this.branchMessage = this.wrapperEl.querySelector('.js-branch-message');
this.createMergeRequestButton = this.wrapperEl.querySelector('.js-create-merge-request');
+ this.createMergeRequestLoading = this.createMergeRequestButton.querySelector('.js-spinner');
this.createTargetButton = this.wrapperEl.querySelector('.js-create-target');
this.dropdownList = this.wrapperEl.querySelector('.dropdown-menu');
this.dropdownToggle = this.wrapperEl.querySelector('.js-dropdown-toggle');
@@ -179,6 +180,10 @@ export default class CreateMergeRequestDropdown {
this.disableCreateAction();
}
+ setLoading(loading) {
+ this.createMergeRequestLoading.classList.toggle('gl-display-none', !loading);
+ }
+
disableCreateAction() {
this.createMergeRequestButton.classList.add('disabled');
this.createMergeRequestButton.setAttribute('disabled', 'disabled');
@@ -387,8 +392,10 @@ export default class CreateMergeRequestDropdown {
this.isCreatingBranch = false;
this.enable();
+ this.setLoading(false);
});
+ this.setLoading(true);
this.disable();
}
diff --git a/app/assets/javascripts/labels_select.js b/app/assets/javascripts/labels_select.js
index 56e69ab9418..ed08a024b59 100644
--- a/app/assets/javascripts/labels_select.js
+++ b/app/assets/javascripts/labels_select.js
@@ -313,7 +313,11 @@ export default class LabelsSelect {
return;
}
- if ($('html').hasClass('issue-boards-page')) {
+ if (
+ $('html')
+ .attr('class')
+ .match(/issue-boards-page|epic-boards-page/)
+ ) {
return;
}
if ($dropdown.hasClass('js-multiselect')) {
diff --git a/app/assets/javascripts/projects/commit/components/commit_options_dropdown.vue b/app/assets/javascripts/projects/commit/components/commit_options_dropdown.vue
new file mode 100644
index 00000000000..d92ccbd5f67
--- /dev/null
+++ b/app/assets/javascripts/projects/commit/components/commit_options_dropdown.vue
@@ -0,0 +1,106 @@
+<script>
+import { GlDropdown, GlDropdownItem, GlDropdownDivider, GlDropdownSectionHeader } from '@gitlab/ui';
+import { OPEN_REVERT_MODAL, OPEN_CHERRY_PICK_MODAL } from '../constants';
+import eventHub from '../event_hub';
+
+export default {
+ components: {
+ GlDropdown,
+ GlDropdownItem,
+ GlDropdownDivider,
+ GlDropdownSectionHeader,
+ },
+ inject: {
+ newProjectTagPath: {
+ default: '',
+ },
+ emailPatchesPath: {
+ default: '',
+ },
+ plainDiffPath: {
+ default: '',
+ },
+ },
+ props: {
+ canRevert: {
+ type: Boolean,
+ required: true,
+ },
+ canCherryPick: {
+ type: Boolean,
+ required: true,
+ },
+ canTag: {
+ type: Boolean,
+ required: true,
+ },
+ canEmailPatches: {
+ type: Boolean,
+ required: true,
+ },
+ },
+ computed: {
+ showDivider() {
+ return this.canRevert || this.canCherryPick || this.canTag;
+ },
+ },
+ methods: {
+ showModal(modalId) {
+ eventHub.$emit(modalId);
+ },
+ },
+ openRevertModal: OPEN_REVERT_MODAL,
+ openCherryPickModal: OPEN_CHERRY_PICK_MODAL,
+};
+</script>
+
+<template>
+ <gl-dropdown
+ :text="__('Options')"
+ right
+ data-testid="commit-options-dropdown"
+ data-qa-selector="options_button"
+ class="gl-xs-w-full"
+ >
+ <gl-dropdown-item
+ v-if="canRevert"
+ data-testid="revert-link"
+ @click="showModal($options.openRevertModal)"
+ >
+ {{ s__('ChangeTypeAction|Revert') }}
+ </gl-dropdown-item>
+ <gl-dropdown-item
+ v-if="canCherryPick"
+ data-testid="cherry-pick-link"
+ @click="showModal($options.openCherryPickModal)"
+ >
+ {{ s__('ChangeTypeAction|Cherry-pick') }}
+ </gl-dropdown-item>
+ <gl-dropdown-item v-if="canTag" :href="newProjectTagPath" data-testid="tag-link">
+ {{ s__('CreateTag|Tag') }}
+ </gl-dropdown-item>
+ <gl-dropdown-divider v-if="showDivider" />
+ <gl-dropdown-section-header>
+ {{ __('Download') }}
+ </gl-dropdown-section-header>
+ <gl-dropdown-item
+ v-if="canEmailPatches"
+ :href="emailPatchesPath"
+ download
+ rel="nofollow"
+ data-testid="email-patches-link"
+ data-qa-selector="email_patches"
+ >
+ {{ s__('DownloadCommit|Email Patches') }}
+ </gl-dropdown-item>
+ <gl-dropdown-item
+ :href="plainDiffPath"
+ download
+ rel="nofollow"
+ data-testid="plain-diff-link"
+ data-qa-selector="plain_diff"
+ >
+ {{ s__('DownloadCommit|Plain Diff') }}
+ </gl-dropdown-item>
+ </gl-dropdown>
+</template>
diff --git a/app/assets/javascripts/projects/commit/components/form_trigger.vue b/app/assets/javascripts/projects/commit/components/form_trigger.vue
deleted file mode 100644
index 3561b5c2473..00000000000
--- a/app/assets/javascripts/projects/commit/components/form_trigger.vue
+++ /dev/null
@@ -1,35 +0,0 @@
-<script>
-import { GlLink } from '@gitlab/ui';
-import eventHub from '../event_hub';
-
-export default {
- components: {
- GlLink,
- },
- inject: {
- displayText: {
- default: '',
- },
- testId: {
- default: '',
- },
- },
- props: {
- openModal: {
- type: String,
- required: true,
- },
- },
- methods: {
- showModal() {
- eventHub.$emit(this.openModal);
- },
- },
-};
-</script>
-
-<template>
- <gl-link data-is-link="true" :data-testid="testId" @click="showModal">
- {{ displayText }}
- </gl-link>
-</template>
diff --git a/app/assets/javascripts/projects/commit/constants.js b/app/assets/javascripts/projects/commit/constants.js
index d6bb4e9483f..d553bca360e 100644
--- a/app/assets/javascripts/projects/commit/constants.js
+++ b/app/assets/javascripts/projects/commit/constants.js
@@ -2,10 +2,8 @@ import { s__, __ } from '~/locale';
export const OPEN_REVERT_MODAL = 'openRevertModal';
export const REVERT_MODAL_ID = 'revert-commit-modal';
-export const REVERT_LINK_TEST_ID = 'revert-commit-link';
export const OPEN_CHERRY_PICK_MODAL = 'openCherryPickModal';
export const CHERRY_PICK_MODAL_ID = 'cherry-pick-commit-modal';
-export const CHERRY_PICK_LINK_TEST_ID = 'cherry-pick-commit-link';
export const I18N_MODAL = {
startMergeRequest: s__('ChangeTypeAction|Start a %{newMergeRequest} with these changes'),
diff --git a/app/assets/javascripts/projects/commit/index.js b/app/assets/javascripts/projects/commit/index.js
index b5fdfc25236..4eb51d566c5 100644
--- a/app/assets/javascripts/projects/commit/index.js
+++ b/app/assets/javascripts/projects/commit/index.js
@@ -1,11 +1,9 @@
import initCherryPickCommitModal from './init_cherry_pick_commit_modal';
-import initCherryPickCommitTrigger from './init_cherry_pick_commit_trigger';
+import initCommitOptionsDropdown from './init_commit_options_dropdown';
import initRevertCommitModal from './init_revert_commit_modal';
-import initRevertCommitTrigger from './init_revert_commit_trigger';
export default () => {
initRevertCommitModal();
- initRevertCommitTrigger();
initCherryPickCommitModal();
- initCherryPickCommitTrigger();
+ initCommitOptionsDropdown();
};
diff --git a/app/assets/javascripts/projects/commit/init_cherry_pick_commit_trigger.js b/app/assets/javascripts/projects/commit/init_cherry_pick_commit_trigger.js
deleted file mode 100644
index 942451dc96a..00000000000
--- a/app/assets/javascripts/projects/commit/init_cherry_pick_commit_trigger.js
+++ /dev/null
@@ -1,20 +0,0 @@
-import Vue from 'vue';
-import CommitFormTrigger from './components/form_trigger.vue';
-import { OPEN_CHERRY_PICK_MODAL, CHERRY_PICK_LINK_TEST_ID } from './constants';
-
-export default function initInviteMembersTrigger() {
- const el = document.querySelector('.js-cherry-pick-commit-trigger');
-
- if (!el) {
- return false;
- }
-
- const { displayText } = el.dataset;
-
- return new Vue({
- el,
- provide: { displayText, testId: CHERRY_PICK_LINK_TEST_ID },
- render: (createElement) =>
- createElement(CommitFormTrigger, { props: { openModal: OPEN_CHERRY_PICK_MODAL } }),
- });
-}
diff --git a/app/assets/javascripts/projects/commit/init_commit_options_dropdown.js b/app/assets/javascripts/projects/commit/init_commit_options_dropdown.js
new file mode 100644
index 00000000000..339918e7661
--- /dev/null
+++ b/app/assets/javascripts/projects/commit/init_commit_options_dropdown.js
@@ -0,0 +1,35 @@
+import Vue from 'vue';
+import { parseBoolean } from '~/lib/utils/common_utils';
+import CommitOptionsDropdown from './components/commit_options_dropdown.vue';
+
+export default function initCommitOptionsDropdown() {
+ const el = document.querySelector('#js-commit-options-dropdown');
+
+ if (!el) {
+ return false;
+ }
+
+ const {
+ newProjectTagPath,
+ emailPatchesPath,
+ plainDiffPath,
+ canRevert,
+ canCherryPick,
+ canTag,
+ canEmailPatches,
+ } = el.dataset;
+
+ return new Vue({
+ el,
+ provide: { newProjectTagPath, emailPatchesPath, plainDiffPath },
+ render: (createElement) =>
+ createElement(CommitOptionsDropdown, {
+ props: {
+ canRevert: parseBoolean(canRevert),
+ canCherryPick: parseBoolean(canCherryPick),
+ canTag: parseBoolean(canTag),
+ canEmailPatches: parseBoolean(canEmailPatches),
+ },
+ }),
+ });
+}
diff --git a/app/assets/javascripts/projects/commit/init_revert_commit_trigger.js b/app/assets/javascripts/projects/commit/init_revert_commit_trigger.js
deleted file mode 100644
index dc5168524ca..00000000000
--- a/app/assets/javascripts/projects/commit/init_revert_commit_trigger.js
+++ /dev/null
@@ -1,20 +0,0 @@
-import Vue from 'vue';
-import CommitFormTrigger from './components/form_trigger.vue';
-import { OPEN_REVERT_MODAL, REVERT_LINK_TEST_ID } from './constants';
-
-export default function initInviteMembersTrigger() {
- const el = document.querySelector('.js-revert-commit-trigger');
-
- if (!el) {
- return false;
- }
-
- const { displayText } = el.dataset;
-
- return new Vue({
- el,
- provide: { displayText, testId: REVERT_LINK_TEST_ID },
- render: (createElement) =>
- createElement(CommitFormTrigger, { props: { openModal: OPEN_REVERT_MODAL } }),
- });
-}
diff --git a/app/assets/javascripts/search/sidebar/components/app.vue b/app/assets/javascripts/search/sidebar/components/app.vue
index 4640259314b..99cf16c8350 100644
--- a/app/assets/javascripts/search/sidebar/components/app.vue
+++ b/app/assets/javascripts/search/sidebar/components/app.vue
@@ -32,7 +32,9 @@ export default {
<status-filter />
<confidentiality-filter />
<div class="gl-display-flex gl-align-items-center gl-mt-3">
- <gl-button variant="success" type="submit">{{ __('Apply') }}</gl-button>
+ <gl-button category="primary" variant="confirm" size="small" type="submit">
+ {{ __('Apply') }}
+ </gl-button>
<gl-link v-if="showReset" class="gl-ml-auto" @click="resetQuery">{{
__('Reset filters')
}}</gl-link>
diff --git a/app/assets/javascripts/search/topbar/components/app.vue b/app/assets/javascripts/search/topbar/components/app.vue
index 987735ed811..2439ab55923 100644
--- a/app/assets/javascripts/search/topbar/components/app.vue
+++ b/app/assets/javascripts/search/topbar/components/app.vue
@@ -65,9 +65,9 @@ export default {
<label class="gl-display-block">{{ __('Project') }}</label>
<project-filter :initial-data="projectInitialData" />
</div>
- <gl-button class="btn-search gl-lg-ml-2" variant="success" type="submit">{{
- __('Search')
- }}</gl-button>
+ <gl-button class="btn-search gl-lg-ml-2" category="primary" variant="confirm" type="submit"
+ >{{ __('Search') }}
+ </gl-button>
</section>
</gl-form>
</template>
diff --git a/app/assets/stylesheets/framework/page_header.scss b/app/assets/stylesheets/framework/page_header.scss
index c0847382544..c8b4e306a2e 100644
--- a/app/assets/stylesheets/framework/page_header.scss
+++ b/app/assets/stylesheets/framework/page_header.scss
@@ -12,25 +12,6 @@
}
}
- .header-action-buttons {
- i {
- color: $gl-text-color-secondary;
- font-size: 13px;
- margin-right: 3px;
- }
-
- @include media-breakpoint-down(xs) {
- .btn {
- width: 100%;
- margin-top: 10px;
- }
-
- .dropdown {
- width: 100%;
- }
- }
- }
-
.avatar {
float: none;
display: inline-block;
diff --git a/app/assets/stylesheets/highlight/common.scss b/app/assets/stylesheets/highlight/common.scss
index 6c050f33b07..8270db9966e 100644
--- a/app/assets/stylesheets/highlight/common.scss
+++ b/app/assets/stylesheets/highlight/common.scss
@@ -1,5 +1,6 @@
@import '../framework/variables';
@import './conflict_colors';
+@import 'page_bundles/mixins_and_variables_and_functions';
@mixin diff-background($background, $idiff, $border) {
background: $background;
@@ -93,3 +94,30 @@
}
}
}
+
+@mixin line-number-link($color) {
+ &::before {
+ @include gl-visibility-hidden;
+ @include gl-display-inline-block;
+ @include gl-align-self-center;
+ @include gl-mt-2;
+ @include gl-mr-2;
+ @include gl-w-4;
+ @include gl-h-4;
+ @include gl-float-left;
+ background-color: $color;
+ mask-image: asset_url('icons-stacked.svg#link');
+ mask-repeat: no-repeat;
+ mask-size: cover;
+ mask-position: center;
+ content: '';
+ }
+
+ &:hover::before {
+ @include gl-visibility-visible;
+ }
+
+ &:focus::before {
+ @include gl-visibility-visible;
+ }
+}
diff --git a/app/assets/stylesheets/highlight/themes/dark.scss b/app/assets/stylesheets/highlight/themes/dark.scss
index 0dc01213606..d6523265a43 100644
--- a/app/assets/stylesheets/highlight/themes/dark.scss
+++ b/app/assets/stylesheets/highlight/themes/dark.scss
@@ -90,6 +90,10 @@ $dark-il: #de935f;
.code.dark {
// Line numbers
+ .file-line-num {
+ @include line-number-link($dark-line-num-color);
+ }
+
.line-numbers,
.diff-line-num {
background-color: $dark-main-bg;
diff --git a/app/assets/stylesheets/highlight/themes/monokai.scss b/app/assets/stylesheets/highlight/themes/monokai.scss
index 95c3e8e9103..027f2fa63d3 100644
--- a/app/assets/stylesheets/highlight/themes/monokai.scss
+++ b/app/assets/stylesheets/highlight/themes/monokai.scss
@@ -91,6 +91,10 @@ $monokai-gh: #75715e;
.code.monokai {
// Line numbers
+ .file-line-num {
+ @include line-number-link($monokai-line-num-color);
+ }
+
.line-numbers,
.diff-line-num {
background-color: $monokai-bg;
diff --git a/app/assets/stylesheets/highlight/themes/none.scss b/app/assets/stylesheets/highlight/themes/none.scss
index 4fc6e5dba39..5002726bbc5 100644
--- a/app/assets/stylesheets/highlight/themes/none.scss
+++ b/app/assets/stylesheets/highlight/themes/none.scss
@@ -11,6 +11,10 @@
.code.none {
// Line numbers
+ .file-line-num {
+ @include line-number-link($black-transparent);
+ }
+
.line-numbers,
.diff-line-num {
background-color: $gray-light;
diff --git a/app/assets/stylesheets/highlight/themes/solarized-dark.scss b/app/assets/stylesheets/highlight/themes/solarized-dark.scss
index f95f5393323..cd0cb65e4e2 100644
--- a/app/assets/stylesheets/highlight/themes/solarized-dark.scss
+++ b/app/assets/stylesheets/highlight/themes/solarized-dark.scss
@@ -94,6 +94,10 @@ $solarized-dark-il: #2aa198;
.code.solarized-dark {
// Line numbers
+ .file-line-num {
+ @include line-number-link($solarized-dark-line-color);
+ }
+
.line-numbers,
.diff-line-num {
background-color: $solarized-dark-line-bg;
diff --git a/app/assets/stylesheets/highlight/themes/solarized-light.scss b/app/assets/stylesheets/highlight/themes/solarized-light.scss
index dc4bc2f32c2..77e88053424 100644
--- a/app/assets/stylesheets/highlight/themes/solarized-light.scss
+++ b/app/assets/stylesheets/highlight/themes/solarized-light.scss
@@ -101,6 +101,10 @@ $solarized-light-il: #2aa198;
.code.solarized-light {
// Line numbers
+ .file-line-num {
+ @include line-number-link($solarized-light-line-color);
+ }
+
.line-numbers,
.diff-line-num {
background-color: $solarized-light-line-bg;
diff --git a/app/assets/stylesheets/highlight/white_base.scss b/app/assets/stylesheets/highlight/white_base.scss
index 128fe0cc046..18b2f0a5d58 100644
--- a/app/assets/stylesheets/highlight/white_base.scss
+++ b/app/assets/stylesheets/highlight/white_base.scss
@@ -78,6 +78,10 @@ $white-gc-bg: #eaf2f5;
}
// Line numbers
+.file-line-num {
+ @include line-number-link($black-transparent);
+}
+
.line-numbers,
.diff-line-num {
background-color: $gray-light;
diff --git a/app/assets/stylesheets/page_bundles/build.scss b/app/assets/stylesheets/page_bundles/build.scss
index 34f88f9405a..307386e2c01 100644
--- a/app/assets/stylesheets/page_bundles/build.scss
+++ b/app/assets/stylesheets/page_bundles/build.scss
@@ -86,18 +86,6 @@
padding: 10px 0 9px;
}
- .header-action-buttons {
- display: flex;
-
- @include media-breakpoint-down(xs) {
- .sidebar-toggle-btn {
- margin-top: 0;
- margin-left: 10px;
- max-height: 34px;
- }
- }
- }
-
.header-content {
a {
color: $gl-text-color;
diff --git a/app/finders/repositories/changelog_tag_finder.rb b/app/finders/repositories/changelog_tag_finder.rb
new file mode 100644
index 00000000000..3c110e6c65d
--- /dev/null
+++ b/app/finders/repositories/changelog_tag_finder.rb
@@ -0,0 +1,82 @@
+# frozen_string_literal: true
+
+module Repositories
+ # A finder class for getting the tag of the last release before a given
+ # version, used when generating changelogs.
+ #
+ # Imagine a project with the following tags:
+ #
+ # * v1.0.0
+ # * v1.1.0
+ # * v2.0.0
+ #
+ # If the version supplied is 2.1.0, the tag returned will be v2.0.0. And when
+ # the version is 1.1.1, or 1.2.0, the returned tag will be v1.1.0.
+ #
+ # To obtain the tags, this finder requires a regular expression (using the re2
+ # syntax) to be provided. This regex must produce the following named
+ # captures:
+ #
+ # - major (required)
+ # - minor (required)
+ # - patch (required)
+ # - pre
+ # - meta
+ #
+ # If the `pre` group has a value, the tag is ignored. If any of the required
+ # capture groups don't have a value, the tag is also ignored.
+ class ChangelogTagFinder
+ def initialize(project, regex: Gitlab::Changelog::Config::DEFAULT_TAG_REGEX)
+ @project = project
+ @regex = regex
+ end
+
+ def execute(new_version)
+ tags = {}
+ versions = [new_version]
+
+ begin
+ regex = Gitlab::UntrustedRegexp.new(@regex)
+ rescue RegexpError => ex
+ # The error messages produced by default are not very helpful, so we
+ # raise a better one here. We raise the specific error here so its
+ # message is displayed in the API (where we catch this specific
+ # error).
+ raise(
+ Gitlab::Changelog::Error,
+ "The regular expression to use for finding the previous tag for a version is invalid: #{ex.message}"
+ )
+ end
+
+ @project.repository.tags.each do |tag|
+ matches = regex.match(tag.name)
+
+ next unless matches
+
+ # When using this class for generating changelog data for a range of
+ # commits, we want to compare against the tag of the last _stable_
+ # release; not some random RC that came after that.
+ next if matches[:pre]
+
+ major = matches[:major]
+ minor = matches[:minor]
+ patch = matches[:patch]
+ build = matches[:meta]
+
+ next unless major && minor && patch
+
+ version = "#{major}.#{minor}.#{patch}"
+ version += "+#{build}" if build
+
+ tags[version] = tag
+ versions << version
+ end
+
+ VersionSorter.sort!(versions)
+
+ index = versions.index(new_version)
+
+ tags[versions[index - 1]] if index&.positive?
+ end
+ end
+end
diff --git a/app/finders/repositories/previous_tag_finder.rb b/app/finders/repositories/previous_tag_finder.rb
deleted file mode 100644
index b5e786c30e9..00000000000
--- a/app/finders/repositories/previous_tag_finder.rb
+++ /dev/null
@@ -1,57 +0,0 @@
-# frozen_string_literal: true
-
-module Repositories
- # A finder class for getting the tag of the last release before a given
- # version.
- #
- # Imagine a project with the following tags:
- #
- # * v1.0.0
- # * v1.1.0
- # * v2.0.0
- #
- # If the version supplied is 2.1.0, the tag returned will be v2.0.0. And when
- # the version is 1.1.1, or 1.2.0, the returned tag will be v1.1.0.
- #
- # This finder expects that all tags to consider meet the following
- # requirements:
- #
- # * They start with the letter "v" followed by a version, or immediately start
- # with a version
- # * They use semantic versioning for the version format
- #
- # Tags not meeting these requirements are ignored.
- class PreviousTagFinder
- TAG_REGEX = /\Av?(?<version>#{Gitlab::Regex.unbounded_semver_regex})\z/.freeze
-
- def initialize(project)
- @project = project
- end
-
- def execute(new_version)
- tags = {}
- versions = [new_version]
-
- @project.repository.tags.each do |tag|
- matches = tag.name.match(TAG_REGEX)
-
- next unless matches
-
- # When using this class for generating changelog data for a range of
- # commits, we want to compare against the tag of the last _stable_
- # release; not some random RC that came after that.
- next if matches[:prerelease]
-
- version = matches[:version]
- tags[version] = tag
- versions << version
- end
-
- VersionSorter.sort!(versions)
-
- index = versions.index(new_version)
-
- tags[versions[index - 1]] if index&.positive?
- end
- end
-end
diff --git a/app/graphql/types/ci/pipeline_config_source_enum.rb b/app/graphql/types/ci/pipeline_config_source_enum.rb
index e1575cb2f99..96c8a5f2941 100644
--- a/app/graphql/types/ci/pipeline_config_source_enum.rb
+++ b/app/graphql/types/ci/pipeline_config_source_enum.rb
@@ -4,7 +4,8 @@ module Types
module Ci
class PipelineConfigSourceEnum < BaseEnum
::Enums::Ci::Pipeline.config_sources.keys.each do |state_symbol|
- value state_symbol.to_s.upcase, value: state_symbol.to_s
+ description = state_symbol == :auto_devops_source ? "Auto DevOps source." : "#{state_symbol.to_s.titleize.capitalize}." # This is needed to avoid failure in doc lint
+ value state_symbol.to_s.upcase, value: state_symbol.to_s, description: description
end
end
end
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
index 9af45daaca4..1115bee0843 100644
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -281,6 +281,7 @@ module ApplicationHelper
def page_class
class_names = []
class_names << 'issue-boards-page gl-overflow-auto' if current_controller?(:boards)
+ class_names << 'epic-boards-page' if current_controller?(:epic_boards)
class_names << 'environment-logs-page' if current_controller?(:logs)
class_names << 'with-performance-bar' if performance_bar_enabled?
class_names << system_message_class
diff --git a/app/helpers/commits_helper.rb b/app/helpers/commits_helper.rb
index 42e4844cc8d..652ba9950bc 100644
--- a/app/helpers/commits_helper.rb
+++ b/app/helpers/commits_helper.rb
@@ -110,16 +110,18 @@ module CommitsHelper
end
end
- def revert_commit_link
- return unless current_user
-
- tag(:div, data: { display_text: 'Revert' }, class: "js-revert-commit-trigger")
- end
-
- def cherry_pick_commit_link
- return unless current_user
-
- tag(:div, data: { display_text: 'Cherry-pick' }, class: "js-cherry-pick-commit-trigger")
+ def commit_options_dropdown_data(project, commit)
+ can_collaborate = current_user && can_collaborate_with_project?(project)
+
+ {
+ new_project_tag_path: new_project_tag_path(project, ref: commit),
+ email_patches_path: project_commit_path(project, commit, format: :patch),
+ plain_diff_path: project_commit_path(project, commit, format: :diff),
+ can_revert: "#{can_collaborate && !commit.has_been_reverted?(current_user)}",
+ can_cherry_pick: can_collaborate.to_s,
+ can_tag: can?(current_user, :push_code, project).to_s,
+ can_email_patches: (commit.parents.length < 2).to_s
+ }
end
def commit_signature_badge_classes(additional_classes)
diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb
index b63ec0c8a97..92e3a3709ff 100644
--- a/app/models/ci/pipeline.rb
+++ b/app/models/ci/pipeline.rb
@@ -510,6 +510,12 @@ module Ci
end
end
+ def git_author_full_text
+ strong_memoize(:git_author_full_text) do
+ commit.try(:author_full_text)
+ end
+ end
+
def git_commit_message
strong_memoize(:git_commit_message) do
commit.try(:message)
@@ -822,6 +828,7 @@ module Ci
variables.append(key: 'CI_COMMIT_DESCRIPTION', value: git_commit_description.to_s)
variables.append(key: 'CI_COMMIT_REF_PROTECTED', value: (!!protected_ref?).to_s)
variables.append(key: 'CI_COMMIT_TIMESTAMP', value: git_commit_timestamp.to_s)
+ variables.append(key: 'CI_COMMIT_AUTHOR', value: git_author_full_text.to_s)
# legacy variables
variables.append(key: 'CI_BUILD_REF', value: sha)
diff --git a/app/models/commit.rb b/app/models/commit.rb
index 5b1f236b333..9d3b89c5996 100644
--- a/app/models/commit.rb
+++ b/app/models/commit.rb
@@ -222,6 +222,14 @@ class Commit
end
end
+ def author_full_text
+ return unless author_name && author_email
+
+ strong_memoize(:author_full_text) do
+ "#{author_name} <#{author_email}>"
+ end
+ end
+
# Returns full commit message if title is truncated (greater than 99 characters)
# otherwise returns commit message without first line
def description
diff --git a/app/services/ci/register_job_service.rb b/app/services/ci/register_job_service.rb
index 7556cb1e69b..e7fb20631e3 100644
--- a/app/services/ci/register_job_service.rb
+++ b/app/services/ci/register_job_service.rb
@@ -112,7 +112,7 @@ module Ci
if Feature.enabled?(:ci_register_job_service_one_by_one, runner, default_enabled: true)
build_ids = retrieve_queue(-> { builds.pluck(:id) })
- @metrics.observe_queue_size(-> { build_ids.size })
+ @metrics.observe_queue_size(-> { build_ids.size }, @runner.runner_type)
build_ids.each do |build_id|
yield Ci::Build.find(build_id)
@@ -120,7 +120,7 @@ module Ci
else
builds_array = retrieve_queue(-> { builds.to_a })
- @metrics.observe_queue_size(-> { builds_array.size })
+ @metrics.observe_queue_size(-> { builds_array.size }, @runner.runner_type)
builds_array.each(&blk)
end
diff --git a/app/services/members/invite_service.rb b/app/services/members/invite_service.rb
index 169500d08f0..140d8c35219 100644
--- a/app/services/members/invite_service.rb
+++ b/app/services/members/invite_service.rb
@@ -13,9 +13,9 @@ module Members
end
def execute(source)
+ @source = source
validate_emails!
- @source = source
emails.each(&method(:process_email))
result
rescue BlankEmailsError, TooManyEmailsError => e
@@ -96,3 +96,5 @@ module Members
end
end
end
+
+Members::InviteService.prepend_if_ee('EE::Members::InviteService')
diff --git a/app/services/repositories/changelog_service.rb b/app/services/repositories/changelog_service.rb
index 3981e91e7f3..0122bfb154d 100644
--- a/app/services/repositories/changelog_service.rb
+++ b/app/services/repositories/changelog_service.rb
@@ -61,14 +61,14 @@ module Repositories
# rubocop: enable Metrics/ParameterLists
def execute
- from = start_of_commit_range
+ config = Gitlab::Changelog::Config.from_git(@project)
+ from = start_of_commit_range(config)
# For every entry we want to only include the merge request that
# originally introduced the commit, which is the oldest merge request that
# contains the commit. We fetch there merge requests in batches, reducing
# the number of SQL queries needed to get this data.
mrs_finder = MergeRequests::OldestPerCommitFinder.new(@project)
- config = Gitlab::Changelog::Config.from_git(@project)
release = Gitlab::Changelog::Release
.new(version: @version, date: @date, config: config)
@@ -98,10 +98,12 @@ module Repositories
.commit(release: release, file: @file, branch: @branch, message: @message)
end
- def start_of_commit_range
+ def start_of_commit_range(config)
return @from if @from
- if (prev_tag = PreviousTagFinder.new(@project).execute(@version))
+ finder = ChangelogTagFinder.new(@project, regex: config.tag_regex)
+
+ if (prev_tag = finder.execute(@version))
return prev_tag.target_commit.id
end
diff --git a/app/views/layouts/_search.html.haml b/app/views/layouts/_search.html.haml
index c902c687378..2032d1e95a6 100644
--- a/app/views/layouts/_search.html.haml
+++ b/app/views/layouts/_search.html.haml
@@ -25,7 +25,7 @@
= hidden_field_tag :group_id, search_context.for_group? ? search_context.group.id : '', class: 'js-search-group-options', data: search_context.group_metadata
= hidden_field_tag :project_id, search_context.for_project? ? search_context.project.id : '', id: 'search_project_id', class: 'js-search-project-options', data: search_context.project_metadata
- - if search_context.for_project?
+ - if search_context.for_project? || search_context.for_group?
= hidden_field_tag :scope, search_context.scope
= hidden_field_tag :search_code, search_context.code_search?
diff --git a/app/views/projects/commit/_commit_box.html.haml b/app/views/projects/commit/_commit_box.html.haml
index 974393b893b..ff51f3f4a21 100644
--- a/app/views/projects/commit/_commit_box.html.haml
+++ b/app/views/projects/commit/_commit_box.html.haml
@@ -1,5 +1,3 @@
-- can_collaborate = can_collaborate_with_project?(@project)
-
.page-content-header
.header-main-content
= render partial: 'signature', object: @commit.signature
@@ -20,36 +18,12 @@
= commit_committer_link(@commit, avatar: true, size: 24)
#{time_ago_with_tooltip(@commit.committed_date)}
- .header-action-buttons
- - if defined?(@notes_count) && @notes_count > 0
- %span.btn.gl-button.btn-default.disabled.gl-button.btn-icon.d-none.d-sm-inline.gl-mr-3.has-tooltip{ title: n_("%d comment on this commit", "%d comments on this commit", @notes_count) % @notes_count }
- = sprite_icon('comment')
- = @notes_count
- = link_to project_tree_path(@project, @commit), class: "btn gl-button btn-default gl-mr-3 d-none d-md-inline" do
- #{ _('Browse files') }
- .dropdown.inline
- %a.btn.gl-button.btn-default.dropdown-toggle.qa-options-button.d-md-inline{ data: { toggle: "dropdown" } }
- %span= _('Options')
- = sprite_icon('chevron-down', css_class: 'gl-text-gray-500')
- %ul.dropdown-menu.dropdown-menu-right
- %li.d-block.d-sm-none
- = link_to project_tree_path(@project, @commit) do
- #{ _('Browse Files') }
- - if can_collaborate && !@commit.has_been_reverted?(current_user)
- %li.clearfix
- = revert_commit_link
- - if can_collaborate
- %li.clearfix
- = cherry_pick_commit_link
- - if can?(current_user, :push_code, @project)
- %li.clearfix
- = link_to s_('CreateTag|Tag'), new_project_tag_path(@project, ref: @commit)
- %li.divider
- %li.dropdown-header
- #{ _('Download') }
- - unless @commit.parents.length > 1
- %li= link_to s_('DownloadCommit|Email Patches'), project_commit_path(@project, @commit, format: :patch), class: "qa-email-patches", rel: 'nofollow', download: ''
- %li= link_to s_('DownloadCommit|Plain Diff'), project_commit_path(@project, @commit, format: :diff), class: "qa-plain-diff", rel: 'nofollow', download: ''
+ - if defined?(@notes_count) && @notes_count > 0
+ %span.btn.gl-button.btn-default.disabled.gl-button.btn-icon.d-none.d-sm-inline.gl-mr-3.has-tooltip{ title: n_("%d comment on this commit", "%d comments on this commit", @notes_count) % @notes_count }
+ = sprite_icon('comment')
+ = @notes_count
+ = link_to _('Browse files'), project_tree_path(@project, @commit), class: "btn gl-button btn-default gl-mr-3 gl-xs-w-full gl-xs-mb-3"
+ #js-commit-options-dropdown{ data: commit_options_dropdown_data(@project, @commit) }
.commit-box{ data: { project_path: project_path(@project) } }
%h3.commit-title
diff --git a/app/views/projects/issues/_new_branch.html.haml b/app/views/projects/issues/_new_branch.html.haml
index d299d2846c6..15685291cde 100644
--- a/app/views/projects/issues/_new_branch.html.haml
+++ b/app/views/projects/issues/_new_branch.html.haml
@@ -18,7 +18,8 @@
Checking branch availability…
.btn-group.btn-group-sm.available.hidden
- %button.btn.js-create-merge-request.btn-success.btn-inverted{ type: 'button', data: { action: data_action } }
+ %button.gl-button.btn.js-create-merge-request.btn-success.btn-inverted{ type: 'button', data: { action: data_action } }
+ .spinner.js-spinner.gl-mr-2.gl-display-none
= value
%button.btn.gl-button.create-merge-request-dropdown-toggle.dropdown-toggle.btn-success.btn-inverted.js-dropdown-toggle.gl-flex-grow-0.gl-h-7{ type: 'button', data: { dropdown: { trigger: '#create-merge-request-dropdown' }, display: 'static' } }
diff --git a/app/views/shared/_file_highlight.html.haml b/app/views/shared/_file_highlight.html.haml
index b1f53e4d0f6..f8ac3832a77 100644
--- a/app/views/shared/_file_highlight.html.haml
+++ b/app/views/shared/_file_highlight.html.haml
@@ -2,13 +2,11 @@
- offset = defined?(first_line_number) ? first_line_number : 1
.line-numbers
- if blob.data.present?
- - link_icon = sprite_icon('link', size: 12)
- link = blob_link if defined?(blob_link)
- blob.data.each_line.each_with_index do |_, index|
- i = index + offset
-# We're not using `link_to` because it is too slow once we get to thousands of lines.
- %a.diff-line-num{ href: "#{link}#L#{i}", id: "L#{i}", 'data-line-number' => i }
- = link_icon
+ %a.file-line-num.diff-line-num{ href: "#{link}#L#{i}", id: "L#{i}", 'data-line-number' => i }
= i
- highlight = defined?(highlight_line) && highlight_line ? highlight_line - offset : nil
.blob-content{ data: { blob_id: blob.id, path: blob.path, highlight_line: highlight, qa_selector: 'file_content' } }
diff --git a/changelogs/unreleased/273311-fy21q4-foundations-kr2-audit-and-update-buttons-on-searchcontrolle.yml b/changelogs/unreleased/273311-fy21q4-foundations-kr2-audit-and-update-buttons-on-searchcontrolle.yml
new file mode 100644
index 00000000000..9ae7e20e178
--- /dev/null
+++ b/changelogs/unreleased/273311-fy21q4-foundations-kr2-audit-and-update-buttons-on-searchcontrolle.yml
@@ -0,0 +1,5 @@
+---
+title: Update Search and Apply buttons to confirm variant to align with Pajamas design system
+merge_request: 56122
+author:
+type: other
diff --git a/changelogs/unreleased/287921-default-global-search-tab-inconsistent.yml b/changelogs/unreleased/287921-default-global-search-tab-inconsistent.yml
new file mode 100644
index 00000000000..3a424de64c5
--- /dev/null
+++ b/changelogs/unreleased/287921-default-global-search-tab-inconsistent.yml
@@ -0,0 +1,5 @@
+---
+title: Set the scope in search context from group issue and MR pages
+merge_request: 56383
+author:
+type: other
diff --git a/changelogs/unreleased/299346-convert-dropdown-on-commit-page-to-vue.yml b/changelogs/unreleased/299346-convert-dropdown-on-commit-page-to-vue.yml
new file mode 100644
index 00000000000..59abd51974e
--- /dev/null
+++ b/changelogs/unreleased/299346-convert-dropdown-on-commit-page-to-vue.yml
@@ -0,0 +1,5 @@
+---
+title: Convert Commit dropdown to Vue
+merge_request: 56142
+author:
+type: other
diff --git a/changelogs/unreleased/31343-remove-unnecessary-use-of-freeze4.yml b/changelogs/unreleased/31343-remove-unnecessary-use-of-freeze4.yml
new file mode 100644
index 00000000000..22e685729e3
--- /dev/null
+++ b/changelogs/unreleased/31343-remove-unnecessary-use-of-freeze4.yml
@@ -0,0 +1,5 @@
+---
+title: Remove unnecessary use of freeze
+merge_request: 57059
+author: Lee Tickett @leetickett
+type: other
diff --git a/changelogs/unreleased/325636-index-namespaces-traversal_ids.yml b/changelogs/unreleased/325636-index-namespaces-traversal_ids.yml
new file mode 100644
index 00000000000..a273e3c9a31
--- /dev/null
+++ b/changelogs/unreleased/325636-index-namespaces-traversal_ids.yml
@@ -0,0 +1,5 @@
+---
+title: Add gin index for namespaces.traversal_ids
+merge_request: 57207
+author:
+type: performance
diff --git a/changelogs/unreleased/candrews-master-patch-98104.yml b/changelogs/unreleased/candrews-master-patch-98104.yml
new file mode 100644
index 00000000000..87f868df920
--- /dev/null
+++ b/changelogs/unreleased/candrews-master-patch-98104.yml
@@ -0,0 +1,5 @@
+---
+title: "Add CI_COMMIT_AUTHOR predefined variable"
+merge_request: 56144
+author: Craig Andrews @candrews
+type: added
diff --git a/changelogs/unreleased/changelog-tag-regex.yml b/changelogs/unreleased/changelog-tag-regex.yml
new file mode 100644
index 00000000000..635f4ad5b80
--- /dev/null
+++ b/changelogs/unreleased/changelog-tag-regex.yml
@@ -0,0 +1,5 @@
+---
+title: Support custom tag formats for changelogs
+merge_request: 56889
+author:
+type: added
diff --git a/changelogs/unreleased/move-link-icon-to-css.yml b/changelogs/unreleased/move-link-icon-to-css.yml
new file mode 100644
index 00000000000..0454aaae571
--- /dev/null
+++ b/changelogs/unreleased/move-link-icon-to-css.yml
@@ -0,0 +1,5 @@
+---
+title: Move link icon to CSS
+merge_request: 56980
+author:
+type: performance
diff --git a/changelogs/unreleased/ph-296784-createMRButtonLoadingIcon.yml b/changelogs/unreleased/ph-296784-createMRButtonLoadingIcon.yml
new file mode 100644
index 00000000000..85346b6bc3a
--- /dev/null
+++ b/changelogs/unreleased/ph-296784-createMRButtonLoadingIcon.yml
@@ -0,0 +1,5 @@
+---
+title: Add loading icon to create merge request button
+merge_request: 57105
+author:
+type: changed
diff --git a/db/migrate/20210323064751_add_namespace_traversal_ids_index.rb b/db/migrate/20210323064751_add_namespace_traversal_ids_index.rb
new file mode 100644
index 00000000000..162173f6629
--- /dev/null
+++ b/db/migrate/20210323064751_add_namespace_traversal_ids_index.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+class AddNamespaceTraversalIdsIndex < ActiveRecord::Migration[6.0]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+ INDEX_NAME = 'index_namespaces_on_traversal_ids'
+
+ disable_ddl_transaction!
+
+ def up
+ add_concurrent_index :namespaces, :traversal_ids, using: :gin, name: INDEX_NAME
+ end
+
+ def down
+ remove_concurrent_index_by_name :namespaces, INDEX_NAME
+ end
+end
diff --git a/db/post_migrate/20210317104032_set_iteration_cadence_automatic_to_false.rb b/db/post_migrate/20210317104032_set_iteration_cadence_automatic_to_false.rb
new file mode 100644
index 00000000000..c151551ae64
--- /dev/null
+++ b/db/post_migrate/20210317104032_set_iteration_cadence_automatic_to_false.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+class SetIterationCadenceAutomaticToFalse < ActiveRecord::Migration[6.0]
+ DOWNTIME = false
+
+ def up
+ ActiveRecord::Base.connection.execute <<~SQL
+ UPDATE iterations_cadences
+ SET automatic = FALSE
+ WHERE iterations_cadences.automatic = TRUE
+ SQL
+ end
+
+ def down
+ # no-op
+ end
+end
diff --git a/db/schema_migrations/20210317104032 b/db/schema_migrations/20210317104032
new file mode 100644
index 00000000000..616397a0663
--- /dev/null
+++ b/db/schema_migrations/20210317104032
@@ -0,0 +1 @@
+c954e1f2bfdfddc98030bfa0ab28f58a41921cbb5b81e088cde12637e3ae5a8f \ No newline at end of file
diff --git a/db/schema_migrations/20210323064751 b/db/schema_migrations/20210323064751
new file mode 100644
index 00000000000..3ce610ecb0d
--- /dev/null
+++ b/db/schema_migrations/20210323064751
@@ -0,0 +1 @@
+4ef75890cf2c30954e0d1ff04c75b58cb910315ea1ed345e351edd035b681cc6 \ No newline at end of file
diff --git a/db/structure.sql b/db/structure.sql
index 9e155ecacc8..a51e47a9317 100644
--- a/db/structure.sql
+++ b/db/structure.sql
@@ -23132,6 +23132,8 @@ CREATE UNIQUE INDEX index_namespaces_on_runners_token_encrypted ON namespaces US
CREATE INDEX index_namespaces_on_shared_and_extra_runners_minutes_limit ON namespaces USING btree (shared_runners_minutes_limit, extra_shared_runners_minutes_limit);
+CREATE INDEX index_namespaces_on_traversal_ids ON namespaces USING gin (traversal_ids);
+
CREATE INDEX index_namespaces_on_type_and_id_partial ON namespaces USING btree (type, id) WHERE (type IS NOT NULL);
CREATE INDEX index_non_requested_project_members_on_source_id_and_type ON members USING btree (source_id, source_type) WHERE ((requested_at IS NULL) AND ((type)::text = 'ProjectMember'::text));
diff --git a/doc/administration/geo/replication/disable_geo.md b/doc/administration/geo/replication/disable_geo.md
index a26adcefef5..c71cf80d0c1 100644
--- a/doc/administration/geo/replication/disable_geo.md
+++ b/doc/administration/geo/replication/disable_geo.md
@@ -12,26 +12,26 @@ situation and you want to disable Geo momentarily, you can use these instruction
Geo setup.
There should be no functional difference between disabling Geo and having an active Geo setup with
-no secondary Geo nodes if you remove them correctly.
+no secondary Geo sites if you remove them correctly.
To disable Geo, follow these steps:
-1. [Remove all secondary Geo nodes](#remove-all-secondary-geo-nodes).
-1. [Remove the primary node from the UI](#remove-the-primary-node-from-the-ui).
+1. [Remove all secondary Geo sites](#remove-all-secondary-geo-sites).
+1. [Remove the primary site from the UI](#remove-the-primary-site-from-the-ui).
1. [Remove secondary replication slots](#remove-secondary-replication-slots).
1. [Remove Geo-related configuration](#remove-geo-related-configuration).
1. [(Optional) Revert PostgreSQL settings to use a password and listen on an IP](#optional-revert-postgresql-settings-to-use-a-password-and-listen-on-an-ip).
-## Remove all secondary Geo nodes
+## Remove all secondary Geo sites
-To disable Geo, you need to first remove all your secondary Geo nodes, which means replication will not happen
-anymore on these nodes. You can follow our docs to [remove your secondary Geo nodes](remove_geo_node.md).
+To disable Geo, you need to first remove all your secondary Geo sites, which means replication will not happen
+anymore on these sites. You can follow our docs to [remove your secondary Geo sites](remove_geo_site.md).
-If the current node that you want to keep using is a secondary node, you need to first promote it to primary.
-You can use our steps on [how to promote a secondary node](../disaster_recovery/#step-3-promoting-a-secondary-node)
+If the current site that you want to keep using is a secondary site, you need to first promote it to primary.
+You can use our steps on [how to promote a secondary site](../disaster_recovery/#step-3-promoting-a-secondary-node)
to do that.
-## Remove the primary node from the UI
+## Remove the primary site from the UI
1. Go to **Admin Area > Geo** (`/admin/geo/nodes`).
1. Click the **Remove** button for the **primary** node.
@@ -59,7 +59,7 @@ Geo node in a PostgreSQL console (`sudo gitlab-psql`):
## Remove Geo-related configuration
-1. SSH into your primary Geo node and log in as root:
+1. For each node on your primary Geo site, SSH into the node and log in as root:
```shell
sudo -i
diff --git a/doc/administration/geo/replication/remove_geo_node.md b/doc/administration/geo/replication/remove_geo_node.md
index 06cf5375f0d..09ea84b6c4b 100644
--- a/doc/administration/geo/replication/remove_geo_node.md
+++ b/doc/administration/geo/replication/remove_geo_node.md
@@ -1,58 +1,8 @@
---
-stage: Enablement
-group: Geo
-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
-type: howto
+redirect_to: '../../geo/replication/remove_geo_site.md'
---
-# Removing secondary Geo nodes **(PREMIUM SELF)**
+This document was moved to [another location](../../geo/replication/remove_geo_site.md).
-**Secondary** nodes can be removed from the Geo cluster using the Geo administration page of the **primary** node. To remove a **secondary** node:
-
-1. Go to **Admin Area > Geo** (`/admin/geo/nodes`).
-1. Click the **Remove** button for the **secondary** node you want to remove.
-1. Confirm by clicking **Remove** when the prompt appears.
-
-Once removed from the Geo administration page, you must stop and uninstall the **secondary** node:
-
-1. On the **secondary** node, stop GitLab:
-
- ```shell
- sudo gitlab-ctl stop
- ```
-
-1. On the **secondary** node, uninstall GitLab:
-
- ```shell
- # Stop gitlab and remove its supervision process
- sudo gitlab-ctl uninstall
-
- # Debian/Ubuntu
- sudo dpkg --remove gitlab-ee
-
- # Redhat/Centos
- sudo rpm --erase gitlab-ee
- ```
-
-Once GitLab has been uninstalled from the **secondary** node, the replication slot must be dropped from the **primary** node's database as follows:
-
-1. On the **primary** node, start a PostgreSQL console session:
-
- ```shell
- sudo gitlab-psql
- ```
-
- NOTE:
- Using `gitlab-rails dbconsole` will not work, because managing replication slots requires superuser permissions.
-
-1. Find the name of the relevant replication slot. This is the slot that is specified with `--slot-name` when running the replicate command: `gitlab-ctl replicate-geo-database`.
-
- ```sql
- SELECT * FROM pg_replication_slots;
- ```
-
-1. Remove the replication slot for the **secondary** node:
-
- ```sql
- SELECT pg_drop_replication_slot('<name_of_slot>');
- ```
+<!-- This redirect file can be deleted after 2022-04-01 -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/administration/geo/replication/remove_geo_site.md b/doc/administration/geo/replication/remove_geo_site.md
new file mode 100644
index 00000000000..a42a4c4eb47
--- /dev/null
+++ b/doc/administration/geo/replication/remove_geo_site.md
@@ -0,0 +1,58 @@
+---
+stage: Enablement
+group: Geo
+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
+type: howto
+---
+
+# Removing secondary Geo sites **(PREMIUM SELF)**
+
+**Secondary** sites can be removed from the Geo cluster using the Geo administration page of the **primary** site. To remove a **secondary** site:
+
+1. Go to **Admin Area > Geo** (`/admin/geo/nodes`).
+1. Select the **Remove** button for the **secondary** site you want to remove.
+1. Confirm by selecting **Remove** when the prompt appears.
+
+Once removed from the Geo administration page, you must stop and uninstall the **secondary** site. For each node on your secondary Geo site:
+
+1. Stop GitLab:
+
+ ```shell
+ sudo gitlab-ctl stop
+ ```
+
+1. Uninstall GitLab:
+
+ ```shell
+ # Stop gitlab and remove its supervision process
+ sudo gitlab-ctl uninstall
+
+ # Debian/Ubuntu
+ sudo dpkg --remove gitlab-ee
+
+ # Redhat/Centos
+ sudo rpm --erase gitlab-ee
+ ```
+
+Once GitLab has been uninstalled from each node on the **secondary** site, the replication slot must be dropped from the **primary** site's database as follows:
+
+1. On the **primary** site's database node, start a PostgreSQL console session:
+
+ ```shell
+ sudo gitlab-psql
+ ```
+
+ NOTE:
+ Using `gitlab-rails dbconsole` will not work, because managing replication slots requires superuser permissions.
+
+1. Find the name of the relevant replication slot. This is the slot that is specified with `--slot-name` when running the replicate command: `gitlab-ctl replicate-geo-database`.
+
+ ```sql
+ SELECT * FROM pg_replication_slots;
+ ```
+
+1. Remove the replication slot for the **secondary** site:
+
+ ```sql
+ SELECT pg_drop_replication_slot('<name_of_slot>');
+ ```
diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md
index 96cea368b04..331d3be2b8c 100644
--- a/doc/api/graphql/reference/index.md
+++ b/doc/api/graphql/reference/index.md
@@ -7842,15 +7842,15 @@ Rotation length unit of an on-call rotation.
| Value | Description |
| ----- | ----------- |
-| `AUTO_DEVOPS_SOURCE` | |
-| `BRIDGE_SOURCE` | |
-| `COMPLIANCE_SOURCE` | |
-| `EXTERNAL_PROJECT_SOURCE` | |
-| `PARAMETER_SOURCE` | |
-| `REMOTE_SOURCE` | |
-| `REPOSITORY_SOURCE` | |
-| `UNKNOWN_SOURCE` | |
-| `WEBIDE_SOURCE` | |
+| `AUTO_DEVOPS_SOURCE` | Auto DevOps source. |
+| `BRIDGE_SOURCE` | Bridge source. |
+| `COMPLIANCE_SOURCE` | Compliance source. |
+| `EXTERNAL_PROJECT_SOURCE` | External project source. |
+| `PARAMETER_SOURCE` | Parameter source. |
+| `REMOTE_SOURCE` | Remote source. |
+| `REPOSITORY_SOURCE` | Repository source. |
+| `UNKNOWN_SOURCE` | Unknown source. |
+| `WEBIDE_SOURCE` | Webide source. |
### `PipelineStatusEnum`
diff --git a/doc/api/repositories.md b/doc/api/repositories.md
index 50dc0803646..10a5d236e80 100644
--- a/doc/api/repositories.md
+++ b/doc/api/repositories.md
@@ -312,8 +312,9 @@ Supported attributes:
If the `from` attribute is unspecified, GitLab uses the Git tag of the last
stable version that came before the version specified in the `version`
-attribute. For this to work, your project must create Git tags for versions
-using one of the following formats:
+attribute. This requires that Git tag names follow a specific format, allowing
+GitLab to extract a version from the tag names. By default, GitLab considers
+tags using these formats:
- `vX.Y.Z`
- `X.Y.Z`
@@ -622,3 +623,51 @@ In an entry, the following variables are available (here `foo.bar` means that
The `author` and `merge_request` objects might not be present if the data
couldn't be determined. For example, when a commit is created without a
corresponding merge request, no merge request is displayed.
+
+### Customize the tag format when extracting versions
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/56889) in GitLab 13.11.
+
+GitLab uses a regular expression (using the
+[re2](https://github.com/google/re2/) engine and syntax) to extract a semantic
+version from tag names. The default regular expression is:
+
+```plaintext
+^v?(?P<major>0|[1-9]\d*)\.(?P<minor>0|[1-9]\d*)\.(?P<patch>0|[1-9]\d*)(?:-(?P<pre>(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+(?P<meta>[0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$
+```
+
+This regular expression is based on the official
+[semantic versioning](https://semver.org/) regular expression, and also includes
+support for tag names that start with the letter `v`.
+
+If your project uses a different format for tags, you can specify a different
+regular expression. The regular expression used _must_ produce the following
+capture groups. If any of these capture groups are missing, the tag is ignored:
+
+- `major`
+- `minor`
+- `patch`
+
+The following capture groups are optional:
+
+- `pre`: If set, the tag is ignored. Ignoring `pre` tags ensures release candidate
+ tags and other pre-release tags are not considered when determining the range of
+ commits to generate a changelog for.
+- `meta`: (Optional) Specifies build metadata.
+
+Using this information, GitLab builds a map of Git tags and their release
+versions. It then determines what the latest tag is, based on the version
+extracted from each tag.
+
+To specify a custom regular expression, use the `tag_regex` setting in your
+changelog configuration YAML file. For example, this pattern matches tag names
+such as `version-1.2.3` but not `version-1.2`.
+
+```yaml
+---
+tag_regex: '^version-(?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)$'
+```
+
+To test if your regular expression is working, you can use websites such as
+[regex101](https://regex101.com/). If the regular expression syntax is invalid,
+an error is produced when generating a changelog.
diff --git a/doc/ci/environments/index.md b/doc/ci/environments/index.md
index 54aa642940e..ed2e7c132de 100644
--- a/doc/ci/environments/index.md
+++ b/doc/ci/environments/index.md
@@ -497,19 +497,17 @@ To delete a stopped environment in the GitLab UI:
1. Next to the environment you want to delete, select **Delete environment**.
1. On the confirmation dialog box, select **Delete environment**.
-### Prepare an environment
+### Prepare an environment without creating a deployment
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/208655) in GitLab 13.2.
-By default, GitLab creates a deployment every time a
-build with the specified environment runs. Newer deployments can also
-[cancel older ones](deployment_safety.md#skip-outdated-deployment-jobs).
+By default, whenever GitLab CI/CD runs a job for a specific environment, it
+triggers a deployment and [(optionally) cancels outdated
+deployments](deployment_safety.md#ensure-only-one-deployment-job-runs-at-a-time).
-You may want to specify an environment keyword to
-[protect builds from unauthorized access](protected_environments.md), or to get
-access to [environment-scoped variables](#scoping-environments-with-specs). In these cases,
-you can use the `action: prepare` keyword to ensure deployments aren't created,
-and no builds are canceled:
+To use an environment without creating a new deployment, and without
+cancelling outdated deployments, append the keyword `action: prepare` to your
+job:
```yaml
build:
@@ -522,6 +520,9 @@ build:
url: https://staging.example.com
```
+This gives you access to [environment-scoped variables](#scoping-environments-with-specs),
+and can be used to [protect builds from unauthorized access](protected_environments.md).
+
### Group similar environments
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/7015) in GitLab 8.14.
diff --git a/doc/ci/variables/predefined_variables.md b/doc/ci/variables/predefined_variables.md
index 95410b3d2d8..2cf2373acf9 100644
--- a/doc/ci/variables/predefined_variables.md
+++ b/doc/ci/variables/predefined_variables.md
@@ -35,6 +35,7 @@ There are also [Kubernetes-specific deployment variables](../../user/project/clu
| `CI_COMMIT_TAG` | 9.0 | 0.5 | The commit tag name. Available only in pipelines for tags. |
| `CI_COMMIT_TIMESTAMP` | 13.4 | all | The timestamp of the commit in the ISO 8601 format. |
| `CI_COMMIT_TITLE` | 10.8 | all | The title of the commit. The full first line of the message. |
+| `CI_COMMIT_AUTHOR` | 13.10 | all | The author of the commit in `Name <email>` format. |
| `CI_CONCURRENT_ID` | all | 11.10 | The unique ID of build execution in a single executor. |
| `CI_CONCURRENT_PROJECT_ID` | all | 11.10 | The unique ID of build execution in a single executor and project. |
| `CI_CONFIG_PATH` | 9.4 | 0.5 | The path to the CI/CD configuration file. Defaults to `.gitlab-ci.yml`. |
diff --git a/doc/ci/yaml/README.md b/doc/ci/yaml/README.md
index 8afba7132ca..ec126dc3ee7 100644
--- a/doc/ci/yaml/README.md
+++ b/doc/ci/yaml/README.md
@@ -2579,9 +2579,9 @@ Use the `action` keyword to specify jobs that prepare, start, or stop environmen
| **Value** | **Description** |
|-----------|---------------------------------------------------------------------------------------------------------------------------------------------------------------|
-| start | Default value. Indicates that job starts the environment. The deployment is created after the job starts. |
-| prepare | Indicates that job is only preparing the environment. Does not affect deployments. [Read more about environments](../environments/index.md#prepare-an-environment) |
-| stop | Indicates that job stops deployment. See the example below. |
+| `start` | Default value. Indicates that job starts the environment. The deployment is created after the job starts. |
+| `prepare` | Indicates that the job is only preparing the environment. It does not trigger deployments. [Read more about preparing environments](../environments/index.md#prepare-an-environment-without-creating-a-deployment). |
+| `stop` | Indicates that job stops deployment. See the example below. |
Take for instance:
diff --git a/lib/generators/gitlab/usage_metric_definition_generator.rb b/lib/generators/gitlab/usage_metric_definition_generator.rb
index 7a01050ed0c..8e53e26ede1 100644
--- a/lib/generators/gitlab/usage_metric_definition_generator.rb
+++ b/lib/generators/gitlab/usage_metric_definition_generator.rb
@@ -5,6 +5,11 @@ require 'rails/generators'
module Gitlab
class UsageMetricDefinitionGenerator < Rails::Generators::Base
Directory = Struct.new(:name, :time_frame, :value_type) do
+ def initialize(...)
+ super
+ freeze
+ end
+
def match?(str)
(name == str || time_frame == str) && str != 'none'
end
diff --git a/lib/gitlab/changelog/config.rb b/lib/gitlab/changelog/config.rb
index 105050936ce..be8009750da 100644
--- a/lib/gitlab/changelog/config.rb
+++ b/lib/gitlab/changelog/config.rb
@@ -17,7 +17,24 @@ module Gitlab
# The default template to use for generating release sections.
DEFAULT_TEMPLATE = File.read(File.join(__dir__, 'template.tpl'))
- attr_accessor :date_format, :categories, :template
+ # The regex to use for extracting the version from a Git tag.
+ #
+ # This regex is based on the official semantic versioning regex (as found
+ # on https://semver.org/), with the addition of allowing a "v" at the
+ # start of a tag name.
+ #
+ # We default to a strict regex as we simply don't know what kind of data
+ # users put in their tags. As such, using simpler patterns (e.g. just
+ # `\d+` for the major version) could lead to unexpected results.
+ #
+ # We use a String here as `Gitlab::UntrustedRegexp` is a mutable object.
+ DEFAULT_TAG_REGEX = '^v?(?P<major>0|[1-9]\d*)' \
+ '\.(?P<minor>0|[1-9]\d*)' \
+ '\.(?P<patch>0|[1-9]\d*)' \
+ '(?:-(?P<pre>(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))' \
+ '?(?:\+(?P<meta>[0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$'
+
+ attr_accessor :date_format, :categories, :template, :tag_regex
def self.from_git(project)
if (yaml = project.repository.changelog_config)
@@ -46,6 +63,10 @@ module Gitlab
end
end
+ if (regex = hash['tag_regex'])
+ config.tag_regex = regex
+ end
+
config
end
@@ -54,6 +75,7 @@ module Gitlab
@date_format = DEFAULT_DATE_FORMAT
@template = Parser.new.parse_and_transform(DEFAULT_TEMPLATE)
@categories = {}
+ @tag_regex = DEFAULT_TAG_REGEX
end
def contributor?(user)
diff --git a/lib/gitlab/ci/queue/metrics.rb b/lib/gitlab/ci/queue/metrics.rb
index b25e715bc00..35ac4e36030 100644
--- a/lib/gitlab/ci/queue/metrics.rb
+++ b/lib/gitlab/ci/queue/metrics.rb
@@ -9,7 +9,7 @@ module Gitlab
QUEUE_DURATION_SECONDS_BUCKETS = [1, 3, 10, 30, 60, 300, 900, 1800, 3600].freeze
QUEUE_ACTIVE_RUNNERS_BUCKETS = [1, 3, 10, 30, 60, 300, 900, 1800, 3600].freeze
QUEUE_DEPTH_TOTAL_BUCKETS = [1, 2, 3, 5, 8, 16, 32, 50, 100, 250, 500, 1000, 2000, 5000].freeze
- QUEUE_SIZE_TOTAL_BUCKETS = [1, 5, 10, 50, 100, 500, 1000, 2000, 5000].freeze
+ QUEUE_SIZE_TOTAL_BUCKETS = [1, 5, 10, 50, 100, 500, 1000, 2000, 5000, 7500, 10000, 15000, 20000].freeze
QUEUE_PROCESSING_DURATION_SECONDS_BUCKETS = [0.01, 0.05, 0.1, 0.3, 0.5, 1, 5, 10, 30, 60, 180, 300].freeze
METRICS_SHARD_TAG_PREFIX = 'metrics_shard::'
@@ -94,10 +94,10 @@ module Gitlab
self.class.queue_depth_total.observe({ queue: queue }, size.to_f)
end
- def observe_queue_size(size_proc)
+ def observe_queue_size(size_proc, runner_type)
return unless Feature.enabled?(:gitlab_ci_builds_queuing_metrics, default_enabled: false)
- self.class.queue_size_total.observe({}, size_proc.call.to_f)
+ self.class.queue_size_total.observe({ runner_type: runner_type }, size_proc.call.to_f)
end
def observe_queue_time(metric)
diff --git a/lib/gitlab/jira/dvcs.rb b/lib/gitlab/jira/dvcs.rb
index 4415f98fc7f..ddf2cd76709 100644
--- a/lib/gitlab/jira/dvcs.rb
+++ b/lib/gitlab/jira/dvcs.rb
@@ -3,8 +3,8 @@
module Gitlab
module Jira
module Dvcs
- ENCODED_SLASH = '@'.freeze
- SLASH = '/'.freeze
+ ENCODED_SLASH = '@'
+ SLASH = '/'
ENCODED_ROUTE_REGEX = /[a-zA-Z0-9_\-\.#{ENCODED_SLASH}]+/.freeze
def self.encode_slash(path)
diff --git a/lib/gitlab/kubernetes/deployment.rb b/lib/gitlab/kubernetes/deployment.rb
index 55ed9a7517e..f2e3a0e6810 100644
--- a/lib/gitlab/kubernetes/deployment.rb
+++ b/lib/gitlab/kubernetes/deployment.rb
@@ -5,7 +5,7 @@ module Gitlab
class Deployment
include Gitlab::Utils::StrongMemoize
- STABLE_TRACK_VALUE = 'stable'.freeze
+ STABLE_TRACK_VALUE = 'stable'
def initialize(attributes = {}, pods: [])
@attributes = attributes
diff --git a/lib/gitlab/performance_bar/stats.rb b/lib/gitlab/performance_bar/stats.rb
index 380340b80be..4551b206e98 100644
--- a/lib/gitlab/performance_bar/stats.rb
+++ b/lib/gitlab/performance_bar/stats.rb
@@ -5,6 +5,11 @@ module Gitlab
# This class fetches Peek stats stored in redis and logs them in a
# structured log (so these can be then analyzed in Kibana)
class Stats
+ IGNORED_BACKTRACE_LOCATIONS = %w[
+ ee/lib/ee/peek
+ lib/peek
+ ].freeze
+
def initialize(redis)
@redis = redis
end
@@ -53,7 +58,8 @@ module Gitlab
end
def parse_backtrace(backtrace)
- return unless match = /(?<filename>.*):(?<filenum>\d+):in `(?<method>.*)'/.match(backtrace.first)
+ return unless backtrace_row = find_caller(backtrace)
+ return unless match = /(?<filename>.*):(?<filenum>\d+):in `(?<method>.*)'/.match(backtrace_row)
{
filename: match[:filename],
@@ -65,6 +71,12 @@ module Gitlab
}
end
+ def find_caller(backtrace)
+ backtrace.find do |line|
+ !line.start_with?(*IGNORED_BACKTRACE_LOCATIONS)
+ end
+ end
+
def logger
@logger ||= Gitlab::PerformanceBar::Logger.build
end
diff --git a/lib/gitlab/search_context.rb b/lib/gitlab/search_context.rb
index c3bb0ff26f2..0fbbcaa6833 100644
--- a/lib/gitlab/search_context.rb
+++ b/lib/gitlab/search_context.rb
@@ -129,7 +129,10 @@ module Gitlab
'wiki_blobs'
elsif view_context.current_controller?(:commits)
'commits'
- else nil
+ elsif view_context.current_controller?(:groups)
+ if %w(issues merge_requests).include?(view_context.controller.action_name)
+ view_context.controller.action_name
+ end
end
end
end
diff --git a/lib/gitlab/tracking/standard_context.rb b/lib/gitlab/tracking/standard_context.rb
index 8ce16c11267..5880116132a 100644
--- a/lib/gitlab/tracking/standard_context.rb
+++ b/lib/gitlab/tracking/standard_context.rb
@@ -3,8 +3,8 @@
module Gitlab
module Tracking
class StandardContext
- GITLAB_STANDARD_SCHEMA_URL = 'iglu:com.gitlab/gitlab_standard/jsonschema/1-0-3'.freeze
- GITLAB_RAILS_SOURCE = 'gitlab-rails'.freeze
+ GITLAB_STANDARD_SCHEMA_URL = 'iglu:com.gitlab/gitlab_standard/jsonschema/1-0-3'
+ GITLAB_RAILS_SOURCE = 'gitlab-rails'
def initialize(namespace: nil, project: nil, user: nil, **data)
@data = data
diff --git a/lib/gitlab/untrusted_regexp.rb b/lib/gitlab/untrusted_regexp.rb
index 6a3e2062070..706c0925302 100644
--- a/lib/gitlab/untrusted_regexp.rb
+++ b/lib/gitlab/untrusted_regexp.rb
@@ -35,6 +35,10 @@ module Gitlab
matches
end
+ def match(text)
+ scan_regexp.match(text)
+ end
+
def match?(text)
text.present? && scan(text).present?
end
diff --git a/lib/gitlab/usage_data_counters/ci_template_unique_counter.rb b/lib/gitlab/usage_data_counters/ci_template_unique_counter.rb
index 772a4623280..f6b78d0ccb1 100644
--- a/lib/gitlab/usage_data_counters/ci_template_unique_counter.rb
+++ b/lib/gitlab/usage_data_counters/ci_template_unique_counter.rb
@@ -2,7 +2,7 @@
module Gitlab::UsageDataCounters
class CiTemplateUniqueCounter
- REDIS_SLOT = 'ci_templates'.freeze
+ REDIS_SLOT = 'ci_templates'
# NOTE: Events originating from implicit Auto DevOps pipelines get prefixed with `implicit_`
TEMPLATE_TO_EVENT = {
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 9cba563a8bf..6b43f79455a 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -29191,9 +29191,6 @@ msgstr ""
msgid "SubscriptionTable|Add seats"
msgstr ""
-msgid "SubscriptionTable|An error occurred while loading billable members list"
-msgstr ""
-
msgid "SubscriptionTable|An error occurred while loading the subscription details."
msgstr ""
diff --git a/qa/qa/page/project/commit/show.rb b/qa/qa/page/project/commit/show.rb
index 8ece81f7088..8dc54c08916 100644
--- a/qa/qa/page/project/commit/show.rb
+++ b/qa/qa/page/project/commit/show.rb
@@ -6,10 +6,13 @@ module QA
module Commit
class Show < Page::Base
view 'app/views/projects/commit/_commit_box.html.haml' do
+ element :commit_sha_content
+ end
+
+ view 'app/assets/javascripts/projects/commit/components/commit_options_dropdown.vue' do
element :options_button
element :email_patches
element :plain_diff
- element :commit_sha_content
end
def select_email_patches
diff --git a/qa/qa/runtime/api/request.rb b/qa/qa/runtime/api/request.rb
index b58be354103..28bae541cb8 100644
--- a/qa/qa/runtime/api/request.rb
+++ b/qa/qa/runtime/api/request.rb
@@ -4,7 +4,7 @@ module QA
module Runtime
module API
class Request
- API_VERSION = 'v4'.freeze
+ API_VERSION = 'v4'
def initialize(api_client, path, **query_string)
query_string[:private_token] ||= api_client.personal_access_token unless query_string[:oauth_access_token]
diff --git a/qa/spec/support/helpers/stub_env.rb b/qa/spec/support/helpers/stub_env.rb
index 8ad864dbec8..de8d2f47adf 100644
--- a/qa/spec/support/helpers/stub_env.rb
+++ b/qa/spec/support/helpers/stub_env.rb
@@ -15,7 +15,7 @@ module Helpers
private
- STUBBED_KEY = '__STUBBED__'.freeze
+ STUBBED_KEY = '__STUBBED__'
def add_stubbed_value(key, value)
allow(ENV).to receive(:[]).with(key).and_return(value)
diff --git a/rubocop/cop/gitlab/rails_logger.rb b/rubocop/cop/gitlab/rails_logger.rb
index ad35d2ccfbb..5a1695ce56e 100644
--- a/rubocop/cop/gitlab/rails_logger.rb
+++ b/rubocop/cop/gitlab/rails_logger.rb
@@ -21,7 +21,7 @@ module RuboCop
# # OK
# Rails.logger.level
MSG = 'Use a structured JSON logger instead of `Rails.logger`. ' \
- 'https://docs.gitlab.com/ee/development/logging.html'.freeze
+ 'https://docs.gitlab.com/ee/development/logging.html'
# See supported log methods:
# https://ruby-doc.org/stdlib-2.6.6/libdoc/logger/rdoc/Logger.html
diff --git a/rubocop/cop/graphql/json_type.rb b/rubocop/cop/graphql/json_type.rb
index 1e3e3d7a7ff..a8c38358535 100644
--- a/rubocop/cop/graphql/json_type.rb
+++ b/rubocop/cop/graphql/json_type.rb
@@ -20,7 +20,7 @@ module RuboCop
module Graphql
class JSONType < RuboCop::Cop::Cop
MSG = 'Avoid using GraphQL::Types::JSON. See: ' \
- 'https://docs.gitlab.com/ee/development/api_graphql_styleguide.html#json'.freeze
+ 'https://docs.gitlab.com/ee/development/api_graphql_styleguide.html#json'
def_node_matcher :has_json_type?, <<~PATTERN
(send nil? {:field :argument}
diff --git a/rubocop/cop/include_sidekiq_worker.rb b/rubocop/cop/include_sidekiq_worker.rb
index e69bc018add..e39b8bf92c2 100644
--- a/rubocop/cop/include_sidekiq_worker.rb
+++ b/rubocop/cop/include_sidekiq_worker.rb
@@ -4,7 +4,7 @@ module RuboCop
module Cop
# Cop that makes sure workers include `ApplicationWorker`, not `Sidekiq::Worker`.
class IncludeSidekiqWorker < RuboCop::Cop::Cop
- MSG = 'Include `ApplicationWorker`, not `Sidekiq::Worker`.'.freeze
+ MSG = 'Include `ApplicationWorker`, not `Sidekiq::Worker`.'
def_node_matcher :includes_sidekiq_worker?, <<~PATTERN
(send nil? :include (const (const nil? :Sidekiq) :Worker))
diff --git a/rubocop/cop/lint/last_keyword_argument.rb b/rubocop/cop/lint/last_keyword_argument.rb
index 9652c1ace8d..80f4660eeb8 100644
--- a/rubocop/cop/lint/last_keyword_argument.rb
+++ b/rubocop/cop/lint/last_keyword_argument.rb
@@ -10,7 +10,7 @@ module RuboCop
# 1. Downloading the complete set of deprecations/ files from a CI
# pipeline (see https://gitlab.com/gitlab-org/gitlab/-/merge_requests/47720)
class LastKeywordArgument < Cop
- MSG = 'Using the last argument as keyword parameters is deprecated'.freeze
+ MSG = 'Using the last argument as keyword parameters is deprecated'
DEPRECATIONS_GLOB = File.expand_path('../../../deprecations/**/*.yml', __dir__)
KEYWORD_DEPRECATION_STR = 'maybe ** should be added to the call'
diff --git a/rubocop/cop/migration/add_column_with_default.rb b/rubocop/cop/migration/add_column_with_default.rb
index 355319b0dfe..afd7d93cd47 100644
--- a/rubocop/cop/migration/add_column_with_default.rb
+++ b/rubocop/cop/migration/add_column_with_default.rb
@@ -8,7 +8,7 @@ module RuboCop
class AddColumnWithDefault < RuboCop::Cop::Cop
include MigrationHelpers
- MSG = '`add_column_with_default` is deprecated, use `add_column` instead'.freeze
+ MSG = '`add_column_with_default` is deprecated, use `add_column` instead'
def on_send(node)
return unless in_migration?(node)
diff --git a/rubocop/cop/migration/add_concurrent_foreign_key.rb b/rubocop/cop/migration/add_concurrent_foreign_key.rb
index 957bd30af63..ebab6aa653e 100644
--- a/rubocop/cop/migration/add_concurrent_foreign_key.rb
+++ b/rubocop/cop/migration/add_concurrent_foreign_key.rb
@@ -10,7 +10,7 @@ module RuboCop
class AddConcurrentForeignKey < RuboCop::Cop::Cop
include MigrationHelpers
- MSG = '`add_foreign_key` requires downtime, use `add_concurrent_foreign_key` instead'.freeze
+ MSG = '`add_foreign_key` requires downtime, use `add_concurrent_foreign_key` instead'
def_node_matcher :false_node?, <<~PATTERN
(false)
diff --git a/rubocop/cop/migration/add_concurrent_index.rb b/rubocop/cop/migration/add_concurrent_index.rb
index 510f98ce373..bfe7c15bfdf 100644
--- a/rubocop/cop/migration/add_concurrent_index.rb
+++ b/rubocop/cop/migration/add_concurrent_index.rb
@@ -11,7 +11,7 @@ module RuboCop
include MigrationHelpers
MSG = '`add_concurrent_index` is not reversible so you must manually define ' \
- 'the `up` and `down` methods in your migration class, using `remove_concurrent_index` in `down`'.freeze
+ 'the `up` and `down` methods in your migration class, using `remove_concurrent_index` in `down`'
def on_send(node)
return unless in_migration?(node)
diff --git a/rubocop/cop/migration/add_index.rb b/rubocop/cop/migration/add_index.rb
index 7415880e554..327e89fb040 100644
--- a/rubocop/cop/migration/add_index.rb
+++ b/rubocop/cop/migration/add_index.rb
@@ -9,7 +9,7 @@ module RuboCop
class AddIndex < RuboCop::Cop::Cop
include MigrationHelpers
- MSG = '`add_index` requires downtime, use `add_concurrent_index` instead'.freeze
+ MSG = '`add_index` requires downtime, use `add_concurrent_index` instead'
def on_def(node)
return unless in_migration?(node)
diff --git a/rubocop/cop/migration/add_limit_to_text_columns.rb b/rubocop/cop/migration/add_limit_to_text_columns.rb
index 126e4e21f22..f45551e60a4 100644
--- a/rubocop/cop/migration/add_limit_to_text_columns.rb
+++ b/rubocop/cop/migration/add_limit_to_text_columns.rb
@@ -14,7 +14,7 @@ module RuboCop
include MigrationHelpers
MSG = 'Text columns should always have a limit set (255 is suggested). ' \
- 'You can add a limit to a `text` column by using `add_text_limit`'.freeze
+ 'You can add a limit to a `text` column by using `add_text_limit`'
def_node_matcher :reverting?, <<~PATTERN
(def :down ...)
diff --git a/rubocop/cop/migration/add_timestamps.rb b/rubocop/cop/migration/add_timestamps.rb
index d16e8b1f45b..01d3f01ef4f 100644
--- a/rubocop/cop/migration/add_timestamps.rb
+++ b/rubocop/cop/migration/add_timestamps.rb
@@ -9,7 +9,7 @@ module RuboCop
class AddTimestamps < RuboCop::Cop::Cop
include MigrationHelpers
- MSG = 'Do not use `add_timestamps`, use `add_timestamps_with_timezone` instead'.freeze
+ MSG = 'Do not use `add_timestamps`, use `add_timestamps_with_timezone` instead'
# Check methods.
def on_send(node)
diff --git a/rubocop/cop/migration/datetime.rb b/rubocop/cop/migration/datetime.rb
index 51e0c3e5a22..c605c8e1b6e 100644
--- a/rubocop/cop/migration/datetime.rb
+++ b/rubocop/cop/migration/datetime.rb
@@ -9,7 +9,7 @@ module RuboCop
class Datetime < RuboCop::Cop::Cop
include MigrationHelpers
- MSG = 'Do not use the `%s` data type, use `datetime_with_timezone` instead'.freeze
+ MSG = 'Do not use the `%s` data type, use `datetime_with_timezone` instead'
# Check methods in table creation.
def on_def(node)
diff --git a/rubocop/cop/project_path_helper.rb b/rubocop/cop/project_path_helper.rb
index bc2454e5b1f..ec3f847faf9 100644
--- a/rubocop/cop/project_path_helper.rb
+++ b/rubocop/cop/project_path_helper.rb
@@ -5,7 +5,7 @@ module RuboCop
class ProjectPathHelper < RuboCop::Cop::Cop
MSG = 'Use short project path helpers without explicitly passing the namespace: ' \
'`foo_project_bar_path(project, bar)` instead of ' \
- '`foo_namespace_project_bar_path(project.namespace, project, bar)`.'.freeze
+ '`foo_namespace_project_bar_path(project.namespace, project, bar)`.'
METHOD_NAME_PATTERN = /\A([a-z_]+_)?namespace_project(?:_[a-z_]+)?_(?:url|path)\z/.freeze
diff --git a/rubocop/cop/safe_params.rb b/rubocop/cop/safe_params.rb
index 250c16232e4..2720732c161 100644
--- a/rubocop/cop/safe_params.rb
+++ b/rubocop/cop/safe_params.rb
@@ -3,7 +3,7 @@
module RuboCop
module Cop
class SafeParams < RuboCop::Cop::Cop
- MSG = 'Use `safe_params` instead of `params` in url_for.'.freeze
+ MSG = 'Use `safe_params` instead of `params` in url_for.'
METHOD_NAME_PATTERN = :url_for
UNSAFE_PARAM = :params
diff --git a/rubocop/cop/sidekiq_options_queue.rb b/rubocop/cop/sidekiq_options_queue.rb
index 499c712175e..2574a229ec2 100644
--- a/rubocop/cop/sidekiq_options_queue.rb
+++ b/rubocop/cop/sidekiq_options_queue.rb
@@ -4,7 +4,7 @@ module RuboCop
module Cop
# Cop that prevents manually setting a queue in Sidekiq workers.
class SidekiqOptionsQueue < RuboCop::Cop::Cop
- MSG = 'Do not manually set a queue; `ApplicationWorker` sets one automatically.'.freeze
+ MSG = 'Do not manually set a queue; `ApplicationWorker` sets one automatically.'
def_node_matcher :sidekiq_options?, <<~PATTERN
(send nil? :sidekiq_options $...)
diff --git a/spec/features/projects/blobs/blob_line_permalink_updater_spec.rb b/spec/features/projects/blobs/blob_line_permalink_updater_spec.rb
index a65a82fab43..1a368676a5e 100644
--- a/spec/features/projects/blobs/blob_line_permalink_updater_spec.rb
+++ b/spec/features/projects/blobs/blob_line_permalink_updater_spec.rb
@@ -44,17 +44,6 @@ RSpec.describe 'Blob button line permalinks (BlobLinePermalinkUpdater)', :js do
expect(find('.js-data-file-blob-permalink-url')['href']).to eq(get_absolute_url(project_blob_path(project, tree_join(sha, path), anchor: ending_fragment)))
end
- it 'changes fragment hash if icon inside line number link is clicked' do
- ending_fragment = "L7"
-
- visit_blob
-
- find("##{ending_fragment}").hover
- find("##{ending_fragment} svg").click
-
- expect(find('.js-data-file-blob-permalink-url')['href']).to eq(get_absolute_url(project_blob_path(project, tree_join(sha, path), anchor: ending_fragment)))
- end
-
it 'with initial fragment hash, changes fragment hash if line number clicked' do
fragment = "L1"
ending_fragment = "L5"
@@ -94,17 +83,6 @@ RSpec.describe 'Blob button line permalinks (BlobLinePermalinkUpdater)', :js do
expect(find('.js-blob-blame-link')['href']).to eq(get_absolute_url(project_blame_path(project, tree_join('master', path), anchor: ending_fragment)))
end
- it 'changes fragment hash if icon inside line number link is clicked' do
- ending_fragment = "L7"
-
- visit_blob
-
- find("##{ending_fragment}").hover
- find("##{ending_fragment} svg").click
-
- expect(find('.js-blob-blame-link')['href']).to eq(get_absolute_url(project_blame_path(project, tree_join('master', path), anchor: ending_fragment)))
- end
-
it 'with initial fragment hash, changes fragment hash if line number clicked' do
fragment = "L1"
ending_fragment = "L5"
diff --git a/spec/features/projects/commit/cherry_pick_spec.rb b/spec/features/projects/commit/cherry_pick_spec.rb
index 489a90cc8fc..cd944436228 100644
--- a/spec/features/projects/commit/cherry_pick_spec.rb
+++ b/spec/features/projects/commit/cherry_pick_spec.rb
@@ -91,7 +91,7 @@ RSpec.describe 'Cherry-pick Commits', :js do
context 'when the project is archived' do
let(:project) { create(:project, :repository, :archived, namespace: user.namespace) }
- it 'does not show the cherry-pick link' do
+ it 'does not show the cherry-pick button' do
open_dropdown
expect(page).not_to have_text("Cherry-pick")
@@ -106,12 +106,15 @@ RSpec.describe 'Cherry-pick Commits', :js do
end
def open_dropdown
- find('.header-action-buttons .dropdown').click
+ find(dropdown_selector).click
end
def open_modal
open_dropdown
- find('[data-testid="cherry-pick-commit-link"]').click
+
+ page.within(dropdown_selector) do
+ click_button 'Cherry-pick'
+ end
end
def submit_cherry_pick(create_merge_request: false)
@@ -121,6 +124,10 @@ RSpec.describe 'Cherry-pick Commits', :js do
end
end
+ def dropdown_selector
+ '[data-testid="commit-options-dropdown"]'
+ end
+
def modal_selector
'[data-testid="modal-commit"]'
end
diff --git a/spec/features/projects/commit/user_reverts_commit_spec.rb b/spec/features/projects/commit/user_reverts_commit_spec.rb
index 72c639a027e..ad327b86aa7 100644
--- a/spec/features/projects/commit/user_reverts_commit_spec.rb
+++ b/spec/features/projects/commit/user_reverts_commit_spec.rb
@@ -62,10 +62,10 @@ RSpec.describe 'User reverts a commit', :js do
context 'when the project is archived' do
let(:project) { create(:project, :repository, :archived, namespace: user.namespace) }
- it 'does not show the revert link' do
+ it 'does not show the revert button' do
open_dropdown
- expect(page).not_to have_link('Revert')
+ expect(page).not_to have_button('Revert')
end
end
end
@@ -75,17 +75,24 @@ RSpec.describe 'User reverts a commit', :js do
page.within(modal_selector) do
uncheck('create_merge_request') unless create_merge_request
- click_button('Revert')
+ click_button 'Revert'
end
end
def open_dropdown
- find('.header-action-buttons .dropdown').click
+ find(dropdown_selector).click
end
def open_modal
open_dropdown
- find('[data-testid="revert-commit-link"]').click
+
+ page.within(dropdown_selector) do
+ click_button 'Revert'
+ end
+ end
+
+ def dropdown_selector
+ '[data-testid="commit-options-dropdown"]'
end
def modal_selector
diff --git a/spec/features/projects/commits/user_browses_commits_spec.rb b/spec/features/projects/commits/user_browses_commits_spec.rb
index 4894e2b7f3e..76162fb800a 100644
--- a/spec/features/projects/commits/user_browses_commits_spec.rb
+++ b/spec/features/projects/commits/user_browses_commits_spec.rb
@@ -20,9 +20,14 @@ RSpec.describe 'User browses commits' do
.and have_content('Side-by-side')
end
- it 'fill commit sha when click new tag from commit page' do
+ it 'fill commit sha when click new tag from commit page', :js do
+ dropdown_selector = '[data-testid="commit-options-dropdown"]'
visit project_commit_path(project, sample_commit.id)
- click_link 'Tag'
+ find(dropdown_selector).click
+
+ page.within(dropdown_selector) do
+ click_link 'Tag'
+ end
expect(page).to have_selector("input[value='#{sample_commit.id}']", visible: false)
end
diff --git a/spec/finders/repositories/previous_tag_finder_spec.rb b/spec/finders/repositories/changelog_tag_finder_spec.rb
index b332dd158d1..cd79beb3e9e 100644
--- a/spec/finders/repositories/previous_tag_finder_spec.rb
+++ b/spec/finders/repositories/changelog_tag_finder_spec.rb
@@ -2,11 +2,18 @@
require 'spec_helper'
-RSpec.describe Repositories::PreviousTagFinder do
+RSpec.describe Repositories::ChangelogTagFinder do
let(:project) { build_stubbed(:project) }
let(:finder) { described_class.new(project) }
describe '#execute' do
+ context 'when the regular expression is invalid' do
+ it 'raises Gitlab::Changelog::Error' do
+ expect { described_class.new(project, regex: 'foo+*').execute('1.2.3') }
+ .to raise_error(Gitlab::Changelog::Error)
+ end
+ end
+
context 'when there is a previous tag' do
it 'returns the previous tag' do
tag1 = double(:tag1, name: 'v1.0.0')
@@ -15,10 +22,11 @@ RSpec.describe Repositories::PreviousTagFinder do
tag4 = double(:tag4, name: '0.9.0')
tag5 = double(:tag5, name: 'v0.8.0-pre1')
tag6 = double(:tag6, name: 'v0.7.0')
+ tag7 = double(:tag7, name: '0.5.0+42.ee.0')
allow(project.repository)
.to receive(:tags)
- .and_return([tag1, tag3, tag2, tag4, tag5, tag6])
+ .and_return([tag1, tag3, tag2, tag4, tag5, tag6, tag7])
expect(finder.execute('2.1.0')).to eq(tag3)
expect(finder.execute('2.0.0')).to eq(tag2)
@@ -26,6 +34,7 @@ RSpec.describe Repositories::PreviousTagFinder do
expect(finder.execute('1.0.1')).to eq(tag1)
expect(finder.execute('1.0.0')).to eq(tag4)
expect(finder.execute('0.9.0')).to eq(tag6)
+ expect(finder.execute('0.6.0')).to eq(tag7)
end
end
diff --git a/spec/fixtures/lib/gitlab/performance_bar/peek_data.json b/spec/fixtures/lib/gitlab/performance_bar/peek_data.json
index 8e207b69ecb..c60e787ddb1 100644
--- a/spec/fixtures/lib/gitlab/performance_bar/peek_data.json
+++ b/spec/fixtures/lib/gitlab/performance_bar/peek_data.json
@@ -13,6 +13,8 @@
"duration": 1.096,
"sql": "SELECT COUNT(*) FROM ((SELECT \"badges\".* FROM \"badges\" WHERE \"badges\".\"type\" = 'ProjectBadge' AND \"badges\".\"project_id\" = 8)\nUNION\n(SELECT \"badges\".* FROM \"badges\" WHERE \"badges\".\"type\" = 'GroupBadge' AND \"badges\".\"group_id\" IN (SELECT \"namespaces\".\"id\" FROM \"namespaces\" WHERE \"namespaces\".\"type\" = 'Group' AND \"namespaces\".\"id\" = 28))) badges",
"backtrace": [
+ "ee/lib/ee/peek/views/active_record.rb:11:in `generate_detail'",
+ "lib/peek/views/active_record.rb:42:in `block in setup_subscribers'",
"lib/gitlab/pagination/offset_pagination.rb:53:in `add_pagination_headers'",
"lib/gitlab/pagination/offset_pagination.rb:15:in `block in paginate'",
"lib/gitlab/pagination/offset_pagination.rb:14:in `tap'",
diff --git a/spec/frontend/boards/stores/getters_spec.js b/spec/frontend/boards/stores/getters_spec.js
index 32d73d861bc..0541c40061c 100644
--- a/spec/frontend/boards/stores/getters_spec.js
+++ b/spec/frontend/boards/stores/getters_spec.js
@@ -177,4 +177,31 @@ describe('Boards - Getters', () => {
expect(getters.activeGroupProjects(state)).toEqual([mockGroupProject1]);
});
});
+
+ describe('isIssueBoard', () => {
+ it.each`
+ issuableType | expected
+ ${'issue'} | ${true}
+ ${'epic'} | ${false}
+ `(
+ 'returns $expected when issuableType on state is $issuableType',
+ ({ issuableType, expected }) => {
+ const state = {
+ issuableType,
+ };
+
+ expect(getters.isIssueBoard(state)).toBe(expected);
+ },
+ );
+ });
+
+ describe('isEpicBoard', () => {
+ afterEach(() => {
+ window.gon = { features: {} };
+ });
+
+ it('returns false', () => {
+ expect(getters.isEpicBoard()).toBe(false);
+ });
+ });
});
diff --git a/spec/frontend/create_merge_request_dropdown_spec.js b/spec/frontend/create_merge_request_dropdown_spec.js
index 08c05c6ec38..b4c13981dd5 100644
--- a/spec/frontend/create_merge_request_dropdown_spec.js
+++ b/spec/frontend/create_merge_request_dropdown_spec.js
@@ -20,7 +20,9 @@ describe('CreateMergeRequestDropdown', () => {
</div>
<div class="js-ref"></div>
<div class="js-create-mr"></div>
- <div class="js-create-merge-request"></div>
+ <div class="js-create-merge-request">
+ <span class="js-spinner"></span>
+ </div>
<div class="js-create-target"></div>
<div class="js-dropdown-toggle"></div>
</div>
@@ -100,4 +102,18 @@ describe('CreateMergeRequestDropdown', () => {
expect(dropdown.createMergeRequestButton.classList).toContain('disabled');
});
});
+
+ describe('setLoading', () => {
+ it.each`
+ loading | hasClass
+ ${true} | ${false}
+ ${false} | ${true}
+ `('it toggle loading spinner when loading is $loading', ({ loading, hasClass }) => {
+ dropdown.setLoading(loading);
+
+ expect(document.querySelector('.js-spinner').classList.contains('gl-display-none')).toEqual(
+ hasClass,
+ );
+ });
+ });
});
diff --git a/spec/frontend/projects/commit/components/commit_options_dropdown_spec.js b/spec/frontend/projects/commit/components/commit_options_dropdown_spec.js
new file mode 100644
index 00000000000..70491405986
--- /dev/null
+++ b/spec/frontend/projects/commit/components/commit_options_dropdown_spec.js
@@ -0,0 +1,123 @@
+import { GlDropdownDivider, GlDropdownSectionHeader } from '@gitlab/ui';
+import { shallowMount } from '@vue/test-utils';
+import { extendedWrapper } from 'helpers/vue_test_utils_helper';
+import CommitOptionsDropdown from '~/projects/commit/components/commit_options_dropdown.vue';
+import { OPEN_REVERT_MODAL, OPEN_CHERRY_PICK_MODAL } from '~/projects/commit/constants';
+import eventHub from '~/projects/commit/event_hub';
+
+describe('BranchesDropdown', () => {
+ let wrapper;
+ const provide = {
+ newProjectTagPath: '_new_project_tag_path_',
+ emailPatchesPath: '_email_patches_path_',
+ plainDiffPath: '_plain_diff_path_',
+ };
+
+ const createComponent = (props = {}) => {
+ wrapper = extendedWrapper(
+ shallowMount(CommitOptionsDropdown, {
+ provide,
+ propsData: {
+ canRevert: true,
+ canCherryPick: true,
+ canTag: true,
+ canEmailPatches: true,
+ ...props,
+ },
+ }),
+ );
+ };
+
+ const findRevertLink = () => wrapper.findByTestId('revert-link');
+ const findCherryPickLink = () => wrapper.findByTestId('cherry-pick-link');
+ const findTagItem = () => wrapper.findByTestId('tag-link');
+ const findEmailPatchesItem = () => wrapper.findByTestId('email-patches-link');
+ const findPlainDiffItem = () => wrapper.findByTestId('plain-diff-link');
+ const findDivider = () => wrapper.findComponent(GlDropdownDivider);
+ const findSectionHeader = () => wrapper.findComponent(GlDropdownSectionHeader);
+
+ describe('Everything enabled', () => {
+ beforeEach(() => {
+ createComponent();
+ });
+
+ it('has expected dropdown button text', () => {
+ expect(wrapper.attributes('text')).toBe('Options');
+ });
+
+ it('has expected items', () => {
+ expect(
+ [
+ findRevertLink().exists(),
+ findCherryPickLink().exists(),
+ findTagItem().exists(),
+ findDivider().exists(),
+ findSectionHeader().exists(),
+ findEmailPatchesItem().exists(),
+ findPlainDiffItem().exists(),
+ ].every((exists) => exists),
+ ).toBe(true);
+ });
+
+ it('has expected href links', () => {
+ expect(findTagItem().attributes('href')).toBe(provide.newProjectTagPath);
+ expect(findEmailPatchesItem().attributes('href')).toBe(provide.emailPatchesPath);
+ expect(findPlainDiffItem().attributes('href')).toBe(provide.plainDiffPath);
+ });
+ });
+
+ describe('Different dropdown item permutations', () => {
+ it('does not have a revert option', () => {
+ createComponent({ canRevert: false });
+
+ expect(findRevertLink().exists()).toBe(false);
+ });
+
+ it('does not have a cherry-pick option', () => {
+ createComponent({ canCherryPick: false });
+
+ expect(findCherryPickLink().exists()).toBe(false);
+ });
+
+ it('does not have a tag option', () => {
+ createComponent({ canTag: false });
+
+ expect(findTagItem().exists()).toBe(false);
+ });
+
+ it('does not have a email patches options', () => {
+ createComponent({ canEmailPatches: false });
+
+ expect(findEmailPatchesItem().exists()).toBe(false);
+ });
+
+ it('only has the download items', () => {
+ createComponent({ canRevert: false, canCherryPick: false, canTag: false });
+
+ expect(findDivider().exists()).toBe(false);
+ expect(findEmailPatchesItem().exists()).toBe(true);
+ expect(findPlainDiffItem().exists()).toBe(true);
+ });
+ });
+
+ describe('Modal triggering', () => {
+ let spy;
+
+ beforeEach(() => {
+ spy = jest.spyOn(eventHub, '$emit');
+ createComponent();
+ });
+
+ it('emits openModal for revert', () => {
+ findRevertLink().vm.$emit('click');
+
+ expect(spy).toHaveBeenCalledWith(OPEN_REVERT_MODAL);
+ });
+
+ it('emits openModal for cherry-pick', () => {
+ findCherryPickLink().vm.$emit('click');
+
+ expect(spy).toHaveBeenCalledWith(OPEN_CHERRY_PICK_MODAL);
+ });
+ });
+});
diff --git a/spec/frontend/projects/commit/components/form_trigger_spec.js b/spec/frontend/projects/commit/components/form_trigger_spec.js
deleted file mode 100644
index 4503493c0a6..00000000000
--- a/spec/frontend/projects/commit/components/form_trigger_spec.js
+++ /dev/null
@@ -1,44 +0,0 @@
-import { GlLink } from '@gitlab/ui';
-import { shallowMount } from '@vue/test-utils';
-import FormTrigger from '~/projects/commit/components/form_trigger.vue';
-import eventHub from '~/projects/commit/event_hub';
-
-const displayText = '_display_text_';
-
-const createComponent = () => {
- return shallowMount(FormTrigger, {
- provide: { displayText },
- propsData: { openModal: '_open_modal_' },
- });
-};
-
-describe('FormTrigger', () => {
- let wrapper;
- let spy;
-
- beforeEach(() => {
- spy = jest.spyOn(eventHub, '$emit');
- wrapper = createComponent();
- });
-
- afterEach(() => {
- wrapper.destroy();
- wrapper = null;
- });
-
- const findLink = () => wrapper.find(GlLink);
-
- describe('displayText', () => {
- it('includes the correct displayText for the link', () => {
- expect(findLink().text()).toBe(displayText);
- });
- });
-
- describe('clicking the link', () => {
- it('emits openModal', () => {
- findLink().vm.$emit('click');
-
- expect(spy).toHaveBeenCalledWith('_open_modal_');
- });
- });
-});
diff --git a/spec/helpers/commits_helper_spec.rb b/spec/helpers/commits_helper_spec.rb
index 2a8e2e04947..9e66fee10c5 100644
--- a/spec/helpers/commits_helper_spec.rb
+++ b/spec/helpers/commits_helper_spec.rb
@@ -5,58 +5,6 @@ require 'spec_helper'
RSpec.describe CommitsHelper do
include ProjectForksHelper
- describe '#revert_commit_link' do
- context 'when current_user exists' do
- before do
- allow(helper).to receive(:current_user).and_return(double('User'))
- end
-
- it 'renders a div for Vue' do
- result = helper.revert_commit_link
-
- expect(result).to include('js-revert-commit-trigger')
- end
- end
-
- context 'when current_user does not exist' do
- before do
- allow(helper).to receive(:current_user).and_return(nil)
- end
-
- it 'does not render anything' do
- result = helper.revert_commit_link
-
- expect(result).to be_nil
- end
- end
- end
-
- describe '#cherry_pick_commit_link' do
- context 'when current_user exists' do
- before do
- allow(helper).to receive(:current_user).and_return(double('User'))
- end
-
- it 'renders a div for Vue' do
- result = helper.cherry_pick_commit_link
-
- expect(result).to include('js-cherry-pick-commit-trigger')
- end
- end
-
- context 'when current_user does not exist' do
- before do
- allow(helper).to receive(:current_user).and_return(nil)
- end
-
- it 'does not render anything' do
- result = helper.cherry_pick_commit_link
-
- expect(result).to be_nil
- end
- end
- end
-
describe 'commit_author_link' do
it 'escapes the author email' do
commit = double(
@@ -268,4 +216,77 @@ RSpec.describe CommitsHelper do
end
end
end
+
+ describe "#commit_options_dropdown_data" do
+ let(:project) { build(:project, :repository) }
+ let(:commit) { build(:commit) }
+ let(:user) { build(:user) }
+
+ subject { helper.commit_options_dropdown_data(project, commit) }
+
+ context "when user is logged in" do
+ before do
+ allow(helper).to receive(:can?).with(user, :push_code, project).and_return(true)
+ allow(helper).to receive(:current_user).and_return(user)
+ end
+
+ it "returns data as expected" do
+ is_expected.to eq standard_expected_data
+ end
+
+ context "when can not collaborate on project" do
+ before do
+ allow(helper).to receive(:can_collaborate_with_project?).with(project).and_return(false)
+ end
+
+ it "returns data as expected" do
+ no_collaboration_values = {
+ can_revert: 'false',
+ can_cherry_pick: 'false'
+ }
+
+ is_expected.to eq standard_expected_data.merge(no_collaboration_values)
+ end
+ end
+
+ context "when commit has already been reverted" do
+ before do
+ allow(commit).to receive(:has_been_reverted?).with(user).and_return(true)
+ end
+
+ it "returns data as expected" do
+ is_expected.to eq standard_expected_data.merge({ can_revert: 'false' })
+ end
+ end
+ end
+
+ context "when user is not logged in" do
+ before do
+ allow(helper).to receive(:can?).with(nil, :push_code, project).and_return(false)
+ allow(helper).to receive(:current_user).and_return(nil)
+ end
+
+ it "returns data as expected" do
+ logged_out_values = {
+ can_revert: '',
+ can_cherry_pick: '',
+ can_tag: 'false'
+ }
+
+ is_expected.to eq standard_expected_data.merge(logged_out_values)
+ end
+ end
+
+ def standard_expected_data
+ {
+ new_project_tag_path: new_project_tag_path(project, ref: commit),
+ email_patches_path: project_commit_path(project, commit, format: :patch),
+ plain_diff_path: project_commit_path(project, commit, format: :diff),
+ can_revert: 'true',
+ can_cherry_pick: 'true',
+ can_tag: 'true',
+ can_email_patches: 'true'
+ }
+ end
+ end
end
diff --git a/spec/lib/gitlab/changelog/config_spec.rb b/spec/lib/gitlab/changelog/config_spec.rb
index 51988acf3d1..2809843b832 100644
--- a/spec/lib/gitlab/changelog/config_spec.rb
+++ b/spec/lib/gitlab/changelog/config_spec.rb
@@ -37,7 +37,8 @@ RSpec.describe Gitlab::Changelog::Config do
project,
'date_format' => 'foo',
'template' => 'bar',
- 'categories' => { 'foo' => 'bar' }
+ 'categories' => { 'foo' => 'bar' },
+ 'tag_regex' => 'foo'
)
expect(config.date_format).to eq('foo')
@@ -45,6 +46,7 @@ RSpec.describe Gitlab::Changelog::Config do
.to be_instance_of(Gitlab::Changelog::AST::Expressions)
expect(config.categories).to eq({ 'foo' => 'bar' })
+ expect(config.tag_regex).to eq('foo')
end
it 'raises Error when the categories are not a Hash' do
diff --git a/spec/lib/gitlab/search_context/builder_spec.rb b/spec/lib/gitlab/search_context/builder_spec.rb
index 5b4190fc67e..079477115bb 100644
--- a/spec/lib/gitlab/search_context/builder_spec.rb
+++ b/spec/lib/gitlab/search_context/builder_spec.rb
@@ -127,6 +127,35 @@ RSpec.describe Gitlab::SearchContext::Builder, type: :controller do
it { is_expected.to be_for_group }
it { is_expected.to be_search_context(group: group) }
+
+ context 'with group scope' do
+ let(:action_name) { '' }
+
+ before do
+ allow(controller).to receive(:controller_name).and_return('groups')
+ allow(controller).to receive(:action_name).and_return(action_name)
+ end
+
+ it 'returns nil without groups controller action' do
+ expect(subject.scope).to be(nil)
+ end
+
+ context 'when on issues scope' do
+ let(:action_name) { 'issues' }
+
+ it 'search context returns issues scope' do
+ expect(subject.scope).to be('issues')
+ end
+ end
+
+ context 'when on merge requests scope' do
+ let(:action_name) { 'merge_requests' }
+
+ it 'search context returns issues scope' do
+ expect(subject.scope).to be('merge_requests')
+ end
+ end
+ end
end
end
diff --git a/spec/lib/gitlab/untrusted_regexp_spec.rb b/spec/lib/gitlab/untrusted_regexp_spec.rb
index aac3d5e27f5..270c4beec97 100644
--- a/spec/lib/gitlab/untrusted_regexp_spec.rb
+++ b/spec/lib/gitlab/untrusted_regexp_spec.rb
@@ -136,4 +136,22 @@ RSpec.describe Gitlab::UntrustedRegexp do
end
end
end
+
+ describe '#match' do
+ context 'when there are matches' do
+ it 'returns a match object' do
+ result = described_class.new('(?P<number>\d+)').match('hello 10')
+
+ expect(result[:number]).to eq('10')
+ end
+ end
+
+ context 'when there are no matches' do
+ it 'returns nil' do
+ result = described_class.new('(?P<number>\d+)').match('hello')
+
+ expect(result).to be_nil
+ end
+ end
+ end
end
diff --git a/spec/models/ci/bridge_spec.rb b/spec/models/ci/bridge_spec.rb
index f3029598b02..db956b26b6b 100644
--- a/spec/models/ci/bridge_spec.rb
+++ b/spec/models/ci/bridge_spec.rb
@@ -50,7 +50,7 @@ RSpec.describe Ci::Bridge do
CI_PROJECT_PATH_SLUG CI_PROJECT_NAMESPACE CI_PROJECT_ROOT_NAMESPACE
CI_PIPELINE_IID CI_CONFIG_PATH CI_PIPELINE_SOURCE CI_COMMIT_MESSAGE
CI_COMMIT_TITLE CI_COMMIT_DESCRIPTION CI_COMMIT_REF_PROTECTED
- CI_COMMIT_TIMESTAMP
+ CI_COMMIT_TIMESTAMP CI_COMMIT_AUTHOR
]
expect(bridge.scoped_variables.map { |v| v[:key] }).to include(*variables)
diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb
index 160faf18db8..aa3cc0800a8 100644
--- a/spec/models/ci/build_spec.rb
+++ b/spec/models/ci/build_spec.rb
@@ -2461,6 +2461,7 @@ RSpec.describe Ci::Build do
{ key: 'CI_COMMIT_DESCRIPTION', value: pipeline.git_commit_description, public: true, masked: false },
{ key: 'CI_COMMIT_REF_PROTECTED', value: (!!pipeline.protected_ref?).to_s, public: true, masked: false },
{ key: 'CI_COMMIT_TIMESTAMP', value: pipeline.git_commit_timestamp, public: true, masked: false },
+ { key: 'CI_COMMIT_AUTHOR', value: pipeline.git_author_full_text, public: true, masked: false },
{ key: 'CI_BUILD_REF', value: build.sha, public: true, masked: false },
{ key: 'CI_BUILD_BEFORE_SHA', value: build.before_sha, public: true, masked: false },
{ key: 'CI_BUILD_REF_NAME', value: build.ref, public: true, masked: false },
diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb
index d57a39d133f..dc113b5f8be 100644
--- a/spec/models/ci/pipeline_spec.rb
+++ b/spec/models/ci/pipeline_spec.rb
@@ -40,6 +40,7 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
it { is_expected.to respond_to :git_author_name }
it { is_expected.to respond_to :git_author_email }
+ it { is_expected.to respond_to :git_author_full_text }
it { is_expected.to respond_to :short_sha }
it { is_expected.to delegate_method(:full_path).to(:project).with_prefix }
@@ -819,6 +820,7 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
CI_COMMIT_DESCRIPTION
CI_COMMIT_REF_PROTECTED
CI_COMMIT_TIMESTAMP
+ CI_COMMIT_AUTHOR
CI_BUILD_REF
CI_BUILD_BEFORE_SHA
CI_BUILD_REF_NAME
diff --git a/spec/services/ci/register_job_service_spec.rb b/spec/services/ci/register_job_service_spec.rb
index 66d96c08fe1..d32c1397c9b 100644
--- a/spec/services/ci/register_job_service_spec.rb
+++ b/spec/services/ci/register_job_service_spec.rb
@@ -617,7 +617,8 @@ module Ci
it "observes queue size of only matching jobs" do
# pending_job + 2 x matching ones
- expect(Gitlab::Ci::Queue::Metrics.queue_size_total).to receive(:observe).with({}, 3)
+ expect(Gitlab::Ci::Queue::Metrics.queue_size_total).to receive(:observe)
+ .with({ runner_type: specific_runner.runner_type }, 3)
expect(execute(specific_runner)).to eq(pending_job)
end
diff --git a/spec/services/repositories/changelog_service_spec.rb b/spec/services/repositories/changelog_service_spec.rb
index dab38445ccf..02d60f076ca 100644
--- a/spec/services/repositories/changelog_service_spec.rb
+++ b/spec/services/repositories/changelog_service_spec.rb
@@ -130,13 +130,14 @@ RSpec.describe Repositories::ChangelogService do
describe '#start_of_commit_range' do
let(:project) { build_stubbed(:project) }
let(:user) { build_stubbed(:user) }
+ let(:config) { Gitlab::Changelog::Config.new(project) }
context 'when the "from" argument is specified' do
it 'returns the value of the argument' do
service = described_class
.new(project, user, version: '1.0.0', from: 'foo', to: 'bar')
- expect(service.start_of_commit_range).to eq('foo')
+ expect(service.start_of_commit_range(config)).to eq('foo')
end
end
@@ -145,12 +146,12 @@ RSpec.describe Repositories::ChangelogService do
service = described_class
.new(project, user, version: '1.0.0', to: 'bar')
- finder_spy = instance_spy(Repositories::PreviousTagFinder)
+ finder_spy = instance_spy(Repositories::ChangelogTagFinder)
tag = double(:tag, target_commit: double(:commit, id: '123'))
- allow(Repositories::PreviousTagFinder)
+ allow(Repositories::ChangelogTagFinder)
.to receive(:new)
- .with(project)
+ .with(project, regex: an_instance_of(String))
.and_return(finder_spy)
allow(finder_spy)
@@ -158,18 +159,18 @@ RSpec.describe Repositories::ChangelogService do
.with('1.0.0')
.and_return(tag)
- expect(service.start_of_commit_range).to eq('123')
+ expect(service.start_of_commit_range(config)).to eq('123')
end
it 'raises an error when no tag is found' do
service = described_class
.new(project, user, version: '1.0.0', to: 'bar')
- finder_spy = instance_spy(Repositories::PreviousTagFinder)
+ finder_spy = instance_spy(Repositories::ChangelogTagFinder)
- allow(Repositories::PreviousTagFinder)
+ allow(Repositories::ChangelogTagFinder)
.to receive(:new)
- .with(project)
+ .with(project, regex: an_instance_of(String))
.and_return(finder_spy)
allow(finder_spy)
@@ -177,7 +178,7 @@ RSpec.describe Repositories::ChangelogService do
.with('1.0.0')
.and_return(nil)
- expect { service.start_of_commit_range }
+ expect { service.start_of_commit_range(config) }
.to raise_error(Gitlab::Changelog::Error)
end
end
diff --git a/spec/views/layouts/_search.html.haml_spec.rb b/spec/views/layouts/_search.html.haml_spec.rb
new file mode 100644
index 00000000000..f0c7cb57b25
--- /dev/null
+++ b/spec/views/layouts/_search.html.haml_spec.rb
@@ -0,0 +1,68 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'layouts/_search' do
+ let(:group) { nil }
+ let(:project) { nil }
+ let(:scope) { 'issues' }
+ let(:search_context) do
+ instance_double(Gitlab::SearchContext,
+ project: project,
+ group: group,
+ scope: scope,
+ ref: nil,
+ snippets: [],
+ search_url: '/search',
+ project_metadata: {},
+ group_metadata: {})
+ end
+
+ before do
+ allow(view).to receive(:search_context).and_return(search_context)
+ allow(search_context).to receive(:code_search?).and_return(false)
+ allow(search_context).to receive(:for_snippets?).and_return(false)
+ end
+
+ shared_examples 'search context scope is set' do
+ context 'when on issues' do
+ it 'sets scope to issues' do
+ render
+
+ expect(rendered).to have_css("input[name='scope'][value='issues']", count: 1, visible: false)
+ end
+ end
+
+ context 'when on merge requests' do
+ let(:scope) { 'merge_requests' }
+
+ it 'sets scope to merge_requests' do
+ render
+
+ expect(rendered).to have_css("input[name='scope'][value='merge_requests']", count: 1, visible: false)
+ end
+ end
+ end
+
+ context 'when doing project level search' do
+ let(:project) { create(:project) }
+
+ before do
+ allow(search_context).to receive(:for_project?).and_return(true)
+ allow(search_context).to receive(:for_group?).and_return(false)
+ end
+
+ it_behaves_like 'search context scope is set'
+ end
+
+ context 'when doing group level search' do
+ let(:group) { create(:group) }
+
+ before do
+ allow(search_context).to receive(:for_project?).and_return(false)
+ allow(search_context).to receive(:for_group?).and_return(true)
+ end
+
+ it_behaves_like 'search context scope is set'
+ end
+end
diff --git a/spec/views/projects/commit/_commit_box.html.haml_spec.rb b/spec/views/projects/commit/_commit_box.html.haml_spec.rb
index 9d18519ade6..aa7e58c22d1 100644
--- a/spec/views/projects/commit/_commit_box.html.haml_spec.rb
+++ b/spec/views/projects/commit/_commit_box.html.haml_spec.rb
@@ -74,30 +74,4 @@ RSpec.describe 'projects/commit/_commit_box.html.haml' do
end
end
end
-
- context 'viewing a commit' do
- context 'as a developer' do
- before do
- allow(view).to receive(:can_collaborate_with_project?).and_return(true)
- end
-
- it 'has a link to create a new tag' do
- render
-
- expect(rendered).to have_link('Tag')
- end
- end
-
- context 'as a non-developer' do
- before do
- project.add_guest(user)
- end
-
- it 'does not have a link to create a new tag' do
- render
-
- expect(rendered).not_to have_link('Tag')
- end
- end
- end
end