diff --git a/.gitlab/ci/rules.gitlab-ci.yml b/.gitlab/ci/rules.gitlab-ci.yml index 9fbb089d9732c34fd9f8f7df01ee724452c96c05..afe900f39a692e10fc672c0fcfe942f3e73c1df0 100644 --- a/.gitlab/ci/rules.gitlab-ci.yml +++ b/.gitlab/ci/rules.gitlab-ci.yml @@ -1001,6 +1001,7 @@ - <<: *if-merge-request-targeting-stable-branch allow_failure: true - <<: *if-ruby3-branch + allow_failure: true - <<: *if-dot-com-gitlab-org-and-security-merge-request-manual-ff-package-and-e2e changes: *feature-flag-development-config-patterns when: manual diff --git a/app/assets/javascripts/projects/settings/branch_rules/components/view/constants.js b/app/assets/javascripts/projects/settings/branch_rules/components/view/constants.js index 1fd87b9897c2b18d627b5f5b152dee2c6e78e065..264c26294333b7d969cda77d348bd44ad77f8606 100644 --- a/app/assets/javascripts/projects/settings/branch_rules/components/view/constants.js +++ b/app/assets/javascripts/projects/settings/branch_rules/components/view/constants.js @@ -19,9 +19,14 @@ export const I18N = { ), disallowForcePushDescription: s__('BranchRules|Force push is not allowed.'), approvalsTitle: s__('BranchRules|Approvals'), + manageApprovalsLinkTitle: s__('BranchRules|Manage in Merge Request Approvals'), + approvalsDescription: s__( + 'BranchRules|Approvals to ensure separation of duties for new merge requests. %{linkStart}Lean more.%{linkEnd}', + ), statusChecksTitle: s__('BranchRules|Status checks'), allowedToPushHeader: s__('BranchRules|Allowed to push (%{total})'), allowedToMergeHeader: s__('BranchRules|Allowed to merge (%{total})'), + approvalsHeader: s__('BranchRules|Required approvals (%{total})'), noData: s__('BranchRules|No data to display'), }; @@ -33,3 +38,5 @@ export const WILDCARDS_HELP_PATH = 'user/project/protected_branches#configure-multiple-protected-branches-by-using-a-wildcard'; export const PROTECTED_BRANCHES_HELP_PATH = 'user/project/protected_branches'; + +export const APPROVALS_HELP_PATH = 'user/project/merge_requests/approvals/index.md'; diff --git a/app/assets/javascripts/projects/settings/branch_rules/components/view/index.vue b/app/assets/javascripts/projects/settings/branch_rules/components/view/index.vue index 6534ff883a6a20313847c263e2366adb62e73066..318940478a82d04ef95b4208a5fa337a0fefc7a1 100644 --- a/app/assets/javascripts/projects/settings/branch_rules/components/view/index.vue +++ b/app/assets/javascripts/projects/settings/branch_rules/components/view/index.vue @@ -11,16 +11,19 @@ import { BRANCH_PARAM_NAME, WILDCARDS_HELP_PATH, PROTECTED_BRANCHES_HELP_PATH, + APPROVALS_HELP_PATH, } from './constants'; const wildcardsHelpDocLink = helpPagePath(WILDCARDS_HELP_PATH); const protectedBranchesHelpDocLink = helpPagePath(PROTECTED_BRANCHES_HELP_PATH); +const approvalsHelpDocLink = helpPagePath(APPROVALS_HELP_PATH); export default { name: 'RuleView', i18n: I18N, wildcardsHelpDocLink, protectedBranchesHelpDocLink, + approvalsHelpDocLink, components: { Protection, GlSprintf, GlLink, GlLoadingIcon }, inject: { projectPath: { @@ -29,6 +32,9 @@ export default { protectedBranchesPath: { default: '', }, + approvalRulesPath: { + default: '', + }, }, apollo: { project: { @@ -48,7 +54,9 @@ export default { data() { return { branch: getParameterByName(BRANCH_PARAM_NAME), - branchProtection: {}, + branchProtection: { + approvalRules: {}, + }, }; }, computed: { @@ -75,6 +83,15 @@ export default { total: this.pushAccessLevels.total, }); }, + approvalsHeader() { + const total = this.approvals.reduce( + (sum, { approvalsRequired }) => sum + approvalsRequired, + 0, + ); + return sprintf(this.$options.i18n.approvalsHeader, { + total, + }); + }, allBranches() { return this.branch === ALL_BRANCHES_WILDCARD; }, @@ -86,6 +103,9 @@ export default { ? this.$options.i18n.targetBranch : this.$options.i18n.branchNameOrPattern; }, + approvals() { + return this.branchProtection?.approvalRules?.nodes || []; + }, }, methods: { getAccessLevels(accessLevels = {}) { @@ -164,7 +184,22 @@ export default { /> <!-- Approvals --> - <!-- Follow-up: add approval section (https://gitlab.com/gitlab-org/gitlab/-/issues/372362) --> + <h4 class="gl-mb-1 gl-mt-5">{{ $options.i18n.approvalsTitle }}</h4> + <gl-sprintf :message="$options.i18n.approvalsDescription"> + <template #link="{ content }"> + <gl-link :href="$options.approvalsHelpDocLink"> + {{ content }} + </gl-link> + </template> + </gl-sprintf> + + <protection + class="gl-mt-3" + :header="approvalsHeader" + :header-link-title="$options.i18n.manageApprovalsLinkTitle" + :header-link-href="approvalRulesPath" + :approvals="approvals" + /> <!-- Status checks --> <!-- Follow-up: add status checks section (https://gitlab.com/gitlab-org/gitlab/-/issues/372362) --> diff --git a/app/assets/javascripts/projects/settings/branch_rules/components/view/protection.vue b/app/assets/javascripts/projects/settings/branch_rules/components/view/protection.vue index 8434b7bfce59fa03cafa0e9327c2140f07890904..cfe2df0dbda1facc5d8ef742c165a4428b5e5a7f 100644 --- a/app/assets/javascripts/projects/settings/branch_rules/components/view/protection.vue +++ b/app/assets/javascripts/projects/settings/branch_rules/components/view/protection.vue @@ -41,6 +41,11 @@ export default { required: false, default: () => [], }, + approvals: { + type: Array, + required: false, + default: () => [], + }, }, computed: { showUsersDivider() { @@ -80,5 +85,15 @@ export default { :title="$options.i18n.groupsTitle" :access-levels="groups" /> + + <!-- Approvals --> + <protection-row + v-for="(approval, index) in approvals" + :key="approval.name" + :show-divider="index !== 0" + :title="approval.name" + :users="approval.eligibleApprovers.nodes" + :approvals-required="approval.approvalsRequired" + /> </gl-card> </template> diff --git a/app/assets/javascripts/projects/settings/branch_rules/components/view/protection_row.vue b/app/assets/javascripts/projects/settings/branch_rules/components/view/protection_row.vue index 2509c2538b20b5acaef8f83c334d77b38e244240..28a1c09fa821232fa32bb948206ed5663323e806 100644 --- a/app/assets/javascripts/projects/settings/branch_rules/components/view/protection_row.vue +++ b/app/assets/javascripts/projects/settings/branch_rules/components/view/protection_row.vue @@ -36,6 +36,11 @@ export default { required: false, default: () => [], }, + approvalsRequired: { + type: Number, + required: false, + default: 0, + }, }, computed: { avatarBadgeSrOnlyText() { @@ -48,6 +53,11 @@ export default { commaSeparateList() { return this.accessLevels.length > 1; }, + approvalsRequiredTitle() { + return this.approvalsRequired + ? n__('%d approval required', '%d approvals required', this.approvalsRequired) + : null; + }, }, }; </script> @@ -57,34 +67,44 @@ export default { class="gl-display-flex gl-align-items-center gl-border-gray-100 gl-mb-4 gl-pt-4" :class="{ 'gl-border-t-solid': showDivider }" > - <div class="gl-mr-7">{{ title }}</div> + <div class="gl-display-flex gl-w-half gl-justify-content-space-between"> + <div class="gl-mr-7 gl-w-quarter">{{ title }}</div> + + <gl-avatars-inline + v-if="users.length" + class="gl-w-quarter!" + :avatars="users" + :collapsed="true" + :max-visible="$options.MAX_VISIBLE_AVATARS" + :avatar-size="$options.AVATAR_SIZE" + badge-tooltip-prop="name" + :badge-tooltip-max-chars="$options.AVATAR_TOOLTIP_MAX_CHARS" + :badge-sr-only-text="avatarBadgeSrOnlyText" + > + <template #avatar="{ avatar }"> + <gl-avatar-link + :key="avatar.username" + v-gl-tooltip + target="_blank" + :href="avatar.webUrl" + :title="avatar.name" + > + <gl-avatar :src="avatar.avatarUrl" :label="avatar.name" :size="$options.AVATAR_SIZE" /> + </gl-avatar-link> + </template> + </gl-avatars-inline> - <gl-avatars-inline - v-if="users.length" - :avatars="users" - :collapsed="true" - :max-visible="$options.MAX_VISIBLE_AVATARS" - :avatar-size="$options.AVATAR_SIZE" - badge-tooltip-prop="name" - :badge-tooltip-max-chars="$options.AVATAR_TOOLTIP_MAX_CHARS" - :badge-sr-only-text="avatarBadgeSrOnlyText" - > - <template #avatar="{ avatar }"> - <gl-avatar-link - :key="avatar.username" - v-gl-tooltip - target="_blank" - :href="avatar.webUrl" - :title="avatar.name" - > - <gl-avatar :src="avatar.avatarUrl" :label="avatar.name" :size="$options.AVATAR_SIZE" /> - </gl-avatar-link> - </template> - </gl-avatars-inline> + <div + v-for="(item, index) in accessLevels" + :key="index" + data-testid="access-level" + class="gl-w-quarter" + > + <span v-if="commaSeparateList && index > 0" data-testid="comma-separator">,</span> + {{ item.accessLevelDescription }} + </div> - <div v-for="(item, index) in accessLevels" :key="index" data-testid="access-level"> - <span v-if="commaSeparateList && index > 0" data-testid="comma-separator">,</span> - {{ item.accessLevelDescription }} + <div class="gl-ml-7 gl-w-quarter">{{ approvalsRequiredTitle }}</div> </div> </div> </template> diff --git a/app/assets/javascripts/projects/settings/branch_rules/mount_branch_rules.js b/app/assets/javascripts/projects/settings/branch_rules/mount_branch_rules.js index 39164063d05ee97dd79979656e28716cbfe78b3c..07fd0a7080f3b35172ed8ef02d91336a1c2dff44 100644 --- a/app/assets/javascripts/projects/settings/branch_rules/mount_branch_rules.js +++ b/app/assets/javascripts/projects/settings/branch_rules/mount_branch_rules.js @@ -14,7 +14,7 @@ export default function mountBranchRules(el) { defaultClient: createDefaultClient(), }); - const { projectPath, protectedBranchesPath } = el.dataset; + const { projectPath, protectedBranchesPath, approvalRulesPath } = el.dataset; return new Vue({ el, @@ -22,6 +22,7 @@ export default function mountBranchRules(el) { provide: { projectPath, protectedBranchesPath, + approvalRulesPath, }, render(h) { return h(View); diff --git a/app/assets/javascripts/sidebar/components/reviewers/sidebar_reviewers.vue b/app/assets/javascripts/sidebar/components/reviewers/sidebar_reviewers.vue index e34c51bc11f9ced0bb3bbff2deba9b2c2c79e0be..ad061dd2e6b9c4f7232e9a10a31e4a476dc65776 100644 --- a/app/assets/javascripts/sidebar/components/reviewers/sidebar_reviewers.vue +++ b/app/assets/javascripts/sidebar/components/reviewers/sidebar_reviewers.vue @@ -85,7 +85,7 @@ export default { return this.loading || this.$apollo.queries.issuable.loading; }, canUpdate() { - return this.issuable.userPermissions?.updateMergeRequest || false; + return this.issuable.userPermissions?.adminMergeRequest || false; }, }, created() { diff --git a/app/assets/javascripts/vue_shared/components/sidebar/queries/get_merge_request_reviewers.query.graphql b/app/assets/javascripts/vue_shared/components/sidebar/queries/get_merge_request_reviewers.query.graphql index 05de680ab059ed6366353dc1908110dde3151acc..f087ca6c9828ef4b0181cc2ba8cf18e6f332fa21 100644 --- a/app/assets/javascripts/vue_shared/components/sidebar/queries/get_merge_request_reviewers.query.graphql +++ b/app/assets/javascripts/vue_shared/components/sidebar/queries/get_merge_request_reviewers.query.graphql @@ -19,7 +19,7 @@ query mergeRequestReviewers($fullPath: ID!, $iid: String!) { } } userPermissions { - updateMergeRequest + adminMergeRequest } } } diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb index 282622c792e3b251c259219ef3bbf4f7852e932d..e05a32ccb5b899327d4444b6b6ed6c04ed93ef62 100644 --- a/app/models/ci/build.rb +++ b/app/models/ci/build.rb @@ -455,8 +455,7 @@ def waiting_for_deployment_approval? def prevent_rollback_deployment? strong_memoize(:prevent_rollback_deployment) do - Feature.enabled?(:prevent_outdated_deployment_jobs, project) && - starts_environment? && + starts_environment? && project.ci_forward_deployment_enabled? && deployment&.older_than_last_successful_deployment? end diff --git a/app/models/deployment.rb b/app/models/deployment.rb index dafcbc593be0721e76091e0c9b1cd737d79c4e74..4d10499f48d4a72f3a35c2af875f8a6da321a893 100644 --- a/app/models/deployment.rb +++ b/app/models/deployment.rb @@ -105,6 +105,7 @@ class Deployment < ApplicationRecord after_transition any => :running do |deployment| next unless deployment.project.ci_forward_deployment_enabled? + next if Feature.enabled?(:prevent_outdated_deployment_jobs, deployment.project) deployment.run_after_commit do Deployments::DropOlderDeploymentsWorker.perform_async(id) diff --git a/app/models/namespace/aggregation_schedule.rb b/app/models/namespace/aggregation_schedule.rb index 72f5ab11060ab12279cbe2dd763b1ef4567314a9..cd7d4fc409a586d3a49d39d0ca538eb2f1f250a7 100644 --- a/app/models/namespace/aggregation_schedule.rb +++ b/app/models/namespace/aggregation_schedule.rb @@ -14,9 +14,9 @@ class Namespace::AggregationSchedule < ApplicationRecord def self.default_lease_timeout if Feature.enabled?(:remove_namespace_aggregator_delay) - 1.hour.to_i + 30.minutes.to_i else - 1.5.hours.to_i + 1.hour.to_i end end diff --git a/app/services/ci/process_build_service.rb b/app/services/ci/process_build_service.rb index e6ec65fcc913892a0a65835296baa5fd1caeb4fa..22cd267806d20c4fc8ace685d70990fb487baaeb 100644 --- a/app/services/ci/process_build_service.rb +++ b/app/services/ci/process_build_service.rb @@ -25,6 +25,8 @@ def process(build) end def enqueue(build) + return build.drop!(:failed_outdated_deployment_job) if build.prevent_rollback_deployment? + build.enqueue end diff --git a/app/views/projects/settings/branch_rules/index.html.haml b/app/views/projects/settings/branch_rules/index.html.haml index ab692a23e4468eb185a5483536f06e31d766c173..a7e80101a88407bc1e8e26c3922b27890eecf7b3 100644 --- a/app/views/projects/settings/branch_rules/index.html.haml +++ b/app/views/projects/settings/branch_rules/index.html.haml @@ -3,4 +3,4 @@ %h3.gl-mb-5= s_('BranchRules|Branch rules details') -#js-branch-rules{ data: { project_path: @project.full_path, protected_branches_path: project_settings_repository_path(@project, anchor: 'js-protected-branches-settings') } } +#js-branch-rules{ data: { project_path: @project.full_path, protected_branches_path: project_settings_repository_path(@project, anchor: 'js-protected-branches-settings'), approval_rules_path: project_settings_merge_requests_path(@project, anchor: 'js-merge-request-approval-settings') } } diff --git a/config/metrics/counts_28d/20220222215951_xmau_plan.yml b/config/metrics/counts_28d/20220222215951_xmau_plan.yml index 587f34e90c3df09b79fcd51f36429b42c8f2df8c..c254ad942c2cfdbb725e1c1f8ec4fb2c36e00b52 100644 --- a/config/metrics/counts_28d/20220222215951_xmau_plan.yml +++ b/config/metrics/counts_28d/20220222215951_xmau_plan.yml @@ -21,6 +21,7 @@ options: - users_updating_work_item_title - users_updating_work_item_dates - users_updating_work_item_labels + - users_updating_work_item_iteration data_category: optional distribution: - ce diff --git a/config/metrics/counts_28d/20220222215952_xmau_project_management.yml b/config/metrics/counts_28d/20220222215952_xmau_project_management.yml index 542f8a131181c626ef1aecafb329f037d6f17677..0dad4fd0979fc4e8c2157d76ed5136c5d1edf894 100644 --- a/config/metrics/counts_28d/20220222215952_xmau_project_management.yml +++ b/config/metrics/counts_28d/20220222215952_xmau_project_management.yml @@ -21,6 +21,7 @@ options: - users_updating_work_item_title - users_updating_work_item_dates - users_updating_work_item_labels + - users_updating_work_item_iteration data_category: optional distribution: - ce diff --git a/config/metrics/counts_28d/20220222215955_users_work_items.yml b/config/metrics/counts_28d/20220222215955_users_work_items.yml index e7a95c9d3356ac94dc9cbd817ba961feac37a99d..ec07fb25f1182fe644d5b03ee8c508e452aa160a 100644 --- a/config/metrics/counts_28d/20220222215955_users_work_items.yml +++ b/config/metrics/counts_28d/20220222215955_users_work_items.yml @@ -21,6 +21,7 @@ options: - users_updating_work_item_title - users_updating_work_item_dates - users_updating_work_item_labels + - users_updating_work_item_iteration data_category: optional distribution: - ce diff --git a/config/metrics/counts_28d/20220922042106_users_updating_work_item_iteration_monthly.yml b/config/metrics/counts_28d/20220922042106_users_updating_work_item_iteration_monthly.yml new file mode 100644 index 0000000000000000000000000000000000000000..4c19e4e32619e47b63d103bdadef04e5a746f297 --- /dev/null +++ b/config/metrics/counts_28d/20220922042106_users_updating_work_item_iteration_monthly.yml @@ -0,0 +1,24 @@ +--- +key_path: redis_hll_counters.work_items.users_updating_work_item_iteration_monthly +description: Unique users updating a work item's iteration +product_section: team planning +product_stage: dev +product_group: plan +product_category: project_management +value_type: number +status: active +milestone: "15.5" +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/98539 +time_frame: 28d +data_source: redis_hll +data_category: optional +instrumentation_class: RedisHLLMetric +options: + events: + - users_updating_work_item_iteration +distribution: +- ce +- ee +tier: +- premium +- ultimate diff --git a/config/metrics/counts_7d/20220222215851_xmau_plan.yml b/config/metrics/counts_7d/20220222215851_xmau_plan.yml index 4fb302d67ac3e985c73480ac4c567ef4f4e741c3..77325a205eeb9713d4f522ab87f5fde61c19d674 100644 --- a/config/metrics/counts_7d/20220222215851_xmau_plan.yml +++ b/config/metrics/counts_7d/20220222215851_xmau_plan.yml @@ -21,6 +21,7 @@ options: - users_updating_work_item_title - users_updating_work_item_dates - users_updating_work_item_labels + - users_updating_work_item_iteration data_category: optional distribution: - ce diff --git a/config/metrics/counts_7d/20220222215852_xmau_project_management.yml b/config/metrics/counts_7d/20220222215852_xmau_project_management.yml index a63adfab8aa9ea87eb0afae8cab28cd9f08eb405..c7e712cf92a55ff87758ea568a35e073bda1e14c 100644 --- a/config/metrics/counts_7d/20220222215852_xmau_project_management.yml +++ b/config/metrics/counts_7d/20220222215852_xmau_project_management.yml @@ -21,6 +21,7 @@ options: - users_updating_work_item_title - users_updating_work_item_dates - users_updating_work_item_labels + - users_updating_work_item_iteration data_category: optional distribution: - ce diff --git a/config/metrics/counts_7d/20220222215855_users_work_items.yml b/config/metrics/counts_7d/20220222215855_users_work_items.yml index cb3120221921c4803951c03ebc27ba96482eeee5..0985f38c83be0253f15c5b72969dce19559feaaa 100644 --- a/config/metrics/counts_7d/20220222215855_users_work_items.yml +++ b/config/metrics/counts_7d/20220222215855_users_work_items.yml @@ -21,6 +21,7 @@ options: - users_updating_work_item_title - users_updating_work_item_dates - users_updating_work_item_labels + - users_updating_work_item_iteration data_category: optional distribution: - ce diff --git a/config/metrics/counts_7d/20220922042528_users_updating_work_item_iteration_weekly.yml b/config/metrics/counts_7d/20220922042528_users_updating_work_item_iteration_weekly.yml new file mode 100644 index 0000000000000000000000000000000000000000..aad949867cfcda41d575450399f6c28c790552c4 --- /dev/null +++ b/config/metrics/counts_7d/20220922042528_users_updating_work_item_iteration_weekly.yml @@ -0,0 +1,24 @@ +--- +key_path: redis_hll_counters.work_items.users_updating_work_item_iteration_weekly +description: Unique users updating a work item's iteration +product_section: team planning +product_stage: dev +product_group: plan +product_category: project_management +value_type: number +status: active +milestone: "15.5" +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/98539 +time_frame: 7d +data_source: redis_hll +data_category: optional +instrumentation_class: RedisHLLMetric +options: + events: + - users_updating_work_item_iteration +distribution: +- ce +- ee +tier: +- premium +- ultimate diff --git a/db/migrate/20221014031033_add_temp_index_to_project_features_where_releases_access_level_gt_repository.rb b/db/migrate/20221014031033_add_temp_index_to_project_features_where_releases_access_level_gt_repository.rb new file mode 100644 index 0000000000000000000000000000000000000000..14077e3078012389e83d0571d717d6b136793d2c --- /dev/null +++ b/db/migrate/20221014031033_add_temp_index_to_project_features_where_releases_access_level_gt_repository.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +class AddTempIndexToProjectFeaturesWhereReleasesAccessLevelGtRepository < Gitlab::Database::Migration[2.0] + disable_ddl_transaction! + + INDEX_NAME = 'tmp_idx_project_features_on_releases_al_and_repo_al_partial' + + # Temporary index to be removed in 15.6 https://gitlab.com/gitlab-org/gitlab/-/issues/377915 + def up + add_concurrent_index :project_features, + [:releases_access_level, :repository_access_level], + name: INDEX_NAME, + where: 'releases_access_level > repository_access_level' + end + + def down + remove_concurrent_index_by_name :project_features, INDEX_NAME + end +end diff --git a/db/migrate/20221014034338_populate_releases_access_level_from_repository.rb b/db/migrate/20221014034338_populate_releases_access_level_from_repository.rb new file mode 100644 index 0000000000000000000000000000000000000000..6e61d972bf6a022e2f14645ffac11d1fa6ee773b --- /dev/null +++ b/db/migrate/20221014034338_populate_releases_access_level_from_repository.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +class PopulateReleasesAccessLevelFromRepository < Gitlab::Database::Migration[2.0] + restrict_gitlab_migration gitlab_schema: :gitlab_main + + disable_ddl_transaction! + + def up + update_column_in_batches( + :project_features, + :releases_access_level, + Arel.sql('repository_access_level') + ) do |table, query| + query.where(table[:releases_access_level].gt(table[:repository_access_level])) + end + end + + def down + # no-op + end +end diff --git a/db/schema_migrations/20221014031033 b/db/schema_migrations/20221014031033 new file mode 100644 index 0000000000000000000000000000000000000000..6a24a2027c78cbe2e03d0288b216a84588c85cb7 --- /dev/null +++ b/db/schema_migrations/20221014031033 @@ -0,0 +1 @@ +bc05939dc672c078161cd9b7dbd7f92601edb6888a77c62adb014964e30c6ae8 \ No newline at end of file diff --git a/db/schema_migrations/20221014034338 b/db/schema_migrations/20221014034338 new file mode 100644 index 0000000000000000000000000000000000000000..c90dfebb72b99af4ee6fa5a2361b957e31987414 --- /dev/null +++ b/db/schema_migrations/20221014034338 @@ -0,0 +1 @@ +58ee7f51a0da4ee4ec471d4492106d1fc3124419ba83591913967d6bd38105e5 \ No newline at end of file diff --git a/db/structure.sql b/db/structure.sql index e95f93982fab082228a9089fb1e39e1567befaa8..b8779dd9416c188b3133245cf70756e54f1161d3 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -30993,6 +30993,8 @@ CREATE UNIQUE INDEX taggings_idx ON taggings USING btree (tag_id, taggable_id, t CREATE UNIQUE INDEX term_agreements_unique_index ON term_agreements USING btree (user_id, term_id); +CREATE INDEX tmp_idx_project_features_on_releases_al_and_repo_al_partial ON project_features USING btree (releases_access_level, repository_access_level) WHERE (releases_access_level > repository_access_level); + CREATE INDEX tmp_idx_vulnerabilities_on_id_where_report_type_7_99 ON vulnerabilities USING btree (id) WHERE (report_type = ANY (ARRAY[7, 99])); CREATE INDEX tmp_index_approval_merge_request_rules_on_report_type_equal_one ON approval_merge_request_rules USING btree (id, report_type) WHERE (report_type = 1); diff --git a/doc/ci/review_apps/index.md b/doc/ci/review_apps/index.md index bc362b07385772b55d468b9096977cdca2ff2b1a..2f23ebbfd9f4fff79626ee667810c5f0eae84f67 100644 --- a/doc/ci/review_apps/index.md +++ b/doc/ci/review_apps/index.md @@ -95,9 +95,14 @@ after a given period of time. The following are example projects that demonstrate Review App configuration: -- [NGINX](https://gitlab.com/gitlab-examples/review-apps-nginx). -- [OpenShift](https://gitlab.com/gitlab-examples/review-apps-openshift). -- [HashiCorp Nomad](https://gitlab.com/gitlab-examples/review-apps-nomad). +| Project | Configuration file | +|-------------------------------------------------------------------------|--------------------| +| [NGINX](https://gitlab.com/gitlab-examples/review-apps-nginx) | [`.gitlab-ci.yml`](https://gitlab.com/gitlab-examples/review-apps-nginx/-/blob/b9c1f6a8a7a0dfd9c8784cbf233c0a7b6a28ff27/.gitlab-ci.yml#L20) | +| [OpenShift](https://gitlab.com/gitlab-examples/review-apps-openshift) | [`.gitlab-ci.yml`](https://gitlab.com/gitlab-examples/review-apps-openshift/-/blob/82ebd572334793deef2d5ddc379f38942f3488be/.gitlab-ci.yml#L42) | +| [HashiCorp Nomad](https://gitlab.com/gitlab-examples/review-apps-nomad) | [`.gitlab-ci.yml`](https://gitlab.com/gitlab-examples/review-apps-nomad/-/blob/ca372c778be7aaed5e82d3be24e98c3f10a465af/.gitlab-ci.yml#L110) | +| [GitLab Documentation](https://gitlab.com/gitlab-org/gitlab-docs/) | [`build-and-deploy.gitlab-ci.yml`](https://gitlab.com/gitlab-org/gitlab-docs/-/blob/a715625496303cbd90ff89f3d3658ea8d36ce0f3/.gitlab/ci/build-and-deploy.gitlab-ci.yml#L59) | +| [`https://about.gitlab.com/`](https://gitlab.com/gitlab-com/www-gitlab-com/) | [`.gitlab-ci.yml`](https://gitlab.com/gitlab-com/www-gitlab-com/-/blob/6ffcdc3cb9af2abed490cbe5b7417df3e83cd76c/.gitlab-ci.yml#L332) | +| [GitLab Insights](https://gitlab.com/gitlab-org/gitlab-insights/) | [`.gitlab-ci.yml`](https://gitlab.com/gitlab-org/gitlab-insights/-/blob/9e63f44ac2a5a4defc965d0d61d411a768e20546/.gitlab-ci.yml#L234) | Other examples of Review Apps: diff --git a/ee/app/services/ee/ci/process_build_service.rb b/ee/app/services/ee/ci/process_build_service.rb index a586057e0eec84bbf83a7fb0f8f9ceec5e74ce2e..d73c2579706cbcefbff68566357afef31c5e0d66 100644 --- a/ee/app/services/ee/ci/process_build_service.rb +++ b/ee/app/services/ee/ci/process_build_service.rb @@ -23,8 +23,6 @@ def enqueue(build) return build.drop!(:protected_environment_failure) end - return build.drop!(:failed_outdated_deployment_job) if build.prevent_rollback_deployment? - super end end diff --git a/ee/app/services/resource_events/change_iteration_service.rb b/ee/app/services/resource_events/change_iteration_service.rb index 53def1785a53b9d7fcd812cd067b0923e1f9c21f..86fc53720ce87a61d550fd7c3f379d6daf658d3e 100644 --- a/ee/app/services/resource_events/change_iteration_service.rb +++ b/ee/app/services/resource_events/change_iteration_service.rb @@ -7,10 +7,20 @@ class ChangeIterationService < ::ResourceEvents::BaseChangeTimeboxService def initialize(resource, user, old_iteration_id:) super(resource, user) + @resource = resource + @user = user @iteration = resource&.iteration @old_iteration_id = old_iteration_id end + def execute + super + + return unless resource.is_a?(WorkItem) + + Gitlab::UsageDataCounters::WorkItemActivityUniqueCounter.track_work_item_iteration_changed_action(author: user) + end + private def create_event diff --git a/ee/lib/ee/gitlab/usage_data_counters/work_item_activity_unique_counter.rb b/ee/lib/ee/gitlab/usage_data_counters/work_item_activity_unique_counter.rb index aedd0e3a828215167104744c3dbce970fcf84c13..46872defa9265aa0f35cb606b4844a6b38b22db9 100644 --- a/ee/lib/ee/gitlab/usage_data_counters/work_item_activity_unique_counter.rb +++ b/ee/lib/ee/gitlab/usage_data_counters/work_item_activity_unique_counter.rb @@ -7,11 +7,16 @@ module WorkItemActivityUniqueCounter extend ActiveSupport::Concern WORK_ITEM_WEIGHT_CHANGED = 'users_updating_weight_estimate' + WORK_ITEM_ITERATION_CHANGED = 'users_updating_work_item_iteration' class_methods do def track_work_item_weight_changed_action(author:) track_unique_action(WORK_ITEM_WEIGHT_CHANGED, author) end + + def track_work_item_iteration_changed_action(author:) + track_unique_action(WORK_ITEM_ITERATION_CHANGED, author) + end end end end diff --git a/ee/spec/lib/ee/gitlab/usage_data_counters/work_item_activity_unique_counter_spec.rb b/ee/spec/lib/ee/gitlab/usage_data_counters/work_item_activity_unique_counter_spec.rb index affc5ce5581285303f4109af0850651f16401e14..fab3057a9783b79890c6c8b1f6315dcc8a3b34b1 100644 --- a/ee/spec/lib/ee/gitlab/usage_data_counters/work_item_activity_unique_counter_spec.rb +++ b/ee/spec/lib/ee/gitlab/usage_data_counters/work_item_activity_unique_counter_spec.rb @@ -12,4 +12,12 @@ it_behaves_like 'work item unique counter' end + + describe '.track_work_item_iteration_changed_action' do + subject(:track_event) { described_class.track_work_item_iteration_changed_action(author: user) } + + let(:event_name) { described_class::WORK_ITEM_ITERATION_CHANGED } + + it_behaves_like 'work item unique counter' + end end diff --git a/ee/spec/services/ee/resource_events/change_iteration_service_spec.rb b/ee/spec/services/ee/resource_events/change_iteration_service_spec.rb index cec3cdabb31b7ee2cba1a73aa6a9beb8f937cdd5..f10db46944bea207e696654d8590a3b4fe45e78a 100644 --- a/ee/spec/services/ee/resource_events/change_iteration_service_spec.rb +++ b/ee/spec/services/ee/resource_events/change_iteration_service_spec.rb @@ -14,4 +14,33 @@ let_it_be(:resource) { create(issuable) } # rubocop:disable Rails/SaveBang end end + + describe 'events tracking' do + let_it_be(:user) { create(:user) } + + subject(:changed_service_instance) { described_class.new(resource, user, old_iteration_id: nil) } + + context 'when the resource is a work item' do + let(:resource) { create(:work_item, iteration: timebox) } + + it 'tracks work item usage data counters' do + expect(Gitlab::UsageDataCounters::WorkItemActivityUniqueCounter) + .to receive(:track_work_item_iteration_changed_action) + .with(author: user) + + changed_service_instance.execute + end + end + + context 'when the resource is not a work item' do + let(:resource) { create(:issue, iteration: timebox) } + + it 'does not track work item usage data counters' do + expect(Gitlab::UsageDataCounters::WorkItemActivityUniqueCounter) + .not_to receive(:track_work_item_iteration_changed_action) + + changed_service_instance.execute + end + end + end end diff --git a/lib/gitlab/usage_data_counters/known_events/work_items.yml b/lib/gitlab/usage_data_counters/known_events/work_items.yml index 94b7a37e67e34b74de2af4a65f1d9b41fedd7773..ee828fc0f72413bf1f0816f619776973598820da 100644 --- a/lib/gitlab/usage_data_counters/known_events/work_items.yml +++ b/lib/gitlab/usage_data_counters/known_events/work_items.yml @@ -19,3 +19,11 @@ redis_slot: users aggregation: weekly feature_flag: track_work_items_activity +- name: users_updating_work_item_iteration + # The event tracks an EE feature. + # It's added here so it can be aggregated into the CE/EE 'OR' aggregate metrics. + # It will report 0 for CE instances and should not be used with 'AND' aggregators. + category: work_items + redis_slot: users + aggregation: weekly + feature_flag: track_work_items_activity diff --git a/locale/gitlab.pot b/locale/gitlab.pot index cf1a3becf8efd5ce259ace241c5b4dc5780d9340..34cdc9c4553615f2382f94f9b497d94fdafb3077 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -143,6 +143,11 @@ msgid_plural "%d additional users" msgstr[0] "" msgstr[1] "" +msgid "%d approval required" +msgid_plural "%d approvals required" +msgstr[0] "" +msgstr[1] "" + msgid "%d approver" msgid_plural "%d approvers" msgstr[0] "" @@ -6826,6 +6831,9 @@ msgstr "" msgid "BranchRules|Approvals" msgstr "" +msgid "BranchRules|Approvals to ensure separation of duties for new merge requests. %{linkStart}Lean more.%{linkEnd}" +msgstr "" + msgid "BranchRules|Branch" msgstr "" @@ -6853,6 +6861,9 @@ msgstr "" msgid "BranchRules|Keep stable branches secure and force developers to use merge requests. %{linkStart}What are protected branches?%{linkEnd}" msgstr "" +msgid "BranchRules|Manage in Merge Request Approvals" +msgstr "" + msgid "BranchRules|Manage in Protected Branches" msgstr "" @@ -6874,6 +6885,9 @@ msgstr "" msgid "BranchRules|Require approval from code owners." msgstr "" +msgid "BranchRules|Required approvals (%{total})" +msgstr "" + msgid "BranchRules|Roles" msgstr "" diff --git a/qa/Gemfile b/qa/Gemfile index 5817dcd2f901731a71307b7426cf8413bbaa389a..ab48865af5e9edef57b98e1a5265c12d86e9d1c9 100644 --- a/qa/Gemfile +++ b/qa/Gemfile @@ -18,7 +18,7 @@ gem 'faker', '~> 2.19', '>= 2.19.0' gem 'knapsack', '~> 4.0' gem 'parallel_tests', '~> 2.29' gem 'rotp', '~> 3.1.0' -gem 'timecop', '~> 0.9.1' +gem 'timecop', '~> 0.9.5' gem 'parallel', '~> 1.19' gem 'rainbow', '~> 3.0.0' gem 'rspec-parameterized', '~> 0.4.2' @@ -45,5 +45,5 @@ gem 'deprecation_toolkit', '~> 1.5.1', require: false group :development do gem 'pry-byebug', '~> 3.5.1', platform: :mri - gem "ruby-debug-ide", "~> 0.7.0" + gem "ruby-debug-ide", "~> 0.7.3" end diff --git a/qa/Gemfile.lock b/qa/Gemfile.lock index 9917df25a65ca1c8436eb7c959f51482e70c6279..232099e0f6e33403bf7efeb0308634e7cd32869e 100644 --- a/qa/Gemfile.lock +++ b/qa/Gemfile.lock @@ -255,7 +255,7 @@ GEM rspec-support (3.10.2) rspec_junit_formatter (0.4.1) rspec-core (>= 2, < 4, != 2.12.0) - ruby-debug-ide (0.7.2) + ruby-debug-ide (0.7.3) rake (>= 0.8.1) ruby2_keywords (0.0.5) rubyzip (2.3.2) @@ -277,7 +277,7 @@ GEM terminal-table (3.0.2) unicode-display_width (>= 1.1.1, < 3) thread_safe (0.3.6) - timecop (0.9.1) + timecop (0.9.5) trailblazer-option (0.1.2) tzinfo (2.0.5) concurrent-ruby (~> 1.0) @@ -342,11 +342,11 @@ DEPENDENCIES rspec-parameterized (~> 0.4.2) rspec-retry (~> 0.6.1) rspec_junit_formatter (~> 0.4.1) - ruby-debug-ide (~> 0.7.0) + ruby-debug-ide (~> 0.7.3) selenium-webdriver (~> 4.0) slack-notifier (~> 2.4) terminal-table (~> 3.0.0) - timecop (~> 0.9.1) + timecop (~> 0.9.5) warning (~> 1.3) webdrivers (~> 5.2) zeitwerk (~> 2.4) diff --git a/scripts/api/pipeline_failed_jobs.rb b/scripts/api/pipeline_failed_jobs.rb index 3c29e8842d3ea123a15fc1fdb85b538761c28924..c25567af69844b4d6ee6dde11e622c3e1640cb9e 100644 --- a/scripts/api/pipeline_failed_jobs.rb +++ b/scripts/api/pipeline_failed_jobs.rb @@ -31,6 +31,13 @@ def execute failed_jobs << job end + client.pipeline_bridges(project, pipeline_id, scope: 'failed', per_page: 100).auto_paginate do |job| + next if exclude_allowed_to_fail_jobs && job.allow_failure + + job.web_url = job.downstream_pipeline.web_url # job.web_url is linking to an invalid page + failed_jobs << job + end + failed_jobs end diff --git a/spec/frontend/projects/settings/branch_rules/components/view/index_spec.js b/spec/frontend/projects/settings/branch_rules/components/view/index_spec.js index 3176b28d5474a356eaf89baa11a2f85bdc2c6ab1..bf4026b65db59ed350f6408ed1c81b9c39a28b5f 100644 --- a/spec/frontend/projects/settings/branch_rules/components/view/index_spec.js +++ b/spec/frontend/projects/settings/branch_rules/components/view/index_spec.js @@ -33,6 +33,7 @@ describe('View branch rules', () => { let fakeApollo; const projectPath = 'test/testing'; const protectedBranchesPath = 'protected/branches'; + const approvalRulesPath = 'approval/rules'; const branchProtectionsMockRequestHandler = jest .fn() .mockResolvedValue(branchProtectionsMockResponse); @@ -42,7 +43,7 @@ describe('View branch rules', () => { wrapper = shallowMountExtended(RuleView, { apolloProvider: fakeApollo, - provide: { projectPath, protectedBranchesPath }, + provide: { projectPath, protectedBranchesPath, approvalRulesPath }, }); await waitForPromises(); @@ -57,6 +58,7 @@ describe('View branch rules', () => { const findBranchProtectionTitle = () => wrapper.findByText(I18N.protectBranchTitle); const findBranchProtections = () => wrapper.findAllComponents(Protection); const findForcePushTitle = () => wrapper.findByText(I18N.allowForcePushDescription); + const findApprovalsTitle = () => wrapper.findByText(I18N.approvalsTitle); it('gets the branch param from url and renders it in the view', () => { expect(util.getParameterByName).toHaveBeenCalledWith('branch'); @@ -98,4 +100,14 @@ describe('View branch rules', () => { ...protectionMockProps, }); }); + + it('renders a branch protection component for approvals', () => { + expect(findApprovalsTitle().exists()).toBe(true); + + expect(findBranchProtections().at(2).props()).toMatchObject({ + header: sprintf(I18N.approvalsHeader, { total: 0 }), + headerLinkHref: approvalRulesPath, + headerLinkTitle: I18N.manageApprovalsLinkTitle, + }); + }); }); diff --git a/spec/frontend/projects/settings/branch_rules/components/view/mock_data.js b/spec/frontend/projects/settings/branch_rules/components/view/mock_data.js index c5774977205f6276d7944ee4e04ed6674bff79cf..c3f573061da7c2e71c5ff52ed9c6be899ba206bf 100644 --- a/spec/frontend/projects/settings/branch_rules/components/view/mock_data.js +++ b/spec/frontend/projects/settings/branch_rules/components/view/mock_data.js @@ -36,6 +36,8 @@ const accessLevelsMock = [ { accessLevelDescription: 'Maintainer' }, ]; +const approvalsRequired = 3; + const groupsMock = [{ name: 'test_group_1' }, { name: 'test_group_2' }]; export const protectionPropsMock = { @@ -45,12 +47,20 @@ export const protectionPropsMock = { roles: accessLevelsMock, users: usersMock, groups: groupsMock, + approvals: [ + { + name: 'test', + eligibleApprovers: { nodes: usersMock }, + approvalsRequired, + }, + ], }; export const protectionRowPropsMock = { title: 'Test title', users: usersMock, accessLevels: accessLevelsMock, + approvalsRequired, }; export const accessLevelsMockResponse = [ diff --git a/spec/frontend/projects/settings/branch_rules/components/view/protection_row_spec.js b/spec/frontend/projects/settings/branch_rules/components/view/protection_row_spec.js index 7770e1fb2aa7a597ad71482d51a3177644b57d83..b0a69bedd3e0a691f87891d7ed8a01ba5a37ca85 100644 --- a/spec/frontend/projects/settings/branch_rules/components/view/protection_row_spec.js +++ b/spec/frontend/projects/settings/branch_rules/components/view/protection_row_spec.js @@ -25,6 +25,8 @@ describe('Branch rule protection row', () => { const findAvatarLinks = () => wrapper.findAllComponents(GlAvatarLink); const findAvatars = () => wrapper.findAllComponents(GlAvatar); const findAccessLevels = () => wrapper.findAllByTestId('access-level'); + const findApprovalsRequired = () => + wrapper.findByText(`${protectionRowPropsMock.approvalsRequired} approvals required`); it('renders a title', () => { expect(findTitle().exists()).toBe(true); @@ -62,4 +64,8 @@ describe('Branch rule protection row', () => { protectionRowPropsMock.accessLevels[1].accessLevelDescription, ); }); + + it('renders the number of approvals required', () => { + expect(findApprovalsRequired().exists()).toBe(true); + }); }); diff --git a/spec/frontend/projects/settings/branch_rules/components/view/protection_spec.js b/spec/frontend/projects/settings/branch_rules/components/view/protection_spec.js index 91d16fd86a6e1ee2d34d3cefc675b6b0dcd7379e..e2fbb4f5bbb05d00364936a5f8b2813e59fafc45 100644 --- a/spec/frontend/projects/settings/branch_rules/components/view/protection_spec.js +++ b/spec/frontend/projects/settings/branch_rules/components/view/protection_spec.js @@ -56,4 +56,13 @@ describe('Branch rule protection', () => { title: i18n.groupsTitle, }); }); + + it('renders a protection row for approvals', () => { + const approval = protectionPropsMock.approvals[0]; + expect(findProtectionRows().at(3).props()).toMatchObject({ + title: approval.name, + users: approval.eligibleApprovers.nodes, + approvalsRequired: approval.approvalsRequired, + }); + }); }); diff --git a/spec/lib/gitlab/usage/metrics/instrumentations/work_items_activity_aggregated_metric_spec.rb b/spec/lib/gitlab/usage/metrics/instrumentations/work_items_activity_aggregated_metric_spec.rb index c13a5ba4d7236cddf88e046ea7979751955a9be2..3e315692d0a2b69e49b267f8d556765b7cfbeb4c 100644 --- a/spec/lib/gitlab/usage/metrics/instrumentations/work_items_activity_aggregated_metric_spec.rb +++ b/spec/lib/gitlab/usage/metrics/instrumentations/work_items_activity_aggregated_metric_spec.rb @@ -6,7 +6,7 @@ let(:metric_definition) do { data_source: 'redis_hll', - time_frame: '7d', + time_frame: time_frame, options: { aggregate: { operator: 'OR' @@ -15,6 +15,7 @@ users_creating_work_items users_updating_work_item_title users_updating_work_item_dates + users_updating_work_item_iteration ] } } @@ -24,31 +25,36 @@ freeze_time { example.run } end - describe '#available?' do - it 'returns false without track_work_items_activity feature' do - stub_feature_flags(track_work_items_activity: false) + where(:time_frame) { [['28d'], ['7d']] } - expect(described_class.new(metric_definition).available?).to eq(false) - end + with_them do + describe '#available?' do + it 'returns false without track_work_items_activity feature' do + stub_feature_flags(track_work_items_activity: false) + + expect(described_class.new(metric_definition).available?).to eq(false) + end - it 'returns true with track_work_items_activity feature' do - stub_feature_flags(track_work_items_activity: true) + it 'returns true with track_work_items_activity feature' do + stub_feature_flags(track_work_items_activity: true) - expect(described_class.new(metric_definition).available?).to eq(true) + expect(described_class.new(metric_definition).available?).to eq(true) + end end - end - describe '#value', :clean_gitlab_redis_shared_state do - let(:counter) { Gitlab::UsageDataCounters::HLLRedisCounter } + describe '#value', :clean_gitlab_redis_shared_state do + let(:counter) { Gitlab::UsageDataCounters::HLLRedisCounter } - before do - counter.track_event(:users_creating_work_items, values: 1, time: 1.week.ago) - counter.track_event(:users_updating_work_item_title, values: 1, time: 1.week.ago) - counter.track_event(:users_updating_work_item_dates, values: 2, time: 1.week.ago) - end + before do + counter.track_event(:users_creating_work_items, values: 1, time: 1.week.ago) + counter.track_event(:users_updating_work_item_title, values: 1, time: 1.week.ago) + counter.track_event(:users_updating_work_item_dates, values: 2, time: 1.week.ago) + counter.track_event(:users_updating_work_item_iteration, values: 2, time: 1.week.ago) + end - it 'has correct value' do - expect(described_class.new(metric_definition).value).to eq 2 + it 'has correct value' do + expect(described_class.new(metric_definition).value).to eq 2 + end end end end diff --git a/spec/migrations/populate_releases_access_level_from_repository_spec.rb b/spec/migrations/populate_releases_access_level_from_repository_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..2bb97662923c09994b34f90e5b499e1dbd146654 --- /dev/null +++ b/spec/migrations/populate_releases_access_level_from_repository_spec.rb @@ -0,0 +1,39 @@ +# frozen_string_literal: true + +require 'spec_helper' + +require_migration! + +RSpec.describe PopulateReleasesAccessLevelFromRepository, :migration do + let(:projects) { table(:projects) } + let(:groups) { table(:namespaces) } + let(:project_features) { table(:project_features) } + + let(:group) { groups.create!(name: 'test-group', path: 'test-group') } + let(:project) { projects.create!(namespace_id: group.id, project_namespace_id: group.id) } + let(:project_feature) do + project_features.create!(project_id: project.id, pages_access_level: 20, **project_feature_attributes) + end + + # repository_access_level and releases_access_level default to ENABLED + describe '#up' do + context 'when releases_access_level is greater than repository_access_level' do + let(:project_feature_attributes) { { repository_access_level: ProjectFeature::PRIVATE } } + + it 'reduces releases_access_level to match repository_access_level' do + expect { migrate! }.to change { project_feature.reload.releases_access_level } + .from(ProjectFeature::ENABLED) + .to(ProjectFeature::PRIVATE) + end + end + + context 'when releases_access_level is less than repository_access_level' do + let(:project_feature_attributes) { { releases_access_level: ProjectFeature::DISABLED } } + + it 'does not change releases_access_level' do + expect { migrate! }.not_to change { project_feature.reload.releases_access_level } + .from(ProjectFeature::DISABLED) + end + end + end +end diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb index ec0fe456d990fe2fe67ba2326df8de0768ebed32..9713734e97ab2ebc817e8f9aa093956d1baaceab 100644 --- a/spec/models/ci/build_spec.rb +++ b/spec/models/ci/build_spec.rb @@ -632,15 +632,6 @@ it { expect(subject).to be_falsey } end - context 'when prevent_outdated_deployment_jobs FF is disabled' do - before do - stub_feature_flags(prevent_outdated_deployment_jobs: false) - expect(build.deployment).not_to receive(:rollback?) - end - - it { expect(subject).to be_falsey } - end - context 'when build can prevent rollback deployment' do before do expect(build.deployment).to receive(:older_than_last_successful_deployment?).and_return(true) diff --git a/spec/models/deployment_spec.rb b/spec/models/deployment_spec.rb index 87fa5289795b0b9be9ea97ecf57f478291ff4259..bf1cf9856a0856c43ef90a50cff8a995f3e2224e 100644 --- a/spec/models/deployment_spec.rb +++ b/spec/models/deployment_spec.rb @@ -171,11 +171,22 @@ end it 'executes Deployments::DropOlderDeploymentsWorker asynchronously' do + stub_feature_flags(prevent_outdated_deployment_jobs: false) + expect(Deployments::DropOlderDeploymentsWorker) .to receive(:perform_async).once.with(deployment.id) deployment.run! end + + it 'does not execute Deployments::DropOlderDeploymentsWorker when FF enabled' do + stub_feature_flags(prevent_outdated_deployment_jobs: true) + + expect(Deployments::DropOlderDeploymentsWorker) + .not_to receive(:perform_async).with(deployment.id) + + deployment.run! + end end context 'when deployment succeeded' do diff --git a/spec/models/namespace/aggregation_schedule_spec.rb b/spec/models/namespace/aggregation_schedule_spec.rb index 3f6a890654a397f9ce9d7eba806415de420e627c..45b66fa12dd5fe89352c237343aa4be417a5e4a9 100644 --- a/spec/models/namespace/aggregation_schedule_spec.rb +++ b/spec/models/namespace/aggregation_schedule_spec.rb @@ -12,14 +12,14 @@ describe "#default_lease_timeout" do subject(:default_lease_timeout) { default_timeout } - it { is_expected.to eq 1.hour.to_i } + it { is_expected.to eq 30.minutes.to_i } context 'when remove_namespace_aggregator_delay FF is disabled' do before do stub_feature_flags(remove_namespace_aggregator_delay: false) end - it { is_expected.to eq 1.5.hours.to_i } + it { is_expected.to eq 1.hour.to_i } end end diff --git a/workhorse/go.mod b/workhorse/go.mod index d2d5e4b44f9477ee31529c28fc45f1ef2333dc14..0843fe481305d8dee3749e6ca8d085e7b2486a68 100644 --- a/workhorse/go.mod +++ b/workhorse/go.mod @@ -34,7 +34,7 @@ require ( golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 golang.org/x/net v0.0.0-20220722155237-a158d28d115b golang.org/x/tools v0.1.12 - google.golang.org/grpc v1.50.0 + google.golang.org/grpc v1.50.1 google.golang.org/protobuf v1.28.1 honnef.co/go/tools v0.3.3 ) diff --git a/workhorse/go.sum b/workhorse/go.sum index 3795e8aae7a9bbecce4703a39610df1a92701fdd..9515f7d0384ac2d498e58f6f338466adfac99307 100644 --- a/workhorse/go.sum +++ b/workhorse/go.sum @@ -1550,8 +1550,8 @@ google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9K google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= -google.golang.org/grpc v1.50.0 h1:fPVVDxY9w++VjTZsYvXWqEf9Rqar/e+9zYfxKK+W+YU= -google.golang.org/grpc v1.50.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= +google.golang.org/grpc v1.50.1 h1:DS/BukOZWp8s6p4Dt/tOaJaTQyPyOoCcrjroHuCeLzY= +google.golang.org/grpc v1.50.1/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=