summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2020-10-20 06:09:03 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2020-10-20 06:09:03 +0000
commit99551d44588b9c815df9691c8e619eb8beaa0045 (patch)
treee1244297818b544c6837b2eefc58f1fe288b8b90
parentfe75b57542f67ae643d42e9ab7f317cedb51df71 (diff)
downloadgitlab-ce-99551d44588b9c815df9691c8e619eb8beaa0045.tar.gz
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--app/assets/javascripts/boards/components/board_extra_actions.vue57
-rw-r--r--app/assets/javascripts/boards/components/board_settings_sidebar.vue4
-rw-r--r--app/assets/javascripts/boards/index.js45
-rw-r--r--app/assets/javascripts/feature_flags/components/edit_feature_flag.vue26
-rw-r--r--app/assets/javascripts/feature_flags/components/environments_dropdown.vue7
-rw-r--r--app/assets/javascripts/feature_flags/components/form.vue22
-rw-r--r--app/assets/javascripts/feature_flags/components/new_environments_dropdown.vue9
-rw-r--r--app/assets/javascripts/feature_flags/components/new_feature_flag.vue21
-rw-r--r--app/assets/javascripts/feature_flags/components/strategy.vue6
-rw-r--r--app/assets/javascripts/feature_flags/edit.js23
-rw-r--r--app/assets/javascripts/feature_flags/new.js20
-rw-r--r--app/assets/javascripts/ide/components/ide_status_bar.vue11
-rw-r--r--app/assets/javascripts/labels_select.js23
-rw-r--r--app/assets/stylesheets/_page_specific_files.scss1
-rw-r--r--app/assets/stylesheets/page_bundles/terminal.scss3
-rw-r--r--app/assets/stylesheets/page_bundles/wiki.scss (renamed from app/assets/stylesheets/pages/wiki.scss)16
-rw-r--r--app/assets/stylesheets/pages/pipelines.scss20
-rw-r--r--app/assets/stylesheets/themes/_dark.scss1
-rw-r--r--app/controllers/concerns/issuable_collections.rb2
-rw-r--r--app/controllers/projects/issues_controller.rb1
-rw-r--r--app/controllers/projects/settings/ci_cd_controller.rb4
-rw-r--r--app/finders/issuable_finder.rb6
-rw-r--r--app/finders/merge_requests_finder.rb4
-rw-r--r--app/graphql/mutations/issues/move.rb33
-rw-r--r--app/graphql/types/mutation_type.rb1
-rw-r--r--app/helpers/issuables_helper.rb19
-rw-r--r--app/helpers/labels_helper.rb19
-rw-r--r--app/models/deployment.rb2
-rw-r--r--app/models/merge_request.rb6
-rw-r--r--app/models/project.rb2
-rw-r--r--app/views/projects/confluences/show.html.haml1
-rw-r--r--app/views/projects/environments/terminal.html.haml4
-rw-r--r--app/views/projects/jobs/terminal.html.haml5
-rw-r--r--app/views/projects/merge_requests/_merge_request.html.haml2
-rw-r--r--app/views/projects/settings/ci_cd/_form.html.haml13
-rw-r--r--app/views/projects/wikis/git_access.html.haml1
-rw-r--r--app/views/shared/issuable/_sidebar.html.haml56
-rw-r--r--app/views/shared/wikis/diff.html.haml1
-rw-r--r--app/views/shared/wikis/edit.html.haml1
-rw-r--r--app/views/shared/wikis/empty.html.haml1
-rw-r--r--app/views/shared/wikis/history.html.haml1
-rw-r--r--app/views/shared/wikis/pages.html.haml1
-rw-r--r--app/views/shared/wikis/show.html.haml1
-rw-r--r--changelogs/unreleased/forward_deployment_enabled-api.yml5
-rw-r--r--changelogs/unreleased/issue-233479-Allow_move_issues_on_graphql.yml5
-rw-r--r--config/application.rb2
-rw-r--r--config/feature_flags/development/merge_request_reviewers.yml2
-rw-r--r--config/feature_flags/development/vue_sidebar_labels.yml7
-rw-r--r--doc/administration/packages/container_registry.md2
-rw-r--r--doc/api/graphql/reference/gitlab_schema.graphql46
-rw-r--r--doc/api/graphql/reference/gitlab_schema.json157
-rw-r--r--doc/api/graphql/reference/index.md10
-rw-r--r--doc/api/groups.md1
-rw-r--r--doc/api/job_artifacts.md2
-rw-r--r--doc/api/merge_requests.md2
-rw-r--r--doc/api/projects.md8
-rw-r--r--doc/api/snippets.md6
-rw-r--r--doc/ci/cloud_deployment/index.md2
-rw-r--r--doc/ci/multi_project_pipelines.md2
-rw-r--r--doc/ci/yaml/README.md4
-rw-r--r--doc/development/database/setting_multiple_values.md4
-rw-r--r--doc/development/integrations/secure.md3
-rw-r--r--doc/user/admin_area/approving_users.md2
-rw-r--r--doc/user/group/epics/index.md2
-rw-r--r--doc/user/packages/composer_repository/index.md8
-rw-r--r--doc/user/packages/nuget_repository/index.md2
-rw-r--r--doc/user/project/labels.md6
-rw-r--r--lib/api/entities/project.rb1
-rw-r--r--lib/api/helpers/projects_helpers.rb10
-rw-r--r--lib/api/projects.rb2
-rw-r--r--locale/gitlab.pot3
-rw-r--r--rubocop/cop/graphql/id_type.rb2
-rw-r--r--spec/controllers/projects/settings/ci_cd_controller_spec.rb15
-rw-r--r--spec/features/dashboard/merge_requests_spec.rb19
-rw-r--r--spec/features/projects/settings/pipelines_settings_spec.rb4
-rw-r--r--spec/finders/merge_requests_finder_spec.rb60
-rw-r--r--spec/frontend/feature_flags/components/edit_feature_flag_spec.js21
-rw-r--r--spec/frontend/feature_flags/components/environments_dropdown_spec.js4
-rw-r--r--spec/frontend/feature_flags/components/form_spec.js28
-rw-r--r--spec/frontend/feature_flags/components/new_environments_dropdown_spec.js4
-rw-r--r--spec/frontend/feature_flags/components/new_feature_flag_spec.js24
-rw-r--r--spec/frontend/feature_flags/components/strategy_spec.js14
-rw-r--r--spec/frontend/fixtures/static/issue_sidebar_label.html26
-rw-r--r--spec/frontend/labels_issue_sidebar_spec.js98
-rw-r--r--spec/graphql/mutations/issues/move_spec.rb41
-rw-r--r--spec/helpers/issuables_helper_spec.rb17
-rw-r--r--spec/helpers/labels_helper_spec.rb20
-rw-r--r--spec/requests/api/graphql/mutations/issues/move_spec.rb73
-rw-r--r--spec/requests/api/projects_spec.rb2
89 files changed, 670 insertions, 610 deletions
diff --git a/app/assets/javascripts/boards/components/board_extra_actions.vue b/app/assets/javascripts/boards/components/board_extra_actions.vue
new file mode 100644
index 00000000000..b802ccc7882
--- /dev/null
+++ b/app/assets/javascripts/boards/components/board_extra_actions.vue
@@ -0,0 +1,57 @@
+<script>
+import { GlTooltip, GlButton } from '@gitlab/ui';
+import { __ } from '~/locale';
+
+export default {
+ name: 'BoardExtraActions',
+ components: {
+ GlTooltip,
+ GlButton,
+ },
+ props: {
+ canAdminList: {
+ type: Boolean,
+ required: true,
+ },
+ disabled: {
+ type: Boolean,
+ required: true,
+ },
+ openModal: {
+ type: Function,
+ required: true,
+ },
+ },
+ computed: {
+ tooltipTitle() {
+ if (this.disabled) {
+ return __('Please add a list to your board first');
+ }
+
+ return '';
+ },
+ },
+};
+</script>
+
+<template>
+ <div class="board-extra-actions">
+ <span ref="addIssuesButtonTooltip" class="gl-ml-3">
+ <gl-button
+ v-if="canAdminList"
+ type="button"
+ data-placement="bottom"
+ data-track-event="click_button"
+ data-track-label="board_add_issues"
+ :disabled="disabled"
+ :aria-disabled="disabled"
+ @click="openModal"
+ >
+ {{ __('Add issues') }}
+ </gl-button>
+ </span>
+ <gl-tooltip v-if="disabled" :target="() => $refs.addIssuesButtonTooltip" placement="bottom">
+ {{ tooltipTitle }}
+ </gl-tooltip>
+ </div>
+</template>
diff --git a/app/assets/javascripts/boards/components/board_settings_sidebar.vue b/app/assets/javascripts/boards/components/board_settings_sidebar.vue
index 6533990d07c..392e056dcbf 100644
--- a/app/assets/javascripts/boards/components/board_settings_sidebar.vue
+++ b/app/assets/javascripts/boards/components/board_settings_sidebar.vue
@@ -34,14 +34,14 @@ export default {
},
},
computed: {
- ...mapGetters(['isSidebarOpen']),
+ ...mapGetters(['isSidebarOpen', 'shouldUseGraphQL']),
...mapState(['activeId', 'sidebarType', 'boardLists']),
activeList() {
/*
Warning: Though a computed property it is not reactive because we are
referencing a List Model class. Reactivity only applies to plain JS objects
*/
- if (this.glFeatures.graphqlBoardLists) {
+ if (this.shouldUseGraphQL) {
return this.boardLists[this.activeId];
}
return boardsStore.state.lists.find(({ id }) => id === this.activeId);
diff --git a/app/assets/javascripts/boards/index.js b/app/assets/javascripts/boards/index.js
index 1f0ac21762d..887abe79059 100644
--- a/app/assets/javascripts/boards/index.js
+++ b/app/assets/javascripts/boards/index.js
@@ -1,6 +1,5 @@
import Vue from 'vue';
import { mapActions, mapState } from 'vuex';
-import { GlTooltip, GlButton } from '@gitlab/ui';
import 'ee_else_ce/boards/models/issue';
import 'ee_else_ce/boards/models/list';
@@ -19,6 +18,7 @@ import {
import VueApollo from 'vue-apollo';
import BoardContent from '~/boards/components/board_content.vue';
+import BoardExtraActions from '~/boards/components/board_extra_actions.vue';
import createDefaultClient from '~/lib/graphql';
import { deprecatedCreateFlash as Flash } from '~/flash';
import { __ } from '~/locale';
@@ -299,10 +299,6 @@ export default () => {
// eslint-disable-next-line no-new
new Vue({
el: issueBoardsModal,
- components: {
- GlTooltip,
- GlButton,
- },
mixins: [modalMixin],
data() {
return {
@@ -319,13 +315,6 @@ export default () => {
}
return !this.store.lists.filter(list => !list.preset).length;
},
- tooltipTitle() {
- if (this.disabled) {
- return __('Please add a list to your board first');
- }
-
- return '';
- },
},
methods: {
openModal() {
@@ -334,29 +323,15 @@ export default () => {
}
},
},
- template: `
- <div class="board-extra-actions">
- <span ref="addIssuesButtonTooltip" class="gl-ml-3">
- <gl-button
- type="button"
- data-placement="bottom"
- data-track-event="click_button"
- data-track-label="board_add_issues"
- :disabled="disabled"
- :aria-disabled="disabled"
- v-if="canAdminList"
- @click="openModal">
- Add issues
- </button>
- </span>
- <gl-tooltip
- v-if="disabled"
- :target="() => $refs.addIssuesButtonTooltip"
- placement="bottom">
- {{tooltipTitle}}
- </gl-tooltip>
- </div>
- `,
+ render(createElement) {
+ return createElement(BoardExtraActions, {
+ props: {
+ canAdminList: this.$options.el.hasAttribute('data-can-admin-list'),
+ openModal: this.openModal,
+ disabled: this.disabled,
+ },
+ });
+ },
});
}
diff --git a/app/assets/javascripts/feature_flags/components/edit_feature_flag.vue b/app/assets/javascripts/feature_flags/components/edit_feature_flag.vue
index b4a6286eb55..26b18f9bf5a 100644
--- a/app/assets/javascripts/feature_flags/components/edit_feature_flag.vue
+++ b/app/assets/javascripts/feature_flags/components/edit_feature_flag.vue
@@ -15,32 +15,13 @@ export default {
FeatureFlagForm,
},
mixins: [glFeatureFlagMixin()],
- props: {
- environmentsEndpoint: {
- type: String,
- required: true,
- },
- projectId: {
- type: String,
- required: true,
- },
- featureFlagIssuesEndpoint: {
- type: String,
- required: true,
- },
- showUserCallout: {
- type: Boolean,
- required: true,
- },
+ inject: {
+ showUserCallout: {},
userCalloutId: {
default: '',
- type: String,
- required: false,
},
userCalloutsPath: {
default: '',
- type: String,
- required: false,
},
},
data() {
@@ -150,13 +131,10 @@ export default {
<feature-flag-form
:name="name"
:description="description"
- :project-id="projectId"
:scopes="scopes"
:strategies="strategies"
:cancel-path="path"
:submit-text="__('Save changes')"
- :environments-endpoint="environmentsEndpoint"
- :feature-flag-issues-endpoint="featureFlagIssuesEndpoint"
:active="active"
:version="version"
@handleSubmit="data => updateFeatureFlag(data)"
diff --git a/app/assets/javascripts/feature_flags/components/environments_dropdown.vue b/app/assets/javascripts/feature_flags/components/environments_dropdown.vue
index 3533771e3ad..3caf536b6a2 100644
--- a/app/assets/javascripts/feature_flags/components/environments_dropdown.vue
+++ b/app/assets/javascripts/feature_flags/components/environments_dropdown.vue
@@ -32,10 +32,6 @@ export default {
GlSearchBoxByType,
},
props: {
- endpoint: {
- type: String,
- required: true,
- },
value: {
type: String,
required: false,
@@ -57,6 +53,7 @@ export default {
required: false,
},
},
+ inject: ['environmentsEndpoint'],
data() {
return {
environmentSearch: this.value,
@@ -82,7 +79,7 @@ export default {
this.isLoading = true;
this.openSuggestions();
axios
- .get(this.endpoint, { params: { query: this.environmentSearch } })
+ .get(this.environmentsEndpoint, { params: { query: this.environmentSearch } })
.then(({ data }) => {
this.results = data || [];
this.isLoading = false;
diff --git a/app/assets/javascripts/feature_flags/components/form.vue b/app/assets/javascripts/feature_flags/components/form.vue
index 2858f02688f..3c1944d91bd 100644
--- a/app/assets/javascripts/feature_flags/components/form.vue
+++ b/app/assets/javascripts/feature_flags/components/form.vue
@@ -64,10 +64,6 @@ export default {
required: false,
default: '',
},
- projectId: {
- type: String,
- required: true,
- },
scopes: {
type: Array,
required: false,
@@ -81,15 +77,6 @@ export default {
type: String,
required: true,
},
- environmentsEndpoint: {
- type: String,
- required: true,
- },
- featureFlagIssuesEndpoint: {
- type: String,
- required: false,
- default: '',
- },
strategies: {
type: Array,
required: false,
@@ -101,6 +88,12 @@ export default {
default: LEGACY_FLAG,
},
},
+ inject: {
+ projectId: {},
+ featureFlagIssuesEndpoint: {
+ default: '',
+ },
+ },
translations: {
allEnvironmentsText: s__('FeatureFlags|* (All Environments)'),
@@ -353,7 +346,6 @@ export default {
:key="keyFor(strategy)"
:strategy="strategy"
:index="index"
- :endpoint="environmentsEndpoint"
:user-lists="userLists"
@change="onFormStrategyChange($event, index)"
@delete="deleteStrategy(strategy)"
@@ -411,7 +403,6 @@ export default {
v-else
class="col-12"
:value="scope.environmentScope"
- :endpoint="environmentsEndpoint"
:disabled="!canUpdateScope(scope) || scope.environmentScope !== ''"
@selectEnvironment="env => (scope.environmentScope = env)"
@createClicked="env => (scope.environmentScope = env)"
@@ -547,7 +538,6 @@ export default {
<div class="table-mobile-content js-feature-flag-status">
<environments-dropdown
class="js-new-scope-name col-12"
- :endpoint="environmentsEndpoint"
:value="newScope"
@selectEnvironment="env => createNewScope({ environmentScope: env })"
@createClicked="env => createNewScope({ environmentScope: env })"
diff --git a/app/assets/javascripts/feature_flags/components/new_environments_dropdown.vue b/app/assets/javascripts/feature_flags/components/new_environments_dropdown.vue
index f1371c0320d..f2017c22abf 100644
--- a/app/assets/javascripts/feature_flags/components/new_environments_dropdown.vue
+++ b/app/assets/javascripts/feature_flags/components/new_environments_dropdown.vue
@@ -21,12 +21,7 @@ export default {
GlIcon,
GlLoadingIcon,
},
- props: {
- endpoint: {
- type: String,
- required: true,
- },
- },
+ inject: ['environmentsEndpoint'],
data() {
return {
environmentSearch: '',
@@ -52,7 +47,7 @@ export default {
fetchEnvironments: debounce(function debouncedFetchEnvironments() {
this.isLoading = true;
axios
- .get(this.endpoint, { params: { query: this.environmentSearch } })
+ .get(this.environmentsEndpoint, { params: { query: this.environmentSearch } })
.then(({ data }) => {
this.results = data || [];
})
diff --git a/app/assets/javascripts/feature_flags/components/new_feature_flag.vue b/app/assets/javascripts/feature_flags/components/new_feature_flag.vue
index 927265b83a1..9472eddf336 100644
--- a/app/assets/javascripts/feature_flags/components/new_feature_flag.vue
+++ b/app/assets/javascripts/feature_flags/components/new_feature_flag.vue
@@ -19,28 +19,13 @@ export default {
FeatureFlagForm,
},
mixins: [featureFlagsMixin()],
- props: {
- environmentsEndpoint: {
- type: String,
- required: true,
- },
- projectId: {
- type: String,
- required: true,
- },
- showUserCallout: {
- type: Boolean,
- required: true,
- },
+ inject: {
+ showUserCallout: {},
userCalloutId: {
default: '',
- type: String,
- required: false,
},
userCalloutsPath: {
default: '',
- type: String,
- required: false,
},
},
data() {
@@ -105,12 +90,10 @@ export default {
</div>
<feature-flag-form
- :project-id="projectId"
:cancel-path="path"
:submit-text="s__('FeatureFlags|Create feature flag')"
:scopes="scopes"
:strategies="strategies"
- :environments-endpoint="environmentsEndpoint"
:version="version"
@handleSubmit="data => createFeatureFlag(data)"
/>
diff --git a/app/assets/javascripts/feature_flags/components/strategy.vue b/app/assets/javascripts/feature_flags/components/strategy.vue
index 6ef7de62386..9c41dde62e4 100644
--- a/app/assets/javascripts/feature_flags/components/strategy.vue
+++ b/app/assets/javascripts/feature_flags/components/strategy.vue
@@ -41,11 +41,6 @@ export default {
type: Number,
required: true,
},
- endpoint: {
- type: String,
- required: false,
- default: '',
- },
userLists: {
type: Array,
required: false,
@@ -182,7 +177,6 @@ export default {
>
<new-environments-dropdown
:id="environmentsDropdownId"
- :endpoint="endpoint"
class="gl-mr-3"
@add="addEnvironment"
/>
diff --git a/app/assets/javascripts/feature_flags/edit.js b/app/assets/javascripts/feature_flags/edit.js
index 2e2e2383eb6..b4d2111acf3 100644
--- a/app/assets/javascripts/feature_flags/edit.js
+++ b/app/assets/javascripts/feature_flags/edit.js
@@ -13,6 +13,12 @@ export default () => {
strategyTypeDocsPagePath,
endpoint,
featureFlagsPath,
+ environmentsEndpoint,
+ projectId,
+ featureFlagIssuesEndpoint,
+ userCalloutsPath,
+ userCalloutId,
+ showUserCallout,
} = el.dataset;
return new Vue({
@@ -21,18 +27,15 @@ export default () => {
provide: {
environmentsScopeDocsPath,
strategyTypeDocsPagePath,
+ environmentsEndpoint,
+ projectId,
+ featureFlagIssuesEndpoint,
+ userCalloutsPath,
+ userCalloutId,
+ showUserCallout: parseBoolean(showUserCallout),
},
render(createElement) {
- return createElement(EditFeatureFlag, {
- props: {
- environmentsEndpoint: el.dataset.environmentsEndpoint,
- projectId: el.dataset.projectId,
- featureFlagIssuesEndpoint: el.dataset.featureFlagIssuesEndpoint,
- userCalloutsPath: el.dataset.userCalloutsPath,
- userCalloutId: el.dataset.userCalloutId,
- showUserCallout: parseBoolean(el.dataset.showUserCallout),
- },
- });
+ return createElement(EditFeatureFlag);
},
});
};
diff --git a/app/assets/javascripts/feature_flags/new.js b/app/assets/javascripts/feature_flags/new.js
index 8f1436314e0..a1efbd87ec4 100644
--- a/app/assets/javascripts/feature_flags/new.js
+++ b/app/assets/javascripts/feature_flags/new.js
@@ -13,6 +13,11 @@ export default () => {
strategyTypeDocsPagePath,
endpoint,
featureFlagsPath,
+ environmentsEndpoint,
+ projectId,
+ userCalloutsPath,
+ userCalloutId,
+ showUserCallout,
} = el.dataset;
return new Vue({
@@ -21,17 +26,14 @@ export default () => {
provide: {
environmentsScopeDocsPath,
strategyTypeDocsPagePath,
+ environmentsEndpoint,
+ projectId,
+ userCalloutsPath,
+ userCalloutId,
+ showUserCallout: parseBoolean(showUserCallout),
},
render(createElement) {
- return createElement(NewFeatureFlag, {
- props: {
- environmentsEndpoint: el.dataset.environmentsEndpoint,
- projectId: el.dataset.projectId,
- userCalloutsPath: el.dataset.userCalloutsPath,
- userCalloutId: el.dataset.userCalloutId,
- showUserCallout: parseBoolean(el.dataset.showUserCallout),
- },
- });
+ return createElement(NewFeatureFlag);
},
});
};
diff --git a/app/assets/javascripts/ide/components/ide_status_bar.vue b/app/assets/javascripts/ide/components/ide_status_bar.vue
index 146e818d654..ee292190e06 100644
--- a/app/assets/javascripts/ide/components/ide_status_bar.vue
+++ b/app/assets/javascripts/ide/components/ide_status_bar.vue
@@ -1,10 +1,9 @@
<script>
/* eslint-disable @gitlab/vue-require-i18n-strings */
import { mapActions, mapState, mapGetters } from 'vuex';
-import { GlIcon } from '@gitlab/ui';
+import { GlIcon, GlTooltipDirective } from '@gitlab/ui';
import IdeStatusList from './ide_status_list.vue';
import IdeStatusMr from './ide_status_mr.vue';
-import tooltip from '~/vue_shared/directives/tooltip';
import timeAgoMixin from '~/vue_shared/mixins/timeago';
import CiIcon from '../../vue_shared/components/ci_icon.vue';
import userAvatarImage from '../../vue_shared/components/user_avatar/user_avatar_image.vue';
@@ -19,7 +18,7 @@ export default {
IdeStatusMr,
},
directives: {
- tooltip,
+ GlTooltip: GlTooltipDirective,
},
mixins: [timeAgoMixin],
data() {
@@ -85,7 +84,7 @@ export default {
@click="openRightPane($options.rightSidebarViews.pipelines)"
>
<ci-icon
- v-tooltip
+ v-gl-tooltip
:status="latestPipeline.details.status"
:title="latestPipeline.details.status.text"
/>
@@ -99,7 +98,7 @@ export default {
<gl-icon name="commit" />
<a
- v-tooltip
+ v-gl-tooltip
:title="lastCommit.message"
:href="getCommitPath(lastCommit.short_id)"
class="commit-sha"
@@ -116,7 +115,7 @@ export default {
/>
{{ lastCommit.author_name }}
<time
- v-tooltip
+ v-gl-tooltip
:datetime="lastCommit.committed_date"
:title="tooltipTitle(lastCommit.committed_date)"
data-placement="top"
diff --git a/app/assets/javascripts/labels_select.js b/app/assets/javascripts/labels_select.js
index 7b8193d5bf3..8bbd4300c96 100644
--- a/app/assets/javascripts/labels_select.js
+++ b/app/assets/javascripts/labels_select.js
@@ -4,7 +4,7 @@
import $ from 'jquery';
import { difference, isEqual, escape, sortBy, template, union } from 'lodash';
-import { sprintf, s__, __ } from './locale';
+import { sprintf, __ } from './locale';
import axios from './lib/utils/axios_utils';
import IssuableBulkUpdateActions from './issuable_bulk_update_actions';
import CreateLabelDropdown from './create_label';
@@ -13,7 +13,6 @@ import ModalStore from './boards/stores/modal_store';
import boardsStore from './boards/stores/boards_store';
import { isScopedLabel } from '~/lib/utils/common_utils';
import initDeprecatedJQueryDropdown from '~/deprecated_jquery_dropdown';
-import { fixTitle } from '~/tooltips';
export default class LabelsSelect {
constructor(els, options = {}) {
@@ -44,7 +43,6 @@ export default class LabelsSelect {
const $block = $selectbox.closest('.block');
const $form = $dropdown.closest('form, .js-issuable-update');
const $sidebarCollapsedValue = $block.find('.sidebar-collapsed-icon span');
- const $sidebarLabelTooltip = $block.find('.js-sidebar-labels-tooltip');
const $value = $block.find('.value');
const $dropdownMenu = $dropdown.parent().find('.dropdown-menu');
// eslint-disable-next-line no-jquery/no-fade
@@ -91,7 +89,6 @@ export default class LabelsSelect {
axios
.put(issueUpdateURL, data)
.then(({ data }) => {
- let labelTooltipTitle;
let template;
// eslint-disable-next-line no-jquery/no-fade
$loading.fadeOut();
@@ -151,24 +148,6 @@ export default class LabelsSelect {
$value.removeAttr('style').html(template);
$sidebarCollapsedValue.text(labelCount);
- if (data.labels.length) {
- let labelTitles = data.labels.map(label => label.title);
-
- if (labelTitles.length > 5) {
- labelTitles = labelTitles.slice(0, 5);
- labelTitles.push(
- sprintf(s__('Labels|and %{count} more'), { count: data.labels.length - 5 }),
- );
- }
-
- labelTooltipTitle = labelTitles.join(', ');
- } else {
- labelTooltipTitle = __('Labels');
- }
-
- $sidebarLabelTooltip.attr('title', labelTooltipTitle);
- fixTitle($sidebarLabelTooltip);
-
$('.has-tooltip', $value).tooltip({
container: 'body',
});
diff --git a/app/assets/stylesheets/_page_specific_files.scss b/app/assets/stylesheets/_page_specific_files.scss
index c912f9bfd3c..a31cb0b0485 100644
--- a/app/assets/stylesheets/_page_specific_files.scss
+++ b/app/assets/stylesheets/_page_specific_files.scss
@@ -44,4 +44,3 @@
@import './pages/tree';
@import './pages/trials';
@import './pages/users';
-@import './pages/wiki';
diff --git a/app/assets/stylesheets/page_bundles/terminal.scss b/app/assets/stylesheets/page_bundles/terminal.scss
new file mode 100644
index 00000000000..627baf96d6f
--- /dev/null
+++ b/app/assets/stylesheets/page_bundles/terminal.scss
@@ -0,0 +1,3 @@
+#terminal > div {
+ min-height: 450px;
+}
diff --git a/app/assets/stylesheets/pages/wiki.scss b/app/assets/stylesheets/page_bundles/wiki.scss
index ccf11058b5b..eb34e7f3876 100644
--- a/app/assets/stylesheets/pages/wiki.scss
+++ b/app/assets/stylesheets/page_bundles/wiki.scss
@@ -1,3 +1,5 @@
+@import 'mixins_and_variables_and_functions';
+
.title .edit-wiki-header {
width: 780px;
margin-left: auto;
@@ -9,7 +11,7 @@
position: relative;
.wiki-breadcrumb {
- border-bottom: 1px solid $white-normal;
+ border-bottom: 1px solid var(--gray-50, $gray-50);
padding: 11px 0;
}
@@ -20,16 +22,16 @@
.wiki-last-edit-by {
display: block;
- color: $gl-text-color-secondary;
+ color: var(--gray-500, $gray-500);
strong {
- color: $gl-text-color;
+ color: var(--gl-text-color, $gl-text-color);
}
}
.light {
font-weight: $gl-font-weight-normal;
- color: $gl-text-color-secondary;
+ color: var(--gray-500, $gray-500);
}
.git-clone-holder {
@@ -92,7 +94,7 @@
}
a {
- color: $layout-link-gray;
+ color: var(--gray-400, $gray-400);
&:hover,
&.active {
@@ -105,7 +107,7 @@
}
.active > a {
- color: $black;
+ color: var(--black, $black);
}
ul.wiki-pages,
@@ -134,7 +136,7 @@
ul.wiki-pages-list.content-list {
a {
- color: $blue-600;
+ color: var(--blue-600, $blue-600);
}
ul {
diff --git a/app/assets/stylesheets/pages/pipelines.scss b/app/assets/stylesheets/pages/pipelines.scss
index 9a2e7565882..2df43b861b2 100644
--- a/app/assets/stylesheets/pages/pipelines.scss
+++ b/app/assets/stylesheets/pages/pipelines.scss
@@ -89,26 +89,6 @@
}
}
-/**
- * Terminal
- */
-[data-page='projects:jobs:terminal'],
-[data-page='projects:environments:terminal'] {
- .terminal-container {
- .content-block {
- border-bottom: 0;
- }
-
- #terminal {
- margin-top: 10px;
-
- > div {
- min-height: 450px;
- }
- }
- }
-}
-
.pipelines-container .top-area .nav-controls > .btn:last-child {
float: none;
}
diff --git a/app/assets/stylesheets/themes/_dark.scss b/app/assets/stylesheets/themes/_dark.scss
index cd607e9b247..66cc4452858 100644
--- a/app/assets/stylesheets/themes/_dark.scss
+++ b/app/assets/stylesheets/themes/_dark.scss
@@ -165,6 +165,7 @@ body.gl-dark {
--border-color: #{$border-color};
--white: #{$white};
+ --black: #{$black};
}
$border-white-light: $gray-900;
diff --git a/app/controllers/concerns/issuable_collections.rb b/app/controllers/concerns/issuable_collections.rb
index 3f5f3b6e9df..0d7af57328a 100644
--- a/app/controllers/concerns/issuable_collections.rb
+++ b/app/controllers/concerns/issuable_collections.rb
@@ -150,7 +150,7 @@ module IssuableCollections
common_attributes + [:project, project: :namespace]
when 'MergeRequest'
common_attributes + [
- :target_project, :latest_merge_request_diff, :approvals, :approved_by_users, :reviewers,
+ :target_project, :latest_merge_request_diff, :approvals, :approved_by_users,
source_project: :route, head_pipeline: :project, target_project: :namespace
]
end
diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb
index e0321b0b657..9a8965dbeb6 100644
--- a/app/controllers/projects/issues_controller.rb
+++ b/app/controllers/projects/issues_controller.rb
@@ -44,7 +44,6 @@ class Projects::IssuesController < Projects::ApplicationController
push_frontend_feature_flag(:vue_issuable_sidebar, project.group)
push_frontend_feature_flag(:tribute_autocomplete, @project)
push_frontend_feature_flag(:vue_issuables_list, project)
- push_frontend_feature_flag(:vue_sidebar_labels, @project, default_enabled: true)
end
before_action only: :show do
diff --git a/app/controllers/projects/settings/ci_cd_controller.rb b/app/controllers/projects/settings/ci_cd_controller.rb
index 0920a27fcfb..2963321f803 100644
--- a/app/controllers/projects/settings/ci_cd_controller.rb
+++ b/app/controllers/projects/settings/ci_cd_controller.rb
@@ -76,9 +76,9 @@ module Projects
[
:runners_token, :builds_enabled, :build_allow_git_fetch,
:build_timeout_human_readable, :build_coverage_regex, :public_builds,
- :auto_cancel_pending_pipelines, :forward_deployment_enabled, :ci_config_path,
+ :auto_cancel_pending_pipelines, :ci_config_path,
auto_devops_attributes: [:id, :domain, :enabled, :deploy_strategy],
- ci_cd_settings_attributes: [:default_git_depth]
+ ci_cd_settings_attributes: [:default_git_depth, :forward_deployment_enabled]
].tap do |list|
list << :max_artifacts_size if can?(current_user, :update_max_artifacts_size, project)
end
diff --git a/app/finders/issuable_finder.rb b/app/finders/issuable_finder.rb
index d543267bd44..9c4aecedd93 100644
--- a/app/finders/issuable_finder.rb
+++ b/app/finders/issuable_finder.rb
@@ -399,7 +399,7 @@ class IssuableFinder
elsif params.filter_by_any_assignee?
items.assigned
elsif params.assignee
- items_assigned_to(items, params.assignee)
+ items.assigned_to(params.assignee)
elsif params.assignee_id? || params.assignee_username? # assignee not found
items.none
else
@@ -407,10 +407,6 @@ class IssuableFinder
end
end
- def items_assigned_to(items, user)
- items.assigned_to(user)
- end
-
def by_negated_assignee(items)
# We want CE users to be able to say "Issues not assigned to either PersonA nor PersonB"
if not_params.assignees.present?
diff --git a/app/finders/merge_requests_finder.rb b/app/finders/merge_requests_finder.rb
index 9669d4acf2d..c998de75ab2 100644
--- a/app/finders/merge_requests_finder.rb
+++ b/app/finders/merge_requests_finder.rb
@@ -164,10 +164,6 @@ class MergeRequestsFinder < IssuableFinder
end
# rubocop: enable CodeReuse/Finder
- def items_assigned_to(items, user)
- MergeRequest.from_union([super, items.reviewer_assigned_to(user)])
- end
-
def by_deployments(items)
# Until this feature flag is enabled permanently, we retain the old
# filtering behaviour/code.
diff --git a/app/graphql/mutations/issues/move.rb b/app/graphql/mutations/issues/move.rb
new file mode 100644
index 00000000000..e6971c9df8c
--- /dev/null
+++ b/app/graphql/mutations/issues/move.rb
@@ -0,0 +1,33 @@
+# frozen_string_literal: true
+
+module Mutations
+ module Issues
+ class Move < Base
+ graphql_name 'IssueMove'
+
+ argument :target_project_path,
+ GraphQL::ID_TYPE,
+ required: true,
+ description: 'The project to move the issue to'
+
+ def resolve(project_path:, iid:, target_project_path:)
+ Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab/-/issues/267762')
+
+ issue = authorized_find!(project_path: project_path, iid: iid)
+ source_project = issue.project
+ target_project = resolve_project(full_path: target_project_path).sync
+
+ begin
+ moved_issue = ::Issues::MoveService.new(source_project, current_user).execute(issue, target_project)
+ rescue ::Issues::MoveService::MoveError => error
+ errors = error.message
+ end
+
+ {
+ issue: moved_issue,
+ errors: Array.wrap(errors)
+ }
+ end
+ end
+ end
+end
diff --git a/app/graphql/types/mutation_type.rb b/app/graphql/types/mutation_type.rb
index de60279a71f..3f48e7b4a16 100644
--- a/app/graphql/types/mutation_type.rb
+++ b/app/graphql/types/mutation_type.rb
@@ -31,6 +31,7 @@ module Types
mount_mutation Mutations::Issues::SetSeverity
mount_mutation Mutations::Issues::SetSubscription
mount_mutation Mutations::Issues::Update
+ mount_mutation Mutations::Issues::Move
mount_mutation Mutations::MergeRequests::Create
mount_mutation Mutations::MergeRequests::Update
mount_mutation Mutations::MergeRequests::SetLabels
diff --git a/app/helpers/issuables_helper.rb b/app/helpers/issuables_helper.rb
index 70ffbf3b1dd..f8e7711959a 100644
--- a/app/helpers/issuables_helper.rb
+++ b/app/helpers/issuables_helper.rb
@@ -49,12 +49,6 @@ module IssuablesHelper
"#{due_date.to_s(:medium)} (#{remaining_days_in_words(due_date, start_date)})"
end
- def sidebar_label_filter_path(base_path, label_name)
- query_params = { label_name: [label_name] }.to_query
-
- "#{base_path}?#{query_params}"
- end
-
def multi_label_name(current_labels, default_label)
return default_label if current_labels.blank?
@@ -228,19 +222,6 @@ module IssuablesHelper
nil
end
- def issuable_labels_tooltip(labels, limit: 5)
- first, last = labels.partition.with_index { |_, i| i < limit }
-
- if labels && labels.any?
- label_names = first.collect { |label| label.fetch(:title) }
- label_names << "and #{last.size} more" unless last.empty?
-
- label_names.join(', ')
- else
- _("Labels")
- end
- end
-
def issuables_state_counter_text(issuable_type, state, display_count)
titles = {
opened: "Open"
diff --git a/app/helpers/labels_helper.rb b/app/helpers/labels_helper.rb
index 436af08ce01..312d535a92c 100644
--- a/app/helpers/labels_helper.rb
+++ b/app/helpers/labels_helper.rb
@@ -241,25 +241,6 @@ module LabelsHelper
}.merge(opts)
end
- def sidebar_label_dropdown_data(issuable_type, issuable_sidebar)
- label_dropdown_data(nil, {
- default_label: "Labels",
- field_name: "#{issuable_type}[label_names][]",
- ability_name: issuable_type,
- namespace_path: issuable_sidebar[:namespace_path],
- project_path: issuable_sidebar[:project_path],
- issue_update: issuable_sidebar[:issuable_json_path],
- labels: issuable_sidebar[:project_labels_path],
- display: 'static'
- })
- end
-
- def label_from_hash(hash)
- klass = hash[:group_id] ? GroupLabel : ProjectLabel
-
- klass.new(hash.slice(:color, :description, :title, :group_id, :project_id))
- end
-
def issuable_types
['issues', 'merge requests']
end
diff --git a/app/models/deployment.rb b/app/models/deployment.rb
index 381ba9d27d3..2d0d98136ec 100644
--- a/app/models/deployment.rb
+++ b/app/models/deployment.rb
@@ -70,7 +70,7 @@ class Deployment < ApplicationRecord
end
after_transition any => :running do |deployment|
- next unless deployment.project.forward_deployment_enabled?
+ next unless deployment.project.ci_forward_deployment_enabled?
deployment.run_after_commit do
Deployments::DropOlderDeploymentsWorker.perform_async(id)
diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb
index a9bccb02689..24541ba3218 100644
--- a/app/models/merge_request.rb
+++ b/app/models/merge_request.rb
@@ -302,10 +302,6 @@ class MergeRequest < ApplicationRecord
includes(:metrics)
end
- scope :reviewer_assigned_to, ->(user) do
- where("EXISTS (SELECT TRUE FROM merge_request_reviewers WHERE user_id = ? AND merge_request_id = merge_requests.id)", user.id)
- end
-
after_save :keep_around_commit, unless: :importing?
alias_attribute :project, :target_project
@@ -1695,7 +1691,7 @@ class MergeRequest < ApplicationRecord
end
def allows_reviewers?
- Feature.enabled?(:merge_request_reviewers, project, default_enabled: true)
+ Feature.enabled?(:merge_request_reviewers, project)
end
def allows_multiple_reviewers?
diff --git a/app/models/project.rb b/app/models/project.rb
index 5b1cdf0a5b4..dbedd6d120c 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -400,7 +400,7 @@ class Project < ApplicationRecord
delegate :external_dashboard_url, to: :metrics_setting, allow_nil: true, prefix: true
delegate :dashboard_timezone, to: :metrics_setting, allow_nil: true, prefix: true
delegate :default_git_depth, :default_git_depth=, to: :ci_cd_settings, prefix: :ci
- delegate :forward_deployment_enabled, :forward_deployment_enabled=, :forward_deployment_enabled?, to: :ci_cd_settings
+ delegate :forward_deployment_enabled, :forward_deployment_enabled=, :forward_deployment_enabled?, to: :ci_cd_settings, prefix: :ci
delegate :actual_limits, :actual_plan_name, to: :namespace, allow_nil: true
delegate :allow_merge_on_skipped_pipeline, :allow_merge_on_skipped_pipeline?,
:allow_merge_on_skipped_pipeline=, :has_confluence?,
diff --git a/app/views/projects/confluences/show.html.haml b/app/views/projects/confluences/show.html.haml
index b87780db4cd..5814b7a00f5 100644
--- a/app/views/projects/confluences/show.html.haml
+++ b/app/views/projects/confluences/show.html.haml
@@ -1,5 +1,6 @@
- breadcrumb_title _('Confluence')
- page_title _('Confluence')
+- add_page_specific_style 'page_bundles/wiki'
= render layout: 'shared/empty_states/wikis_layout', locals: { image_path: 'illustrations/wiki_login_empty.svg' } do
%h4
= s_('WikiEmpty|Confluence is enabled')
diff --git a/app/views/projects/environments/terminal.html.haml b/app/views/projects/environments/terminal.html.haml
index 3a705d736f3..ed0bc0680d7 100644
--- a/app/views/projects/environments/terminal.html.haml
+++ b/app/views/projects/environments/terminal.html.haml
@@ -1,5 +1,5 @@
- page_title _("Terminal for environment"), @environment.name
-
+- add_page_specific_style 'page_bundles/terminal'
- content_for :page_specific_javascripts do
= stylesheet_link_tag "xterm.css"
@@ -18,4 +18,4 @@
= render 'projects/deployments/actions', deployment: @environment.last_deployment
.terminal-container{ class: container_class }
- #terminal{ data: { project_path: "#{terminal_project_environment_path(@project, @environment)}.ws" } }
+ #terminal.gl-mt-4{ data: { project_path: "#{terminal_project_environment_path(@project, @environment)}.ws" } }
diff --git a/app/views/projects/jobs/terminal.html.haml b/app/views/projects/jobs/terminal.html.haml
index 01f40543926..95acbcae6d9 100644
--- a/app/views/projects/jobs/terminal.html.haml
+++ b/app/views/projects/jobs/terminal.html.haml
@@ -2,9 +2,10 @@
- add_to_breadcrumbs "##{@build.id}", project_job_path(@project, @build)
- breadcrumb_title _('Terminal')
- page_title _('Terminal'), "#{@build.name} (##{@build.id})", _('Jobs')
-
+- add_page_specific_style 'page_bundles/terminal'
- content_for :page_specific_javascripts do
= stylesheet_link_tag "xterm.css"
+
.terminal-container
- #terminal{ data: { project_path: terminal_project_job_path(@project, @build, format: :ws) } }
+ #terminal.gl-mt-4{ data: { project_path: terminal_project_job_path(@project, @build, format: :ws) } }
diff --git a/app/views/projects/merge_requests/_merge_request.html.haml b/app/views/projects/merge_requests/_merge_request.html.haml
index 5c1b42bb0c1..092055a5f85 100644
--- a/app/views/projects/merge_requests/_merge_request.html.haml
+++ b/app/views/projects/merge_requests/_merge_request.html.haml
@@ -55,7 +55,7 @@
- if merge_request.assignees.any?
%li.gl-display-flex.gl-align-items-center
= render 'shared/issuable/assignees', project: merge_request.project, issuable: merge_request
- - if Feature.enabled?(:merge_request_reviewers, @project, default_enabled: true) && merge_request.reviewers.any?
+ - if Feature.enabled?(:merge_request_reviewers, @project) && merge_request.reviewers.any?
%li.gl-display-flex.issuable-reviewers
= render 'shared/issuable/reviewers', project: merge_request.project, issuable: merge_request
= render 'projects/merge_requests/approvals_count', merge_request: merge_request
diff --git a/app/views/projects/settings/ci_cd/_form.html.haml b/app/views/projects/settings/ci_cd/_form.html.haml
index 414a5f264bd..4793e685163 100644
--- a/app/views/projects/settings/ci_cd/_form.html.haml
+++ b/app/views/projects/settings/ci_cd/_form.html.haml
@@ -90,12 +90,13 @@
.form-group
.form-check
- = f.check_box :forward_deployment_enabled, { class: 'form-check-input' }
- = f.label :forward_deployment_enabled, class: 'form-check-label' do
- %strong= _("Skip outdated deployment jobs")
- .form-text.text-muted
- = _("When a deployment job is successful, skip older deployment jobs that are still pending")
- = link_to sprite_icon('question-o'), help_page_path('ci/pipelines/settings', anchor: 'skip-outdated-deployment-jobs'), target: '_blank'
+ = f.fields_for :ci_cd_settings_attributes, @project.ci_cd_settings do |form|
+ = form.check_box :forward_deployment_enabled, { class: 'form-check-input' }
+ = form.label :forward_deployment_enabled, class: 'form-check-label' do
+ %strong= _("Skip outdated deployment jobs")
+ .form-text.text-muted
+ = _("When a deployment job is successful, skip older deployment jobs that are still pending")
+ = link_to sprite_icon('question-o'), help_page_path('ci/pipelines/settings', anchor: 'skip-outdated-deployment-jobs'), target: '_blank'
%hr
.form-group
diff --git a/app/views/projects/wikis/git_access.html.haml b/app/views/projects/wikis/git_access.html.haml
index 1db4554541d..c166642bae2 100644
--- a/app/views/projects/wikis/git_access.html.haml
+++ b/app/views/projects/wikis/git_access.html.haml
@@ -1,5 +1,6 @@
- @content_class = "limit-container-width" unless fluid_layout
- page_title s_("WikiClone|Git Access"), _("Wiki")
+- add_page_specific_style 'page_bundles/wiki'
.wiki-page-header.top-area.has-sidebar-toggle.py-3.flex-column.flex-lg-row
= wiki_sidebar_toggle_button
diff --git a/app/views/shared/issuable/_sidebar.html.haml b/app/views/shared/issuable/_sidebar.html.haml
index 42da9db4b09..458703ebc5f 100644
--- a/app/views/shared/issuable/_sidebar.html.haml
+++ b/app/views/shared/issuable/_sidebar.html.haml
@@ -25,7 +25,7 @@
.block.assignee.qa-assignee-block
= render "shared/issuable/sidebar_assignees", issuable_sidebar: issuable_sidebar, assignees: assignees, signed_in: signed_in
- - if Feature.enabled?(:merge_request_reviewers, @project, default_enabled: true) && reviewers
+ - if Feature.enabled?(:merge_request_reviewers, @project) && reviewers
.block.reviewer.qa-reviewer-block
= render "shared/issuable/sidebar_reviewers", issuable_sidebar: issuable_sidebar, reviewers: reviewers, signed_in: signed_in
@@ -103,49 +103,17 @@
.js-due-date-calendar
- - if Feature.enabled?(:vue_sidebar_labels, @project, default_enabled: true)
- .js-sidebar-labels{ data: { allow_label_create: issuable_sidebar.dig(:current_user, :can_admin_label).to_s,
- allow_scoped_labels: issuable_sidebar[:scoped_labels_available].to_s,
- can_edit: can_edit_issuable.to_s,
- iid: issuable_sidebar[:iid],
- issuable_type: issuable_type,
- labels_fetch_path: issuable_sidebar[:project_labels_path],
- labels_manage_path: project_labels_path(@project),
- labels_update_path: issuable_sidebar[:issuable_json_path],
- project_issues_path: issuable_sidebar[:project_issuables_path],
- project_path: @project.full_path,
- selected_labels: issuable_sidebar[:labels].to_json } }
- - else
- - selected_labels = issuable_sidebar[:labels]
- .block.labels{ data: { qa_selector: 'labels_block' } }
- .sidebar-collapsed-icon.has-tooltip.js-sidebar-labels-tooltip{ title: issuable_labels_tooltip(selected_labels), data: { placement: "left", container: "body", boundary: 'viewport' } }
- = sprite_icon('labels')
- %span
- = selected_labels.size
- .title.hide-collapsed
- = _('Labels')
- = loading_icon(css_class: 'gl-vertical-align-text-bottom hidden block-loading')
- - if can_edit_issuable
- = link_to _('Edit'), '#', class: 'js-sidebar-dropdown-toggle edit-link float-right', data: { qa_selector: "labels_edit_button", track_label: "right_sidebar", track_property: "labels", track_event: "click_edit_button", track_value: "" }
- .value.issuable-show-labels.dont-hide.hide-collapsed{ class: ("has-labels" if selected_labels.any?) }
- - if selected_labels.any?
- - selected_labels.each do |label_hash|
- = render_label(label_from_hash(label_hash).present(issuable_subject: nil), link: sidebar_label_filter_path(issuable_sidebar[:project_issuables_path], label_hash[:title]), dataset: { qa_selector: 'selected_label_content', qa_label_name: label_hash[:title] })
- - else
- %span.no-value
- = _('None')
- .selectbox.hide-collapsed
- - selected_labels.each do |label|
- = hidden_field_tag "#{issuable_type}[label_names][]", label[:id], id: nil
- .dropdown
- %button.dropdown-menu-toggle.js-label-select.js-multiselect.js-label-sidebar-dropdown{ type: "button", data: sidebar_label_dropdown_data(issuable_type, issuable_sidebar) }
- %span.dropdown-toggle-text{ class: ("is-default" if selected_labels.empty?) }
- = multi_label_name(selected_labels, "Labels")
- = icon('chevron-down', 'aria-hidden': 'true')
- .dropdown-menu.dropdown-select.dropdown-menu-paging.dropdown-menu-labels.dropdown-menu-selectable.dropdown-extended-height{ data: { qa_selector: "labels_dropdown_content"} }
- = render partial: "shared/issuable/label_page_default"
- - if issuable_sidebar.dig(:current_user, :can_admin_label)
- = render partial: "shared/issuable/label_page_create"
+ .js-sidebar-labels{ data: { allow_label_create: issuable_sidebar.dig(:current_user, :can_admin_label).to_s,
+ allow_scoped_labels: issuable_sidebar[:scoped_labels_available].to_s,
+ can_edit: can_edit_issuable.to_s,
+ iid: issuable_sidebar[:iid],
+ issuable_type: issuable_type,
+ labels_fetch_path: issuable_sidebar[:project_labels_path],
+ labels_manage_path: project_labels_path(@project),
+ labels_update_path: issuable_sidebar[:issuable_json_path],
+ project_issues_path: issuable_sidebar[:project_issuables_path],
+ project_path: @project.full_path,
+ selected_labels: issuable_sidebar[:labels].to_json } }
= render_if_exists 'shared/issuable/sidebar_weight', issuable_sidebar: issuable_sidebar
diff --git a/app/views/shared/wikis/diff.html.haml b/app/views/shared/wikis/diff.html.haml
index ff6a4eb3bb7..68bbbd66f4a 100644
--- a/app/views/shared/wikis/diff.html.haml
+++ b/app/views/shared/wikis/diff.html.haml
@@ -1,4 +1,5 @@
- wiki_page_title @page, _('Changes')
+- add_page_specific_style 'page_bundles/wiki'
- commit = @diffs.diffable
.wiki-page-header.top-area.has-sidebar-toggle.flex-column.flex-lg-row
diff --git a/app/views/shared/wikis/edit.html.haml b/app/views/shared/wikis/edit.html.haml
index c7d1742da8a..834749caaba 100644
--- a/app/views/shared/wikis/edit.html.haml
+++ b/app/views/shared/wikis/edit.html.haml
@@ -1,4 +1,5 @@
- wiki_page_title @page, @page.persisted? ? _('Edit') : _('New')
+- add_page_specific_style 'page_bundles/wiki'
= wiki_page_errors(@error)
diff --git a/app/views/shared/wikis/empty.html.haml b/app/views/shared/wikis/empty.html.haml
index 62fa6e1907b..c52ead74b4c 100644
--- a/app/views/shared/wikis/empty.html.haml
+++ b/app/views/shared/wikis/empty.html.haml
@@ -1,4 +1,5 @@
- page_title _("Wiki")
- @right_sidebar = false
+- add_page_specific_style 'page_bundles/wiki'
= render 'shared/empty_states/wikis'
diff --git a/app/views/shared/wikis/history.html.haml b/app/views/shared/wikis/history.html.haml
index f9d21c8fb57..50ccfdeabd5 100644
--- a/app/views/shared/wikis/history.html.haml
+++ b/app/views/shared/wikis/history.html.haml
@@ -1,4 +1,5 @@
- wiki_page_title @page, _('History')
+- add_page_specific_style 'page_bundles/wiki'
.wiki-page-header.top-area.has-sidebar-toggle.flex-column.flex-lg-row
= wiki_sidebar_toggle_button
diff --git a/app/views/shared/wikis/pages.html.haml b/app/views/shared/wikis/pages.html.haml
index ef99d0aabed..76fc9510740 100644
--- a/app/views/shared/wikis/pages.html.haml
+++ b/app/views/shared/wikis/pages.html.haml
@@ -2,6 +2,7 @@
- breadcrumb_title s_("Wiki|Pages")
- page_title s_("Wiki|Pages"), _("Wiki")
- sort_title = wiki_sort_title(params[:sort])
+- add_page_specific_style 'page_bundles/wiki'
.wiki-page-header.top-area.flex-column.flex-lg-row
diff --git a/app/views/shared/wikis/show.html.haml b/app/views/shared/wikis/show.html.haml
index 1c83ebee450..6f1c1a3a801 100644
--- a/app/views/shared/wikis/show.html.haml
+++ b/app/views/shared/wikis/show.html.haml
@@ -1,4 +1,5 @@
- wiki_page_title @page
+- add_page_specific_style 'page_bundles/wiki'
.wiki-page-header.top-area.has-sidebar-toggle.flex-column.flex-lg-row
= wiki_sidebar_toggle_button
diff --git a/changelogs/unreleased/forward_deployment_enabled-api.yml b/changelogs/unreleased/forward_deployment_enabled-api.yml
new file mode 100644
index 00000000000..5da83dd8036
--- /dev/null
+++ b/changelogs/unreleased/forward_deployment_enabled-api.yml
@@ -0,0 +1,5 @@
+---
+title: Support ci_forward_deployment_enabled in edit API
+merge_request: 44510
+author:
+type: added
diff --git a/changelogs/unreleased/issue-233479-Allow_move_issues_on_graphql.yml b/changelogs/unreleased/issue-233479-Allow_move_issues_on_graphql.yml
new file mode 100644
index 00000000000..21def475bb2
--- /dev/null
+++ b/changelogs/unreleased/issue-233479-Allow_move_issues_on_graphql.yml
@@ -0,0 +1,5 @@
+---
+title: Allow to move issues between projects on GraphQL
+merge_request: 44491
+author:
+type: added
diff --git a/config/application.rb b/config/application.rb
index 3934afc11fe..75befc8a248 100644
--- a/config/application.rb
+++ b/config/application.rb
@@ -191,9 +191,11 @@ module Gitlab
config.assets.precompile << "page_bundles/pipeline.css"
config.assets.precompile << "page_bundles/pipelines.css"
config.assets.precompile << "page_bundles/productivity_analytics.css"
+ config.assets.precompile << "page_bundles/terminal.css"
config.assets.precompile << "page_bundles/todos.css"
config.assets.precompile << "page_bundles/reports.css"
config.assets.precompile << "page_bundles/xterm.css"
+ config.assets.precompile << "page_bundles/wiki.css"
config.assets.precompile << "lazy_bundles/cropper.css"
config.assets.precompile << "performance_bar.css"
config.assets.precompile << "lib/ace.js"
diff --git a/config/feature_flags/development/merge_request_reviewers.yml b/config/feature_flags/development/merge_request_reviewers.yml
index 3ee4c1547aa..2180662b9df 100644
--- a/config/feature_flags/development/merge_request_reviewers.yml
+++ b/config/feature_flags/development/merge_request_reviewers.yml
@@ -4,4 +4,4 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/40488
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/245190
group: group::source code
type: development
-default_enabled: true
+default_enabled: false
diff --git a/config/feature_flags/development/vue_sidebar_labels.yml b/config/feature_flags/development/vue_sidebar_labels.yml
deleted file mode 100644
index 4dbfa7aca73..00000000000
--- a/config/feature_flags/development/vue_sidebar_labels.yml
+++ /dev/null
@@ -1,7 +0,0 @@
----
-name: vue_sidebar_labels
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/41561
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/263440
-group: group::project management
-type: development
-default_enabled: true
diff --git a/doc/administration/packages/container_registry.md b/doc/administration/packages/container_registry.md
index 16e149999fd..56b7f01e1ad 100644
--- a/doc/administration/packages/container_registry.md
+++ b/doc/administration/packages/container_registry.md
@@ -180,7 +180,7 @@ docker login gitlab.example.com:5050
### Configure Container Registry under its own domain
When the Registry is configured to use its own domain, you need a TLS
-certificate for that specific domain (for example, `registry.example.com`). You might need
+certificate for that specific domain (for example, `registry.example.com`). You might need
a wildcard certificate if hosted under a subdomain of your existing GitLab
domain, for example, `registry.gitlab.example.com`.
diff --git a/doc/api/graphql/reference/gitlab_schema.graphql b/doc/api/graphql/reference/gitlab_schema.graphql
index effe16fc41a..f5a7b9839b1 100644
--- a/doc/api/graphql/reference/gitlab_schema.graphql
+++ b/doc/api/graphql/reference/gitlab_schema.graphql
@@ -9257,6 +9257,31 @@ Identifier of Issue
scalar IssueID
"""
+Autogenerated input type of IssueMove
+"""
+input IssueMoveInput {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The IID of the issue to mutate
+ """
+ iid: String!
+
+ """
+ The project the issue to mutate is in
+ """
+ projectPath: ID!
+
+ """
+ The project to move the issue to
+ """
+ targetProjectPath: ID!
+}
+
+"""
Autogenerated input type of IssueMoveList
"""
input IssueMoveListInput {
@@ -9327,6 +9352,26 @@ type IssueMoveListPayload {
}
"""
+Autogenerated return type of IssueMove
+"""
+type IssueMovePayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ Errors encountered during execution of the mutation.
+ """
+ errors: [String!]!
+
+ """
+ The issue after mutation
+ """
+ issue: Issue
+}
+
+"""
Check permissions for the current user on a issue
"""
type IssuePermissions {
@@ -12179,6 +12224,7 @@ type Mutation {
epicAddIssue(input: EpicAddIssueInput!): EpicAddIssuePayload
epicSetSubscription(input: EpicSetSubscriptionInput!): EpicSetSubscriptionPayload
epicTreeReorder(input: EpicTreeReorderInput!): EpicTreeReorderPayload
+ issueMove(input: IssueMoveInput!): IssueMovePayload
issueMoveList(input: IssueMoveListInput!): IssueMoveListPayload
issueSetAssignees(input: IssueSetAssigneesInput!): IssueSetAssigneesPayload
issueSetConfidential(input: IssueSetConfidentialInput!): IssueSetConfidentialPayload
diff --git a/doc/api/graphql/reference/gitlab_schema.json b/doc/api/graphql/reference/gitlab_schema.json
index 408b7e13c15..8edd30c0172 100644
--- a/doc/api/graphql/reference/gitlab_schema.json
+++ b/doc/api/graphql/reference/gitlab_schema.json
@@ -25225,6 +25225,69 @@
},
{
"kind": "INPUT_OBJECT",
+ "name": "IssueMoveInput",
+ "description": "Autogenerated input type of IssueMove",
+ "fields": null,
+ "inputFields": [
+ {
+ "name": "projectPath",
+ "description": "The project the issue to mutate is in",
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "ID",
+ "ofType": null
+ }
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "iid",
+ "description": "The IID of the issue to mutate",
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ }
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "targetProjectPath",
+ "description": "The project to move the issue to",
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "ID",
+ "ofType": null
+ }
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "clientMutationId",
+ "description": "A unique identifier for the client performing the mutation.",
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "defaultValue": null
+ }
+ ],
+ "interfaces": null,
+ "enumValues": null,
+ "possibleTypes": null
+ },
+ {
+ "kind": "INPUT_OBJECT",
"name": "IssueMoveListInput",
"description": "Autogenerated input type of IssueMoveList",
"fields": null,
@@ -25405,6 +25468,73 @@
},
{
"kind": "OBJECT",
+ "name": "IssueMovePayload",
+ "description": "Autogenerated return type of IssueMove",
+ "fields": [
+ {
+ "name": "clientMutationId",
+ "description": "A unique identifier for the client performing the mutation.",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "errors",
+ "description": "Errors encountered during execution of the mutation.",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "LIST",
+ "name": null,
+ "ofType": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ }
+ }
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "issue",
+ "description": "The issue after mutation",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "OBJECT",
+ "name": "Issue",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ }
+ ],
+ "inputFields": null,
+ "interfaces": [
+
+ ],
+ "enumValues": null,
+ "possibleTypes": null
+ },
+ {
+ "kind": "OBJECT",
"name": "IssuePermissions",
"description": "Check permissions for the current user on a issue",
"fields": [
@@ -34620,6 +34750,33 @@
"deprecationReason": null
},
{
+ "name": "issueMove",
+ "description": null,
+ "args": [
+ {
+ "name": "input",
+ "description": null,
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "INPUT_OBJECT",
+ "name": "IssueMoveInput",
+ "ofType": null
+ }
+ },
+ "defaultValue": null
+ }
+ ],
+ "type": {
+ "kind": "OBJECT",
+ "name": "IssueMovePayload",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
"name": "issueMoveList",
"description": null,
"args": [
diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md
index c6eb8603da9..dca00fc1286 100644
--- a/doc/api/graphql/reference/index.md
+++ b/doc/api/graphql/reference/index.md
@@ -1308,6 +1308,16 @@ Autogenerated return type of IssueMoveList.
| `errors` | String! => Array | Errors encountered during execution of the mutation. |
| `issue` | Issue | The issue after mutation |
+### IssueMovePayload
+
+Autogenerated return type of IssueMove.
+
+| Field | Type | Description |
+| ----- | ---- | ----------- |
+| `clientMutationId` | String | A unique identifier for the client performing the mutation. |
+| `errors` | String! => Array | Errors encountered during execution of the mutation. |
+| `issue` | Issue | The issue after mutation |
+
### IssuePermissions
Check permissions for the current user on a issue.
diff --git a/doc/api/groups.md b/doc/api/groups.md
index 462972206d9..53c92cf85ec 100644
--- a/doc/api/groups.md
+++ b/doc/api/groups.md
@@ -440,6 +440,7 @@ Example response:
"import_status":"failed",
"open_issues_count":10,
"ci_default_git_depth":50,
+ "ci_forward_deployment_enabled":true,
"public_jobs":true,
"build_timeout":3600,
"auto_cancel_pending_pipelines":"enabled",
diff --git a/doc/api/job_artifacts.md b/doc/api/job_artifacts.md
index d6971484edc..f5510f6ee91 100644
--- a/doc/api/job_artifacts.md
+++ b/doc/api/job_artifacts.md
@@ -64,7 +64,7 @@ is the same as [getting the job's artifacts](#get-job-artifacts), but by
defining the job's name instead of its ID.
NOTE: **Note:**
-If a pipeline is [parent of other child pipelines](../ci/parent_child_pipelines.md), artifacts
+If a pipeline is [parent of other child pipelines](../ci/parent_child_pipelines.md), artifacts
are searched in hierarchical order from parent to child. For example, if both parent and
child pipelines have a job with the same name, the artifact from the parent pipeline will be returned.
diff --git a/doc/api/merge_requests.md b/doc/api/merge_requests.md
index 072a8c31705..194f48c6e84 100644
--- a/doc/api/merge_requests.md
+++ b/doc/api/merge_requests.md
@@ -1007,7 +1007,7 @@ order for it to take effect:
value of zero disables approvals for that project.
1. The provided value of `approvals_before_merge` must be greater than the
target project's `approvals_before_merge`.
-1. This API returns 201 (created) for a successful response.
+1. This API returns 201 (created) for a successful response.
```json
{
diff --git a/doc/api/projects.md b/doc/api/projects.md
index b1307e673db..f6ed905cda1 100644
--- a/doc/api/projects.md
+++ b/doc/api/projects.md
@@ -156,6 +156,7 @@ When the user is authenticated and `simple` is not set this returns something li
"star_count": 0,
"runners_token": "b8547b1dc37721d05889db52fa2f02",
"ci_default_git_depth": 50,
+ "ci_forward_deployment_enabled": true,
"public_jobs": true,
"shared_with_groups": [],
"only_allow_merge_if_pipeline_succeeds": false,
@@ -248,6 +249,7 @@ When the user is authenticated and `simple` is not set this returns something li
"star_count": 0,
"runners_token": "b8547b1dc37721d05889db52fa2f02",
"ci_default_git_depth": 0,
+ "ci_forward_deployment_enabled": true,
"public_jobs": true,
"shared_with_groups": [],
"only_allow_merge_if_pipeline_succeeds": false,
@@ -410,6 +412,7 @@ This endpoint supports [keyset pagination](README.md#keyset-based-pagination) fo
"star_count": 0,
"runners_token": "b8547b1dc37721d05889db52fa2f02",
"ci_default_git_depth": 50,
+ "ci_forward_deployment_enabled": true,
"public_jobs": true,
"shared_with_groups": [],
"only_allow_merge_if_pipeline_succeeds": false,
@@ -502,6 +505,7 @@ This endpoint supports [keyset pagination](README.md#keyset-based-pagination) fo
"star_count": 0,
"runners_token": "b8547b1dc37721d05889db52fa2f02",
"ci_default_git_depth": 0,
+ "ci_forward_deployment_enabled": true,
"public_jobs": true,
"shared_with_groups": [],
"only_allow_merge_if_pipeline_succeeds": false,
@@ -856,6 +860,7 @@ GET /projects/:id
"star_count": 0,
"runners_token": "b8bc4a7a29eb76ea83cf79e4908c2b",
"ci_default_git_depth": 50,
+ "ci_forward_deployment_enabled": true,
"public_jobs": true,
"shared_with_groups": [
{
@@ -1218,6 +1223,7 @@ PUT /projects/:id
| `build_coverage_regex` | string | no | Test coverage parsing |
| `ci_config_path` | string | no | The path to CI configuration file |
| `ci_default_git_depth` | integer | no | Default number of revisions for [shallow cloning](../ci/pipelines/settings.md#git-shallow-clone) |
+| `ci_forward_deployment_enabled` | boolean | no | When a new deployment job starts, [skip older deployment jobs](../ci/pipelines/settings.md#skip-outdated-deployment-jobs) that are still pending |
| `auto_devops_enabled` | boolean | no | Enable Auto DevOps for this project |
| `auto_devops_deploy_strategy` | string | no | Auto Deploy strategy (`continuous`, `manual` or `timed_incremental`) |
| `repository_storage` | string | no | Which storage shard the repository is on. Available only to admins |
@@ -1701,6 +1707,7 @@ Example response:
"star_count": 0,
"runners_token": "b8bc4a7a29eb76ea83cf79e4908c2b",
"ci_default_git_depth": 50,
+ "ci_forward_deployment_enabled": true,
"public_jobs": true,
"shared_with_groups": [],
"only_allow_merge_if_pipeline_succeeds": false,
@@ -1811,6 +1818,7 @@ Example response:
"star_count": 0,
"runners_token": "b8bc4a7a29eb76ea83cf79e4908c2b",
"ci_default_git_depth": 50,
+ "ci_forward_deployment_enabled": true,
"public_jobs": true,
"shared_with_groups": [],
"only_allow_merge_if_pipeline_succeeds": false,
diff --git a/doc/api/snippets.md b/doc/api/snippets.md
index aab9394e888..431d745ac84 100644
--- a/doc/api/snippets.md
+++ b/doc/api/snippets.md
@@ -222,7 +222,7 @@ curl --request POST "https://gitlab.example.com/api/v4/snippets" \
```json
{
- "title": "This is a snippet",
+ "title": "This is a snippet",
"description": "Hello World snippet",
"visibility": "internal",
"files": [
@@ -310,10 +310,10 @@ curl --request PUT "https://gitlab.example.com/api/v4/snippets/1" \
```json
{
- "title": "foo",
+ "title": "foo",
"files": [
{
- "action": "move",
+ "action": "move",
"previous_path": "test.txt",
"file_path": "renamed.md"
}
diff --git a/doc/ci/cloud_deployment/index.md b/doc/ci/cloud_deployment/index.md
index b98e939e080..af7df0e1153 100644
--- a/doc/ci/cloud_deployment/index.md
+++ b/doc/ci/cloud_deployment/index.md
@@ -9,7 +9,7 @@ type: howto
Interacting with a major cloud provider may have become a much needed task that's
part of your delivery process. With GitLab you can
-[deploy your application anywhere](https://about.gitlab.com/stages-devops-lifecycle/deploy-targets/).
+[deploy your application anywhere](https://about.gitlab.com/stages-devops-lifecycle/deploy-targets/).
For some specific deployment targets, GitLab makes this process less painful by providing Docker images
that come with the needed libraries and tools pre-installed.
diff --git a/doc/ci/multi_project_pipelines.md b/doc/ci/multi_project_pipelines.md
index 7eb387d51b3..378adcd35e9 100644
--- a/doc/ci/multi_project_pipelines.md
+++ b/doc/ci/multi_project_pipelines.md
@@ -225,7 +225,7 @@ In the upstream pipeline:
1. Save the variables in a `.env` file.
1. Save the `.env` file as a `dotenv` report.
-1. Trigger the downstream pipeline.
+1. Trigger the downstream pipeline.
```yaml
build_vars:
diff --git a/doc/ci/yaml/README.md b/doc/ci/yaml/README.md
index 5334e3e3368..2e4ab68a0e8 100644
--- a/doc/ci/yaml/README.md
+++ b/doc/ci/yaml/README.md
@@ -2613,7 +2613,7 @@ The `stop_review_app` job is **required** to have the following keywords defined
- `environment:action`
Additionally, both jobs should have matching [`rules`](../yaml/README.md#onlyexcept-basic)
-or [`only/except`](../yaml/README.md#onlyexcept-basic) configuration.
+or [`only/except`](../yaml/README.md#onlyexcept-basic) configuration.
In the example above, if the configuration is not identical:
@@ -4409,7 +4409,7 @@ You can use the `$CI_COMMIT_REF_SLUG` variable to specify your [`cache:key`](#ca
For example, if your `$CI_COMMIT_REF_SLUG` is `test` you can set a job
to download cache that's tagged with `test`.
-If a cache with this tag is not found, you can use `CACHE_FALLBACK_KEY` to
+If a cache with this tag is not found, you can use `CACHE_FALLBACK_KEY` to
specify a cache to use when none exists.
For example:
diff --git a/doc/development/database/setting_multiple_values.md b/doc/development/database/setting_multiple_values.md
index 428c04b9a0f..5569a0e10b7 100644
--- a/doc/development/database/setting_multiple_values.md
+++ b/doc/development/database/setting_multiple_values.md
@@ -22,7 +22,7 @@ with updates(obj_id, new_title, new_weight) as (
values (1 :: integer, 'Very difficult issue' :: text, 8 :: integer),
(2, 'Very easy issue', 1)
)
-update issues
+update issues
set title = new_title, weight = new_weight
from updates
where id = obj_id
@@ -88,7 +88,7 @@ objects = Foo.from_union([
# At this point, all the objects are instances of Foo, even the ones from the
# Bar table
mapping = objects.to_h { |obj| [obj, bazzes[obj.id] }
-
+
# Issues at most 2 queries
::Gitlab::Database::BulkUpdate.execute(%i[baz], mapping) do |obj|
obj.object_type.constantize
diff --git a/doc/development/integrations/secure.md b/doc/development/integrations/secure.md
index b085f1244c1..1094074cab6 100644
--- a/doc/development/integrations/secure.md
+++ b/doc/development/integrations/secure.md
@@ -384,6 +384,9 @@ reported for the same commit, except for `CWE` and `WASC`.
Not all vulnerabilities have CVEs, and a CVE can be identified multiple times. As a result, a CVE
isn't a stable identifier and you shouldn't assume it as such when tracking vulnerabilities.
+The maximum number of identifiers for a vulnerability is set as 20. If a vulnerability has more than 20 identifiers,
+the system will save only the first 20 of them.
+
### Location
The `location` indicates where the vulnerability has been detected.
diff --git a/doc/user/admin_area/approving_users.md b/doc/user/admin_area/approving_users.md
index a8537666581..486d0b6a25d 100644
--- a/doc/user/admin_area/approving_users.md
+++ b/doc/user/admin_area/approving_users.md
@@ -11,7 +11,7 @@ type: howto
When [Require admin approval for new sign-ups](settings/sign_up_restrictions.md#require-admin-approval-for-new-sign-ups) is enabled, any user that signs up for an account using the registration form is placed under a **Pending approval** state.
-A user pending approval is functionally identical to a [blocked](blocking_unblocking_users.md) user.
+A user pending approval is functionally identical to a [blocked](blocking_unblocking_users.md) user.
A user pending approval:
diff --git a/doc/user/group/epics/index.md b/doc/user/group/epics/index.md
index b76aa856227..f380b36cc00 100644
--- a/doc/user/group/epics/index.md
+++ b/doc/user/group/epics/index.md
@@ -177,7 +177,7 @@ You can also consult the [group permissions table](../../permissions.md#group-me
Once you write your comment, you can either:
-- Click **Comment** to publish your comment.
+- Click **Comment** to publish your comment.
- Click **Start thread** to start a thread within that epic's discussion.
### Activity sort order
diff --git a/doc/user/packages/composer_repository/index.md b/doc/user/packages/composer_repository/index.md
index a052b590e92..d4fb662c3a6 100644
--- a/doc/user/packages/composer_repository/index.md
+++ b/doc/user/packages/composer_repository/index.md
@@ -136,7 +136,7 @@ Install a package from the Package Registry so you can use it as a dependency.
Prerequisites:
- A package in the Package Registry.
-- The group ID, which is on the group's home page.
+- The group ID, which is on the group's home page.
- A [personal access token](../../../user/profile/personal_access_tokens.md) with the scope set to, at minimum, `read_api`.
NOTE: **Note:**
@@ -155,7 +155,7 @@ To install a package:
- Set the required package version:
- ```shell
+ ```shell
composer require <package_name>:<version>
```
@@ -223,7 +223,7 @@ To install a package:
```
Result in the `composer.json` file:
-
+
```json
{
...
@@ -240,7 +240,7 @@ To install a package:
},
...
}
- ```
+ ```
You can unset this with the command:
diff --git a/doc/user/packages/nuget_repository/index.md b/doc/user/packages/nuget_repository/index.md
index 7a77fa2cf53..22e1a95649d 100644
--- a/doc/user/packages/nuget_repository/index.md
+++ b/doc/user/packages/nuget_repository/index.md
@@ -154,7 +154,7 @@ To add the GitLab NuGet Repository as a source for .NET, create a file named `nu
When uploading packages, note that:
-- The Package Registry on GitLab.com can store up to 500 MB of content. This limit is [configurable for self-managed GitLab instances](../../../administration/instance_limits.md#package-registry-limits).
+- The Package Registry on GitLab.com can store up to 500 MB of content. This limit is [configurable for self-managed GitLab instances](../../../administration/instance_limits.md#package-registry-limits).
- If you upload the same package with the same version multiple times, each consecutive upload
is saved as a separate file. When installing a package, GitLab serves the most recent file.
- When uploading packages to GitLab, they are not displayed in the packages UI of your project
diff --git a/doc/user/project/labels.md b/doc/user/project/labels.md
index db1a8d7d5a2..4f0354f86af 100644
--- a/doc/user/project/labels.md
+++ b/doc/user/project/labels.md
@@ -98,7 +98,7 @@ If you delete a label, it is permanently deleted. All references to the label ar
If you previously created a project label and now want to make it available for other
projects within the same group, you can promote it to a group label.
-If other projects in the same group have a label with the same title, they are all
+If other projects in the same group have a label with the same title, they are all
merged with the new group label. If a group label with the same title exists, it is
also merged.
@@ -126,7 +126,7 @@ follow the same process as [creating a project label](#project-labels).
#### Create group labels from epics **(ULTIMATE)**
-You can create group labels from the epic sidebar. The labels you create
+You can create group labels from the epic sidebar. The labels you create
belong to the immediate group to which the epic belongs. The process is the same as
creating a [project label from an issue or merge request](#project-labels).
@@ -160,7 +160,7 @@ title, for example:
![Scoped labels](img/labels_key_value_v13_5.png)
An issue, merge request or epic cannot have two scoped labels, of the form `key::value`,
-with the same `key`. Adding a new label with the same `key`, but a different `value`
+with the same `key`. Adding a new label with the same `key`, but a different `value`
causes the previous `key` label to be replaced with the new label.
For example:
diff --git a/lib/api/entities/project.rb b/lib/api/entities/project.rb
index fb599d68d72..82a44c75382 100644
--- a/lib/api/entities/project.rb
+++ b/lib/api/entities/project.rb
@@ -84,6 +84,7 @@ module API
expose :open_issues_count, if: lambda { |project, options| project.feature_available?(:issues, options[:current_user]) }
expose :runners_token, if: lambda { |_project, options| options[:user_can_admin_project] }
expose :ci_default_git_depth
+ expose :ci_forward_deployment_enabled
expose :public_builds, as: :public_jobs
expose :build_git_strategy, if: lambda { |project, options| options[:user_can_admin_project] } do |project, options|
project.build_allow_git_fetch ? 'fetch' : 'clone'
diff --git a/lib/api/helpers/projects_helpers.rb b/lib/api/helpers/projects_helpers.rb
index 8c20f5b8fc2..0364ba2ad9e 100644
--- a/lib/api/helpers/projects_helpers.rb
+++ b/lib/api/helpers/projects_helpers.rb
@@ -83,9 +83,18 @@ module API
params :optional_filter_params_ee do
end
+ params :optional_update_params_ce do
+ optional :ci_forward_deployment_enabled, type: Boolean, desc: 'Skip older deployment jobs that are still pending'
+ end
+
params :optional_update_params_ee do
end
+ params :optional_update_params do
+ use :optional_update_params_ce
+ use :optional_update_params_ee
+ end
+
params :optional_container_expiration_policy_params do
optional :cadence, type: String, desc: 'Container expiration policy cadence for recurring job'
optional :keep_n, type: String, desc: 'Container expiration policy number of images to keep'
@@ -108,6 +117,7 @@ module API
:builds_access_level,
:ci_config_path,
:ci_default_git_depth,
+ :ci_forward_deployment_enabled,
:container_registry_enabled,
:container_expiration_policy_attributes,
:default_branch,
diff --git a/lib/api/projects.rb b/lib/api/projects.rb
index 5310f0c65f6..ecee76ae60c 100644
--- a/lib/api/projects.rb
+++ b/lib/api/projects.rb
@@ -353,7 +353,7 @@ module API
optional :path, type: String, desc: 'The path of the repository'
use :optional_project_params
- use :optional_update_params_ee
+ use :optional_update_params
at_least_one_of(*Helpers::ProjectsHelpers.update_params_at_least_one_of)
end
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index defe5420973..73f2116facc 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -15181,9 +15181,6 @@ msgstr ""
msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. If a group label with the same title exists, it will also be merged. This action cannot be reversed."
msgstr ""
-msgid "Labels|and %{count} more"
-msgstr ""
-
msgid "Language"
msgstr ""
diff --git a/rubocop/cop/graphql/id_type.rb b/rubocop/cop/graphql/id_type.rb
index 9ec877900ec..96f90ac136a 100644
--- a/rubocop/cop/graphql/id_type.rb
+++ b/rubocop/cop/graphql/id_type.rb
@@ -6,7 +6,7 @@ module RuboCop
class IDType < RuboCop::Cop::Cop
MSG = 'Do not use GraphQL::ID_TYPE, use a specific GlobalIDType instead'
- WHITELISTED_ARGUMENTS = %i[iid full_path project_path group_path].freeze
+ WHITELISTED_ARGUMENTS = %i[iid full_path project_path group_path target_project_path].freeze
def_node_search :graphql_id_type?, <<~PATTERN
(send nil? :argument (_ #does_not_match?) (const (const nil? :GraphQL) :ID_TYPE) ...)
diff --git a/spec/controllers/projects/settings/ci_cd_controller_spec.rb b/spec/controllers/projects/settings/ci_cd_controller_spec.rb
index 9e26eca88f2..7a6e11d53d4 100644
--- a/spec/controllers/projects/settings/ci_cd_controller_spec.rb
+++ b/spec/controllers/projects/settings/ci_cd_controller_spec.rb
@@ -230,6 +230,21 @@ RSpec.describe Projects::Settings::CiCdController do
end
end
+ context 'when forward_deployment_enabled is not specified' do
+ let(:params) { { ci_cd_settings_attributes: { forward_deployment_enabled: false } } }
+
+ before do
+ project.ci_cd_settings.update!(forward_deployment_enabled: nil)
+ end
+
+ it 'sets forward deployment enabled' do
+ subject
+
+ project.reload
+ expect(project.ci_forward_deployment_enabled).to eq(false)
+ end
+ end
+
context 'when max_artifacts_size is specified' do
let(:params) { { max_artifacts_size: 10 } }
diff --git a/spec/features/dashboard/merge_requests_spec.rb b/spec/features/dashboard/merge_requests_spec.rb
index a47f2285e37..952a78ec79a 100644
--- a/spec/features/dashboard/merge_requests_spec.rb
+++ b/spec/features/dashboard/merge_requests_spec.rb
@@ -52,29 +52,20 @@ RSpec.describe 'Dashboard Merge Requests' do
end
context 'merge requests exist' do
- let_it_be(:author_user) { create(:user) }
let(:label) { create(:label) }
let!(:assigned_merge_request) do
create(:merge_request,
assignees: [current_user],
source_project: project,
- author: author_user)
- end
-
- let!(:review_requested_merge_request) do
- create(:merge_request,
- reviewers: [current_user],
- source_branch: 'review',
- source_project: project,
- author: author_user)
+ author: create(:user))
end
let!(:assigned_merge_request_from_fork) do
create(:merge_request,
source_branch: 'markdown', assignees: [current_user],
target_project: public_project, source_project: forked_project,
- author: author_user)
+ author: create(:user))
end
let!(:authored_merge_request) do
@@ -103,7 +94,7 @@ RSpec.describe 'Dashboard Merge Requests' do
create(:merge_request,
source_branch: 'fix',
source_project: project,
- author: author_user)
+ author: create(:user))
end
before do
@@ -120,10 +111,6 @@ RSpec.describe 'Dashboard Merge Requests' do
expect(page).not_to have_content(labeled_merge_request.title)
end
- it 'shows review requested merge requests' do
- expect(page).to have_content(review_requested_merge_request.title)
- end
-
it 'shows authored merge requests', :js do
reset_filters
input_filtered_search("author:=#{current_user.to_reference}")
diff --git a/spec/features/projects/settings/pipelines_settings_spec.rb b/spec/features/projects/settings/pipelines_settings_spec.rb
index 0358acc8dcc..ffc0ecc4966 100644
--- a/spec/features/projects/settings/pipelines_settings_spec.rb
+++ b/spec/features/projects/settings/pipelines_settings_spec.rb
@@ -64,7 +64,7 @@ RSpec.describe "Projects > Settings > Pipelines settings" do
it 'updates forward_deployment_enabled' do
visit project_settings_ci_cd_path(project)
- checkbox = find_field('project_forward_deployment_enabled')
+ checkbox = find_field('project_ci_cd_settings_attributes_forward_deployment_enabled')
expect(checkbox).to be_checked
checkbox.set(false)
@@ -79,7 +79,7 @@ RSpec.describe "Projects > Settings > Pipelines settings" do
expect(page).to have_button('Save changes', disabled: false)
end
- checkbox = find_field('project_forward_deployment_enabled')
+ checkbox = find_field('project_ci_cd_settings_attributes_forward_deployment_enabled')
expect(checkbox).not_to be_checked
end
diff --git a/spec/finders/merge_requests_finder_spec.rb b/spec/finders/merge_requests_finder_spec.rb
index b3d315e984e..68958e37001 100644
--- a/spec/finders/merge_requests_finder_spec.rb
+++ b/spec/finders/merge_requests_finder_spec.rb
@@ -333,8 +333,6 @@ RSpec.describe MergeRequestsFinder do
end
context 'assignee filtering' do
- let_it_be(:user3) { create(:user) }
-
let(:issuables) { described_class.new(user, params).execute }
it_behaves_like 'assignee ID filter' do
@@ -353,6 +351,7 @@ RSpec.describe MergeRequestsFinder do
merge_request3.assignees = [user2, user3]
end
+ let_it_be(:user3) { create(:user) }
let(:params) { { assignee_username: [user2.username, user3.username] } }
let(:expected_issuables) { [merge_request3] }
end
@@ -367,6 +366,7 @@ RSpec.describe MergeRequestsFinder do
end
it_behaves_like 'no assignee filter' do
+ let_it_be(:user3) { create(:user) }
let(:expected_issuables) { [merge_request4, merge_request5] }
end
@@ -374,54 +374,30 @@ RSpec.describe MergeRequestsFinder do
let(:expected_issuables) { [merge_request1, merge_request2, merge_request3] }
end
- context 'with just reviewers' do
- it_behaves_like 'assignee username filter' do
- before do
- merge_request4.reviewers = [user3]
- merge_request4.assignees = []
- end
-
- let(:params) { { assignee_username: [user3.username] } }
- let(:expected_issuables) { [merge_request4] }
- end
- end
-
- context 'with an additional reviewer' do
- it_behaves_like 'assignee username filter' do
- before do
- merge_request3.assignees = [user3]
- merge_request4.reviewers = [user3]
- end
+ context 'filtering by group milestone' do
+ let(:group_milestone) { create(:milestone, group: group) }
- let(:params) { { assignee_username: [user3.username] } }
- let(:expected_issuables) { [merge_request3, merge_request4] }
+ before do
+ merge_request1.update!(milestone: group_milestone)
+ merge_request2.update!(milestone: group_milestone)
end
- end
- end
- context 'filtering by group milestone' do
- let(:group_milestone) { create(:milestone, group: group) }
+ it 'returns merge requests assigned to that group milestone' do
+ params = { milestone_title: group_milestone.title }
- before do
- merge_request1.update!(milestone: group_milestone)
- merge_request2.update!(milestone: group_milestone)
- end
-
- it 'returns merge requests assigned to that group milestone' do
- params = { milestone_title: group_milestone.title }
-
- merge_requests = described_class.new(user, params).execute
+ merge_requests = described_class.new(user, params).execute
- expect(merge_requests).to contain_exactly(merge_request1, merge_request2)
- end
+ expect(merge_requests).to contain_exactly(merge_request1, merge_request2)
+ end
- context 'using NOT' do
- let(:params) { { not: { milestone_title: group_milestone.title } } }
+ context 'using NOT' do
+ let(:params) { { not: { milestone_title: group_milestone.title } } }
- it 'returns MRs not assigned to that group milestone' do
- merge_requests = described_class.new(user, params).execute
+ it 'returns MRs not assigned to that group milestone' do
+ merge_requests = described_class.new(user, params).execute
- expect(merge_requests).to contain_exactly(merge_request3, merge_request4, merge_request5)
+ expect(merge_requests).to contain_exactly(merge_request3, merge_request4, merge_request5)
+ end
end
end
end
diff --git a/spec/frontend/feature_flags/components/edit_feature_flag_spec.js b/spec/frontend/feature_flags/components/edit_feature_flag_spec.js
index 49551568e6a..6a394251060 100644
--- a/spec/frontend/feature_flags/components/edit_feature_flag_spec.js
+++ b/spec/frontend/feature_flags/components/edit_feature_flag_spec.js
@@ -32,21 +32,16 @@ describe('Edit feature flag form', () => {
}
wrapper = shallowMount(EditFeatureFlag, {
localVue,
- propsData: {
- environmentsEndpoint: 'environments.json',
- projectId: '8',
- featureFlagIssuesEndpoint: `${TEST_HOST}/feature_flags/5/issues`,
+ store,
+ provide: {
showUserCallout: true,
userCalloutId,
userCalloutsPath,
- },
- store,
- provide: {
glFeatures: {
featureFlagsNewVersion: true,
},
+ ...opts,
},
- ...opts,
});
};
@@ -145,12 +140,6 @@ describe('Edit feature flag form', () => {
});
});
- it('renders the related issues widget', () => {
- const expected = `${TEST_HOST}/feature_flags/5/issues`;
-
- expect(wrapper.find(Form).props('featureFlagIssuesEndpoint')).toBe(expected);
- });
-
it('should track when the toggle is clicked', () => {
const toggle = wrapper.find(GlToggle);
const spy = mockTracking('_category_', toggle.element, jest.spyOn);
@@ -164,7 +153,7 @@ describe('Edit feature flag form', () => {
});
describe('without new version flags', () => {
- beforeEach(() => factory({ provide: { glFeatures: { featureFlagsNewVersion: false } } }));
+ beforeEach(() => factory({ glFeatures: { featureFlagsNewVersion: false } }));
it('should alert users that feature flags are changing soon', () => {
expect(findAlert().text()).toBe(NEW_FLAG_ALERT);
@@ -173,7 +162,7 @@ describe('Edit feature flag form', () => {
describe('dismissing new version alert', () => {
beforeEach(() => {
- factory({ provide: { glFeatures: { featureFlagsNewVersion: false } } });
+ factory({ glFeatures: { featureFlagsNewVersion: false } });
mock.onPost(userCalloutsPath, { feature_name: userCalloutId }).reply(200);
findAlert().vm.$emit('dismiss');
return wrapper.vm.$nextTick();
diff --git a/spec/frontend/feature_flags/components/environments_dropdown_spec.js b/spec/frontend/feature_flags/components/environments_dropdown_spec.js
index 2aa75ef6652..917f5f5ccd3 100644
--- a/spec/frontend/feature_flags/components/environments_dropdown_spec.js
+++ b/spec/frontend/feature_flags/components/environments_dropdown_spec.js
@@ -14,9 +14,11 @@ describe('Feature flags > Environments dropdown ', () => {
const factory = props => {
wrapper = shallowMount(EnvironmentsDropdown, {
propsData: {
- endpoint: `${TEST_HOST}/environments.json'`,
...props,
},
+ provide: {
+ environmentsEndpoint: `${TEST_HOST}/environments.json'`,
+ },
});
};
diff --git a/spec/frontend/feature_flags/components/form_spec.js b/spec/frontend/feature_flags/components/form_spec.js
index 451bb4176ef..33c7eeb54b7 100644
--- a/spec/frontend/feature_flags/components/form_spec.js
+++ b/spec/frontend/feature_flags/components/form_spec.js
@@ -24,18 +24,23 @@ describe('feature flag form', () => {
const requiredProps = {
cancelPath: 'feature_flags',
submitText: 'Create',
+ };
+
+ const requiredInjections = {
environmentsEndpoint: '/environments.json',
projectId: '1',
+ glFeatures: {
+ featureFlagPermissions: true,
+ featureFlagsNewVersion: true,
+ },
};
- const factory = (props = {}) => {
+ const factory = (props = {}, provide = {}) => {
wrapper = shallowMount(Form, {
- propsData: props,
+ propsData: { ...requiredProps, ...props },
provide: {
- glFeatures: {
- featureFlagPermissions: true,
- featureFlagsNewVersion: true,
- },
+ ...requiredInjections,
+ ...provide,
},
});
};
@@ -67,10 +72,13 @@ describe('feature flag form', () => {
});
it('renders the related issues widget when the featureFlagIssuesEndpoint is provided', () => {
- factory({
- ...requiredProps,
- featureFlagIssuesEndpoint: '/some/endpoint',
- });
+ factory(
+ {},
+ {
+ ...requiredInjections,
+ featureFlagIssuesEndpoint: '/some/endpoint',
+ },
+ );
expect(wrapper.find(RelatedIssuesRoot).exists()).toBe(true);
});
diff --git a/spec/frontend/feature_flags/components/new_environments_dropdown_spec.js b/spec/frontend/feature_flags/components/new_environments_dropdown_spec.js
index 10e9ed4d3bf..12dc98fbde8 100644
--- a/spec/frontend/feature_flags/components/new_environments_dropdown_spec.js
+++ b/spec/frontend/feature_flags/components/new_environments_dropdown_spec.js
@@ -14,7 +14,9 @@ describe('New Environments Dropdown', () => {
beforeEach(() => {
axiosMock = new MockAdapter(axios);
- wrapper = shallowMount(NewEnvironmentsDropdown, { propsData: { endpoint: TEST_HOST } });
+ wrapper = shallowMount(NewEnvironmentsDropdown, {
+ provide: { environmentsEndpoint: TEST_HOST },
+ });
});
afterEach(() => {
diff --git a/spec/frontend/feature_flags/components/new_feature_flag_spec.js b/spec/frontend/feature_flags/components/new_feature_flag_spec.js
index 1844806ed95..dbc6e03d922 100644
--- a/spec/frontend/feature_flags/components/new_feature_flag_spec.js
+++ b/spec/frontend/feature_flags/components/new_feature_flag_spec.js
@@ -35,20 +35,18 @@ describe('New feature flag form', () => {
}
wrapper = shallowMount(NewFeatureFlag, {
localVue,
- propsData: {
- environmentsEndpoint: 'environments.json',
- projectId: '8',
+ store,
+ provide: {
showUserCallout: true,
userCalloutId,
userCalloutsPath,
- },
- store,
- provide: {
+ environmentsEndpoint: 'environments.json',
+ projectId: '8',
glFeatures: {
featureFlagsNewVersion: true,
},
+ ...opts,
},
- ...opts,
});
};
@@ -80,10 +78,6 @@ describe('New feature flag form', () => {
expect(wrapper.find(Form).exists()).toEqual(true);
});
- it('does not render the related issues widget', () => {
- expect(wrapper.find(Form).props('featureFlagIssuesEndpoint')).toBe('');
- });
-
it('should render default * row', () => {
const defaultScope = {
id: expect.any(String),
@@ -102,10 +96,6 @@ describe('New feature flag form', () => {
expect(wrapper.find(GlAlert).exists()).toBe(false);
});
- it('should pass in the project ID', () => {
- expect(wrapper.find(Form).props('projectId')).toBe('8');
- });
-
it('has an all users strategy by default', () => {
const strategies = wrapper.find(Form).props('strategies');
@@ -113,7 +103,7 @@ describe('New feature flag form', () => {
});
describe('without new version flags', () => {
- beforeEach(() => factory({ provide: { glFeatures: { featureFlagsNewVersion: false } } }));
+ beforeEach(() => factory({ glFeatures: { featureFlagsNewVersion: false } }));
it('should alert users that feature flags are changing soon', () => {
expect(findAlert().text()).toBe(NEW_FLAG_ALERT);
@@ -126,7 +116,7 @@ describe('New feature flag form', () => {
beforeEach(() => {
mock = new MockAdapter(axios);
mock.onPost(userCalloutsPath, { feature_name: userCalloutId }).reply(200);
- factory({ provide: { glFeatures: { featureFlagsNewVersion: false } } });
+ factory({ glFeatures: { featureFlagsNewVersion: false } });
findAlert().vm.$emit('dismiss');
return wrapper.vm.$nextTick();
});
diff --git a/spec/frontend/feature_flags/components/strategy_spec.js b/spec/frontend/feature_flags/components/strategy_spec.js
index 1e3e1a76afb..7d6700ba184 100644
--- a/spec/frontend/feature_flags/components/strategy_spec.js
+++ b/spec/frontend/feature_flags/components/strategy_spec.js
@@ -18,6 +18,7 @@ import { userList } from '../mock_data';
const provide = {
strategyTypeDocsPagePath: 'link-to-strategy-docs',
environmentsScopeDocsPath: 'link-scope-docs',
+ environmentsEndpoint: '',
};
describe('Feature flags strategy', () => {
@@ -31,7 +32,6 @@ describe('Feature flags strategy', () => {
propsData: {
strategy: {},
index: 0,
- endpoint: '',
userLists: [userList],
},
provide,
@@ -52,7 +52,7 @@ describe('Feature flags strategy', () => {
});
describe('helper links', () => {
- const propsData = { strategy: {}, index: 0, endpoint: '', userLists: [userList] };
+ const propsData = { strategy: {}, index: 0, userLists: [userList] };
factory({ propsData, provide });
it('should display 2 helper links', () => {
@@ -76,7 +76,7 @@ describe('Feature flags strategy', () => {
beforeEach(() => {
strategy = { name, parameters: {}, scopes: [] };
- propsData = { strategy, index: 0, endpoint: '' };
+ propsData = { strategy, index: 0 };
factory({ propsData, provide });
return wrapper.vm.$nextTick();
});
@@ -102,7 +102,7 @@ describe('Feature flags strategy', () => {
parameters: { percentage: '50', groupId: 'default' },
scopes: [{ environmentScope: 'production' }],
};
- const propsData = { strategy, index: 0, endpoint: '' };
+ const propsData = { strategy, index: 0 };
factory({ propsData, provide });
});
@@ -123,7 +123,7 @@ describe('Feature flags strategy', () => {
parameters: { percentage: '50', groupId: 'default' },
scopes: [{ environmentScope: 'production' }],
};
- const propsData = { strategy, index: 0, endpoint: '' };
+ const propsData = { strategy, index: 0 };
factory({ propsData, provide });
});
@@ -152,7 +152,7 @@ describe('Feature flags strategy', () => {
parameters: { percentage: '50', groupId: PERCENT_ROLLOUT_GROUP_ID },
scopes: [{ environmentScope: '*' }],
};
- const propsData = { strategy, index: 0, endpoint: '' };
+ const propsData = { strategy, index: 0 };
factory({ propsData, provide });
});
@@ -221,7 +221,7 @@ describe('Feature flags strategy', () => {
parameters: { percentage: '50', groupId: PERCENT_ROLLOUT_GROUP_ID },
scopes: [],
};
- const propsData = { strategy, index: 0, endpoint: '' };
+ const propsData = { strategy, index: 0 };
factory({ propsData, provide });
});
diff --git a/spec/frontend/fixtures/static/issue_sidebar_label.html b/spec/frontend/fixtures/static/issue_sidebar_label.html
deleted file mode 100644
index ec8fb30f219..00000000000
--- a/spec/frontend/fixtures/static/issue_sidebar_label.html
+++ /dev/null
@@ -1,26 +0,0 @@
-<div class="block labels">
-<div class="sidebar-collapsed-icon js-sidebar-labels-tooltip"></div>
-<div class="title hide-collapsed">
-<a class="edit-link float-right" href="#">
-Edit
-</a>
-</div>
-<div class="selectbox hide-collapsed" style="display: none;">
-<div class="dropdown">
-<button class="dropdown-menu-toggle js-label-select js-multiselect" data-ability-name="issue" data-field-name="issue[label_names][]" data-issue-update="/root/test/issues/2.json" data-labels="/root/test/labels.json" data-project-id="12" data-show-any="true" data-show-no="true" data-toggle="dropdown" type="button">
-<span class="dropdown-toggle-text">
-Label
-</span>
-<i class="fa fa-chevron-down"></i>
-</button>
-<div class="dropdown-menu dropdown-select dropdown-menu-paging dropdown-menu-labels dropdown-menu-selectable">
-<div class="dropdown-page-one">
-<div class="dropdown-content"></div>
-<div class="dropdown-loading">
-<i class="fa fa-spinner fa-spin"></i>
-</div>
-</div>
-</div>
-</div>
-</div>
-</div>
diff --git a/spec/frontend/labels_issue_sidebar_spec.js b/spec/frontend/labels_issue_sidebar_spec.js
deleted file mode 100644
index f74547c0554..00000000000
--- a/spec/frontend/labels_issue_sidebar_spec.js
+++ /dev/null
@@ -1,98 +0,0 @@
-/* eslint-disable no-new */
-
-import $ from 'jquery';
-import MockAdapter from 'axios-mock-adapter';
-import { shuffle } from 'lodash';
-import axios from '~/lib/utils/axios_utils';
-import IssuableContext from '~/issuable_context';
-import LabelsSelect from '~/labels_select';
-
-import 'select2';
-import '~/api';
-import '~/create_label';
-import '~/users_select';
-
-let saveLabelCount = 0;
-let mock;
-
-function testLabelClicks(labelOrder, done) {
- $('.edit-link')
- .get(0)
- .click();
-
- jest.runOnlyPendingTimers();
-
- setImmediate(() => {
- const labelsInDropdown = $('.dropdown-content a');
-
- expect(labelsInDropdown.length).toBe(10);
-
- const arrayOfLabels = labelsInDropdown.get();
- const randomArrayOfLabels = shuffle(arrayOfLabels);
- randomArrayOfLabels.forEach((label, i) => {
- if (i < saveLabelCount) {
- $(label).click();
- }
- });
-
- $('.edit-link')
- .get(0)
- .click();
-
- setImmediate(() => {
- expect($('.sidebar-collapsed-icon').attr('data-original-title')).toBe(labelOrder);
- done();
- });
- });
-}
-
-describe('Issue dropdown sidebar', () => {
- preloadFixtures('static/issue_sidebar_label.html');
-
- beforeEach(() => {
- loadFixtures('static/issue_sidebar_label.html');
-
- mock = new MockAdapter(axios);
-
- new IssuableContext('{"id":1,"name":"Administrator","username":"root"}');
- new LabelsSelect();
-
- mock.onGet('/root/test/labels.json').reply(() => {
- const labels = Array(10)
- .fill()
- .map((_val, i) => ({
- id: i,
- title: `test ${i}`,
- color: '#5CB85C',
- }));
-
- return [200, labels];
- });
-
- mock.onPut('/root/test/issues/2.json').reply(() => {
- const labels = Array(saveLabelCount)
- .fill()
- .map((_val, i) => ({
- id: i,
- title: `test ${i}`,
- color: '#5CB85C',
- }));
-
- return [200, { labels }];
- });
- });
-
- afterEach(() => {
- mock.restore();
- });
-
- it('changes collapsed tooltip when changing labels when less than 5', done => {
- saveLabelCount = 5;
- testLabelClicks('test 0, test 1, test 2, test 3, test 4', done);
- });
-
- it('changes collapsed tooltip when changing labels when more than 5', done => {
- saveLabelCount = 6;
- testLabelClicks('test 0, test 1, test 2, test 3, test 4, and 1 more', done);
- });
-});
diff --git a/spec/graphql/mutations/issues/move_spec.rb b/spec/graphql/mutations/issues/move_spec.rb
new file mode 100644
index 00000000000..c8e9c556a3f
--- /dev/null
+++ b/spec/graphql/mutations/issues/move_spec.rb
@@ -0,0 +1,41 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Mutations::Issues::Move do
+ let_it_be(:issue) { create(:issue) }
+ let_it_be(:user) { create(:user) }
+ let_it_be(:target_project) { create(:project) }
+
+ subject(:mutation) { described_class.new(object: nil, context: { current_user: user }, field: nil) }
+
+ describe '#resolve' do
+ subject(:resolve) { mutation.resolve(project_path: issue.project.full_path, iid: issue.iid, target_project_path: target_project.full_path) }
+
+ it 'raises an error if the resource is not accessible to the user' do
+ expect { resolve }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
+ end
+
+ context 'when user does not have permissions' do
+ before do
+ issue.project.add_developer(user)
+ end
+
+ it 'returns error message' do
+ expect(resolve[:issue]).to eq(nil)
+ expect(resolve[:errors].first).to eq('Cannot move issue due to insufficient permissions!')
+ end
+ end
+
+ context 'when user has sufficient permissions' do
+ before do
+ issue.project.add_developer(user)
+ target_project.add_developer(user)
+ end
+
+ it 'moves issue' do
+ expect(resolve[:issue].project).to eq(target_project)
+ end
+ end
+ end
+end
diff --git a/spec/helpers/issuables_helper_spec.rb b/spec/helpers/issuables_helper_spec.rb
index 2475620221c..e8e5adaa274 100644
--- a/spec/helpers/issuables_helper_spec.rb
+++ b/spec/helpers/issuables_helper_spec.rb
@@ -44,23 +44,6 @@ RSpec.describe IssuablesHelper do
end
end
- describe '#issuable_labels_tooltip' do
- let(:label_entity) { LabelEntity.represent(label).as_json }
- let(:label2_entity) { LabelEntity.represent(label2).as_json }
-
- it 'returns label text with no labels' do
- expect(issuable_labels_tooltip([])).to eq(_('Labels'))
- end
-
- it 'returns label text with labels within max limit' do
- expect(issuable_labels_tooltip([label_entity])).to eq(label[:title])
- end
-
- it 'returns label text with labels exceeding max limit' do
- expect(issuable_labels_tooltip([label_entity, label2_entity], limit: 1)).to eq("#{label[:title]}, and 1 more")
- end
- end
-
describe '#issuables_state_counter_text' do
let(:user) { create(:user) }
diff --git a/spec/helpers/labels_helper_spec.rb b/spec/helpers/labels_helper_spec.rb
index 555decd284a..b93dc03e434 100644
--- a/spec/helpers/labels_helper_spec.rb
+++ b/spec/helpers/labels_helper_spec.rb
@@ -244,26 +244,6 @@ RSpec.describe LabelsHelper do
end
end
- describe 'label_from_hash' do
- it 'builds a group label with whitelisted attributes' do
- label = label_from_hash({ title: 'foo', color: 'bar', id: 1, group_id: 1 })
-
- expect(label).to be_a(GroupLabel)
- expect(label.id).to be_nil
- expect(label.title).to eq('foo')
- expect(label.color).to eq('bar')
- end
-
- it 'builds a project label with whitelisted attributes' do
- label = label_from_hash({ title: 'foo', color: 'bar', id: 1, project_id: 1 })
-
- expect(label).to be_a(ProjectLabel)
- expect(label.id).to be_nil
- expect(label.title).to eq('foo')
- expect(label.color).to eq('bar')
- end
- end
-
describe '#label_status_tooltip' do
let(:status) { 'unsubscribed'.inquiry }
diff --git a/spec/requests/api/graphql/mutations/issues/move_spec.rb b/spec/requests/api/graphql/mutations/issues/move_spec.rb
new file mode 100644
index 00000000000..5bbaff61edd
--- /dev/null
+++ b/spec/requests/api/graphql/mutations/issues/move_spec.rb
@@ -0,0 +1,73 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'Moving an issue' do
+ include GraphqlHelpers
+
+ let_it_be(:user) { create(:user) }
+ let_it_be(:issue) { create(:issue) }
+ let_it_be(:target_project) { create(:project) }
+
+ let(:mutation) do
+ variables = {
+ project_path: issue.project.full_path,
+ target_project_path: target_project.full_path,
+ iid: issue.iid.to_s
+ }
+
+ graphql_mutation(:issue_move, variables,
+ <<-QL.strip_heredoc
+ clientMutationId
+ errors
+ issue {
+ title
+ }
+ QL
+ )
+ end
+
+ def mutation_response
+ graphql_mutation_response(:issue_move)
+ end
+
+ context 'when the user is not allowed to read source project' do
+ it 'returns an error' do
+ error = "The resource that you are attempting to access does not exist or you don't have permission to perform this action"
+ post_graphql_mutation(mutation, current_user: user)
+
+ expect(response).to have_gitlab_http_status(:success)
+ expect(graphql_errors).to include(a_hash_including('message' => error))
+ end
+ end
+
+ context 'when the user is not allowed to move issue to target project' do
+ before do
+ issue.project.add_developer(user)
+ end
+
+ it 'returns an error' do
+ error = "Cannot move issue due to insufficient permissions!"
+ post_graphql_mutation(mutation, current_user: user)
+
+ expect(response).to have_gitlab_http_status(:success)
+ expect(mutation_response['errors'][0]).to eq(error)
+ end
+ end
+
+ context 'when the user is allowed to move issue' do
+ before do
+ issue.project.add_developer(user)
+ target_project.add_developer(user)
+ end
+
+ it 'moves the issue' do
+ post_graphql_mutation(mutation, current_user: user)
+
+ expect(response).to have_gitlab_http_status(:success)
+ expect(mutation_response.dig('issue', 'title')).to eq(issue.title)
+ expect(issue.reload.state).to eq('closed')
+ expect(target_project.issues.find_by_title(issue.title)).to be_present
+ end
+ end
+end
diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb
index 831b0d6e678..2abcb39a1c8 100644
--- a/spec/requests/api/projects_spec.rb
+++ b/spec/requests/api/projects_spec.rb
@@ -1615,6 +1615,7 @@ RSpec.describe API::Projects do
expect(json_response['allow_merge_on_skipped_pipeline']).to eq(project.allow_merge_on_skipped_pipeline)
expect(json_response['only_allow_merge_if_all_discussions_are_resolved']).to eq(project.only_allow_merge_if_all_discussions_are_resolved)
expect(json_response['ci_default_git_depth']).to eq(project.ci_default_git_depth)
+ expect(json_response['ci_forward_deployment_enabled']).to eq(project.ci_forward_deployment_enabled)
expect(json_response['merge_method']).to eq(project.merge_method.to_s)
expect(json_response['readme_url']).to eq(project.readme_url)
expect(json_response).to have_key 'packages_enabled'
@@ -2607,6 +2608,7 @@ RSpec.describe API::Projects do
merge_requests_enabled: true,
merge_method: 'ff',
ci_default_git_depth: 20,
+ ci_forward_deployment_enabled: false,
description: 'new description' }
put api("/projects/#{project3.id}", user4), params: project_param