diff --git a/CHANGELOG.md b/CHANGELOG.md index 039c62b9930..4bb66363d78 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,17 @@ documentation](doc/development/changelog.md) for instructions on adding your own entry. +## 14.1.3 (2021-08-17) + +### Fixed (2 changes) + +- [Geo 2.0 Regression - Add ability to remove primary](gitlab-org/gitlab@1635f3d07d421edd2a83be109d7c54635aa4f58c) ([merge request](gitlab-org/gitlab!68383)) **GitLab Enterprise Edition** +- [[RUN AS-IF-FOSS] AS Fix SAML SSO login redirects not working](gitlab-org/gitlab@7b551e3d2a4ba6127549c613ee95e2c12c014b90) ([merge request](gitlab-org/gitlab!68383)) **GitLab Enterprise Edition** + +### Changed (1 change) + +- [Resolve "operator does not exist: integer[] || bigint in...](gitlab-org/gitlab@99e6457b6d9d39805dc7758c47091cf6ad0f2bdd) ([merge request](gitlab-org/gitlab!68383)) + ## 14.1.2 (2021-08-03) ### Security (19 changes) diff --git a/app/assets/javascripts/content_editor/extensions/reference.js b/app/assets/javascripts/content_editor/extensions/reference.js new file mode 100644 index 00000000000..5f4484af9c8 --- /dev/null +++ b/app/assets/javascripts/content_editor/extensions/reference.js @@ -0,0 +1,78 @@ +import { Node } from '@tiptap/core'; + +export default Node.create({ + name: 'reference', + + inline: true, + + group: 'inline', + + atom: true, + + addAttributes() { + return { + className: { + default: null, + parseHTML: (element) => { + return { + className: element.className, + }; + }, + }, + referenceType: { + default: null, + parseHTML: (element) => { + return { + referenceType: element.dataset.referenceType, + }; + }, + }, + originalText: { + default: null, + parseHTML: (element) => { + return { + originalText: element.dataset.original, + }; + }, + }, + href: { + default: null, + parseHTML: (element) => { + return { + href: element.getAttribute('href'), + }; + }, + }, + text: { + default: null, + parseHTML: (element) => { + return { + text: element.textContent, + }; + }, + }, + }; + }, + + parseHTML() { + return [ + { + tag: 'a.gfm:not([data-link=true])', + priority: 51, + }, + ]; + }, + + renderHTML({ node }) { + return [ + 'a', + { + class: node.attrs.className, + href: node.attrs.href, + 'data-reference-type': node.attrs.referenceType, + 'data-original': node.attrs.originalText, + }, + node.attrs.text, + ]; + }, +}); diff --git a/app/assets/javascripts/content_editor/services/create_content_editor.js b/app/assets/javascripts/content_editor/services/create_content_editor.js index 4bb4d55a816..8997960203a 100644 --- a/app/assets/javascripts/content_editor/services/create_content_editor.js +++ b/app/assets/javascripts/content_editor/services/create_content_editor.js @@ -23,6 +23,7 @@ import ListItem from '../extensions/list_item'; import Loading from '../extensions/loading'; import OrderedList from '../extensions/ordered_list'; import Paragraph from '../extensions/paragraph'; +import Reference from '../extensions/reference'; import Strike from '../extensions/strike'; import Subscript from '../extensions/subscript'; import Superscript from '../extensions/superscript'; @@ -82,6 +83,7 @@ export const createContentEditor = ({ Loading, OrderedList, Paragraph, + Reference, Strike, Subscript, Superscript, diff --git a/app/assets/javascripts/content_editor/services/markdown_serializer.js b/app/assets/javascripts/content_editor/services/markdown_serializer.js index 80ad3e15a42..df4d31c3d7f 100644 --- a/app/assets/javascripts/content_editor/services/markdown_serializer.js +++ b/app/assets/javascripts/content_editor/services/markdown_serializer.js @@ -19,6 +19,7 @@ import Link from '../extensions/link'; import ListItem from '../extensions/list_item'; import OrderedList from '../extensions/ordered_list'; import Paragraph from '../extensions/paragraph'; +import Reference from '../extensions/reference'; import Strike from '../extensions/strike'; import Subscript from '../extensions/subscript'; import Superscript from '../extensions/superscript'; @@ -91,6 +92,9 @@ const defaultSerializerConfig = { [ListItem.name]: defaultMarkdownSerializer.nodes.list_item, [OrderedList.name]: defaultMarkdownSerializer.nodes.ordered_list, [Paragraph.name]: defaultMarkdownSerializer.nodes.paragraph, + [Reference.name]: (state, node) => { + state.write(node.attrs.originalText || node.attrs.text); + }, [Table.name]: (state, node) => { state.renderContent(node); }, diff --git a/app/assets/javascripts/pipelines/components/graph/graph_component_legacy.vue b/app/assets/javascripts/pipelines/components/graph/graph_component_legacy.vue deleted file mode 100644 index 39d0fa8a8ca..00000000000 --- a/app/assets/javascripts/pipelines/components/graph/graph_component_legacy.vue +++ /dev/null @@ -1,269 +0,0 @@ - - diff --git a/app/assets/javascripts/pipelines/components/graph/linked_pipelines_column_legacy.vue b/app/assets/javascripts/pipelines/components/graph/linked_pipelines_column_legacy.vue deleted file mode 100644 index 39baeb6e1c3..00000000000 --- a/app/assets/javascripts/pipelines/components/graph/linked_pipelines_column_legacy.vue +++ /dev/null @@ -1,91 +0,0 @@ - - - diff --git a/app/assets/javascripts/pipelines/components/graph/stage_column_component_legacy.vue b/app/assets/javascripts/pipelines/components/graph/stage_column_component_legacy.vue deleted file mode 100644 index cbaf07c05cf..00000000000 --- a/app/assets/javascripts/pipelines/components/graph/stage_column_component_legacy.vue +++ /dev/null @@ -1,112 +0,0 @@ - - diff --git a/app/assets/javascripts/pipelines/components/pipelines_list/pipelines_filtered_search.vue b/app/assets/javascripts/pipelines/components/pipelines_list/pipelines_filtered_search.vue index a7e1443df30..0b70e74b8ff 100644 --- a/app/assets/javascripts/pipelines/components/pipelines_list/pipelines_filtered_search.vue +++ b/app/assets/javascripts/pipelines/components/pipelines_list/pipelines_filtered_search.vue @@ -4,6 +4,7 @@ import { map } from 'lodash'; import { s__ } from '~/locale'; import { OPERATOR_IS_ONLY } from '~/vue_shared/components/filtered_search_bar/constants'; import PipelineBranchNameToken from './tokens/pipeline_branch_name_token.vue'; +import PipelineSourceToken from './tokens/pipeline_source_token.vue'; import PipelineStatusToken from './tokens/pipeline_status_token.vue'; import PipelineTagNameToken from './tokens/pipeline_tag_name_token.vue'; import PipelineTriggerAuthorToken from './tokens/pipeline_trigger_author_token.vue'; @@ -13,6 +14,7 @@ export default { branchType: 'ref', tagType: 'tag', statusType: 'status', + sourceType: 'source', defaultTokensLength: 1, components: { GlFilteredSearch, @@ -37,7 +39,7 @@ export default { return this.value.map((i) => i.type); }, tokens() { - return [ + const tokens = [ { type: this.$options.userType, icon: 'user', @@ -76,6 +78,19 @@ export default { operators: OPERATOR_IS_ONLY, }, ]; + + if (gon.features.pipelineSourceFilter) { + tokens.push({ + type: this.$options.sourceType, + icon: 'trigger-source', + title: s__('Pipeline|Source'), + unique: true, + token: PipelineSourceToken, + operators: OPERATOR_IS_ONLY, + }); + } + + return tokens; }, parsedParams() { return map(this.params, (val, key) => ({ diff --git a/app/assets/javascripts/pipelines/components/pipelines_list/tokens/pipeline_source_token.vue b/app/assets/javascripts/pipelines/components/pipelines_list/tokens/pipeline_source_token.vue new file mode 100644 index 00000000000..71efa8b2ab4 --- /dev/null +++ b/app/assets/javascripts/pipelines/components/pipelines_list/tokens/pipeline_source_token.vue @@ -0,0 +1,106 @@ + + + diff --git a/app/assets/javascripts/pipelines/constants.js b/app/assets/javascripts/pipelines/constants.js index a2c7eaec113..5678b613ec6 100644 --- a/app/assets/javascripts/pipelines/constants.js +++ b/app/assets/javascripts/pipelines/constants.js @@ -4,7 +4,7 @@ export const CANCEL_REQUEST = 'CANCEL_REQUEST'; export const LAYOUT_CHANGE_DELAY = 300; export const FILTER_PIPELINES_SEARCH_DELAY = 200; export const ANY_TRIGGER_AUTHOR = 'Any'; -export const SUPPORTED_FILTER_PARAMETERS = ['username', 'ref', 'status']; +export const SUPPORTED_FILTER_PARAMETERS = ['username', 'ref', 'status', 'source']; export const FILTER_TAG_IDENTIFIER = 'tag'; export const SCHEDULE_ORIGIN = 'schedule'; diff --git a/app/assets/javascripts/pipelines/mixins/graph_pipeline_bundle_mixin.js b/app/assets/javascripts/pipelines/mixins/graph_pipeline_bundle_mixin.js deleted file mode 100644 index 5c34f4e4f7e..00000000000 --- a/app/assets/javascripts/pipelines/mixins/graph_pipeline_bundle_mixin.js +++ /dev/null @@ -1,65 +0,0 @@ -import createFlash from '~/flash'; -import { __ } from '~/locale'; - -export default { - methods: { - getExpandedPipelines(pipeline) { - this.mediator.service - .getPipeline(this.mediator.getExpandedParameters()) - .then((response) => { - this.mediator.store.toggleLoading(pipeline); - this.mediator.store.storePipeline(response.data); - this.mediator.poll.enable({ data: this.mediator.getExpandedParameters() }); - }) - .catch(() => { - this.mediator.store.toggleLoading(pipeline); - createFlash({ - message: __('An error occurred while fetching the pipeline.'), - }); - }); - }, - /** - * Called when a linked pipeline is clicked. - * - * If the pipeline is collapsed we will start polling it & we will reset the other pipelines. - * If the pipeline is expanded we will close it. - * - * @param {String} method Method to fetch the pipeline - * @param {String} storeKey Store property that will be updates - * @param {String} resetStoreKey Store key for the visible pipeline that will need to be reset - * @param {Object} pipeline The clicked pipeline - */ - clickPipeline(pipeline, openMethod, closeMethod) { - if (!pipeline.isExpanded) { - this.mediator.store[openMethod](pipeline); - this.mediator.store.toggleLoading(pipeline); - this.mediator.poll.stop(); - - this.getExpandedPipelines(pipeline); - } else { - this.mediator.store[closeMethod](pipeline); - this.mediator.poll.stop(); - - this.mediator.poll.enable({ data: this.mediator.getExpandedParameters() }); - } - }, - resetDownstreamPipelines(parentPipeline, pipeline) { - this.mediator.store.resetTriggeredPipelines(parentPipeline, pipeline); - }, - clickUpstreamPipeline(pipeline) { - this.clickPipeline(pipeline, 'openPipeline', 'closePipeline'); - }, - clickDownstreamPipeline(pipeline) { - this.clickPipeline(pipeline, 'openPipeline', 'closePipeline'); - }, - requestRefreshPipelineGraph() { - // When an action is clicked - // (whether in the dropdown or in the main nodes, we refresh the big graph) - this.mediator.refreshPipeline().catch(() => - createFlash({ - message: __('An error occurred while making the request.'), - }), - ); - }, - }, -}; diff --git a/app/assets/javascripts/pipelines/pipeline_details_bundle.js b/app/assets/javascripts/pipelines/pipeline_details_bundle.js index e8d5ed175ba..c6e767d5424 100644 --- a/app/assets/javascripts/pipelines/pipeline_details_bundle.js +++ b/app/assets/javascripts/pipelines/pipeline_details_bundle.js @@ -3,15 +3,12 @@ import createFlash from '~/flash'; import { parseBoolean } from '~/lib/utils/common_utils'; import { __ } from '~/locale'; import Translate from '~/vue_shared/translate'; -import PipelineGraphLegacy from './components/graph/graph_component_legacy.vue'; import TestReports from './components/test_reports/test_reports.vue'; -import GraphBundleMixin from './mixins/graph_pipeline_bundle_mixin'; import createDagApp from './pipeline_details_dag'; import { createPipelinesDetailApp } from './pipeline_details_graph'; import { createPipelineHeaderApp } from './pipeline_details_header'; import { apolloProvider } from './pipeline_shared_client'; import createTestReportsStore from './stores/test_reports'; -import { reportToSentry } from './utils'; Vue.use(Translate); @@ -22,44 +19,6 @@ const SELECTORS = { PIPELINE_TESTS: '#js-pipeline-tests-detail', }; -const createLegacyPipelinesDetailApp = (mediator) => { - if (!document.querySelector(SELECTORS.PIPELINE_GRAPH)) { - return; - } - // eslint-disable-next-line no-new - new Vue({ - el: SELECTORS.PIPELINE_GRAPH, - components: { - PipelineGraphLegacy, - }, - mixins: [GraphBundleMixin], - data() { - return { - mediator, - }; - }, - errorCaptured(err, _vm, info) { - reportToSentry('pipeline_details_bundle_legacy_details', `error: ${err}, info: ${info}`); - }, - render(createElement) { - return createElement('pipeline-graph-legacy', { - props: { - isLoading: this.mediator.state.isLoading, - pipeline: this.mediator.store.state.pipeline, - mediator: this.mediator, - }, - on: { - refreshPipelineGraph: this.requestRefreshPipelineGraph, - onResetDownstream: (parentPipeline, pipeline) => - this.resetDownstreamPipelines(parentPipeline, pipeline), - onClickUpstreamPipeline: (pipeline) => this.clickUpstreamPipeline(pipeline), - onClickDownstreamPipeline: (pipeline) => this.clickDownstreamPipeline(pipeline), - }, - }); - }, - }); -}; - const createTestDetails = () => { const el = document.querySelector(SELECTORS.PIPELINE_TESTS); const { blobPath, emptyStateImagePath, hasTestReport, summaryEndpoint, suiteEndpoint } = @@ -88,9 +47,6 @@ const createTestDetails = () => { }; export default async function initPipelineDetailsBundle() { - const canShowNewPipelineDetails = - gon.features.graphqlPipelineDetails || gon.features.graphqlPipelineDetailsUsers; - const { dataset } = document.querySelector(SELECTORS.PIPELINE_DETAILS); try { @@ -101,22 +57,12 @@ export default async function initPipelineDetailsBundle() { }); } - if (canShowNewPipelineDetails) { - try { - createPipelinesDetailApp(SELECTORS.PIPELINE_GRAPH, apolloProvider, dataset); - } catch { - createFlash({ - message: __('An error occurred while loading the pipeline.'), - }); - } - } else { - const { default: PipelinesMediator } = await import( - /* webpackChunkName: 'PipelinesMediator' */ './pipeline_details_mediator' - ); - const mediator = new PipelinesMediator({ endpoint: dataset.endpoint }); - mediator.fetchPipeline(); - - createLegacyPipelinesDetailApp(mediator); + try { + createPipelinesDetailApp(SELECTORS.PIPELINE_GRAPH, apolloProvider, dataset); + } catch { + createFlash({ + message: __('An error occurred while loading the pipeline.'), + }); } createDagApp(apolloProvider); diff --git a/app/assets/javascripts/pipelines/pipeline_details_mediator.js b/app/assets/javascripts/pipelines/pipeline_details_mediator.js deleted file mode 100644 index 72c4fedc64c..00000000000 --- a/app/assets/javascripts/pipelines/pipeline_details_mediator.js +++ /dev/null @@ -1,81 +0,0 @@ -import Visibility from 'visibilityjs'; -import createFlash from '~/flash'; -import Poll from '../lib/utils/poll'; -import { __ } from '../locale'; -import PipelineService from './services/pipeline_service'; -import PipelineStore from './stores/pipeline_store'; - -export default class pipelinesMediator { - constructor(options = {}) { - this.options = options; - this.store = new PipelineStore(); - this.service = new PipelineService(options.endpoint); - - this.state = {}; - this.state.isLoading = false; - } - - fetchPipeline() { - this.poll = new Poll({ - resource: this.service, - method: 'getPipeline', - data: this.store.state.expandedPipelines ? this.getExpandedParameters() : undefined, - successCallback: this.successCallback.bind(this), - errorCallback: this.errorCallback.bind(this), - }); - - if (!Visibility.hidden()) { - this.state.isLoading = true; - this.poll.makeRequest(); - } else { - this.refreshPipeline(); - } - - Visibility.change(() => { - if (!Visibility.hidden()) { - this.poll.restart(); - } else { - this.stopPipelinePoll(); - } - }); - } - - successCallback(response) { - this.state.isLoading = false; - this.store.storePipeline(response.data); - } - - errorCallback() { - this.state.isLoading = false; - createFlash({ - message: __('An error occurred while fetching the pipeline.'), - }); - } - - refreshPipeline() { - this.stopPipelinePoll(); - - return this.service - .getPipeline() - .then((response) => this.successCallback(response)) - .catch(() => this.errorCallback()) - .finally(() => - this.poll.restart( - this.store.state.expandedPipelines ? this.getExpandedParameters() : undefined, - ), - ); - } - - stopPipelinePoll() { - this.poll.stop(); - } - - /** - * Backend expects paramets in the following format: `expanded[]=id&expanded[]=id` - */ - getExpandedParameters() { - return { - expanded: this.store.state.expandedPipelines, - }; - } -} diff --git a/app/assets/javascripts/pipelines/services/pipeline_service.js b/app/assets/javascripts/pipelines/services/pipeline_service.js deleted file mode 100644 index ba2830ec596..00000000000 --- a/app/assets/javascripts/pipelines/services/pipeline_service.js +++ /dev/null @@ -1,21 +0,0 @@ -import axios from '../../lib/utils/axios_utils'; - -export default class PipelineService { - constructor(endpoint) { - this.pipeline = endpoint; - } - - getPipeline(params) { - return axios.get(this.pipeline, { params }); - } - - // eslint-disable-next-line class-methods-use-this - deleteAction(endpoint) { - return axios.delete(`${endpoint}.json`); - } - - // eslint-disable-next-line class-methods-use-this - postAction(endpoint) { - return axios.post(`${endpoint}.json`); - } -} diff --git a/app/assets/javascripts/pipelines/stores/pipeline_store.js b/app/assets/javascripts/pipelines/stores/pipeline_store.js deleted file mode 100644 index 1f804a107a8..00000000000 --- a/app/assets/javascripts/pipelines/stores/pipeline_store.js +++ /dev/null @@ -1,206 +0,0 @@ -import Vue from 'vue'; - -export default class PipelineStore { - constructor() { - this.state = {}; - this.state.pipeline = {}; - this.state.expandedPipelines = []; - } - /** - * For the triggered pipelines adds the `isExpanded` key - * - * For the triggered_by pipeline adds the `isExpanded` key - * and saves it as an array - * - * @param {Object} pipeline - */ - storePipeline(pipeline = {}) { - const pipelineCopy = { ...pipeline }; - - if (pipelineCopy.triggered_by) { - pipelineCopy.triggered_by = [pipelineCopy.triggered_by]; - - const oldTriggeredBy = - this.state.pipeline && - this.state.pipeline.triggered_by && - this.state.pipeline.triggered_by[0]; - - this.parseTriggeredByPipelines(oldTriggeredBy, pipelineCopy.triggered_by[0]); - } - - if (pipelineCopy.triggered && pipelineCopy.triggered.length) { - pipelineCopy.triggered.forEach((el) => { - const oldPipeline = - this.state.pipeline && - this.state.pipeline.triggered && - this.state.pipeline.triggered.find((element) => element.id === el.id); - - this.parseTriggeredPipelines(oldPipeline, el); - }); - } - - this.state.pipeline = pipelineCopy; - } - - /** - * Recursiverly parses the triggered by pipelines. - * - * Sets triggered_by as an array, there is always only 1 triggered_by pipeline. - * Adds key `isExpanding` - * Keeps old isExpading value due to polling - * - * @param {Array} parentPipeline - * @param {Object} pipeline - */ - parseTriggeredByPipelines(oldPipeline = {}, newPipeline) { - // keep old value in case it's opened because we're polling - Vue.set(newPipeline, 'isExpanded', oldPipeline.isExpanded || false); - // add isLoading property - Vue.set(newPipeline, 'isLoading', false); - - // Because there can only ever be one `triggered_by` for any given pipeline, - // the API returns an object for the value instead of an Array. However, - // it's easier to deal with an array in the FE so we convert it. - if (newPipeline.triggered_by) { - if (!Array.isArray(newPipeline.triggered_by)) { - Object.assign(newPipeline, { triggered_by: [newPipeline.triggered_by] }); - } - - if (newPipeline.triggered_by?.length > 0) { - newPipeline.triggered_by.forEach((el) => { - const oldTriggeredBy = oldPipeline.triggered_by?.find((element) => element.id === el.id); - this.parseTriggeredPipelines(oldTriggeredBy, el); - }); - } - } - } - - /** - * Recursively parses the triggered pipelines - * @param {Array} parentPipeline - * @param {Object} pipeline - */ - parseTriggeredPipelines(oldPipeline = {}, newPipeline) { - // keep old value in case it's opened because we're polling - Vue.set(newPipeline, 'isExpanded', oldPipeline.isExpanded || false); - - // add isLoading property - Vue.set(newPipeline, 'isLoading', false); - - if (newPipeline.triggered && newPipeline.triggered.length > 0) { - newPipeline.triggered.forEach((el) => { - const oldTriggered = - oldPipeline.triggered && oldPipeline.triggered.find((element) => element.id === el.id); - this.parseTriggeredPipelines(oldTriggered, el); - }); - } - } - - /** - * Recursively resets all triggered by pipelines - * - * @param {Object} pipeline - */ - resetTriggeredByPipeline(parentPipeline, pipeline) { - parentPipeline.triggered_by.forEach((el) => this.closePipeline(el)); - - if (pipeline.triggered_by && pipeline.triggered_by) { - this.resetTriggeredByPipeline(pipeline, pipeline.triggered_by); - } - } - - /** - * Opens the clicked pipeline and closes all other ones. - * @param {Object} pipeline - */ - openTriggeredByPipeline(parentPipeline, pipeline) { - // first we need to reset all triggeredBy pipelines - this.resetTriggeredByPipeline(parentPipeline, pipeline); - - this.openPipeline(pipeline); - } - - /** - * On click, will close the given pipeline and all nested triggered by pipelines - * - * @param {Object} pipeline - */ - closeTriggeredByPipeline(pipeline) { - this.closePipeline(pipeline); - - if (pipeline.triggered_by && pipeline.triggered_by.length) { - pipeline.triggered_by.forEach((triggeredBy) => this.closeTriggeredByPipeline(triggeredBy)); - } - } - - /** - * Recursively closes all triggered pipelines for the given one. - * - * @param {Object} pipeline - */ - resetTriggeredPipelines(parentPipeline, pipeline) { - parentPipeline.triggered.forEach((el) => this.closePipeline(el)); - - if (pipeline.triggered && pipeline.triggered.length) { - pipeline.triggered.forEach((el) => this.resetTriggeredPipelines(pipeline, el)); - } - } - - /** - * Opens the clicked triggered pipeline and closes all other ones. - * - * @param {Object} pipeline - */ - openTriggeredPipeline(parentPipeline, pipeline) { - this.resetTriggeredPipelines(parentPipeline, pipeline); - - this.openPipeline(pipeline); - } - - /** - * On click, will close the given pipeline and all the nested triggered ones - * @param {Object} pipeline - */ - closeTriggeredPipeline(pipeline) { - this.closePipeline(pipeline); - - if (pipeline.triggered && pipeline.triggered.length) { - pipeline.triggered.forEach((triggered) => this.closeTriggeredPipeline(triggered)); - } - } - - /** - * Utility function, Closes the given pipeline - * @param {Object} pipeline - */ - closePipeline(pipeline) { - Vue.set(pipeline, 'isExpanded', false); - // remove the pipeline from the parameters - this.removeExpandedPipelineToRequestData(pipeline.id); - } - - /** - * Utility function, Opens the given pipeline - * @param {Object} pipeline - */ - openPipeline(pipeline) { - Vue.set(pipeline, 'isExpanded', true); - // add the pipeline to the parameters - this.addExpandedPipelineToRequestData(pipeline.id); - } - // eslint-disable-next-line class-methods-use-this - toggleLoading(pipeline) { - Vue.set(pipeline, 'isLoading', !pipeline.isLoading); - } - - addExpandedPipelineToRequestData(id) { - this.state.expandedPipelines.push(id); - } - - removeExpandedPipelineToRequestData(id) { - this.state.expandedPipelines.splice( - this.state.expandedPipelines.findIndex((el) => el === id), - 1, - ); - } -} diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_auto_merge_enabled.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_auto_merge_enabled.vue index 2e314d18f24..0eb173edbcb 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_auto_merge_enabled.vue +++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_auto_merge_enabled.vue @@ -37,12 +37,10 @@ export default { mr: { type: Object, required: true, - default: () => ({}), }, service: { type: Object, required: true, - default: () => ({}), }, }, data() { diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_closed.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_closed.vue index 302a30dab54..6d5ca58aa20 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_closed.vue +++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_closed.vue @@ -14,7 +14,6 @@ export default { mr: { type: Object, required: true, - default: () => ({}), }, }, }; diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_conflicts.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_conflicts.vue index 5a93021978c..1596f852b74 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_conflicts.vue +++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_conflicts.vue @@ -45,7 +45,6 @@ export default { mr: { type: Object, required: true, - default: () => ({}), }, }, data() { diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_failed_to_merge.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_failed_to_merge.vue index e973a2350a3..42e9261b82c 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_failed_to_merge.vue +++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_failed_to_merge.vue @@ -17,7 +17,6 @@ export default { mr: { type: Object, required: true, - default: () => ({}), }, }, diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merged.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merged.vue index 5177eab790b..a1759b1a815 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merged.vue +++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merged.vue @@ -25,12 +25,10 @@ export default { mr: { type: Object, required: true, - default: () => ({}), }, service: { type: Object, required: true, - default: () => ({}), }, }, data() { diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merging.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merging.vue index 32749b8b018..1c245b584ea 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merging.vue +++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merging.vue @@ -11,7 +11,6 @@ export default { mr: { type: Object, required: true, - default: () => ({}), }, }, data() { diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/ready_to_merge.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/ready_to_merge.vue index 2d0b7fe46a6..f33f4d3fda0 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/states/ready_to_merge.vue +++ b/app/assets/javascripts/vue_merge_request_widget/components/states/ready_to_merge.vue @@ -188,13 +188,6 @@ export default { return this.mr.preferredAutoMergeStrategy; }, - isSHAMismatch() { - if (this.glFeatures.mergeRequestWidgetGraphql) { - return this.mr.sha !== this.state.diffHeadSha; - } - - return this.mr.isSHAMismatch; - }, squashIsSelected() { if (this.glFeatures.mergeRequestWidgetGraphql) { return this.isSquashReadOnly ? this.state.squashOnMerge : this.state.squash; @@ -573,21 +566,6 @@ export default { -
- - - - - - -
-
+import { GlButton } from '@gitlab/ui'; +import { I18N_SHA_MISMATCH } from '../../i18n'; import statusIcon from '../mr_widget_status_icon.vue'; export default { name: 'ShaMismatch', components: { statusIcon, + GlButton, + }, + i18n: { + I18N_SHA_MISMATCH, + }, + props: { + mr: { + type: Object, + required: true, + }, }, }; diff --git a/app/assets/javascripts/vue_merge_request_widget/i18n.js b/app/assets/javascripts/vue_merge_request_widget/i18n.js index e8e522a01e9..c88e795e5f3 100644 --- a/app/assets/javascripts/vue_merge_request_widget/i18n.js +++ b/app/assets/javascripts/vue_merge_request_widget/i18n.js @@ -5,3 +5,8 @@ export const SQUASH_BEFORE_MERGE = { checkboxLabel: __('Squash commits'), helpLabel: __('What is squashing?'), }; + +export const I18N_SHA_MISMATCH = { + warningMessage: __('Merge blocked: new changes were just added.'), + actionButtonLabel: __('Review changes'), +}; diff --git a/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue b/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue index 75e74e17182..a8a9df598f5 100644 --- a/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue +++ b/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue @@ -38,6 +38,7 @@ import RebaseState from './components/states/mr_widget_rebase.vue'; import NothingToMergeState from './components/states/nothing_to_merge.vue'; import PipelineFailedState from './components/states/pipeline_failed.vue'; import ReadyToMergeState from './components/states/ready_to_merge.vue'; +import ShaMismatch from './components/states/sha_mismatch.vue'; import UnresolvedDiscussionsState from './components/states/unresolved_discussions.vue'; import WorkInProgressState from './components/states/work_in_progress.vue'; // import ExtensionsContainer from './components/extensions/container'; @@ -72,7 +73,7 @@ export default { 'mr-widget-not-allowed': NotAllowedState, 'mr-widget-missing-branch': MissingBranchState, 'mr-widget-ready-to-merge': ReadyToMergeState, - 'sha-mismatch': ReadyToMergeState, + 'sha-mismatch': ShaMismatch, 'mr-widget-checking': CheckingState, 'mr-widget-unresolved-discussions': UnresolvedDiscussionsState, 'mr-widget-pipeline-blocked': PipelineBlockedState, diff --git a/app/controllers/import/available_namespaces_controller.rb b/app/controllers/import/available_namespaces_controller.rb index c6211b33d28..0c2af13d3f3 100644 --- a/app/controllers/import/available_namespaces_controller.rb +++ b/app/controllers/import/available_namespaces_controller.rb @@ -4,6 +4,6 @@ class Import::AvailableNamespacesController < ApplicationController feature_category :importers def index - render json: NamespaceSerializer.new.represent(current_user.manageable_groups_with_routes) + render json: NamespaceSerializer.new.represent(current_user.manageable_groups_with_routes(include_groups_with_developer_maintainer_access: true)) end end diff --git a/app/controllers/import/base_controller.rb b/app/controllers/import/base_controller.rb index 1121ecfb65c..53856e4575b 100644 --- a/app/controllers/import/base_controller.rb +++ b/app/controllers/import/base_controller.rb @@ -11,8 +11,7 @@ class Import::BaseController < ApplicationController format.json do render json: { imported_projects: serialized_imported_projects, provider_repos: serialized_provider_repos, - incompatible_repos: serialized_incompatible_repos, - namespaces: serialized_namespaces } + incompatible_repos: serialized_incompatible_repos } end format.html end @@ -74,14 +73,6 @@ class Import::BaseController < ApplicationController @already_added_projects ||= filtered(find_already_added_projects(provider_name)) end - def serialized_namespaces - NamespaceSerializer.new.represent(namespaces) - end - - def namespaces - current_user.manageable_groups_with_routes - end - # rubocop: disable CodeReuse/ActiveRecord def find_already_added_projects(import_type) current_user.created_projects.where(import_type: import_type).with_import_state diff --git a/app/controllers/projects/pipelines_controller.rb b/app/controllers/projects/pipelines_controller.rb index 1ee92009a76..e983fd9c993 100644 --- a/app/controllers/projects/pipelines_controller.rb +++ b/app/controllers/projects/pipelines_controller.rb @@ -12,12 +12,12 @@ class Projects::PipelinesController < Projects::ApplicationController before_action :authorize_read_ci_cd_analytics!, only: [:charts] before_action :authorize_create_pipeline!, only: [:new, :create, :config_variables] before_action :authorize_update_pipeline!, only: [:retry, :cancel] - before_action do - push_frontend_feature_flag(:graphql_pipeline_details, project, type: :development, default_enabled: :yaml) - push_frontend_feature_flag(:graphql_pipeline_details_users, current_user, type: :development, default_enabled: :yaml) - end before_action :ensure_pipeline, only: [:show, :downloadable_artifacts] + before_action do + push_frontend_feature_flag(:pipeline_source_filter, project, type: :development, default_enabled: :yaml) + end + # Will be removed with https://gitlab.com/gitlab-org/gitlab/-/issues/225596 before_action :redirect_for_legacy_scope_filter, only: [:index], if: -> { request.format.html? } diff --git a/app/controllers/repositories/git_http_client_controller.rb b/app/controllers/repositories/git_http_client_controller.rb index 76d9983d341..b3adda8c633 100644 --- a/app/controllers/repositories/git_http_client_controller.rb +++ b/app/controllers/repositories/git_http_client_controller.rb @@ -33,7 +33,7 @@ module Repositories end def authenticate_user - @authentication_result = Gitlab::Auth::Result.new + @authentication_result = Gitlab::Auth::Result::EMPTY if allow_basic_auth? && basic_auth_provided? login, password = user_name_and_password(request) diff --git a/app/experiments/application_experiment.rb b/app/experiments/application_experiment.rb index 0dd829e259e..37d87baf30b 100644 --- a/app/experiments/application_experiment.rb +++ b/app/experiments/application_experiment.rb @@ -13,7 +13,7 @@ class ApplicationExperiment < Gitlab::Experiment # rubocop:disable Gitlab/Namesp super publish_to_client - publish_to_database + publish_to_database if @record end def publish_to_client @@ -25,7 +25,6 @@ class ApplicationExperiment < Gitlab::Experiment # rubocop:disable Gitlab/Namesp end def publish_to_database - return unless @record return unless should_track? # if the context contains a namespace, group, project, user, or actor diff --git a/app/models/projects/ci_feature_usage.rb b/app/models/projects/ci_feature_usage.rb new file mode 100644 index 00000000000..a10426e50c9 --- /dev/null +++ b/app/models/projects/ci_feature_usage.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +module Projects + class CiFeatureUsage < ApplicationRecord + self.table_name = 'project_ci_feature_usages' + + belongs_to :project + + validates :project, :feature, presence: true + + enum feature: { + code_coverage: 1, + security_report: 2 + } + + def self.insert_usage(project_id:, feature:, default_branch:) + insert( + { + project_id: project_id, + feature: feature, + default_branch: default_branch + }, + unique_by: 'index_project_ci_feature_usages_unique_columns' + ) + end + end +end diff --git a/app/services/ci/daily_build_group_report_result_service.rb b/app/services/ci/daily_build_group_report_result_service.rb index 820e6e08fc5..25c6d57d961 100644 --- a/app/services/ci/daily_build_group_report_result_service.rb +++ b/app/services/ci/daily_build_group_report_result_service.rb @@ -3,7 +3,13 @@ module Ci class DailyBuildGroupReportResultService def execute(pipeline) - DailyBuildGroupReportResult.upsert_reports(coverage_reports(pipeline)) + if DailyBuildGroupReportResult.upsert_reports(coverage_reports(pipeline)) + Projects::CiFeatureUsage.insert_usage( + project_id: pipeline.project_id, + feature: :code_coverage, + default_branch: pipeline.default_branch? + ) + end end private diff --git a/app/views/projects/pipelines/show.html.haml b/app/views/projects/pipelines/show.html.haml index 1c134d914e9..30ebe4f20b6 100644 --- a/app/views/projects/pipelines/show.html.haml +++ b/app/views/projects/pipelines/show.html.haml @@ -5,9 +5,7 @@ - add_page_specific_style 'page_bundles/pipeline' - add_page_specific_style 'page_bundles/reports' - add_page_specific_style 'page_bundles/ci_status' - -- if Feature.enabled?(:graphql_pipeline_details, @project, default_enabled: :yaml) || Feature.enabled?(:graphql_pipeline_details_users, @current_user, default_enabled: :yaml) - - add_page_startup_graphql_call('pipelines/get_pipeline_details', { projectPath: @project.full_path, iid: @pipeline.iid }) +- add_page_startup_graphql_call('pipelines/get_pipeline_details', { projectPath: @project.full_path, iid: @pipeline.iid }) .js-pipeline-container{ data: { controller_action: "#{controller.action_name}" } } #js-pipeline-header-vue.pipeline-header-container{ data: { full_path: @project.full_path, pipeline_iid: @pipeline.iid, pipeline_id: @pipeline.id, pipelines_path: project_pipelines_path(@project) } } diff --git a/config/feature_flags/development/graphql_pipeline_details.yml b/config/feature_flags/development/graphql_pipeline_details.yml deleted file mode 100644 index 57b32c55da3..00000000000 --- a/config/feature_flags/development/graphql_pipeline_details.yml +++ /dev/null @@ -1,8 +0,0 @@ ---- -name: graphql_pipeline_details -introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/46380 -rollout_issue_url: -type: development -group: group::pipeline authoring -default_enabled: true -milestone: '13.6' diff --git a/config/feature_flags/development/graphql_pipeline_details_users.yml b/config/feature_flags/development/graphql_pipeline_details_users.yml deleted file mode 100644 index 97a2918bed5..00000000000 --- a/config/feature_flags/development/graphql_pipeline_details_users.yml +++ /dev/null @@ -1,8 +0,0 @@ ---- -name: graphql_pipeline_details_users -introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/52092 -rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/299112 -milestone: '13.9' -type: development -group: group::pipeline authoring -default_enabled: false diff --git a/db/migrate/20210812171704_create_project_ci_feature_usages.rb b/db/migrate/20210812171704_create_project_ci_feature_usages.rb new file mode 100644 index 00000000000..376512bcb44 --- /dev/null +++ b/db/migrate/20210812171704_create_project_ci_feature_usages.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +class CreateProjectCiFeatureUsages < ActiveRecord::Migration[6.1] + def change + create_table :project_ci_feature_usages do |t| + t.references :project, index: false, foreign_key: { on_delete: :cascade }, null: false + t.integer :feature, null: false, limit: 2 + t.boolean :default_branch, default: false, null: false + t.index [:project_id, :feature, :default_branch], unique: true, name: 'index_project_ci_feature_usages_unique_columns' + end + end +end diff --git a/db/migrate/20210816161107_remove_index_containing_faulty_regex.rb b/db/migrate/20210816161107_remove_index_containing_faulty_regex.rb new file mode 100644 index 00000000000..d625ae6d13d --- /dev/null +++ b/db/migrate/20210816161107_remove_index_containing_faulty_regex.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +class RemoveIndexContainingFaultyRegex < ActiveRecord::Migration[6.1] + include Gitlab::Database::MigrationHelpers + + INDEX_NAME = "tmp_index_merge_requests_draft_and_status" + + disable_ddl_transaction! + + def up + remove_concurrent_index_by_name :merge_requests, INDEX_NAME + end + + def down + # noop + # + end +end diff --git a/db/post_migrate/20210609202501_schedule_backfill_draft_status_on_merge_requests.rb b/db/post_migrate/20210609202501_schedule_backfill_draft_status_on_merge_requests.rb index 72c4168af50..ed9a64c84ab 100644 --- a/db/post_migrate/20210609202501_schedule_backfill_draft_status_on_merge_requests.rb +++ b/db/post_migrate/20210609202501_schedule_backfill_draft_status_on_merge_requests.rb @@ -1,32 +1,13 @@ # frozen_string_literal: true class ScheduleBackfillDraftStatusOnMergeRequests < ActiveRecord::Migration[6.1] - include Gitlab::Database::MigrationHelpers - - INDEX_NAME = "tmp_index_merge_requests_draft_and_status" - MIGRATION = 'BackfillDraftStatusOnMergeRequests' - DELAY_INTERVAL = 2.minutes - BATCH_SIZE = 100 - - disable_ddl_transaction! - def up - add_concurrent_index :merge_requests, :id, - where: "draft = false AND state_id = 1 AND ((title)::text ~* '^\\[draft\\]|\\(draft\\)|draft:|draft|\\[WIP\\]|WIP:|WIP'::text)", - name: INDEX_NAME - - eligible_mrs = Gitlab::BackgroundMigration::BackfillDraftStatusOnMergeRequests::MergeRequest.eligible - - queue_background_migration_jobs_by_range_at_intervals( - eligible_mrs, - MIGRATION, - DELAY_INTERVAL, - track_jobs: true, - batch_size: BATCH_SIZE - ) + # noop + # end def down - remove_concurrent_index_by_name :merge_requests, INDEX_NAME + # noop + # end end diff --git a/db/post_migrate/20210806011811_schedule_backfill_draft_column_on_merge_requests_rerun.rb b/db/post_migrate/20210806011811_schedule_backfill_draft_column_on_merge_requests_rerun.rb index c3403d6ba47..90bca79624d 100644 --- a/db/post_migrate/20210806011811_schedule_backfill_draft_column_on_merge_requests_rerun.rb +++ b/db/post_migrate/20210806011811_schedule_backfill_draft_column_on_merge_requests_rerun.rb @@ -1,24 +1,9 @@ # frozen_string_literal: true class ScheduleBackfillDraftColumnOnMergeRequestsRerun < ActiveRecord::Migration[6.1] - include Gitlab::Database::MigrationHelpers - - MIGRATION = 'BackfillDraftStatusOnMergeRequests' - DELAY_INTERVAL = 2.minutes - BATCH_SIZE = 50 - - disable_ddl_transaction! - def up - eligible_mrs = Gitlab::BackgroundMigration::BackfillDraftStatusOnMergeRequests::MergeRequest.eligible - - queue_background_migration_jobs_by_range_at_intervals( - eligible_mrs, - MIGRATION, - DELAY_INTERVAL, - track_jobs: true, - batch_size: BATCH_SIZE - ) + # noop + # end def down diff --git a/db/schema_migrations/20210812171704 b/db/schema_migrations/20210812171704 new file mode 100644 index 00000000000..2471f7cdd38 --- /dev/null +++ b/db/schema_migrations/20210812171704 @@ -0,0 +1 @@ +7c62c47ebad110a343c1f9834ae34bd0fa2bad763025da06f911e127a7380542 \ No newline at end of file diff --git a/db/schema_migrations/20210816161107 b/db/schema_migrations/20210816161107 new file mode 100644 index 00000000000..1b8ab5265b7 --- /dev/null +++ b/db/schema_migrations/20210816161107 @@ -0,0 +1 @@ +1e4d0b062c8e43b1af37c6cf869f9c173248d7bf5451b4aa5468d48c1004b97c \ No newline at end of file diff --git a/db/structure.sql b/db/structure.sql index 27c3ed52db4..c5ab3c943b3 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -16935,6 +16935,22 @@ CREATE SEQUENCE project_ci_cd_settings_id_seq ALTER SEQUENCE project_ci_cd_settings_id_seq OWNED BY project_ci_cd_settings.id; +CREATE TABLE project_ci_feature_usages ( + id bigint NOT NULL, + project_id bigint NOT NULL, + feature smallint NOT NULL, + default_branch boolean DEFAULT false NOT NULL +); + +CREATE SEQUENCE project_ci_feature_usages_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER SEQUENCE project_ci_feature_usages_id_seq OWNED BY project_ci_feature_usages.id; + CREATE TABLE project_compliance_framework_settings ( project_id bigint NOT NULL, framework_id bigint, @@ -20649,6 +20665,8 @@ ALTER TABLE ONLY project_auto_devops ALTER COLUMN id SET DEFAULT nextval('projec ALTER TABLE ONLY project_ci_cd_settings ALTER COLUMN id SET DEFAULT nextval('project_ci_cd_settings_id_seq'::regclass); +ALTER TABLE ONLY project_ci_feature_usages ALTER COLUMN id SET DEFAULT nextval('project_ci_feature_usages_id_seq'::regclass); + ALTER TABLE ONLY project_compliance_framework_settings ALTER COLUMN project_id SET DEFAULT nextval('project_compliance_framework_settings_project_id_seq'::regclass); ALTER TABLE ONLY project_custom_attributes ALTER COLUMN id SET DEFAULT nextval('project_custom_attributes_id_seq'::regclass); @@ -22239,6 +22257,9 @@ ALTER TABLE ONLY project_auto_devops ALTER TABLE ONLY project_ci_cd_settings ADD CONSTRAINT project_ci_cd_settings_pkey PRIMARY KEY (id); +ALTER TABLE ONLY project_ci_feature_usages + ADD CONSTRAINT project_ci_feature_usages_pkey PRIMARY KEY (id); + ALTER TABLE ONLY project_compliance_framework_settings ADD CONSTRAINT project_compliance_framework_settings_pkey PRIMARY KEY (project_id); @@ -24806,6 +24827,8 @@ CREATE UNIQUE INDEX index_project_auto_devops_on_project_id ON project_auto_devo CREATE UNIQUE INDEX index_project_ci_cd_settings_on_project_id ON project_ci_cd_settings USING btree (project_id); +CREATE UNIQUE INDEX index_project_ci_feature_usages_unique_columns ON project_ci_feature_usages USING btree (project_id, feature, default_branch); + CREATE INDEX index_project_compliance_framework_settings_on_framework_id ON project_compliance_framework_settings USING btree (framework_id); CREATE INDEX index_project_compliance_framework_settings_on_project_id ON project_compliance_framework_settings USING btree (project_id); @@ -25720,8 +25743,6 @@ CREATE INDEX tmp_idx_on_namespaces_delayed_project_removal ON namespaces USING b CREATE INDEX tmp_index_approval_project_rules_scanners ON approval_project_rules USING gin (scanners) WHERE (scanners @> '{cluster_image_scanning}'::text[]); -CREATE INDEX tmp_index_merge_requests_draft_and_status ON merge_requests USING btree (id) WHERE ((draft = false) AND (state_id = 1) AND ((title)::text ~* '^\[draft\]|\(draft\)|draft:|draft|\[WIP\]|WIP:|WIP'::text)); - CREATE INDEX tmp_index_namespaces_empty_traversal_ids_with_child_namespaces ON namespaces USING btree (id) WHERE ((parent_id IS NOT NULL) AND (traversal_ids = '{}'::integer[])); CREATE INDEX tmp_index_namespaces_empty_traversal_ids_with_root_namespaces ON namespaces USING btree (id) WHERE ((parent_id IS NULL) AND (traversal_ids = '{}'::integer[])); @@ -27056,6 +27077,9 @@ ALTER TABLE ONLY epic_user_mentions ALTER TABLE ONLY approver_groups ADD CONSTRAINT fk_rails_1cdcbd7723 FOREIGN KEY (group_id) REFERENCES namespaces(id) ON DELETE CASCADE; +ALTER TABLE ONLY project_ci_feature_usages + ADD CONSTRAINT fk_rails_1deedbf64b FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE; + ALTER TABLE ONLY packages_tags ADD CONSTRAINT fk_rails_1dfc868911 FOREIGN KEY (package_id) REFERENCES packages_packages(id) ON DELETE CASCADE; diff --git a/doc/administration/geo/index.md b/doc/administration/geo/index.md index 6afbf76ac94..7175d41abd8 100644 --- a/doc/administration/geo/index.md +++ b/doc/administration/geo/index.md @@ -193,11 +193,8 @@ This list of limitations only reflects the latest version of GitLab. If you are - The **primary** site has to be online for OAuth login to happen. Existing sessions and Git are not affected. Support for the **secondary** site to use an OAuth provider independent from the primary is [being planned](https://gitlab.com/gitlab-org/gitlab/-/issues/208465). - The installation takes multiple manual steps that together can take about an hour depending on circumstances. We are working on improving this experience. See [Omnibus GitLab issue #2978](https://gitlab.com/gitlab-org/omnibus-gitlab/-/issues/2978) for details. - Real-time updates of issues/merge requests (for example, via long polling) doesn't work on the **secondary** site. -- [Selective synchronization](replication/configuration.md#selective-synchronization) applies only to files and repositories. Other datasets are replicated to the **secondary** site in full, making it inappropriate for use as an access control mechanism. -- Object pools for forked project deduplication work only on the **primary** site, and are duplicated on the **secondary** site. - GitLab Runners cannot register with a **secondary** site. Support for this is [planned for the future](https://gitlab.com/gitlab-org/gitlab/-/issues/3294). -- Configuring Geo **secondary** sites to [use high-availability configurations of PostgreSQL](https://gitlab.com/groups/gitlab-org/-/epics/2536) is currently in **alpha** support. -- [Selective synchronization](replication/configuration.md#selective-synchronization) only limits what repositories are replicated. The entire PostgreSQL data is still replicated. Selective synchronization is not built to accommodate compliance / export control use cases. +- [Selective synchronization](replication/configuration.md#selective-synchronization) only limits what repositories and files are replicated. The entire PostgreSQL data is still replicated. Selective synchronization is not built to accommodate compliance / export control use cases. ### Limitations on replication/verification diff --git a/doc/administration/geo/replication/datatypes.md b/doc/administration/geo/replication/datatypes.md index 62b366e9a4a..a696e5410e5 100644 --- a/doc/administration/geo/replication/datatypes.md +++ b/doc/administration/geo/replication/datatypes.md @@ -32,7 +32,6 @@ verification methods: | Git | Project repository | Geo with Gitaly | Gitaly Checksum | | Git | Project wiki repository | Geo with Gitaly | Gitaly Checksum | | Git | Project designs repository | Geo with Gitaly | Gitaly Checksum | -| Git | Object pools for forked project deduplication | Geo with Gitaly | _Not implemented_ | | Git | Project Snippets | Geo with Gitaly | Gitaly Checksum | | Git | Personal Snippets | Geo with Gitaly | Gitaly Checksum | | Git | Group wiki repository | Geo with Gitaly | _Not implemented_ | @@ -69,6 +68,8 @@ or using LVM. It requires no special file system and can work with NFS or a mounted Storage Appliance (there may be performance limitations when using a remote file system). +Geo will trigger garbage collection in Gitaly to [deduplicate forked repositories](../../../development/git_object_deduplication.md#git-object-deduplication-and-gitlab-geo) on Geo secondary sites. + Communication is done via Gitaly's own gRPC API. There are three possible ways of synchronization: - Using regular Git clone/fetch from one Geo site to another (with special authentication). @@ -186,7 +187,6 @@ successfully, you must replicate their data using some other means. |[CI job artifacts (other than Job Logs)](../../../ci/pipelines/job_artifacts.md) | **Yes** (10.4) | [No](https://gitlab.com/gitlab-org/gitlab/-/issues/8923) | Via Object Storage provider if supported. Native Geo support (Beta). | Verified only manually using [Integrity Check Rake Task](../../raketasks/check.md) on both sites and comparing the output between them. | |[CI Pipeline Artifacts](https://gitlab.com/gitlab-org/gitlab/-/blob/master/app/models/ci/pipeline_artifact.rb) | [**Yes** (13.11)](https://gitlab.com/gitlab-org/gitlab/-/issues/238464) | [**Yes** (13.11)](https://gitlab.com/gitlab-org/gitlab/-/issues/238464) | Via Object Storage provider if supported. Native Geo support (Beta). | Persists additional artifacts after a pipeline completes | |[Job logs](../../job_logs.md) | **Yes** (10.4) | [No](https://gitlab.com/gitlab-org/gitlab/-/issues/8923) | Via Object Storage provider if supported. Native Geo support (Beta). | Verified only on transfer or manually using [Integrity Check Rake Task](../../raketasks/check.md) on both sites and comparing the output between them. | -|[Object pools for forked project deduplication](../../../development/git_object_deduplication.md) | **Yes** | No | No | | |[Container Registry](../../packages/container_registry.md) | **Yes** (12.3) | No | No | Disabled by default. See [instructions](docker_registry.md) to enable. | |[Content in object storage (beta)](object_storage.md) | **Yes** (12.4) | [No](https://gitlab.com/gitlab-org/gitlab/-/issues/13845) | No | | |[Infrastructure Registry for Terraform Module](../../../user/packages/terraform_module_registry/index.md) | **Yes** (14.0) | [**Yes**](#limitation-of-verification-for-files-in-object-storage) (14.0) | Via Object Storage provider if supported. Native Geo support (Beta). | Behind feature flag `geo_package_file_replication`, enabled by default. | diff --git a/doc/ci/jobs/index.md b/doc/ci/jobs/index.md index 0ed8b68c0ce..aa12e1181cb 100644 --- a/doc/ci/jobs/index.md +++ b/doc/ci/jobs/index.md @@ -168,7 +168,7 @@ for a single run of the manual job. > [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/21767) in GitLab 11.4. -When you do not want to run a job immediately, you can use the [`when:delayed`](../yaml/index.md#whendelayed) keyword to +When you do not want to run a job immediately, you can use the [`when:delayed`](../jobs/job_control.md#run-a-job-after-a-delay) keyword to delay a job's execution for a certain period. This is especially useful for timed incremental rollout where new code is rolled out gradually. diff --git a/doc/ci/jobs/job_control.md b/doc/ci/jobs/job_control.md index 634214aedc3..b8b05426297 100644 --- a/doc/ci/jobs/job_control.md +++ b/doc/ci/jobs/job_control.md @@ -497,6 +497,120 @@ test: - "README.md" ``` +## Create a job that must be run manually + +You can require that a job doesn't run unless a user starts it. This is called a **manual job**. +You might want to use a manual job for something like deploying to production. + +To specify a job as manual, add [`when: manual`](../yaml/index.md#when) to the job +in the `.gitlab-ci.yml` file. + +By default, manual jobs display as skipped when the pipeline starts. + +You can use [protected branches](../../user/project/protected_branches.md) to more strictly +[protect manual deployments](#protect-manual-jobs) from being run by unauthorized users. + +### Types of manual jobs + +Manual jobs can be either optional or blocking: + +- **Optional**: The default setting for manual jobs. + - They have [`allow_failure: true`](../yaml/index.md#allow_failure) by default. + - The status does not contribute to the overall pipeline status. A pipeline can + succeed even if all of its manual jobs fail. +- **Blocking**: An optional setting for manual jobs. + - Add `allow_failure: false` to the job configuration. + - The pipeline stops at the stage where the job is defined. To let the pipeline + continue running, [run the manual job](#run-a-manual-job). + - Merge requests in projects with [merge when pipeline succeeds](../../user/project/merge_requests/merge_when_pipeline_succeeds.md) + enabled can't be merged with a blocked pipeline. Blocked pipelines show a status + of **blocked**. + +### Run a manual job + +To run a manual job, you must have permission to merge to the assigned branch. + +To run a manual job: + +1. Go to the pipeline, job, [environment](../environments/index.md#configure-manual-deployments), + or deployment view. +1. Next to the manual job, select **Play** (**{play}**). + +### Protect manual jobs **(PREMIUM)** + +Use [protected environments](../environments/protected_environments.md) +to define a list of users authorized to run a manual job. You can authorize only +the users associated with a protected environment to trigger manual jobs, which can: + +- More precisely limit who can deploy to an environment. +- Block a pipeline until an approved user "approves" it. + +To protect a manual job: + +1. Add an `environment` to the job. For example: + + ```yaml + deploy_prod: + stage: deploy + script: + - echo "Deploy to production server" + environment: + name: production + url: https://example.com + when: manual + rules: + - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH + ``` + +1. In the [protected environments settings](../environments/protected_environments.md#protecting-environments), + select the environment (`production` in this example) and add the users, roles or groups + that are authorized to trigger the manual job to the **Allowed to Deploy** list. Only those in + this list can trigger this manual job, as well as GitLab administrators + who are always able to use protected environments. + +You can use protected environments with blocking manual jobs to have a list of users +allowed to approve later pipeline stages. Add `allow_failure: false` to the protected +manual job and the pipeline's next stages only run after the manual job is triggered +by authorized users. + +## Run a job after a delay + +> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/51352) in GitLab 11.4. + +Use [`when: delayed`](../yaml/index.md#when) to execute scripts after a waiting period, or if you want to avoid +jobs immediately entering the `pending` state. + +You can set the period with `start_in` keyword. The value of `start_in` is an elapsed time in seconds, unless a unit is +provided. `start_in` must be less than or equal to one week. Examples of valid values include: + +- `'5'` (a value with no unit must be surrounded by single quotes) +- `5 seconds` +- `30 minutes` +- `1 day` +- `1 week` + +When a stage includes a delayed job, the pipeline doesn't progress until the delayed job finishes. +You can use this keyword to insert delays between different stages. + +The timer of a delayed job starts immediately after the previous stage completes. +Similar to other types of jobs, a delayed job's timer doesn't start unless the previous stage passes. + +The following example creates a job named `timed rollout 10%` that is executed 30 minutes after the previous stage completes: + +```yaml +timed rollout 10%: + stage: deploy + script: echo 'Rolling out 10% ...' + when: delayed + start_in: 30 minutes +``` + +To stop the active timer of a delayed job, click the **{time-out}** (**Unschedule**) button. +This job can no longer be scheduled to run automatically. You can, however, execute the job manually. + +To start a delayed job immediately, select **Play** (**{play}**). +Soon GitLab Runner starts the job. + ## Use predefined CI/CD variables to run jobs only in specific pipeline types You can use [predefined CI/CD variables](../variables/predefined_variables.md) to choose diff --git a/doc/ci/migration/circleci.md b/doc/ci/migration/circleci.md index 968adf2e161..106f5b3d819 100644 --- a/doc/ci/migration/circleci.md +++ b/doc/ci/migration/circleci.md @@ -188,7 +188,7 @@ release-branch-workflow: - testing ``` -Example of the same workflow using [`when: manual`](../yaml/index.md#whenmanual) in GitLab CI/CD: +Example of the same workflow using [`when: manual`](../jobs/job_control.md#create-a-job-that-must-be-run-manually) in GitLab CI/CD: ```yaml deploy_prod: diff --git a/doc/ci/migration/jenkins.md b/doc/ci/migration/jenkins.md index ea33932f1d6..1a987a0ce47 100644 --- a/doc/ci/migration/jenkins.md +++ b/doc/ci/migration/jenkins.md @@ -108,8 +108,8 @@ There are some high level differences between the products worth mentioning: - The `.gitlab-ci.yml` file is checked in to the root of your repository, much like a Jenkinsfile, but is in the YAML format (see [complete reference](../yaml/index.md)) instead of a Groovy DSL. It's most analogous to the declarative Jenkinsfile format. -- Manual approvals or gates can be set up as [`when:manual` jobs](../yaml/index.md#whenmanual). These can - also leverage [`protected environments`](../yaml/index.md#protecting-manual-jobs) +- Manual approvals or gates can be set up as [`when:manual` jobs](../jobs/job_control.md#create-a-job-that-must-be-run-manually). These can + also leverage [`protected environments`](../jobs/job_control.md#run-a-job-after-a-delay) to control who is able to approve them. - GitLab comes with a [container registry](../../user/packages/container_registry/index.md), and we recommend using container images to set up your build environment. For example, set up one pipeline that builds your build environment diff --git a/doc/ci/pipelines/index.md b/doc/ci/pipelines/index.md index 0ccbac2ab2d..22f18235f99 100644 --- a/doc/ci/pipelines/index.md +++ b/doc/ci/pipelines/index.md @@ -206,7 +206,7 @@ For each `var` or `file_var`, a key and value are required. ### Add manual interaction to your pipeline -Manual actions, configured using the [`when:manual`](../yaml/index.md#whenmanual) keyword, +[Manual jobs](../jobs/job_control.md#create-a-job-that-must-be-run-manually), allow you to require manual interaction before moving forward in the pipeline. You can do this straight from the pipeline graph. Just click the play button diff --git a/doc/ci/yaml/index.md b/doc/ci/yaml/index.md index 46115529b53..293390b2c67 100644 --- a/doc/ci/yaml/index.md +++ b/doc/ci/yaml/index.md @@ -1887,8 +1887,8 @@ variables: ### `allow_failure` Use `allow_failure` when you want to let a job fail without impacting the rest of the CI -suite. The default value is `false`, except for [manual](#whenmanual) jobs that use -the `when: manual` syntax. +suite. The default value is `false`, except for [manual](../jobs/job_control.md#create-a-job-that-must-be-run-manually) jobs that use +the [`when: manual`](#when) syntax. In jobs that use [`rules:`](#rules), all jobs default to `allow_failure: false`, *including* `when: manual` jobs. @@ -1952,28 +1952,23 @@ test_job_2: ### `when` -Use `when` to implement jobs that run in case of failure or despite the -failure. +Use `when` to configure the conditions for when jobs run. If not defined in a job, +the default value is `when: on_success`. -The valid values of `when` are: +**Keyword type**: Job keyword. You can use it only as part of a job. -1. `on_success` (default) - Execute job only when all jobs in earlier stages succeed, - or are considered successful because they have `allow_failure: true`. -1. `on_failure` - Execute job only when at least one job in an earlier stage fails. -1. `always` - Execute job regardless of the status of jobs in earlier stages. -1. `manual` - Execute job [manually](#whenmanual). -1. `delayed` - [Delay the execution of a job](#whendelayed) for a specified duration. - Added in GitLab 11.14. -1. `never`: - - With job [`rules`](#rules), don't execute job. - - With [`workflow:rules`](#workflow), don't run pipeline. +**Possible inputs**: -In the following example, the script: +- `on_success` (default): Run the job only when all jobs in earlier stages succeed + or have `allow_failure: true`. +- `manual`: Run the job only when [triggered manually](../jobs/job_control.md#create-a-job-that-must-be-run-manually). +- `always`: Run the job regardless of the status of jobs in earlier stages. +- `on_failure`: Run the job only when at least one job in an earlier stage fails. +- `delayed`: [Delay the execution of a job](../jobs/job_control.md#run-a-job-after-a-delay) + for a specified duration. +- `never`: Don't run the job. -1. Executes `cleanup_build_job` only when `build_job` fails. -1. Always executes `cleanup_job` as the last step in pipeline regardless of - success or failure. -1. Executes `deploy_job` when you run it manually in the GitLab UI. +**Example of `when`**: ```yaml stages: @@ -2012,116 +2007,26 @@ cleanup_job: when: always ``` -#### `when:manual` +In this example, the script: -A manual job is a type of job that is not executed automatically and must be explicitly -started by a user. You might want to use manual jobs for things like deploying to production. +1. Executes `cleanup_build_job` only when `build_job` fails. +1. Always executes `cleanup_job` as the last step in pipeline regardless of + success or failure. +1. Executes `deploy_job` when you run it manually in the GitLab UI. -To make a job manual, add `when: manual` to its configuration. +**Additional details**: -When the pipeline starts, manual jobs display as skipped and do not run automatically. -They can be started from the pipeline, job, [environment](../environments/index.md#configure-manual-deployments), -and deployment views. +- In [GitLab 13.5](https://gitlab.com/gitlab-org/gitlab/-/issues/201938) and later, you + can use `when:manual` in the same job as [`trigger`](#trigger). In GitLab 13.4 and + earlier, using them together causes the error `jobs:#{job-name} when should be on_success, on_failure or always`. +- The default behavior of `allow_failure` changes to `true` with `when: manual`. + However, if you use `when: manual` with [`rules`](#rules), `allow_failure` defaults + to `false`. -Manual jobs can be either optional or blocking: +**Related topics**: -- **Optional**: Manual jobs have [`allow_failure: true](#allow_failure) set by default - and are considered optional. The status of an optional manual job does not contribute - to the overall pipeline status. A pipeline can succeed even if all its manual jobs fail. - -- **Blocking**: To make a blocking manual job, add `allow_failure: false` to its configuration. - Blocking manual jobs stop further execution of the pipeline at the stage where the - job is defined. To let the pipeline continue running, click **{play}** (play) on - the blocking manual job. - - Merge requests in projects with [merge when pipeline succeeds](../../user/project/merge_requests/merge_when_pipeline_succeeds.md) - enabled can't be merged with a blocked pipeline. Blocked pipelines show a status - of **blocked**. - -When you use [`rules:`](#rules), `allow_failure` defaults to `false`, including for manual jobs. - -To trigger a manual job, a user must have permission to merge to the assigned branch. -You can use [protected branches](../../user/project/protected_branches.md) to more strictly -[protect manual deployments](#protecting-manual-jobs) from being run by unauthorized users. - -In [GitLab 13.5](https://gitlab.com/gitlab-org/gitlab/-/issues/201938) and later, you -can use `when:manual` in the same job as [`trigger`](#trigger). In GitLab 13.4 and -earlier, using them together causes the error `jobs:#{job-name} when should be on_success, on_failure or always`. - -##### Protecting manual jobs **(PREMIUM)** - -Use [protected environments](../environments/protected_environments.md) -to define a list of users authorized to run a manual job. You can authorize only -the users associated with a protected environment to trigger manual jobs, which can: - -- More precisely limit who can deploy to an environment. -- Block a pipeline until an approved user "approves" it. - -To protect a manual job: - -1. Add an `environment` to the job. For example: - - ```yaml - deploy_prod: - stage: deploy - script: - - echo "Deploy to production server" - environment: - name: production - url: https://example.com - when: manual - rules: - - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH - ``` - -1. In the [protected environments settings](../environments/protected_environments.md#protecting-environments), - select the environment (`production` in this example) and add the users, roles or groups - that are authorized to trigger the manual job to the **Allowed to Deploy** list. Only those in - this list can trigger this manual job, as well as GitLab administrators - who are always able to use protected environments. - -You can use protected environments with blocking manual jobs to have a list of users -allowed to approve later pipeline stages. Add `allow_failure: false` to the protected -manual job and the pipeline's next stages only run after the manual job is triggered -by authorized users. - -#### `when:delayed` - -> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/51352) in GitLab 11.4. - -Use `when: delayed` to execute scripts after a waiting period, or if you want to avoid -jobs immediately entering the `pending` state. - -You can set the period with `start_in` keyword. The value of `start_in` is an elapsed time in seconds, unless a unit is -provided. `start_in` must be less than or equal to one week. Examples of valid values include: - -- `'5'` -- `5 seconds` -- `30 minutes` -- `1 day` -- `1 week` - -When a stage includes a delayed job, the pipeline doesn't progress until the delayed job finishes. -You can use this keyword to insert delays between different stages. - -The timer of a delayed job starts immediately after the previous stage completes. -Similar to other types of jobs, a delayed job's timer doesn't start unless the previous stage passes. - -The following example creates a job named `timed rollout 10%` that is executed 30 minutes after the previous stage completes: - -```yaml -timed rollout 10%: - stage: deploy - script: echo 'Rolling out 10% ...' - when: delayed - start_in: 30 minutes -``` - -To stop the active timer of a delayed job, click the **{time-out}** (**Unschedule**) button. -This job can no longer be scheduled to run automatically. You can, however, execute the job manually. - -To start a delayed job immediately, click the **Play** button. -Soon GitLab Runner picks up and starts the job. +- `when` can be used with [`rules`](#rules) for more dynamic job control. +- `when` can be used with [`workflow`](#workflow) to control when a pipeline can start. ### `environment` @@ -2249,7 +2154,7 @@ In the above example, the `review_app` job deploys to the `review` environment. A new `stop_review_app` job is listed under `on_stop`. After the `review_app` job is finished, it triggers the `stop_review_app` job based on what is defined under `when`. In this case, -it is set to `manual`, so it needs a [manual action](#whenmanual) from +it is set to `manual`, so it needs a [manual action](../jobs/job_control.md#create-a-job-that-must-be-run-manually) from the GitLab UI to run. Also in the example, `GIT_STRATEGY` is set to `none`. If the @@ -3697,7 +3602,7 @@ view which job triggered a downstream pipeline. In the [pipeline graph](../pipel hover over the downstream pipeline job. In [GitLab 13.5](https://gitlab.com/gitlab-org/gitlab/-/issues/201938) and later, you -can use [`when:manual`](#whenmanual) in the same job as `trigger`. In GitLab 13.4 and +can use [`when:manual`](#when) in the same job as `trigger`. In GitLab 13.4 and earlier, using them together causes the error `jobs:#{job-name} when should be on_success, on_failure or always`. You [cannot start `manual` trigger jobs with the API](https://gitlab.com/gitlab-org/gitlab/-/issues/284086). diff --git a/doc/development/cicd/index.md b/doc/development/cicd/index.md index a5ab0faaadd..76c756b0e95 100644 --- a/doc/development/cicd/index.md +++ b/doc/development/cicd/index.md @@ -79,7 +79,7 @@ case the runner downloads them using a dedicated API endpoint. Artifacts are stored in object storage, while metadata is kept in the database. An important example of artifacts are reports (like JUnit, SAST, and DAST) which are parsed and rendered in the merge request. -Job status transitions are not all automated. A user may run [manual jobs](../../ci/yaml/index.md#whenmanual), cancel a pipeline, retry +Job status transitions are not all automated. A user may run [manual jobs](../../ci/jobs/job_control.md#create-a-job-that-must-be-run-manually), cancel a pipeline, retry specific failed jobs or the entire pipeline. Anything that causes a job to change status triggers `ProcessPipelineService`, as it's responsible for tracking the status of the entire pipeline. diff --git a/doc/development/documentation/index.md b/doc/development/documentation/index.md index ebc07b72ec1..59a1b8c7b99 100644 --- a/doc/development/documentation/index.md +++ b/doc/development/documentation/index.md @@ -469,7 +469,7 @@ If you want to know the in-depth details, here's what's really happening: The following GitLab features are used among others: -- [Manual actions](../../ci/yaml/index.md#whenmanual) +- [Manual jobs](../../ci/jobs/job_control.md#create-a-job-that-must-be-run-manually) - [Multi project pipelines](../../ci/pipelines/multi_project_pipelines.md) - [Review Apps](../../ci/review_apps/index.md) - [Artifacts](../../ci/yaml/index.md#artifacts) diff --git a/doc/install/docker.md b/doc/install/docker.md index a6bc18bd374..7be97a1f2e6 100644 --- a/doc/install/docker.md +++ b/doc/install/docker.md @@ -119,9 +119,16 @@ sudo docker logs -f gitlab After starting a container you can visit `gitlab.example.com` (or `http://192.168.59.103` if you used boot2docker on macOS). It might take a while before the Docker container starts to respond to queries. -The very first time you visit GitLab, you will be asked to set up the admin -password. After you change it, you can log in with username `root` and the -password you set up. + +Visit the GitLab URL, and log in with username `root` +and the password from the following command: + +```shell +sudo docker exec -it gitlab grep 'Password:' /etc/gitlab/initial_root_password +``` + +NOTE: +The password file will be automatically deleted in the first reconfigure run after 24 hours. ### Install GitLab using Docker Compose diff --git a/doc/integration/jira/index.md b/doc/integration/jira/index.md index ebc61b29c5b..febd9907028 100644 --- a/doc/integration/jira/index.md +++ b/doc/integration/jira/index.md @@ -19,7 +19,7 @@ in your GitLab project with any of your projects in Jira. ### Jira integration -This integration connects one or more GitLab project to a Jira instance. The Jira instance +This integration connects one or more GitLab projects to a Jira instance. The Jira instance can be hosted by you or in [Atlassian cloud](https://www.atlassian.com/cloud). The supported Jira versions are `v6.x`, `v7.x`, and `v8.x`. @@ -83,26 +83,31 @@ If these features do not work as expected, it is likely due to a problem with th ### GitLab is unable to comment on a Jira issue -Make sure that the Jira user you set up for the integration has the -correct access permission to post comments on a Jira issue and also to transition -the issue, if you'd like GitLab to also be able to do so. +If GitLab cannot comment on Jira issues, make sure the Jira user you +set up for the integration has permission to: + +- Post comments on a Jira issue. +- Transition the Jira issue. + Jira issue references and update comments do not work if the GitLab issue tracker is disabled. ### GitLab is unable to close a Jira issue -Make sure the `Transition ID` you set within the Jira settings matches the one +Make sure the `Transition ID` you set in the Jira settings matches the one your project needs to close an issue. -Make sure that the Jira issue is not already marked as resolved; that is, -the Jira issue resolution field is not set. (It should not be struck through in -Jira lists.) +Make sure that the Jira issue is not already marked as resolved. That is, +the Jira issue resolution field is not set, and the issue is not struck through in +Jira lists. ### CAPTCHA -CAPTCHA may be triggered after several consecutive failed login attempts +CAPTCHA may be triggered after several consecutive failed login attempts, which may lead to a `401 unauthorized` error when testing your Jira integration. If CAPTCHA has been triggered, you can't use Jira's REST API to -authenticate with the Jira site. You need to log in to your Jira instance +authenticate with the Jira site. + +To fix this error, sign in to your Jira instance and complete the CAPTCHA. ## Third-party Jira integrations diff --git a/doc/user/admin_area/custom_project_templates.md b/doc/user/admin_area/custom_project_templates.md index 12d143b3a13..8c0586e5851 100644 --- a/doc/user/admin_area/custom_project_templates.md +++ b/doc/user/admin_area/custom_project_templates.md @@ -18,9 +18,13 @@ is created, based on the user's access permissions: - Public projects can be selected by any signed-in user as a template for a new project, if all enabled [project features](../project/settings/index.md#sharing-and-permissions) - except for GitLab Pages are set to **Everyone With Access**. + except for **GitLab Pages** and **Security & Compliance** are set to **Everyone With Access**. + The same applies to internal projects. - Private projects can be selected only by users who are members of the projects. +The **Metrics Dashboard** is set to **Only Project Members** when you create a new project. Make +sure you change it to **Everyone With Access** before making it a project template. + Repository and database information that are copied over to each new project are identical to the data exported with the [GitLab Project Import/Export](../project/settings/import_export.md). diff --git a/doc/user/application_security/dependency_scanning/index.md b/doc/user/application_security/dependency_scanning/index.md index 0eb4d77a783..565b9c29934 100644 --- a/doc/user/application_security/dependency_scanning/index.md +++ b/doc/user/application_security/dependency_scanning/index.md @@ -49,6 +49,17 @@ If you have need of this, please explain why by filling out the survey [here](ht ## Supported languages and package managers +Dependency Scanning automatically detects the languages used in the repository. All analyzers +matching the detected languages are run. There is usually no need to customize the selection of +analyzers. We recommend not specifying the analyzers so you automatically use the full selection +for best coverage, avoiding the need to make adjustments when there are deprecations or removals. +However, you can override the selection using the variable `DS_EXCLUDED_ANALYZERS`. + +The language detection relies on CI job [`rules`](../../../ci/yaml/index.md#rules) and searches a +maximum of two directory levels from the repository's root. For example, the +`gemnasium-dependency_scanning` job is enabled if a repository contains either a `Gemfile` or +`api/Gemfile` file, but not if the only supported dependency file is `api/client/Gemfile`. + The following languages and dependency managers are supported: