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

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2021-01-21 21:09:11 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2021-01-21 21:09:11 +0300
commit37a492326e7cde47928ee72abcc284ff9cf8f254 (patch)
tree832417ddab170745bca91ec158b70973ba0700af
parent81884c35b86035f89364db482e4fff91718bb63a (diff)
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--app/assets/javascripts/merge_request/components/status_box.vue2
-rw-r--r--app/assets/javascripts/monitoring/components/charts/single_stat.vue14
-rw-r--r--app/assets/javascripts/pages/projects/commit/pipelines/index.js2
-rw-r--r--app/assets/javascripts/pages/projects/commit/show/index.js6
-rw-r--r--app/assets/javascripts/pipelines/pipeline_details_bundle.js7
-rw-r--r--app/assets/javascripts/projects/commit/components/branches_dropdown.vue1
-rw-r--r--app/assets/javascripts/projects/commit/components/form_trigger.vue5
-rw-r--r--app/assets/javascripts/projects/commit/constants.js9
-rw-r--r--app/assets/javascripts/projects/commit/index.js11
-rw-r--r--app/assets/javascripts/projects/commit/init_cherry_pick_commit_modal.js51
-rw-r--r--app/assets/javascripts/projects/commit/init_cherry_pick_commit_trigger.js20
-rw-r--r--app/assets/javascripts/projects/commit/init_revert_commit_trigger.js8
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/terraform/terraform_plan.vue24
-rw-r--r--app/assets/javascripts/vue_shared/components/gfm_autocomplete/utils.js4
-rw-r--r--app/assets/javascripts/vue_shared/security_reports/security_reports_app.vue98
-rw-r--r--app/controllers/projects/merge_requests_controller.rb1
-rw-r--r--app/controllers/projects/pipelines_controller.rb3
-rw-r--r--app/helpers/commits_helper.rb40
-rw-r--r--app/models/pages/lookup_path.rb12
-rw-r--r--app/models/pages/virtual_domain.rb11
-rw-r--r--app/views/projects/commit/_change.html.haml12
-rw-r--r--app/views/projects/commit/_commit_box.html.haml4
-rw-r--r--app/views/projects/commit/pipelines.html.haml4
-rw-r--r--app/views/projects/commit/show.html.haml2
-rw-r--r--app/views/projects/pipelines/show.html.haml2
-rw-r--r--app/workers/container_expiration_policies/cleanup_container_repository_worker.rb3
-rw-r--r--changelogs/unreleased/229679-migrate-data-toggle-modal-to-glmodal-for-revert-cherry-picking-com.yml5
-rw-r--r--changelogs/unreleased/262863-delete-oncall-rotation-graphql.yml5
-rw-r--r--changelogs/unreleased/283912-remove-redundant-text.yml5
-rw-r--r--changelogs/unreleased/compliance_pipeline_configuration.yml5
-rw-r--r--changelogs/unreleased/ph-mergeRequestLockedStatus.yml5
-rw-r--r--changelogs/unreleased/variables-in-include-enable.yml5
-rw-r--r--config/feature_flags/development/core_security_mr_widget_downloads.yml8
-rw-r--r--config/feature_flags/development/drop_license_management_artifact.yml6
-rw-r--r--config/feature_flags/development/graphql_pipeline_details_users.yml8
-rw-r--r--config/feature_flags/development/pages_serve_from_legacy_storage.yml8
-rw-r--r--config/feature_flags/development/variables_in_include_section_ci.yml8
-rw-r--r--data/whats_new/202101140001_13_08.yml83
-rw-r--r--db/migrate/20210113224909_add_pipeline_configuration_full_path_to_compliance_pipeline.rb16
-rw-r--r--db/migrate/20210119162812_add_text_limit_to_compliance_pipeline_configuration_full_path.rb17
-rw-r--r--db/schema_migrations/202101132249091
-rw-r--r--db/schema_migrations/202101191628121
-rw-r--r--db/structure.sql4
-rw-r--r--doc/administration/geo/replication/datatypes.md2
-rw-r--r--doc/administration/geo/setup/database.md2
-rw-r--r--doc/api/graphql/reference/gitlab_schema.graphql58
-rw-r--r--doc/api/graphql/reference/gitlab_schema.json181
-rw-r--r--doc/api/graphql/reference/index.md11
-rw-r--r--doc/api/merge_requests.md2
-rw-r--r--doc/api/protected_branches.md2
-rw-r--r--doc/api/runners.md2
-rw-r--r--doc/ci/pipeline_editor/index.md6
-rw-r--r--doc/ci/pipelines/pipeline_efficiency.md2
-rw-r--r--doc/ci/yaml/README.md57
-rw-r--r--doc/development/code_review.md2
-rw-r--r--doc/development/elasticsearch.md4
-rw-r--r--doc/development/iterating_tables_in_batches.md2
-rw-r--r--doc/development/profiling.md2
-rw-r--r--doc/development/secure_coding_guidelines.md2
-rw-r--r--doc/development/testing_guide/frontend_testing.md2
-rw-r--r--doc/integration/saml.md2
-rw-r--r--doc/operations/product_analytics.md2
-rw-r--r--doc/user/admin_area/settings/sign_up_restrictions.md2
-rw-r--r--doc/user/group/saml_sso/index.md5
-rw-r--r--doc/user/project/issues/due_dates.md2
-rw-r--r--lib/gitlab/ci/config/external/mapper.rb2
-rw-r--r--locale/gitlab.pot20
-rw-r--r--package.json2
-rw-r--r--spec/features/projects/commit/cherry_pick_spec.rb160
-rw-r--r--spec/features/projects/commit/user_reverts_commit_spec.rb91
-rw-r--r--spec/features/projects/pipelines/pipeline_spec.rb1
-rw-r--r--spec/features/projects/pipelines/pipelines_spec.rb1
-rw-r--r--spec/frontend/merge_request/components/status_box_spec.js6
-rw-r--r--spec/frontend/monitoring/components/charts/single_stat_spec.js11
-rw-r--r--spec/frontend/vue_mr_widget/components/terraform/terraform_plan_spec.js8
-rw-r--r--spec/frontend/vue_mr_widget/mr_widget_options_spec.js57
-rw-r--r--spec/frontend/vue_shared/components/gfm_autocomplete/__snapshots__/utils_spec.js.snap8
-rw-r--r--spec/frontend/vue_shared/security_reports/mock_data.js17
-rw-r--r--spec/frontend/vue_shared/security_reports/security_reports_app_spec.js358
-rw-r--r--spec/helpers/commits_helper_spec.rb31
-rw-r--r--spec/lib/gitlab/ci/config/external/mapper_spec.rb14
-rw-r--r--spec/models/pages/lookup_path_spec.rb9
-rw-r--r--spec/models/pages/virtual_domain_spec.rb21
-rw-r--r--spec/workers/container_expiration_policies/cleanup_container_repository_worker_spec.rb5
-rw-r--r--yarn.lock8
85 files changed, 1034 insertions, 694 deletions
diff --git a/app/assets/javascripts/merge_request/components/status_box.vue b/app/assets/javascripts/merge_request/components/status_box.vue
index fd99802caff..b2754dd4ad6 100644
--- a/app/assets/javascripts/merge_request/components/status_box.vue
+++ b/app/assets/javascripts/merge_request/components/status_box.vue
@@ -5,12 +5,14 @@ import mrEventHub from '../eventhub';
const CLASSES = {
opened: 'status-box-open',
+ locked: 'status-box-open',
closed: 'status-box-mr-closed',
merged: 'status-box-mr-merged',
};
const STATUS = {
opened: [__('Open'), 'issue-open-m'],
+ locked: [__('Open'), 'issue-open-m'],
closed: [__('Closed'), 'close'],
merged: [__('Merged'), 'git-merge'],
};
diff --git a/app/assets/javascripts/monitoring/components/charts/single_stat.vue b/app/assets/javascripts/monitoring/components/charts/single_stat.vue
index a8ab41ebf26..4cf583fed18 100644
--- a/app/assets/javascripts/monitoring/components/charts/single_stat.vue
+++ b/app/assets/javascripts/monitoring/components/charts/single_stat.vue
@@ -45,12 +45,18 @@ export default {
}
if (this.graphData?.maxValue) {
- formatter = getFormatter(SUPPORTED_FORMATS.percent);
- return formatter(this.queryResult / Number(this.graphData.maxValue), defaultPrecision);
+ formatter = getFormatter(SUPPORTED_FORMATS.number);
+ return formatter(
+ (this.queryResult / Number(this.graphData.maxValue)) * 100,
+ defaultPrecision,
+ );
}
formatter = getFormatter(SUPPORTED_FORMATS.number);
- return `${formatter(this.queryResult, defaultPrecision)}${this.queryInfo.unit ?? ''}`;
+ return `${formatter(this.queryResult, defaultPrecision)}`;
+ },
+ unit() {
+ return this.graphData?.maxValue ? '%' : this.queryInfo.unit;
},
graphTitle() {
return this.queryInfo.label;
@@ -60,6 +66,6 @@ export default {
</script>
<template>
<div>
- <gl-single-stat :value="statValue" :title="graphTitle" variant="success" />
+ <gl-single-stat :value="statValue" :title="graphTitle" :unit="unit" variant="success" />
</div>
</template>
diff --git a/app/assets/javascripts/pages/projects/commit/pipelines/index.js b/app/assets/javascripts/pages/projects/commit/pipelines/index.js
index eaf340f2725..f542014c5b9 100644
--- a/app/assets/javascripts/pages/projects/commit/pipelines/index.js
+++ b/app/assets/javascripts/pages/projects/commit/pipelines/index.js
@@ -1,5 +1,7 @@
import { initCommitBoxInfo } from '~/projects/commit_box/info';
import initPipelines from '~/commit/pipelines/pipelines_bundle';
+import initCommitActions from '~/projects/commit';
initCommitBoxInfo();
initPipelines();
+initCommitActions();
diff --git a/app/assets/javascripts/pages/projects/commit/show/index.js b/app/assets/javascripts/pages/projects/commit/show/index.js
index 5cfdb125e4f..5a3b486fd40 100644
--- a/app/assets/javascripts/pages/projects/commit/show/index.js
+++ b/app/assets/javascripts/pages/projects/commit/show/index.js
@@ -14,8 +14,7 @@ import flash from '~/flash';
import { __ } from '~/locale';
import loadAwardsHandler from '~/awards_handler';
import { initCommitBoxInfo } from '~/projects/commit_box/info';
-import initRevertCommitTrigger from '~/projects/commit/init_revert_commit_trigger';
-import initRevertCommitModal from '~/projects/commit/init_revert_commit_modal';
+import initCommitActions from '~/projects/commit';
const hasPerfBar = document.querySelector('.with-performance-bar');
const performanceHeight = hasPerfBar ? 35 : 0;
@@ -47,5 +46,4 @@ if (filesContainer.length) {
new Diff();
}
loadAwardsHandler();
-initRevertCommitModal();
-initRevertCommitTrigger();
+initCommitActions();
diff --git a/app/assets/javascripts/pipelines/pipeline_details_bundle.js b/app/assets/javascripts/pipelines/pipeline_details_bundle.js
index 133608b9801..1c6f0b3f036 100644
--- a/app/assets/javascripts/pipelines/pipeline_details_bundle.js
+++ b/app/assets/javascripts/pipelines/pipeline_details_bundle.js
@@ -136,10 +136,13 @@ export default async function () {
createTestDetails();
createDagApp();
+ const canShowNewPipelineDetails =
+ gon.features.graphqlPipelineDetails || gon.features.graphqlPipelineDetailsUsers;
+
const { dataset } = document.querySelector(SELECTORS.PIPELINE_DETAILS);
let mediator;
- if (!gon.features.graphqlPipelineHeader || !gon.features.graphqlPipelineDetails) {
+ if (!gon.features.graphqlPipelineHeader || !canShowNewPipelineDetails) {
try {
const { default: PipelinesMediator } = await import(
/* webpackChunkName: 'PipelinesMediator' */ './pipeline_details_mediator'
@@ -151,7 +154,7 @@ export default async function () {
}
}
- if (gon.features.graphqlPipelineDetails) {
+ if (canShowNewPipelineDetails) {
try {
const { createPipelinesDetailApp } = await import(
/* webpackChunkName: 'createPipelinesDetailApp' */ './pipeline_details_graph'
diff --git a/app/assets/javascripts/projects/commit/components/branches_dropdown.vue b/app/assets/javascripts/projects/commit/components/branches_dropdown.vue
index 3ecc3f1d1d3..36da4128450 100644
--- a/app/assets/javascripts/projects/commit/components/branches_dropdown.vue
+++ b/app/assets/javascripts/projects/commit/components/branches_dropdown.vue
@@ -68,6 +68,7 @@ export default {
autocomplete="off"
:debounce="250"
:placeholder="$options.i18n.searchPlaceholder"
+ data-testid="dropdown-search-box"
@input="searchTermChanged"
/>
<gl-dropdown-item
diff --git a/app/assets/javascripts/projects/commit/components/form_trigger.vue b/app/assets/javascripts/projects/commit/components/form_trigger.vue
index e92854c1ac3..3561b5c2473 100644
--- a/app/assets/javascripts/projects/commit/components/form_trigger.vue
+++ b/app/assets/javascripts/projects/commit/components/form_trigger.vue
@@ -10,6 +10,9 @@ export default {
displayText: {
default: '',
},
+ testId: {
+ default: '',
+ },
},
props: {
openModal: {
@@ -26,7 +29,7 @@ export default {
</script>
<template>
- <gl-link data-is-link="true" data-testid="revert-commit-link" @click="showModal">
+ <gl-link data-is-link="true" :data-testid="testId" @click="showModal">
{{ displayText }}
</gl-link>
</template>
diff --git a/app/assets/javascripts/projects/commit/constants.js b/app/assets/javascripts/projects/commit/constants.js
index 233f43d56b9..b47c744e5fb 100644
--- a/app/assets/javascripts/projects/commit/constants.js
+++ b/app/assets/javascripts/projects/commit/constants.js
@@ -2,6 +2,10 @@ import { s__, __ } from '~/locale';
export const OPEN_REVERT_MODAL = 'openRevertModal';
export const REVERT_MODAL_ID = 'revert-commit-modal';
+export const REVERT_LINK_TEST_ID = 'revert-commit-link';
+export const OPEN_CHERRY_PICK_MODAL = 'openCherryPickModal';
+export const CHERRY_PICK_MODAL_ID = 'cherry-pick-commit-modal';
+export const CHERRY_PICK_LINK_TEST_ID = 'cherry-pick-commit-link';
export const I18N_MODAL = {
startMergeRequest: s__('ChangeTypeAction|Start a %{newMergeRequest} with these changes'),
@@ -20,6 +24,11 @@ export const I18N_REVERT_MODAL = {
actionPrimaryText: s__('ChangeTypeAction|Revert'),
};
+export const I18N_CHERRY_PICK_MODAL = {
+ branchLabel: s__('ChangeTypeAction|Pick into branch'),
+ actionPrimaryText: s__('ChangeTypeAction|Cherry-pick'),
+};
+
export const PREPENDED_MODAL_TEXT = s__(
'ChangeTypeAction|This will create a new commit in order to revert the existing changes.',
);
diff --git a/app/assets/javascripts/projects/commit/index.js b/app/assets/javascripts/projects/commit/index.js
new file mode 100644
index 00000000000..d5d7e62ce32
--- /dev/null
+++ b/app/assets/javascripts/projects/commit/index.js
@@ -0,0 +1,11 @@
+import initRevertCommitTrigger from './init_revert_commit_trigger';
+import initRevertCommitModal from './init_revert_commit_modal';
+import initCherryPickCommitTrigger from './init_cherry_pick_commit_trigger';
+import initCherryPickCommitModal from './init_cherry_pick_commit_modal';
+
+export default () => {
+ initRevertCommitModal();
+ initRevertCommitTrigger();
+ initCherryPickCommitModal();
+ initCherryPickCommitTrigger();
+};
diff --git a/app/assets/javascripts/projects/commit/init_cherry_pick_commit_modal.js b/app/assets/javascripts/projects/commit/init_cherry_pick_commit_modal.js
new file mode 100644
index 00000000000..0bac569e5b7
--- /dev/null
+++ b/app/assets/javascripts/projects/commit/init_cherry_pick_commit_modal.js
@@ -0,0 +1,51 @@
+import Vue from 'vue';
+import CommitFormModal from './components/form_modal.vue';
+import { parseBoolean } from '~/lib/utils/common_utils';
+import createStore from './store';
+import {
+ I18N_MODAL,
+ I18N_CHERRY_PICK_MODAL,
+ OPEN_CHERRY_PICK_MODAL,
+ CHERRY_PICK_MODAL_ID,
+} from './constants';
+
+export default function initInviteMembersModal() {
+ const el = document.querySelector('.js-cherry-pick-commit-modal');
+ if (!el) {
+ return false;
+ }
+
+ const {
+ title,
+ endpoint,
+ branch,
+ pushCode,
+ branchCollaboration,
+ existingBranch,
+ branchesEndpoint,
+ } = el.dataset;
+
+ const store = createStore({
+ endpoint,
+ branchesEndpoint,
+ branch,
+ pushCode: parseBoolean(pushCode),
+ branchCollaboration: parseBoolean(branchCollaboration),
+ defaultBranch: branch,
+ modalTitle: title,
+ existingBranch,
+ });
+
+ return new Vue({
+ el,
+ store,
+ render: (createElement) =>
+ createElement(CommitFormModal, {
+ props: {
+ i18n: { ...I18N_CHERRY_PICK_MODAL, ...I18N_MODAL },
+ openModal: OPEN_CHERRY_PICK_MODAL,
+ modalId: CHERRY_PICK_MODAL_ID,
+ },
+ }),
+ });
+}
diff --git a/app/assets/javascripts/projects/commit/init_cherry_pick_commit_trigger.js b/app/assets/javascripts/projects/commit/init_cherry_pick_commit_trigger.js
new file mode 100644
index 00000000000..942451dc96a
--- /dev/null
+++ b/app/assets/javascripts/projects/commit/init_cherry_pick_commit_trigger.js
@@ -0,0 +1,20 @@
+import Vue from 'vue';
+import CommitFormTrigger from './components/form_trigger.vue';
+import { OPEN_CHERRY_PICK_MODAL, CHERRY_PICK_LINK_TEST_ID } from './constants';
+
+export default function initInviteMembersTrigger() {
+ const el = document.querySelector('.js-cherry-pick-commit-trigger');
+
+ if (!el) {
+ return false;
+ }
+
+ const { displayText } = el.dataset;
+
+ return new Vue({
+ el,
+ provide: { displayText, testId: CHERRY_PICK_LINK_TEST_ID },
+ render: (createElement) =>
+ createElement(CommitFormTrigger, { props: { openModal: OPEN_CHERRY_PICK_MODAL } }),
+ });
+}
diff --git a/app/assets/javascripts/projects/commit/init_revert_commit_trigger.js b/app/assets/javascripts/projects/commit/init_revert_commit_trigger.js
index 0bb57f22663..dc5168524ca 100644
--- a/app/assets/javascripts/projects/commit/init_revert_commit_trigger.js
+++ b/app/assets/javascripts/projects/commit/init_revert_commit_trigger.js
@@ -1,6 +1,6 @@
import Vue from 'vue';
-import RevertCommitTrigger from './components/form_trigger.vue';
-import { OPEN_REVERT_MODAL } from './constants';
+import CommitFormTrigger from './components/form_trigger.vue';
+import { OPEN_REVERT_MODAL, REVERT_LINK_TEST_ID } from './constants';
export default function initInviteMembersTrigger() {
const el = document.querySelector('.js-revert-commit-trigger');
@@ -13,8 +13,8 @@ export default function initInviteMembersTrigger() {
return new Vue({
el,
- provide: { displayText },
+ provide: { displayText, testId: REVERT_LINK_TEST_ID },
render: (createElement) =>
- createElement(RevertCommitTrigger, { props: { openModal: OPEN_REVERT_MODAL } }),
+ createElement(CommitFormTrigger, { props: { openModal: OPEN_REVERT_MODAL } }),
});
}
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/terraform/terraform_plan.vue b/app/assets/javascripts/vue_merge_request_widget/components/terraform/terraform_plan.vue
index b74e036d9d9..5f65d1fa49a 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/terraform/terraform_plan.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/terraform/terraform_plan.vue
@@ -15,6 +15,16 @@ export default {
type: Object,
},
},
+ i18n: {
+ changes: s__(
+ 'Terraform|Reported Resource Changes: %{addNum} to add, %{changeNum} to change, %{deleteNum} to delete',
+ ),
+ generationErrored: s__('Terraform|Generating the report caused an error.'),
+ namedReportFailed: s__('Terraform|The report %{name} failed to generate.'),
+ namedReportGenerated: s__('Terraform|The report %{name} was generated in your pipelines.'),
+ reportFailed: s__('Terraform|A report failed to generate.'),
+ reportGenerated: s__('Terraform|A report was generated in your pipelines.'),
+ },
computed: {
addNum() {
return Number(this.plan.create);
@@ -30,23 +40,21 @@ export default {
},
reportChangeText() {
if (this.validPlanValues) {
- return s__(
- 'Terraform|Reported Resource Changes: %{addNum} to add, %{changeNum} to change, %{deleteNum} to delete',
- );
+ return this.$options.i18n.changes;
}
- return s__('Terraform|Generating the report caused an error.');
+ return this.$options.i18n.generationErrored;
},
reportHeaderText() {
if (this.validPlanValues) {
return this.plan.job_name
- ? s__('Terraform|The Terraform report %{name} was generated in your pipelines.')
- : s__('Terraform|A Terraform report was generated in your pipelines.');
+ ? this.$options.i18n.namedReportGenerated
+ : this.$options.i18n.reportGenerated;
}
return this.plan.job_name
- ? s__('Terraform|The Terraform report %{name} failed to generate.')
- : s__('Terraform|A Terraform report failed to generate.');
+ ? this.$options.i18n.namedReportFailed
+ : this.$options.i18n.reportFailed;
},
validPlanValues() {
return this.addNum + this.changeNum + this.deleteNum >= 0;
diff --git a/app/assets/javascripts/vue_shared/components/gfm_autocomplete/utils.js b/app/assets/javascripts/vue_shared/components/gfm_autocomplete/utils.js
index 809932b0f29..44c3fc34ba6 100644
--- a/app/assets/javascripts/vue_shared/components/gfm_autocomplete/utils.js
+++ b/app/assets/javascripts/vue_shared/components/gfm_autocomplete/utils.js
@@ -84,7 +84,7 @@ export const tributeConfig = {
value.type === groupType ? last(value.name.split(' / ')) : `${value.name}${value.username}`,
menuItemLimit: memberLimit,
menuItemTemplate: ({ original }) => {
- const commonClasses = 'gl-avatar gl-avatar-s24 gl-flex-shrink-0';
+ const commonClasses = 'gl-avatar gl-avatar-s32 gl-flex-shrink-0';
const noAvatarClasses = `${commonClasses} gl-rounded-small
gl-display-flex gl-align-items-center gl-justify-content-center`;
@@ -111,7 +111,7 @@ export const tributeConfig = {
return `
<div class="gl-display-flex gl-align-items-center">
${avatar}
- <div class="gl-font-sm gl-line-height-normal gl-ml-3">
+ <div class="gl-line-height-normal gl-ml-4">
<div>${escape(displayName)}${count}</div>
<div class="gl-text-gray-700">${escape(parentGroupOrUsername)}</div>
</div>
diff --git a/app/assets/javascripts/vue_shared/security_reports/security_reports_app.vue b/app/assets/javascripts/vue_shared/security_reports/security_reports_app.vue
index a6c7b59aa71..b6b32167ed6 100644
--- a/app/assets/javascripts/vue_shared/security_reports/security_reports_app.vue
+++ b/app/assets/javascripts/vue_shared/security_reports/security_reports_app.vue
@@ -1,13 +1,10 @@
<script>
import { mapActions, mapGetters } from 'vuex';
-import { GlLink, GlSprintf } from '@gitlab/ui';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import ReportSection from '~/reports/components/report_section.vue';
-import { LOADING, ERROR, SLOT_SUCCESS, SLOT_LOADING, SLOT_ERROR } from '~/reports/constants';
+import { ERROR, SLOT_SUCCESS, SLOT_LOADING, SLOT_ERROR } from '~/reports/constants';
import { s__ } from '~/locale';
-import { normalizeHeaders, parseIntPagination } from '~/lib/utils/common_utils';
import createFlash from '~/flash';
-import Api from '~/api';
import HelpIcon from './components/help_icon.vue';
import SecurityReportDownloadDropdown from './components/security_report_download_dropdown.vue';
import SecuritySummary from './components/security_summary.vue';
@@ -24,8 +21,6 @@ import { extractSecurityReportArtifacts } from './utils';
export default {
store,
components: {
- GlLink,
- GlSprintf,
ReportSection,
HelpIcon,
SecurityReportDownloadDropdown,
@@ -101,9 +96,6 @@ export default {
),
};
},
- skip() {
- return !this.canShowDownloads;
- },
update(data) {
return extractSecurityReportArtifacts(this.$options.reportTypes, data);
},
@@ -124,9 +116,6 @@ export default {
},
computed: {
...mapGetters(['groupedSummaryText', 'summaryStatus']),
- canShowDownloads() {
- return this.glFeatures.coreSecurityMrWidgetDownloads;
- },
hasSecurityReports() {
return this.availableSecurityReports.length > 0;
},
@@ -139,23 +128,6 @@ export default {
isLoadingReportArtifacts() {
return this.$apollo.queries.reportArtifacts.loading;
},
- shouldShowDownloadGuidance() {
- return !this.canShowDownloads && this.summaryStatus !== LOADING;
- },
- scansHaveRunMessage() {
- return this.canShowDownloads
- ? this.$options.i18n.scansHaveRun
- : this.$options.i18n.scansHaveRunWithDownloadGuidance;
- },
- },
- created() {
- if (!this.canShowDownloads) {
- this.checkAvailableSecurityReports(this.$options.reportTypes)
- .then((availableSecurityReports) => {
- this.onCheckingAvailableSecurityReports(Array.from(availableSecurityReports));
- })
- .catch(this.showError);
- }
},
methods: {
...mapActions(MODULE_SAST, {
@@ -166,36 +138,6 @@ export default {
setSecretDetectionDiffEndpoint: 'setDiffEndpoint',
fetchSecretDetectionDiff: 'fetchDiff',
}),
- async checkAvailableSecurityReports(reportTypes) {
- const reportTypesSet = new Set(reportTypes);
- const availableReportTypes = new Set();
-
- let page = 1;
- while (page) {
- // eslint-disable-next-line no-await-in-loop
- const { data: jobs, headers } = await Api.pipelineJobs(this.projectId, this.pipelineId, {
- per_page: 100,
- page,
- });
-
- jobs.forEach(({ artifacts = [] }) => {
- artifacts.forEach(({ file_type }) => {
- if (reportTypesSet.has(file_type)) {
- availableReportTypes.add(file_type);
- }
- });
- });
-
- // If we've found artifacts for all the report types, stop looking!
- if (availableReportTypes.size === reportTypesSet.size) {
- return availableReportTypes;
- }
-
- page = parseIntPagination(normalizeHeaders(headers)).nextPage;
- }
-
- return availableReportTypes;
- },
fetchCounts() {
if (!this.glFeatures.coreSecurityMrWidgetCounts) {
return;
@@ -213,11 +155,6 @@ export default {
this.canShowCounts = true;
}
},
- activatePipelinesTab() {
- if (window.mrTabs) {
- window.mrTabs.tabShown('pipelines');
- }
- },
onCheckingAvailableSecurityReports(availableSecurityReports) {
this.availableSecurityReports = availableSecurityReports;
this.fetchCounts();
@@ -236,12 +173,6 @@ export default {
'SecurityReports|Failed to get security report information. Please reload the page or try again later.',
),
scansHaveRun: s__('SecurityReports|Security scans have run'),
- scansHaveRunWithDownloadGuidance: s__(
- 'SecurityReports|Security scans have run. Go to the %{linkStart}pipelines tab%{linkEnd} to download the security reports',
- ),
- downloadFromPipelineTab: s__(
- 'SecurityReports|Go to the %{linkStart}pipelines tab%{linkEnd} to download the security reports',
- ),
},
summarySlots: [SLOT_SUCCESS, SLOT_LOADING, SLOT_ERROR],
};
@@ -265,22 +196,7 @@ export default {
</span>
</template>
- <template v-if="shouldShowDownloadGuidance" #sub-heading>
- <span class="gl-font-sm">
- <gl-sprintf :message="$options.i18n.downloadFromPipelineTab">
- <template #link="{ content }">
- <gl-link
- class="gl-font-sm"
- data-testid="show-pipelines"
- @click="activatePipelinesTab"
- >{{ content }}</gl-link
- >
- </template>
- </gl-sprintf>
- </span>
- </template>
-
- <template v-if="canShowDownloads" #action-buttons>
+ <template #action-buttons>
<security-report-download-dropdown
:artifacts="reportArtifacts"
:loading="isLoadingReportArtifacts"
@@ -298,13 +214,7 @@ export default {
data-testid="security-mr-widget"
>
<template #error>
- <gl-sprintf :message="scansHaveRunMessage">
- <template #link="{ content }">
- <gl-link data-testid="show-pipelines" @click="activatePipelinesTab">{{
- content
- }}</gl-link>
- </template>
- </gl-sprintf>
+ {{ $options.i18n.scansHaveRun }}
<help-icon
:help-path="securityReportsDocsPath"
@@ -312,7 +222,7 @@ export default {
/>
</template>
- <template v-if="canShowDownloads" #action-buttons>
+ <template #action-buttons>
<security-report-download-dropdown
:artifacts="reportArtifacts"
:loading="isLoadingReportArtifacts"
diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb
index d452a5e02e2..27402573c42 100644
--- a/app/controllers/projects/merge_requests_controller.rb
+++ b/app/controllers/projects/merge_requests_controller.rb
@@ -38,7 +38,6 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
push_frontend_feature_flag(:default_merge_ref_for_diffs, @project)
push_frontend_feature_flag(:core_security_mr_widget, @project, default_enabled: true)
push_frontend_feature_flag(:core_security_mr_widget_counts, @project)
- push_frontend_feature_flag(:core_security_mr_widget_downloads, @project, default_enabled: true)
push_frontend_feature_flag(:remove_resolve_note, @project, default_enabled: true)
push_frontend_feature_flag(:diffs_gradual_load, @project, default_enabled: true)
push_frontend_feature_flag(:codequality_mr_diff, @project)
diff --git a/app/controllers/projects/pipelines_controller.rb b/app/controllers/projects/pipelines_controller.rb
index e44c00e501e..f8fa22ac859 100644
--- a/app/controllers/projects/pipelines_controller.rb
+++ b/app/controllers/projects/pipelines_controller.rb
@@ -16,7 +16,8 @@ class Projects::PipelinesController < Projects::ApplicationController
push_frontend_feature_flag(:pipelines_security_report_summary, project)
push_frontend_feature_flag(:new_pipeline_form, project, default_enabled: true)
push_frontend_feature_flag(:graphql_pipeline_header, project, type: :development, default_enabled: false)
- push_frontend_feature_flag(:graphql_pipeline_details, project, type: :development, default_enabled: false)
+ push_frontend_feature_flag(:graphql_pipeline_details, project, type: :development, default_enabled: :yaml)
+ push_frontend_feature_flag(:graphql_pipeline_details_users, current_user, type: :development, default_enabled: :yaml)
push_frontend_feature_flag(:new_pipeline_form_prefilled_vars, project, type: :development, default_enabled: true)
end
before_action :ensure_pipeline, only: [:show]
diff --git a/app/helpers/commits_helper.rb b/app/helpers/commits_helper.rb
index e6e2b5b128b..2eb959856a5 100644
--- a/app/helpers/commits_helper.rb
+++ b/app/helpers/commits_helper.rb
@@ -110,20 +110,16 @@ module CommitsHelper
end
end
- def revert_commit_link(commit, continue_to_path, btn_class: nil, pajamas: false)
+ def revert_commit_link
return unless current_user
- action = 'revert'
-
- if pajamas && can_collaborate_with_project?(@project)
- tag(:div, data: { display_text: action.capitalize }, class: "js-revert-commit-trigger")
- else
- commit_action_link(action, commit, continue_to_path, btn_class: btn_class, has_tooltip: false)
- end
+ tag(:div, data: { display_text: 'Revert' }, class: "js-revert-commit-trigger")
end
- def cherry_pick_commit_link(commit, continue_to_path, btn_class: nil, has_tooltip: true)
- commit_action_link('cherry-pick', commit, continue_to_path, btn_class: btn_class, has_tooltip: has_tooltip)
+ def cherry_pick_commit_link
+ return unless current_user
+
+ tag(:div, data: { display_text: 'Cherry-pick' }, class: "js-cherry-pick-commit-trigger")
end
def commit_signature_badge_classes(additional_classes)
@@ -143,7 +139,7 @@ module CommitsHelper
def commit_person_link(commit, options = {})
user = commit.public_send(options[:source]) # rubocop:disable GitlabSecurity/PublicSend
- source_name = clean(commit.public_send(:"#{options[:source]}_name")) # rubocop:disable GitlabSecurity/PublicSend
+ source_name = clean(commit.public_send(:"#{options[:source]}_name")) # rubocop:disable GitlabSecurity/PublicSend
source_email = clean(commit.public_send(:"#{options[:source]}_email")) # rubocop:disable GitlabSecurity/PublicSend
person_name = user.try(:name) || source_name
@@ -166,28 +162,6 @@ module CommitsHelper
end
end
- def commit_action_link(action, commit, continue_to_path, btn_class: nil, has_tooltip: true)
- return unless current_user
-
- tooltip = "#{action.capitalize} this #{commit.change_type_title(current_user)} in a new merge request" if has_tooltip
- btn_class = "btn btn-#{btn_class}" unless btn_class.nil?
-
- if can_collaborate_with_project?(@project)
- link_to action.capitalize, "#modal-#{action}-commit", 'data-toggle' => 'modal', 'data-container' => 'body', title: (tooltip if has_tooltip), class: "#{btn_class} #{'has-tooltip' if has_tooltip}"
- elsif can?(current_user, :fork_project, @project)
- continue_params = {
- to: continue_to_path,
- notice: "#{edit_in_new_fork_notice} Try to #{action} this commit again.",
- notice_now: edit_in_new_fork_notice_now
- }
- fork_path = project_forks_path(@project,
- namespace_key: current_user.namespace.id,
- continue: continue_params)
-
- link_to action.capitalize, fork_path, class: btn_class, method: :post, 'data-toggle' => 'tooltip', 'data-container' => 'body', title: (tooltip if has_tooltip)
- end
- end
-
def view_file_button(commit_sha, diff_new_path, project, replaced: false)
path = project_blob_path(project, tree_join(commit_sha, diff_new_path))
title = replaced ? _('View replaced file @ ') : _('View file @ ')
diff --git a/app/models/pages/lookup_path.rb b/app/models/pages/lookup_path.rb
index 84928468ad1..b7e27f460a8 100644
--- a/app/models/pages/lookup_path.rb
+++ b/app/models/pages/lookup_path.rb
@@ -4,6 +4,8 @@ module Pages
class LookupPath
include Gitlab::Utils::StrongMemoize
+ LegacyStorageDisabledError = Class.new(::StandardError)
+
def initialize(project, trim_prefix: nil, domain: nil)
@project = project
@domain = domain
@@ -24,7 +26,7 @@ module Pages
end
def source
- zip_source || file_source
+ zip_source || legacy_source
end
def prefix
@@ -64,11 +66,17 @@ module Pages
}
end
- def file_source
+ def legacy_source
+ raise LegacyStorageDisabledError unless Feature.enabled?(:pages_serve_from_legacy_storage, default_enabled: true)
+
{
type: 'file',
path: File.join(project.full_path, 'public/')
}
+ rescue LegacyStorageDisabledError => e
+ Gitlab::ErrorTracking.track_exception(e)
+
+ nil
end
end
end
diff --git a/app/models/pages/virtual_domain.rb b/app/models/pages/virtual_domain.rb
index 7e42b8e6ae2..90cb8253b52 100644
--- a/app/models/pages/virtual_domain.rb
+++ b/app/models/pages/virtual_domain.rb
@@ -17,9 +17,16 @@ module Pages
end
def lookup_paths
- projects.map do |project|
+ paths = projects.map do |project|
project.pages_lookup_path(trim_prefix: trim_prefix, domain: domain)
- end.sort_by(&:prefix).reverse
+ end
+
+ # TODO: remove in https://gitlab.com/gitlab-org/gitlab/-/issues/297524
+ # source can only be nil if pages_serve_from_legacy_storage FF is disabled
+ # we can remove this filtering once we remove legacy storage
+ paths = paths.select(&:source)
+
+ paths.sort_by(&:prefix).reverse
end
private
diff --git a/app/views/projects/commit/_change.html.haml b/app/views/projects/commit/_change.html.haml
index 69b20fbc6d0..3c5f291b157 100644
--- a/app/views/projects/commit/_change.html.haml
+++ b/app/views/projects/commit/_change.html.haml
@@ -22,4 +22,14 @@
- label = s_('ChangeTypeAction|Cherry-pick')
- branch_label = s_('ChangeTypeActionLabel|Pick into branch')
- title = commit.merged_merge_request(current_user) ? _('Cherry-pick this merge request') : _('Cherry-pick this commit')
- = render "projects/commit/commit_modal", title: title, type: type, commit: commit, branch_label: branch_label, description: description, label: label
+
+ - if defined?(pajamas)
+ .js-cherry-pick-commit-modal{ data: { title: title,
+ endpoint: cherry_pick_namespace_project_commit_path(commit, namespace_id: @project.namespace.full_path, project_id: @project),
+ branch: @project.default_branch,
+ push_code: can?(current_user, :push_code, @project).to_s,
+ branch_collaboration: @project.branch_allows_collaboration?(current_user, selected_branch).to_s,
+ existing_branch: ERB::Util.html_escape(selected_branch),
+ branches_endpoint: project_branches_path(@project) } }
+ - else
+ = render "projects/commit/commit_modal", title: title, type: type, commit: commit, branch_label: branch_label, description: description, label: label
diff --git a/app/views/projects/commit/_commit_box.html.haml b/app/views/projects/commit/_commit_box.html.haml
index e8d524daced..a5cfbaa7bad 100644
--- a/app/views/projects/commit/_commit_box.html.haml
+++ b/app/views/projects/commit/_commit_box.html.haml
@@ -37,10 +37,10 @@
#{ _('Browse Files') }
- if can_collaborate && !@commit.has_been_reverted?(current_user)
%li.clearfix
- = revert_commit_link(@commit, project_commit_path(@project, @commit.id), pajamas: true)
+ = revert_commit_link
- if can_collaborate
%li.clearfix
- = cherry_pick_commit_link(@commit, project_commit_path(@project, @commit.id), has_tooltip: false)
+ = cherry_pick_commit_link
- if can?(current_user, :push_code, @project)
%li.clearfix
= link_to s_('CreateTag|Tag'), new_project_tag_path(@project, ref: @commit)
diff --git a/app/views/projects/commit/pipelines.html.haml b/app/views/projects/commit/pipelines.html.haml
index 0dbd6e53212..b4c59c73e3b 100644
--- a/app/views/projects/commit/pipelines.html.haml
+++ b/app/views/projects/commit/pipelines.html.haml
@@ -4,3 +4,7 @@
= render 'commit_box'
= render 'ci_menu'
= render 'projects/commit/pipelines_list', endpoint: pipelines_project_commit_path(@project, @commit.id)
+
+- if can_collaborate_with_project?(@project)
+ = render "projects/commit/change", type: 'revert', commit: @commit, pajamas: true
+ = render "projects/commit/change", type: 'cherry-pick', commit: @commit, pajamas: true
diff --git a/app/views/projects/commit/show.html.haml b/app/views/projects/commit/show.html.haml
index e7b2e757ce4..5e3bc270d5f 100644
--- a/app/views/projects/commit/show.html.haml
+++ b/app/views/projects/commit/show.html.haml
@@ -18,4 +18,4 @@
= render "shared/notes/notes_with_form", :autocomplete => true
- if can_collaborate_with_project?(@project)
= render "projects/commit/change", type: 'revert', commit: @commit, pajamas: true
- = render "projects/commit/change", type: 'cherry-pick', commit: @commit, title: @commit.title
+ = render "projects/commit/change", type: 'cherry-pick', commit: @commit, pajamas: true
diff --git a/app/views/projects/pipelines/show.html.haml b/app/views/projects/pipelines/show.html.haml
index b3ad210aa47..b431ef202b3 100644
--- a/app/views/projects/pipelines/show.html.haml
+++ b/app/views/projects/pipelines/show.html.haml
@@ -6,7 +6,7 @@
- add_page_specific_style 'page_bundles/reports'
- add_page_specific_style 'page_bundles/ci_status'
-- if Feature.enabled?(:graphql_pipeline_details, @project)
+- if Feature.enabled?(:graphql_pipeline_details, @project, default_enabled: :yaml) || Feature.enabled?(:graphql_pipeline_details_users, @current_user, default_enabled: :yaml)
- add_page_startup_graphql_call('pipelines/get_pipeline_details', { projectPath: @project.full_path, iid: @pipeline.iid })
.js-pipeline-container{ data: { controller_action: "#{controller.action_name}" } }
diff --git a/app/workers/container_expiration_policies/cleanup_container_repository_worker.rb b/app/workers/container_expiration_policies/cleanup_container_repository_worker.rb
index 7c86b194574..b4afe53d4bc 100644
--- a/app/workers/container_expiration_policies/cleanup_container_repository_worker.rb
+++ b/app/workers/container_expiration_policies/cleanup_container_repository_worker.rb
@@ -25,6 +25,7 @@ module ContainerExpirationPolicies
return unless container_repository
log_extra_metadata_on_done(:container_repository_id, container_repository.id)
+ log_extra_metadata_on_done(:project_id, project.id)
unless allowed_to_run?(container_repository)
container_repository.cleanup_unscheduled!
@@ -78,7 +79,7 @@ module ContainerExpirationPolicies
end
def project
- container_repository&.project
+ container_repository.project
end
def container_repository
diff --git a/changelogs/unreleased/229679-migrate-data-toggle-modal-to-glmodal-for-revert-cherry-picking-com.yml b/changelogs/unreleased/229679-migrate-data-toggle-modal-to-glmodal-for-revert-cherry-picking-com.yml
new file mode 100644
index 00000000000..6648c60600f
--- /dev/null
+++ b/changelogs/unreleased/229679-migrate-data-toggle-modal-to-glmodal-for-revert-cherry-picking-com.yml
@@ -0,0 +1,5 @@
+---
+title: "[Commit Page] Migrate to GlModal for cherry-pick commit"
+merge_request: 51650
+author:
+type: other
diff --git a/changelogs/unreleased/262863-delete-oncall-rotation-graphql.yml b/changelogs/unreleased/262863-delete-oncall-rotation-graphql.yml
new file mode 100644
index 00000000000..dabfe3a861c
--- /dev/null
+++ b/changelogs/unreleased/262863-delete-oncall-rotation-graphql.yml
@@ -0,0 +1,5 @@
+---
+title: Add On-call Rotations destroy mutation to GraphQL
+merge_request: 51860
+author:
+type: added
diff --git a/changelogs/unreleased/283912-remove-redundant-text.yml b/changelogs/unreleased/283912-remove-redundant-text.yml
new file mode 100644
index 00000000000..6b02d648185
--- /dev/null
+++ b/changelogs/unreleased/283912-remove-redundant-text.yml
@@ -0,0 +1,5 @@
+---
+title: Remove redundant text in Terraform Widget
+merge_request: 52013
+author:
+type: changed
diff --git a/changelogs/unreleased/compliance_pipeline_configuration.yml b/changelogs/unreleased/compliance_pipeline_configuration.yml
new file mode 100644
index 00000000000..cd2a9649f7b
--- /dev/null
+++ b/changelogs/unreleased/compliance_pipeline_configuration.yml
@@ -0,0 +1,5 @@
+---
+title: Database migration for compliance pipeline configuration location
+merge_request: 51663
+author:
+type: added
diff --git a/changelogs/unreleased/ph-mergeRequestLockedStatus.yml b/changelogs/unreleased/ph-mergeRequestLockedStatus.yml
new file mode 100644
index 00000000000..069e2b6ae5c
--- /dev/null
+++ b/changelogs/unreleased/ph-mergeRequestLockedStatus.yml
@@ -0,0 +1,5 @@
+---
+title: Fixed merge requests locked status not showing
+merge_request: 52078
+author:
+type: fixed
diff --git a/changelogs/unreleased/variables-in-include-enable.yml b/changelogs/unreleased/variables-in-include-enable.yml
new file mode 100644
index 00000000000..95a6c31ed1b
--- /dev/null
+++ b/changelogs/unreleased/variables-in-include-enable.yml
@@ -0,0 +1,5 @@
+---
+title: Support Project variables in `include` section of `gitlab-ci.yml`
+merge_request: 52108
+author:
+type: added
diff --git a/config/feature_flags/development/core_security_mr_widget_downloads.yml b/config/feature_flags/development/core_security_mr_widget_downloads.yml
deleted file mode 100644
index d89fbc302b3..00000000000
--- a/config/feature_flags/development/core_security_mr_widget_downloads.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: core_security_mr_widget_downloads
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/48769
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/273418
-milestone: '13.7'
-type: development
-group: group::static analysis
-default_enabled: true
diff --git a/config/feature_flags/development/drop_license_management_artifact.yml b/config/feature_flags/development/drop_license_management_artifact.yml
index 1be40187b43..23c2290c07d 100644
--- a/config/feature_flags/development/drop_license_management_artifact.yml
+++ b/config/feature_flags/development/drop_license_management_artifact.yml
@@ -1,8 +1,8 @@
---
name: drop_license_management_artifact
-introduced_by_url:
-rollout_issue_url:
-milestone:
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/31247
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/299114
+milestone: 13.0
type: development
group: group::composition analysis
default_enabled: true
diff --git a/config/feature_flags/development/graphql_pipeline_details_users.yml b/config/feature_flags/development/graphql_pipeline_details_users.yml
new file mode 100644
index 00000000000..a52cf23c878
--- /dev/null
+++ b/config/feature_flags/development/graphql_pipeline_details_users.yml
@@ -0,0 +1,8 @@
+---
+name: graphql_pipeline_details_users
+introduced_by_url:
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/299112
+milestone: '13.9'
+type: development
+group: group::pipeline authoring
+default_enabled: false
diff --git a/config/feature_flags/development/pages_serve_from_legacy_storage.yml b/config/feature_flags/development/pages_serve_from_legacy_storage.yml
new file mode 100644
index 00000000000..37d83106737
--- /dev/null
+++ b/config/feature_flags/development/pages_serve_from_legacy_storage.yml
@@ -0,0 +1,8 @@
+---
+name: pages_serve_from_legacy_storage
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/issues/297228
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/297524
+milestone: '13.8'
+type: development
+group: group::release
+default_enabled: true
diff --git a/config/feature_flags/development/variables_in_include_section_ci.yml b/config/feature_flags/development/variables_in_include_section_ci.yml
deleted file mode 100644
index f6fc810e6f2..00000000000
--- a/config/feature_flags/development/variables_in_include_section_ci.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: variables_in_include_section_ci
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/50188/
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/294294
-milestone: '13.8'
-type: development
-group: group::compliance
-default_enabled: false
diff --git a/data/whats_new/202101140001_13_08.yml b/data/whats_new/202101140001_13_08.yml
new file mode 100644
index 00000000000..826259fe7be
--- /dev/null
+++ b/data/whats_new/202101140001_13_08.yml
@@ -0,0 +1,83 @@
+---
+- title: Pipeline editor
+ body: |
+ GitLab CI/CD provides you with flexible options to support a variety of advanced pipeline use cases. Pipeline syntax can be verbose and sometimes complicated, especially for those who are new to GitLab CI/CD. In this release, we are proud to introduce our first iteration of the Pipeline Editor.
+
+ The editor makes the CI configuration authoring experience much easier for both novice and advanced users alike. The pipeline editor is a single solution that groups all the existing CI authoring features (and future ones) in a single location. The pipeline editor is the best place to go when configuring your pipeline.
+ stage: Verify
+ self-managed: true
+ gitlab-com: true
+ packages: [Core, Starter, Premium, Ultimate]
+ url: https://docs.gitlab.com/ee/ci/pipeline_editor/index.html
+ image_url: https://img.youtube.com/vi/MQpSyvMpsHA/hqdefault.jpg
+ published_at: 2021-01-22
+ release: 13.8
+- title: Visualization of pipeline configuration
+ body: |
+ A complex CI configuration can be difficult to understand as a developer, especially when trying to predict how your pipeline might behave (or misbehave). Without a visual aid, it is challenging to form a mental image of the relationships between all of the jobs and determine how they are interconnected. In our first iteration of a pipeline visualization, you can now see a graphic representation of your `.gitlab-ci.yml` configuration to better understand and predict how your pipelines will perform.
+ stage: verify
+ self-managed: true
+ gitlab-com: true
+ packages: [Core, Starter, Premium, Ultimate]
+ url: https://docs.gitlab.com/ee/ci/yaml/visualization.html
+ image_url: https://about.gitlab.com/images/13_8/pipeline_visual_builder.png
+ published_at: 2021-01-22
+ release: 13.8
+- title: Deployment frequency charts
+ packages: [Ultimate]
+ self-managed: true
+ gitlab-com: true
+ image_url: https://about.gitlab.com/images/13_8/deployment_graph.png
+ url: https://docs.gitlab.com/ee/user/analytics/ci_cd_analytics.html#deployment-frequency-charts
+ stage: Release
+ body: |
+ Knowing and monitoring deployment frequency is a starting point for organizations adopting DevOps. We are proud to introduce the deployment frequency charts at the project level so that you and your development teams can monitor the efficiency of deployments over time, find bottlenecks, and make improvements when necessary. This is the first of the DORA metrics that we are making available within GitLab out of the box.
+ published_at: 2021-01-22
+ release: 13.8
+- title: Scope a board to the current iteration
+ body: |
+ Many teams use boards to manage issues during an iteration. In 13.6, we added support for [filtering issues on a board to a specific Iteration](https://gitlab.com/gitlab-org/gitlab/-/issues/118742), but it is cumbersome to remember to apply that filter every time you go to your board. In this release, we've added the ability to scope your board to the currently active iteration.
+ stage: plan
+ self-managed: true
+ gitlab-com: true
+ packages: [Starter, Premium, Ultimate]
+ url: https://docs.gitlab.com/ee/user/project/issue_board.html#configurable-issue-boards
+ image_url: https://about.gitlab.com/images/13_8/scope_board_to_current_iteration.png
+ published_at: 2021-01-22
+ release: 13.8
+- title: Download artifacts directly from the merge request widget
+ body: |
+ We added the ability to download build artifacts directly from the MR widget. This is especially useful for mobile development. An example of this is where users want to test an Android package of a specific build created on a physical device or an emulator. You can now access these artifacts directly from the merge request widget without having to find the artifacts buried in the pipeline view.
+ stage: Release
+ self-managed: true
+ gitlab-com: true
+ packages: [Core, Starter, Premium, Ultimate]
+ url: https://docs.gitlab.com/ee/ci/pipelines/job_artifacts.html#downloading-artifacts
+ image_url: https://about.gitlab.com/images/13_8/artifact_mr.png
+ published_at: 2021-01-22
+ release: 13.8
+- title: Rebase quick action for merge requests
+ body: |
+ Rebase is a Git command used to reapply commits on top of a new commit. In practice, this means reapplying commits from a feature branch on top of the latest version of the target branch (e.g. `main`). In this way, it is possible to bring the feature branch up to date and resolve any conflicts without using a merge commit with the benefit of a simpler linear Git history.
+
+ GitLab 13.8 brings the ability to execute the rebase quick action in merge requests, allowing you to quickly invoke the rebase Git utility.
+ stage: create
+ self-managed: true
+ gitlab-com: true
+ packages: [Core, Starter, Premium, Ultimate]
+ url: https://docs.gitlab.com/ee/user/project/quick_actions.html#quick-actions-for-issues-merge-requests-and-epics
+ image_url: https://about.gitlab.com/images/13_8/rebase_comment.png
+ published_at: 2021-01-22
+ release: 13.8
+- title: GitLab Pages is now available for Kubernetes deployments of GitLab
+ body: |
+ GitLab Pages is a popular static-site hosting service built into GitLab, and we are excited to announce that it is now available for GitLab instances running on Kubernetes. Pages was one of the last remaining feature gaps compared to an Omnibus deployment.
+ stage: enablement
+ self-managed: true
+ gitlab-com: false
+ packages: [Core, Starter, Premium, Ultimate]
+ url: https://docs.gitlab.com/charts/charts/gitlab/gitlab-pages/index.html
+ image_url: https://about.gitlab.com/images/home/kubernetes.png
+ published_at: 2021-01-22
+ release: 13.8
+
diff --git a/db/migrate/20210113224909_add_pipeline_configuration_full_path_to_compliance_pipeline.rb b/db/migrate/20210113224909_add_pipeline_configuration_full_path_to_compliance_pipeline.rb
new file mode 100644
index 00000000000..408d0579031
--- /dev/null
+++ b/db/migrate/20210113224909_add_pipeline_configuration_full_path_to_compliance_pipeline.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+class AddPipelineConfigurationFullPathToCompliancePipeline < ActiveRecord::Migration[6.0]
+ DOWNTIME = false
+
+ # rubocop:disable Migration/AddLimitToTextColumns
+ # limit is added in 20210119162812_add_text_limit_to_compliance_pipeline_configuration_full_path.rb
+ def up
+ add_column :compliance_management_frameworks, :pipeline_configuration_full_path, :text
+ end
+ # rubocop:enable Migration/AddLimitToTextColumns
+
+ def down
+ remove_column :compliance_management_frameworks, :pipeline_configuration_full_path
+ end
+end
diff --git a/db/migrate/20210119162812_add_text_limit_to_compliance_pipeline_configuration_full_path.rb b/db/migrate/20210119162812_add_text_limit_to_compliance_pipeline_configuration_full_path.rb
new file mode 100644
index 00000000000..2958dc8d0ec
--- /dev/null
+++ b/db/migrate/20210119162812_add_text_limit_to_compliance_pipeline_configuration_full_path.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+class AddTextLimitToCompliancePipelineConfigurationFullPath < ActiveRecord::Migration[6.0]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ def up
+ add_text_limit :compliance_management_frameworks, :pipeline_configuration_full_path, 255
+ end
+
+ def down
+ remove_text_limit :compliance_management_frameworks, :pipeline_configuration_full_path
+ end
+end
diff --git a/db/schema_migrations/20210113224909 b/db/schema_migrations/20210113224909
new file mode 100644
index 00000000000..ab03538f520
--- /dev/null
+++ b/db/schema_migrations/20210113224909
@@ -0,0 +1 @@
+e6841491cd7d2cc015fd628f5c14270720d59cbb17b7efb160937963f074f5c2 \ No newline at end of file
diff --git a/db/schema_migrations/20210119162812 b/db/schema_migrations/20210119162812
new file mode 100644
index 00000000000..dfbf33a9fcf
--- /dev/null
+++ b/db/schema_migrations/20210119162812
@@ -0,0 +1 @@
+cd7643fc762d8b9236ef5ac7cc285ffbd29f1953178b9b6e129082efd7b9e07b \ No newline at end of file
diff --git a/db/structure.sql b/db/structure.sql
index ecee8f5a3e9..ddbd329d9f2 100644
--- a/db/structure.sql
+++ b/db/structure.sql
@@ -11457,9 +11457,11 @@ CREATE TABLE compliance_management_frameworks (
color text NOT NULL,
namespace_id integer NOT NULL,
regulated boolean DEFAULT true NOT NULL,
+ pipeline_configuration_full_path text,
CONSTRAINT check_08cd34b2c2 CHECK ((char_length(color) <= 10)),
CONSTRAINT check_1617e0b87e CHECK ((char_length(description) <= 255)),
- CONSTRAINT check_ab00bc2193 CHECK ((char_length(name) <= 255))
+ CONSTRAINT check_ab00bc2193 CHECK ((char_length(name) <= 255)),
+ CONSTRAINT check_e7a9972435 CHECK ((char_length(pipeline_configuration_full_path) <= 255))
);
CREATE SEQUENCE compliance_management_frameworks_id_seq
diff --git a/doc/administration/geo/replication/datatypes.md b/doc/administration/geo/replication/datatypes.md
index 0d2922c3347..be27179c600 100644
--- a/doc/administration/geo/replication/datatypes.md
+++ b/doc/administration/geo/replication/datatypes.md
@@ -173,7 +173,7 @@ successfully, you must replicate their data using some other means.
| Feature | Replicated (added in GitLab version) | Verified (added in GitLab version) | Object Storage replication (see [Geo with Object Storage](object_storage.md)) | Notes |
|:---------------------------------------------------------------------------------------------------------------|:-----------------------------------------------------------------------------------|:----------------------------------------------------------|:-------------------------------------------------------------------------------------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| [Application data in PostgreSQL](../../postgresql/index.md) | **Yes** (10.2) | **Yes** (10.2) | No | |
-| [Project repository](../../..//user/project/repository/) | **Yes** (10.2) | **Yes** (10.7) | No | |
+| [Project repository](../../../user/project/repository/) | **Yes** (10.2) | **Yes** (10.7) | No | |
| [Project wiki repository](../../../user/project/wiki/) | **Yes** (10.2) | **Yes** (10.7) | No |
| [Group wiki repository](../../../user/group/index.md#group-wikis) | [No](https://gitlab.com/gitlab-org/gitlab/-/issues/208147) | [No](https://gitlab.com/gitlab-org/gitlab/-/issues/208147) | No | |
| [Uploads](../../uploads.md) | **Yes** (10.2) | [No](https://gitlab.com/groups/gitlab-org/-/epics/1817) | No | Verified only on transfer or manually using [Integrity Check Rake Task](../../raketasks/check.md) on both nodes and comparing the output between them. |
diff --git a/doc/administration/geo/setup/database.md b/doc/administration/geo/setup/database.md
index 95f67b20ab5..7563ea91998 100644
--- a/doc/administration/geo/setup/database.md
+++ b/doc/administration/geo/setup/database.md
@@ -472,7 +472,7 @@ information, see [High Availability with Omnibus GitLab](../../postgresql/replic
## Patroni support
Support for Patroni is intended to replace `repmgr` as a
-[highly availabile PostgreSQL solution](../../postgresql/replication_and_failover.md)
+[highly available PostgreSQL solution](../../postgresql/replication_and_failover.md)
on the primary node, but it can also be used for PostgreSQL HA on a secondary
node.
diff --git a/doc/api/graphql/reference/gitlab_schema.graphql b/doc/api/graphql/reference/gitlab_schema.graphql
index c6f7c4d9a6d..08ee4a4d5db 100644
--- a/doc/api/graphql/reference/gitlab_schema.graphql
+++ b/doc/api/graphql/reference/gitlab_schema.graphql
@@ -3808,6 +3808,12 @@ type ComplianceFramework {
Name of the compliance framework
"""
name: String!
+
+ """
+ Full path of the compliance pipeline configuration stored in a project
+ repository, such as `.gitlab/compliance/soc2/.gitlab-ci.yml`.
+ """
+ pipelineConfigurationFullPath: String
}
"""
@@ -3860,6 +3866,12 @@ input ComplianceFrameworkInput {
New name for the compliance framework.
"""
name: String
+
+ """
+ Full path of the compliance pipeline configuration stored in a project
+ repository, such as `.gitlab/compliance/soc2/.gitlab-ci.yml`.
+ """
+ pipelineConfigurationFullPath: String
}
"""
@@ -15836,6 +15848,7 @@ type Mutation {
mergeRequestUpdate(input: MergeRequestUpdateInput!): MergeRequestUpdatePayload
namespaceIncreaseStorageTemporarily(input: NamespaceIncreaseStorageTemporarilyInput!): NamespaceIncreaseStorageTemporarilyPayload
oncallRotationCreate(input: OncallRotationCreateInput!): OncallRotationCreatePayload
+ oncallRotationDestroy(input: OncallRotationDestroyInput!): OncallRotationDestroyPayload
oncallScheduleCreate(input: OncallScheduleCreateInput!): OncallScheduleCreatePayload
oncallScheduleDestroy(input: OncallScheduleDestroyInput!): OncallScheduleDestroyPayload
oncallScheduleUpdate(input: OncallScheduleUpdateInput!): OncallScheduleUpdatePayload
@@ -16598,6 +16611,51 @@ input OncallRotationDateInputType {
}
"""
+Autogenerated input type of OncallRotationDestroy
+"""
+input OncallRotationDestroyInput {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The ID of the on-call rotation to remove.
+ """
+ id: IncidentManagementOncallRotationID!
+
+ """
+ The project to remove the on-call schedule from.
+ """
+ projectPath: ID!
+
+ """
+ The IID of the on-call schedule to the on-call rotation belongs to.
+ """
+ scheduleIid: String!
+}
+
+"""
+Autogenerated return type of OncallRotationDestroy
+"""
+type OncallRotationDestroyPayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ Errors encountered during execution of the mutation.
+ """
+ errors: [String!]!
+
+ """
+ The on-call rotation.
+ """
+ oncallRotation: IncidentManagementOncallRotation
+}
+
+"""
The rotation length of the on-call rotation
"""
input OncallRotationLengthInputType {
diff --git a/doc/api/graphql/reference/gitlab_schema.json b/doc/api/graphql/reference/gitlab_schema.json
index 0fae8b46475..6d1a1397c0e 100644
--- a/doc/api/graphql/reference/gitlab_schema.json
+++ b/doc/api/graphql/reference/gitlab_schema.json
@@ -10370,6 +10370,20 @@
},
"isDeprecated": false,
"deprecationReason": null
+ },
+ {
+ "name": "pipelineConfigurationFullPath",
+ "description": "Full path of the compliance pipeline configuration stored in a project repository, such as `.gitlab/compliance/soc2/.gitlab-ci.yml`.",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
}
],
"inputFields": null,
@@ -10526,6 +10540,16 @@
"ofType": null
},
"defaultValue": null
+ },
+ {
+ "name": "pipelineConfigurationFullPath",
+ "description": "Full path of the compliance pipeline configuration stored in a project repository, such as `.gitlab/compliance/soc2/.gitlab-ci.yml`.",
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "defaultValue": null
}
],
"interfaces": null,
@@ -45794,6 +45818,33 @@
"deprecationReason": null
},
{
+ "name": "oncallRotationDestroy",
+ "description": null,
+ "args": [
+ {
+ "name": "input",
+ "description": null,
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "INPUT_OBJECT",
+ "name": "OncallRotationDestroyInput",
+ "ofType": null
+ }
+ },
+ "defaultValue": null
+ }
+ ],
+ "type": {
+ "kind": "OBJECT",
+ "name": "OncallRotationDestroyPayload",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
"name": "oncallScheduleCreate",
"description": null,
"args": [
@@ -49120,6 +49171,136 @@
},
{
"kind": "INPUT_OBJECT",
+ "name": "OncallRotationDestroyInput",
+ "description": "Autogenerated input type of OncallRotationDestroy",
+ "fields": null,
+ "inputFields": [
+ {
+ "name": "projectPath",
+ "description": "The project to remove the on-call schedule from.",
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "ID",
+ "ofType": null
+ }
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "scheduleIid",
+ "description": "The IID of the on-call schedule to the on-call rotation belongs to.",
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ }
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "id",
+ "description": "The ID of the on-call rotation to remove.",
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "IncidentManagementOncallRotationID",
+ "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": "OBJECT",
+ "name": "OncallRotationDestroyPayload",
+ "description": "Autogenerated return type of OncallRotationDestroy",
+ "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": "oncallRotation",
+ "description": "The on-call rotation.",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "OBJECT",
+ "name": "IncidentManagementOncallRotation",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ }
+ ],
+ "inputFields": null,
+ "interfaces": [
+
+ ],
+ "enumValues": null,
+ "possibleTypes": null
+ },
+ {
+ "kind": "INPUT_OBJECT",
"name": "OncallRotationLengthInputType",
"description": "The rotation length of the on-call rotation",
"fields": null,
diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md
index 2e8044bb5bb..b2f1f00c4cb 100644
--- a/doc/api/graphql/reference/index.md
+++ b/doc/api/graphql/reference/index.md
@@ -591,6 +591,7 @@ Represents a ComplianceFramework associated with a Project.
| `description` | String! | Description of the compliance framework |
| `id` | ID! | Compliance framework ID |
| `name` | String! | Name of the compliance framework |
+| `pipelineConfigurationFullPath` | String | Full path of the compliance pipeline configuration stored in a project repository, such as `.gitlab/compliance/soc2/.gitlab-ci.yml`. |
### ConfigureSastPayload
@@ -2494,6 +2495,16 @@ Autogenerated return type of OncallRotationCreate.
| `errors` | String! => Array | Errors encountered during execution of the mutation. |
| `oncallRotation` | IncidentManagementOncallRotation | The on-call rotation. |
+### OncallRotationDestroyPayload
+
+Autogenerated return type of OncallRotationDestroy.
+
+| Field | Type | Description |
+| ----- | ---- | ----------- |
+| `clientMutationId` | String | A unique identifier for the client performing the mutation. |
+| `errors` | String! => Array | Errors encountered during execution of the mutation. |
+| `oncallRotation` | IncidentManagementOncallRotation | The on-call rotation. |
+
### OncallScheduleCreatePayload
Autogenerated return type of OncallScheduleCreate.
diff --git a/doc/api/merge_requests.md b/doc/api/merge_requests.md
index b6ae62dc47a..e16c08e4d97 100644
--- a/doc/api/merge_requests.md
+++ b/doc/api/merge_requests.md
@@ -1406,7 +1406,7 @@ Parameters:
| `merge_request_iid` | integer | yes | The internal ID of the merge request. |
| `merge_commit_message` | string | no | Custom merge commit message. |
| `squash_commit_message` | string | no | Custom squash commit message. |
-| `squash` | boolean | no | If `true` the commits the commits are squashed into a single commit on merge. |
+| `squash` | boolean | no | If `true` the commits are squashed into a single commit on merge. |
| `should_remove_source_branch` | boolean | no | If `true` removes the source branch. |
| `merge_when_pipeline_succeeds` | boolean | no | If `true` the MR is merged when the pipeline succeeds. |
| `sha` | string | no | If present, then this SHA must match the HEAD of the source branch, otherwise the merge fails. |
diff --git a/doc/api/protected_branches.md b/doc/api/protected_branches.md
index 1dd15231dd8..c0d923ba268 100644
--- a/doc/api/protected_branches.md
+++ b/doc/api/protected_branches.md
@@ -375,7 +375,7 @@ curl --request DELETE --header "PRIVATE-TOKEN: <your_access_token>" "https://git
## Require code owner approvals for a single branch
-Update the "code owner approval required" option for the given protected branch protected branch.
+Update the "code owner approval required" option for the given protected branch.
```plaintext
PATCH /projects/:id/protected_branches/:name
diff --git a/doc/api/runners.md b/doc/api/runners.md
index 131418de725..1782e236c36 100644
--- a/doc/api/runners.md
+++ b/doc/api/runners.md
@@ -575,7 +575,7 @@ Example response:
```json
{
- "id": "12345",
+ "id": 12345,
"token": "6337ff461c94fd3fa32ba3b1ff4125"
}
```
diff --git a/doc/ci/pipeline_editor/index.md b/doc/ci/pipeline_editor/index.md
index 61b8e289509..dd3a10bcd19 100644
--- a/doc/ci/pipeline_editor/index.md
+++ b/doc/ci/pipeline_editor/index.md
@@ -10,7 +10,7 @@ type: reference
> - [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/4540) in GitLab 13.8.
> - It's [deployed behind a feature flag](../../user/feature_flags.md), enabled by default.
> - It's enabled on GitLab.com.
-> - It's not recommended for production use.
+> - It's recommended for production use.
> - To use it in GitLab self-managed instances, ask a GitLab administrator to [enable it](#enable-or-disable-pipeline-editor). **(CORE ONLY)**
WARNING:
@@ -62,7 +62,7 @@ reflected in the CI lint. It displays the same results as the existing [CI Lint
> - It was [deployed behind a feature flag](../../user/feature_flags.md), disabled by default.
> - [Became enabled by default](https://gitlab.com/gitlab-org/gitlab/-/issues/290117) in GitLab 13.8.
> - It's enabled on GitLab.com.
-> - It's not recommended for production use.
+> - It's recommended for production use.
> - To use it in GitLab self-managed instances, ask a GitLab administrator to [enable it](#enable-or-disable-cicd-configuration-visualization). **(CORE ONLY)**
WARNING:
@@ -115,7 +115,7 @@ checkbox appears. Select it to start a new merge request after you commit the ch
## Enable or disable pipeline editor **(CORE ONLY)**
-The pipeline editor is under development and not ready for production use. It is
+The pipeline editor is under development but ready for production use. It is
deployed behind a feature flag that is **enabled by default**.
[GitLab administrators with access to the GitLab Rails console](../../administration/feature_flags.md)
can disable it.
diff --git a/doc/ci/pipelines/pipeline_efficiency.md b/doc/ci/pipelines/pipeline_efficiency.md
index de78b8558c4..a2df228e3a2 100644
--- a/doc/ci/pipelines/pipeline_efficiency.md
+++ b/doc/ci/pipelines/pipeline_efficiency.md
@@ -161,7 +161,7 @@ Try to find which jobs don't need to run in all situations, and use pipeline con
to stop them from running:
- Use the [`interruptible`](../yaml/README.md#interruptible) keyword to stop old pipelines
- when they are superceded by a newer pipeline.
+ when they are superseded by a newer pipeline.
- Use [`rules`](../yaml/README.md#rules) to skip tests that aren't needed. For example,
skip backend tests when only the frontend code is changed.
- Run non-essential [scheduled pipelines](schedules.md) less frequently.
diff --git a/doc/ci/yaml/README.md b/doc/ci/yaml/README.md
index 56594103362..c49292c7210 100644
--- a/doc/ci/yaml/README.md
+++ b/doc/ci/yaml/README.md
@@ -377,16 +377,10 @@ NOTE:
Use merging to customize and override included CI/CD configurations with local
definitions. Local definitions in `.gitlab-ci.yml` override included definitions.
-#### Variables with `include`
+#### Variables with `include` **(CORE ONLY)**
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/284883) in GitLab 13.8.
-> - It's [deployed behind a feature flag](../../user/feature_flags.md), disabled by default.
-> - It's disabled on GitLab.com.
-> - It's not recommended for production use.
-> - To use it in GitLab self-managed instances, ask a GitLab administrator to [enable it](#enable-or-disable-includepredefined-project-variables). **(CORE ONLY)**
-
-WARNING:
-This feature might not be available to you. Check the **version history** note above for details.
+> - [Feature flag removed](https://gitlab.com/gitlab-org/gitlab/-/issues/294294) in GitLab 13.9.
You can [use some predefined variables in `include` sections](../variables/where_variables_can_be_used.md#gitlab-ciyml-file)
in your `.gitlab-ci.yml`:
@@ -400,25 +394,6 @@ include:
For an example of how you can include these predefined variables, and their impact on CI jobs,
see the following [CI variable demo](https://youtu.be/4XR8gw3Pkos).
-##### Enable or disable include:predefined-project-variables **(CORE ONLY)**
-
-Use of predefined project variables in `include` section of `.gitlab-ci.yml` is under development and not ready for production use. It is
-deployed behind a feature flag that is **disabled by default**.
-[GitLab administrators with access to the GitLab Rails console](../../administration/feature_flags.md)
-can enable it.
-
-To enable it:
-
-```ruby
-Feature.enable(:variables_in_include_section_ci)
-```
-
-To disable it:
-
-```ruby
-Feature.disable(:variables_in_include_section_ci)
-```
-
#### `include:local`
`include:local` includes a file from the same repository as `.gitlab-ci.yml`.
@@ -3097,6 +3072,32 @@ job:
- path/*xyz/*
```
+#### `artifacts:public`
+
+> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/49775) in GitLab 13.8
+> - It's [deployed behind a feature flag](../../user/feature_flags.md), disabled by default.
+> - It's enabled on GitLab.com.
+> - It's recommended for production use.
+
+`artifacts:public` is used to determine whether the job artifacts should be
+publicly available.
+
+The default for `artifacts:public` is `true` which means that the artifacts in
+public pipelines are available for download by anonymous and guest users:
+
+```yaml
+artifacts:
+ public: true
+```
+
+To deny read access for anonymous and guest users to artifacts in public
+pipelines, set `artifacts:public` to `false`:
+
+```yaml
+artifacts:
+ public: false
+```
+
#### `artifacts:exclude`
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/15122) in GitLab 13.1
@@ -3453,7 +3454,7 @@ job1:
The coverage is shown in the UI if at least one line in the job output matches the regular expression.
If there is more than one matched line in the job output, the last line is used.
-For the matched line, the first occurence of `\d+(\.\d+)?` is the code coverage.
+For the matched line, the first occurrence of `\d+(\.\d+)?` is the code coverage.
Leading zeros are removed.
Coverage output from [child pipelines](../parent_child_pipelines.md) is not recorded
diff --git a/doc/development/code_review.md b/doc/development/code_review.md
index fe395dc2304..04b8c66888e 100644
--- a/doc/development/code_review.md
+++ b/doc/development/code_review.md
@@ -107,7 +107,7 @@ with [domain expertise](#domain-experts).
be **approved by a [frontend foundations member](https://about.gitlab.com/direction/create/ecosystem/frontend-ux-foundations/)**.
- If the license used by the new library hasn't been approved for use in
GitLab, the license must be **approved by a [legal department member](https://about.gitlab.com/handbook/legal/)**.
- More information about license compatiblity can be found in our
+ More information about license compatibility can be found in our
[GitLab Licensing and Compatibility documentation](licensing.md).
1. If your merge request includes adding a new UI/UX paradigm (*1*), it must be
**approved by a [UX lead](https://about.gitlab.com/company/team/)**.
diff --git a/doc/development/elasticsearch.md b/doc/development/elasticsearch.md
index 8bf8a5fccb8..51497b51e9a 100644
--- a/doc/development/elasticsearch.md
+++ b/doc/development/elasticsearch.md
@@ -337,7 +337,7 @@ cluster.routing.allocation.disk.watermark.low: 15gb
cluster.routing.allocation.disk.watermark.high: 10gb
```
-Restart Elasticsearch, and the `read_only_allow_delete` will clear on it's own.
+Restart Elasticsearch, and the `read_only_allow_delete` will clear on its own.
_from "Disk-based Shard Allocation | Elasticsearch Reference" [5.6](https://www.elastic.co/guide/en/elasticsearch/reference/5.6/disk-allocator.html#disk-allocator) and [6.x](https://www.elastic.co/guide/en/elasticsearch/reference/6.7/disk-allocator.html)_
@@ -351,7 +351,7 @@ simply reindex everything from scratch.
If your Elasticsearch index is incredibly large it may be too time consuming or
cause too much downtime to reindex from scratch. There aren't any built in
-mechanisms for automatically finding discrepencies and resyncing an
+mechanisms for automatically finding discrepancies and resyncing an
Elasticsearch index if it gets out of sync but one tool that may be useful is
looking at the logs for all the updates that occurred in a time range you
believe may have been missed. This information is very low level and only
diff --git a/doc/development/iterating_tables_in_batches.md b/doc/development/iterating_tables_in_batches.md
index 43d7f32ad7f..bae55bccf7c 100644
--- a/doc/development/iterating_tables_in_batches.md
+++ b/doc/development/iterating_tables_in_batches.md
@@ -46,7 +46,7 @@ all of the arguments that `in_batches` supports. You should always use
One should proceed with extra caution, and possibly avoid iterating over a column that can contain duplicate values.
When you iterate over an attribute that is not unique, even with the applied max batch size, there is no guarantee that the resulting batches will not surpass it.
-The following snippet demonstrates this situation, whe one attempt to select `Ci::Build` entries for users with `id` between `1` and `10,s000`, database returns `1 215 178`
+The following snippet demonstrates this situation, when one attempt to select `Ci::Build` entries for users with `id` between `1` and `10,s000`, database returns `1 215 178`
matching rows
```ruby
diff --git a/doc/development/profiling.md b/doc/development/profiling.md
index ce9c1191648..5714d25cedd 100644
--- a/doc/development/profiling.md
+++ b/doc/development/profiling.md
@@ -227,5 +227,5 @@ The following table lists these variables along with their default values.
GitLab may decide to change these settings in order to speed up application performance, lower memory requirements, or both.
You can see how each of these settings affect GC performance, memory use and application start-up time for an idle instance of
-GitLab by runnning the `scripts/perf/gc/collect_gc_stats.rb` script. It will output GC stats and general timing data to standard
+GitLab by running the `scripts/perf/gc/collect_gc_stats.rb` script. It will output GC stats and general timing data to standard
out as CSV.
diff --git a/doc/development/secure_coding_guidelines.md b/doc/development/secure_coding_guidelines.md
index bd98ea170e5..43b2a3e173b 100644
--- a/doc/development/secure_coding_guidelines.md
+++ b/doc/development/secure_coding_guidelines.md
@@ -180,7 +180,7 @@ For other regular expressions, here are a few guidelines:
- If there's a clean non-regex solution, such as `String#start_with?`, consider using it
- Ruby supports some advanced regex features like [atomic groups](https://www.regular-expressions.info/atomic.html)
-and [possessive quantifiers](https://www.regular-expressions.info/possessive.html) that eleminate backtracking
+and [possessive quantifiers](https://www.regular-expressions.info/possessive.html) that eliminate backtracking
- Avoid nested quantifiers if possible (for example `(a+)+`)
- Try to be as precise as possible in your regex and avoid the `.` if there's an alternative
- For example, Use `_[^_]+_` instead of `_.*_` to match `_text here_`
diff --git a/doc/development/testing_guide/frontend_testing.md b/doc/development/testing_guide/frontend_testing.md
index 94bc80abcdb..81b4689c521 100644
--- a/doc/development/testing_guide/frontend_testing.md
+++ b/doc/development/testing_guide/frontend_testing.md
@@ -31,7 +31,7 @@ Jest tests can be found in `/spec/frontend` and `/ee/spec/frontend` in EE.
## Karma test suite
While GitLab has switched over to [Jest](https://jestjs.io), Karma tests still exist in our
-application because some of our specs require a browser and can't be easiliy migrated to Jest.
+application because some of our specs require a browser and can't be easily migrated to Jest.
Those specs intend to eventually drop Karma in favor of either Jest or RSpec. You can track this migration
in the [related epic](https://gitlab.com/groups/gitlab-org/-/epics/4900).
diff --git a/doc/integration/saml.md b/doc/integration/saml.md
index af0a58eab59..8f94fcbf641 100644
--- a/doc/integration/saml.md
+++ b/doc/integration/saml.md
@@ -17,7 +17,7 @@ You should also reference the [OmniAuth documentation](omniauth.md) for general
|------|-------------|
| Identity Provider (IdP) | The service which manages your user identities such as ADFS, Okta, Onelogin, or Ping Identity. |
| Service Provider (SP) | SAML considers GitLab to be a service provider. |
-| Assertion | A piece of information about a user's identity, such as their name or role. Also know as claims or attributes. |
+| Assertion | A piece of information about a user's identity, such as their name or role. Also known as claims or attributes. |
| SSO | Single Sign-On. |
| Assertion consumer service URL | The callback on GitLab where users will be redirected after successfully authenticating with the identity provider. |
| Issuer | How GitLab identifies itself to the identity provider. Also known as a "Relying party trust identifier". |
diff --git a/doc/operations/product_analytics.md b/doc/operations/product_analytics.md
index e6e887b9738..b0113aeb8de 100644
--- a/doc/operations/product_analytics.md
+++ b/doc/operations/product_analytics.md
@@ -1,6 +1,6 @@
---
stage: Growth
-group: Product Analytics
+group: Product Intelligence
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
---
diff --git a/doc/user/admin_area/settings/sign_up_restrictions.md b/doc/user/admin_area/settings/sign_up_restrictions.md
index ddd3dbda9cc..91cc7f07fca 100644
--- a/doc/user/admin_area/settings/sign_up_restrictions.md
+++ b/doc/user/admin_area/settings/sign_up_restrictions.md
@@ -86,7 +86,7 @@ Feature.enable(:admin_new_user_signups_cap)
## Soft email confirmation
> - [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/47003) in GitLab 12.2.
-> - It's [deployed behind a feature flag](../../..//user/feature_flags.md), disabled by default.
+> - It's [deployed behind a feature flag](../../../user/feature_flags.md), disabled by default.
> - It's enabled on GitLab.com.
> - It's recommended for production use.
> - To use it in GitLab self-managed instances, ask a GitLab administrator to [enable it](#enable-or-disable-soft-email-confirmation).
diff --git a/doc/user/group/saml_sso/index.md b/doc/user/group/saml_sso/index.md
index d72ab197388..a56e9ad471b 100644
--- a/doc/user/group/saml_sso/index.md
+++ b/doc/user/group/saml_sso/index.md
@@ -110,7 +110,8 @@ When [configuring your identify provider](#configuring-your-identity-provider),
### Azure setup notes
<i class="fa fa-youtube-play youtube" aria-hidden="true"></i>
-For a demo of the Azure SAML setup including SCIM, see [SCIM Provisioning on Azure Using SAML SSO for Groups Demo](https://youtu.be/24-ZxmTeEBU). Please note that the video is outdated in regards to objectID mapping and the [SCIM documentation should be followed](scim_setup.md#azure-configuration-steps).
+For a demo of the Azure SAML setup including SCIM, see [SCIM Provisioning on Azure Using SAML SSO for Groups Demo](https://youtu.be/24-ZxmTeEBU). Please note that the video is outdated in regard to
+objectID mapping and the [SCIM documentation should be followed](scim_setup.md#azure-configuration-steps).
| GitLab Setting | Azure Field |
|--------------|----------------|
@@ -343,7 +344,7 @@ access.
|------|-------------|
| Identity Provider | The service which manages your user identities such as ADFS, Okta, Onelogin, or Ping Identity. |
| Service Provider | SAML considers GitLab to be a service provider. |
-| Assertion | A piece of information about a user's identity, such as their name or role. Also know as claims or attributes. |
+| Assertion | A piece of information about a user's identity, such as their name or role. Also known as claims or attributes. |
| SSO | Single Sign On. |
| Assertion consumer service URL | The callback on GitLab where users are redirected after successfully authenticating with the identity provider. |
| Issuer | How GitLab identifies itself to the identity provider. Also known as a "Relying party trust identifier". |
diff --git a/doc/user/project/issues/due_dates.md b/doc/user/project/issues/due_dates.md
index 34e9340067c..909a20f0e2f 100644
--- a/doc/user/project/issues/due_dates.md
+++ b/doc/user/project/issues/due_dates.md
@@ -41,7 +41,7 @@ You can see issues with their due dates in the [issues list](index.md#issues-lis
Overdue issues have their icon and date colored red.
To sort issues by their due dates, select **Due date** from the dropdown menu on the right.
Issues are then sorted from the earliest due date to the latest.
-To display isses with the latest due dates at the top, select **Sort direction** (**{sort-lowest}**).
+To display issues with the latest due dates at the top, select **Sort direction** (**{sort-lowest}**).
Due dates also appear in your [to-do list](../../todos.md).
diff --git a/lib/gitlab/ci/config/external/mapper.rb b/lib/gitlab/ci/config/external/mapper.rb
index 4d91cfd4c57..b85b7a9edeb 100644
--- a/lib/gitlab/ci/config/external/mapper.rb
+++ b/lib/gitlab/ci/config/external/mapper.rb
@@ -99,8 +99,6 @@ module Gitlab
end
def expand_variables(data)
- return data unless ::Feature.enabled?(:variables_in_include_section_ci)
-
if data.is_a?(String)
expand(data)
else
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 5590ca4d2fc..798b8dba2fe 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -5269,6 +5269,9 @@ msgstr ""
msgid "ChangeTypeAction|Cherry-pick"
msgstr ""
+msgid "ChangeTypeAction|Pick into branch"
+msgstr ""
+
msgid "ChangeTypeAction|Revert"
msgstr ""
@@ -25171,9 +25174,6 @@ msgstr ""
msgid "SecurityReports|Fuzzing artifacts"
msgstr ""
-msgid "SecurityReports|Go to the %{linkStart}pipelines tab%{linkEnd} to download the security reports"
-msgstr ""
-
msgid "SecurityReports|Hide dismissed"
msgstr ""
@@ -25237,9 +25237,6 @@ msgstr ""
msgid "SecurityReports|Security scans have run"
msgstr ""
-msgid "SecurityReports|Security scans have run. Go to the %{linkStart}pipelines tab%{linkEnd} to download the security reports"
-msgstr ""
-
msgid "SecurityReports|Select a project to add by using the project search field above."
msgstr ""
@@ -27752,10 +27749,10 @@ msgstr[1] ""
msgid "Terraform|%{user} updated %{timeAgo}"
msgstr ""
-msgid "Terraform|A Terraform report failed to generate."
+msgid "Terraform|A report failed to generate."
msgstr ""
-msgid "Terraform|A Terraform report was generated in your pipelines."
+msgid "Terraform|A report was generated in your pipelines."
msgstr ""
msgid "Terraform|Actions"
@@ -27815,10 +27812,10 @@ msgstr ""
msgid "Terraform|States"
msgstr ""
-msgid "Terraform|The Terraform report %{name} failed to generate."
+msgid "Terraform|The report %{name} failed to generate."
msgstr ""
-msgid "Terraform|The Terraform report %{name} was generated in your pipelines."
+msgid "Terraform|The report %{name} was generated in your pipelines."
msgstr ""
msgid "Terraform|To remove the State file and its versions, type %{name} to confirm:"
@@ -32552,6 +32549,9 @@ msgstr ""
msgid "You have insufficient permissions to create an on-call schedule for this project"
msgstr ""
+msgid "You have insufficient permissions to remove an on-call rotation from this project"
+msgstr ""
+
msgid "You have insufficient permissions to remove an on-call schedule from this project"
msgstr ""
diff --git a/package.json b/package.json
index e3bb1cf8ff8..505b97aa948 100644
--- a/package.json
+++ b/package.json
@@ -45,7 +45,7 @@
"@gitlab/favicon-overlay": "2.0.0",
"@gitlab/svgs": "1.179.0",
"@gitlab/tributejs": "1.0.0",
- "@gitlab/ui": "25.12.2",
+ "@gitlab/ui": "26.0.0",
"@gitlab/visual-review-tools": "1.6.1",
"@rails/actioncable": "^6.0.3-4",
"@rails/ujs": "^6.0.3-4",
diff --git a/spec/features/projects/commit/cherry_pick_spec.rb b/spec/features/projects/commit/cherry_pick_spec.rb
index 9fe3f4cd63e..489a90cc8fc 100644
--- a/spec/features/projects/commit/cherry_pick_spec.rb
+++ b/spec/features/projects/commit/cherry_pick_spec.rb
@@ -2,108 +2,126 @@
require 'spec_helper'
-RSpec.describe 'Cherry-pick Commits' do
- let(:user) { create(:user) }
- let(:group) { create(:group) }
- let(:project) { create(:project, :repository, namespace: group) }
- let(:master_pickable_commit) { project.commit('7d3b0f7cff5f37573aea97cebfd5692ea1689924') }
- let(:master_pickable_merge) { project.commit('e56497bb5f03a90a51293fc6d516788730953899') }
+RSpec.describe 'Cherry-pick Commits', :js do
+ let_it_be(:user) { create(:user) }
+ let_it_be(:sha) { '7d3b0f7cff5f37573aea97cebfd5692ea1689924' }
+ let!(:project) { create_default(:project, :repository, namespace: user.namespace) }
+ let(:master_pickable_commit) { project.commit(sha) }
before do
sign_in(user)
- project.add_maintainer(user)
- visit project_commit_path(project, master_pickable_commit.id)
end
- context "I cherry-pick a commit" do
- it do
- find("a[href='#modal-cherry-pick-commit']").click
- expect(page).not_to have_content('v1.0.0') # Only branches, not tags
- page.within('#modal-cherry-pick-commit') do
- uncheck 'create_merge_request'
- click_button 'Cherry-pick'
- end
- expect(page).to have_content('The commit has been successfully cherry-picked into master.')
- end
- end
+ context 'when clicking cherry-pick from the dropdown for a commit on pipelines tab' do
+ it 'launches the modal form' do
+ create(:ci_empty_pipeline, sha: sha)
+ visit project_commit_path(project, master_pickable_commit.id)
+ click_link 'Pipelines'
- context "I cherry-pick a merge commit" do
- it do
- find("a[href='#modal-cherry-pick-commit']").click
- page.within('#modal-cherry-pick-commit') do
- uncheck 'create_merge_request'
- click_button 'Cherry-pick'
+ open_modal
+
+ page.within(modal_selector) do
+ expect(page).to have_content('Cherry-pick this commit')
end
- expect(page).to have_content('The commit has been successfully cherry-picked into master.')
end
end
- context "I cherry-pick a commit that was previously cherry-picked" do
- it do
- find("a[href='#modal-cherry-pick-commit']").click
- page.within('#modal-cherry-pick-commit') do
- uncheck 'create_merge_request'
- click_button 'Cherry-pick'
- end
+ context 'when starting from the commit tab' do
+ before do
visit project_commit_path(project, master_pickable_commit.id)
- find("a[href='#modal-cherry-pick-commit']").click
- page.within('#modal-cherry-pick-commit') do
- uncheck 'create_merge_request'
- click_button 'Cherry-pick'
- end
- expect(page).to have_content('Sorry, we cannot cherry-pick this commit automatically.')
end
- end
- context "I cherry-pick a commit in a new merge request", :js do
- it do
- find('.header-action-buttons a.dropdown-toggle').click
- find("a[href='#modal-cherry-pick-commit']").click
- page.within('#modal-cherry-pick-commit') do
- click_button 'Cherry-pick'
+ context 'when cherry-picking a commit' do
+ specify do
+ cherry_pick_commit
+
+ expect(page).to have_content('The commit has been successfully cherry-picked into master.')
end
+ end
- wait_for_requests
+ context 'when cherry-picking a merge commit' do
+ specify do
+ cherry_pick_commit
- expect(page).to have_content("The commit has been successfully cherry-picked into cherry-pick-#{master_pickable_commit.short_id}. You can now submit a merge request to get this change into the original branch.")
- expect(page).to have_content("From cherry-pick-#{master_pickable_commit.short_id} into master")
+ expect(page).to have_content('The commit has been successfully cherry-picked into master.')
+ end
end
- end
- context "I cherry-pick a commit from a different branch", :js do
- it do
- find('.header-action-buttons a.dropdown-toggle').click
- find(:css, "a[href='#modal-cherry-pick-commit']").click
+ context 'when cherry-picking a commit that was previously cherry-picked' do
+ specify do
+ cherry_pick_commit
- page.within('#modal-cherry-pick-commit') do
- click_button 'master'
+ visit project_commit_path(project, master_pickable_commit.id)
+
+ cherry_pick_commit
+
+ expect(page).to have_content('Sorry, we cannot cherry-pick this commit automatically.')
end
+ end
- wait_for_requests
+ context 'when cherry-picking a commit in a new merge request' do
+ specify do
+ cherry_pick_commit(create_merge_request: true)
- page.within('#modal-cherry-pick-commit .dropdown-menu') do
- find('.dropdown-input input').set('feature')
- wait_for_requests
- click_link "feature"
+ expect(page).to have_content("The commit has been successfully cherry-picked into cherry-pick-#{master_pickable_commit.short_id}. You can now submit a merge request to get this change into the original branch.")
+ expect(page).to have_content("From cherry-pick-#{master_pickable_commit.short_id} into master")
end
+ end
- page.within('#modal-cherry-pick-commit') do
- uncheck 'create_merge_request'
- click_button 'Cherry-pick'
+ context 'when I cherry-picking a commit from a different branch' do
+ specify do
+ open_modal
+
+ page.within(modal_selector) do
+ click_button 'master'
+ end
+
+ page.within("#{modal_selector} .dropdown-menu") do
+ find('[data-testid="dropdown-search-box"]').set('feature')
+ wait_for_requests
+ click_button 'feature'
+ end
+
+ submit_cherry_pick
+
+ expect(page).to have_content('The commit has been successfully cherry-picked into feature.')
end
+ end
+
+ context 'when the project is archived' do
+ let(:project) { create(:project, :repository, :archived, namespace: user.namespace) }
- expect(page).to have_content('The commit has been successfully cherry-picked into feature.')
+ it 'does not show the cherry-pick link' do
+ open_dropdown
+
+ expect(page).not_to have_text("Cherry-pick")
+ end
end
end
- context 'when the project is archived' do
- let(:project) { create(:project, :repository, :archived, namespace: group) }
+ def cherry_pick_commit(create_merge_request: false)
+ open_modal
- it 'does not show the cherry-pick link' do
- find('.header-action-buttons a.dropdown-toggle').click
+ submit_cherry_pick(create_merge_request: create_merge_request)
+ end
+
+ def open_dropdown
+ find('.header-action-buttons .dropdown').click
+ end
- expect(page).not_to have_text("Cherry-pick")
- expect(page).not_to have_css("a[href='#modal-cherry-pick-commit']")
+ def open_modal
+ open_dropdown
+ find('[data-testid="cherry-pick-commit-link"]').click
+ end
+
+ def submit_cherry_pick(create_merge_request: false)
+ page.within(modal_selector) do
+ uncheck('create_merge_request') unless create_merge_request
+ click_button('Cherry-pick')
end
end
+
+ def modal_selector
+ '[data-testid="modal-commit"]'
+ end
end
diff --git a/spec/features/projects/commit/user_reverts_commit_spec.rb b/spec/features/projects/commit/user_reverts_commit_spec.rb
index f3c364dab97..72c639a027e 100644
--- a/spec/features/projects/commit/user_reverts_commit_spec.rb
+++ b/spec/features/projects/commit/user_reverts_commit_spec.rb
@@ -6,58 +6,89 @@ RSpec.describe 'User reverts a commit', :js do
include RepoHelpers
let_it_be(:user) { create(:user) }
- let(:project) { create(:project, :repository, namespace: user.namespace) }
+ let!(:project) { create_default(:project, :repository, namespace: user.namespace) }
before do
sign_in(user)
-
- visit(project_commit_path(project, sample_commit.id))
end
- def revert_commit(create_merge_request: false)
- find('.header-action-buttons .dropdown').click
- find('[data-testid="revert-commit-link"]').click
+ context 'when clicking revert from the dropdown for a commit on pipelines tab' do
+ it 'launches the modal and is able to submit the revert' do
+ sha = '7d3b0f7cff5f37573aea97cebfd5692ea1689924'
+ create(:ci_empty_pipeline, sha: sha)
+ visit project_commit_path(project, project.commit(sha).id)
+ click_link 'Pipelines'
- page.within('[data-testid="modal-commit"]') do
- uncheck('create_merge_request') unless create_merge_request
- click_button('Revert')
+ open_modal
+
+ page.within(modal_selector) do
+ expect(page).to have_content('Revert this commit')
+ end
end
end
- context 'without creating a new merge request' do
- it 'reverts a commit' do
- revert_commit
+ context 'when starting from the commit tab' do
+ before do
+ visit project_commit_path(project, sample_commit.id)
+ end
+
+ context 'without creating a new merge request' do
+ it 'reverts a commit' do
+ revert_commit
+
+ expect(page).to have_content('The commit has been successfully reverted.')
+ end
+
+ it 'does not revert a previously reverted commit' do
+ revert_commit
+ # Visit the comment again once it was reverted.
+ visit project_commit_path(project, sample_commit.id)
+
+ revert_commit
- expect(page).to have_content('The commit has been successfully reverted.')
+ expect(page).to have_content('Sorry, we cannot revert this commit automatically.')
+ end
end
- it 'does not revert a previously reverted commit' do
- revert_commit
- # Visit the comment again once it was reverted.
- visit project_commit_path(project, sample_commit.id)
+ context 'with creating a new merge request' do
+ it 'reverts a commit' do
+ revert_commit(create_merge_request: true)
+
+ expect(page).to have_content('The commit has been successfully reverted. You can now submit a merge request to get this change into the original branch.')
+ expect(page).to have_content("From revert-#{Commit.truncate_sha(sample_commit.id)} into master")
+ end
+ end
- revert_commit
+ context 'when the project is archived' do
+ let(:project) { create(:project, :repository, :archived, namespace: user.namespace) }
- expect(page).to have_content('Sorry, we cannot revert this commit automatically.')
+ it 'does not show the revert link' do
+ open_dropdown
+
+ expect(page).not_to have_link('Revert')
+ end
end
end
- context 'with creating a new merge request' do
- it 'reverts a commit' do
- revert_commit(create_merge_request: true)
+ def revert_commit(create_merge_request: false)
+ open_modal
- expect(page).to have_content('The commit has been successfully reverted. You can now submit a merge request to get this change into the original branch.')
- expect(page).to have_content("From revert-#{Commit.truncate_sha(sample_commit.id)} into master")
+ page.within(modal_selector) do
+ uncheck('create_merge_request') unless create_merge_request
+ click_button('Revert')
end
end
- context 'when the project is archived' do
- let(:project) { create(:project, :repository, :archived, namespace: user.namespace) }
+ def open_dropdown
+ find('.header-action-buttons .dropdown').click
+ end
- it 'does not show the revert link' do
- find('.header-action-buttons .dropdown').click
+ def open_modal
+ open_dropdown
+ find('[data-testid="revert-commit-link"]').click
+ end
- expect(page).not_to have_link('Revert')
- end
+ def modal_selector
+ '[data-testid="modal-commit"]'
end
end
diff --git a/spec/features/projects/pipelines/pipeline_spec.rb b/spec/features/projects/pipelines/pipeline_spec.rb
index ac3566fbbdd..ba75d8b5bb8 100644
--- a/spec/features/projects/pipelines/pipeline_spec.rb
+++ b/spec/features/projects/pipelines/pipeline_spec.rb
@@ -15,6 +15,7 @@ RSpec.describe 'Pipeline', :js do
sign_in(user)
project.add_role(user, role)
stub_feature_flags(graphql_pipeline_details: false)
+ stub_feature_flags(graphql_pipeline_details_users: false)
end
shared_context 'pipeline builds' do
diff --git a/spec/features/projects/pipelines/pipelines_spec.rb b/spec/features/projects/pipelines/pipelines_spec.rb
index 450524b8d70..5c4552510cb 100644
--- a/spec/features/projects/pipelines/pipelines_spec.rb
+++ b/spec/features/projects/pipelines/pipelines_spec.rb
@@ -13,6 +13,7 @@ RSpec.describe 'Pipelines', :js do
before do
sign_in(user)
stub_feature_flags(graphql_pipeline_details: false)
+ stub_feature_flags(graphql_pipeline_details_users: false)
project.add_developer(user)
project.update!(auto_devops_attributes: { enabled: false })
end
diff --git a/spec/frontend/merge_request/components/status_box_spec.js b/spec/frontend/merge_request/components/status_box_spec.js
index e6b6512476b..e7623aed4b3 100644
--- a/spec/frontend/merge_request/components/status_box_spec.js
+++ b/spec/frontend/merge_request/components/status_box_spec.js
@@ -18,6 +18,12 @@ const testCases = [
icon: 'issue-open-m',
},
{
+ name: 'Open',
+ state: 'locked',
+ class: 'status-box-open',
+ icon: 'issue-open-m',
+ },
+ {
name: 'Closed',
state: 'closed',
class: 'status-box-mr-closed',
diff --git a/spec/frontend/monitoring/components/charts/single_stat_spec.js b/spec/frontend/monitoring/components/charts/single_stat_spec.js
index 37712eb3012..144a4e0c968 100644
--- a/spec/frontend/monitoring/components/charts/single_stat_spec.js
+++ b/spec/frontend/monitoring/components/charts/single_stat_spec.js
@@ -27,8 +27,12 @@ describe('Single Stat Chart component', () => {
describe('computed', () => {
describe('statValue', () => {
- it('should interpolate the value and unit props', () => {
- expect(findChart().props('value')).toBe('1.00MB');
+ it('should display the correct value', () => {
+ expect(findChart().props('value')).toBe('1.00');
+ });
+
+ it('should display the correct value unit', () => {
+ expect(findChart().props('unit')).toBe('MB');
});
it('should change the value representation to a percentile one', () => {
@@ -36,7 +40,8 @@ describe('Single Stat Chart component', () => {
graphData: singleStatGraphData({ max_value: 120 }, { value: 91 }),
});
- expect(findChart().props('value')).toContain('75.83%');
+ expect(findChart().props('value')).toBe('75.83');
+ expect(findChart().props('unit')).toBe('%');
});
it('should display NaN for non numeric maxValue values', () => {
diff --git a/spec/frontend/vue_mr_widget/components/terraform/terraform_plan_spec.js b/spec/frontend/vue_mr_widget/components/terraform/terraform_plan_spec.js
index ea4eb44ebfe..f95a92c2cb1 100644
--- a/spec/frontend/vue_mr_widget/components/terraform/terraform_plan_spec.js
+++ b/spec/frontend/vue_mr_widget/components/terraform/terraform_plan_spec.js
@@ -33,7 +33,7 @@ describe('TerraformPlan', () => {
it('diplays the header text with a name', () => {
expect(wrapper.text()).toContain(
- `The Terraform report ${validPlanWithName.job_name} was generated in your pipelines.`,
+ `The report ${validPlanWithName.job_name} was generated in your pipelines.`,
);
});
@@ -55,7 +55,7 @@ describe('TerraformPlan', () => {
});
it('diplays the header text without a name', () => {
- expect(wrapper.text()).toContain('A Terraform report was generated in your pipelines.');
+ expect(wrapper.text()).toContain('A report was generated in your pipelines.');
});
});
@@ -70,7 +70,7 @@ describe('TerraformPlan', () => {
it('diplays the header text with a name', () => {
expect(wrapper.text()).toContain(
- `The Terraform report ${invalidPlanWithName.job_name} failed to generate.`,
+ `The report ${invalidPlanWithName.job_name} failed to generate.`,
);
});
@@ -85,7 +85,7 @@ describe('TerraformPlan', () => {
});
it('diplays the header text without a name', () => {
- expect(wrapper.text()).toContain('A Terraform report failed to generate.');
+ expect(wrapper.text()).toContain('A report failed to generate.');
});
it('does not render button because url is missing', () => {
diff --git a/spec/frontend/vue_mr_widget/mr_widget_options_spec.js b/spec/frontend/vue_mr_widget/mr_widget_options_spec.js
index 747d46ca20b..8c8c81cab24 100644
--- a/spec/frontend/vue_mr_widget/mr_widget_options_spec.js
+++ b/spec/frontend/vue_mr_widget/mr_widget_options_spec.js
@@ -1,7 +1,9 @@
import { mount } from '@vue/test-utils';
import Vue, { nextTick } from 'vue';
+import VueApollo from 'vue-apollo';
import MockAdapter from 'axios-mock-adapter';
-import Api from '~/api';
+import createMockApollo from 'helpers/mock_apollo_helper';
+import { securityReportDownloadPathsQueryResponse } from 'jest/vue_shared/security_reports/mock_data';
import axios from '~/lib/utils/axios_utils';
import MrWidgetOptions from '~/vue_merge_request_widget/mr_widget_options.vue';
import eventHub from '~/vue_merge_request_widget/event_hub';
@@ -12,11 +14,14 @@ import { stateKey } from '~/vue_merge_request_widget/stores/state_maps';
import mockData from './mock_data';
import { faviconDataUrl, overlayDataUrl } from '../lib/utils/mock_data';
import { SUCCESS } from '~/vue_merge_request_widget/components/deployment/constants';
+import securityReportDownloadPathsQuery from '~/vue_shared/security_reports/queries/security_report_download_paths.query.graphql';
jest.mock('~/smart_interval');
jest.mock('~/lib/utils/favicon');
+Vue.use(VueApollo);
+
describe('MrWidgetOptions', () => {
let wrapper;
let mock;
@@ -41,7 +46,7 @@ describe('MrWidgetOptions', () => {
gon.features = {};
});
- const createComponent = (mrData = mockData) => {
+ const createComponent = (mrData = mockData, options = {}) => {
if (wrapper) {
wrapper.destroy();
}
@@ -50,6 +55,7 @@ describe('MrWidgetOptions', () => {
propsData: {
mrData: { ...mrData },
},
+ ...options,
});
return axios.waitForAll();
@@ -815,36 +821,37 @@ describe('MrWidgetOptions', () => {
describe('security widget', () => {
describe.each`
- context | hasPipeline | reportType | isFlagEnabled | shouldRender
- ${'security report and flag enabled'} | ${true} | ${'sast'} | ${true} | ${true}
- ${'security report and flag disabled'} | ${true} | ${'sast'} | ${false} | ${false}
- ${'no security report and flag enabled'} | ${true} | ${'foo'} | ${true} | ${false}
- ${'no pipeline and flag enabled'} | ${false} | ${'sast'} | ${true} | ${false}
- `('given $context', ({ hasPipeline, reportType, isFlagEnabled, shouldRender }) => {
+ context | hasPipeline | isFlagEnabled | shouldRender
+ ${'has pipeline and flag enabled'} | ${true} | ${true} | ${true}
+ ${'has pipeline and flag disabled'} | ${true} | ${false} | ${false}
+ ${'no pipeline and flag enabled'} | ${false} | ${true} | ${false}
+ `('given $context', ({ hasPipeline, isFlagEnabled, shouldRender }) => {
beforeEach(() => {
gon.features.coreSecurityMrWidget = isFlagEnabled;
- if (hasPipeline) {
- jest.spyOn(Api, 'pipelineJobs').mockResolvedValue({
- data: [{ artifacts: [{ file_type: reportType }] }],
- });
- }
-
- return createComponent({
+ const mrData = {
...mockData,
- ...(hasPipeline ? {} : { pipeline: undefined }),
+ ...(hasPipeline ? {} : { pipeline: null }),
+ };
+
+ // Override top-level mocked requests, which always use a fresh copy of
+ // mockData, which always includes the full pipeline object.
+ mock.onGet(mockData.merge_request_widget_path).reply(() => [200, mrData]);
+ mock.onGet(mockData.merge_request_cached_widget_path).reply(() => [200, mrData]);
+
+ return createComponent(mrData, {
+ apolloProvider: createMockApollo([
+ [
+ securityReportDownloadPathsQuery,
+ async () => ({ data: securityReportDownloadPathsQueryResponse }),
+ ],
+ ]),
});
});
- if (shouldRender) {
- it('renders', () => {
- expect(findSecurityMrWidget().exists()).toBe(true);
- });
- } else {
- it('does not render', () => {
- expect(findSecurityMrWidget().exists()).toBe(false);
- });
- }
+ it(shouldRender ? 'renders' : 'does not render', () => {
+ expect(findSecurityMrWidget().exists()).toBe(shouldRender);
+ });
});
});
diff --git a/spec/frontend/vue_shared/components/gfm_autocomplete/__snapshots__/utils_spec.js.snap b/spec/frontend/vue_shared/components/gfm_autocomplete/__snapshots__/utils_spec.js.snap
index d8e6e37bb89..5f55502567b 100644
--- a/spec/frontend/vue_shared/components/gfm_autocomplete/__snapshots__/utils_spec.js.snap
+++ b/spec/frontend/vue_shared/components/gfm_autocomplete/__snapshots__/utils_spec.js.snap
@@ -21,10 +21,10 @@ exports[`gfm_autocomplete/utils labels config shows the title in the menu item 1
exports[`gfm_autocomplete/utils members config shows an avatar character, name, parent name, and count in the menu item for a group 1`] = `
"
<div class=\\"gl-display-flex gl-align-items-center\\">
- <div class=\\"gl-avatar gl-avatar-s24 gl-flex-shrink-0 gl-rounded-small
+ <div class=\\"gl-avatar gl-avatar-s32 gl-flex-shrink-0 gl-rounded-small
gl-display-flex gl-align-items-center gl-justify-content-center\\" aria-hidden=\\"true\\">
G</div>
- <div class=\\"gl-font-sm gl-line-height-normal gl-ml-3\\">
+ <div class=\\"gl-line-height-normal gl-ml-4\\">
<div>1-1s &lt;script&gt;alert(&#39;hi&#39;)&lt;/script&gt; (2)</div>
<div class=\\"gl-text-gray-700\\">GitLab Support Team</div>
</div>
@@ -36,8 +36,8 @@ exports[`gfm_autocomplete/utils members config shows an avatar character, name,
exports[`gfm_autocomplete/utils members config shows the avatar, name and username in the menu item for a user 1`] = `
"
<div class=\\"gl-display-flex gl-align-items-center\\">
- <img class=\\"gl-avatar gl-avatar-s24 gl-flex-shrink-0 gl-avatar-circle\\" src=\\"/uploads/-/system/user/avatar/123456/avatar.png\\" alt=\\"\\" />
- <div class=\\"gl-font-sm gl-line-height-normal gl-ml-3\\">
+ <img class=\\"gl-avatar gl-avatar-s32 gl-flex-shrink-0 gl-avatar-circle\\" src=\\"/uploads/-/system/user/avatar/123456/avatar.png\\" alt=\\"\\" />
+ <div class=\\"gl-line-height-normal gl-ml-4\\">
<div>My Name &lt;script&gt;alert(&#39;hi&#39;)&lt;/script&gt;</div>
<div class=\\"gl-text-gray-700\\">@myusername</div>
</div>
diff --git a/spec/frontend/vue_shared/security_reports/mock_data.js b/spec/frontend/vue_shared/security_reports/mock_data.js
index b3ff7daef2b..7918f70d702 100644
--- a/spec/frontend/vue_shared/security_reports/mock_data.js
+++ b/spec/frontend/vue_shared/security_reports/mock_data.js
@@ -322,6 +322,23 @@ export const secretScanningDiffSuccessMock = {
head_report_created_at: '2020-01-10T10:00:00.000Z',
};
+export const securityReportDownloadPathsQueryNoArtifactsResponse = {
+ project: {
+ mergeRequest: {
+ headPipeline: {
+ id: 'gid://gitlab/Ci::Pipeline/176',
+ jobs: {
+ nodes: [],
+ __typename: 'CiJobConnection',
+ },
+ __typename: 'Pipeline',
+ },
+ __typename: 'MergeRequest',
+ },
+ __typename: 'Project',
+ },
+};
+
export const securityReportDownloadPathsQueryResponse = {
project: {
mergeRequest: {
diff --git a/spec/frontend/vue_shared/security_reports/security_reports_app_spec.js b/spec/frontend/vue_shared/security_reports/security_reports_app_spec.js
index 50d1d130675..434b66379ea 100644
--- a/spec/frontend/vue_shared/security_reports/security_reports_app_spec.js
+++ b/spec/frontend/vue_shared/security_reports/security_reports_app_spec.js
@@ -8,11 +8,11 @@ import { trimText } from 'helpers/text_helper';
import waitForPromises from 'helpers/wait_for_promises';
import {
expectedDownloadDropdownProps,
+ securityReportDownloadPathsQueryNoArtifactsResponse,
securityReportDownloadPathsQueryResponse,
sastDiffSuccessMock,
secretScanningDiffSuccessMock,
} from 'jest/vue_shared/security_reports/mock_data';
-import Api from '~/api';
import createFlash from '~/flash';
import axios from '~/lib/utils/axios_utils';
import {
@@ -60,6 +60,8 @@ describe('Security reports app', () => {
const pendingHandler = () => new Promise(() => {});
const successHandler = () => Promise.resolve({ data: securityReportDownloadPathsQueryResponse });
+ const successEmptyHandler = () =>
+ Promise.resolve({ data: securityReportDownloadPathsQueryNoArtifactsResponse });
const failureHandler = () => Promise.resolve({ errors: [{ message: 'some error' }] });
const createMockApolloProvider = (handler) => {
localVue.use(VueApollo);
@@ -69,178 +71,85 @@ describe('Security reports app', () => {
return createMockApollo(requestHandlers);
};
- const anyParams = expect.any(Object);
-
const findDownloadDropdown = () => wrapper.find(SecurityReportDownloadDropdown);
- const findPipelinesTabAnchor = () => wrapper.find('[data-testid="show-pipelines"]');
const findHelpIconComponent = () => wrapper.find(HelpIcon);
- const setupMockJobArtifact = (reportType) => {
- jest
- .spyOn(Api, 'pipelineJobs')
- .mockResolvedValue({ data: [{ artifacts: [{ file_type: reportType }] }] });
- };
- const expectPipelinesTabAnchor = () => {
- const mrTabsMock = { tabShown: jest.fn() };
- window.mrTabs = mrTabsMock;
- findPipelinesTabAnchor().trigger('click');
- expect(mrTabsMock.tabShown.mock.calls).toEqual([['pipelines']]);
- };
afterEach(() => {
wrapper.destroy();
- delete window.mrTabs;
});
- describe.each([false, true])(
- 'given the coreSecurityMrWidgetCounts feature flag is %p',
- (coreSecurityMrWidgetCounts) => {
- const createComponentWithFlag = (options) =>
- createComponent(
- merge(
- {
- provide: {
- glFeatures: {
- coreSecurityMrWidgetCounts,
- },
- },
- },
- options,
- ),
- );
-
- describe.each(SecurityReportsApp.reportTypes)('given a report type %p', (reportType) => {
- beforeEach(() => {
- window.mrTabs = { tabShown: jest.fn() };
- setupMockJobArtifact(reportType);
- createComponentWithFlag();
- return wrapper.vm.$nextTick();
- });
-
- it('calls the pipelineJobs API correctly', () => {
- expect(Api.pipelineJobs).toHaveBeenCalledTimes(1);
- expect(Api.pipelineJobs).toHaveBeenCalledWith(
- props.projectId,
- props.pipelineId,
- anyParams,
- );
- });
-
- it('renders the expected message', () => {
- expect(wrapper.text()).toMatchInterpolatedText(
- SecurityReportsApp.i18n.scansHaveRunWithDownloadGuidance,
- );
- });
-
- describe('clicking the anchor to the pipelines tab', () => {
- it('calls the mrTabs.tabShown global', () => {
- expectPipelinesTabAnchor();
- });
- });
-
- it('renders a help link', () => {
- expect(findHelpIconComponent().props()).toEqual({
- helpPath: props.securityReportsDocsPath,
- discoverProjectSecurityPath: props.discoverProjectSecurityPath,
- });
- });
+ describe('given the artifacts query is loading', () => {
+ beforeEach(() => {
+ createComponent({
+ apolloProvider: createMockApolloProvider(pendingHandler),
});
+ });
- describe('given a report type "foo"', () => {
- beforeEach(() => {
- setupMockJobArtifact('foo');
- createComponentWithFlag();
- return wrapper.vm.$nextTick();
- });
-
- it('calls the pipelineJobs API correctly', () => {
- expect(Api.pipelineJobs).toHaveBeenCalledTimes(1);
- expect(Api.pipelineJobs).toHaveBeenCalledWith(
- props.projectId,
- props.pipelineId,
- anyParams,
- );
- });
+ // TODO: Remove this assertion as part of
+ // https://gitlab.com/gitlab-org/gitlab/-/issues/273431
+ it('initially renders nothing', () => {
+ expect(wrapper.html()).toBe('');
+ });
+ });
- it('renders nothing', () => {
- expect(wrapper.html()).toBe('');
- });
+ describe('given the artifacts query loads successfully', () => {
+ beforeEach(() => {
+ createComponent({
+ apolloProvider: createMockApolloProvider(successHandler),
});
+ });
- describe('security artifacts on last page of multi-page response', () => {
- const numPages = 3;
-
- beforeEach(() => {
- jest
- .spyOn(Api, 'pipelineJobs')
- .mockImplementation(async (projectId, pipelineId, { page }) => {
- const requestedPage = parseInt(page, 10);
- if (requestedPage < numPages) {
- return {
- // Some jobs with no relevant artifacts
- data: [{}, {}],
- headers: { 'x-next-page': String(requestedPage + 1) },
- };
- } else if (requestedPage === numPages) {
- return {
- data: [{ artifacts: [{ file_type: SecurityReportsApp.reportTypes[0] }] }],
- };
- }
-
- throw new Error('Test failed due to request of non-existent jobs page');
- });
-
- createComponentWithFlag();
- return wrapper.vm.$nextTick();
- });
+ it('renders the download dropdown', () => {
+ expect(findDownloadDropdown().props()).toEqual(expectedDownloadDropdownProps);
+ });
- it('fetches all pages', () => {
- expect(Api.pipelineJobs).toHaveBeenCalledTimes(numPages);
- });
+ it('renders the expected message', () => {
+ expect(wrapper.text()).toContain(SecurityReportsApp.i18n.scansHaveRun);
+ });
- it('renders the expected message', () => {
- expect(wrapper.text()).toMatchInterpolatedText(
- SecurityReportsApp.i18n.scansHaveRunWithDownloadGuidance,
- );
- });
+ it('renders a help link', () => {
+ expect(findHelpIconComponent().props()).toEqual({
+ helpPath: props.securityReportsDocsPath,
+ discoverProjectSecurityPath: props.discoverProjectSecurityPath,
});
+ });
+ });
- describe('given an error from the API', () => {
- let error;
-
- beforeEach(() => {
- error = new Error('an error');
- jest.spyOn(Api, 'pipelineJobs').mockRejectedValue(error);
- createComponentWithFlag();
- return wrapper.vm.$nextTick();
- });
+ describe('given the artifacts query loads successfully with no artifacts', () => {
+ beforeEach(() => {
+ createComponent({
+ apolloProvider: createMockApolloProvider(successEmptyHandler),
+ });
+ });
- it('calls the pipelineJobs API correctly', () => {
- expect(Api.pipelineJobs).toHaveBeenCalledTimes(1);
- expect(Api.pipelineJobs).toHaveBeenCalledWith(
- props.projectId,
- props.pipelineId,
- anyParams,
- );
- });
+ // TODO: Remove this assertion as part of
+ // https://gitlab.com/gitlab-org/gitlab/-/issues/273431
+ it('initially renders nothing', () => {
+ expect(wrapper.html()).toBe('');
+ });
+ });
- it('renders nothing', () => {
- expect(wrapper.html()).toBe('');
- });
+ describe('given the artifacts query fails', () => {
+ beforeEach(() => {
+ createComponent({
+ apolloProvider: createMockApolloProvider(failureHandler),
+ });
+ });
- it('calls createFlash correctly', () => {
- expect(createFlash.mock.calls).toEqual([
- [
- {
- message: SecurityReportsApp.i18n.apiError,
- captureError: true,
- error,
- },
- ],
- ]);
- });
+ it('calls createFlash correctly', () => {
+ expect(createFlash).toHaveBeenCalledWith({
+ message: SecurityReportsApp.i18n.apiError,
+ captureError: true,
+ error: expect.any(Error),
});
- },
- );
+ });
+
+ // TODO: Remove this assertion as part of
+ // https://gitlab.com/gitlab-org/gitlab/-/issues/273431
+ it('renders nothing', () => {
+ expect(wrapper.html()).toBe('');
+ });
+ });
describe('given the coreSecurityMrWidgetCounts feature flag is enabled', () => {
let mock;
@@ -253,6 +162,7 @@ describe('Security reports app', () => {
coreSecurityMrWidgetCounts: true,
},
},
+ apolloProvider: createMockApolloProvider(successHandler),
}),
);
@@ -274,11 +184,7 @@ describe('Security reports app', () => {
${REPORT_TYPE_SECRET_DETECTION} | ${'secretScanningComparisonPath'} | ${SECRET_SCANNING_COMPARISON_PATH} | ${secretScanningDiffSuccessMock} | ${SECRET_SCANNING_SUCCESS_MESSAGE}
`(
'given a $pathProp and $reportType artifact',
- ({ reportType, pathProp, path, successResponse, successMessage }) => {
- beforeEach(() => {
- setupMockJobArtifact(reportType);
- });
-
+ ({ pathProp, path, successResponse, successMessage }) => {
describe('when loading', () => {
beforeEach(() => {
mock = new MockAdapter(axios, { delayResponse: 1 });
@@ -294,11 +200,11 @@ describe('Security reports app', () => {
});
it('should have loading message', () => {
- expect(wrapper.text()).toBe('Security scanning is loading');
+ expect(wrapper.text()).toContain('Security scanning is loading');
});
- it('should not render the pipeline tab anchor', () => {
- expect(findPipelinesTabAnchor().exists()).toBe(false);
+ it('renders the download dropdown', () => {
+ expect(findDownloadDropdown().props()).toEqual(expectedDownloadDropdownProps);
});
});
@@ -319,8 +225,8 @@ describe('Security reports app', () => {
expect(trimText(wrapper.text())).toContain(successMessage);
});
- it('should render the pipeline tab anchor', () => {
- expectPipelinesTabAnchor();
+ it('renders the download dropdown', () => {
+ expect(findDownloadDropdown().props()).toEqual(expectedDownloadDropdownProps);
});
});
@@ -341,125 +247,25 @@ describe('Security reports app', () => {
expect(trimText(wrapper.text())).toContain('Loading resulted in an error');
});
- it('should render the pipeline tab anchor', () => {
- expectPipelinesTabAnchor();
+ it('renders the download dropdown', () => {
+ expect(findDownloadDropdown().props()).toEqual(expectedDownloadDropdownProps);
});
});
- },
- );
- });
- describe('given coreSecurityMrWidgetDownloads feature flag is enabled', () => {
- const createComponentWithFlagEnabled = (options) =>
- createComponent(
- merge(options, {
- provide: {
- glFeatures: {
- coreSecurityMrWidgetDownloads: true,
- },
- },
- }),
- );
-
- describe('given the query is loading', () => {
- beforeEach(() => {
- createComponentWithFlagEnabled({
- apolloProvider: createMockApolloProvider(pendingHandler),
- });
- });
-
- // TODO: Remove this assertion as part of
- // https://gitlab.com/gitlab-org/gitlab/-/issues/273431
- it('initially renders nothing', () => {
- expect(wrapper.html()).toBe('');
- });
- });
-
- describe('given the query loads successfully', () => {
- beforeEach(() => {
- createComponentWithFlagEnabled({
- apolloProvider: createMockApolloProvider(successHandler),
- });
- });
-
- it('renders the download dropdown', () => {
- expect(findDownloadDropdown().props()).toEqual(expectedDownloadDropdownProps);
- });
-
- it('renders the expected message', () => {
- const text = wrapper.text();
- expect(text).not.toContain(SecurityReportsApp.i18n.scansHaveRunWithDownloadGuidance);
- expect(text).toContain(SecurityReportsApp.i18n.scansHaveRun);
- });
+ describe('when the comparison endpoint is not provided', () => {
+ beforeEach(() => {
+ mock.onGet(path).replyOnce(500);
- it('should not render the pipeline tab anchor', () => {
- expect(findPipelinesTabAnchor().exists()).toBe(false);
- });
- });
+ createComponentWithFlagEnabled();
- describe('given the query fails', () => {
- beforeEach(() => {
- createComponentWithFlagEnabled({
- apolloProvider: createMockApolloProvider(failureHandler),
- });
- });
+ return waitForPromises();
+ });
- it('calls createFlash correctly', () => {
- expect(createFlash).toHaveBeenCalledWith({
- message: SecurityReportsApp.i18n.apiError,
- captureError: true,
- error: expect.any(Error),
+ it('renders the basic scansHaveRun message', () => {
+ expect(wrapper.text()).toContain(SecurityReportsApp.i18n.scansHaveRun);
+ });
});
- });
-
- // TODO: Remove this assertion as part of
- // https://gitlab.com/gitlab-org/gitlab/-/issues/273431
- it('renders nothing', () => {
- expect(wrapper.html()).toBe('');
- });
- });
- });
-
- describe('given coreSecurityMrWidgetCounts and coreSecurityMrWidgetDownloads feature flags are enabled', () => {
- let mock;
-
- beforeEach(() => {
- mock = new MockAdapter(axios);
- mock.onGet(SAST_COMPARISON_PATH).replyOnce(200, sastDiffSuccessMock);
- mock.onGet(SECRET_SCANNING_COMPARISON_PATH).replyOnce(200, secretScanningDiffSuccessMock);
- createComponent({
- propsData: {
- sastComparisonPath: SAST_COMPARISON_PATH,
- secretScanningComparisonPath: SECRET_SCANNING_COMPARISON_PATH,
- },
- provide: {
- glFeatures: {
- coreSecurityMrWidgetCounts: true,
- coreSecurityMrWidgetDownloads: true,
- },
- },
- apolloProvider: createMockApolloProvider(successHandler),
- });
-
- return waitForPromises();
- });
-
- afterEach(() => {
- mock.restore();
- });
-
- it('renders the download dropdown', () => {
- expect(findDownloadDropdown().props()).toEqual(expectedDownloadDropdownProps);
- });
-
- it('renders the expected counts message', () => {
- expect(trimText(wrapper.text())).toContain(
- 'Security scanning detected 3 potential vulnerabilities 2 Critical 1 High and 0 Others',
- );
- });
-
- it('should not render the pipeline tab anchor', () => {
- expect(findPipelinesTabAnchor().exists()).toBe(false);
- });
+ },
+ );
});
});
diff --git a/spec/helpers/commits_helper_spec.rb b/spec/helpers/commits_helper_spec.rb
index 8a570bf9a90..45485a12574 100644
--- a/spec/helpers/commits_helper_spec.rb
+++ b/spec/helpers/commits_helper_spec.rb
@@ -7,19 +7,38 @@ RSpec.describe CommitsHelper do
context 'when current_user exists' do
before do
allow(helper).to receive(:current_user).and_return(double('User'))
- allow(helper).to receive(:can_collaborate_with_project?).and_return(true)
end
it 'renders a div for Vue' do
- result = helper.revert_commit_link('_commit_', '_path_', pajamas: true)
+ result = helper.revert_commit_link
expect(result).to include('js-revert-commit-trigger')
end
+ end
+
+ context 'when current_user does not exist' do
+ before do
+ allow(helper).to receive(:current_user).and_return(nil)
+ end
+
+ it 'does not render anything' do
+ result = helper.revert_commit_link
- it 'does not render a div for Vue' do
- result = helper.revert_commit_link('_commit_', '_path_')
+ expect(result).to be_nil
+ end
+ end
+ end
+
+ describe '#cherry_pick_commit_link' do
+ context 'when current_user exists' do
+ before do
+ allow(helper).to receive(:current_user).and_return(double('User'))
+ end
+
+ it 'renders a div for Vue' do
+ result = helper.cherry_pick_commit_link
- expect(result).not_to include('js-revert-commit-trigger')
+ expect(result).to include('js-cherry-pick-commit-trigger')
end
end
@@ -29,7 +48,7 @@ RSpec.describe CommitsHelper do
end
it 'does not render anything' do
- result = helper.revert_commit_link(double('Commit'), '_path_')
+ result = helper.cherry_pick_commit_link
expect(result).to be_nil
end
diff --git a/spec/lib/gitlab/ci/config/external/mapper_spec.rb b/spec/lib/gitlab/ci/config/external/mapper_spec.rb
index 4fdaaca8316..99f546ceb37 100644
--- a/spec/lib/gitlab/ci/config/external/mapper_spec.rb
+++ b/spec/lib/gitlab/ci/config/external/mapper_spec.rb
@@ -323,20 +323,6 @@ RSpec.describe Gitlab::Ci::Config::External::Mapper do
expect { subject }.to raise_error(described_class::AmbigiousSpecificationError)
end
end
-
- context 'when feature flag is turned off' do
- let(:values) do
- { include: full_local_file_path }
- end
-
- before do
- stub_feature_flags(variables_in_include_section_ci: false)
- end
-
- it 'does not expand the variables' do
- expect(subject[0].location).to eq('$CI_PROJECT_PATH' + local_file)
- end
- end
end
end
end
diff --git a/spec/models/pages/lookup_path_spec.rb b/spec/models/pages/lookup_path_spec.rb
index 30712af6b32..25b282f3758 100644
--- a/spec/models/pages/lookup_path_spec.rb
+++ b/spec/models/pages/lookup_path_spec.rb
@@ -56,6 +56,15 @@ RSpec.describe Pages::LookupPath do
include_examples 'uses disk storage'
+ it 'return nil when legacy storage is disabled and there is no deployment' do
+ stub_feature_flags(pages_serve_from_legacy_storage: false)
+ expect(Gitlab::ErrorTracking).to receive(:track_exception)
+ .with(described_class::LegacyStorageDisabledError)
+ .and_call_original
+
+ expect(source).to eq(nil)
+ end
+
context 'when there is pages deployment' do
let(:deployment) { create(:pages_deployment, project: project) }
diff --git a/spec/models/pages/virtual_domain_spec.rb b/spec/models/pages/virtual_domain_spec.rb
index 38f5f4d2538..29c14cbeb3e 100644
--- a/spec/models/pages/virtual_domain_spec.rb
+++ b/spec/models/pages/virtual_domain_spec.rb
@@ -26,31 +26,34 @@ RSpec.describe Pages::VirtualDomain do
describe '#lookup_paths' do
let(:project_a) { instance_double(Project) }
- let(:project_z) { instance_double(Project) }
- let(:pages_lookup_path_a) { instance_double(Pages::LookupPath, prefix: 'aaa') }
- let(:pages_lookup_path_z) { instance_double(Pages::LookupPath, prefix: 'zzz') }
+ let(:project_b) { instance_double(Project) }
+ let(:project_c) { instance_double(Project) }
+ let(:pages_lookup_path_a) { instance_double(Pages::LookupPath, prefix: 'aaa', source: { type: 'zip', path: 'https://example.com' }) }
+ let(:pages_lookup_path_b) { instance_double(Pages::LookupPath, prefix: 'bbb', source: { type: 'zip', path: 'https://example.com' }) }
+ let(:pages_lookup_path_without_source) { instance_double(Pages::LookupPath, prefix: 'ccc', source: nil) }
context 'when there is pages domain provided' do
let(:domain) { instance_double(PagesDomain) }
- subject(:virtual_domain) { described_class.new([project_a, project_z], domain: domain) }
+ subject(:virtual_domain) { described_class.new([project_a, project_b, project_c], domain: domain) }
it 'returns collection of projects pages lookup paths sorted by prefix in reverse' do
expect(project_a).to receive(:pages_lookup_path).with(domain: domain, trim_prefix: nil).and_return(pages_lookup_path_a)
- expect(project_z).to receive(:pages_lookup_path).with(domain: domain, trim_prefix: nil).and_return(pages_lookup_path_z)
+ expect(project_b).to receive(:pages_lookup_path).with(domain: domain, trim_prefix: nil).and_return(pages_lookup_path_b)
+ expect(project_c).to receive(:pages_lookup_path).with(domain: domain, trim_prefix: nil).and_return(pages_lookup_path_without_source)
- expect(virtual_domain.lookup_paths).to eq([pages_lookup_path_z, pages_lookup_path_a])
+ expect(virtual_domain.lookup_paths).to eq([pages_lookup_path_b, pages_lookup_path_a])
end
end
context 'when there is trim_prefix provided' do
- subject(:virtual_domain) { described_class.new([project_a, project_z], trim_prefix: 'group/') }
+ subject(:virtual_domain) { described_class.new([project_a, project_b], trim_prefix: 'group/') }
it 'returns collection of projects pages lookup paths sorted by prefix in reverse' do
expect(project_a).to receive(:pages_lookup_path).with(trim_prefix: 'group/', domain: nil).and_return(pages_lookup_path_a)
- expect(project_z).to receive(:pages_lookup_path).with(trim_prefix: 'group/', domain: nil).and_return(pages_lookup_path_z)
+ expect(project_b).to receive(:pages_lookup_path).with(trim_prefix: 'group/', domain: nil).and_return(pages_lookup_path_b)
- expect(virtual_domain.lookup_paths).to eq([pages_lookup_path_z, pages_lookup_path_a])
+ expect(virtual_domain.lookup_paths).to eq([pages_lookup_path_b, pages_lookup_path_a])
end
end
end
diff --git a/spec/workers/container_expiration_policies/cleanup_container_repository_worker_spec.rb b/spec/workers/container_expiration_policies/cleanup_container_repository_worker_spec.rb
index e6592f7f204..ba6cf133b0c 100644
--- a/spec/workers/container_expiration_policies/cleanup_container_repository_worker_spec.rb
+++ b/spec/workers/container_expiration_policies/cleanup_container_repository_worker_spec.rb
@@ -82,6 +82,7 @@ RSpec.describe ContainerExpirationPolicies::CleanupContainerRepositoryWorker do
it 'skips the repository' do
expect(ContainerExpirationPolicies::CleanupService).not_to receive(:new)
expect(worker).to receive(:log_extra_metadata_on_done).with(:container_repository_id, repository.id)
+ expect(worker).to receive(:log_extra_metadata_on_done).with(:project_id, repository.project.id)
expect(worker).to receive(:log_extra_metadata_on_done).with(:cleanup_status, :skipped)
expect { subject }.to change { ContainerRepository.waiting_for_cleanup.count }.from(1).to(0)
@@ -213,8 +214,10 @@ RSpec.describe ContainerExpirationPolicies::CleanupContainerRepositoryWorker do
end
def expect_log_extra_metadata(service_response:, cleanup_status: :finished, truncated: false)
- expect(worker).to receive(:log_extra_metadata_on_done).with(:cleanup_status, cleanup_status)
expect(worker).to receive(:log_extra_metadata_on_done).with(:container_repository_id, repository.id)
+ expect(worker).to receive(:log_extra_metadata_on_done).with(:project_id, repository.project.id)
+ expect(worker).to receive(:log_extra_metadata_on_done).with(:cleanup_status, cleanup_status)
+
%i[cleanup_tags_service_original_size cleanup_tags_service_before_truncate_size cleanup_tags_service_after_truncate_size cleanup_tags_service_before_delete_size].each do |field|
value = service_response.payload[field]
expect(worker).to receive(:log_extra_metadata_on_done).with(field, value) unless value.nil?
diff --git a/yarn.lock b/yarn.lock
index 737f59102cc..f98180d20bb 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -876,10 +876,10 @@
resolved "https://registry.yarnpkg.com/@gitlab/tributejs/-/tributejs-1.0.0.tgz#672befa222aeffc83e7d799b0500a7a4418e59b8"
integrity sha512-nmKw1+hB6MHvlmPz63yPwVs1qQkycHwsKgxpEbzmky16Y6mL4EJMk3w1b8QlOAF/AIAzjCERPhe/R4MJiohbZw==
-"@gitlab/ui@25.12.2":
- version "25.12.2"
- resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-25.12.2.tgz#ad47680da4b067140e8d48a04e807352660b9cca"
- integrity sha512-y+uks00z+4kivTYu+l2mrjYT3nfnBS+xKWIUQ9xrkZVCC069V+DffPK+jVRzzhQ67hOMP5LVdaUEOcUplgFvGA==
+"@gitlab/ui@26.0.0":
+ version "26.0.0"
+ resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-26.0.0.tgz#7fa93726478042b1570b2bd3b909217a31177b36"
+ integrity sha512-X22mc3qVBAkfAZ2DRqbPnJ6upzjWlzGLWmHR4l+3MhOBbMBi4EXIuF19nixC5u8bjCMGkK3wMIiZj3C3HsmQ7A==
dependencies:
"@babel/standalone" "^7.0.0"
"@gitlab/vue-toasted" "^1.3.0"