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
path: root/app
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 /app
parentb38fc20ae0e90d5b1c538a139aa0a7da1b7b5726 (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
-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
38 files changed, 368 insertions, 243 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' } }