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-13 18:10:40 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2021-01-13 18:10:40 +0300
commit9b1b702f0fc3820e13fd3810bf096687d3378dc5 (patch)
tree8ec6e084f9b0c84ebc0996c8ea64d47389f49e81
parent39c1496527de559d5d3a5c3b53d11575f435a4dc (diff)
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--Gemfile2
-rw-r--r--Gemfile.lock8
-rw-r--r--app/assets/javascripts/blob/file_template_selector.js2
-rw-r--r--app/assets/javascripts/blob/template_selectors/license_selector.js1
-rw-r--r--app/assets/javascripts/boards/components/board_card_layout.vue4
-rw-r--r--app/assets/javascripts/deprecated_jquery_dropdown/gl_dropdown.js1
-rw-r--r--app/assets/javascripts/editor/extensions/editor_ci_schema_ext.js10
-rw-r--r--app/assets/javascripts/ide/stores/actions/file.js21
-rw-r--r--app/assets/javascripts/incidents_settings/constants.js2
-rw-r--r--app/assets/javascripts/issue_show/components/app.vue7
-rw-r--r--app/assets/javascripts/issue_show/components/fields/description_template.vue11
-rw-r--r--app/assets/javascripts/issue_show/components/form.vue11
-rw-r--r--app/assets/javascripts/issue_show/issue.js1
-rw-r--r--app/assets/javascripts/issue_show/stores/index.js2
-rw-r--r--app/assets/javascripts/pipeline_editor/components/lint/ci_lint.vue15
-rw-r--r--app/assets/javascripts/pipeline_editor/components/lint/ci_lint_results_value.vue2
-rw-r--r--app/assets/javascripts/pipeline_editor/graphql/mutations/lint_ci.mutation.graphql2
-rw-r--r--app/assets/javascripts/pipeline_editor/graphql/resolvers.js2
-rw-r--r--app/assets/javascripts/pipeline_editor/pipeline_editor_app.vue36
-rw-r--r--app/assets/javascripts/pipelines/graphql/fragments/pipeline_stages_connection.fragment.graphql13
-rw-r--r--app/assets/javascripts/templates/issuable_template_selector.js23
-rw-r--r--app/assets/stylesheets/page_bundles/oncall_schedules.scss8
-rw-r--r--app/controllers/projects/templates_controller.rb6
-rw-r--r--app/finders/license_template_finder.rb1
-rw-r--r--app/helpers/blob_helper.rb2
-rw-r--r--app/helpers/issuables_description_templates_helper.rb66
-rw-r--r--app/helpers/issuables_helper.rb48
-rw-r--r--app/models/concerns/can_housekeep_repository.rb23
-rw-r--r--app/models/license_template.rb21
-rw-r--r--app/models/project.rb17
-rw-r--r--app/services/merge_requests/merge_service.rb8
-rw-r--r--app/views/projects/_service_desk_settings.html.haml2
-rw-r--r--app/views/projects/edit.html.haml11
-rw-r--r--app/views/shared/issuable/form/_template_selector.html.haml4
-rw-r--r--app/views/shared/issuable/form/_title.html.haml2
-rw-r--r--app/workers/all_queues.yml4
-rw-r--r--app/workers/ci/daily_build_group_report_results_worker.rb2
-rw-r--r--app/workers/ci/pipeline_artifacts/coverage_report_worker.rb2
-rw-r--r--changelogs/unreleased/21686_persist_squash_commit_sha.yml5
-rw-r--r--changelogs/unreleased/292498-webide-switch-before-closing.yml5
-rw-r--r--changelogs/unreleased/track-ci-template-usage-by-default.yml5
-rw-r--r--changelogs/unreleased/yo-master-patch-52337.yml5
-rw-r--r--config/feature_flags/development/persist_squash_commit_sha_for_squashes.yml8
-rw-r--r--config/feature_flags/development/usage_data_track_ci_templates_unique_projects.yml2
-rw-r--r--danger/roulette/Dangerfile3
-rw-r--r--doc/development/feature_flags/process.md27
-rw-r--r--doc/development/features_inside_dot_gitlab.md4
-rw-r--r--doc/operations/incident_management/incidents.md2
-rw-r--r--doc/user/group/index.md3
-rw-r--r--doc/user/project/description_templates.md100
-rw-r--r--doc/user/project/issues/index.md2
-rw-r--r--doc/user/project/static_site_editor/index.md2
-rw-r--r--lib/api/project_templates.rb7
-rw-r--r--lib/gitlab/template/base_template.rb21
-rw-r--r--lib/gitlab/template/issue_template.rb4
-rw-r--r--lib/gitlab/template/merge_request_template.rb4
-rw-r--r--locale/gitlab.pot12
-rwxr-xr-xscripts/verify-tff-mapping4
-rw-r--r--spec/controllers/projects/templates_controller_spec.rb6
-rw-r--r--spec/features/issues/issue_state_spec.rb4
-rw-r--r--spec/features/projects/releases/user_creates_release_spec.rb2
-rw-r--r--spec/frontend/editor/editor_ci_schema_ext_spec.js15
-rw-r--r--spec/frontend/ide/stores/actions/file_spec.js12
-rw-r--r--spec/frontend/incidents_settings/components/__snapshots__/alerts_form_spec.js.snap2
-rw-r--r--spec/frontend/issue_show/components/app_spec.js4
-rw-r--r--spec/frontend/issue_show/components/fields/description_template_spec.js7
-rw-r--r--spec/frontend/issue_show/components/form_spec.js7
-rw-r--r--spec/frontend/issue_show/mock_data.js1
-rw-r--r--spec/frontend/pipeline_editor/components/lint/ci_lint_spec.js15
-rw-r--r--spec/frontend/pipeline_editor/graphql/__snapshots__/resolvers_spec.js.snap4
-rw-r--r--spec/frontend/pipeline_editor/mock_data.js31
-rw-r--r--spec/frontend/pipeline_editor/pipeline_editor_app_spec.js94
-rw-r--r--spec/helpers/issuables_description_templates_helper_spec.rb43
-rw-r--r--spec/helpers/issuables_helper_spec.rb1
-rw-r--r--spec/models/license_template_spec.rb2
-rw-r--r--spec/models/project_spec.rb53
-rw-r--r--spec/services/merge_requests/merge_service_spec.rb12
-rw-r--r--spec/support/shared_examples/helpers/issuable_description_templates_shared_examples.rb49
-rw-r--r--spec/support/shared_examples/models/concerns/can_housekeep_repository_shared_examples.rb45
-rw-r--r--spec/tooling/lib/tooling/test_file_finder_spec.rb175
-rw-r--r--tooling/lib/tooling/test_file_finder.rb94
81 files changed, 691 insertions, 615 deletions
diff --git a/Gemfile b/Gemfile
index 768d28fa9b2..d091e51f931 100644
--- a/Gemfile
+++ b/Gemfile
@@ -309,7 +309,7 @@ gem 'pg_query', '~> 1.3.0'
gem 'premailer-rails', '~> 1.10.3'
# LabKit: Tracing and Correlation
-gem 'gitlab-labkit', '0.13.5'
+gem 'gitlab-labkit', '0.14.0'
# I18n
gem 'ruby_parser', '~> 3.15', require: false
diff --git a/Gemfile.lock b/Gemfile.lock
index a39a504e34b..af8c1d5b50f 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -432,9 +432,9 @@ GEM
fog-json (~> 1.2.0)
mime-types
ms_rest_azure (~> 0.12.0)
- gitlab-labkit (0.13.5)
- actionpack (>= 5.0.0, < 6.1.0)
- activesupport (>= 5.0.0, < 6.1.0)
+ gitlab-labkit (0.14.0)
+ actionpack (>= 5.0.0, < 7.0.0)
+ activesupport (>= 5.0.0, < 7.0.0)
gitlab-pg_query (~> 1.3)
grpc (~> 1.19)
jaeger-client (~> 1.1)
@@ -1363,7 +1363,7 @@ DEPENDENCIES
gitlab-chronic (~> 0.10.5)
gitlab-experiment (~> 0.4.4)
gitlab-fog-azure-rm (~> 1.0)
- gitlab-labkit (= 0.13.5)
+ gitlab-labkit (= 0.14.0)
gitlab-license (~> 1.0)
gitlab-mail_room (~> 0.0.8)
gitlab-markup (~> 1.7.1)
diff --git a/app/assets/javascripts/blob/file_template_selector.js b/app/assets/javascripts/blob/file_template_selector.js
index 2532aeea989..a5c8050b772 100644
--- a/app/assets/javascripts/blob/file_template_selector.js
+++ b/app/assets/javascripts/blob/file_template_selector.js
@@ -66,6 +66,8 @@ export default class FileTemplateSelector {
reportSelectionName(options) {
const opts = options;
opts.query = options.selectedObj.name;
+ opts.data = options.selectedObj;
+ opts.data.source_template_project_id = options.selectedObj.project_id;
this.reportSelection(opts);
}
diff --git a/app/assets/javascripts/blob/template_selectors/license_selector.js b/app/assets/javascripts/blob/template_selectors/license_selector.js
index affa20997e9..7e32ede96df 100644
--- a/app/assets/javascripts/blob/template_selectors/license_selector.js
+++ b/app/assets/javascripts/blob/template_selectors/license_selector.js
@@ -30,6 +30,7 @@ export default class BlobLicenseSelector extends FileTemplateSelector {
const data = {
project: this.$dropdown.data('project'),
fullname: this.$dropdown.data('fullname'),
+ source_template_project_id: query.project_id,
};
this.reportSelection({
diff --git a/app/assets/javascripts/boards/components/board_card_layout.vue b/app/assets/javascripts/boards/components/board_card_layout.vue
index 350d709abfd..8b0265237ba 100644
--- a/app/assets/javascripts/boards/components/board_card_layout.vue
+++ b/app/assets/javascripts/boards/components/board_card_layout.vue
@@ -4,7 +4,7 @@ import IssueCardInnerDeprecated from './issue_card_inner_deprecated.vue';
import boardsStore from '../stores/boards_store';
export default {
- name: 'BoardsIssueCard',
+ name: 'BoardCardLayout',
components: {
IssueCardInner: gon.features?.graphqlBoardLists ? IssueCardInner : IssueCardInnerDeprecated,
},
@@ -81,7 +81,7 @@ export default {
:data-issue-iid="issue.iid"
:data-issue-path="issue.referencePath"
data-testid="board_card"
- class="board-card p-3 rounded"
+ class="board-card gl-p-5 gl-rounded-base"
@mousedown="mouseDown"
@mousemove="mouseMove"
@mouseup="showIssue($event)"
diff --git a/app/assets/javascripts/deprecated_jquery_dropdown/gl_dropdown.js b/app/assets/javascripts/deprecated_jquery_dropdown/gl_dropdown.js
index 99351231520..98858f20518 100644
--- a/app/assets/javascripts/deprecated_jquery_dropdown/gl_dropdown.js
+++ b/app/assets/javascripts/deprecated_jquery_dropdown/gl_dropdown.js
@@ -437,6 +437,7 @@ export class GitLabDropdown {
groupName = el.data('group');
if (groupName) {
selectedIndex = el.data('index');
+ this.selectedIndex = selectedIndex;
selectedObject = this.renderedData[groupName][selectedIndex];
} else {
selectedIndex = el.closest('li').index();
diff --git a/app/assets/javascripts/editor/extensions/editor_ci_schema_ext.js b/app/assets/javascripts/editor/extensions/editor_ci_schema_ext.js
index 05d938c57ce..eb47c20912e 100644
--- a/app/assets/javascripts/editor/extensions/editor_ci_schema_ext.js
+++ b/app/assets/javascripts/editor/extensions/editor_ci_schema_ext.js
@@ -17,15 +17,21 @@ export class CiSchemaExtension extends EditorLiteExtension {
* @param {String?} opts.ref - Current ref. Defaults to master
*/
registerCiSchema({ projectNamespace, projectPath, ref = 'master' } = {}) {
- const ciSchemaUri = Api.buildUrl(Api.projectFileSchemaPath)
+ const ciSchemaPath = Api.buildUrl(Api.projectFileSchemaPath)
.replace(':namespace_path', projectNamespace)
.replace(':project_path', projectPath)
.replace(':ref', ref)
.replace(':filename', EXTENSION_CI_SCHEMA_FILE_NAME_MATCH);
+ // In order for workers loaded from `data://` as the
+ // ones loaded by monaco editor, we use absolute URLs
+ // to fetch schema files, hence the `gon.gitlab_url`
+ // reference. This prevents error:
+ // "Failed to execute 'fetch' on 'WorkerGlobalScope'"
+ const absoluteSchemaUrl = gon.gitlab_url + ciSchemaPath;
const modelFileName = this.getModel().uri.path.split('/').pop();
registerSchema({
- uri: ciSchemaUri,
+ uri: absoluteSchemaUrl,
fileMatch: [modelFileName],
});
}
diff --git a/app/assets/javascripts/ide/stores/actions/file.js b/app/assets/javascripts/ide/stores/actions/file.js
index 004e38b0a25..42668dec63a 100644
--- a/app/assets/javascripts/ide/stores/actions/file.js
+++ b/app/assets/javascripts/ide/stores/actions/file.js
@@ -17,15 +17,8 @@ export const closeFile = ({ commit, state, dispatch, getters }, file) => {
const indexOfClosedFile = state.openFiles.findIndex((f) => f.key === file.key);
const fileWasActive = file.active;
- if (file.pending) {
- commit(types.REMOVE_PENDING_TAB, file);
- } else {
- commit(types.TOGGLE_FILE_OPEN, path);
- commit(types.SET_FILE_ACTIVE, { path, active: false });
- }
-
- if (state.openFiles.length > 0 && fileWasActive) {
- const nextIndexToOpen = indexOfClosedFile === 0 ? 0 : indexOfClosedFile - 1;
+ if (state.openFiles.length > 1 && fileWasActive) {
+ const nextIndexToOpen = indexOfClosedFile === 0 ? 1 : indexOfClosedFile - 1;
const nextFileToOpen = state.openFiles[nextIndexToOpen];
if (nextFileToOpen.pending) {
@@ -35,14 +28,22 @@ export const closeFile = ({ commit, state, dispatch, getters }, file) => {
keyPrefix: nextFileToOpen.staged ? 'staged' : 'unstaged',
});
} else {
+ dispatch('setFileActive', nextFileToOpen.path);
dispatch('router/push', getters.getUrlForPath(nextFileToOpen.path), { root: true });
}
- } else if (!state.openFiles.length) {
+ } else if (state.openFiles.length === 1) {
dispatch('router/push', `/project/${state.currentProjectId}/tree/${state.currentBranchId}/`, {
root: true,
});
}
+ if (file.pending) {
+ commit(types.REMOVE_PENDING_TAB, file);
+ } else {
+ commit(types.TOGGLE_FILE_OPEN, path);
+ commit(types.SET_FILE_ACTIVE, { path, active: false });
+ }
+
eventHub.$emit(`editor.update.model.dispose.${file.key}`);
};
diff --git a/app/assets/javascripts/incidents_settings/constants.js b/app/assets/javascripts/incidents_settings/constants.js
index fcac9c519c2..818af4ecb90 100644
--- a/app/assets/javascripts/incidents_settings/constants.js
+++ b/app/assets/javascripts/incidents_settings/constants.js
@@ -51,7 +51,7 @@ export const NO_ISSUE_TEMPLATE_SELECTED = { key: '', name: __('No template selec
export const TAKING_INCIDENT_ACTION_DOCS_LINK =
'/help/operations/metrics/alerts#trigger-actions-from-alerts';
export const ISSUE_TEMPLATES_DOCS_LINK =
- '/help/user/project/description_templates#creating-issue-templates';
+ '/help/user/project/description_templates#create-an-issue-template';
/* PagerDuty integration settings constants */
diff --git a/app/assets/javascripts/issue_show/components/app.vue b/app/assets/javascripts/issue_show/components/app.vue
index d569ad573a2..87dc31e6292 100644
--- a/app/assets/javascripts/issue_show/components/app.vue
+++ b/app/assets/javascripts/issue_show/components/app.vue
@@ -132,6 +132,10 @@ export default {
type: String,
required: true,
},
+ projectId: {
+ type: Number,
+ required: true,
+ },
projectNamespace: {
type: String,
required: true,
@@ -303,7 +307,7 @@ export default {
});
},
- updateAndShowForm(templates = []) {
+ updateAndShowForm(templates = {}) {
if (!this.showForm) {
this.showForm = true;
this.store.setFormState({
@@ -419,6 +423,7 @@ export default {
:markdown-docs-path="markdownDocsPath"
:markdown-preview-path="markdownPreviewPath"
:project-path="projectPath"
+ :project-id="projectId"
:project-namespace="projectNamespace"
:show-delete-button="showDeleteButton"
:can-attach-file="canAttachFile"
diff --git a/app/assets/javascripts/issue_show/components/fields/description_template.vue b/app/assets/javascripts/issue_show/components/fields/description_template.vue
index 71299381aae..f23bb394683 100644
--- a/app/assets/javascripts/issue_show/components/fields/description_template.vue
+++ b/app/assets/javascripts/issue_show/components/fields/description_template.vue
@@ -13,14 +13,18 @@ export default {
required: true,
},
issuableTemplates: {
- type: Array,
+ type: Object,
required: false,
- default: () => [],
+ default: () => {},
},
projectPath: {
type: String,
required: true,
},
+ projectId: {
+ type: Number,
+ required: true,
+ },
projectNamespace: {
type: String,
required: true,
@@ -48,11 +52,12 @@ export default {
</script>
<template>
- <div class="dropdown js-issuable-selector-wrap" data-issuable-type="issue">
+ <div class="dropdown js-issuable-selector-wrap" data-issuable-type="issues">
<button
ref="toggle"
:data-namespace-path="projectNamespace"
:data-project-path="projectPath"
+ :data-project-id="projectId"
:data-data="issuableTemplatesJson"
class="dropdown-menu-toggle js-issuable-selector"
type="button"
diff --git a/app/assets/javascripts/issue_show/components/form.vue b/app/assets/javascripts/issue_show/components/form.vue
index d48bf1fe7a9..9d1ce01116b 100644
--- a/app/assets/javascripts/issue_show/components/form.vue
+++ b/app/assets/javascripts/issue_show/components/form.vue
@@ -26,9 +26,9 @@ export default {
required: true,
},
issuableTemplates: {
- type: Array,
+ type: Object,
required: false,
- default: () => [],
+ default: () => {},
},
issuableType: {
type: String,
@@ -46,6 +46,10 @@ export default {
type: String,
required: true,
},
+ projectId: {
+ type: Number,
+ required: true,
+ },
projectNamespace: {
type: String,
required: true,
@@ -68,7 +72,7 @@ export default {
},
computed: {
hasIssuableTemplates() {
- return this.issuableTemplates.length;
+ return Object.values(Object(this.issuableTemplates)).length;
},
showLockedWarning() {
return this.formState.lockedWarningVisible && !this.formState.updateLoading;
@@ -127,6 +131,7 @@ export default {
:form-state="formState"
:issuable-templates="issuableTemplates"
:project-path="projectPath"
+ :project-id="projectId"
:project-namespace="projectNamespace"
/>
</div>
diff --git a/app/assets/javascripts/issue_show/issue.js b/app/assets/javascripts/issue_show/issue.js
index 83fd1355f26..a93abbf64df 100644
--- a/app/assets/javascripts/issue_show/issue.js
+++ b/app/assets/javascripts/issue_show/issue.js
@@ -54,6 +54,7 @@ export function initIssueHeaderActions(store) {
issueType: el.dataset.issueType,
newIssuePath: el.dataset.newIssuePath,
projectPath: el.dataset.projectPath,
+ projectId: el.dataset.projectId,
reportAbusePath: el.dataset.reportAbusePath,
submitAsSpamPath: el.dataset.submitAsSpamPath,
},
diff --git a/app/assets/javascripts/issue_show/stores/index.js b/app/assets/javascripts/issue_show/stores/index.js
index 06bbd406e3a..a50913d3455 100644
--- a/app/assets/javascripts/issue_show/stores/index.js
+++ b/app/assets/javascripts/issue_show/stores/index.js
@@ -11,7 +11,7 @@ export default class Store {
lockedWarningVisible: false,
updateLoading: false,
lock_version: 0,
- issuableTemplates: [],
+ issuableTemplates: {},
};
}
diff --git a/app/assets/javascripts/pipeline_editor/components/lint/ci_lint.vue b/app/assets/javascripts/pipeline_editor/components/lint/ci_lint.vue
index 22f734be5aa..b27ab9a39d3 100644
--- a/app/assets/javascripts/pipeline_editor/components/lint/ci_lint.vue
+++ b/app/assets/javascripts/pipeline_editor/components/lint/ci_lint.vue
@@ -1,4 +1,5 @@
<script>
+import { flatten } from 'lodash';
import { CI_CONFIG_STATUS_VALID } from '../../constants';
import CiLintResults from './ci_lint_results.vue';
@@ -25,14 +26,18 @@ export default {
return this.ciConfig?.stages || [];
},
jobs() {
- return this.stages.reduce((acc, { groups, name: stageName }) => {
+ const groupedJobs = this.stages.reduce((acc, { groups, name: stageName }) => {
return acc.concat(
- groups.map(({ name: groupName }) => ({
- stage: stageName,
- name: groupName,
- })),
+ groups.map(({ jobs }) => {
+ return jobs.map((job) => ({
+ stage: stageName,
+ ...job,
+ }));
+ }),
);
}, []);
+
+ return flatten(groupedJobs);
},
},
};
diff --git a/app/assets/javascripts/pipeline_editor/components/lint/ci_lint_results_value.vue b/app/assets/javascripts/pipeline_editor/components/lint/ci_lint_results_value.vue
index 6c7e03e4920..e2529613844 100644
--- a/app/assets/javascripts/pipeline_editor/components/lint/ci_lint_results_value.vue
+++ b/app/assets/javascripts/pipeline_editor/components/lint/ci_lint_results_value.vue
@@ -14,7 +14,7 @@ export default {
},
computed: {
tagList() {
- return this.item.tagList?.join(', ');
+ return this.item.tags?.join(', ');
},
onlyPolicy() {
return this.item.only ? this.item.only.refs.join(', ') : this.item.only;
diff --git a/app/assets/javascripts/pipeline_editor/graphql/mutations/lint_ci.mutation.graphql b/app/assets/javascripts/pipeline_editor/graphql/mutations/lint_ci.mutation.graphql
index 496036f690f..5091d63111f 100644
--- a/app/assets/javascripts/pipeline_editor/graphql/mutations/lint_ci.mutation.graphql
+++ b/app/assets/javascripts/pipeline_editor/graphql/mutations/lint_ci.mutation.graphql
@@ -15,7 +15,7 @@ mutation lintCI($endpoint: String, $content: String, $dry: Boolean) {
}
afterScript
stage
- tagList
+ tags
when
}
}
diff --git a/app/assets/javascripts/pipeline_editor/graphql/resolvers.js b/app/assets/javascripts/pipeline_editor/graphql/resolvers.js
index ec55191c946..81e75c32846 100644
--- a/app/assets/javascripts/pipeline_editor/graphql/resolvers.js
+++ b/app/assets/javascripts/pipeline_editor/graphql/resolvers.js
@@ -27,7 +27,7 @@ export const resolvers = {
beforeScript: job.before_script,
script: job.script,
afterScript: job.after_script,
- tagList: job.tag_list,
+ tags: job.tag_list,
environment: job.environment,
when: job.when,
allowFailure: job.allow_failure,
diff --git a/app/assets/javascripts/pipeline_editor/pipeline_editor_app.vue b/app/assets/javascripts/pipeline_editor/pipeline_editor_app.vue
index 9217466a0b5..d6884ae121f 100644
--- a/app/assets/javascripts/pipeline_editor/pipeline_editor_app.vue
+++ b/app/assets/javascripts/pipeline_editor/pipeline_editor_app.vue
@@ -3,6 +3,7 @@ import { GlAlert, GlLoadingIcon, GlTabs, GlTab } from '@gitlab/ui';
import { __, s__, sprintf } from '~/locale';
import { mergeUrlParams, redirectTo } from '~/lib/utils/url_utility';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
+import httpStatusCodes from '~/lib/utils/http_status';
import PipelineGraph from '~/pipelines/components/pipeline_graph/pipeline_graph.vue';
import CiLint from './components/lint/ci_lint.vue';
@@ -23,7 +24,6 @@ const COMMIT_FAILURE = 'COMMIT_FAILURE';
const COMMIT_SUCCESS = 'COMMIT_SUCCESS';
const DEFAULT_FAILURE = 'DEFAULT_FAILURE';
const LOAD_FAILURE_NO_FILE = 'LOAD_FAILURE_NO_FILE';
-const LOAD_FAILURE_NO_REF = 'LOAD_FAILURE_NO_REF';
const LOAD_FAILURE_UNKNOWN = 'LOAD_FAILURE_UNKNOWN';
export default {
@@ -125,6 +125,9 @@ export default {
isBlobContentLoading() {
return this.$apollo.queries.content.loading;
},
+ isBlobContentError() {
+ return this.failureType === LOAD_FAILURE_NO_FILE || this.failureType === LOAD_FAILURE_UNKNOWN;
+ },
isCiConfigDataLoading() {
return this.$apollo.queries.ciConfigData.loading;
},
@@ -144,14 +147,11 @@ export default {
},
failure() {
switch (this.failureType) {
- case LOAD_FAILURE_NO_REF:
- return {
- text: this.$options.alertTexts[LOAD_FAILURE_NO_REF],
- variant: 'danger',
- };
case LOAD_FAILURE_NO_FILE:
return {
- text: this.$options.alertTexts[LOAD_FAILURE_NO_FILE],
+ text: sprintf(this.$options.alertTexts[LOAD_FAILURE_NO_FILE], {
+ filePath: this.ciConfigPath,
+ }),
variant: 'danger',
};
case LOAD_FAILURE_UNKNOWN:
@@ -182,9 +182,8 @@ export default {
[COMMIT_FAILURE]: s__('Pipelines|The GitLab CI configuration could not be updated.'),
[COMMIT_SUCCESS]: __('Your changes have been successfully committed.'),
[DEFAULT_FAILURE]: __('Something went wrong on our end.'),
- [LOAD_FAILURE_NO_FILE]: s__('Pipelines|No CI file found in this repository, please add one.'),
- [LOAD_FAILURE_NO_REF]: s__(
- 'Pipelines|Repository does not have a default branch, please set one.',
+ [LOAD_FAILURE_NO_FILE]: s__(
+ 'Pipelines|There is no %{filePath} file in this repository, please add one and visit the Pipeline Editor again.',
),
[LOAD_FAILURE_UNKNOWN]: s__('Pipelines|The CI configuration was not loaded, please try again.'),
},
@@ -193,12 +192,13 @@ export default {
const { networkError } = error;
const { response } = networkError;
- if (response?.status === 404) {
- // 404 for missing CI file
+ // 404 for missing CI file
+ // 400 for blank projects with no repository
+ if (
+ response?.status === httpStatusCodes.NOT_FOUND ||
+ response?.status === httpStatusCodes.BAD_REQUEST
+ ) {
this.reportFailure(LOAD_FAILURE_NO_FILE);
- } else if (response?.status === 400) {
- // 400 for a missing ref when no default branch is set
- this.reportFailure(LOAD_FAILURE_NO_REF);
} else {
this.reportFailure(LOAD_FAILURE_UNKNOWN);
}
@@ -299,9 +299,9 @@ export default {
<li v-for="reason in failureReasons" :key="reason">{{ reason }}</li>
</ul>
</gl-alert>
- <div class="gl-mt-4">
- <gl-loading-icon v-if="isBlobContentLoading" size="lg" class="gl-m-3" />
- <div v-else class="file-editor gl-mb-3">
+ <gl-loading-icon v-if="isBlobContentLoading" size="lg" class="gl-m-3" />
+ <div v-else-if="!isBlobContentError" class="gl-mt-4">
+ <div class="file-editor gl-mb-3">
<div class="info-well gl-display-none gl-display-sm-block">
<validation-segment
class="well-segment"
diff --git a/app/assets/javascripts/pipelines/graphql/fragments/pipeline_stages_connection.fragment.graphql b/app/assets/javascripts/pipelines/graphql/fragments/pipeline_stages_connection.fragment.graphql
index 683e0ee6a14..f93908aeb04 100644
--- a/app/assets/javascripts/pipelines/graphql/fragments/pipeline_stages_connection.fragment.graphql
+++ b/app/assets/javascripts/pipelines/graphql/fragments/pipeline_stages_connection.fragment.graphql
@@ -8,6 +8,19 @@ fragment PipelineStagesConnection on CiConfigStageConnection {
jobs {
nodes {
name
+ script
+ beforeScript
+ afterScript
+ environment
+ allowFailure
+ tags
+ when
+ only {
+ refs
+ }
+ except {
+ refs
+ }
needs {
nodes {
name
diff --git a/app/assets/javascripts/templates/issuable_template_selector.js b/app/assets/javascripts/templates/issuable_template_selector.js
index 22bbd083a5d..bcae79c9679 100644
--- a/app/assets/javascripts/templates/issuable_template_selector.js
+++ b/app/assets/javascripts/templates/issuable_template_selector.js
@@ -9,6 +9,7 @@ export default class IssuableTemplateSelector extends TemplateSelector {
constructor(...args) {
super(...args);
+ this.projectId = this.dropdown.data('projectId');
this.projectPath = this.dropdown.data('projectPath');
this.namespacePath = this.dropdown.data('namespacePath');
this.issuableType = this.$dropdownContainer.data('issuableType');
@@ -81,21 +82,21 @@ export default class IssuableTemplateSelector extends TemplateSelector {
}
requestFile(query) {
+ const callback = (currentTemplate) => {
+ this.currentTemplate = currentTemplate;
+ this.stopLoadingSpinner();
+ this.setInputValueToTemplateContent();
+ };
+
this.startLoadingSpinner();
- Api.issueTemplate(
- this.namespacePath,
- this.projectPath,
- query.name,
+ Api.projectTemplate(
+ this.projectId,
this.issuableType,
- (err, currentTemplate) => {
- this.currentTemplate = currentTemplate;
- this.stopLoadingSpinner();
- if (err) return; // Error handled by global AJAX error handler
- this.setInputValueToTemplateContent();
- },
+ query.name,
+ { source_template_project_id: query.project_id },
+ callback,
);
- return;
}
setInputValueToTemplateContent() {
diff --git a/app/assets/stylesheets/page_bundles/oncall_schedules.scss b/app/assets/stylesheets/page_bundles/oncall_schedules.scss
index c7e90ba3451..2ab3bdcc474 100644
--- a/app/assets/stylesheets/page_bundles/oncall_schedules.scss
+++ b/app/assets/stylesheets/page_bundles/oncall_schedules.scss
@@ -32,13 +32,17 @@
.rotations-modal {
.gl-card {
min-width: 75%;
- width: fit-content;
- @include gl-bg-gray-10;
}
&.gl-modal .modal-md {
max-width: 640px;
}
+
+ // TODO: move to gitlab/ui utilities
+ // https://gitlab.com/gitlab-org/gitlab/-/issues/297502
+ .gl-w-fit-content {
+ width: fit-content;
+ }
}
//// Copied from roadmaps.scss - adapted for on-call schedules
diff --git a/app/controllers/projects/templates_controller.rb b/app/controllers/projects/templates_controller.rb
index f4726638777..71f426a68ab 100644
--- a/app/controllers/projects/templates_controller.rb
+++ b/app/controllers/projects/templates_controller.rb
@@ -1,6 +1,8 @@
# frozen_string_literal: true
class Projects::TemplatesController < Projects::ApplicationController
+ include IssuablesDescriptionTemplatesHelper
+
before_action :authenticate_user!
before_action :authorize_can_read_issuable!
before_action :get_template_class
@@ -24,10 +26,8 @@ class Projects::TemplatesController < Projects::ApplicationController
end
def names
- templates = @template_type.dropdown_names(project)
-
respond_to do |format|
- format.json { render json: templates }
+ format.json { render json: issuable_templates(project, params[:template_type]) }
end
end
diff --git a/app/finders/license_template_finder.rb b/app/finders/license_template_finder.rb
index 4d68b963dc3..2419141f260 100644
--- a/app/finders/license_template_finder.rb
+++ b/app/finders/license_template_finder.rb
@@ -36,6 +36,7 @@ class LicenseTemplateFinder
LicenseTemplate.new(
key: license.key,
name: license.name,
+ project: project,
nickname: license.nickname,
category: (license.featured? ? :Popular : :Other),
content: license.content,
diff --git a/app/helpers/blob_helper.rb b/app/helpers/blob_helper.rb
index 254af62f47a..7a4913748e8 100644
--- a/app/helpers/blob_helper.rb
+++ b/app/helpers/blob_helper.rb
@@ -199,7 +199,7 @@ module BlobHelper
categories.each_with_object({}) do |category, hash|
hash[category] = grouped[category].map do |item|
- { name: item.name, id: item.key }
+ { name: item.name, id: item.key, project_id: item.project_id }
end
end
end
diff --git a/app/helpers/issuables_description_templates_helper.rb b/app/helpers/issuables_description_templates_helper.rb
new file mode 100644
index 00000000000..ffb20db65d0
--- /dev/null
+++ b/app/helpers/issuables_description_templates_helper.rb
@@ -0,0 +1,66 @@
+# frozen_string_literal: true
+
+module IssuablesDescriptionTemplatesHelper
+ include Gitlab::Utils::StrongMemoize
+ include GitlabRoutingHelper
+
+ def template_dropdown_tag(issuable, &block)
+ title = selected_template(issuable) || "Choose a template"
+ options = {
+ toggle_class: 'js-issuable-selector',
+ title: title,
+ filter: true,
+ placeholder: 'Filter',
+ footer_content: true,
+ data: {
+ data: issuable_templates(ref_project, issuable.to_ability_name),
+ field_name: 'issuable_template',
+ selected: selected_template(issuable),
+ project_id: ref_project.id,
+ project_path: ref_project.path,
+ namespace_path: ref_project.namespace.full_path
+ }
+ }
+
+ dropdown_tag(title, options: options) do
+ capture(&block)
+ end
+ end
+
+ def issuable_templates(project, issuable_type)
+ strong_memoize(:issuable_templates) do
+ supported_issuable_types = %w[issue merge_request]
+
+ next [] unless supported_issuable_types.include?(issuable_type)
+
+ template_dropdown_names(TemplateFinder.build(issuable_type.pluralize.to_sym, project).execute)
+ end
+ end
+
+ private
+
+ def issuable_templates_names(issuable)
+ issuable_templates(ref_project, issuable.to_ability_name).map { |template| template[:name] }
+ end
+
+ def selected_template(issuable)
+ params[:issuable_template] if issuable_templates(ref_project, issuable.to_ability_name).values.flatten.any? { |template| template[:name] == params[:issuable_template] }
+ end
+
+ def template_names_path(parent, issuable)
+ return '' unless parent.is_a?(Project)
+
+ project_template_names_path(parent, template_type: issuable.to_ability_name)
+ end
+
+ def template_dropdown_names(items)
+ grouped = items.group_by(&:category)
+ categories = grouped.keys
+
+ categories.each_with_object({}) do |category, hash|
+ hash[category] = grouped[category].map do |item|
+ { name: item.name, id: item.key, project_id: item.try(:project_id) }
+ end
+ end
+ end
+end
diff --git a/app/helpers/issuables_helper.rb b/app/helpers/issuables_helper.rb
index da142cbed0e..de5cae2e9e2 100644
--- a/app/helpers/issuables_helper.rb
+++ b/app/helpers/issuables_helper.rb
@@ -2,6 +2,7 @@
module IssuablesHelper
include GitlabRoutingHelper
+ include IssuablesDescriptionTemplatesHelper
def sidebar_gutter_toggle_icon
content_tag(:span, class: 'js-sidebar-toggle-container', data: { is_expanded: !sidebar_gutter_collapsed? }) do
@@ -75,28 +76,6 @@ module IssuablesHelper
.to_json
end
- def template_dropdown_tag(issuable, &block)
- title = selected_template(issuable) || "Choose a template"
- options = {
- toggle_class: 'js-issuable-selector',
- title: title,
- filter: true,
- placeholder: 'Filter',
- footer_content: true,
- data: {
- data: issuable_templates(issuable),
- field_name: 'issuable_template',
- selected: selected_template(issuable),
- project_path: ref_project.path,
- namespace_path: ref_project.namespace.full_path
- }
- }
-
- dropdown_tag(title, options: options) do
- capture(&block)
- end
- end
-
def users_dropdown_label(selected_users)
case selected_users.length
when 0
@@ -294,6 +273,7 @@ module IssuablesHelper
{
projectPath: ref_project.path,
+ projectId: ref_project.id,
projectNamespace: ref_project.namespace.full_path
}
end
@@ -369,24 +349,6 @@ module IssuablesHelper
cookies[:collapsed_gutter] == 'true'
end
- def issuable_templates(issuable)
- @issuable_templates ||=
- case issuable
- when Issue
- ref_project.repository.issue_template_names
- when MergeRequest
- ref_project.repository.merge_request_template_names
- end
- end
-
- def issuable_templates_names(issuable)
- issuable_templates(issuable).map { |template| template[:name] }
- end
-
- def selected_template(issuable)
- params[:issuable_template] if issuable_templates(issuable).any? { |template| template[:name] == params[:issuable_template] }
- end
-
def issuable_todo_button_data(issuable, is_collapsed)
{
todo_text: _('Add a to do'),
@@ -424,12 +386,6 @@ module IssuablesHelper
end
end
- def template_names_path(parent, issuable)
- return '' unless parent.is_a?(Project)
-
- project_template_names_path(parent, template_type: issuable.class.name.underscore)
- end
-
def issuable_sidebar_options(issuable)
{
endpoint: "#{issuable[:issuable_json_path]}?serializer=sidebar_extras",
diff --git a/app/models/concerns/can_housekeep_repository.rb b/app/models/concerns/can_housekeep_repository.rb
new file mode 100644
index 00000000000..6c7d6ac37ef
--- /dev/null
+++ b/app/models/concerns/can_housekeep_repository.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+module CanHousekeepRepository
+ extend ActiveSupport::Concern
+
+ def pushes_since_gc
+ Gitlab::Redis::SharedState.with { |redis| redis.get(pushes_since_gc_redis_shared_state_key).to_i }
+ end
+
+ def increment_pushes_since_gc
+ Gitlab::Redis::SharedState.with { |redis| redis.incr(pushes_since_gc_redis_shared_state_key) }
+ end
+
+ def reset_pushes_since_gc
+ Gitlab::Redis::SharedState.with { |redis| redis.del(pushes_since_gc_redis_shared_state_key) }
+ end
+
+ private
+
+ def pushes_since_gc_redis_shared_state_key
+ "#{self.class.name.underscore.pluralize}/#{id}/pushes_since_gc"
+ end
+end
diff --git a/app/models/license_template.rb b/app/models/license_template.rb
index bd24259984b..f6961da6d33 100644
--- a/app/models/license_template.rb
+++ b/app/models/license_template.rb
@@ -12,11 +12,12 @@ class LicenseTemplate
(fullname|name\sof\s(author|copyright\sowner))
[\>\}\]]}xi.freeze
- attr_reader :key, :name, :category, :nickname, :url, :meta
+ attr_reader :key, :name, :project, :category, :nickname, :url, :meta
- def initialize(key:, name:, category:, content:, nickname: nil, url: nil, meta: {})
+ def initialize(key:, name:, project:, category:, content:, nickname: nil, url: nil, meta: {})
@key = key
@name = name
+ @project = project
@category = category
@content = content
@nickname = nickname
@@ -24,6 +25,22 @@ class LicenseTemplate
@meta = meta
end
+ def project_id
+ project&.id
+ end
+
+ def project_path
+ project&.path
+ end
+
+ def namespace_id
+ project&.namespace&.id
+ end
+
+ def namespace_path
+ project&.namespace&.full_path
+ end
+
def popular?
category == :Popular
end
diff --git a/app/models/project.rb b/app/models/project.rb
index ce965140252..b9911fd308b 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -34,6 +34,7 @@ class Project < ApplicationRecord
include FromUnion
include IgnorableColumns
include Integration
+ include CanHousekeepRepository
include EachBatch
extend Gitlab::Cache::RequestCache
extend Gitlab::Utils::Override
@@ -2122,18 +2123,6 @@ class Project < ApplicationRecord
(auto_devops || build_auto_devops)&.predefined_variables
end
- def pushes_since_gc
- Gitlab::Redis::SharedState.with { |redis| redis.get(pushes_since_gc_redis_shared_state_key).to_i }
- end
-
- def increment_pushes_since_gc
- Gitlab::Redis::SharedState.with { |redis| redis.incr(pushes_since_gc_redis_shared_state_key) }
- end
-
- def reset_pushes_since_gc
- Gitlab::Redis::SharedState.with { |redis| redis.del(pushes_since_gc_redis_shared_state_key) }
- end
-
def route_map_for(commit_sha)
@route_maps_by_commit ||= Hash.new do |h, sha|
h[sha] = begin
@@ -2634,10 +2623,6 @@ class Project < ApplicationRecord
from && self != from
end
- def pushes_since_gc_redis_shared_state_key
- "projects/#{id}/pushes_since_gc"
- end
-
def update_project_statistics
stats = statistics || build_statistics
stats.update(namespace_id: namespace_id)
diff --git a/app/services/merge_requests/merge_service.rb b/app/services/merge_requests/merge_service.rb
index 407f2ad70f8..f4454db0af8 100644
--- a/app/services/merge_requests/merge_service.rb
+++ b/app/services/merge_requests/merge_service.rb
@@ -88,13 +88,9 @@ module MergeRequests
end
def try_merge
- merge = repository.merge(current_user, source, merge_request, commit_message)
-
- if merge_request.squash_on_merge? && Feature.enabled?(:persist_squash_commit_sha_for_squashes, project)
- merge_request.update_column(:squash_commit_sha, source)
+ repository.merge(current_user, source, merge_request, commit_message).tap do
+ merge_request.update_column(:squash_commit_sha, source) if merge_request.squash_on_merge?
end
-
- merge
rescue Gitlab::Git::PreReceiveError => e
raise MergeError,
"Something went wrong during merge pre-receive hook. #{e.message}".strip
diff --git a/app/views/projects/_service_desk_settings.html.haml b/app/views/projects/_service_desk_settings.html.haml
index 5fab242ed5c..153235c37d2 100644
--- a/app/views/projects/_service_desk_settings.html.haml
+++ b/app/views/projects/_service_desk_settings.html.haml
@@ -2,7 +2,7 @@
%section.settings.js-service-desk-setting-wrapper.no-animate#js-service-desk{ class: ('expanded' if expanded) }
.settings-header
%h4.settings-title.js-settings-toggle.js-settings-toggle-trigger-only= _('Service Desk')
- %button.btn.js-settings-toggle
+ %button.gl-button.btn.btn-default.js-settings-toggle{ type: 'button' }
= expanded ? _('Collapse') : _('Expand')
- link_start = "<a href='#{help_page_path('user/project/service_desk')}' target='_blank' rel='noopener noreferrer'>".html_safe
%p= _('Enable and disable Service Desk. Some additional configuration might be required. %{link_start}Learn more%{link_end}.').html_safe % { link_start: link_start, link_end: '</a>'.html_safe }
diff --git a/app/views/projects/edit.html.haml b/app/views/projects/edit.html.haml
index 1dfb0594b27..cde8a5f69dd 100644
--- a/app/views/projects/edit.html.haml
+++ b/app/views/projects/edit.html.haml
@@ -8,14 +8,14 @@
%section.settings.general-settings.no-animate.expanded#js-general-settings
.settings-header
%h4.settings-title.js-settings-toggle.js-settings-toggle-trigger-only= _('Naming, topics, avatar')
- %button.btn.btn-default.js-settings-toggle{ type: 'button' }= _('Collapse')
+ %button.gl-button.btn.btn-default.js-settings-toggle{ type: 'button' }= _('Collapse')
%p= _('Update your project name, topics, description, and avatar.')
.settings-content= render 'projects/settings/general'
%section.settings.sharing-permissions.no-animate#js-shared-permissions{ class: ('expanded' if expanded), data: { qa_selector: 'visibility_features_permissions_content' } }
.settings-header
%h4.settings-title.js-settings-toggle.js-settings-toggle-trigger-only= _('Visibility, project features, permissions')
- %button.btn.btn-default.js-settings-toggle{ type: 'button' }= expanded ? _('Collapse') : _('Expand')
+ %button.gl-button.btn.btn-default.js-settings-toggle{ type: 'button' }= expanded ? _('Collapse') : _('Expand')
%p= _('Choose visibility level, enable/disable project features (issues, repository, wiki, snippets) and set permissions.')
.settings-content
@@ -30,7 +30,7 @@
%section.qa-merge-request-settings.rspec-merge-request-settings.settings.merge-requests-feature.no-animate#js-merge-request-settings{ class: [('expanded' if expanded), ('hidden' if @project.project_feature.send(:merge_requests_access_level) == 0)] }
.settings-header
%h4.settings-title.js-settings-toggle.js-settings-toggle-trigger-only= _('Merge requests')
- %button.btn.btn-default.js-settings-toggle{ type: 'button' }= expanded ? _('Collapse') : _('Expand')
+ %button.gl-button.btn.btn-default.js-settings-toggle{ type: 'button' }= expanded ? _('Collapse') : _('Expand')
= render_if_exists 'projects/merge_request_settings_description_text'
.settings-content
@@ -48,8 +48,7 @@
.settings-header
%h4.settings-title.js-settings-toggle.js-settings-toggle-trigger-only
= s_('ProjectSettings|Badges')
- %button.btn.btn-default.js-settings-toggle{ type: 'button' }
- = expanded ? _('Collapse') : _('Expand')
+ %button.gl-button.btn.btn-default.js-settings-toggle{ type: 'button' }= expanded ? _('Collapse') : _('Expand')
%p
= s_('ProjectSettings|Customize this project\'s badges.')
= link_to s_('ProjectSettings|What are badges?'), help_page_path('user/project/badges')
@@ -63,7 +62,7 @@
%section.qa-advanced-settings.settings.advanced-settings.no-animate#js-project-advanced-settings{ class: ('expanded' if expanded) }
.settings-header
%h4.settings-title.js-settings-toggle.js-settings-toggle-trigger-only= _('Advanced')
- %button.btn.btn-default.js-settings-toggle{ type: 'button' }= expanded ? _('Collapse') : _('Expand')
+ %button.gl-button.btn.btn-default.js-settings-toggle{ type: 'button' }= expanded ? _('Collapse') : _('Expand')
%p= _('Housekeeping, export, path, transfer, remove, archive.')
.settings-content
diff --git a/app/views/shared/issuable/form/_template_selector.html.haml b/app/views/shared/issuable/form/_template_selector.html.haml
index bf34ea4a1b2..a58bde5aa9e 100644
--- a/app/views/shared/issuable/form/_template_selector.html.haml
+++ b/app/views/shared/issuable/form/_template_selector.html.haml
@@ -1,9 +1,9 @@
- issuable = local_assigns.fetch(:issuable, nil)
-- return unless issuable && issuable_templates(issuable).any?
+- return unless issuable && issuable_templates(ref_project, issuable.class.name.underscore).any?
.issuable-form-select-holder.selectbox.form-group
- .js-issuable-selector-wrap{ data: { issuable_type: issuable.to_ability_name } }
+ .js-issuable-selector-wrap{ data: { issuable_type: issuable.to_ability_name.pluralize } }
= template_dropdown_tag(issuable) do
%ul.dropdown-footer-list
%li
diff --git a/app/views/shared/issuable/form/_title.html.haml b/app/views/shared/issuable/form/_title.html.haml
index 98c9f73fa3a..cb4b712ffbe 100644
--- a/app/views/shared/issuable/form/_title.html.haml
+++ b/app/views/shared/issuable/form/_title.html.haml
@@ -1,7 +1,7 @@
- issuable = local_assigns.fetch(:issuable)
- has_wip_commits = local_assigns.fetch(:has_wip_commits)
- form = local_assigns.fetch(:form)
-- no_issuable_templates = issuable_templates(issuable).empty?
+- no_issuable_templates = issuable_templates(ref_project, issuable.class.name.underscore).empty?
- div_class = no_issuable_templates ? 'col-sm-10' : 'col-sm-7 col-lg-8'
- toggle_wip_link_start = '<a href="" class="js-toggle-wip">'
- toggle_wip_link_end = '</a>'
diff --git a/app/workers/all_queues.yml b/app/workers/all_queues.yml
index 9ac10aa2d7c..4c4a314a1e6 100644
--- a/app/workers/all_queues.yml
+++ b/app/workers/all_queues.yml
@@ -1094,7 +1094,7 @@
:idempotent: true
:tags: []
- :name: pipeline_background:ci_daily_build_group_report_results
- :feature_category: :continuous_integration
+ :feature_category: :code_testing
:has_external_dependencies:
:urgency: :low
:resource_boundary: :unknown
@@ -1102,7 +1102,7 @@
:idempotent: true
:tags: []
- :name: pipeline_background:ci_pipeline_artifacts_coverage_report
- :feature_category: :continuous_integration
+ :feature_category: :code_testing
:has_external_dependencies:
:urgency: :low
:resource_boundary: :unknown
diff --git a/app/workers/ci/daily_build_group_report_results_worker.rb b/app/workers/ci/daily_build_group_report_results_worker.rb
index a6d3c485e24..687cadc6366 100644
--- a/app/workers/ci/daily_build_group_report_results_worker.rb
+++ b/app/workers/ci/daily_build_group_report_results_worker.rb
@@ -5,6 +5,8 @@ module Ci
include ApplicationWorker
include PipelineBackgroundQueue
+ feature_category :code_testing
+
idempotent!
def perform(pipeline_id)
diff --git a/app/workers/ci/pipeline_artifacts/coverage_report_worker.rb b/app/workers/ci/pipeline_artifacts/coverage_report_worker.rb
index f8c9994a746..4de56f54f44 100644
--- a/app/workers/ci/pipeline_artifacts/coverage_report_worker.rb
+++ b/app/workers/ci/pipeline_artifacts/coverage_report_worker.rb
@@ -6,6 +6,8 @@ module Ci
include ApplicationWorker
include PipelineBackgroundQueue
+ feature_category :code_testing
+
idempotent!
def perform(pipeline_id)
diff --git a/changelogs/unreleased/21686_persist_squash_commit_sha.yml b/changelogs/unreleased/21686_persist_squash_commit_sha.yml
new file mode 100644
index 00000000000..c95028b8492
--- /dev/null
+++ b/changelogs/unreleased/21686_persist_squash_commit_sha.yml
@@ -0,0 +1,5 @@
+---
+title: Persist 'squash_commit_sha' when squashing
+merge_request: 51074
+author:
+type: added
diff --git a/changelogs/unreleased/292498-webide-switch-before-closing.yml b/changelogs/unreleased/292498-webide-switch-before-closing.yml
new file mode 100644
index 00000000000..310dd76275c
--- /dev/null
+++ b/changelogs/unreleased/292498-webide-switch-before-closing.yml
@@ -0,0 +1,5 @@
+---
+title: In WebIDE switch files before closing the active one
+merge_request: 51483
+author:
+type: fixed
diff --git a/changelogs/unreleased/track-ci-template-usage-by-default.yml b/changelogs/unreleased/track-ci-template-usage-by-default.yml
new file mode 100644
index 00000000000..64d08d7aa51
--- /dev/null
+++ b/changelogs/unreleased/track-ci-template-usage-by-default.yml
@@ -0,0 +1,5 @@
+---
+title: Instrument CI template usage across projects
+merge_request: 51391
+author:
+type: added
diff --git a/changelogs/unreleased/yo-master-patch-52337.yml b/changelogs/unreleased/yo-master-patch-52337.yml
new file mode 100644
index 00000000000..01578c618e2
--- /dev/null
+++ b/changelogs/unreleased/yo-master-patch-52337.yml
@@ -0,0 +1,5 @@
+---
+title: Update toggle button in repo general settings
+merge_request: 51036
+author: Yogi (@yo)
+type: other
diff --git a/config/feature_flags/development/persist_squash_commit_sha_for_squashes.yml b/config/feature_flags/development/persist_squash_commit_sha_for_squashes.yml
deleted file mode 100644
index 835437278c4..00000000000
--- a/config/feature_flags/development/persist_squash_commit_sha_for_squashes.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: persist_squash_commit_sha_for_squashes
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/50178
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/294243
-milestone: '13.8'
-type: development
-group: group::source code
-default_enabled: false
diff --git a/config/feature_flags/development/usage_data_track_ci_templates_unique_projects.yml b/config/feature_flags/development/usage_data_track_ci_templates_unique_projects.yml
index f3ddfe1f3c1..306e37ac308 100644
--- a/config/feature_flags/development/usage_data_track_ci_templates_unique_projects.yml
+++ b/config/feature_flags/development/usage_data_track_ci_templates_unique_projects.yml
@@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/296880
milestone: '13.8'
type: development
group: group::configure
-default_enabled: false
+default_enabled: true
diff --git a/danger/roulette/Dangerfile b/danger/roulette/Dangerfile
index 753fff940e4..424114a3d33 100644
--- a/danger/roulette/Dangerfile
+++ b/danger/roulette/Dangerfile
@@ -15,7 +15,8 @@ CATEGORY_TABLE_HEADER = <<MARKDOWN
To spread load more evenly across eligible reviewers, Danger has picked a candidate for each
review slot, based on their timezone. Feel free to
[override these selections](https://about.gitlab.com/handbook/engineering/projects/#gitlab)
-if you think someone else would be better-suited, or the chosen person is unavailable.
+if you think someone else would be better-suited
+or use the [GitLab Review Workload Dashboard](https://gitlab-org.gitlab.io/gitlab-roulette/) to find other available reviewers.
To read more on how to use the reviewer roulette, please take a look at the
[Engineering workflow](https://about.gitlab.com/handbook/engineering/workflow/#basics)
diff --git a/doc/development/feature_flags/process.md b/doc/development/feature_flags/process.md
index 2e3680bb103..7e6299c193c 100644
--- a/doc/development/feature_flags/process.md
+++ b/doc/development/feature_flags/process.md
@@ -148,3 +148,30 @@ they speed up the process as managing incidents now becomes _much_ easier. Once
continuous deployments are easier to perform, the time to iterate on a feature
is reduced even further, as you no longer need to wait weeks before your changes
are available on GitLab.com.
+
+### The benefits of feature flags
+
+It may seem like feature flags are configuration, which goes against our [convention-over-configuration](https://about.gitlab.com/handbook/product/product-principles/#convention-over-configuration)
+principle. However, configuration is by definition something that is user-manageable.
+Feature flags are not intended to be user-editable. Instead, they are intended as a tool for Engineers
+and Site Reliability Engineers to use to de-risk their changes. Feature flags are the shim that gets us
+to Continuous Delivery with our mono repo and without having to deploy the entire codebase on every change.
+Feature flags are created to ensure that we can safely rollout our work on our terms.
+If we use Feature Flags as a configuration, we are doing it wrong and are indeed in violation of our
+principles. If something needs to be configured, we should intentionally make it configuration from the
+first moment.
+
+Some of the benefits of using development-type feature flags are:
+
+1. It enables Continuous Delivery for GitLab.com.
+1. It significantly reduces Mean-Time-To-Recovery.
+1. It helps engineers to monitor and reduce the impact of their changes gradually, at any scale,
+ allowing us to be more metrics-driven and execute good DevOps practices, [shifting some responsibility "left"](https://devops.com/why-its-time-for-site-reliability-engineering-to-shift-left/).
+1. Controlled feature rollout timing: without feature flags, we would need to wait until a specific
+ deployment was complete (which at GitLab could be at any time).
+1. Increased psychological safety: when a feature flag is used, an engineer has the confidence that if anything goes wrong they can quickly disable the code and minimize the impact of a change that might be risky.
+1. Improved throughput: when a change is less risky because a flag exists, theoretical tests about
+ scalability can potentially become unnecessary or less important. This allows an engineer to
+ potentially test a feature on a small project, monitor the impact, and proceed. The alternative might
+ be to build complex benchmarks locally, or on staging, or on another GitLab deployment, which has an
+ outsized impact on the time it can take to build and release a feature.
diff --git a/doc/development/features_inside_dot_gitlab.md b/doc/development/features_inside_dot_gitlab.md
index 08adb7faeb2..36b9064bbc4 100644
--- a/doc/development/features_inside_dot_gitlab.md
+++ b/doc/development/features_inside_dot_gitlab.md
@@ -10,8 +10,8 @@ We have implemented standard features that depend on configuration files in the
When implementing new features, please refer to these existing features to avoid conflicts:
- [Custom Dashboards](../operations/metrics/dashboards/index.md#add-a-new-dashboard-to-your-project): `.gitlab/dashboards/`.
-- [Issue Templates](../user/project/description_templates.md#creating-issue-templates): `.gitlab/issue_templates/`.
-- [Merge Request Templates](../user/project/description_templates.md#creating-merge-request-templates): `.gitlab/merge_request_templates/`.
+- [Issue Templates](../user/project/description_templates.md#create-an-issue-template): `.gitlab/issue_templates/`.
+- [Merge Request Templates](../user/project/description_templates.md#create-a-merge-request-template): `.gitlab/merge_request_templates/`.
- [GitLab Kubernetes Agents](https://gitlab.com/gitlab-org/cluster-integration/gitlab-agent/-/blob/master/doc/configuration_repository.md#layout): `.gitlab/agents/`.
- [CODEOWNERS](../user/project/code_owners.md#how-to-set-up-code-owners): `.gitlab/CODEOWNERS`.
- [Route Maps](../ci/review_apps/#route-maps): `.gitlab/route-map.yml`.
diff --git a/doc/operations/incident_management/incidents.md b/doc/operations/incident_management/incidents.md
index 450ee6620a4..78e90d5f1c4 100644
--- a/doc/operations/incident_management/incidents.md
+++ b/doc/operations/incident_management/incidents.md
@@ -52,7 +52,7 @@ With Maintainer or higher [permissions](../../user/permissions.md), you can enab
1. Navigate to **Settings > Operations > Incidents** and expand **Incidents**.
1. Check the **Create an incident** checkbox.
1. To customize the incident, select an
- [issue template](../../user/project/description_templates.md#creating-issue-templates).
+ [issue template](../../user/project/description_templates.md#create-an-issue-template).
1. To send [an email notification](alert_notifications.md#email-notifications) to users
with [Developer permissions](../../user/permissions.md), select
**Send a separate email notification to Developers**. Email notifications are
diff --git a/doc/user/group/index.md b/doc/user/group/index.md
index 74406d3e5cf..36410f2e1a7 100644
--- a/doc/user/group/index.md
+++ b/doc/user/group/index.md
@@ -726,6 +726,9 @@ To enable this feature, navigate to the group settings page, expand the
![Group file template settings](img/group_file_template_settings.png)
+To learn how to create templates for issues and merge requests, visit
+[Description templates](../project/description_templates.md).
+
#### Group-level project templates **(PREMIUM)**
Define project templates at a group level by setting a group as the template source.
diff --git a/doc/user/project/description_templates.md b/doc/user/project/description_templates.md
index 3108bdda7a0..fe1ab292d44 100644
--- a/doc/user/project/description_templates.md
+++ b/doc/user/project/description_templates.md
@@ -6,16 +6,13 @@ info: To determine the technical writer assigned to the Stage/Group associated w
# Description templates
->[Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/4981) in GitLab 8.11.
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/4981) in GitLab 8.11.
We all know that a properly submitted issue is more likely to be addressed in
a timely manner by the developers of a project.
-Description templates allow you to define context-specific templates for issue
-and merge request description fields for your project, as well as help filter
-out a lot of unnecessary noise from issues.
-
-## Overview
+With description templates, you can define context-specific templates for issue and merge request
+description fields for your project, and filter out a lot of unnecessary noise from issues.
By using the description templates, users that create a new issue or merge
request can select a description template to help them communicate with other
@@ -28,7 +25,10 @@ Description templates must be written in [Markdown](../markdown.md) and stored
in your project's repository under a directory named `.gitlab`. Only the
templates of the default branch are taken into account.
-## Use-cases
+To learn how to create templates for various file types in groups, visit
+[Group file templates](../group/index.md#group-file-templates).
+
+## Use cases
- Add a template to be used in every issue for a specific project,
giving instructions and guidelines, requiring for information specific to that subject.
@@ -40,7 +40,7 @@ templates of the default branch are taken into account.
- You can also create issues and merge request templates for different
stages of your workflow, for example, feature proposal, feature improvement, or a bug report.
-## Creating issue templates
+## Create an issue template
Create a new Markdown (`.md`) file inside the `.gitlab/issue_templates/`
directory in your repository. Commit and push to your default branch.
@@ -65,13 +65,13 @@ To create the `.gitlab/issue_templates` directory:
To check if this has worked correctly, [create a new issue](issues/managing_issues.md#create-a-new-issue)
and see if you can choose a description template.
-## Creating merge request templates
+## Create a merge request template
Similarly to issue templates, create a new Markdown (`.md`) file inside the
`.gitlab/merge_request_templates/` directory in your repository. Commit and
push to your default branch.
-## Using the templates
+## Use the templates
Let's take for example that you've created the file `.gitlab/issue_templates/Bug.md`.
This enables the `Bug` dropdown option when creating or editing issues. When
@@ -80,15 +80,46 @@ to the issue description field. The **Reset template** button discards any
changes you made after picking the template and returns it to its initial status.
NOTE:
-You can create short-cut links to create an issue using a designated template. For example: `https://gitlab.com/gitlab-org/gitlab/-/issues/new?issuable_template=Feature%20proposal`.
+You can create shortcut links to create an issue using a designated template.
+For example: `https://gitlab.com/gitlab-org/gitlab/-/issues/new?issuable_template=Feature%20proposal`.
![Description templates](img/description_templates.png)
-## Setting a default template for merge requests and issues **(STARTER)**
+### Set an issue and merge request description template at group level **(PREMIUM)**
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/46222) in GitLab 13.8.
+
+Templates are most useful, because you can create a template once and use it multiple times.
+To re-use templates [you've created](../project/description_templates.md#create-an-issue-template):
+
+1. Go to your project's `Settings > General > Templates`.
+1. From the dropdown, select your template project as the template repository at group level.
+
+![Group template settings](../group/img/group_file_template_settings.png)
+
+### Set an issue and merge request description template at instance level **(PREMIUM ONLY)**
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/46222) in GitLab 13.8.
+
+Similar to group templates, issue and merge request templates can also be set up at the instance level.
+This results in those templates being available in all projects within the instance.
+Only instance administrators can set instance-level templates.
+
+To set the instance-level description template repository:
+
+1. Select the **Admin Area** icon (**{admin}**).
+1. Select **Templates**.
+1. From the dropdown, select your template project as the template repository at instance level.
-> - This feature was introduced before [description templates](#overview) and is available in [GitLab Starter](https://about.gitlab.com/pricing/). It can be enabled in the project's settings.
-> - Templates for issues were [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/28) in GitLab EE 8.1.
-> - Templates for merge requests were [introduced](https://gitlab.com/gitlab-org/gitlab/commit/7478ece8b48e80782b5465b96c79f85cc91d391b) in GitLab EE 6.9.
+Learn more about [instance template repository](../admin_area/settings/instance_template_repository.md).
+
+![Setting templates in the Admin Area](../admin_area/settings/img/file_template_admin_area.png)
+
+### Set a default template for merge requests and issues **(STARTER)**
+
+> - This feature was introduced before [description templates](#description-templates) and is available in [GitLab Starter](https://about.gitlab.com/pricing/). It can be enabled in the project's settings.
+> - Templates for issues [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/28) in GitLab EE 8.1.
+> - Templates for merge requests [introduced](https://gitlab.com/gitlab-org/gitlab/commit/7478ece8b48e80782b5465b96c79f85cc91d391b) in GitLab EE 6.9.
The visibility of issues and/or merge requests should be set to either "Everyone
with access" or "Only Project Members" in your project's **Settings / Visibility, project features, permissions** section, otherwise the
@@ -113,52 +144,47 @@ pre-filled with the text you entered in the template(s).
## Description template example
-We make use of Description Templates for Issues and Merge Requests within the GitLab Community
-Edition project. Please refer to the [`.gitlab` folder](https://gitlab.com/gitlab-org/gitlab/tree/master/.gitlab)
+We make use of description templates for issues and merge requests in the GitLab project.
+Please refer to the [`.gitlab` folder](https://gitlab.com/gitlab-org/gitlab/tree/master/.gitlab)
for some examples.
NOTE:
-It's possible to use [quick actions](quick_actions.md) within description templates to quickly add
+It's possible to use [quick actions](quick_actions.md) in description templates to quickly add
labels, assignees, and milestones. The quick actions are only executed if the user submitting
the issue or merge request has the permissions to perform the relevant actions.
Here is an example of a Bug report template:
-```plaintext
-Summary
+```markdown
+## Summary
(Summarize the bug encountered concisely)
-
-Steps to reproduce
+## Steps to reproduce
(How one can reproduce the issue - this is very important)
+## Example Project
-Example Project
-
-(If possible, please create an example project here on GitLab.com that exhibits the problematic behaviour, and link to it here in the bug report)
+(If possible, please create an example project here on GitLab.com that exhibits the problematic
+behaviour, and link to it here in the bug report.
+If you are using an older version of GitLab, this will also determine whether the bug has been fixed
+in a more recent version)
-(If you are using an older version of GitLab, this will also determine whether the bug has been fixed in a more recent version)
-
-
-What is the current bug behavior?
+## What is the current bug behavior?
(What actually happens)
-
-What is the expected correct behavior?
+## What is the expected correct behavior?
(What you should see instead)
+## Relevant logs and/or screenshots
-Relevant logs and/or screenshots
-
-(Paste any relevant logs - please use code blocks (```) to format console output,
-logs, and code as it's very hard to read otherwise.)
-
+(Paste any relevant logs - please use code blocks (```) to format console output, logs, and code, as
+it's very hard to read otherwise.)
-Possible fixes
+## Possible fixes
(If you can, link to the line of code that might be responsible for the problem)
diff --git a/doc/user/project/issues/index.md b/doc/user/project/issues/index.md
index 74311eefd83..014e903a012 100644
--- a/doc/user/project/issues/index.md
+++ b/doc/user/project/issues/index.md
@@ -217,7 +217,7 @@ You can then see issue statuses in the [issue list](#issues-list) and the
## Other Issue actions
-- [Create an issue from a template](../../project/description_templates.md#using-the-templates)
+- [Create an issue from a template](../../project/description_templates.md#use-the-templates)
- [Set a due date](due_dates.md)
- [Bulk edit issues](../bulk_editing.md) - From the Issues List, select multiple issues
in order to change their status, assignee, milestone, or labels in bulk.
diff --git a/doc/user/project/static_site_editor/index.md b/doc/user/project/static_site_editor/index.md
index 07f122a7828..f7ccbf8a565 100644
--- a/doc/user/project/static_site_editor/index.md
+++ b/doc/user/project/static_site_editor/index.md
@@ -102,7 +102,7 @@ To edit a file:
in the bottom-right corner.
1. When you're done, click **Submit changes...**.
1. (Optional) Adjust the default title and description of the merge request that will be submitted
- with your changes. Alternatively, select a [merge request template](../../../user/project/description_templates.md#creating-merge-request-templates)
+ with your changes. Alternatively, select a [merge request template](../../../user/project/description_templates.md#create-a-merge-request-template)
from the dropdown menu and edit it accordingly.
1. Click **Submit changes**.
1. A new merge request is automatically created and you can assign a colleague for review.
diff --git a/lib/api/project_templates.rb b/lib/api/project_templates.rb
index 19244ed697f..95bb35e0dd9 100644
--- a/lib/api/project_templates.rb
+++ b/lib/api/project_templates.rb
@@ -45,9 +45,10 @@ module API
get ':id/templates/:type/:name', requirements: TEMPLATE_NAMES_ENDPOINT_REQUIREMENTS do
begin
- template = TemplateFinder
- .build(params[:type], user_project, name: params[:name])
- .execute
+ template = TemplateFinder.build(
+ params[:type], user_project, name: params[:name],
+ source_template_project_id: params[:source_template_project_id]
+ ).execute
rescue ::Gitlab::Template::Finders::RepoTemplateFinder::FileNotFoundError
not_found!('Template')
end
diff --git a/lib/gitlab/template/base_template.rb b/lib/gitlab/template/base_template.rb
index b659bff52ad..c5c4277719c 100644
--- a/lib/gitlab/template/base_template.rb
+++ b/lib/gitlab/template/base_template.rb
@@ -8,6 +8,7 @@ module Gitlab
def initialize(path, project = nil, category: nil)
@path = path
@category = category
+ @project = project
@finder = self.class.finder(project)
end
@@ -31,6 +32,22 @@ module Gitlab
# override with a comment to be placed at the top of the blob.
end
+ def project_id
+ @project&.id
+ end
+
+ def project_path
+ @project&.path
+ end
+
+ def namespace_id
+ @project&.namespace&.id
+ end
+
+ def namespace_path
+ @project&.namespace&.full_path
+ end
+
# Present for compatibility with license templates, which can replace text
# like `[fullname]` with a user-specified string. This is a no-op for
# other templates
@@ -82,11 +99,11 @@ module Gitlab
raise NotImplementedError
end
- def by_category(category, project = nil)
+ def by_category(category, project = nil, empty_category_title: nil)
directory = category_directory(category)
files = finder(project).list_files_for(directory)
- files.map { |f| new(f, project, category: category) }.sort
+ files.map { |f| new(f, project, category: category.presence || empty_category_title) }.sort
end
def category_directory(category)
diff --git a/lib/gitlab/template/issue_template.rb b/lib/gitlab/template/issue_template.rb
index 01b191733d4..244231acb65 100644
--- a/lib/gitlab/template/issue_template.rb
+++ b/lib/gitlab/template/issue_template.rb
@@ -15,6 +15,10 @@ module Gitlab
def finder(project)
Gitlab::Template::Finders::RepoTemplateFinder.new(project, self.base_dir, self.extension, self.categories)
end
+
+ def by_category(category, project = nil, empty_category_title: nil)
+ super(category, project, empty_category_title: _('Project Templates'))
+ end
end
end
end
diff --git a/lib/gitlab/template/merge_request_template.rb b/lib/gitlab/template/merge_request_template.rb
index 357b31cd82e..5a29d770f53 100644
--- a/lib/gitlab/template/merge_request_template.rb
+++ b/lib/gitlab/template/merge_request_template.rb
@@ -15,6 +15,10 @@ module Gitlab
def finder(project)
Gitlab::Template::Finders::RepoTemplateFinder.new(project, self.base_dir, self.extension, self.categories)
end
+
+ def by_category(category, project = nil, empty_category_title: nil)
+ super(category, project, empty_category_title: _('Project Templates'))
+ end
end
end
end
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 4951cd4aa3d..a96f1f00c43 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -20790,9 +20790,6 @@ msgstr ""
msgid "Pipelines|More Information"
msgstr ""
-msgid "Pipelines|No CI file found in this repository, please add one."
-msgstr ""
-
msgid "Pipelines|No triggers have been created yet. Add one using the form above."
msgstr ""
@@ -20805,9 +20802,6 @@ msgstr ""
msgid "Pipelines|Project cache successfully reset."
msgstr ""
-msgid "Pipelines|Repository does not have a default branch, please set one."
-msgstr ""
-
msgid "Pipelines|Revoke"
msgstr ""
@@ -20829,6 +20823,9 @@ msgstr ""
msgid "Pipelines|There are currently no pipelines."
msgstr ""
+msgid "Pipelines|There is no %{filePath} file in this repository, please add one and visit the Pipeline Editor again."
+msgstr ""
+
msgid "Pipelines|There was an error fetching the pipelines. Try again in a few moments or contact your support team."
msgstr ""
@@ -21882,6 +21879,9 @@ msgstr ""
msgid "Project ID"
msgstr ""
+msgid "Project Templates"
+msgstr ""
+
msgid "Project URL"
msgstr ""
diff --git a/scripts/verify-tff-mapping b/scripts/verify-tff-mapping
index 1c66e19df50..8bf25ea3b5f 100755
--- a/scripts/verify-tff-mapping
+++ b/scripts/verify-tff-mapping
@@ -41,8 +41,8 @@ tests = [
{
explanation: 'Tooling should map to respective spec',
- source: 'tooling/lib/tooling/test_file_finder.rb',
- expected: ['spec/tooling/lib/tooling/test_file_finder_spec.rb']
+ source: 'tooling/lib/tooling/helm3_client.rb',
+ expected: ['spec/tooling/lib/tooling/helm3_client_spec.rb']
},
{
diff --git a/spec/controllers/projects/templates_controller_spec.rb b/spec/controllers/projects/templates_controller_spec.rb
index 01593f4133c..13f329cb0b6 100644
--- a/spec/controllers/projects/templates_controller_spec.rb
+++ b/spec/controllers/projects/templates_controller_spec.rb
@@ -160,12 +160,12 @@ RSpec.describe Projects::TemplatesController do
end
shared_examples 'template names request' do
- it 'returns the template names' do
+ it 'returns the template names', :aggregate_failures do
get(:names, params: { namespace_id: project.namespace, template_type: template_type, project_id: project }, format: :json)
expect(response).to have_gitlab_http_status(:ok)
- expect(json_response.size).to eq(2)
- expect(json_response).to match(expected_template_names)
+ expect(json_response['Project Templates'].size).to eq(2)
+ expect(json_response['Project Templates'].map { |x| { "name" => x['name'] } }).to match(expected_template_names)
end
it 'fails for user with no access' do
diff --git a/spec/features/issues/issue_state_spec.rb b/spec/features/issues/issue_state_spec.rb
index 9145089f5fc..d5a115433aa 100644
--- a/spec/features/issues/issue_state_spec.rb
+++ b/spec/features/issues/issue_state_spec.rb
@@ -41,7 +41,7 @@ RSpec.describe 'issue state', :js do
end
end
- describe 'when open' do
+ describe 'when open', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/297348' do
context 'when clicking the top `Close issue` button', :aggregate_failures do
let(:open_issue) { create(:issue, project: project) }
@@ -63,7 +63,7 @@ RSpec.describe 'issue state', :js do
end
end
- describe 'when closed' do
+ describe 'when closed', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/297201' do
context 'when clicking the top `Reopen issue` button', :aggregate_failures do
let(:closed_issue) { create(:issue, project: project, state: 'closed') }
diff --git a/spec/features/projects/releases/user_creates_release_spec.rb b/spec/features/projects/releases/user_creates_release_spec.rb
index 0a5f7cc7edd..2acdf983cf2 100644
--- a/spec/features/projects/releases/user_creates_release_spec.rb
+++ b/spec/features/projects/releases/user_creates_release_spec.rb
@@ -36,7 +36,7 @@ RSpec.describe 'User creates release', :js do
expect(page.find('.ref-selector button')).to have_content(project.default_branch)
end
- context 'when the "Save release" button is clicked' do
+ context 'when the "Save release" button is clicked', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/297507' do
let(:tag_name) { 'v1.0' }
let(:release_title) { 'A most magnificent release' }
let(:release_notes) { 'Best. Release. **Ever.** :rocket:' }
diff --git a/spec/frontend/editor/editor_ci_schema_ext_spec.js b/spec/frontend/editor/editor_ci_schema_ext_spec.js
index 4d65b77cdfc..9dd88aad7e6 100644
--- a/spec/frontend/editor/editor_ci_schema_ext_spec.js
+++ b/spec/frontend/editor/editor_ci_schema_ext_spec.js
@@ -1,4 +1,5 @@
import { languages } from 'monaco-editor';
+import { TEST_HOST } from 'helpers/test_constants';
import EditorLite from '~/editor/editor_lite';
import { CiSchemaExtension } from '~/editor/extensions/editor_ci_schema_ext';
import { EXTENSION_CI_SCHEMA_FILE_NAME_MATCH } from '~/editor/constants';
@@ -9,6 +10,7 @@ describe('~/editor/editor_ci_config_ext', () => {
let editor;
let instance;
let editorEl;
+ let originalGitlabUrl;
const createMockEditor = ({ blobPath = defaultBlobPath } = {}) => {
setFixtures('<div id="editor"></div>');
@@ -22,6 +24,15 @@ describe('~/editor/editor_ci_config_ext', () => {
instance.use(new CiSchemaExtension());
};
+ beforeAll(() => {
+ originalGitlabUrl = gon.gitlab_url;
+ gon.gitlab_url = TEST_HOST;
+ });
+
+ afterAll(() => {
+ gon.gitlab_url = originalGitlabUrl;
+ });
+
beforeEach(() => {
createMockEditor();
});
@@ -73,7 +84,7 @@ describe('~/editor/editor_ci_config_ext', () => {
});
expect(getConfiguredYmlSchema()).toEqual({
- uri: `/${mockProjectNamespace}/${mockProjectPath}/-/schema/${mockRef}/${EXTENSION_CI_SCHEMA_FILE_NAME_MATCH}`,
+ uri: `${TEST_HOST}/${mockProjectNamespace}/${mockProjectPath}/-/schema/${mockRef}/${EXTENSION_CI_SCHEMA_FILE_NAME_MATCH}`,
fileMatch: [defaultBlobPath],
});
});
@@ -87,7 +98,7 @@ describe('~/editor/editor_ci_config_ext', () => {
});
expect(getConfiguredYmlSchema()).toEqual({
- uri: `/${mockProjectNamespace}/${mockProjectPath}/-/schema/master/${EXTENSION_CI_SCHEMA_FILE_NAME_MATCH}`,
+ uri: `${TEST_HOST}/${mockProjectNamespace}/${mockProjectPath}/-/schema/master/${EXTENSION_CI_SCHEMA_FILE_NAME_MATCH}`,
fileMatch: ['another-ci-filename.yml'],
});
});
diff --git a/spec/frontend/ide/stores/actions/file_spec.js b/spec/frontend/ide/stores/actions/file_spec.js
index 71166234363..9d367714bbe 100644
--- a/spec/frontend/ide/stores/actions/file_spec.js
+++ b/spec/frontend/ide/stores/actions/file_spec.js
@@ -75,7 +75,7 @@ describe('IDE store file actions', () => {
});
});
- it('closes file & opens next available file', () => {
+ it('switches to the next available file before closing the current one ', () => {
const f = file('newOpenFile');
store.state.openFiles.push(f);
@@ -90,10 +90,12 @@ describe('IDE store file actions', () => {
});
it('removes file if it pending', () => {
- store.state.openFiles.push({
- ...localFile,
- pending: true,
- });
+ store.state.openFiles = [
+ {
+ ...localFile,
+ pending: true,
+ },
+ ];
return store.dispatch('closeFile', localFile).then(() => {
expect(store.state.openFiles.length).toBe(0);
diff --git a/spec/frontend/incidents_settings/components/__snapshots__/alerts_form_spec.js.snap b/spec/frontend/incidents_settings/components/__snapshots__/alerts_form_spec.js.snap
index c3fd4a9bab2..1d34ed3c9ba 100644
--- a/spec/frontend/incidents_settings/components/__snapshots__/alerts_form_spec.js.snap
+++ b/spec/frontend/incidents_settings/components/__snapshots__/alerts_form_spec.js.snap
@@ -35,7 +35,7 @@ exports[`Alert integration settings form default state should match the default
Incident template (optional)
<gl-link-stub
- href="/help/user/project/description_templates#creating-issue-templates"
+ href="/help/user/project/description_templates#create-an-issue-template"
target="_blank"
>
<gl-icon-stub
diff --git a/spec/frontend/issue_show/components/app_spec.js b/spec/frontend/issue_show/components/app_spec.js
index ec2055ca7d1..74d195e1eb2 100644
--- a/spec/frontend/issue_show/components/app_spec.js
+++ b/spec/frontend/issue_show/components/app_spec.js
@@ -423,7 +423,9 @@ describe('Issuable output', () => {
});
it('shows the form if template names request is successful', () => {
- const mockData = [{ name: 'Bug' }];
+ const mockData = {
+ test: [{ name: 'test', id: 'test', project_path: '/', namespace_path: '/' }],
+ };
mock.onGet('/issuable-templates-path').reply(() => Promise.resolve([200, mockData]));
return wrapper.vm.requestTemplatesAndShowForm().then(() => {
diff --git a/spec/frontend/issue_show/components/fields/description_template_spec.js b/spec/frontend/issue_show/components/fields/description_template_spec.js
index 9ebab31f1ad..446c03f70e1 100644
--- a/spec/frontend/issue_show/components/fields/description_template_spec.js
+++ b/spec/frontend/issue_show/components/fields/description_template_spec.js
@@ -14,7 +14,10 @@ describe('Issue description template component', () => {
vm = new Component({
propsData: {
formState,
- issuableTemplates: [{ name: 'test' }],
+ issuableTemplates: {
+ test: [{ name: 'test', id: 'test', project_path: '/', namespace_path: '/' }],
+ },
+ projectId: 1,
projectPath: '/',
projectNamespace: '/',
},
@@ -23,7 +26,7 @@ describe('Issue description template component', () => {
it('renders templates as JSON array in data attribute', () => {
expect(vm.$el.querySelector('.js-issuable-selector').getAttribute('data-data')).toBe(
- '[{"name":"test"}]',
+ '{"test":[{"name":"test","id":"test","project_path":"/","namespace_path":"/"}]}',
);
});
diff --git a/spec/frontend/issue_show/components/form_spec.js b/spec/frontend/issue_show/components/form_spec.js
index 4e123f606f6..3a38883d1ec 100644
--- a/spec/frontend/issue_show/components/form_spec.js
+++ b/spec/frontend/issue_show/components/form_spec.js
@@ -19,6 +19,7 @@ describe('Inline edit form component', () => {
markdownPreviewPath: '/',
markdownDocsPath: '/',
projectPath: '/',
+ projectId: 1,
projectNamespace: '/',
};
@@ -42,7 +43,11 @@ describe('Inline edit form component', () => {
});
it('renders template selector when templates exists', () => {
- createComponent({ issuableTemplates: ['test'] });
+ createComponent({
+ issuableTemplates: {
+ test: [{ name: 'test', id: 'test', project_path: 'test', namespace_path: 'test' }],
+ },
+ });
expect(vm.$el.querySelector('.js-issuable-selector-wrap')).not.toBeNull();
});
diff --git a/spec/frontend/issue_show/mock_data.js b/spec/frontend/issue_show/mock_data.js
index 5a31a550088..fd08c95b454 100644
--- a/spec/frontend/issue_show/mock_data.js
+++ b/spec/frontend/issue_show/mock_data.js
@@ -52,6 +52,7 @@ export const appProps = {
markdownDocsPath: '/',
projectNamespace: '/',
projectPath: '/',
+ projectId: 1,
issuableTemplateNamesPath: '/issuable-templates-path',
zoomMeetingUrl,
publishedIncidentUrl,
diff --git a/spec/frontend/pipeline_editor/components/lint/ci_lint_spec.js b/spec/frontend/pipeline_editor/components/lint/ci_lint_spec.js
index f2d33bd2ad5..5ccf4bbdab4 100644
--- a/spec/frontend/pipeline_editor/components/lint/ci_lint_spec.js
+++ b/spec/frontend/pipeline_editor/components/lint/ci_lint_spec.js
@@ -23,6 +23,7 @@ describe('~/pipeline_editor/components/lint/ci_lint.vue', () => {
const findAlert = () => wrapper.find(GlAlert);
const findLintParameters = () => findAllByTestId('ci-lint-parameter');
const findLintParameterAt = (i) => findLintParameters().at(i);
+ const findLintValueAt = (i) => findAllByTestId('ci-lint-value').at(i);
afterEach(() => {
wrapper.destroy();
@@ -50,6 +51,20 @@ describe('~/pipeline_editor/components/lint/ci_lint.vue', () => {
expect(findLintParameterAt(2).text()).toBe('Build Job - job_build');
});
+ it('displays jobs details', () => {
+ expect(findLintParameters()).toHaveLength(3);
+
+ expect(findLintValueAt(0).text()).toMatchInterpolatedText(
+ 'echo "test 1" Only policy: branches, tags When: on_success',
+ );
+ expect(findLintValueAt(1).text()).toMatchInterpolatedText(
+ 'echo "test 2" Only policy: branches, tags When: on_success',
+ );
+ expect(findLintValueAt(2).text()).toMatchInterpolatedText(
+ 'echo "build" Only policy: branches, tags When: on_success',
+ );
+ });
+
it('displays invalid results', () => {
createComponent(
{
diff --git a/spec/frontend/pipeline_editor/graphql/__snapshots__/resolvers_spec.js.snap b/spec/frontend/pipeline_editor/graphql/__snapshots__/resolvers_spec.js.snap
index d7d4d0af90c..8670c44f6f6 100644
--- a/spec/frontend/pipeline_editor/graphql/__snapshots__/resolvers_spec.js.snap
+++ b/spec/frontend/pipeline_editor/graphql/__snapshots__/resolvers_spec.js.snap
@@ -27,7 +27,7 @@ Object {
"echo 'script 1'",
],
"stage": "test",
- "tagList": Array [
+ "tags": Array [
"tag 1",
],
"when": "on_success",
@@ -61,7 +61,7 @@ Object {
"echo 'script 2'",
],
"stage": "test",
- "tagList": Array [
+ "tags": Array [
"tag 2",
],
"when": "on_success",
diff --git a/spec/frontend/pipeline_editor/mock_data.js b/spec/frontend/pipeline_editor/mock_data.js
index b3526b47731..3692e51cf0b 100644
--- a/spec/frontend/pipeline_editor/mock_data.js
+++ b/spec/frontend/pipeline_editor/mock_data.js
@@ -35,6 +35,21 @@ job_build:
needs: ["job_test_2"]
`;
+const mockJobFields = {
+ beforeScript: [],
+ afterScript: [],
+ environment: null,
+ allowFailure: false,
+ tags: [],
+ when: 'on_success',
+ only: { refs: ['branches', 'tags'], __typename: 'CiJobLimitType' },
+ except: null,
+ needs: { nodes: [], __typename: 'CiConfigNeedConnection' },
+ __typename: 'CiConfigJob',
+};
+
+// Mock result of the graphql query at:
+// app/assets/javascripts/pipeline_editor/graphql/queries/ci_config.graphql
export const mockCiConfigQueryResponse = {
data: {
ciConfig: {
@@ -54,8 +69,8 @@ export const mockCiConfigQueryResponse = {
nodes: [
{
name: 'job_test_1',
- needs: { nodes: [], __typename: 'CiConfigNeedConnection' },
- __typename: 'CiConfigJob',
+ script: ['echo "test 1"'],
+ ...mockJobFields,
},
],
__typename: 'CiConfigJobConnection',
@@ -69,9 +84,8 @@ export const mockCiConfigQueryResponse = {
nodes: [
{
name: 'job_test_2',
-
- needs: { nodes: [], __typename: 'CiConfigNeedConnection' },
- __typename: 'CiConfigJob',
+ script: ['echo "test 2"'],
+ ...mockJobFields,
},
],
__typename: 'CiConfigJobConnection',
@@ -94,11 +108,8 @@ export const mockCiConfigQueryResponse = {
nodes: [
{
name: 'job_build',
- needs: {
- nodes: [{ name: 'job_test_2', __typename: 'CiConfigNeed' }],
- __typename: 'CiConfigNeedConnection',
- },
- __typename: 'CiConfigJob',
+ script: ['echo "build"'],
+ ...mockJobFields,
},
],
__typename: 'CiConfigJobConnection',
diff --git a/spec/frontend/pipeline_editor/pipeline_editor_app_spec.js b/spec/frontend/pipeline_editor/pipeline_editor_app_spec.js
index 00db3553ea5..50434a83ffe 100644
--- a/spec/frontend/pipeline_editor/pipeline_editor_app_spec.js
+++ b/spec/frontend/pipeline_editor/pipeline_editor_app_spec.js
@@ -5,6 +5,7 @@ import waitForPromises from 'helpers/wait_for_promises';
import VueApollo from 'vue-apollo';
import createMockApollo from 'jest/helpers/mock_apollo_helper';
+import httpStatusCodes from '~/lib/utils/http_status';
import { objectToQuery, redirectTo, refreshCurrentPage } from '~/lib/utils/url_utility';
import {
mockCiConfigPath,
@@ -414,58 +415,81 @@ describe('~/pipeline_editor/pipeline_editor_app.vue', () => {
mockCiConfigData.mockResolvedValue(mockCiConfigQueryResponse);
});
- it('no error is shown when data is set', async () => {
- createComponentWithApollo();
+ describe('when file exists', () => {
+ beforeEach(async () => {
+ createComponentWithApollo();
- await waitForPromises();
+ await waitForPromises();
+ });
- expect(findAlert().exists()).toBe(false);
- expect(findEditorLite().attributes('value')).toBe(mockCiYml);
- });
+ it('shows editor and commit form', () => {
+ expect(findEditorLite().exists()).toBe(true);
+ expect(findTextEditor().exists()).toBe(true);
+ });
- it('ci config query is called with correct variables', async () => {
- createComponentWithApollo();
+ it('no error is shown when data is set', async () => {
+ expect(findAlert().exists()).toBe(false);
+ expect(findEditorLite().attributes('value')).toBe(mockCiYml);
+ });
- await waitForPromises();
+ it('ci config query is called with correct variables', async () => {
+ createComponentWithApollo();
- expect(mockCiConfigData).toHaveBeenCalledWith({
- content: mockCiYml,
- projectPath: mockProjectFullPath,
+ await waitForPromises();
+
+ expect(mockCiConfigData).toHaveBeenCalledWith({
+ content: mockCiYml,
+ projectPath: mockProjectFullPath,
+ });
});
});
- it('shows a 404 error message', async () => {
- mockBlobContentData.mockRejectedValueOnce({
- response: {
- status: 404,
- },
+ describe('when no file exists', () => {
+ const expectedAlertMsg =
+ 'There is no .gitlab-ci.yml file in this repository, please add one and visit the Pipeline Editor again.';
+
+ it('does not show editor or commit form', async () => {
+ mockBlobContentData.mockRejectedValueOnce(new Error('My error!'));
+ createComponentWithApollo();
+ await waitForPromises();
+
+ expect(findEditorLite().exists()).toBe(false);
+ expect(findTextEditor().exists()).toBe(false);
});
- createComponentWithApollo();
- await waitForPromises();
+ it('shows a 404 error message', async () => {
+ mockBlobContentData.mockRejectedValueOnce({
+ response: {
+ status: httpStatusCodes.NOT_FOUND,
+ },
+ });
+ createComponentWithApollo();
- expect(findAlert().text()).toBe('No CI file found in this repository, please add one.');
- });
+ await waitForPromises();
- it('shows a 400 error message', async () => {
- mockBlobContentData.mockRejectedValueOnce({
- response: {
- status: 400,
- },
+ expect(findAlert().text()).toBe(expectedAlertMsg);
});
- createComponentWithApollo();
- await waitForPromises();
+ it('shows a 400 error message', async () => {
+ mockBlobContentData.mockRejectedValueOnce({
+ response: {
+ status: httpStatusCodes.BAD_REQUEST,
+ },
+ });
+ createComponentWithApollo();
- expect(findAlert().text()).toBe('Repository does not have a default branch, please set one.');
- });
+ await waitForPromises();
- it('shows a unkown error message', async () => {
- mockBlobContentData.mockRejectedValueOnce(new Error('My error!'));
- createComponentWithApollo();
- await waitForPromises();
+ expect(findAlert().text()).toBe(expectedAlertMsg);
+ });
+
+ it('shows a unkown error message', async () => {
+ mockBlobContentData.mockRejectedValueOnce(new Error('My error!'));
+ createComponentWithApollo();
+ await waitForPromises();
- expect(findAlert().text()).toBe('The CI configuration was not loaded, please try again.');
+ expect(findAlert().text()).toBe('The CI configuration was not loaded, please try again.');
+ });
});
});
});
diff --git a/spec/helpers/issuables_description_templates_helper_spec.rb b/spec/helpers/issuables_description_templates_helper_spec.rb
new file mode 100644
index 00000000000..199b43c2131
--- /dev/null
+++ b/spec/helpers/issuables_description_templates_helper_spec.rb
@@ -0,0 +1,43 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe IssuablesDescriptionTemplatesHelper do
+ include_context 'project issuable templates context'
+
+ describe '#issuable_templates' do
+ let_it_be(:inherited_from) { nil }
+ let_it_be(:user) { create(:user) }
+ let_it_be(:parent_group) { create(:group) }
+ let_it_be(:project) { create(:project, :custom_repo, files: issuable_template_files) }
+ let_it_be(:group_member) { create(:group_member, :developer, group: parent_group, user: user) }
+ let_it_be(:project_member) { create(:project_member, :developer, user: user, project: project) }
+
+ context 'when project has no parent group' do
+ it_behaves_like 'project issuable templates'
+ end
+
+ context 'when project has parent group' do
+ before do
+ project.update!(group: parent_group)
+ end
+
+ context 'when project parent group does not have a file template project' do
+ it_behaves_like 'project issuable templates'
+ end
+
+ context 'when project parent group has a file template project' do
+ let_it_be(:file_template_project) { create(:project, :custom_repo, group: parent_group, files: issuable_template_files) }
+ let_it_be(:group) { create(:group, parent: parent_group) }
+ let_it_be(:project) { create(:project, :custom_repo, group: group, files: issuable_template_files) }
+
+ before do
+ project.update!(group: group)
+ parent_group.update_columns(file_template_project_id: file_template_project.id)
+ end
+
+ it_behaves_like 'project issuable templates'
+ end
+ end
+ end
+end
diff --git a/spec/helpers/issuables_helper_spec.rb b/spec/helpers/issuables_helper_spec.rb
index 57845904d32..a9ac98fa65b 100644
--- a/spec/helpers/issuables_helper_spec.rb
+++ b/spec/helpers/issuables_helper_spec.rb
@@ -199,6 +199,7 @@ RSpec.describe IssuablesHelper do
markdownDocsPath: '/help/user/markdown',
lockVersion: issue.lock_version,
projectPath: @project.path,
+ projectId: @project.id,
projectNamespace: @project.namespace.path,
initialTitleHtml: issue.title,
initialTitleText: issue.title,
diff --git a/spec/models/license_template_spec.rb b/spec/models/license_template_spec.rb
index 515f728f515..fe06d1a357c 100644
--- a/spec/models/license_template_spec.rb
+++ b/spec/models/license_template_spec.rb
@@ -57,6 +57,6 @@ RSpec.describe LicenseTemplate do
end
def build_template(content)
- described_class.new(key: 'foo', name: 'foo', category: :Other, content: content)
+ described_class.new(key: 'foo', name: 'foo', project: nil, category: :Other, content: content)
end
end
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index c0579566d1f..a2b51684d4d 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -2977,56 +2977,9 @@ RSpec.describe Project, factory_default: :keep do
end
end
- describe '#pushes_since_gc' do
- let(:project) { build_stubbed(:project) }
-
- after do
- project.reset_pushes_since_gc
- end
-
- context 'without any pushes' do
- it 'returns 0' do
- expect(project.pushes_since_gc).to eq(0)
- end
- end
-
- context 'with a number of pushes' do
- it 'returns the number of pushes' do
- 3.times { project.increment_pushes_since_gc }
-
- expect(project.pushes_since_gc).to eq(3)
- end
- end
- end
-
- describe '#increment_pushes_since_gc' do
- let(:project) { build_stubbed(:project) }
-
- after do
- project.reset_pushes_since_gc
- end
-
- it 'increments the number of pushes since the last GC' do
- 3.times { project.increment_pushes_since_gc }
-
- expect(project.pushes_since_gc).to eq(3)
- end
- end
-
- describe '#reset_pushes_since_gc' do
- let(:project) { build_stubbed(:project) }
-
- after do
- project.reset_pushes_since_gc
- end
-
- it 'resets the number of pushes since the last GC' do
- 3.times { project.increment_pushes_since_gc }
-
- project.reset_pushes_since_gc
-
- expect(project.pushes_since_gc).to eq(0)
- end
+ it_behaves_like 'can housekeep repository' do
+ let(:resource) { build_stubbed(:project) }
+ let(:resource_key) { 'projects' }
end
describe '#deployment_variables' do
diff --git a/spec/services/merge_requests/merge_service_spec.rb b/spec/services/merge_requests/merge_service_spec.rb
index 4e1d8b3a9a9..dd37d87e3f5 100644
--- a/spec/services/merge_requests/merge_service_spec.rb
+++ b/spec/services/merge_requests/merge_service_spec.rb
@@ -19,12 +19,8 @@ RSpec.describe MergeRequests::MergeService do
{ commit_message: 'Awesome message', sha: merge_request.diff_head_sha }
end
- let(:feature_flag_persist_squash) { true }
-
context 'valid params' do
before do
- stub_feature_flags(persist_squash_commit_sha_for_squashes: feature_flag_persist_squash)
-
allow(service).to receive(:execute_hooks)
expect(merge_request).to receive(:update_and_mark_in_progress_merge_commit_sha).twice.and_call_original
@@ -90,14 +86,6 @@ RSpec.describe MergeRequests::MergeService do
expect(merge_request.squash_commit_sha).to eq(squash_commit.id)
end
-
- context 'when feature flag is disabled' do
- let(:feature_flag_persist_squash) { false }
-
- it 'does not populate squash_commit_sha' do
- expect(merge_request.squash_commit_sha).to be_nil
- end
- end
end
end
diff --git a/spec/support/shared_examples/helpers/issuable_description_templates_shared_examples.rb b/spec/support/shared_examples/helpers/issuable_description_templates_shared_examples.rb
new file mode 100644
index 00000000000..dbd4f1927e3
--- /dev/null
+++ b/spec/support/shared_examples/helpers/issuable_description_templates_shared_examples.rb
@@ -0,0 +1,49 @@
+# frozen_string_literal: true
+
+RSpec.shared_context 'project issuable templates context' do
+ let_it_be(:issuable_template_files) do
+ {
+ '.gitlab/issue_templates/issue-bar.md' => 'Issue Template Bar',
+ '.gitlab/issue_templates/issue-foo.md' => 'Issue Template Foo',
+ '.gitlab/issue_templates/issue-bad.txt' => 'Issue Template Bad',
+ '.gitlab/issue_templates/issue-baz.xyz' => 'Issue Template Baz',
+
+ '.gitlab/merge_request_templates/merge_request-bar.md' => 'Merge Request Template Bar',
+ '.gitlab/merge_request_templates/merge_request-foo.md' => 'Merge Request Template Foo',
+ '.gitlab/merge_request_templates/merge_request-bad.txt' => 'Merge Request Template Bad',
+ '.gitlab/merge_request_templates/merge_request-baz.xyz' => 'Merge Request Template Baz'
+ }
+ end
+end
+
+RSpec.shared_examples 'project issuable templates' do
+ context 'issuable templates' do
+ before do
+ allow(helper).to receive(:current_user).and_return(user)
+ end
+
+ it 'returns only md files as issue templates' do
+ expect(helper.issuable_templates(project, 'issue')).to eq(expected_templates('issue'))
+ end
+
+ it 'returns only md files as merge_request templates' do
+ expect(helper.issuable_templates(project, 'merge_request')).to eq(expected_templates('merge_request'))
+ end
+ end
+
+ def expected_templates(issuable_type)
+ expectation = {}
+
+ expectation["Project Templates"] = templates(issuable_type, project)
+ expectation["Group #{inherited_from.namespace.full_name}"] = templates(issuable_type, inherited_from) if inherited_from.present?
+
+ expectation
+ end
+
+ def templates(issuable_type, inherited_from)
+ [
+ { id: "#{issuable_type}-bar", name: "#{issuable_type}-bar", project_id: inherited_from.id },
+ { id: "#{issuable_type}-foo", name: "#{issuable_type}-foo", project_id: inherited_from.id }
+ ]
+ end
+end
diff --git a/spec/support/shared_examples/models/concerns/can_housekeep_repository_shared_examples.rb b/spec/support/shared_examples/models/concerns/can_housekeep_repository_shared_examples.rb
new file mode 100644
index 00000000000..2f0b95427d2
--- /dev/null
+++ b/spec/support/shared_examples/models/concerns/can_housekeep_repository_shared_examples.rb
@@ -0,0 +1,45 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'can housekeep repository' do
+ context 'with a clean redis state', :clean_gitlab_redis_shared_state do
+ describe '#pushes_since_gc' do
+ context 'without any pushes' do
+ it 'returns 0' do
+ expect(resource.pushes_since_gc).to eq(0)
+ end
+ end
+
+ context 'with a number of pushes' do
+ it 'returns the number of pushes' do
+ 3.times { resource.increment_pushes_since_gc }
+
+ expect(resource.pushes_since_gc).to eq(3)
+ end
+ end
+ end
+
+ describe '#increment_pushes_since_gc' do
+ it 'increments the number of pushes since the last GC' do
+ 3.times { resource.increment_pushes_since_gc }
+
+ expect(resource.pushes_since_gc).to eq(3)
+ end
+ end
+
+ describe '#reset_pushes_since_gc' do
+ it 'resets the number of pushes since the last GC' do
+ 3.times { resource.increment_pushes_since_gc }
+
+ resource.reset_pushes_since_gc
+
+ expect(resource.pushes_since_gc).to eq(0)
+ end
+ end
+
+ describe '#pushes_since_gc_redis_shared_state_key' do
+ it 'returns the proper redis key format' do
+ expect(resource.send(:pushes_since_gc_redis_shared_state_key)).to eq("#{resource_key}/#{resource.id}/pushes_since_gc")
+ end
+ end
+ end
+end
diff --git a/spec/tooling/lib/tooling/test_file_finder_spec.rb b/spec/tooling/lib/tooling/test_file_finder_spec.rb
deleted file mode 100644
index 683bc647b8a..00000000000
--- a/spec/tooling/lib/tooling/test_file_finder_spec.rb
+++ /dev/null
@@ -1,175 +0,0 @@
-# frozen_string_literal: true
-
-require_relative '../../../../tooling/lib/tooling/test_file_finder'
-
-RSpec.describe Tooling::TestFileFinder do
- subject { described_class.new(file) }
-
- describe '#test_files' do
- context 'when given non .rb files' do
- let(:file) { 'app/assets/images/emoji.png' }
-
- it 'does not return a test file' do
- expect(subject.test_files).to be_empty
- end
- end
-
- context 'when given file in app/' do
- let(:file) { 'app/finders/admin/projects_finder.rb' }
-
- it 'returns the matching app spec file' do
- expect(subject.test_files).to contain_exactly('spec/finders/admin/projects_finder_spec.rb')
- end
- end
-
- context 'when given file in lib/' do
- let(:file) { 'lib/banzai/color_parser.rb' }
-
- it 'returns the matching app spec file' do
- expect(subject.test_files).to contain_exactly('spec/lib/banzai/color_parser_spec.rb')
- end
- end
-
- context 'when given a file in tooling/' do
- let(:file) { 'tooling/lib/tooling/test_file_finder.rb' }
-
- it 'returns the matching tooling test' do
- expect(subject.test_files).to contain_exactly('spec/tooling/lib/tooling/test_file_finder_spec.rb')
- end
- end
-
- context 'when given a test file' do
- let(:file) { 'spec/lib/banzai/color_parser_spec.rb' }
-
- it 'returns the matching test file itself' do
- expect(subject.test_files).to contain_exactly('spec/lib/banzai/color_parser_spec.rb')
- end
- end
-
- context 'when given an app file in ee/' do
- let(:file) { 'ee/app/models/analytics/cycle_analytics/group_level.rb' }
-
- it 'returns the matching ee/ test file' do
- expect(subject.test_files).to contain_exactly('ee/spec/models/analytics/cycle_analytics/group_level_spec.rb')
- end
- end
-
- context 'when given an ee extension module file' do
- let(:file) { 'ee/app/models/ee/user.rb' }
-
- it 'returns the matching ee/ class test file, ee extension module test file and the foss class test file' do
- test_files = ['ee/spec/models/user_spec.rb', 'ee/spec/models/ee/user_spec.rb', 'spec/app/models/user_spec.rb']
- expect(subject.test_files).to contain_exactly(*test_files)
- end
- end
-
- context 'when given a test file in ee/' do
- let(:file) { 'ee/spec/models/container_registry/event_spec.rb' }
-
- it 'returns the test file itself' do
- expect(subject.test_files).to contain_exactly('ee/spec/models/container_registry/event_spec.rb')
- end
- end
-
- context 'when given a module test file in ee/' do
- let(:file) { 'ee/spec/models/ee/appearance_spec.rb' }
-
- it 'returns the matching module test file itself and the corresponding spec model test file' do
- test_files = ['ee/spec/models/ee/appearance_spec.rb', 'spec/models/appearance_spec.rb']
- expect(subject.test_files).to contain_exactly(*test_files)
- end
- end
-
- context 'when given a factory file' do
- let(:file) { 'spec/factories/users.rb' }
-
- it 'returns spec/factories_spec.rb file' do
- expect(subject.test_files).to contain_exactly('spec/factories_spec.rb')
- end
- end
-
- context 'when given an ee factory file' do
- let(:file) { 'ee/spec/factories/users.rb' }
-
- it 'returns spec/factories_spec.rb file' do
- expect(subject.test_files).to contain_exactly('spec/factories_spec.rb')
- end
- end
-
- context 'when given db/structure.sql' do
- let(:file) { 'db/structure.sql' }
-
- it 'returns spec/db/schema_spec.rb' do
- expect(subject.test_files).to contain_exactly('spec/db/schema_spec.rb')
- end
- end
-
- context 'when given an initializer' do
- let(:file) { 'config/initializers/action_mailer_hooks.rb' }
-
- it 'returns the matching initializer spec' do
- expect(subject.test_files).to contain_exactly('spec/initializers/action_mailer_hooks_spec.rb')
- end
- end
-
- context 'when given a haml view' do
- let(:file) { 'app/views/admin/users/_user.html.haml' }
-
- it 'returns the matching view spec' do
- expect(subject.test_files).to contain_exactly('spec/views/admin/users/_user.html.haml_spec.rb')
- end
- end
-
- context 'when given a haml view in ee/' do
- let(:file) { 'ee/app/views/admin/users/_user.html.haml' }
-
- it 'returns the matching view spec' do
- expect(subject.test_files).to contain_exactly('ee/spec/views/admin/users/_user.html.haml_spec.rb')
- end
- end
-
- context 'when given a migration file' do
- let(:file) { 'db/migrate/20191023152913_add_default_and_free_plans.rb' }
-
- it 'returns the matching migration spec' do
- test_files = %w[
- spec/migrations/add_default_and_free_plans_spec.rb
- spec/migrations/20191023152913_add_default_and_free_plans_spec.rb
- ]
- expect(subject.test_files).to contain_exactly(*test_files)
- end
- end
-
- context 'when given a post-migration file' do
- let(:file) { 'db/post_migrate/20200608072931_backfill_imported_snippet_repositories.rb' }
-
- it 'returns the matching migration spec' do
- test_files = %w[
- spec/migrations/backfill_imported_snippet_repositories_spec.rb
- spec/migrations/20200608072931_backfill_imported_snippet_repositories_spec.rb
- ]
- expect(subject.test_files).to contain_exactly(*test_files)
- end
- end
-
- context 'with foss_test_only: true' do
- subject { Tooling::TestFileFinder.new(file, foss_test_only: true) }
-
- context 'when given a module file in ee/' do
- let(:file) { 'ee/app/models/ee/user.rb' }
-
- it 'returns only the corresponding spec model test file in foss' do
- expect(subject.test_files).to contain_exactly('spec/app/models/user_spec.rb')
- end
- end
-
- context 'when given an app file in ee/' do
- let(:file) { 'ee/app/models/approval.rb' }
-
- it 'returns no test file in foss' do
- expect(subject.test_files).to be_empty
- end
- end
- end
- end
-end
diff --git a/tooling/lib/tooling/test_file_finder.rb b/tooling/lib/tooling/test_file_finder.rb
deleted file mode 100644
index cf5de190c4a..00000000000
--- a/tooling/lib/tooling/test_file_finder.rb
+++ /dev/null
@@ -1,94 +0,0 @@
-# frozen_string_literal: true
-
-require 'set'
-
-module Tooling
- class TestFileFinder
- EE_PREFIX = 'ee/'
-
- def initialize(file, foss_test_only: false)
- @file = file
- @foss_test_only = foss_test_only
- end
-
- def test_files
- impacted_tests = ee_impact | non_ee_impact | either_impact
- impacted_tests.impact(@file)
- end
-
- private
-
- attr_reader :file, :foss_test_only, :result
-
- class ImpactedTestFile
- attr_reader :pattern_matchers
-
- def initialize(prefix: nil)
- @pattern_matchers = {}
- @prefix = prefix
-
- yield self if block_given?
- end
-
- def associate(pattern, &block)
- @pattern_matchers[%r{^#{@prefix}#{pattern}}] = block
- end
-
- def impact(file)
- @pattern_matchers.each_with_object(Set.new) do |(pattern, block), result|
- if (match = pattern.match(file))
- test_files = block.call(match)
- result.merge(Array(test_files))
- end
- end.to_a
- end
-
- def |(other)
- self.class.new do |combined_matcher|
- self.pattern_matchers.each do |pattern, block|
- combined_matcher.associate(pattern, &block)
- end
- other.pattern_matchers.each do |pattern, block|
- combined_matcher.associate(pattern, &block)
- end
- end
- end
- end
-
- def ee_impact
- ImpactedTestFile.new(prefix: EE_PREFIX) do |impact|
- unless foss_test_only
- impact.associate(%r{app/(.+)\.rb$}) { |match| "#{EE_PREFIX}spec/#{match[1]}_spec.rb" }
- impact.associate(%r{app/(.*/)ee/(.+)\.rb$}) { |match| "#{EE_PREFIX}spec/#{match[1]}#{match[2]}_spec.rb" }
- impact.associate(%r{lib/(.+)\.rb$}) { |match| "#{EE_PREFIX}spec/lib/#{match[1]}_spec.rb" }
- end
-
- impact.associate(%r{(?!spec)(.*/)ee/(.+)\.rb$}) { |match| "spec/#{match[1]}#{match[2]}_spec.rb" }
- impact.associate(%r{spec/(.*/)ee/(.+)\.rb$}) { |match| "spec/#{match[1]}#{match[2]}.rb" }
- end
- end
-
- def non_ee_impact
- ImpactedTestFile.new do |impact|
- impact.associate(%r{app/(.+)\.rb$}) { |match| "spec/#{match[1]}_spec.rb" }
- impact.associate(%r{(tooling/)?lib/(.+)\.rb$}) { |match| "spec/#{match[1]}lib/#{match[2]}_spec.rb" }
- impact.associate(%r{config/initializers/(.+)\.rb$}) { |match| "spec/initializers/#{match[1]}_spec.rb" }
- impact.associate('db/structure.sql') { 'spec/db/schema_spec.rb' }
- impact.associate(%r{db/(?:post_)?migrate/([0-9]+)_(.+)\.rb$}) do |match|
- [
- "spec/migrations/#{match[2]}_spec.rb",
- "spec/migrations/#{match[1]}_#{match[2]}_spec.rb"
- ]
- end
- end
- end
-
- def either_impact
- ImpactedTestFile.new(prefix: %r{^(?<prefix>#{EE_PREFIX})?}) do |impact|
- impact.associate(%r{app/views/(?<view>.+)\.haml$}) { |match| "#{match[:prefix]}spec/views/#{match[:view]}.haml_spec.rb" }
- impact.associate(%r{spec/(.+)_spec\.rb$}) { |match| match[0] }
- impact.associate(%r{spec/factories/.+\.rb$}) { 'spec/factories_spec.rb' }
- end
- end
- end
-end