diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2022-02-16 12:13:53 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2022-02-16 12:13:53 +0000 |
commit | abed3501da6ecd7ae31cfe2b8fa7654e91a26fb6 (patch) | |
tree | 65318870e1bff62cd176e7fe5bd26155cc38c08f | |
parent | 6259da15b5ede93a9f688ddd062860166e7cf21a (diff) | |
download | gitlab-ce-abed3501da6ecd7ae31cfe2b8fa7654e91a26fb6.tar.gz |
Add latest changes from gitlab-org/gitlab@master
103 files changed, 1501 insertions, 668 deletions
diff --git a/.gitlab/CODEOWNERS b/.gitlab/CODEOWNERS index f31b6a12c4c..ab9805bda7d 100644 --- a/.gitlab/CODEOWNERS +++ b/.gitlab/CODEOWNERS @@ -619,7 +619,6 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab /doc/operations/product_analytics.md @fneill /doc/policy/ @axil /doc/public_access/public_access.md @fneill -/doc/push_rules/push_rules.md @aqualls /doc/raketasks/ @axil /doc/raketasks/generate_sample_prometheus_data.md @ngaskill /doc/raketasks/migrate_snippets.md @aqualls diff --git a/app/assets/javascripts/boards/components/board_new_issue.vue b/app/assets/javascripts/boards/components/board_new_issue.vue index 84c9191975e..8db366e4995 100644 --- a/app/assets/javascripts/boards/components/board_new_issue.vue +++ b/app/assets/javascripts/boards/components/board_new_issue.vue @@ -25,7 +25,7 @@ export default { }, computed: { ...mapState(['selectedProject', 'fullPath']), - ...mapGetters(['isGroupBoard']), + ...mapGetters(['isGroupBoard', 'getBoardItemsByList']), formEventPrefix() { return toggleFormEventPrefix.issue; }, @@ -42,6 +42,7 @@ export default { const labels = this.list.label ? [this.list.label] : []; const assignees = this.list.assignee ? [this.list.assignee] : []; const milestone = getMilestone(this.list); + const firstItemId = this.getBoardItemsByList(this.list.id)[0]?.id; return this.addListNewIssue({ list: this.list, @@ -51,6 +52,7 @@ export default { assigneeIds: assignees?.map((a) => a?.id), milestoneId: milestone?.id, projectPath: this.projectPath, + moveAfterId: firstItemId, }, }).then(() => { this.cancel(); diff --git a/app/assets/javascripts/boards/config_toggle.js b/app/assets/javascripts/boards/config_toggle.js index 945a508c55d..1e54c2511b8 100644 --- a/app/assets/javascripts/boards/config_toggle.js +++ b/app/assets/javascripts/boards/config_toggle.js @@ -12,6 +12,7 @@ export default () => { // eslint-disable-next-line no-new new Vue({ el, + name: 'ConfigToggleRoot', render(h) { return h(ConfigToggle, { props: { diff --git a/app/assets/javascripts/boards/index.js b/app/assets/javascripts/boards/index.js index 9f44380781e..f6073f9d981 100644 --- a/app/assets/javascripts/boards/index.js +++ b/app/assets/javascripts/boards/index.js @@ -64,6 +64,7 @@ function mountBoardApp(el) { // eslint-disable-next-line no-new new Vue({ el, + name: 'BoardAppRoot', store, apolloProvider, provide: { @@ -121,6 +122,7 @@ export default () => { // eslint-disable-next-line no-new new Vue({ el: createColumnTriggerEl, + name: 'BoardAddNewColumnTriggerRoot', components: { BoardAddNewColumnTrigger, }, diff --git a/app/assets/javascripts/boards/mount_filtered_search_issue_boards.js b/app/assets/javascripts/boards/mount_filtered_search_issue_boards.js index a8ade58e316..327fb9ba8d7 100644 --- a/app/assets/javascripts/boards/mount_filtered_search_issue_boards.js +++ b/app/assets/javascripts/boards/mount_filtered_search_issue_boards.js @@ -18,6 +18,7 @@ export default (apolloProvider, isSignedIn, releasesFetchPath) => { return new Vue({ el, + name: 'BoardFilteredSearchRoot', provide: { initialFilterParams, isSignedIn, diff --git a/app/assets/javascripts/boards/mount_multiple_boards_switcher.js b/app/assets/javascripts/boards/mount_multiple_boards_switcher.js index 3838b4f2a83..0bc9cfbd867 100644 --- a/app/assets/javascripts/boards/mount_multiple_boards_switcher.js +++ b/app/assets/javascripts/boards/mount_multiple_boards_switcher.js @@ -16,6 +16,7 @@ export default (params = {}) => { const { dataset } = boardsSwitcherElement; return new Vue({ el: boardsSwitcherElement, + name: 'BoardsSelectorRoot', components: { BoardsSelector, }, diff --git a/app/assets/javascripts/boards/toggle_focus.js b/app/assets/javascripts/boards/toggle_focus.js index 0a230f72dcc..8f057e192dd 100644 --- a/app/assets/javascripts/boards/toggle_focus.js +++ b/app/assets/javascripts/boards/toggle_focus.js @@ -6,6 +6,7 @@ export default () => { return new Vue({ el: '#js-toggle-focus-btn', + name: 'ToggleFocusRoot', render(h) { return h(ToggleFocus, { props: { diff --git a/app/assets/javascripts/environments/components/canary_ingress.vue b/app/assets/javascripts/environments/components/canary_ingress.vue index 02d660a91c1..30f3f9dfc75 100644 --- a/app/assets/javascripts/environments/components/canary_ingress.vue +++ b/app/assets/javascripts/environments/components/canary_ingress.vue @@ -17,6 +17,11 @@ export default { required: true, type: Object, }, + graphql: { + required: false, + type: Boolean, + default: false, + }, }, ingressOptions: Array(100 / 5 + 1) .fill(0) @@ -47,11 +52,17 @@ export default { canaryWeightId() { return uniqueId('canary-weight-'); }, + weight() { + if (this.graphql) { + return this.canaryIngress.canaryWeight; + } + return this.canaryIngress.canary_weight; + }, stableWeight() { - return (100 - this.canaryIngress.canary_weight).toString(); + return (100 - this.weight).toString(); }, canaryWeight() { - return this.canaryIngress.canary_weight.toString(); + return this.weight.toString(); }, }, methods: { diff --git a/app/assets/javascripts/environments/components/canary_update_modal.vue b/app/assets/javascripts/environments/components/canary_update_modal.vue index 8b1121c7158..fd4885a9dbd 100644 --- a/app/assets/javascripts/environments/components/canary_update_modal.vue +++ b/app/assets/javascripts/environments/components/canary_update_modal.vue @@ -71,7 +71,7 @@ export default { mutation: updateCanaryIngress, variables: { input: { - id: this.environment.global_id, + id: this.environment.global_id || this.environment.globalId, weight: this.weight, }, }, diff --git a/app/assets/javascripts/environments/components/deploy_board.vue b/app/assets/javascripts/environments/components/deploy_board.vue index c642a07fd1e..8a379ebdf66 100644 --- a/app/assets/javascripts/environments/components/deploy_board.vue +++ b/app/assets/javascripts/environments/components/deploy_board.vue @@ -1,5 +1,4 @@ <script> -/* eslint-disable @gitlab/vue-require-i18n-strings */ /** * Renders a deploy board. * @@ -17,11 +16,11 @@ import { GlTooltip, GlTooltipDirective, GlSafeHtmlDirective as SafeHtml, + GlSprintf, } from '@gitlab/ui'; import { isEmpty } from 'lodash'; -import { n__ } from '~/locale'; +import { s__, n__ } from '~/locale'; import instanceComponent from '~/vue_shared/components/deployment_instance.vue'; -import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; import { STATUS_MAP, CANARY_STATUS } from '../constants'; import CanaryIngress from './canary_ingress.vue'; @@ -32,13 +31,13 @@ export default { GlIcon, GlLoadingIcon, GlLink, + GlSprintf, GlTooltip, }, directives: { GlTooltip: GlTooltipDirective, SafeHtml, }, - mixins: [glFeatureFlagsMixin()], props: { deployBoardData: { type: Object, @@ -57,6 +56,11 @@ export default { required: false, default: '', }, + graphql: { + type: Boolean, + required: false, + default: false, + }, }, computed: { canRenderDeployBoard() { @@ -65,8 +69,15 @@ export default { canRenderEmptyState() { return this.isEmpty; }, + canaryIngress() { + if (this.graphql) { + return this.deployBoardData.canaryIngress; + } + + return this.deployBoardData.canary_ingress; + }, canRenderCanaryWeight() { - return !isEmpty(this.deployBoardData.canary_ingress); + return !isEmpty(this.canaryIngress); }, instanceCount() { const { instances } = this.deployBoardData; @@ -90,8 +101,20 @@ export default { deployBoardSvg() { return deployBoardSvg; }, + rollbackUrl() { + if (this.graphql) { + return this.deployBoardData.rollbackUrl; + } + return this.deployBoardData.rollback_url; + }, + abortUrl() { + if (this.graphql) { + return this.deployBoardData.abortUrl; + } + return this.deployBoardData.abort_url; + }, deployBoardActions() { - return this.deployBoardData.rollback_url || this.deployBoardData.abort_url; + return this.rollbackUrl || this.abortUrl; }, statuses() { // Canary is not a pod status but it needs to be in the legend. @@ -106,7 +129,17 @@ export default { changeCanaryWeight(weight) { this.$emit('changeCanaryWeight', weight); }, + podName(instance) { + if (this.graphql) { + return instance.podName; + } + + return instance.pod_name; + }, }, + emptyStateText: s__( + 'DeployBoards|To see deployment progress for your environments, make sure you are deploying to %{codeStart}$KUBE_NAMESPACE%{codeEnd} and annotating with %{codeStart}app.gitlab.com/app=$CI_PROJECT_PATH_SLUG%{codeEnd} and %{codeStart}app.gitlab.com/env=$CI_ENVIRONMENT_SLUG%{codeEnd}.', + ), }; </script> <template> @@ -152,7 +185,7 @@ export default { :key="i" :status="instance.status" :tooltip-text="instance.tooltip" - :pod-name="instance.pod_name" + :pod-name="podName(instance)" :logs-path="logsPath" :stable="instance.stable" /> @@ -163,22 +196,23 @@ export default { <canary-ingress v-if="canRenderCanaryWeight" class="deploy-board-canary-ingress" - :canary-ingress="deployBoardData.canary_ingress" + :canary-ingress="canaryIngress" + :graphql="graphql" @change="changeCanaryWeight" /> <section v-if="deployBoardActions" class="deploy-board-actions"> <gl-link - v-if="deployBoardData.rollback_url" - :href="deployBoardData.rollback_url" + v-if="rollbackUrl" + :href="rollbackUrl" class="btn" data-method="post" rel="nofollow" >{{ __('Rollback') }}</gl-link > <gl-link - v-if="deployBoardData.abort_url" - :href="deployBoardData.abort_url" + v-if="abortUrl" + :href="abortUrl" class="btn btn-danger btn-inverted" data-method="post" rel="nofollow" @@ -196,11 +230,11 @@ export default { __('Kubernetes deployment not found') }}</span> <span> - To see deployment progress for your environments, make sure you are deploying to - <code>$KUBE_NAMESPACE</code> and annotating with - <code>app.gitlab.com/app=$CI_PROJECT_PATH_SLUG</code> - and - <code>app.gitlab.com/env=$CI_ENVIRONMENT_SLUG</code>. + <gl-sprintf :message="$options.emptyStateText"> + <template #code="{ content }"> + <code>{{ content }}</code> + </template> + </gl-sprintf> </span> </section> </div> diff --git a/app/assets/javascripts/environments/components/deploy_board_wrapper.vue b/app/assets/javascripts/environments/components/deploy_board_wrapper.vue new file mode 100644 index 00000000000..d9d77088ad3 --- /dev/null +++ b/app/assets/javascripts/environments/components/deploy_board_wrapper.vue @@ -0,0 +1,86 @@ +<script> +import { GlCollapse, GlButton } from '@gitlab/ui'; +import { __, s__ } from '~/locale'; +import setEnvironmentToChangeCanaryMutation from '../graphql/mutations/set_environment_to_change_canary.mutation.graphql'; +import DeployBoard from './deploy_board.vue'; + +export default { + components: { + DeployBoard, + GlButton, + GlCollapse, + }, + props: { + rolloutStatus: { + required: true, + type: Object, + }, + environment: { + required: true, + type: Object, + }, + }, + data() { + return { visible: false }; + }, + computed: { + icon() { + return this.visible ? 'angle-down' : 'angle-right'; + }, + label() { + return this.visible ? this.$options.i18n.collapse : this.$options.i18n.expand; + }, + isLoading() { + return this.rolloutStatus.status === 'loading'; + }, + isEmpty() { + return this.rolloutStatus.status === 'not_found'; + }, + }, + methods: { + toggleCollapse() { + this.visible = !this.visible; + }, + changeCanaryWeight(weight) { + this.$apollo.mutate({ + mutation: setEnvironmentToChangeCanaryMutation, + variables: { + environment: this.environment, + weight, + }, + }); + }, + }, + i18n: { + collapse: __('Collapse'), + expand: __('Expand'), + pods: s__('DeployBoard|Kubernetes Pods'), + }, +}; +</script> +<template> + <div> + <div> + <gl-button + class="gl-mr-4 gl-min-w-fit-content" + :icon="icon" + :aria-label="label" + size="small" + category="tertiary" + @click="toggleCollapse" + /> + <span>{{ $options.i18n.pods }}</span> + </div> + <gl-collapse :visible="visible"> + <deploy-board + :deploy-board-data="rolloutStatus" + :is-loading="isLoading" + :is-empty="isEmpty" + :environment="environment" + graphql + class="gl-reset-bg!" + @changeCanaryWeight="changeCanaryWeight" + /> + </gl-collapse> + </div> +</template> diff --git a/app/assets/javascripts/environments/components/new_environment_item.vue b/app/assets/javascripts/environments/components/new_environment_item.vue index 48a77c021bf..27a763fb9c4 100644 --- a/app/assets/javascripts/environments/components/new_environment_item.vue +++ b/app/assets/javascripts/environments/components/new_environment_item.vue @@ -20,6 +20,7 @@ import Monitoring from './environment_monitoring.vue'; import Terminal from './environment_terminal_button.vue'; import Delete from './environment_delete.vue'; import Deployment from './deployment.vue'; +import DeployBoardWrapper from './deploy_board_wrapper.vue'; export default { components: { @@ -30,6 +31,7 @@ export default { GlSprintf, Actions, Deployment, + DeployBoardWrapper, ExternalUrl, StopComponent, Rollback, @@ -145,6 +147,9 @@ export default { displayName() { return truncate(this.name, 80); }, + rolloutStatus() { + return this.environment?.rolloutStatus; + }, }, methods: { toggleCollapse() { @@ -159,6 +164,14 @@ export default { 'gl-md-pl-7', 'gl-bg-gray-10', ], + deployBoardClasses: [ + 'gl-border-gray-100', + 'gl-border-t-solid', + 'gl-border-1', + 'gl-py-4', + 'gl-md-pl-7', + 'gl-bg-gray-10', + ], }; </script> <template> @@ -298,6 +311,14 @@ export default { </template> </gl-sprintf> </div> + <div v-if="rolloutStatus" :class="$options.deployBoardClasses"> + <deploy-board-wrapper + :rollout-status="rolloutStatus" + :environment="environment" + :class="{ 'gl-ml-7': inFolder }" + class="gl-pl-4" + /> + </div> </gl-collapse> </div> </template> diff --git a/app/assets/javascripts/environments/components/new_environments_app.vue b/app/assets/javascripts/environments/components/new_environments_app.vue index cb36e226d0e..3699f39b611 100644 --- a/app/assets/javascripts/environments/components/new_environments_app.vue +++ b/app/assets/javascripts/environments/components/new_environments_app.vue @@ -8,16 +8,19 @@ import pageInfoQuery from '../graphql/queries/page_info.query.graphql'; import environmentToDeleteQuery from '../graphql/queries/environment_to_delete.query.graphql'; import environmentToRollbackQuery from '../graphql/queries/environment_to_rollback.query.graphql'; import environmentToStopQuery from '../graphql/queries/environment_to_stop.query.graphql'; +import environmentToChangeCanaryQuery from '../graphql/queries/environment_to_change_canary.query.graphql'; import EnvironmentFolder from './new_environment_folder.vue'; import EnableReviewAppModal from './enable_review_app_modal.vue'; import StopEnvironmentModal from './stop_environment_modal.vue'; import EnvironmentItem from './new_environment_item.vue'; import ConfirmRollbackModal from './confirm_rollback_modal.vue'; import DeleteEnvironmentModal from './delete_environment_modal.vue'; +import CanaryUpdateModal from './canary_update_modal.vue'; export default { components: { DeleteEnvironmentModal, + CanaryUpdateModal, ConfirmRollbackModal, EnvironmentFolder, EnableReviewAppModal, @@ -56,6 +59,12 @@ export default { environmentToStop: { query: environmentToStopQuery, }, + environmentToChangeCanary: { + query: environmentToChangeCanaryQuery, + }, + weight: { + query: environmentToChangeCanaryQuery, + }, }, inject: ['newEnvironmentPath', 'canCreateEnvironment'], i18n: { @@ -80,6 +89,8 @@ export default { environmentToDelete: {}, environmentToRollback: {}, environmentToStop: {}, + environmentToChangeCanary: {}, + weight: 0, }; }, computed: { @@ -186,6 +197,7 @@ export default { <delete-environment-modal :environment="environmentToDelete" graphql /> <stop-environment-modal :environment="environmentToStop" graphql /> <confirm-rollback-modal :environment="environmentToRollback" graphql /> + <canary-update-modal :environment="environmentToChangeCanary" :weight="weight" /> <gl-tabs :action-secondary="addEnvironment" :action-primary="openReviewAppModal" diff --git a/app/assets/javascripts/environments/graphql/mutations/set_environment_to_change_canary.mutation.graphql b/app/assets/javascripts/environments/graphql/mutations/set_environment_to_change_canary.mutation.graphql new file mode 100644 index 00000000000..0f48c1f5c05 --- /dev/null +++ b/app/assets/javascripts/environments/graphql/mutations/set_environment_to_change_canary.mutation.graphql @@ -0,0 +1,3 @@ +mutation SetEnvironmentToChangeCanary($environment: LocalEnvironmentInput, $weight: Int!) { + setEnvironmentToChangeCanary(environment: $environment, weight: $weight) @client +} diff --git a/app/assets/javascripts/environments/graphql/queries/environment_to_change_canary.query.graphql b/app/assets/javascripts/environments/graphql/queries/environment_to_change_canary.query.graphql new file mode 100644 index 00000000000..b582ae55ba1 --- /dev/null +++ b/app/assets/javascripts/environments/graphql/queries/environment_to_change_canary.query.graphql @@ -0,0 +1,4 @@ +query environmentToChangeCanary { + environmentToChangeCanary @client + weight @client +} diff --git a/app/assets/javascripts/environments/graphql/resolvers.js b/app/assets/javascripts/environments/graphql/resolvers.js index 9e0e0fff47f..dc763b77157 100644 --- a/app/assets/javascripts/environments/graphql/resolvers.js +++ b/app/assets/javascripts/environments/graphql/resolvers.js @@ -10,6 +10,7 @@ import pollIntervalQuery from './queries/poll_interval.query.graphql'; import environmentToRollbackQuery from './queries/environment_to_rollback.query.graphql'; import environmentToStopQuery from './queries/environment_to_stop.query.graphql'; import environmentToDeleteQuery from './queries/environment_to_delete.query.graphql'; +import environmentToChangeCanaryQuery from './queries/environment_to_change_canary.query.graphql'; import pageInfoQuery from './queries/page_info.query.graphql'; const buildErrors = (errors = []) => ({ @@ -134,6 +135,12 @@ export const resolvers = (endpoint) => ({ data: { environmentToRollback: environment }, }); }, + setEnvironmentToChangeCanary(_, { environment, weight }, { client }) { + client.writeQuery({ + query: environmentToChangeCanaryQuery, + data: { environmentToChangeCanary: environment, weight }, + }); + }, cancelAutoStop(_, { autoStopUrl }) { return axios .post(autoStopUrl) diff --git a/app/assets/javascripts/environments/graphql/typedefs.graphql b/app/assets/javascripts/environments/graphql/typedefs.graphql index cc54b4ecd67..b4d1f7326f6 100644 --- a/app/assets/javascripts/environments/graphql/typedefs.graphql +++ b/app/assets/javascripts/environments/graphql/typedefs.graphql @@ -81,5 +81,6 @@ extend type Mutation { setEnvironmentToDelete(environment: LocalEnvironmentInput): LocalErrors setEnvironmentToRollback(environment: LocalEnvironmentInput): LocalErrors setEnvironmentToStop(environment: LocalEnvironmentInput): LocalErrors + setEnvironmentToChangeCanary(environment: LocalEnvironmentInput, weight: Int): LocalErrors action(environment: LocalEnvironmentInput): LocalErrors } diff --git a/app/assets/javascripts/repository/components/blob_content_viewer.vue b/app/assets/javascripts/repository/components/blob_content_viewer.vue index 9263b112dfe..52963b49f68 100644 --- a/app/assets/javascripts/repository/components/blob_content_viewer.vue +++ b/app/assets/javascripts/repository/components/blob_content_viewer.vue @@ -49,9 +49,7 @@ export default { }; }, result() { - this.switchViewer( - this.hasRichViewer && !window.location.hash ? RICH_BLOB_VIEWER : SIMPLE_BLOB_VIEWER, - ); + this.switchViewer(this.hasRichViewer ? RICH_BLOB_VIEWER : SIMPLE_BLOB_VIEWER); }, error() { this.displayError(); diff --git a/app/models/namespaces/traversal/linear.rb b/app/models/namespaces/traversal/linear.rb index 757a0e40eb3..99a5b8cb063 100644 --- a/app/models/namespaces/traversal/linear.rb +++ b/app/models/namespaces/traversal/linear.rb @@ -43,14 +43,23 @@ module Namespaces included do before_update :lock_both_roots, if: -> { sync_traversal_ids? && parent_id_changed? } - after_create :sync_traversal_ids, if: -> { sync_traversal_ids? } after_update :sync_traversal_ids, if: -> { sync_traversal_ids? && saved_change_to_parent_id? } + # sync traversal_ids on namespace create, which can happen quite early within a transaction, thus keeping the lock on root namespace record + # for a relatively long time, e.g. creating the project namespace when a project is being created. + after_create :sync_traversal_ids, if: -> { sync_traversal_ids? && !sync_traversal_ids_before_commit? } + # This uses rails internal before_commit API to sync traversal_ids on namespace create, right before transaction is committed. + # This helps reduce the time during which the root namespace record is locked to ensure updated traversal_ids are valid + before_commit :sync_traversal_ids, on: [:create], if: -> { sync_traversal_ids? && sync_traversal_ids_before_commit? } end def sync_traversal_ids? Feature.enabled?(:sync_traversal_ids, root_ancestor, default_enabled: :yaml) end + def sync_traversal_ids_before_commit? + Feature.enabled?(:sync_traversal_ids_before_commit, root_ancestor, default_enabled: :yaml) + end + def use_traversal_ids? return false unless Feature.enabled?(:use_traversal_ids, default_enabled: :yaml) diff --git a/app/services/issues/base_service.rb b/app/services/issues/base_service.rb index 7f4ae60602a..61a95e49228 100644 --- a/app/services/issues/base_service.rb +++ b/app/services/issues/base_service.rb @@ -74,9 +74,7 @@ module Issues def issuable_for_positioning(id, positioning_scope) return unless id - issue = positioning_scope.find(id) - - issue if can?(current_user, :update_issue, issue) + positioning_scope.find(id) end def create_assignee_note(issue, old_assignees) diff --git a/app/views/projects/environments/index.html.haml b/app/views/projects/environments/index.html.haml index 72ccc8d830c..2b05ffe3eea 100644 --- a/app/views/projects/environments/index.html.haml +++ b/app/views/projects/environments/index.html.haml @@ -1,4 +1,5 @@ - page_title _("Environments") +- add_page_specific_style 'page_bundles/environments' - if Feature.enabled?(:new_environments_table) #environments-table{ data: { endpoint: project_environments_path(@project, format: :json), @@ -9,7 +10,6 @@ "project-path" => @project.full_path, "default-branch-name" => @project.default_branch_or_main } } - else - - add_page_specific_style 'page_bundles/environments' #environments-list-view{ data: { environments_data: environments_list_data, "can-read-environment" => can?(current_user, :read_environment, @project).to_s, "can-create-environment" => can?(current_user, :create_environment, @project).to_s, diff --git a/config/feature_flags/development/api_caching_rate_limit_repository_compare.yml b/config/feature_flags/development/api_caching_rate_limit_repository_compare.yml deleted file mode 100644 index 81200aff786..00000000000 --- a/config/feature_flags/development/api_caching_rate_limit_repository_compare.yml +++ /dev/null @@ -1,8 +0,0 @@ ---- -name: api_caching_rate_limit_repository_compare -introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/64407 -rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/334264 -milestone: '14.1' -type: development -group: group::source code -default_enabled: true diff --git a/config/feature_flags/development/new_route_storage_purchase.yml b/config/feature_flags/development/sync_traversal_ids_before_commit.yml index 8a81af55bd5..f8f1e854fa5 100644 --- a/config/feature_flags/development/new_route_storage_purchase.yml +++ b/config/feature_flags/development/sync_traversal_ids_before_commit.yml @@ -1,8 +1,8 @@ --- -name: new_route_storage_purchase -introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/68834 -rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/327896 -milestone: '14.3' +name: sync_traversal_ids_before_commit +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/79964 +rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/352499 +group: group::workspace type: development -group: group::purchase -default_enabled: true +default_enabled: false +milestone: '14.8' diff --git a/config/metrics/aggregates/common.yml b/config/metrics/aggregates/common.yml index beabb72dd72..50d5122e806 100644 --- a/config/metrics/aggregates/common.yml +++ b/config/metrics/aggregates/common.yml @@ -1,6 +1,6 @@ # Aggregated metrics that include EE only event names within `events:` attribute have to be defined at ee/config/metrics/aggregates/common.yml # instead of this file. -#- name: unique name of aggregated metric +# - name: unique name of aggregated metric # operator: aggregation operator. Valid values are: # - "OR": counts unique elements that were observed triggering any of following events # - "AND": counts unique elements that were observed triggering all of following events diff --git a/config/metrics/counts_28d/20210201124930_deployments.yml b/config/metrics/counts_28d/20210201124930_deployments.yml index fc8c37f5a21..385bdb47484 100644 --- a/config/metrics/counts_28d/20210201124930_deployments.yml +++ b/config/metrics/counts_28d/20210201124930_deployments.yml @@ -5,7 +5,7 @@ description: Total deployments count for recent 28 days product_section: ops product_stage: release product_group: group::ops release -product_category: +product_category: value_type: number status: active milestone: "13.2" diff --git a/config/metrics/counts_28d/20210216174956_i_analytics_cohorts_monthly.yml b/config/metrics/counts_28d/20210216174956_i_analytics_cohorts_monthly.yml index 75871428fab..0b235a98677 100644 --- a/config/metrics/counts_28d/20210216174956_i_analytics_cohorts_monthly.yml +++ b/config/metrics/counts_28d/20210216174956_i_analytics_cohorts_monthly.yml @@ -1,7 +1,7 @@ --- data_category: optional key_path: redis_hll_counters.analytics.i_analytics_cohorts_monthly -description: "Unique visitors to /-/instance_statistics/cohorts" +description: "Unique visitors to /-/instance_statistics/cohorts" product_section: fulfillment product_stage: fulfillment product_group: group::utilization diff --git a/config/metrics/counts_28d/20210216184140_testing_total_unique_counts_monthly.yml b/config/metrics/counts_28d/20210216184140_testing_total_unique_counts_monthly.yml index 14042fc25f7..fc007008b87 100644 --- a/config/metrics/counts_28d/20210216184140_testing_total_unique_counts_monthly.yml +++ b/config/metrics/counts_28d/20210216184140_testing_total_unique_counts_monthly.yml @@ -2,7 +2,6 @@ data_category: optional key_path: redis_hll_counters.testing.testing_total_unique_counts_monthly description: Total users for events under testing category -product_section: devops product_section: growth product_stage: growth product_group: group::product intelligence diff --git a/config/metrics/counts_28d/20210216184559_ci_templates_total_unique_counts_monthly.yml b/config/metrics/counts_28d/20210216184559_ci_templates_total_unique_counts_monthly.yml index 5b864531c4c..05603ec14e0 100644 --- a/config/metrics/counts_28d/20210216184559_ci_templates_total_unique_counts_monthly.yml +++ b/config/metrics/counts_28d/20210216184559_ci_templates_total_unique_counts_monthly.yml @@ -91,6 +91,8 @@ options: - p_ci_templates_jobs_secret_detection - p_ci_templates_jobs_code_intelligence - p_ci_templates_jobs_code_quality + - p_ci_templates_jobs_dependency_scanning + - p_ci_templates_jobs_license_scanning - p_ci_templates_jobs_deploy_ecs - p_ci_templates_jobs_deploy_ec2 - p_ci_templates_jobs_deploy @@ -135,6 +137,8 @@ options: - p_ci_templates_implicit_jobs_secret_detection - p_ci_templates_implicit_jobs_code_intelligence - p_ci_templates_implicit_jobs_code_quality + - p_ci_templates_implicit_jobs_dependency_scanning + - p_ci_templates_implicit_jobs_license_scanning - p_ci_templates_implicit_jobs_deploy_ecs - p_ci_templates_implicit_jobs_deploy_ec2 - p_ci_templates_implicit_auto_devops_deploy diff --git a/config/metrics/counts_28d/20210222041235_i_quickactions_invite_email_multiple_monthly.yml b/config/metrics/counts_28d/20210222041235_i_quickactions_invite_email_multiple_monthly.yml index bd178cb07bc..034eda07221 100644 --- a/config/metrics/counts_28d/20210222041235_i_quickactions_invite_email_multiple_monthly.yml +++ b/config/metrics/counts_28d/20210222041235_i_quickactions_invite_email_multiple_monthly.yml @@ -20,7 +20,5 @@ distribution: - ce tier: - free -tier: -- free - premium - ultimate diff --git a/config/metrics/counts_28d/20210427103010_code_review_extension_category_monthly_active_users.yml b/config/metrics/counts_28d/20210427103010_code_review_extension_category_monthly_active_users.yml index 1e196ab49c4..faa452f73a6 100644 --- a/config/metrics/counts_28d/20210427103010_code_review_extension_category_monthly_active_users.yml +++ b/config/metrics/counts_28d/20210427103010_code_review_extension_category_monthly_active_users.yml @@ -5,7 +5,7 @@ description: Number of users performing i_code_review_user_vs_code_api_request e product_section: dev product_stage: devops::create product_group: group::code review -product_category: +product_category: value_type: number status: active milestone: "13.12" diff --git a/config/metrics/counts_28d/20210427103119_code_review_group_monthly_active_users.yml b/config/metrics/counts_28d/20210427103119_code_review_group_monthly_active_users.yml index 3d66f1cdf8c..5cbdd9afa54 100644 --- a/config/metrics/counts_28d/20210427103119_code_review_group_monthly_active_users.yml +++ b/config/metrics/counts_28d/20210427103119_code_review_group_monthly_active_users.yml @@ -5,7 +5,7 @@ description: Number of users performing at least one of the code review events product_section: dev product_stage: devops::create product_group: group::code review -product_category: +product_category: value_type: number status: active milestone: "13.12" diff --git a/config/metrics/counts_28d/20210514013545_i_code_review_user_resolve_conflict_monthly.yml b/config/metrics/counts_28d/20210514013545_i_code_review_user_resolve_conflict_monthly.yml index 29ec7855453..c854fe7e34d 100644 --- a/config/metrics/counts_28d/20210514013545_i_code_review_user_resolve_conflict_monthly.yml +++ b/config/metrics/counts_28d/20210514013545_i_code_review_user_resolve_conflict_monthly.yml @@ -3,7 +3,6 @@ data_category: optional key_path: redis_hll_counters.code_review.i_code_review_user_resolve_conflict_monthly name: resolve_conflict description: Count of unique users per week who attempt to resolve a conflict through the ui -product_section: product_stage: create product_group: group::code review product_category: code_review diff --git a/config/metrics/counts_28d/20210514013549_i_code_review_user_load_conflict_ui_monthly.yml b/config/metrics/counts_28d/20210514013549_i_code_review_user_load_conflict_ui_monthly.yml index dceee0cc3ff..49500d88646 100644 --- a/config/metrics/counts_28d/20210514013549_i_code_review_user_load_conflict_ui_monthly.yml +++ b/config/metrics/counts_28d/20210514013549_i_code_review_user_load_conflict_ui_monthly.yml @@ -3,7 +3,6 @@ data_category: optional key_path: redis_hll_counters.code_review.i_code_review_user_load_conflict_ui_monthly name: load_conflict_ui description: Count of unique users per week who load the conflict resolution page -product_section: product_stage: create product_group: group::code review product_category: code_review diff --git a/config/metrics/counts_28d/20210902191057_i_quickactions_unapprove_monthly.yml b/config/metrics/counts_28d/20210902191057_i_quickactions_unapprove_monthly.yml index ccefa1f5dd3..6cd2704b058 100644 --- a/config/metrics/counts_28d/20210902191057_i_quickactions_unapprove_monthly.yml +++ b/config/metrics/counts_28d/20210902191057_i_quickactions_unapprove_monthly.yml @@ -15,10 +15,6 @@ instrumentation_class: RedisHLLMetric options: events: - i_quickactions_unapprove -instrumentation_class: RedisHLLMetric -options: - events: - - i_quickactions_unapprove distribution: - ce - ee diff --git a/config/metrics/counts_28d/20211201140658_users_expanding_testing_license_compliance_report_monthly.yml b/config/metrics/counts_28d/20211201140658_users_expanding_testing_license_compliance_report_monthly.yml index f8f7f9933cf..75e9fb02788 100644 --- a/config/metrics/counts_28d/20211201140658_users_expanding_testing_license_compliance_report_monthly.yml +++ b/config/metrics/counts_28d/20211201140658_users_expanding_testing_license_compliance_report_monthly.yml @@ -13,8 +13,8 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/75643 time_frame: 28d data_source: redis_hll instrumentation_class: RedisHLLMetric -options: - events: +options: + events: - users_expanding_testing_license_compliance_report distribution: - ce diff --git a/config/metrics/counts_28d/20211201154341_users_visiting_license_compliance_full_report_monthly.yml b/config/metrics/counts_28d/20211201154341_users_visiting_license_compliance_full_report_monthly.yml index 638a64b20ce..96e1bc1f949 100644 --- a/config/metrics/counts_28d/20211201154341_users_visiting_license_compliance_full_report_monthly.yml +++ b/config/metrics/counts_28d/20211201154341_users_visiting_license_compliance_full_report_monthly.yml @@ -13,8 +13,8 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/75643 time_frame: 28d data_source: redis_hll instrumentation_class: RedisHLLMetric -options: - events: +options: + events: - users_visiting_testing_license_compliance_full_report distribution: - ce diff --git a/config/metrics/counts_28d/20211202094237_users_visiting_manage_license_compliance_monthly.yml b/config/metrics/counts_28d/20211202094237_users_visiting_manage_license_compliance_monthly.yml index 81b728a5ebd..dc4bc631759 100644 --- a/config/metrics/counts_28d/20211202094237_users_visiting_manage_license_compliance_monthly.yml +++ b/config/metrics/counts_28d/20211202094237_users_visiting_manage_license_compliance_monthly.yml @@ -13,8 +13,8 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/75643 time_frame: 28d data_source: redis_hll instrumentation_class: RedisHLLMetric -options: - events: +options: + events: - users_visiting_testing_manage_license_compliance distribution: - ce diff --git a/config/metrics/counts_28d/20211216083832_users_clicking_license_testing_visiting_external_website_monthly.yml b/config/metrics/counts_28d/20211216083832_users_clicking_license_testing_visiting_external_website_monthly.yml index 24f062f9a10..99b64d7a379 100644 --- a/config/metrics/counts_28d/20211216083832_users_clicking_license_testing_visiting_external_website_monthly.yml +++ b/config/metrics/counts_28d/20211216083832_users_clicking_license_testing_visiting_external_website_monthly.yml @@ -13,8 +13,8 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/76917 time_frame: 28d data_source: redis_hll instrumentation_class: RedisHLLMetric -options: - events: +options: + events: - users_clicking_license_testing_visiting_external_website distribution: - ce @@ -22,4 +22,4 @@ distribution: tier: - free - premium - - ultimate
\ No newline at end of file + - ultimate diff --git a/config/metrics/counts_28d/20220210134101_p_ci_templates_implicit_jobs_dependency_scanning_monthly.yml b/config/metrics/counts_28d/20220210134101_p_ci_templates_implicit_jobs_dependency_scanning_monthly.yml new file mode 100644 index 00000000000..8f913307356 --- /dev/null +++ b/config/metrics/counts_28d/20220210134101_p_ci_templates_implicit_jobs_dependency_scanning_monthly.yml @@ -0,0 +1,25 @@ +--- +key_path: redis_hll_counters.ci_templates.p_ci_templates_implicit_jobs_dependency_scanning_monthly +description: Monthly counts for implicit use of Dependency Scanning CI template (Jobs folder) +product_section: sec +product_stage: secure +product_group: composition_analysis +product_category: dependency_scanning +value_type: number +status: active +milestone: '14.8' +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/79454 +time_frame: 28d +data_source: redis_hll +data_category: optional +instrumentation_class: RedisHLLMetric +distribution: +- ce +- ee +tier: +- free +- premium +- ultimate +options: + events: + - p_ci_templates_implicit_jobs_dependency_scanning diff --git a/config/metrics/counts_28d/20220210134101_p_ci_templates_implicit_jobs_license_scanning_monthly.yml b/config/metrics/counts_28d/20220210134101_p_ci_templates_implicit_jobs_license_scanning_monthly.yml new file mode 100644 index 00000000000..2b776b28c5d --- /dev/null +++ b/config/metrics/counts_28d/20220210134101_p_ci_templates_implicit_jobs_license_scanning_monthly.yml @@ -0,0 +1,25 @@ +--- +key_path: redis_hll_counters.ci_templates.p_ci_templates_implicit_jobs_license_scanning_monthly +description: Monthly counts for implicit use of License Scanning CI template (Jobs folder) +product_section: sec +product_stage: secure +product_group: composition_analysis +product_category: license_compliance +value_type: number +status: active +milestone: '14.8' +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/79454 +time_frame: 28d +data_source: redis_hll +data_category: optional +instrumentation_class: RedisHLLMetric +distribution: +- ce +- ee +tier: +- free +- premium +- ultimate +options: + events: + - p_ci_templates_implicit_jobs_license_scanning diff --git a/config/metrics/counts_28d/20220210134101_p_ci_templates_jobs_dependency_scanning_monthly.yml b/config/metrics/counts_28d/20220210134101_p_ci_templates_jobs_dependency_scanning_monthly.yml new file mode 100644 index 00000000000..6d166725a67 --- /dev/null +++ b/config/metrics/counts_28d/20220210134101_p_ci_templates_jobs_dependency_scanning_monthly.yml @@ -0,0 +1,25 @@ +--- +key_path: redis_hll_counters.ci_templates.p_ci_templates_jobs_dependency_scanning_monthly +description: Monthly counts for Dependency Scanning CI template (Jobs folder) +product_section: sec +product_stage: secure +product_group: composition_analysis +product_category: dependency_scanning +value_type: number +status: active +milestone: '14.8' +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/79454 +time_frame: 28d +data_source: redis_hll +data_category: optional +instrumentation_class: RedisHLLMetric +distribution: +- ce +- ee +tier: +- free +- premium +- ultimate +options: + events: + - p_ci_templates_jobs_dependency_scanning diff --git a/config/metrics/counts_28d/20220210134101_p_ci_templates_jobs_license_scanning_monthly.yml b/config/metrics/counts_28d/20220210134101_p_ci_templates_jobs_license_scanning_monthly.yml new file mode 100644 index 00000000000..db215d242e0 --- /dev/null +++ b/config/metrics/counts_28d/20220210134101_p_ci_templates_jobs_license_scanning_monthly.yml @@ -0,0 +1,25 @@ +--- +key_path: redis_hll_counters.ci_templates.p_ci_templates_jobs_license_scanning_monthly +description: Monthly counts for License Scanning CI template (Jobs folder) +product_section: sec +product_stage: secure +product_group: composition_analysis +product_category: license_compliance +value_type: number +status: active +milestone: '14.8' +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/79454 +time_frame: 28d +data_source: redis_hll +data_category: optional +instrumentation_class: RedisHLLMetric +distribution: +- ce +- ee +tier: +- free +- premium +- ultimate +options: + events: + - p_ci_templates_jobs_license_scanning diff --git a/config/metrics/counts_7d/20210216184508_p_ci_templates_implicit_security_sast_weekly.yml b/config/metrics/counts_7d/20210216184508_p_ci_templates_implicit_security_sast_weekly.yml index 2a70231dcac..b5d9e4f7992 100644 --- a/config/metrics/counts_7d/20210216184508_p_ci_templates_implicit_security_sast_weekly.yml +++ b/config/metrics/counts_7d/20210216184508_p_ci_templates_implicit_security_sast_weekly.yml @@ -15,7 +15,6 @@ options: events: - p_ci_templates_implicit_security_sast distribution: -distribution: - ce - ee tier: diff --git a/config/metrics/counts_7d/20210216184536_p_ci_templates_auto_devops_deploy_weekly.yml b/config/metrics/counts_7d/20210216184536_p_ci_templates_auto_devops_deploy_weekly.yml index b06a4fa5577..408c03894fd 100644 --- a/config/metrics/counts_7d/20210216184536_p_ci_templates_auto_devops_deploy_weekly.yml +++ b/config/metrics/counts_7d/20210216184536_p_ci_templates_auto_devops_deploy_weekly.yml @@ -25,4 +25,3 @@ tier: - premium - ultimate performance_indicator_type: [] -milestone: "<13.9" diff --git a/config/metrics/counts_7d/20210216184557_ci_templates_total_unique_counts_weekly.yml b/config/metrics/counts_7d/20210216184557_ci_templates_total_unique_counts_weekly.yml index ecb654a399c..290ceb5754a 100644 --- a/config/metrics/counts_7d/20210216184557_ci_templates_total_unique_counts_weekly.yml +++ b/config/metrics/counts_7d/20210216184557_ci_templates_total_unique_counts_weekly.yml @@ -91,6 +91,8 @@ options: - p_ci_templates_jobs_secret_detection - p_ci_templates_jobs_code_intelligence - p_ci_templates_jobs_code_quality + - p_ci_templates_jobs_dependency_scanning + - p_ci_templates_jobs_license_scanning - p_ci_templates_jobs_deploy_ecs - p_ci_templates_jobs_deploy_ec2 - p_ci_templates_jobs_deploy @@ -135,6 +137,8 @@ options: - p_ci_templates_implicit_jobs_secret_detection - p_ci_templates_implicit_jobs_code_intelligence - p_ci_templates_implicit_jobs_code_quality + - p_ci_templates_implicit_jobs_dependency_scanning + - p_ci_templates_implicit_jobs_license_scanning - p_ci_templates_implicit_jobs_deploy_ecs - p_ci_templates_implicit_jobs_deploy_ec2 - p_ci_templates_implicit_auto_devops_deploy diff --git a/config/metrics/counts_7d/20210427103328_code_review_group_monthly_active_users.yml b/config/metrics/counts_7d/20210427103328_code_review_group_monthly_active_users.yml index 112e8655d37..fa58494cc05 100644 --- a/config/metrics/counts_7d/20210427103328_code_review_group_monthly_active_users.yml +++ b/config/metrics/counts_7d/20210427103328_code_review_group_monthly_active_users.yml @@ -18,4 +18,3 @@ tier: - free - premium - ultimate - diff --git a/config/metrics/counts_7d/20210427103407_code_review_category_monthly_active_users.yml b/config/metrics/counts_7d/20210427103407_code_review_category_monthly_active_users.yml index 8680e59f141..e1c61db272b 100644 --- a/config/metrics/counts_7d/20210427103407_code_review_category_monthly_active_users.yml +++ b/config/metrics/counts_7d/20210427103407_code_review_category_monthly_active_users.yml @@ -19,4 +19,3 @@ tier: - free - premium - ultimate - diff --git a/config/metrics/counts_7d/20210902191054_i_quickactions_unapprove_weekly.yml b/config/metrics/counts_7d/20210902191054_i_quickactions_unapprove_weekly.yml index 38659cdba82..b3a4699307e 100644 --- a/config/metrics/counts_7d/20210902191054_i_quickactions_unapprove_weekly.yml +++ b/config/metrics/counts_7d/20210902191054_i_quickactions_unapprove_weekly.yml @@ -15,10 +15,6 @@ instrumentation_class: RedisHLLMetric options: events: - i_quickactions_unapprove -instrumentation_class: RedisHLLMetric -options: - events: - - i_quickactions_unapprove distribution: - ce - ee diff --git a/config/metrics/counts_7d/20211126154206_users_expanding_testing_license_compliance_report_weekly.yml b/config/metrics/counts_7d/20211126154206_users_expanding_testing_license_compliance_report_weekly.yml index b96d0fff1bd..ae251b65362 100644 --- a/config/metrics/counts_7d/20211126154206_users_expanding_testing_license_compliance_report_weekly.yml +++ b/config/metrics/counts_7d/20211126154206_users_expanding_testing_license_compliance_report_weekly.yml @@ -13,8 +13,8 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/75643 time_frame: 7d data_source: redis_hll instrumentation_class: RedisHLLMetric -options: - events: +options: + events: - users_expanding_testing_license_compliance_report distribution: - ce diff --git a/config/metrics/counts_7d/20211201154118_users_visiting_license_compliance_full_report_weekly.yml b/config/metrics/counts_7d/20211201154118_users_visiting_license_compliance_full_report_weekly.yml index 4a6ee19fffe..96c2fb4ced9 100644 --- a/config/metrics/counts_7d/20211201154118_users_visiting_license_compliance_full_report_weekly.yml +++ b/config/metrics/counts_7d/20211201154118_users_visiting_license_compliance_full_report_weekly.yml @@ -13,8 +13,8 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/75643 time_frame: 7d data_source: redis_hll instrumentation_class: RedisHLLMetric -options: - events: +options: + events: - users_visiting_testing_license_compliance_full_report distribution: - ce diff --git a/config/metrics/counts_7d/20211202094430_users_visiting_manage_license_compliance_weekly.yml b/config/metrics/counts_7d/20211202094430_users_visiting_manage_license_compliance_weekly.yml index 9bff8263121..831ec2509b1 100644 --- a/config/metrics/counts_7d/20211202094430_users_visiting_manage_license_compliance_weekly.yml +++ b/config/metrics/counts_7d/20211202094430_users_visiting_manage_license_compliance_weekly.yml @@ -13,8 +13,8 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/75643 time_frame: 7d data_source: redis_hll instrumentation_class: RedisHLLMetric -options: - events: +options: + events: - users_visiting_testing_manage_license_compliance distribution: - ce diff --git a/config/metrics/counts_7d/20211216084934_users_clicking_license_testing_visiting_external_website_weekly.yml b/config/metrics/counts_7d/20211216084934_users_clicking_license_testing_visiting_external_website_weekly.yml index aaa5a4f2675..cfdbc345ef9 100644 --- a/config/metrics/counts_7d/20211216084934_users_clicking_license_testing_visiting_external_website_weekly.yml +++ b/config/metrics/counts_7d/20211216084934_users_clicking_license_testing_visiting_external_website_weekly.yml @@ -13,8 +13,8 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/76917 time_frame: 7d data_source: redis_hll instrumentation_class: RedisHLLMetric -options: - events: +options: + events: - users_clicking_license_testing_visiting_external_website distribution: - ce diff --git a/config/metrics/counts_7d/20220210134101_p_ci_templates_implicit_jobs_dependency_scanning_weekly.yml b/config/metrics/counts_7d/20220210134101_p_ci_templates_implicit_jobs_dependency_scanning_weekly.yml new file mode 100644 index 00000000000..cc04ade9317 --- /dev/null +++ b/config/metrics/counts_7d/20220210134101_p_ci_templates_implicit_jobs_dependency_scanning_weekly.yml @@ -0,0 +1,25 @@ +--- +key_path: redis_hll_counters.ci_templates.p_ci_templates_implicit_jobs_dependency_scanning_weekly +description: Weekly counts for implicit use of Dependency Scanning CI template (Jobs folder) +product_section: sec +product_stage: secure +product_group: composition_analysis +product_category: dependency_scanning +value_type: number +status: active +milestone: '14.8' +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/79454 +time_frame: 7d +data_source: redis_hll +data_category: optional +instrumentation_class: RedisHLLMetric +distribution: +- ce +- ee +tier: +- free +- premium +- ultimate +options: + events: + - p_ci_templates_implicit_jobs_dependency_scanning diff --git a/config/metrics/counts_7d/20220210134101_p_ci_templates_implicit_jobs_license_scanning_weekly.yml b/config/metrics/counts_7d/20220210134101_p_ci_templates_implicit_jobs_license_scanning_weekly.yml new file mode 100644 index 00000000000..78256aeeea8 --- /dev/null +++ b/config/metrics/counts_7d/20220210134101_p_ci_templates_implicit_jobs_license_scanning_weekly.yml @@ -0,0 +1,25 @@ +--- +key_path: redis_hll_counters.ci_templates.p_ci_templates_implicit_jobs_license_scanning_weekly +description: Weekly counts for implicit use of License Scanning CI template (Jobs folder) +product_section: sec +product_stage: secure +product_group: composition_analysis +product_category: license_compliance +value_type: number +status: active +milestone: '14.8' +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/79454 +time_frame: 7d +data_source: redis_hll +data_category: optional +instrumentation_class: RedisHLLMetric +distribution: +- ce +- ee +tier: +- free +- premium +- ultimate +options: + events: + - p_ci_templates_implicit_jobs_license_scanning diff --git a/config/metrics/counts_7d/20220210134101_p_ci_templates_jobs_dependency_scanning_weekly.yml b/config/metrics/counts_7d/20220210134101_p_ci_templates_jobs_dependency_scanning_weekly.yml new file mode 100644 index 00000000000..19bf2cf65ba --- /dev/null +++ b/config/metrics/counts_7d/20220210134101_p_ci_templates_jobs_dependency_scanning_weekly.yml @@ -0,0 +1,25 @@ +--- +key_path: redis_hll_counters.ci_templates.p_ci_templates_jobs_dependency_scanning_weekly +description: Weekly counts for Dependency Scanning CI template (Jobs folder) +product_section: sec +product_stage: secure +product_group: composition_analysis +product_category: dependency_scanning +value_type: number +status: active +milestone: '14.8' +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/79454 +time_frame: 7d +data_source: redis_hll +data_category: optional +instrumentation_class: RedisHLLMetric +distribution: +- ce +- ee +tier: +- free +- premium +- ultimate +options: + events: + - p_ci_templates_jobs_dependency_scanning diff --git a/config/metrics/counts_7d/20220210134101_p_ci_templates_jobs_license_scanning_weekly.yml b/config/metrics/counts_7d/20220210134101_p_ci_templates_jobs_license_scanning_weekly.yml new file mode 100644 index 00000000000..c8977d75e9c --- /dev/null +++ b/config/metrics/counts_7d/20220210134101_p_ci_templates_jobs_license_scanning_weekly.yml @@ -0,0 +1,25 @@ +--- +key_path: redis_hll_counters.ci_templates.p_ci_templates_jobs_license_scanning_weekly +description: Weekly counts for License Scanning CI template (Jobs folder) +product_section: sec +product_stage: secure +product_group: composition_analysis +product_category: license_compliance +value_type: number +status: active +milestone: '14.8' +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/79454 +time_frame: 7d +data_source: redis_hll +data_category: optional +instrumentation_class: RedisHLLMetric +distribution: +- ce +- ee +tier: +- free +- premium +- ultimate +options: + events: + - p_ci_templates_jobs_license_scanning diff --git a/config/metrics/counts_all/20210216183023_wiki_pages_view.yml b/config/metrics/counts_all/20210216183023_wiki_pages_view.yml index c4cc4bfcf9c..16cec4839a5 100644 --- a/config/metrics/counts_all/20210216183023_wiki_pages_view.yml +++ b/config/metrics/counts_all/20210216183023_wiki_pages_view.yml @@ -20,4 +20,3 @@ tier: - premium - ultimate performance_indicator_type: [] -milestone: "<13.9" diff --git a/config/metrics/counts_all/20210303153004_package_events_i_package_rubygems_push_package.yml b/config/metrics/counts_all/20210303153004_package_events_i_package_rubygems_push_package.yml index 0ba6d527e3a..73b2e4a5a25 100644 --- a/config/metrics/counts_all/20210303153004_package_events_i_package_rubygems_push_package.yml +++ b/config/metrics/counts_all/20210303153004_package_events_i_package_rubygems_push_package.yml @@ -19,4 +19,3 @@ tier: - free - premium - ultimate - diff --git a/config/metrics/counts_all/20210423005644_i_analytics_dev_ops_adoption.yml b/config/metrics/counts_all/20210423005644_i_analytics_dev_ops_adoption.yml index b769b5d47f1..5a7073d3525 100644 --- a/config/metrics/counts_all/20210423005644_i_analytics_dev_ops_adoption.yml +++ b/config/metrics/counts_all/20210423005644_i_analytics_dev_ops_adoption.yml @@ -18,4 +18,3 @@ tier: - free - premium - ultimate - diff --git a/config/metrics/counts_all/20210505015532_kubernetes_agent_k8s_api_proxy_request.yml b/config/metrics/counts_all/20210505015532_kubernetes_agent_k8s_api_proxy_request.yml index e558763e2a0..a1a49a1a07e 100644 --- a/config/metrics/counts_all/20210505015532_kubernetes_agent_k8s_api_proxy_request.yml +++ b/config/metrics/counts_all/20210505015532_kubernetes_agent_k8s_api_proxy_request.yml @@ -9,7 +9,7 @@ product_category: kubernetes_management value_type: number status: active milestone: '13.12' -introduced_by_url: +introduced_by_url: time_frame: all data_source: redis distribution: diff --git a/config/metrics/counts_all/20210510201919_in_product_marketing_email_create_0_cta_clicked.yml b/config/metrics/counts_all/20210510201919_in_product_marketing_email_create_0_cta_clicked.yml index cb5f3185eb0..92209dffa2d 100644 --- a/config/metrics/counts_all/20210510201919_in_product_marketing_email_create_0_cta_clicked.yml +++ b/config/metrics/counts_all/20210510201919_in_product_marketing_email_create_0_cta_clicked.yml @@ -20,4 +20,3 @@ tier: - free - premium - ultimate - diff --git a/config/metrics/counts_all/20210510202356_in_product_marketing_email_create_1_cta_clicked.yml b/config/metrics/counts_all/20210510202356_in_product_marketing_email_create_1_cta_clicked.yml index ab50a629468..a864858ae3a 100644 --- a/config/metrics/counts_all/20210510202356_in_product_marketing_email_create_1_cta_clicked.yml +++ b/config/metrics/counts_all/20210510202356_in_product_marketing_email_create_1_cta_clicked.yml @@ -20,4 +20,3 @@ tier: - free - premium - ultimate - diff --git a/config/metrics/counts_all/20210510203005_in_product_marketing_email_verify_1_cta_clicked.yml b/config/metrics/counts_all/20210510203005_in_product_marketing_email_verify_1_cta_clicked.yml index 2b08899e228..6675c6e9a0b 100644 --- a/config/metrics/counts_all/20210510203005_in_product_marketing_email_verify_1_cta_clicked.yml +++ b/config/metrics/counts_all/20210510203005_in_product_marketing_email_verify_1_cta_clicked.yml @@ -20,4 +20,3 @@ tier: - free - premium - ultimate - diff --git a/config/metrics/counts_all/20210510203104_in_product_marketing_email_trial_1_cta_clicked.yml b/config/metrics/counts_all/20210510203104_in_product_marketing_email_trial_1_cta_clicked.yml index 67543b98020..0b68b88570c 100644 --- a/config/metrics/counts_all/20210510203104_in_product_marketing_email_trial_1_cta_clicked.yml +++ b/config/metrics/counts_all/20210510203104_in_product_marketing_email_trial_1_cta_clicked.yml @@ -20,4 +20,3 @@ tier: - free - premium - ultimate - diff --git a/config/metrics/counts_all/20210510203203_in_product_marketing_email_team_1_cta_clicked.yml b/config/metrics/counts_all/20210510203203_in_product_marketing_email_team_1_cta_clicked.yml index 3d5150f6042..7bb67a87c75 100644 --- a/config/metrics/counts_all/20210510203203_in_product_marketing_email_team_1_cta_clicked.yml +++ b/config/metrics/counts_all/20210510203203_in_product_marketing_email_team_1_cta_clicked.yml @@ -20,4 +20,3 @@ tier: - free - premium - ultimate - diff --git a/config/metrics/counts_all/20210709210941_package_events_i_package_pull_symbol_package.yml b/config/metrics/counts_all/20210709210941_package_events_i_package_pull_symbol_package.yml index 8edd10b3a42..7d390649020 100644 --- a/config/metrics/counts_all/20210709210941_package_events_i_package_pull_symbol_package.yml +++ b/config/metrics/counts_all/20210709210941_package_events_i_package_pull_symbol_package.yml @@ -19,4 +19,4 @@ distribution: tier: - free - premium -- ultimate
\ No newline at end of file +- ultimate diff --git a/config/metrics/counts_all/20210709211248_package_events_i_package_pull_symbol_package_by_guest.yml b/config/metrics/counts_all/20210709211248_package_events_i_package_pull_symbol_package_by_guest.yml index 33b164a9653..c80ddea659f 100644 --- a/config/metrics/counts_all/20210709211248_package_events_i_package_pull_symbol_package_by_guest.yml +++ b/config/metrics/counts_all/20210709211248_package_events_i_package_pull_symbol_package_by_guest.yml @@ -19,4 +19,4 @@ distribution: tier: - free - premium -- ultimate
\ No newline at end of file +- ultimate diff --git a/config/metrics/settings/20210323120839_topology.yml b/config/metrics/settings/20210323120839_topology.yml index a254ad36dfe..d07de69e530 100644 --- a/config/metrics/settings/20210323120839_topology.yml +++ b/config/metrics/settings/20210323120839_topology.yml @@ -5,7 +5,7 @@ description: Topology data product_section: enablement product_stage: enablement product_group: group::memory -product_category: +product_category: value_type: object status: active milestone: "13.11" diff --git a/doc/administration/compliance.md b/doc/administration/compliance.md index 843d5d0930a..7c3f36c71c2 100644 --- a/doc/administration/compliance.md +++ b/doc/administration/compliance.md @@ -16,7 +16,7 @@ relevant compliance standards. ## Policy management Organizations have unique policy requirements, either due to organizational -standards or mandates from regulatory bodies. The following features help you +standards or mandates from regulatory bodies. The following features help you define rules and policies to adhere to workflow requirements, separation of duties, and secure supply chain best practices: @@ -31,7 +31,7 @@ and secure supply chain best practices: - [**Merge request approvals**](../user/project/merge_requests/approvals/index.md) (for instances, groups, and projects): Configure approvals required for merge requests. -- [**Push rules**](../push_rules/push_rules.md) (for instances, groups, and +- [**Push rules**](../user/project/repository/push_rules.md) (for instances, groups, and projects): Control pushes to your repositories. - Separation of duties using [**protected branches**](../user/project/protected_branches.md#require-code-owner-approval-on-a-protected-branch) and [**custom CI/CD configuration paths**](../ci/pipelines/settings.md#specify-a-custom-cicd-configuration-file) (for projects): Users can leverage the GitLab cross-project YAML configurations diff --git a/doc/administration/packages/container_registry.md b/doc/administration/packages/container_registry.md index d3cf153c0a0..33a5311709f 100644 --- a/doc/administration/packages/container_registry.md +++ b/doc/administration/packages/container_registry.md @@ -1379,7 +1379,7 @@ project or branch name. Special characters can include: To get around this, you can [change the group path](../../user/group/index.md#change-a-groups-path), [change the project path](../../user/project/settings/index.md#renaming-a-repository) or change the -branch name. Another option is to create a [push rule](../../push_rules/push_rules.md) to prevent +branch name. Another option is to create a [push rule](../../user/project/repository/push_rules.md) to prevent this at the instance level. ### Image push errors diff --git a/doc/api/projects.md b/doc/api/projects.md index e1f248f77b3..db8d2361439 100644 --- a/doc/api/projects.md +++ b/doc/api/projects.md @@ -2491,7 +2491,7 @@ POST /projects/:id/housekeeping ### Get project push rules -Get the [push rules](../push_rules/push_rules.md#enabling-push-rules) of a +Get the [push rules](../user/project/repository/push_rules.md#enabling-push-rules) of a project. ```plaintext diff --git a/doc/ci/jobs/job_control.md b/doc/ci/jobs/job_control.md index c93dd75285a..6523de0ed1e 100644 --- a/doc/ci/jobs/job_control.md +++ b/doc/ci/jobs/job_control.md @@ -950,3 +950,19 @@ and can cause unexpected behavior, including: Additionally, rules with `changes` always evaluate as true in [scheduled pipelines](../pipelines/schedules.md). All files are considered to have changed when a scheduled pipeline runs, so jobs might always be added to scheduled pipelines that use `changes`. + +### `You are not allowed to download code from this project.` error message + +You might see pipelines fail when a GitLab administrator runs a protected manual job +in a private project. + +CI/CD jobs usually clone the project when the job starts, and this uses [the permissions](../../user/permissions.md#job-permissions) +of the user that runs the job. All users, including administrators, must be direct members +of a private project to clone the source of that project. [An issue exists](https://gitlab.com/gitlab-org/gitlab/-/issues/23130) +to change this behavior. + +To run protected manual jobs: + +- Add the administrator as a direct member of the private project (any role) +- [Impersonate a user](../../user/admin_area/index.md#user-impersonation) who is a + direct member of the project. diff --git a/doc/push_rules/push_rules.md b/doc/push_rules/push_rules.md index 2fb04d98d34..3c4b0e10bf9 100644 --- a/doc/push_rules/push_rules.md +++ b/doc/push_rules/push_rules.md @@ -1,288 +1,9 @@ --- -stage: Create -group: Source Code -info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments" +redirect_to: '../user/project/repository/push_rules.md' +remove_date: '2022-05-10' --- -# Push rules **(PREMIUM)** +This document was moved to [another location](../user/project/repository/push_rules.md). -Gain additional control over what can and can't be pushed to your repository by using -regular expressions to reject pushes based on commit contents, branch names or file details. - -GitLab already offers [protected branches](../user/project/protected_branches.md), but there are -cases when you need some specific rules. Some common scenarios: preventing Git tag removal, or -enforcing a special format for commit messages. - -Push rules are [pre-receive Git hooks](https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks) you -can enable in a user-friendly interface. They are defined either: - -- Globally if you are an administrator. -- Per project, so you can have different rules applied to different - projects depending on your needs. - -## Use cases - -Every push rule could have its own use case, but let's consider some examples. - -### Commit messages with a specific reference - -Let's assume you have the following requirements for your workflow: - -- every commit should reference a Jira issue, for example: `Refactored css. Fixes JIRA-123.` -- users should not be able to remove Git tags with `git push` - -Write a regular expression that requires the mention -of a Jira issue in the commit message, like `JIRA\-\d+`. - -Now when a user tries to push a commit with a message `Bugfix`, their push is -declined. Only pushing commits with messages like `Bugfix according to JIRA-123` -is accepted. - -### Restrict branch names - -If your company has a strict policy for branch names, you may want the branches to start -with a certain name. This approach enables different -GitLab CI/CD jobs (such as `feature`, `hotfix`, `docker`, `android`) that rely on the -branch name. - -Your developers may not remember that policy, so they might push to -various branches, and CI pipelines might not work as expected. By restricting the -branch names globally in Push Rules, such mistakes are prevented. -All branch names that don't match your push rule are rejected. - -Note that the name of your default branch is always allowed, regardless of the branch naming -regular expression (regex) specified. GitLab is configured this way -because merges typically have the default branch as their target. -If you have other target branches, include them in your regex. (See [Enabling push rules](#enabling-push-rules)). - -The default branch also defaults to being a [protected branch](../user/project/protected_branches.md), -which already limits users from pushing directly. - -Some example regular expressions you can use in push rules: - -- `^JIRA-` Branches must start with `JIRA-`. -- `-JIRA$` Branches must end with `-JIRA`. -- `^[a-z0-9\\-]{4,15}$` Branches must be between `4` and `15` characters long, - accepting only lowercase letters, numbers and dashes. - -#### Default restricted branch names - -> Introduced in GitLab 12.10. - -By default, GitLab restricts certain formats of branch names for security purposes. -40-character hexadecimal names, similar to Git commit hashes, are prohibited. - -### Custom Push Rules **(PREMIUM SELF)** - -It's possible to create custom push rules rather than the push rules available in -**Admin Area > Push Rules** by using more advanced server hooks. - -See [server hooks](../administration/server_hooks.md) for more information. - -## Enabling push rules - -You can create push rules for all new projects to inherit, but they can be overridden -at the project level or the [group level](../user/group/index.md#group-push-rules). - -To create global push rules: - -1. On the top bar, select **Menu > Admin**. -1. On the left sidebar, select **Push Rules**. - -To override global push rules in a project's settings: - -1. On the top bar, select **Menu > Projects** and find your project. -1. On the left sidebar, select **Settings > Repository**. -1. Expand **Push rules**. -1. Set the rule you want. -1. Select **Save push rules**. - -The following options are available: - -| Push rule | Description | -|---------------------------------|-------------| -| Removal of tags with `git push` | Forbid users to remove Git tags with `git push`. Tags can be deleted through the web UI. | -| Check whether the commit author is a GitLab user | Restrict commits to existing GitLab users (checked against their emails). <sup>1</sup> | -| Reject unverified users | GitLab rejects any commit that was not committed by the same user as the user who pushed it, or where the committer's email address is not [confirmed](../security/user_email_confirmation.md). | -| Check whether commit is signed through GPG | Reject commit when it is not signed through GPG. Read [signing commits with GPG](../user/project/repository/gpg_signed_commits/index.md). | -| Prevent pushing secret files | GitLab rejects any files that are likely to contain secrets. See the [forbidden file names](#prevent-pushing-secrets-to-the-repository). | -| Require expression in commit messages | Only commit messages that match this regular expression are allowed to be pushed. <sup>2</sup> Leave empty to allow any commit message. Uses multiline mode, which can be disabled using `(?-m)`. | -| Reject expression in commit messages | Only commit messages that do not match this regular expression are allowed to be pushed. <sup>2</sup> Leave empty to allow any commit message. Uses multiline mode, which can be disabled using `(?-m)`. | -| Restrict by branch name | Only branch names that match this regular expression are allowed to be pushed. <sup>2</sup> Leave empty to allow all branch names. | -| Restrict by commit author's email | Only commit author's email that match this regular expression are allowed to be pushed. <sup>1</sup> <sup>2</sup> Leave empty to allow any email. | -| Prohibited file names | Any committed filenames that match this regular expression and do not already exist in the repository are not allowed to be pushed. <sup>2</sup> Leave empty to allow any filenames. See [common examples](#prohibited-file-names). | -| Maximum file size | Pushes that contain added or updated files that exceed this file size (in MB) are rejected. Set to 0 to allow files of any size. Files tracked by Git LFS are exempted. | - -1. Checks both the commit author and committer. -1. GitLab uses [RE2 syntax](https://github.com/google/re2/wiki/Syntax) for regular expressions in push rules, and you can test them at the [regex101 regex tester](https://regex101.com/). - -### Caveat to "Reject unsigned commits" push rule - -This push rule ignores commits that are authenticated and created by GitLab -(either through the UI or API). When the **Reject unsigned commits** push rule is -enabled, unsigned commits may still show up in the commit history if a commit was -created **in** GitLab itself. As expected, commits created outside GitLab and -pushed to the repository are rejected. For more information about how GitLab -plans to fix this issue, read [issue #19185](https://gitlab.com/gitlab-org/gitlab/-/issues/19185). - -#### "Reject unsigned commits" push rule disables Web IDE - -In 13.10, if a project has the "Reject unsigned commits" push rule, the user is not allowed to -commit through GitLab Web IDE. - -To allow committing through the Web IDE on a project with this push rule, a GitLab administrator -must disable the feature flag `reject_unsigned_commits_by_gitlab`. This can be done through a -[rails console](../administration/operations/rails_console.md) and running: - -```ruby -Feature.disable(:reject_unsigned_commits_by_gitlab) -``` - -## Prevent pushing secrets to the repository - -> Moved to GitLab Premium in 13.9. - -Secrets, such as credential files and SSH private keys, should never be committed to a version control -system. In GitLab, you can use a predefined list of files to block those files from a -repository. Any merge request containing a file matching the list is blocked from being merged. -Files already committed to the repository are not restricted by this push rule. - -Files blocked by this rule are listed below. For a complete list of criteria, see -[`files_denylist.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/lib/gitlab/checks/files_denylist.yml). - -- AWS CLI credential blobs: - - - `.aws/credentials` - - `aws/credentials` - - `homefolder/aws/credentials` - -- Private RSA SSH keys: - - - `/ssh/id_rsa` - - `/.ssh/personal_rsa` - - `/config/server_rsa` - - `id_rsa` - - `.id_rsa` - -- Private DSA SSH keys: - - - `/ssh/id_dsa` - - `/.ssh/personal_dsa` - - `/config/server_dsa` - - `id_dsa` - - `.id_dsa` - -- Private ED25519 SSH keys: - - - `/ssh/id_ed25519` - - `/.ssh/personal_ed25519` - - `/config/server_ed25519` - - `id_ed25519` - - `.id_ed25519` - -- Private ECDSA SSH keys: - - - `/ssh/id_ecdsa` - - `/.ssh/personal_ecdsa` - - `/config/server_ecdsa` - - `id_ecdsa` - - `.id_ecdsa` - -- Private ECDSA_SK SSH keys (GitLab 14.8 and later): - - - `/ssh/id_ecdsa_sk` - - `/.ssh/personal_ecdsa_sk` - - `/config/server_ecdsa_sk` - - `id_ecdsa_sk` - - `.id_ecdsa_sk` - -- Private ED25519_SK SSH keys (GitLab 14.8 and later): - - - `/ssh/id_ed25519_sk` - - `/.ssh/personal_ed25519_sk` - - `/config/server_ed25519_sk` - - `id_ed25519_sk` - - `.id_ed25519_sk` - -- Any files ending with these suffixes: - - - `*.pem` - - `*.key` - - `*.history` - - `*_history` - -### Prevent pushing secrets to all projects - -To set a global push rule to prevent pushing secrets to all projects: - -1. On the top bar, select **Menu > Admin**. -1. On the left sidebar, select **Push Rules**. -1. Expand **Push rules**. -1. Select **Prevent pushing secret files**. -1. Select **Save push rules**. - -### Prevent pushing secrets to a project - -The push rule of a project overrides the global push rule. - -To prevent pushing secrets to a project: - -1. On the top bar, select **Menu > Projects** and find your project. -1. On the left sidebar, select **Settings > Repository**. -1. Expand **Push rules**. -1. Select **Prevent pushing secret files**. -1. Select **Save push rules**. - -## Prohibited file names - -> Moved to GitLab Premium in 13.9. - -Each filename contained in a Git push is compared to the regular expression in this field. Filenames in Git consist of both the file's name and any directory that may precede it. A singular regular expression can contain multiple independent matches used as exclusions. File names can be broadly matched to any location in the repository, or restricted to specific locations. Filenames can also be partial matches used to exclude file types by extension. - -The following examples make use of regex string boundary characters which match the beginning of a string (`^`), and the end (`$`). They also include instances where either the directory path or the filename can include `.` or `/`. Both of these special regex characters have to be escaped with a backslash `\\` to be used as normal characters in a match condition. - -Example: prevent pushing any `.exe` files to any location in the repository. This uses a partial match, which matches any filename that contains `.exe` at the end: - -```plaintext -\.exe$ -``` - -Example: prevent a specific configuration file in the repository root from being pushed: - -```plaintext -^config\.yml$ -``` - -Example: prevent a specific configuration file in a known directory from being pushed: - -```plaintext -^directory-name\/config\.yml$ -``` - -Example: prevent the specific file named `install.exe` from being pushed to any -location in the repository. The parenthesized expression `(^|\/)` matches either -a file following a directory separator or a file in the root directory of the repository: - -```plaintext -(^|\/)install\.exe$ -``` - -Example: combining all of the above in a single expression. The preceding expressions rely -on the end-of-string character `$`. We can move that part of each expression to the -end of the grouped collection of match conditions where it is appended to all matches: - -```plaintext -(\.exe|^config\.yml|^directory-name\/config\.yml|(^|\/)install\.exe)$ -``` - -<!-- ## Troubleshooting - -Include any troubleshooting steps that you can foresee. If you know beforehand what issues -one might have when setting this up, or when something is changed, or on upgrading, it's -important to describe those, too. Think of things that may go wrong and include them here. -This is important to minimize requests for support, and to avoid doc comments with -questions that you know someone might ask. - -Each scenario can be a third-level heading, e.g. `### Getting error message X`. -If you have none to add when creating a doc, leave this section in place -but commented out to help encourage others to add to it in the future. --> +<!-- This redirect file can be deleted after <2022-05-10>. --> +<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page --> diff --git a/doc/subscriptions/bronze_starter.md b/doc/subscriptions/bronze_starter.md index 78ac39395af..7ca3a3ffade 100644 --- a/doc/subscriptions/bronze_starter.md +++ b/doc/subscriptions/bronze_starter.md @@ -98,7 +98,7 @@ the tiers are no longer mentioned in GitLab documentation: - Runners: - Run pipelines in the parent project [for merge requests from a forked project](../ci/pipelines/merge_request_pipelines.md#run-pipelines-in-the-parent-project) - [Shared runners CI/CD minutes](../ci/pipelines/cicd_minutes.md) -- [Push rules](../push_rules/push_rules.md) +- [Push rules](../user/project/repository/push_rules.md) - SAML for self-managed GitLab instance: - [Administrator groups](../integration/saml.md#administrator-groups) - [Auditor groups](../integration/saml.md#auditor-groups) diff --git a/doc/user/admin_area/index.md b/doc/user/admin_area/index.md index 49a8e8a35b7..57a4a746ff0 100644 --- a/doc/user/admin_area/index.md +++ b/doc/user/admin_area/index.md @@ -32,7 +32,7 @@ The Admin Area is made up of the following sections: | **{slight-frown}** Abuse Reports | Manage [abuse reports](review_abuse_reports.md) submitted by your users. | | **{license}** License | Upload, display, and remove [licenses](license.md). | | **{cloud-gear}** Kubernetes | Create and manage instance-level [Kubernetes clusters](../instance/clusters/index.md). | -| **{push-rules}** Push rules | Configure pre-defined Git [push rules](../../push_rules/push_rules.md) for projects. Also, configure [merge requests approvers rules](merge_requests_approvals.md). | +| **{push-rules}** Push rules | Configure pre-defined Git [push rules](../project/repository/push_rules.md) for projects. Also, configure [merge requests approvers rules](merge_requests_approvals.md). | | **{location-dot}** Geo | Configure and maintain [Geo nodes](geo_nodes.md). | | **{key}** Deploy keys | Create instance-wide [SSH deploy keys](../project/deploy_keys/index.md). | | **{lock}** Credentials | View [credentials](credentials_inventory.md) that can be used to access your instance. | diff --git a/doc/user/admin_area/settings/email.md b/doc/user/admin_area/settings/email.md index 6bc9e97629c..e4fc3b6e6d4 100644 --- a/doc/user/admin_area/settings/email.md +++ b/doc/user/admin_area/settings/email.md @@ -56,7 +56,7 @@ To change the hostname used in private commit emails: NOTE: After the hostname is configured, every private commit email using the previous hostname is not -recognized by GitLab. This can directly conflict with certain [Push rules](../../../push_rules/push_rules.md) such as +recognized by GitLab. This can directly conflict with certain [Push rules](../../project/repository/push_rules.md) such as `Check whether author is a GitLab user` and `Check whether committer is the current authenticated user`. ## Custom additional text **(PREMIUM SELF)** diff --git a/doc/user/group/index.md b/doc/user/group/index.md index adfc0ddf7ed..ec76dc52516 100644 --- a/doc/user/group/index.md +++ b/doc/user/group/index.md @@ -789,7 +789,7 @@ Existing forks are not removed. > - [Feature flag removed](https://gitlab.com/gitlab-org/gitlab/-/issues/224129) in GitLab 13.4. Group push rules allow group maintainers to set -[push rules](../../push_rules/push_rules.md) for newly created projects in the specific group. +[push rules](../project/repository/push_rules.md) for newly created projects in the specific group. To configure push rules for a group: diff --git a/doc/user/permissions.md b/doc/user/permissions.md index e45bce4b24f..4476b0dd75b 100644 --- a/doc/user/permissions.md +++ b/doc/user/permissions.md @@ -171,7 +171,7 @@ The following table lists project permissions available for each role: | [Repository](project/repository/index.md):<br>Rewrite or remove Git tags | | | ✓ | ✓ | ✓ | | [Repository](project/repository/index.md):<br>Enable or disable branch protection | | | | ✓ | ✓ | | [Repository](project/repository/index.md):<br>Enable or disable tag protection | | | | ✓ | ✓ | -| [Repository](project/repository/index.md):<br>Manage [push rules](../push_rules/push_rules.md) | | | | ✓ | ✓ | +| [Repository](project/repository/index.md):<br>Manage [push rules](project/repository/push_rules.md) | | | | ✓ | ✓ | | [Repository](project/repository/index.md):<br>Push to protected branches (*4*) | | | | ✓ | ✓ | | [Repository](project/repository/index.md):<br>Turn on or off protected branch push for developers | | | | ✓ | ✓ | | [Repository](project/repository/index.md):<br>Remove fork relationship | | | | | ✓ | diff --git a/doc/user/project/push_options.md b/doc/user/project/push_options.md index 846d4732533..20dd37578fd 100644 --- a/doc/user/project/push_options.md +++ b/doc/user/project/push_options.md @@ -7,7 +7,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w # Push Options **(FREE)** GitLab supports using client-side [Git push options](https://git-scm.com/docs/git-push#Documentation/git-push.txt--oltoptiongt) -to perform various actions at the same time as pushing changes. Additionally, [Push Rules](../../push_rules/push_rules.md) offer server-side control and enforcement options. +to perform various actions at the same time as pushing changes. Additionally, [Push Rules](repository/push_rules.md) offer server-side control and enforcement options. Currently, there are push options available for: diff --git a/doc/user/project/repository/gpg_signed_commits/index.md b/doc/user/project/repository/gpg_signed_commits/index.md index 0c5c0d5fa7c..f5cea8a8075 100644 --- a/doc/user/project/repository/gpg_signed_commits/index.md +++ b/doc/user/project/repository/gpg_signed_commits/index.md @@ -273,7 +273,7 @@ To remove a GPG key from your account: ## Rejecting commits that are not signed **(PREMIUM)** You can configure your project to reject commits that aren't GPG-signed -via [push rules](../../../../push_rules/push_rules.md). +via [push rules](../push_rules.md). ## GPG signing API diff --git a/doc/user/project/repository/push_rules.md b/doc/user/project/repository/push_rules.md new file mode 100644 index 00000000000..bb473a2830b --- /dev/null +++ b/doc/user/project/repository/push_rules.md @@ -0,0 +1,288 @@ +--- +stage: Create +group: Source Code +info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments" +--- + +# Push rules **(PREMIUM)** + +Gain additional control over what can and can't be pushed to your repository by using +regular expressions to reject pushes based on commit contents, branch names or file details. + +GitLab already offers [protected branches](../protected_branches.md), but there are +cases when you need some specific rules. Some common scenarios: preventing Git tag removal, or +enforcing a special format for commit messages. + +Push rules are [pre-receive Git hooks](https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks) you +can enable in a user-friendly interface. They are defined either: + +- Globally if you are an administrator. +- Per project, so you can have different rules applied to different + projects depending on your needs. + +## Use cases + +Every push rule could have its own use case, but let's consider some examples. + +### Commit messages with a specific reference + +Let's assume you have the following requirements for your workflow: + +- every commit should reference a Jira issue, for example: `Refactored css. Fixes JIRA-123.` +- users should not be able to remove Git tags with `git push` + +Write a regular expression that requires the mention +of a Jira issue in the commit message, like `JIRA\-\d+`. + +Now when a user tries to push a commit with a message `Bugfix`, their push is +declined. Only pushing commits with messages like `Bugfix according to JIRA-123` +is accepted. + +### Restrict branch names + +If your company has a strict policy for branch names, you may want the branches to start +with a certain name. This approach enables different +GitLab CI/CD jobs (such as `feature`, `hotfix`, `docker`, `android`) that rely on the +branch name. + +Your developers may not remember that policy, so they might push to +various branches, and CI pipelines might not work as expected. By restricting the +branch names globally in Push Rules, such mistakes are prevented. +All branch names that don't match your push rule are rejected. + +Note that the name of your default branch is always allowed, regardless of the branch naming +regular expression (regex) specified. GitLab is configured this way +because merges typically have the default branch as their target. +If you have other target branches, include them in your regex. (See [Enabling push rules](#enabling-push-rules)). + +The default branch also defaults to being a [protected branch](../protected_branches.md), +which already limits users from pushing directly. + +Some example regular expressions you can use in push rules: + +- `^JIRA-` Branches must start with `JIRA-`. +- `-JIRA$` Branches must end with `-JIRA`. +- `^[a-z0-9\\-]{4,15}$` Branches must be between `4` and `15` characters long, + accepting only lowercase letters, numbers and dashes. + +#### Default restricted branch names + +> Introduced in GitLab 12.10. + +By default, GitLab restricts certain formats of branch names for security purposes. +40-character hexadecimal names, similar to Git commit hashes, are prohibited. + +### Custom Push Rules **(PREMIUM SELF)** + +It's possible to create custom push rules rather than the push rules available in +**Admin Area > Push Rules** by using more advanced server hooks. + +See [server hooks](../../../administration/server_hooks.md) for more information. + +## Enabling push rules + +You can create push rules for all new projects to inherit, but they can be overridden +at the project level or the [group level](../../group/index.md#group-push-rules). + +To create global push rules: + +1. On the top bar, select **Menu > Admin**. +1. On the left sidebar, select **Push Rules**. + +To override global push rules in a project's settings: + +1. On the top bar, select **Menu > Projects** and find your project. +1. On the left sidebar, select **Settings > Repository**. +1. Expand **Push rules**. +1. Set the rule you want. +1. Select **Save push rules**. + +The following options are available: + +| Push rule | Description | +|---------------------------------|-------------| +| Removal of tags with `git push` | Forbid users to remove Git tags with `git push`. Tags can be deleted through the web UI. | +| Check whether the commit author is a GitLab user | Restrict commits to existing GitLab users (checked against their emails). <sup>1</sup> | +| Reject unverified users | GitLab rejects any commit that was not committed by the same user as the user who pushed it, or where the committer's email address is not [confirmed](../../../security/user_email_confirmation.md). | +| Check whether commit is signed through GPG | Reject commit when it is not signed through GPG. Read [signing commits with GPG](gpg_signed_commits/index.md). | +| Prevent pushing secret files | GitLab rejects any files that are likely to contain secrets. See the [forbidden file names](#prevent-pushing-secrets-to-the-repository). | +| Require expression in commit messages | Only commit messages that match this regular expression are allowed to be pushed. <sup>2</sup> Leave empty to allow any commit message. Uses multiline mode, which can be disabled using `(?-m)`. | +| Reject expression in commit messages | Only commit messages that do not match this regular expression are allowed to be pushed. <sup>2</sup> Leave empty to allow any commit message. Uses multiline mode, which can be disabled using `(?-m)`. | +| Restrict by branch name | Only branch names that match this regular expression are allowed to be pushed. <sup>2</sup> Leave empty to allow all branch names. | +| Restrict by commit author's email | Only commit author's email that match this regular expression are allowed to be pushed. <sup>1</sup> <sup>2</sup> Leave empty to allow any email. | +| Prohibited file names | Any committed filenames that match this regular expression and do not already exist in the repository are not allowed to be pushed. <sup>2</sup> Leave empty to allow any filenames. See [common examples](#prohibited-file-names). | +| Maximum file size | Pushes that contain added or updated files that exceed this file size (in MB) are rejected. Set to 0 to allow files of any size. Files tracked by Git LFS are exempted. | + +1. Checks both the commit author and committer. +1. GitLab uses [RE2 syntax](https://github.com/google/re2/wiki/Syntax) for regular expressions in push rules, and you can test them at the [regex101 regex tester](https://regex101.com/). + +### Caveat to "Reject unsigned commits" push rule + +This push rule ignores commits that are authenticated and created by GitLab +(either through the UI or API). When the **Reject unsigned commits** push rule is +enabled, unsigned commits may still show up in the commit history if a commit was +created **in** GitLab itself. As expected, commits created outside GitLab and +pushed to the repository are rejected. For more information about how GitLab +plans to fix this issue, read [issue #19185](https://gitlab.com/gitlab-org/gitlab/-/issues/19185). + +#### "Reject unsigned commits" push rule disables Web IDE + +In 13.10, if a project has the "Reject unsigned commits" push rule, the user is not allowed to +commit through GitLab Web IDE. + +To allow committing through the Web IDE on a project with this push rule, a GitLab administrator +must disable the feature flag `reject_unsigned_commits_by_gitlab`. This can be done through a +[rails console](../../../administration/operations/rails_console.md) and running: + +```ruby +Feature.disable(:reject_unsigned_commits_by_gitlab) +``` + +## Prevent pushing secrets to the repository + +> Moved to GitLab Premium in 13.9. + +Secrets, such as credential files and SSH private keys, should never be committed to a version control +system. In GitLab, you can use a predefined list of files to block those files from a +repository. Any merge request containing a file matching the list is blocked from being merged. +Files already committed to the repository are not restricted by this push rule. + +Files blocked by this rule are listed below. For a complete list of criteria, see +[`files_denylist.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/lib/gitlab/checks/files_denylist.yml). + +- AWS CLI credential blobs: + + - `.aws/credentials` + - `aws/credentials` + - `homefolder/aws/credentials` + +- Private RSA SSH keys: + + - `/ssh/id_rsa` + - `/.ssh/personal_rsa` + - `/config/server_rsa` + - `id_rsa` + - `.id_rsa` + +- Private DSA SSH keys: + + - `/ssh/id_dsa` + - `/.ssh/personal_dsa` + - `/config/server_dsa` + - `id_dsa` + - `.id_dsa` + +- Private ED25519 SSH keys: + + - `/ssh/id_ed25519` + - `/.ssh/personal_ed25519` + - `/config/server_ed25519` + - `id_ed25519` + - `.id_ed25519` + +- Private ECDSA SSH keys: + + - `/ssh/id_ecdsa` + - `/.ssh/personal_ecdsa` + - `/config/server_ecdsa` + - `id_ecdsa` + - `.id_ecdsa` + +- Private ECDSA_SK SSH keys (GitLab 14.8 and later): + + - `/ssh/id_ecdsa_sk` + - `/.ssh/personal_ecdsa_sk` + - `/config/server_ecdsa_sk` + - `id_ecdsa_sk` + - `.id_ecdsa_sk` + +- Private ED25519_SK SSH keys (GitLab 14.8 and later): + + - `/ssh/id_ed25519_sk` + - `/.ssh/personal_ed25519_sk` + - `/config/server_ed25519_sk` + - `id_ed25519_sk` + - `.id_ed25519_sk` + +- Any files ending with these suffixes: + + - `*.pem` + - `*.key` + - `*.history` + - `*_history` + +### Prevent pushing secrets to all projects + +To set a global push rule to prevent pushing secrets to all projects: + +1. On the top bar, select **Menu > Admin**. +1. On the left sidebar, select **Push Rules**. +1. Expand **Push rules**. +1. Select **Prevent pushing secret files**. +1. Select **Save push rules**. + +### Prevent pushing secrets to a project + +The push rule of a project overrides the global push rule. + +To prevent pushing secrets to a project: + +1. On the top bar, select **Menu > Projects** and find your project. +1. On the left sidebar, select **Settings > Repository**. +1. Expand **Push rules**. +1. Select **Prevent pushing secret files**. +1. Select **Save push rules**. + +## Prohibited file names + +> Moved to GitLab Premium in 13.9. + +Each filename contained in a Git push is compared to the regular expression in this field. Filenames in Git consist of both the file's name and any directory that may precede it. A singular regular expression can contain multiple independent matches used as exclusions. File names can be broadly matched to any location in the repository, or restricted to specific locations. Filenames can also be partial matches used to exclude file types by extension. + +The following examples make use of regex string boundary characters which match the beginning of a string (`^`), and the end (`$`). They also include instances where either the directory path or the filename can include `.` or `/`. Both of these special regex characters have to be escaped with a backslash `\\` to be used as normal characters in a match condition. + +Example: prevent pushing any `.exe` files to any location in the repository. This uses a partial match, which matches any filename that contains `.exe` at the end: + +```plaintext +\.exe$ +``` + +Example: prevent a specific configuration file in the repository root from being pushed: + +```plaintext +^config\.yml$ +``` + +Example: prevent a specific configuration file in a known directory from being pushed: + +```plaintext +^directory-name\/config\.yml$ +``` + +Example: prevent the specific file named `install.exe` from being pushed to any +location in the repository. The parenthesized expression `(^|\/)` matches either +a file following a directory separator or a file in the root directory of the repository: + +```plaintext +(^|\/)install\.exe$ +``` + +Example: combining all of the above in a single expression. The preceding expressions rely +on the end-of-string character `$`. We can move that part of each expression to the +end of the grouped collection of match conditions where it is appended to all matches: + +```plaintext +(\.exe|^config\.yml|^directory-name\/config\.yml|(^|\/)install\.exe)$ +``` + +<!-- ## Troubleshooting + +Include any troubleshooting steps that you can foresee. If you know beforehand what issues +one might have when setting this up, or when something is changed, or on upgrading, it's +important to describe those, too. Think of things that may go wrong and include them here. +This is important to minimize requests for support, and to avoid doc comments with +questions that you know someone might ask. + +Each scenario can be a third-level heading, e.g. `### Getting error message X`. +If you have none to add when creating a doc, leave this section in place +but commented out to help encourage others to add to it in the future. --> diff --git a/lib/api/repositories.rb b/lib/api/repositories.rb index fc976c23726..c3632c812f3 100644 --- a/lib/api/repositories.rb +++ b/lib/api/repositories.rb @@ -171,7 +171,6 @@ module API optional :straight, type: Boolean, desc: 'Comparison method, `true` for direct comparison between `from` and `to` (`from`..`to`), `false` to compare using merge base (`from`...`to`)', default: false end get ':id/repository/compare', urgency: :low do - ff_enabled = Feature.enabled?(:api_caching_rate_limit_repository_compare, user_project, default_enabled: :yaml) target_project = fetch_target_project(current_user, user_project, params) if target_project.blank? @@ -180,7 +179,7 @@ module API cache_key = compare_cache_key(current_user, user_project, target_project, declared_params) - cache_action_if(ff_enabled, cache_key, expires_in: 1.minute) do + cache_action(cache_key, expires_in: 1.minute) do compare = CompareService.new(user_project, params[:to]).execute(target_project, params[:from], straight: params[:straight]) if compare diff --git a/lib/gitlab/ci/templates/Jobs/Dependency-Scanning.gitlab-ci.yml b/lib/gitlab/ci/templates/Jobs/Dependency-Scanning.gitlab-ci.yml new file mode 100644 index 00000000000..c73e84ea620 --- /dev/null +++ b/lib/gitlab/ci/templates/Jobs/Dependency-Scanning.gitlab-ci.yml @@ -0,0 +1,166 @@ +# To contribute improvements to CI/CD templates, please follow the Development guide at: +# https://docs.gitlab.com/ee/development/cicd/templates.html +# This specific template is located at: +# https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Jobs/Dependency-Scanning.gitlab-ci.yml + +# Read more about this feature here: https://docs.gitlab.com/ee/user/application_security/dependency_scanning/ +# +# Configure dependency scanning with CI/CD variables (https://docs.gitlab.com/ee/ci/variables/index.html). +# List of available variables: https://docs.gitlab.com/ee/user/application_security/dependency_scanning/index.html#available-variables + +variables: + # Setting this variable will affect all Security templates + # (SAST, Dependency Scanning, ...) + SECURE_ANALYZERS_PREFIX: "registry.gitlab.com/gitlab-org/security-products/analyzers" + DS_DEFAULT_ANALYZERS: "bundler-audit, retire.js, gemnasium, gemnasium-maven, gemnasium-python" + DS_EXCLUDED_ANALYZERS: "" + DS_EXCLUDED_PATHS: "spec, test, tests, tmp" + DS_MAJOR_VERSION: 2 + +dependency_scanning: + stage: test + script: + - echo "$CI_JOB_NAME is used for configuration only, and its script should not be executed" + - exit 1 + artifacts: + reports: + dependency_scanning: gl-dependency-scanning-report.json + dependencies: [] + rules: + - when: never + +.ds-analyzer: + extends: dependency_scanning + allow_failure: true + # `rules` must be overridden explicitly by each child job + # see https://gitlab.com/gitlab-org/gitlab/-/issues/218444 + script: + - /analyzer run + +gemnasium-dependency_scanning: + extends: .ds-analyzer + image: + name: "$DS_ANALYZER_IMAGE" + variables: + # DS_ANALYZER_IMAGE is an undocumented variable used internally to allow QA to + # override the analyzer image with a custom value. This may be subject to change or + # breakage across GitLab releases. + DS_ANALYZER_IMAGE: "$SECURE_ANALYZERS_PREFIX/gemnasium:$DS_MAJOR_VERSION" + GEMNASIUM_LIBRARY_SCAN_ENABLED: "true" + rules: + - if: $DEPENDENCY_SCANNING_DISABLED + when: never + - if: $DS_EXCLUDED_ANALYZERS =~ /gemnasium([^-]|$)/ + when: never + - if: $CI_COMMIT_BRANCH && + $GITLAB_FEATURES =~ /\bdependency_scanning\b/ && + $DS_DEFAULT_ANALYZERS =~ /gemnasium([^-]|$)/ + exists: + - '{Gemfile.lock,*/Gemfile.lock,*/*/Gemfile.lock}' + - '{composer.lock,*/composer.lock,*/*/composer.lock}' + - '{gems.locked,*/gems.locked,*/*/gems.locked}' + - '{go.sum,*/go.sum,*/*/go.sum}' + - '{npm-shrinkwrap.json,*/npm-shrinkwrap.json,*/*/npm-shrinkwrap.json}' + - '{package-lock.json,*/package-lock.json,*/*/package-lock.json}' + - '{yarn.lock,*/yarn.lock,*/*/yarn.lock}' + - '{packages.lock.json,*/packages.lock.json,*/*/packages.lock.json}' + - '{conan.lock,*/conan.lock,*/*/conan.lock}' + +gemnasium-maven-dependency_scanning: + extends: .ds-analyzer + image: + name: "$DS_ANALYZER_IMAGE" + variables: + # DS_ANALYZER_IMAGE is an undocumented variable used internally to allow QA to + # override the analyzer image with a custom value. This may be subject to change or + # breakage across GitLab releases. + DS_ANALYZER_IMAGE: "$SECURE_ANALYZERS_PREFIX/gemnasium-maven:$DS_MAJOR_VERSION" + # Stop reporting Gradle as "maven". + # See https://gitlab.com/gitlab-org/gitlab/-/issues/338252 + DS_REPORT_PACKAGE_MANAGER_MAVEN_WHEN_JAVA: "false" + rules: + - if: $DEPENDENCY_SCANNING_DISABLED + when: never + - if: $DS_EXCLUDED_ANALYZERS =~ /gemnasium-maven/ + when: never + - if: $CI_COMMIT_BRANCH && + $GITLAB_FEATURES =~ /\bdependency_scanning\b/ && + $DS_DEFAULT_ANALYZERS =~ /gemnasium-maven/ + exists: + - '{build.gradle,*/build.gradle,*/*/build.gradle}' + - '{build.gradle.kts,*/build.gradle.kts,*/*/build.gradle.kts}' + - '{build.sbt,*/build.sbt,*/*/build.sbt}' + - '{pom.xml,*/pom.xml,*/*/pom.xml}' + +gemnasium-python-dependency_scanning: + extends: .ds-analyzer + image: + name: "$DS_ANALYZER_IMAGE" + variables: + # DS_ANALYZER_IMAGE is an undocumented variable used internally to allow QA to + # override the analyzer image with a custom value. This may be subject to change or + # breakage across GitLab releases. + DS_ANALYZER_IMAGE: "$SECURE_ANALYZERS_PREFIX/gemnasium-python:$DS_MAJOR_VERSION" + # Stop reporting Pipenv and Setuptools as "pip". + # See https://gitlab.com/gitlab-org/gitlab/-/issues/338252 + DS_REPORT_PACKAGE_MANAGER_PIP_WHEN_PYTHON: "false" + rules: + - if: $DEPENDENCY_SCANNING_DISABLED + when: never + - if: $DS_EXCLUDED_ANALYZERS =~ /gemnasium-python/ + when: never + - if: $CI_COMMIT_BRANCH && + $GITLAB_FEATURES =~ /\bdependency_scanning\b/ && + $DS_DEFAULT_ANALYZERS =~ /gemnasium-python/ + exists: + - '{requirements.txt,*/requirements.txt,*/*/requirements.txt}' + - '{requirements.pip,*/requirements.pip,*/*/requirements.pip}' + - '{Pipfile,*/Pipfile,*/*/Pipfile}' + - '{requires.txt,*/requires.txt,*/*/requires.txt}' + - '{setup.py,*/setup.py,*/*/setup.py}' + # Support passing of $PIP_REQUIREMENTS_FILE + # See https://docs.gitlab.com/ee/user/application_security/dependency_scanning/#configuring-specific-analyzers-used-by-dependency-scanning + - if: $CI_COMMIT_BRANCH && + $GITLAB_FEATURES =~ /\bdependency_scanning\b/ && + $DS_DEFAULT_ANALYZERS =~ /gemnasium-python/ && + $PIP_REQUIREMENTS_FILE + +bundler-audit-dependency_scanning: + extends: .ds-analyzer + image: + name: "$DS_ANALYZER_IMAGE" + variables: + # DS_ANALYZER_IMAGE is an undocumented variable used internally to allow QA to + # override the analyzer image with a custom value. This may be subject to change or + # breakage across GitLab releases. + DS_ANALYZER_IMAGE: "$SECURE_ANALYZERS_PREFIX/bundler-audit:$DS_MAJOR_VERSION" + rules: + - if: $DEPENDENCY_SCANNING_DISABLED + when: never + - if: $DS_EXCLUDED_ANALYZERS =~ /bundler-audit/ + when: never + - if: $CI_COMMIT_BRANCH && + $GITLAB_FEATURES =~ /\bdependency_scanning\b/ && + $DS_DEFAULT_ANALYZERS =~ /bundler-audit/ + exists: + - '{Gemfile.lock,*/Gemfile.lock,*/*/Gemfile.lock}' + +retire-js-dependency_scanning: + extends: .ds-analyzer + image: + name: "$DS_ANALYZER_IMAGE" + variables: + # DS_ANALYZER_IMAGE is an undocumented variable used internally to allow QA to + # override the analyzer image with a custom value. This may be subject to change or + # breakage across GitLab releases. + DS_ANALYZER_IMAGE: "$SECURE_ANALYZERS_PREFIX/retire.js:$DS_MAJOR_VERSION" + rules: + - if: $DEPENDENCY_SCANNING_DISABLED + when: never + - if: $DS_EXCLUDED_ANALYZERS =~ /retire.js/ + when: never + - if: $CI_COMMIT_BRANCH && + $GITLAB_FEATURES =~ /\bdependency_scanning\b/ && + $DS_DEFAULT_ANALYZERS =~ /retire.js/ + exists: + - '{package.json,*/package.json,*/*/package.json}' diff --git a/lib/gitlab/ci/templates/Jobs/License-Scanning.gitlab-ci.yml b/lib/gitlab/ci/templates/Jobs/License-Scanning.gitlab-ci.yml new file mode 100644 index 00000000000..fc51f5adb3c --- /dev/null +++ b/lib/gitlab/ci/templates/Jobs/License-Scanning.gitlab-ci.yml @@ -0,0 +1,38 @@ +# To contribute improvements to CI/CD templates, please follow the Development guide at: +# https://docs.gitlab.com/ee/development/cicd/templates.html +# This specific template is located at: +# https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Jobs/License-Scanning.gitlab-ci.yml + +# Read more about this feature here: https://docs.gitlab.com/ee/user/compliance/license_compliance/index.html +# +# Configure license scanning with CI/CD variables (https://docs.gitlab.com/ee/ci/variables/index.html). +# List of available variables: https://docs.gitlab.com/ee/user/compliance/license_compliance/#available-variables + +variables: + # Setting this variable will affect all Security templates + # (SAST, Dependency Scanning, ...) + SECURE_ANALYZERS_PREFIX: "registry.gitlab.com/gitlab-org/security-products/analyzers" + + LICENSE_MANAGEMENT_SETUP_CMD: '' # If needed, specify a command to setup your environment with a custom package manager. + LICENSE_MANAGEMENT_VERSION: 3 + +license_scanning: + stage: test + image: + name: "$SECURE_ANALYZERS_PREFIX/license-finder:$LICENSE_MANAGEMENT_VERSION" + entrypoint: [""] + variables: + LM_REPORT_VERSION: '2.1' + SETUP_CMD: $LICENSE_MANAGEMENT_SETUP_CMD + allow_failure: true + script: + - /run.sh analyze . + artifacts: + reports: + license_scanning: gl-license-scanning-report.json + dependencies: [] + rules: + - if: $LICENSE_MANAGEMENT_DISABLED + when: never + - if: $CI_COMMIT_BRANCH && + $GITLAB_FEATURES =~ /\blicense_scanning\b/ diff --git a/lib/gitlab/ci/templates/Security/Dependency-Scanning.gitlab-ci.yml b/lib/gitlab/ci/templates/Security/Dependency-Scanning.gitlab-ci.yml index f5bd4195f75..1785d4216e7 100644 --- a/lib/gitlab/ci/templates/Security/Dependency-Scanning.gitlab-ci.yml +++ b/lib/gitlab/ci/templates/Security/Dependency-Scanning.gitlab-ci.yml @@ -1,166 +1,5 @@ -# To contribute improvements to CI/CD templates, please follow the Development guide at: -# https://docs.gitlab.com/ee/development/cicd/templates.html -# This specific template is located at: -# https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Security/Dependency-Scanning.gitlab-ci.yml +# This template moved to Jobs/Dependency-Scanning.gitlab-ci.yml in GitLab 14.8 +# Issue: https://gitlab.com/gitlab-org/gitlab/-/issues/292977 -# Read more about this feature here: https://docs.gitlab.com/ee/user/application_security/dependency_scanning/ -# -# Configure dependency scanning with CI/CD variables (https://docs.gitlab.com/ee/ci/variables/index.html). -# List of available variables: https://docs.gitlab.com/ee/user/application_security/dependency_scanning/index.html#available-variables - -variables: - # Setting this variable will affect all Security templates - # (SAST, Dependency Scanning, ...) - SECURE_ANALYZERS_PREFIX: "registry.gitlab.com/gitlab-org/security-products/analyzers" - DS_DEFAULT_ANALYZERS: "bundler-audit, retire.js, gemnasium, gemnasium-maven, gemnasium-python" - DS_EXCLUDED_ANALYZERS: "" - DS_EXCLUDED_PATHS: "spec, test, tests, tmp" - DS_MAJOR_VERSION: 2 - -dependency_scanning: - stage: test - script: - - echo "$CI_JOB_NAME is used for configuration only, and its script should not be executed" - - exit 1 - artifacts: - reports: - dependency_scanning: gl-dependency-scanning-report.json - dependencies: [] - rules: - - when: never - -.ds-analyzer: - extends: dependency_scanning - allow_failure: true - # `rules` must be overridden explicitly by each child job - # see https://gitlab.com/gitlab-org/gitlab/-/issues/218444 - script: - - /analyzer run - -gemnasium-dependency_scanning: - extends: .ds-analyzer - image: - name: "$DS_ANALYZER_IMAGE" - variables: - # DS_ANALYZER_IMAGE is an undocumented variable used internally to allow QA to - # override the analyzer image with a custom value. This may be subject to change or - # breakage across GitLab releases. - DS_ANALYZER_IMAGE: "$SECURE_ANALYZERS_PREFIX/gemnasium:$DS_MAJOR_VERSION" - GEMNASIUM_LIBRARY_SCAN_ENABLED: "true" - rules: - - if: $DEPENDENCY_SCANNING_DISABLED - when: never - - if: $DS_EXCLUDED_ANALYZERS =~ /gemnasium([^-]|$)/ - when: never - - if: $CI_COMMIT_BRANCH && - $GITLAB_FEATURES =~ /\bdependency_scanning\b/ && - $DS_DEFAULT_ANALYZERS =~ /gemnasium([^-]|$)/ - exists: - - '{Gemfile.lock,*/Gemfile.lock,*/*/Gemfile.lock}' - - '{composer.lock,*/composer.lock,*/*/composer.lock}' - - '{gems.locked,*/gems.locked,*/*/gems.locked}' - - '{go.sum,*/go.sum,*/*/go.sum}' - - '{npm-shrinkwrap.json,*/npm-shrinkwrap.json,*/*/npm-shrinkwrap.json}' - - '{package-lock.json,*/package-lock.json,*/*/package-lock.json}' - - '{yarn.lock,*/yarn.lock,*/*/yarn.lock}' - - '{packages.lock.json,*/packages.lock.json,*/*/packages.lock.json}' - - '{conan.lock,*/conan.lock,*/*/conan.lock}' - -gemnasium-maven-dependency_scanning: - extends: .ds-analyzer - image: - name: "$DS_ANALYZER_IMAGE" - variables: - # DS_ANALYZER_IMAGE is an undocumented variable used internally to allow QA to - # override the analyzer image with a custom value. This may be subject to change or - # breakage across GitLab releases. - DS_ANALYZER_IMAGE: "$SECURE_ANALYZERS_PREFIX/gemnasium-maven:$DS_MAJOR_VERSION" - # Stop reporting Gradle as "maven". - # See https://gitlab.com/gitlab-org/gitlab/-/issues/338252 - DS_REPORT_PACKAGE_MANAGER_MAVEN_WHEN_JAVA: "false" - rules: - - if: $DEPENDENCY_SCANNING_DISABLED - when: never - - if: $DS_EXCLUDED_ANALYZERS =~ /gemnasium-maven/ - when: never - - if: $CI_COMMIT_BRANCH && - $GITLAB_FEATURES =~ /\bdependency_scanning\b/ && - $DS_DEFAULT_ANALYZERS =~ /gemnasium-maven/ - exists: - - '{build.gradle,*/build.gradle,*/*/build.gradle}' - - '{build.gradle.kts,*/build.gradle.kts,*/*/build.gradle.kts}' - - '{build.sbt,*/build.sbt,*/*/build.sbt}' - - '{pom.xml,*/pom.xml,*/*/pom.xml}' - -gemnasium-python-dependency_scanning: - extends: .ds-analyzer - image: - name: "$DS_ANALYZER_IMAGE" - variables: - # DS_ANALYZER_IMAGE is an undocumented variable used internally to allow QA to - # override the analyzer image with a custom value. This may be subject to change or - # breakage across GitLab releases. - DS_ANALYZER_IMAGE: "$SECURE_ANALYZERS_PREFIX/gemnasium-python:$DS_MAJOR_VERSION" - # Stop reporting Pipenv and Setuptools as "pip". - # See https://gitlab.com/gitlab-org/gitlab/-/issues/338252 - DS_REPORT_PACKAGE_MANAGER_PIP_WHEN_PYTHON: "false" - rules: - - if: $DEPENDENCY_SCANNING_DISABLED - when: never - - if: $DS_EXCLUDED_ANALYZERS =~ /gemnasium-python/ - when: never - - if: $CI_COMMIT_BRANCH && - $GITLAB_FEATURES =~ /\bdependency_scanning\b/ && - $DS_DEFAULT_ANALYZERS =~ /gemnasium-python/ - exists: - - '{requirements.txt,*/requirements.txt,*/*/requirements.txt}' - - '{requirements.pip,*/requirements.pip,*/*/requirements.pip}' - - '{Pipfile,*/Pipfile,*/*/Pipfile}' - - '{requires.txt,*/requires.txt,*/*/requires.txt}' - - '{setup.py,*/setup.py,*/*/setup.py}' - # Support passing of $PIP_REQUIREMENTS_FILE - # See https://docs.gitlab.com/ee/user/application_security/dependency_scanning/#configuring-specific-analyzers-used-by-dependency-scanning - - if: $CI_COMMIT_BRANCH && - $GITLAB_FEATURES =~ /\bdependency_scanning\b/ && - $DS_DEFAULT_ANALYZERS =~ /gemnasium-python/ && - $PIP_REQUIREMENTS_FILE - -bundler-audit-dependency_scanning: - extends: .ds-analyzer - image: - name: "$DS_ANALYZER_IMAGE" - variables: - # DS_ANALYZER_IMAGE is an undocumented variable used internally to allow QA to - # override the analyzer image with a custom value. This may be subject to change or - # breakage across GitLab releases. - DS_ANALYZER_IMAGE: "$SECURE_ANALYZERS_PREFIX/bundler-audit:$DS_MAJOR_VERSION" - rules: - - if: $DEPENDENCY_SCANNING_DISABLED - when: never - - if: $DS_EXCLUDED_ANALYZERS =~ /bundler-audit/ - when: never - - if: $CI_COMMIT_BRANCH && - $GITLAB_FEATURES =~ /\bdependency_scanning\b/ && - $DS_DEFAULT_ANALYZERS =~ /bundler-audit/ - exists: - - '{Gemfile.lock,*/Gemfile.lock,*/*/Gemfile.lock}' - -retire-js-dependency_scanning: - extends: .ds-analyzer - image: - name: "$DS_ANALYZER_IMAGE" - variables: - # DS_ANALYZER_IMAGE is an undocumented variable used internally to allow QA to - # override the analyzer image with a custom value. This may be subject to change or - # breakage across GitLab releases. - DS_ANALYZER_IMAGE: "$SECURE_ANALYZERS_PREFIX/retire.js:$DS_MAJOR_VERSION" - rules: - - if: $DEPENDENCY_SCANNING_DISABLED - when: never - - if: $DS_EXCLUDED_ANALYZERS =~ /retire.js/ - when: never - - if: $CI_COMMIT_BRANCH && - $GITLAB_FEATURES =~ /\bdependency_scanning\b/ && - $DS_DEFAULT_ANALYZERS =~ /retire.js/ - exists: - - '{package.json,*/package.json,*/*/package.json}' +include: + template: Jobs/Dependency-Scanning.gitlab-ci.yml diff --git a/lib/gitlab/ci/templates/Security/License-Scanning.gitlab-ci.yml b/lib/gitlab/ci/templates/Security/License-Scanning.gitlab-ci.yml index 1249b8d6fdc..a99fe4a6dcf 100644 --- a/lib/gitlab/ci/templates/Security/License-Scanning.gitlab-ci.yml +++ b/lib/gitlab/ci/templates/Security/License-Scanning.gitlab-ci.yml @@ -1,38 +1,5 @@ -# To contribute improvements to CI/CD templates, please follow the Development guide at: -# https://docs.gitlab.com/ee/development/cicd/templates.html -# This specific template is located at: -# https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Security/License-Scanning.gitlab-ci.yml +# This template moved to Jobs/License-Scanning.gitlab-ci.yml in GitLab 14.8 +# Issue: https://gitlab.com/gitlab-org/gitlab/-/issues/292977 -# Read more about this feature here: https://docs.gitlab.com/ee/user/compliance/license_compliance/index.html -# -# Configure license scanning with CI/CD variables (https://docs.gitlab.com/ee/ci/variables/index.html). -# List of available variables: https://docs.gitlab.com/ee/user/compliance/license_compliance/#available-variables - -variables: - # Setting this variable will affect all Security templates - # (SAST, Dependency Scanning, ...) - SECURE_ANALYZERS_PREFIX: "registry.gitlab.com/gitlab-org/security-products/analyzers" - - LICENSE_MANAGEMENT_SETUP_CMD: '' # If needed, specify a command to setup your environment with a custom package manager. - LICENSE_MANAGEMENT_VERSION: 3 - -license_scanning: - stage: test - image: - name: "$SECURE_ANALYZERS_PREFIX/license-finder:$LICENSE_MANAGEMENT_VERSION" - entrypoint: [""] - variables: - LM_REPORT_VERSION: '2.1' - SETUP_CMD: $LICENSE_MANAGEMENT_SETUP_CMD - allow_failure: true - script: - - /run.sh analyze . - artifacts: - reports: - license_scanning: gl-license-scanning-report.json - dependencies: [] - rules: - - if: $LICENSE_MANAGEMENT_DISABLED - when: never - - if: $CI_COMMIT_BRANCH && - $GITLAB_FEATURES =~ /\blicense_scanning\b/ +include: + template: Jobs/License-Scanning.gitlab-ci.yml diff --git a/lib/gitlab/usage_data_counters/known_events/ci_templates.yml b/lib/gitlab/usage_data_counters/known_events/ci_templates.yml index 5735cb46318..a39fa7aca4f 100644 --- a/lib/gitlab/usage_data_counters/known_events/ci_templates.yml +++ b/lib/gitlab/usage_data_counters/known_events/ci_templates.yml @@ -83,10 +83,6 @@ category: ci_templates redis_slot: ci_templates aggregation: weekly -- name: p_ci_templates_security_sast_iac_latest - category: ci_templates - redis_slot: ci_templates - aggregation: weekly - name: p_ci_templates_security_dast_runner_validation category: ci_templates redis_slot: ci_templates @@ -123,10 +119,6 @@ category: ci_templates redis_slot: ci_templates aggregation: weekly -- name: p_ci_templates_security_dast_api_latest - category: ci_templates - redis_slot: ci_templates - aggregation: weekly - name: p_ci_templates_security_container_scanning category: ci_templates redis_slot: ci_templates @@ -139,6 +131,10 @@ category: ci_templates redis_slot: ci_templates aggregation: weekly +- name: p_ci_templates_security_dast_api_latest + category: ci_templates + redis_slot: ci_templates + aggregation: weekly - name: p_ci_templates_security_api_fuzzing category: ci_templates redis_slot: ci_templates @@ -147,6 +143,10 @@ category: ci_templates redis_slot: ci_templates aggregation: weekly +- name: p_ci_templates_security_sast_iac_latest + category: ci_templates + redis_slot: ci_templates + aggregation: weekly - name: p_ci_templates_security_cluster_image_scanning category: ci_templates redis_slot: ci_templates @@ -203,6 +203,10 @@ category: ci_templates redis_slot: ci_templates aggregation: weekly +- name: p_ci_templates_kaniko + category: ci_templates + redis_slot: ci_templates + aggregation: weekly - name: p_ci_templates_managed_cluster_applications category: ci_templates redis_slot: ci_templates @@ -283,11 +287,11 @@ category: ci_templates redis_slot: ci_templates aggregation: weekly -- name: p_ci_templates_jobs_sast_iac_latest +- name: p_ci_templates_jobs_secret_detection category: ci_templates redis_slot: ci_templates aggregation: weekly -- name: p_ci_templates_jobs_secret_detection +- name: p_ci_templates_jobs_license_scanning category: ci_templates redis_slot: ci_templates aggregation: weekly @@ -323,6 +327,10 @@ category: ci_templates redis_slot: ci_templates aggregation: weekly +- name: p_ci_templates_jobs_dependency_scanning + category: ci_templates + redis_slot: ci_templates + aggregation: weekly - name: p_ci_templates_jobs_deploy_latest category: ci_templates redis_slot: ci_templates @@ -339,6 +347,10 @@ category: ci_templates redis_slot: ci_templates aggregation: weekly +- name: p_ci_templates_jobs_sast_iac_latest + category: ci_templates + redis_slot: ci_templates + aggregation: weekly - name: p_ci_templates_terraform_latest category: ci_templates redis_slot: ci_templates @@ -467,11 +479,11 @@ category: ci_templates redis_slot: ci_templates aggregation: weekly -- name: p_ci_templates_implicit_jobs_sast_iac_latest +- name: p_ci_templates_implicit_jobs_secret_detection category: ci_templates redis_slot: ci_templates aggregation: weekly -- name: p_ci_templates_implicit_jobs_secret_detection +- name: p_ci_templates_implicit_jobs_license_scanning category: ci_templates redis_slot: ci_templates aggregation: weekly @@ -507,6 +519,10 @@ category: ci_templates redis_slot: ci_templates aggregation: weekly +- name: p_ci_templates_implicit_jobs_dependency_scanning + category: ci_templates + redis_slot: ci_templates + aggregation: weekly - name: p_ci_templates_implicit_jobs_deploy_latest category: ci_templates redis_slot: ci_templates @@ -523,11 +539,11 @@ category: ci_templates redis_slot: ci_templates aggregation: weekly -- name: p_ci_templates_implicit_security_sast +- name: p_ci_templates_implicit_jobs_sast_iac_latest category: ci_templates redis_slot: ci_templates aggregation: weekly -- name: p_ci_templates_implicit_security_sast_iac_latest +- name: p_ci_templates_implicit_security_sast category: ci_templates redis_slot: ci_templates aggregation: weekly @@ -567,10 +583,6 @@ category: ci_templates redis_slot: ci_templates aggregation: weekly -- name: p_ci_templates_implicit_security_dast_api_latest - category: ci_templates - redis_slot: ci_templates - aggregation: weekly - name: p_ci_templates_implicit_security_container_scanning category: ci_templates redis_slot: ci_templates @@ -583,6 +595,10 @@ category: ci_templates redis_slot: ci_templates aggregation: weekly +- name: p_ci_templates_implicit_security_dast_api_latest + category: ci_templates + redis_slot: ci_templates + aggregation: weekly - name: p_ci_templates_implicit_security_api_fuzzing category: ci_templates redis_slot: ci_templates @@ -591,11 +607,11 @@ category: ci_templates redis_slot: ci_templates aggregation: weekly -- name: p_ci_templates_implicit_security_cluster_image_scanning +- name: p_ci_templates_implicit_security_sast_iac_latest category: ci_templates redis_slot: ci_templates aggregation: weekly -- name: p_ci_templates_kaniko +- name: p_ci_templates_implicit_security_cluster_image_scanning category: ci_templates redis_slot: ci_templates aggregation: weekly diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 8769f0fcede..5369190c1bb 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -11948,6 +11948,12 @@ msgstr "" msgid "Deploy to..." msgstr "" +msgid "DeployBoards|To see deployment progress for your environments, make sure you are deploying to %{codeStart}$KUBE_NAMESPACE%{codeEnd} and annotating with %{codeStart}app.gitlab.com/app=$CI_PROJECT_PATH_SLUG%{codeEnd} and %{codeStart}app.gitlab.com/env=$CI_ENVIRONMENT_SLUG%{codeEnd}." +msgstr "" + +msgid "DeployBoard|Kubernetes Pods" +msgstr "" + msgid "DeployFreeze|Add a freeze period to prevent unintended releases during a period of time for a given environment. You must update the deployment jobs in %{filename} according to the deploy freezes added here. %{freeze_period_link_start}Learn more.%{freeze_period_link_end}" msgstr "" diff --git a/spec/features/boards/new_issue_spec.rb b/spec/features/boards/new_issue_spec.rb index f88d31bda88..5f4517d47ee 100644 --- a/spec/features/boards/new_issue_spec.rb +++ b/spec/features/boards/new_issue_spec.rb @@ -3,12 +3,13 @@ require 'spec_helper' RSpec.describe 'Issue Boards new issue', :js do - let_it_be(:project) { create(:project, :public) } - let_it_be(:board) { create(:board, project: project) } - let_it_be(:backlog_list) { create(:backlog_list, board: board) } - let_it_be(:label) { create(:label, project: project, name: 'Label 1') } - let_it_be(:list) { create(:list, board: board, label: label, position: 0) } - let_it_be(:user) { create(:user) } + let_it_be(:project) { create(:project, :public) } + let_it_be(:board) { create(:board, project: project) } + let_it_be(:backlog_list) { create(:backlog_list, board: board) } + let_it_be(:label) { create(:label, project: project, name: 'Label 1') } + let_it_be(:list) { create(:list, board: board, label: label, position: 0) } + let_it_be(:user) { create(:user) } + let_it_be(:existing_issue) { create(:issue, project: project, title: 'other issue', relative_position: 50) } let(:board_list_header) { first('[data-testid="board-list-header"]') } let(:project_select_dropdown) { find('[data-testid="project-select-dropdown"]') } @@ -56,7 +57,7 @@ RSpec.describe 'Issue Boards new issue', :js do end end - it 'creates new issue and opens sidebar' do + it 'creates new issue, places it on top of the list, and opens sidebar' do page.within(first('.board')) do click_button 'New issue' end @@ -69,12 +70,14 @@ RSpec.describe 'Issue Boards new issue', :js do wait_for_requests page.within(first('.board [data-testid="issue-count-badge"]')) do - expect(page).to have_content('1') + expect(page).to have_content('2') end page.within(first('.board-card')) do issue = project.issues.find_by_title('bug') + expect(issue.relative_position).to be < existing_issue.relative_position + expect(page).to have_content(issue.to_reference) expect(page).to have_link(issue.title, href: /#{issue_path(issue)}/) end diff --git a/spec/features/issues/user_interacts_with_awards_spec.rb b/spec/features/issues/user_interacts_with_awards_spec.rb index 94f6c1e1a8f..892b57bac5c 100644 --- a/spec/features/issues/user_interacts_with_awards_spec.rb +++ b/spec/features/issues/user_interacts_with_awards_spec.rb @@ -49,7 +49,7 @@ RSpec.describe 'User interacts with awards' do end end - it 'toggles a custom award emoji', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/351878' do + it 'toggles a custom award emoji' do page.within('.awards') do page.find('.add-reaction-button').click end @@ -65,9 +65,10 @@ RSpec.describe 'User interacts with awards' do expect(page.find('[data-testid="award-button"].selected .js-counter')).to have_content('1') expect(page).to have_css('[data-testid="award-button"].selected[title="You reacted with :8ball:"]') + wait_for_requests + expect do page.find('[data-testid="award-button"].selected').click - wait_for_requests end.to change { page.all('[data-testid="award-button"]').size }.from(3).to(2) end end diff --git a/spec/frontend/boards/components/board_new_issue_spec.js b/spec/frontend/boards/components/board_new_issue_spec.js index 08ef119aad8..8b0100d069a 100644 --- a/spec/frontend/boards/components/board_new_issue_spec.js +++ b/spec/frontend/boards/components/board_new_issue_spec.js @@ -6,7 +6,7 @@ import BoardNewItem from '~/boards/components/board_new_item.vue'; import ProjectSelect from '~/boards/components/project_select.vue'; import eventHub from '~/boards/eventhub'; -import { mockList, mockGroupProjects } from '../mock_data'; +import { mockList, mockGroupProjects, mockIssue, mockIssue2 } from '../mock_data'; Vue.use(Vuex); @@ -16,7 +16,7 @@ const mockActions = { addListNewIssue: addListNewIssuesSpy }; const createComponent = ({ state = { selectedProject: mockGroupProjects[0], fullPath: mockGroupProjects[0].fullPath }, actions = mockActions, - getters = { isGroupBoard: () => true, isProjectBoard: () => false }, + getters = { isGroupBoard: () => true, getBoardItemsByList: () => () => [] }, } = {}) => shallowMount(BoardNewIssue, { store: new Vuex.Store({ @@ -75,10 +75,39 @@ describe('Issue boards new issue form', () => { assigneeIds: [], milestoneId: undefined, projectPath: mockGroupProjects[0].fullPath, + moveAfterId: undefined, }, }); }); + describe('when list has an existing issues', () => { + beforeEach(() => { + wrapper = createComponent({ + getters: { + isGroupBoard: () => true, + getBoardItemsByList: () => () => [mockIssue, mockIssue2], + }, + }); + }); + + it('it uses the first issue ID as moveAfterId', async () => { + findBoardNewItem().vm.$emit('form-submit', { title: 'Foo' }); + + await nextTick(); + expect(addListNewIssuesSpy).toHaveBeenCalledWith(expect.any(Object), { + list: mockList, + issueInput: { + title: 'Foo', + labelIds: [], + assigneeIds: [], + milestoneId: undefined, + projectPath: mockGroupProjects[0].fullPath, + moveAfterId: mockIssue.id, + }, + }); + }); + }); + it('emits event `toggle-issue-form` with current list Id suffix on eventHub when `board-new-item` emits form-cancel event', async () => { jest.spyOn(eventHub, '$emit').mockImplementation(); findBoardNewItem().vm.$emit('form-cancel'); @@ -99,7 +128,7 @@ describe('Issue boards new issue form', () => { describe('when in project issue board', () => { beforeEach(() => { wrapper = createComponent({ - getters: { isGroupBoard: () => false, isProjectBoard: () => true }, + getters: { isGroupBoard: () => false }, }); }); diff --git a/spec/frontend/environments/canary_ingress_spec.js b/spec/frontend/environments/canary_ingress_spec.js index 6c7a786e652..d58f9f9b8a2 100644 --- a/spec/frontend/environments/canary_ingress_spec.js +++ b/spec/frontend/environments/canary_ingress_spec.js @@ -3,6 +3,7 @@ import { mount } from '@vue/test-utils'; import { createMockDirective, getBinding } from 'helpers/vue_mock_directive'; import CanaryIngress from '~/environments/components/canary_ingress.vue'; import { CANARY_UPDATE_MODAL } from '~/environments/constants'; +import { rolloutStatus } from './graphql/mock_data'; describe('/environments/components/canary_ingress.vue', () => { let wrapper; @@ -13,16 +14,18 @@ describe('/environments/components/canary_ingress.vue', () => { .at(x / 5) .vm.$emit('click'); - const createComponent = () => { + const createComponent = (props = {}, options = {}) => { wrapper = mount(CanaryIngress, { propsData: { canaryIngress: { canary_weight: 60, }, + ...props, }, directives: { GlModal: createMockDirective(), }, + ...options, }); }; @@ -94,9 +97,25 @@ describe('/environments/components/canary_ingress.vue', () => { }); it('is set to open the change modal', () => { - const options = canaryWeightDropdown.findAll(GlDropdownItem); - expect(options).toHaveLength(21); - options.wrappers.forEach((w, i) => expect(w.text()).toBe((i * 5).toString())); + canaryWeightDropdown + .findAll(GlDropdownItem) + .wrappers.forEach((w) => + expect(getBinding(w.element, 'gl-modal')).toMatchObject({ value: CANARY_UPDATE_MODAL }), + ); + }); + }); + + describe('graphql', () => { + beforeEach(() => { + createComponent({ + graphql: true, + canaryIngress: rolloutStatus.canaryIngress, + }); + }); + + it('shows the correct weight', () => { + const canaryWeightDropdown = wrapper.find('[data-testid="canary-weight"]'); + expect(canaryWeightDropdown.props('text')).toBe('50'); }); }); }); diff --git a/spec/frontend/environments/deploy_board_component_spec.js b/spec/frontend/environments/deploy_board_component_spec.js index 2033cf00cff..f0fb4d1027c 100644 --- a/spec/frontend/environments/deploy_board_component_spec.js +++ b/spec/frontend/environments/deploy_board_component_spec.js @@ -4,6 +4,7 @@ import Vue, { nextTick } from 'vue'; import CanaryIngress from '~/environments/components/canary_ingress.vue'; import DeployBoard from '~/environments/components/deploy_board.vue'; import { deployBoardMockData, environment } from './mock_data'; +import { rolloutStatus } from './graphql/mock_data'; const logsPath = `gitlab-org/gitlab-test/-/logs?environment_name=${environment.name}`; @@ -28,7 +29,7 @@ describe('Deploy Board', () => { }); it('should render percentage with completion value provided', () => { - expect(wrapper.vm.$refs.percentage.innerText).toEqual(`${deployBoardMockData.completion}%`); + expect(wrapper.find({ ref: 'percentage' }).text()).toBe(`${deployBoardMockData.completion}%`); }); it('should render total instance count', () => { @@ -57,20 +58,74 @@ describe('Deploy Board', () => { it('sets up a tooltip for the legend', () => { const iconSpan = wrapper.find('[data-testid="legend-tooltip-target"]'); - const tooltip = wrapper.find(GlTooltip); - const icon = iconSpan.find(GlIcon); + const tooltip = wrapper.findComponent(GlTooltip); + const icon = iconSpan.findComponent(GlIcon); expect(tooltip.props('target')()).toBe(iconSpan.element); expect(icon.props('name')).toBe('question'); }); it('renders the canary weight selector', () => { - const canary = wrapper.find(CanaryIngress); + const canary = wrapper.findComponent(CanaryIngress); expect(canary.exists()).toBe(true); expect(canary.props('canaryIngress')).toEqual({ canary_weight: 50 }); }); }); + describe('with new valid data', () => { + beforeEach(async () => { + wrapper = createComponent({ + graphql: true, + deployBoardData: rolloutStatus, + }); + await nextTick(); + }); + + it('should render percentage with completion value provided', () => { + expect(wrapper.find({ ref: 'percentage' }).text()).toBe(`${rolloutStatus.completion}%`); + }); + + it('should render total instance count', () => { + const renderedTotal = wrapper.find('.deploy-board-instances-text'); + const actualTotal = rolloutStatus.instances.length; + const output = `${actualTotal > 1 ? 'Instances' : 'Instance'} (${actualTotal})`; + + expect(renderedTotal.text()).toEqual(output); + }); + + it('should render all instances', () => { + const instances = wrapper.findAll('.deploy-board-instances-container a'); + + expect(instances).toHaveLength(rolloutStatus.instances.length); + expect( + instances.at(1).classes(`deployment-instance-${rolloutStatus.instances[2].status}`), + ).toBe(true); + }); + + it('should render an abort and a rollback button with the provided url', () => { + const buttons = wrapper.findAll('.deploy-board-actions a'); + + expect(buttons.at(0).attributes('href')).toEqual(rolloutStatus.rollbackUrl); + expect(buttons.at(1).attributes('href')).toEqual(rolloutStatus.abortUrl); + }); + + it('sets up a tooltip for the legend', () => { + const iconSpan = wrapper.find('[data-testid="legend-tooltip-target"]'); + const tooltip = wrapper.findComponent(GlTooltip); + const icon = iconSpan.findComponent(GlIcon); + + expect(tooltip.props('target')()).toBe(iconSpan.element); + expect(icon.props('name')).toBe('question'); + }); + + it('renders the canary weight selector', () => { + const canary = wrapper.findComponent(CanaryIngress); + expect(canary.exists()).toBe(true); + expect(canary.props('canaryIngress')).toEqual({ canaryWeight: 50 }); + expect(canary.props('graphql')).toBe(true); + }); + }); + describe('with empty state', () => { beforeEach((done) => { wrapper = createComponent({ @@ -102,7 +157,7 @@ describe('Deploy Board', () => { }); it('should render loading spinner', () => { - expect(wrapper.find(GlLoadingIcon).exists()).toBe(true); + expect(wrapper.findComponent(GlLoadingIcon).exists()).toBe(true); }); }); diff --git a/spec/frontend/environments/deploy_board_wrapper_spec.js b/spec/frontend/environments/deploy_board_wrapper_spec.js new file mode 100644 index 00000000000..c8e6df4d324 --- /dev/null +++ b/spec/frontend/environments/deploy_board_wrapper_spec.js @@ -0,0 +1,124 @@ +import Vue from 'vue'; +import VueApollo from 'vue-apollo'; +import { GlCollapse, GlIcon } from '@gitlab/ui'; +import { mountExtended } from 'helpers/vue_test_utils_helper'; +import { stubTransition } from 'helpers/stub_transition'; +import createMockApollo from 'helpers/mock_apollo_helper'; +import { __, s__ } from '~/locale'; +import DeployBoardWrapper from '~/environments/components/deploy_board_wrapper.vue'; +import DeployBoard from '~/environments/components/deploy_board.vue'; +import setEnvironmentToChangeCanaryMutation from '~/environments/graphql/mutations/set_environment_to_change_canary.mutation.graphql'; +import { resolvedEnvironment, rolloutStatus } from './graphql/mock_data'; + +Vue.use(VueApollo); + +describe('~/environments/components/deploy_board_wrapper.vue', () => { + let wrapper; + let mockApollo; + + const findDeployBoard = () => wrapper.findComponent(DeployBoard); + + const createWrapper = ({ propsData = {} } = {}) => { + mockApollo = createMockApollo(); + return mountExtended(DeployBoardWrapper, { + propsData: { environment: resolvedEnvironment, rolloutStatus, ...propsData }, + provide: { helpPagePath: '/help' }, + stubs: { transition: stubTransition() }, + apolloProvider: mockApollo, + }); + }; + + const expandCollapsedSection = async () => { + const button = wrapper.findByRole('button', { name: __('Expand') }); + await button.trigger('click'); + + return button; + }; + + afterEach(() => { + wrapper?.destroy(); + }); + + it('is labeled Kubernetes Pods', () => { + wrapper = createWrapper(); + + expect(wrapper.findByText(s__('DeployBoard|Kubernetes Pods')).exists()).toBe(true); + }); + + describe('collapse', () => { + let icon; + let collapse; + + beforeEach(() => { + wrapper = createWrapper(); + collapse = wrapper.findComponent(GlCollapse); + icon = wrapper.findComponent(GlIcon); + }); + + it('is collapsed by default', () => { + expect(collapse.attributes('visible')).toBeUndefined(); + expect(icon.props('name')).toBe('angle-right'); + }); + + it('opens on click', async () => { + const button = await expandCollapsedSection(); + + expect(button.attributes('aria-label')).toBe(__('Collapse')); + expect(collapse.attributes('visible')).toBe('visible'); + expect(icon.props('name')).toBe('angle-down'); + + const deployBoard = findDeployBoard(); + expect(deployBoard.exists()).toBe(true); + }); + }); + + describe('deploy board', () => { + it('passes the rollout status on and sets graphql to true', async () => { + wrapper = createWrapper(); + await expandCollapsedSection(); + + const deployBoard = findDeployBoard(); + expect(deployBoard.props('deployBoardData')).toEqual(rolloutStatus); + expect(deployBoard.props('graphql')).toBe(true); + }); + + it('sets the update to the canary via graphql', () => { + wrapper = createWrapper(); + jest.spyOn(mockApollo.defaultClient, 'mutate'); + const deployBoard = findDeployBoard(); + deployBoard.vm.$emit('changeCanaryWeight', 15); + expect(mockApollo.defaultClient.mutate).toHaveBeenCalledWith({ + mutation: setEnvironmentToChangeCanaryMutation, + variables: { environment: resolvedEnvironment, weight: 15 }, + }); + }); + + describe('is loading', () => { + it('should set the loading prop', async () => { + wrapper = createWrapper({ + propsData: { rolloutStatus: { ...rolloutStatus, status: 'loading' } }, + }); + + await expandCollapsedSection(); + + const deployBoard = findDeployBoard(); + + expect(deployBoard.props('isLoading')).toBe(true); + }); + }); + + describe('is empty', () => { + it('should set the empty prop', async () => { + wrapper = createWrapper({ + propsData: { rolloutStatus: { ...rolloutStatus, status: 'not_found' } }, + }); + + await expandCollapsedSection(); + + const deployBoard = findDeployBoard(); + + expect(deployBoard.props('isEmpty')).toBe(true); + }); + }); + }); +}); diff --git a/spec/frontend/environments/graphql/mock_data.js b/spec/frontend/environments/graphql/mock_data.js index fce30973547..1b7b35702de 100644 --- a/spec/frontend/environments/graphql/mock_data.js +++ b/spec/frontend/environments/graphql/mock_data.js @@ -1,3 +1,69 @@ +export const rolloutStatus = { + instances: [ + { + status: 'succeeded', + tooltip: 'tanuki-2334 Finished', + podName: 'production-tanuki-1', + stable: false, + }, + { + status: 'succeeded', + tooltip: 'tanuki-2335 Finished', + podName: 'production-tanuki-1', + stable: false, + }, + { + status: 'succeeded', + tooltip: 'tanuki-2336 Finished', + podName: 'production-tanuki-1', + stable: false, + }, + { + status: 'succeeded', + tooltip: 'tanuki-2337 Finished', + podName: 'production-tanuki-1', + stable: false, + }, + { + status: 'succeeded', + tooltip: 'tanuki-2338 Finished', + podName: 'production-tanuki-1', + stable: false, + }, + { + status: 'succeeded', + tooltip: 'tanuki-2339 Finished', + podName: 'production-tanuki-1', + stable: false, + }, + { status: 'succeeded', tooltip: 'tanuki-2340 Finished', podName: 'production-tanuki-1' }, + { status: 'succeeded', tooltip: 'tanuki-2334 Finished', podName: 'production-tanuki-1' }, + { status: 'succeeded', tooltip: 'tanuki-2335 Finished', podName: 'production-tanuki-1' }, + { status: 'succeeded', tooltip: 'tanuki-2336 Finished', podName: 'production-tanuki-1' }, + { status: 'succeeded', tooltip: 'tanuki-2337 Finished', podName: 'production-tanuki-1' }, + { status: 'succeeded', tooltip: 'tanuki-2338 Finished', podName: 'production-tanuki-1' }, + { status: 'succeeded', tooltip: 'tanuki-2339 Finished', podName: 'production-tanuki-1' }, + { status: 'succeeded', tooltip: 'tanuki-2340 Finished', podName: 'production-tanuki-1' }, + { status: 'running', tooltip: 'tanuki-2341 Deploying', podName: 'production-tanuki-1' }, + { status: 'running', tooltip: 'tanuki-2342 Deploying', podName: 'production-tanuki-1' }, + { status: 'running', tooltip: 'tanuki-2343 Deploying', podName: 'production-tanuki-1' }, + { status: 'failed', tooltip: 'tanuki-2344 Failed', podName: 'production-tanuki-1' }, + { status: 'unknown', tooltip: 'tanuki-2345 Ready', podName: 'production-tanuki-1' }, + { status: 'unknown', tooltip: 'tanuki-2346 Ready', podName: 'production-tanuki-1' }, + { status: 'pending', tooltip: 'tanuki-2348 Preparing', podName: 'production-tanuki-1' }, + { status: 'pending', tooltip: 'tanuki-2349 Preparing', podName: 'production-tanuki-1' }, + { status: 'pending', tooltip: 'tanuki-2350 Preparing', podName: 'production-tanuki-1' }, + { status: 'pending', tooltip: 'tanuki-2353 Preparing', podName: 'production-tanuki-1' }, + { status: 'pending', tooltip: 'tanuki-2354 waiting', podName: 'production-tanuki-1' }, + { status: 'pending', tooltip: 'tanuki-2355 waiting', podName: 'production-tanuki-1' }, + { status: 'pending', tooltip: 'tanuki-2356 waiting', podName: 'production-tanuki-1' }, + ], + abortUrl: 'url', + rollbackUrl: 'url', + completion: 100, + status: 'found', + canaryIngress: { canaryWeight: 50 }, +}; export const environmentsApp = { environments: [ { diff --git a/spec/frontend/environments/new_environment_item_spec.js b/spec/frontend/environments/new_environment_item_spec.js index 0d5d216ba02..db596688dad 100644 --- a/spec/frontend/environments/new_environment_item_spec.js +++ b/spec/frontend/environments/new_environment_item_spec.js @@ -8,7 +8,8 @@ import { formatDate, getTimeago } from '~/lib/utils/datetime_utility'; import { __, s__, sprintf } from '~/locale'; import EnvironmentItem from '~/environments/components/new_environment_item.vue'; import Deployment from '~/environments/components/deployment.vue'; -import { resolvedEnvironment } from './graphql/mock_data'; +import DeployBoardWrapper from '~/environments/components/deploy_board_wrapper.vue'; +import { resolvedEnvironment, rolloutStatus } from './graphql/mock_data'; Vue.use(VueApollo); @@ -455,4 +456,35 @@ describe('~/environments/components/new_environment_item.vue', () => { expect(emptyState.exists()).toBe(false); }); }); + + describe('deploy boards', () => { + it('should show a deploy board if the environment has a rollout status', async () => { + const environment = { + ...resolvedEnvironment, + rolloutStatus, + }; + + wrapper = createWrapper({ + propsData: { environment }, + apolloProvider: createApolloProvider(), + }); + + await expandCollapsedSection(); + + const deployBoard = wrapper.findComponent(DeployBoardWrapper); + expect(deployBoard.exists()).toBe(true); + expect(deployBoard.props('rolloutStatus')).toBe(rolloutStatus); + }); + + it('should not show a deploy board if the environment has no rollout status', async () => { + wrapper = createWrapper({ + apolloProvider: createApolloProvider(), + }); + + await expandCollapsedSection(); + + const deployBoard = wrapper.findComponent(DeployBoardWrapper); + expect(deployBoard.exists()).toBe(false); + }); + }); }); diff --git a/spec/frontend/environments/new_environments_app_spec.js b/spec/frontend/environments/new_environments_app_spec.js index be86a202499..42e3608109b 100644 --- a/spec/frontend/environments/new_environments_app_spec.js +++ b/spec/frontend/environments/new_environments_app_spec.js @@ -10,6 +10,7 @@ import EnvironmentsApp from '~/environments/components/new_environments_app.vue' import EnvironmentsFolder from '~/environments/components/new_environment_folder.vue'; import EnvironmentsItem from '~/environments/components/new_environment_item.vue'; import StopEnvironmentModal from '~/environments/components/stop_environment_modal.vue'; +import CanaryUpdateModal from '~/environments/components/canary_update_modal.vue'; import { resolvedEnvironmentsApp, resolvedFolder, resolvedEnvironment } from './graphql/mock_data'; Vue.use(VueApollo); @@ -20,6 +21,8 @@ describe('~/environments/components/new_environments_app.vue', () => { let environmentFolderMock; let paginationMock; let environmentToStopMock; + let environmentToChangeCanaryMock; + let weightMock; const createApolloProvider = () => { const mockResolvers = { @@ -30,6 +33,8 @@ describe('~/environments/components/new_environments_app.vue', () => { environmentToStop: environmentToStopMock, environmentToDelete: jest.fn().mockResolvedValue(resolvedEnvironment), environmentToRollback: jest.fn().mockResolvedValue(resolvedEnvironment), + environmentToChangeCanary: environmentToChangeCanaryMock, + weight: weightMock, }, }; @@ -53,6 +58,8 @@ describe('~/environments/components/new_environments_app.vue', () => { environmentsApp, folder, environmentToStop = {}, + environmentToChangeCanary = {}, + weight = 0, pageInfo = { total: 20, perPage: 5, @@ -67,6 +74,8 @@ describe('~/environments/components/new_environments_app.vue', () => { environmentFolderMock.mockReturnValue(folder); paginationMock.mockReturnValue(pageInfo); environmentToStopMock.mockReturnValue(environmentToStop); + environmentToChangeCanaryMock.mockReturnValue(environmentToChangeCanary); + weightMock.mockReturnValue(weight); const apolloProvider = createApolloProvider(); wrapper = createWrapper({ apolloProvider, provide }); @@ -78,6 +87,8 @@ describe('~/environments/components/new_environments_app.vue', () => { environmentAppMock = jest.fn(); environmentFolderMock = jest.fn(); environmentToStopMock = jest.fn(); + environmentToChangeCanaryMock = jest.fn(); + weightMock = jest.fn(); paginationMock = jest.fn(); }); @@ -207,6 +218,19 @@ describe('~/environments/components/new_environments_app.vue', () => { expect(modal.props('environment')).toMatchObject(resolvedEnvironment); }); + + it('should pass the environment to change canary to the canary update modal', async () => { + await createWrapperWithMocked({ + environmentsApp: resolvedEnvironmentsApp, + folder: resolvedFolder, + environmentToChangeCanary: resolvedEnvironment, + weight: 10, + }); + + const modal = wrapper.findComponent(CanaryUpdateModal); + + expect(modal.props('environment')).toMatchObject(resolvedEnvironment); + }); }); describe('pagination', () => { diff --git a/spec/models/namespace_spec.rb b/spec/models/namespace_spec.rb index 481cae6031b..1728d4fc3f3 100644 --- a/spec/models/namespace_spec.rb +++ b/spec/models/namespace_spec.rb @@ -425,7 +425,7 @@ RSpec.describe Namespace do end context 'traversal_ids on create' do - context 'default traversal_ids' do + shared_examples 'default traversal_ids' do let(:namespace) { build(:namespace) } before do @@ -435,6 +435,18 @@ RSpec.describe Namespace do it { expect(namespace.traversal_ids).to eq [namespace.id] } end + + context 'with before_commit callback' do + it_behaves_like 'default traversal_ids' + end + + context 'with after_create callback' do + before do + stub_feature_flags(sync_traversal_ids_before_commit: false) + end + + it_behaves_like 'default traversal_ids' + end end describe "after_commit :expire_child_caches" do diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index f24e5a42e95..c487c87db1c 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -236,27 +236,42 @@ RSpec.describe Project, factory_default: :keep do end context 'with project namespaces' do - it 'automatically creates a project namespace' do - project = build(:project, path: 'hopefully-valid-path1') - project.save! + shared_examples 'creates project namespace' do + it 'automatically creates a project namespace' do + project = build(:project, path: 'hopefully-valid-path1') + project.save! - expect(project).to be_persisted - expect(project.project_namespace).to be_persisted - expect(project.project_namespace).to be_in_sync_with_project(project) - end + expect(project).to be_persisted + expect(project.project_namespace).to be_persisted + expect(project.project_namespace).to be_in_sync_with_project(project) + expect(project.reload.project_namespace.traversal_ids).to eq([project.namespace.traversal_ids, project.project_namespace.id].flatten.compact) + end - context 'with FF disabled' do - before do - stub_feature_flags(create_project_namespace_on_project_create: false) + context 'with FF disabled' do + before do + stub_feature_flags(create_project_namespace_on_project_create: false) + end + + it 'does not create a project namespace' do + project = build(:project, path: 'hopefully-valid-path2') + project.save! + + expect(project).to be_persisted + expect(project.project_namespace).to be_nil + end end + end - it 'does not create a project namespace' do - project = build(:project, path: 'hopefully-valid-path2') - project.save! + context 'sync-ing traversal_ids in before_commit callback' do + it_behaves_like 'creates project namespace' + end - expect(project).to be_persisted - expect(project.project_namespace).to be_nil + context 'sync-ing traversal_ids in after_create callback' do + before do + stub_feature_flags(sync_traversal_ids_before_commit: false) end + + it_behaves_like 'creates project namespace' end end end diff --git a/spec/requests/api/repositories_spec.rb b/spec/requests/api/repositories_spec.rb index 21a8622e08d..f42fc7aabc2 100644 --- a/spec/requests/api/repositories_spec.rb +++ b/spec/requests/api/repositories_spec.rb @@ -561,17 +561,6 @@ RSpec.describe API::Repositories do let(:request) { get api(route, guest) } end end - - context 'api_caching_rate_limit_repository_compare is disabled' do - before do - stub_feature_flags(api_caching_rate_limit_repository_compare: false) - end - - it_behaves_like 'repository compare' do - let(:project) { create(:project, :public, :repository) } - let(:current_user) { nil } - end - end end describe 'GET /projects/:id/repository/contributors' do diff --git a/spec/services/groups/create_service_spec.rb b/spec/services/groups/create_service_spec.rb index 1a3dde78580..7ec523a1f2b 100644 --- a/spec/services/groups/create_service_spec.rb +++ b/spec/services/groups/create_service_spec.rb @@ -8,6 +8,10 @@ RSpec.describe Groups::CreateService, '#execute' do subject { service.execute } + shared_examples 'has sync-ed traversal_ids' do + specify { expect(subject.reload.traversal_ids).to eq([subject.parent&.traversal_ids, subject.id].flatten.compact) } + end + describe 'visibility level restrictions' do let!(:service) { described_class.new(user, group_params) } @@ -77,6 +81,18 @@ RSpec.describe Groups::CreateService, '#execute' do it 'adds an onboarding progress record' do expect { subject }.to change(OnboardingProgress, :count).from(0).to(1) end + + context 'with before_commit callback' do + it_behaves_like 'has sync-ed traversal_ids' + end + + context 'with after_create callback' do + before do + stub_feature_flags(sync_traversal_ids_before_commit: false) + end + + it_behaves_like 'has sync-ed traversal_ids' + end end context 'when user can not create a group' do @@ -102,6 +118,18 @@ RSpec.describe Groups::CreateService, '#execute' do it 'does not add an onboarding progress record' do expect { subject }.not_to change(OnboardingProgress, :count).from(0) end + + context 'with before_commit callback' do + it_behaves_like 'has sync-ed traversal_ids' + end + + context 'with after_create callback' do + before do + stub_feature_flags(sync_traversal_ids_before_commit: false) + end + + it_behaves_like 'has sync-ed traversal_ids' + end end context 'as guest' do diff --git a/spec/services/issues/create_service_spec.rb b/spec/services/issues/create_service_spec.rb index 90bab5bf76a..f4bb1f0877b 100644 --- a/spec/services/issues/create_service_spec.rb +++ b/spec/services/issues/create_service_spec.rb @@ -97,8 +97,6 @@ RSpec.describe Issues::CreateService do let(:issue_after) { create(:issue, project: project, relative_position: 50) } before do - project.add_reporter(user) - opts.merge!(move_between_ids: [issue_before.id, issue_after.id]) end diff --git a/spec/services/projects/create_service_spec.rb b/spec/services/projects/create_service_spec.rb index 43a9d69f3bb..10f694827e1 100644 --- a/spec/services/projects/create_service_spec.rb +++ b/spec/services/projects/create_service_spec.rb @@ -190,9 +190,13 @@ RSpec.describe Projects::CreateService, '#execute' do user.refresh_authorized_projects # Ensure cache is warm end - it do - project = create_project(user, opts.merge!(namespace_id: group.id)) + subject(:project) { create_project(user, opts.merge!(namespace_id: group.id)) } + shared_examples 'has sync-ed traversal_ids' do + specify { expect(project.reload.project_namespace.traversal_ids).to eq([project.namespace.traversal_ids, project.project_namespace.id].flatten.compact) } + end + + it 'creates the project' do expect(project).to be_valid expect(project.owner).to eq(group) expect(project.namespace).to eq(group) @@ -200,6 +204,18 @@ RSpec.describe Projects::CreateService, '#execute' do expect(user.authorized_projects).to include(project) expect(project.project_namespace).to be_in_sync_with_project(project) end + + context 'with before_commit callback' do + it_behaves_like 'has sync-ed traversal_ids' + end + + context 'with after_create callback' do + before do + stub_feature_flags(sync_traversal_ids_before_commit: false) + end + + it_behaves_like 'has sync-ed traversal_ids' + end end context 'group sharing', :sidekiq_inline do |