Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2022-10-17 09:10:44 +00:00
parent 7118851f02
commit b9b58dba70
48 changed files with 431 additions and 87 deletions

View File

@ -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

View File

@ -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';

View File

@ -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) -->

View File

@ -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>

View File

@ -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"
: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"
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>
<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
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 class="gl-ml-7 gl-w-quarter">{{ approvalsRequiredTitle }}</div>
</div>
</div>
</template>

View File

@ -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);

View File

@ -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() {

View File

@ -53,6 +53,11 @@ export default {
required: false,
default: false,
},
allowMultipleScopedLabels: {
type: Boolean,
required: false,
default: false,
},
variant: {
type: String,
required: false,
@ -164,6 +169,7 @@ export default {
allowLabelCreate: this.allowLabelCreate,
allowMultiselect: this.allowMultiselect,
allowScopedLabels: this.allowScopedLabels,
allowMultipleScopedLabels: this.allowMultipleScopedLabels,
dropdownButtonText: this.dropdownButtonText,
selectedLabels: this.selectedLabels,
labelsFetchPath: this.labelsFetchPath,

View File

@ -94,14 +94,13 @@ export default {
candidateLabel.indeterminate = false;
}
if (isScopedLabel(candidateLabel)) {
if (isScopedLabel(candidateLabel) && !state.allowMultipleScopedLabels) {
const currentActiveScopedLabel = state.labels.find(
({ set, title }) =>
set &&
title !== candidateLabel.title &&
scopedLabelKey({ title }) === scopedLabelKey(candidateLabel),
);
if (currentActiveScopedLabel) {
currentActiveScopedLabel.set = false;
}

View File

@ -19,7 +19,7 @@ query mergeRequestReviewers($fullPath: ID!, $iid: String!) {
}
}
userPermissions {
updateMergeRequest
adminMergeRequest
}
}
}

View File

@ -455,8 +455,7 @@ module Ci
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

View File

@ -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)

View File

@ -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

View File

@ -25,6 +25,8 @@ module Ci
end
def enqueue(build)
return build.drop!(:failed_outdated_deployment_job) if build.prevent_rollback_deployment?
build.enqueue
end

View File

@ -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') } }

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -0,0 +1 @@
bc05939dc672c078161cd9b7dbd7f92601edb6888a77c62adb014964e30c6ae8

View File

@ -0,0 +1 @@
58ee7f51a0da4ee4ec471d4492106d1fc3124419ba83591913967d6bd38105e5

View File

@ -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);

View File

@ -248,7 +248,6 @@ GitLab SaaS runners have different cost factors, depending on the runner type (L
| Linux OS + Docker executor| Small |1|
| Linux OS + Docker executor| Medium |2|
| Linux OS + Docker executor| Large |3|
| macOS + shell executor | Large| 6 |
### Monthly reset of CI/CD minutes

View File

@ -39,6 +39,9 @@ To add a pipeline schedule:
These variables are available only when the scheduled pipeline runs,
and not in any other pipeline run.
If the project already has the [maximum number of pipeline schedules](../../administration/instance_limits.md#number-of-pipeline-schedules),
you must delete unused schedules before you can add another.
## Edit a pipeline schedule
> Introduced in GitLab 14.8, only a pipeline schedule owner can edit the schedule.

View File

@ -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:

View File

@ -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

View File

@ -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 ""
@ -9841,6 +9855,9 @@ msgstr ""
msgid "Complete verification to sign up."
msgstr ""
msgid "Complete with errors"
msgstr ""
msgid "Completed"
msgstr ""
@ -12572,6 +12589,9 @@ msgstr ""
msgid "Decrease"
msgstr ""
msgid "Default - Never run"
msgstr ""
msgid "Default CI/CD configuration file"
msgstr ""
@ -21985,6 +22005,9 @@ msgstr ""
msgid "Invalid yaml"
msgstr ""
msgid "Invalidated"
msgstr ""
msgid "Investigate vulnerability: %{title}"
msgstr ""
@ -30441,6 +30464,27 @@ msgstr ""
msgid "Pre-defined push rules"
msgstr ""
msgid "PreScanVerification|(optional)"
msgstr ""
msgid "PreScanVerification|Last run %{timeAgo} in pipeline"
msgstr ""
msgid "PreScanVerification|Pre-scan verification"
msgstr ""
msgid "PreScanVerification|Started %{timeAgo} in pipeline"
msgstr ""
msgid "PreScanVerification|Test your configuration and identify potential errors before running a full scan."
msgstr ""
msgid "PreScanVerification|Verify configuration"
msgstr ""
msgid "PreScanVerification|View results"
msgstr ""
msgid "Preferences"
msgstr ""

View File

@ -16,9 +16,9 @@ gem 'rspec-retry', '~> 0.6.1', require: 'rspec/retry'
gem 'rspec_junit_formatter', '~> 0.4.1'
gem 'faker', '~> 2.19', '>= 2.19.0'
gem 'knapsack', '~> 4.0'
gem 'parallel_tests', '~> 2.29'
gem 'parallel_tests', '~> 2.32'
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

View File

@ -196,7 +196,7 @@ GEM
oj (3.13.11)
os (1.1.4)
parallel (1.19.2)
parallel_tests (2.29.0)
parallel_tests (2.32.0)
parallel
parser (3.1.2.1)
ast (~> 2.4.1)
@ -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)
@ -332,7 +332,7 @@ DEPENDENCIES
nokogiri (~> 1.13, >= 1.13.8)
octokit (~> 5.6.1)
parallel (~> 1.19)
parallel_tests (~> 2.29)
parallel_tests (~> 2.32)
pry-byebug (~> 3.5.1)
rainbow (~> 3.0.0)
rake (~> 13)
@ -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)

View File

@ -31,6 +31,13 @@ class PipelineFailedJobs
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

View File

@ -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,
});
});
});

View File

@ -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 = [

View File

@ -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);
});
});

View File

@ -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,
});
});
});

View File

@ -193,6 +193,16 @@ describe('LabelsSelect Mutations', () => {
expect(state.labels[l.id - 1].set).toBe(false);
});
});
it('allows selection of multiple scoped labels', () => {
const state = { labels: cloneDeep(labels), allowMultipleScopedLabels: true };
mutations[types.UPDATE_SELECTED_LABELS](state, { labels: [{ id: labels[4].id }] });
mutations[types.UPDATE_SELECTED_LABELS](state, { labels: [{ id: labels[5].id }] });
expect(state.labels[4].set).toBe(true);
expect(state.labels[5].set).toBe(true);
expect(state.labels[6].set).toBe(true);
});
});
describe(`${types.UPDATE_LABELS_SET_STATE}`, () => {

View File

@ -6,7 +6,7 @@ RSpec.describe Gitlab::Usage::Metrics::Instrumentations::WorkItemsActivityAggreg
let(:metric_definition) do
{
data_source: 'redis_hll',
time_frame: '7d',
time_frame: time_frame,
options: {
aggregate: {
operator: 'OR'
@ -15,6 +15,7 @@ RSpec.describe Gitlab::Usage::Metrics::Instrumentations::WorkItemsActivityAggreg
users_creating_work_items
users_updating_work_item_title
users_updating_work_item_dates
users_updating_work_item_iteration
]
}
}
@ -24,31 +25,36 @@ RSpec.describe Gitlab::Usage::Metrics::Instrumentations::WorkItemsActivityAggreg
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)
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)
expect(described_class.new(metric_definition).available?).to eq(true)
end
end
it 'returns true with track_work_items_activity feature' do
stub_feature_flags(track_work_items_activity: true)
describe '#value', :clean_gitlab_redis_shared_state do
let(:counter) { Gitlab::UsageDataCounters::HLLRedisCounter }
expect(described_class.new(metric_definition).available?).to eq(true)
end
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
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
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

View File

@ -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

View File

@ -632,15 +632,6 @@ RSpec.describe Ci::Build do
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)

View File

@ -171,11 +171,22 @@ RSpec.describe Deployment do
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

View File

@ -12,14 +12,14 @@ RSpec.describe Namespace::AggregationSchedule, :clean_gitlab_redis_shared_state,
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

View File

@ -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
)

View File

@ -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=