Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--GITALY_SERVER_VERSION2
-rw-r--r--app/assets/javascripts/flash.js2
-rw-r--r--app/assets/javascripts/issuable/auto_width_dropdown_select.js56
-rw-r--r--app/assets/javascripts/issuable/issuable_form.js52
-rw-r--r--app/assets/stylesheets/page_bundles/merge_requests.scss4
-rw-r--r--app/controllers/concerns/registrations_tracking.rb15
-rw-r--r--app/controllers/groups/runners_controller.rb13
-rw-r--r--app/controllers/projects/merge_requests_controller.rb5
-rw-r--r--app/controllers/registrations/welcome_controller.rb1
-rw-r--r--app/controllers/registrations_controller.rb8
-rw-r--r--app/graphql/types/projects/branch_rule_type.rb7
-rw-r--r--app/models/ci/build_metadata.rb2
-rw-r--r--app/models/concerns/ci/partitionable.rb27
-rw-r--r--app/models/protected_branch.rb4
-rw-r--r--app/services/merge_requests/merge_base_service.rb4
-rw-r--r--app/services/packages/rpm/repository_metadata/base_builder.rb2
-rw-r--r--app/services/packages/rpm/repository_metadata/build_repomd_xml.rb5
-rw-r--r--app/views/devise/registrations/new.html.haml2
-rw-r--r--app/views/registrations/welcome/show.html.haml4
-rw-r--r--db/post_migrate/20220928225711_schedule_update_ci_pipeline_artifacts_locked_status.rb27
-rw-r--r--db/schema_migrations/202209282257111
-rw-r--r--doc/administration/compliance.md4
-rw-r--r--doc/api/graphql/reference/index.md1
-rw-r--r--doc/architecture/blueprints/ci_data_decay/pipeline_partitioning.md15
-rw-r--r--doc/ci/yaml/index.md2
-rw-r--r--doc/gitlab-basics/start-using-git.md12
-rw-r--r--doc/user/admin_area/settings/continuous_integration.md2
-rw-r--r--doc/user/application_security/configuration/index.md15
-rw-r--r--doc/user/application_security/get-started-security.md2
-rw-r--r--doc/user/application_security/index.md2
-rw-r--r--doc/user/application_security/policies/scan-execution-policies.md2
-rw-r--r--doc/user/compliance/compliance_report/index.md2
-rw-r--r--doc/user/group/manage.md184
-rw-r--r--doc/user/permissions.md2
-rw-r--r--doc/user/profile/account/two_factor_authentication.md9
-rw-r--r--doc/user/project/settings/index.md190
-rw-r--r--doc/user/usage_quotas.md26
-rw-r--r--lib/gitlab/background_migration/update_ci_pipeline_artifacts_unknown_locked_status.rb24
-rw-r--r--locale/gitlab.pot4
-rw-r--r--qa/qa/tools/ci/qa_changes.rb33
-rw-r--r--qa/spec/tools/ci/qa_changes_spec.rb40
-rw-r--r--qa/tasks/ci.rake4
-rw-r--r--spec/controllers/groups/runners_controller_spec.rb89
-rw-r--r--spec/factories/ci/build_metadata.rb4
-rw-r--r--spec/factories/ci/builds.rb26
-rw-r--r--spec/factories/ci/pipelines.rb2
-rw-r--r--spec/features/milestones/user_creates_milestone_spec.rb99
-rw-r--r--spec/frontend/flash_spec.js7
-rw-r--r--spec/graphql/types/branch_rule_type_spec.rb1
-rw-r--r--spec/lib/gitlab/background_migration/update_ci_pipeline_artifacts_unknown_locked_status_spec.rb62
-rw-r--r--spec/migrations/20220928225711_schedule_update_ci_pipeline_artifacts_locked_status_spec.rb31
-rw-r--r--spec/models/ci/build_metadata_spec.rb57
-rw-r--r--spec/models/ci/build_spec.rb8
-rw-r--r--spec/models/ci/pipeline_spec.rb2
-rw-r--r--spec/models/concerns/ci/partitionable_spec.rb26
-rw-r--r--spec/models/protected_branch_spec.rb24
-rw-r--r--spec/requests/api/graphql/project/branch_rules_spec.rb68
-rw-r--r--spec/services/ci/job_artifacts/create_service_spec.rb3
-rw-r--r--spec/services/packages/rpm/repository_metadata/build_repomd_xml_spec.rb20
-rw-r--r--spec/support/models/partitionable_check.rb46
-rw-r--r--spec/views/registrations/welcome/show.html.haml_spec.rb1
61 files changed, 956 insertions, 438 deletions
diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION
index e50ed641116..1c20838c039 100644
--- a/GITALY_SERVER_VERSION
+++ b/GITALY_SERVER_VERSION
@@ -1 +1 @@
-4c15523cf680c107c5aa2b8268674cd0345a6b78
+7397b0e1c1a4a8fc0290347da1ddf7ac11547a18
diff --git a/app/assets/javascripts/flash.js b/app/assets/javascripts/flash.js
index edf83a33812..5665231e613 100644
--- a/app/assets/javascripts/flash.js
+++ b/app/assets/javascripts/flash.js
@@ -158,7 +158,7 @@ const createAlert = function createAlert({
onDismiss();
}
this.$destroy();
- this.$el.parentNode.removeChild(this.$el);
+ this.$el.parentNode?.removeChild(this.$el);
},
},
render(h) {
diff --git a/app/assets/javascripts/issuable/auto_width_dropdown_select.js b/app/assets/javascripts/issuable/auto_width_dropdown_select.js
deleted file mode 100644
index 243d82f55aa..00000000000
--- a/app/assets/javascripts/issuable/auto_width_dropdown_select.js
+++ /dev/null
@@ -1,56 +0,0 @@
-import $ from 'jquery';
-import { loadCSSFile } from '../lib/utils/css_utils';
-
-let instanceCount = 0;
-
-class AutoWidthDropdownSelect {
- constructor(selectElement) {
- this.$selectElement = $(selectElement);
- this.dropdownClass = `js-auto-width-select-dropdown-${instanceCount}`;
- instanceCount += 1;
- }
-
- init() {
- const { dropdownClass } = this;
- import(/* webpackChunkName: 'select2' */ 'select2/select2')
- .then(() => {
- // eslint-disable-next-line promise/no-nesting
- loadCSSFile(gon.select2_css_path)
- .then(() => {
- this.$selectElement.select2({
- dropdownCssClass: dropdownClass,
- ...AutoWidthDropdownSelect.selectOptions(this.dropdownClass),
- });
- })
- .catch(() => {});
- })
- .catch(() => {});
-
- return this;
- }
-
- static selectOptions(dropdownClass) {
- return {
- dropdownCss() {
- let resultantWidth = 'auto';
- const $dropdown = $(`.${dropdownClass}`);
-
- // We have to look at the parent because
- // `offsetParent` on a `display: none;` is `null`
- const offsetParentWidth = $(this).parent().offsetParent().width();
- // Reset any width to let it naturally flow
- $dropdown.css('width', 'auto');
- if ($dropdown.outerWidth(false) > offsetParentWidth) {
- resultantWidth = offsetParentWidth;
- }
-
- return {
- width: resultantWidth,
- maxWidth: offsetParentWidth,
- };
- },
- };
- }
-}
-
-export default AutoWidthDropdownSelect;
diff --git a/app/assets/javascripts/issuable/issuable_form.js b/app/assets/javascripts/issuable/issuable_form.js
index 81bf7ca6ccc..e8ba99e0e9e 100644
--- a/app/assets/javascripts/issuable/issuable_form.js
+++ b/app/assets/javascripts/issuable/issuable_form.js
@@ -2,10 +2,7 @@ import $ from 'jquery';
import Pikaday from 'pikaday';
import GfmAutoComplete from 'ee_else_ce/gfm_auto_complete';
import Autosave from '~/autosave';
-import AutoWidthDropdownSelect from '~/issuable/auto_width_dropdown_select';
-import { loadCSSFile } from '~/lib/utils/css_utils';
import { parsePikadayDate, pikadayToString } from '~/lib/utils/datetime_utility';
-import { select2AxiosTransport } from '~/lib/utils/select2_utils';
import { queryToObject, objectToQuery } from '~/lib/utils/url_utility';
import UsersSelect from '~/users_select';
import ZenMode from '~/zen_mode';
@@ -118,12 +115,6 @@ export default class IssuableForm {
});
calendar.setDate(parsePikadayDate($issuableDueDate.val()));
}
-
- this.$targetBranchSelect = $('.js-target-branch-select', this.form);
-
- if (this.$targetBranchSelect.length) {
- this.initTargetBranchDropdown();
- }
}
initAutosave() {
@@ -214,47 +205,4 @@ export default class IssuableForm {
addWip() {
this.titleField.val(`Draft: ${this.titleField.val()}`);
}
-
- initTargetBranchDropdown() {
- import(/* webpackChunkName: 'select2' */ 'select2/select2')
- .then(() => {
- // eslint-disable-next-line promise/no-nesting
- loadCSSFile(gon.select2_css_path)
- .then(() => {
- this.$targetBranchSelect.select2({
- ...AutoWidthDropdownSelect.selectOptions('js-target-branch-select'),
- ajax: {
- url: this.$targetBranchSelect.data('endpoint'),
- dataType: 'JSON',
- quietMillis: 250,
- data(search) {
- return {
- search,
- };
- },
- results({ results }) {
- return {
- // `data` keys are translated so we can't just access them with a string based key
- results: results[Object.keys(results)[0]].map((name) => ({
- id: name,
- text: name,
- })),
- };
- },
- transport: select2AxiosTransport,
- },
- initSelection(el, callback) {
- const val = el.val();
-
- callback({
- id: val,
- text: val,
- });
- },
- });
- })
- .catch(() => {});
- })
- .catch(() => {});
- }
}
diff --git a/app/assets/stylesheets/page_bundles/merge_requests.scss b/app/assets/stylesheets/page_bundles/merge_requests.scss
index 9550b5440d6..7bef2934371 100644
--- a/app/assets/stylesheets/page_bundles/merge_requests.scss
+++ b/app/assets/stylesheets/page_bundles/merge_requests.scss
@@ -834,6 +834,10 @@ $tabs-holder-z-index: 250;
@include gl-ml-auto;
@include gl-rounded-pill;
@include gl-w-9;
+
+ &.is-checked:hover {
+ background-color: $blue-500;
+ }
}
}
diff --git a/app/controllers/concerns/registrations_tracking.rb b/app/controllers/concerns/registrations_tracking.rb
new file mode 100644
index 00000000000..14743349c1a
--- /dev/null
+++ b/app/controllers/concerns/registrations_tracking.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+module RegistrationsTracking
+ extend ActiveSupport::Concern
+
+ included do
+ helper_method :glm_tracking_params
+ end
+
+ private
+
+ def glm_tracking_params
+ params.permit(:glm_source, :glm_content)
+ end
+end
diff --git a/app/controllers/groups/runners_controller.rb b/app/controllers/groups/runners_controller.rb
index 652f12e34ba..0ca0fa729c0 100644
--- a/app/controllers/groups/runners_controller.rb
+++ b/app/controllers/groups/runners_controller.rb
@@ -2,7 +2,7 @@
class Groups::RunnersController < Groups::ApplicationController
before_action :authorize_read_group_runners!, only: [:index, :show]
- before_action :authorize_admin_group_runners!, only: [:edit, :update, :destroy, :pause, :resume]
+ before_action :authorize_update_runner!, only: [:edit, :update, :destroy, :pause, :resume]
before_action :runner, only: [:edit, :update, :destroy, :pause, :resume, :show]
before_action only: [:show] do
@@ -37,7 +37,10 @@ class Groups::RunnersController < Groups::ApplicationController
private
def runner
- @runner ||= Ci::RunnersFinder.new(current_user: current_user, params: { group: @group }).execute
+ group_params = { group: @group }
+ group_params[:membership] = :all_available if Feature.enabled?(:runners_finder_all_available, @group)
+
+ @runner ||= Ci::RunnersFinder.new(current_user: current_user, params: group_params).execute
.except(:limit, :offset)
.find(params[:id])
end
@@ -45,6 +48,12 @@ class Groups::RunnersController < Groups::ApplicationController
def runner_params
params.require(:runner).permit(Ci::Runner::FORM_EDITABLE)
end
+
+ def authorize_update_runner!
+ return if can?(current_user, :admin_group_runners, group) && can?(current_user, :update_runner, runner)
+
+ render_404
+ end
end
Groups::RunnersController.prepend_mod
diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb
index 5a212e9a152..8abffa4f2bd 100644
--- a/app/controllers/projects/merge_requests_controller.rb
+++ b/app/controllers/projects/merge_requests_controller.rb
@@ -451,15 +451,16 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
return :failed
end
+ squashing = params.fetch(:squash, false)
merge_service = ::MergeRequests::MergeService.new(project: @project, current_user: current_user, params: merge_params)
- unless merge_service.hooks_validation_pass?(@merge_request)
+ unless merge_service.hooks_validation_pass?(@merge_request, validate_squash_message: squashing)
return :hook_validation_error
end
return :sha_mismatch if params[:sha] != @merge_request.diff_head_sha
- @merge_request.update(merge_error: nil, squash: params.fetch(:squash, false))
+ @merge_request.update(merge_error: nil, squash: squashing)
if auto_merge_requested?
if merge_request.auto_merge_enabled?
diff --git a/app/controllers/registrations/welcome_controller.rb b/app/controllers/registrations/welcome_controller.rb
index 829905db955..a49b82319da 100644
--- a/app/controllers/registrations/welcome_controller.rb
+++ b/app/controllers/registrations/welcome_controller.rb
@@ -4,6 +4,7 @@ module Registrations
class WelcomeController < ApplicationController
include OneTrustCSP
include GoogleAnalyticsCSP
+ include RegistrationsTracking
layout 'minimal'
skip_before_action :authenticate_user!, :required_signup_info, :check_two_factor_requirement, only: [:show, :update]
diff --git a/app/controllers/registrations_controller.rb b/app/controllers/registrations_controller.rb
index f931a4b3018..31fe30f3f06 100644
--- a/app/controllers/registrations_controller.rb
+++ b/app/controllers/registrations_controller.rb
@@ -8,6 +8,7 @@ class RegistrationsController < Devise::RegistrationsController
include OneTrustCSP
include BizibleCSP
include GoogleAnalyticsCSP
+ include RegistrationsTracking
layout 'devise'
@@ -114,13 +115,18 @@ class RegistrationsController < Devise::RegistrationsController
def after_sign_up_path_for(user)
Gitlab::AppLogger.info(user_created_message(confirmed: user.confirmed?))
- users_sign_up_welcome_path
+ users_sign_up_welcome_path(glm_tracking_params)
end
def after_inactive_sign_up_path_for(resource)
Gitlab::AppLogger.info(user_created_message)
return new_user_session_path(anchor: 'login-pane') if resource.blocked_pending_approval?
return dashboard_projects_path if Feature.enabled?(:soft_email_confirmation)
+
+ # when email confirmation is enabled, path to redirect is saved
+ # after user confirms and comes back, he will be redirected
+ store_location_for(:redirect, users_sign_up_welcome_path(glm_tracking_params))
+
return identity_verification_redirect_path if custom_confirmation_enabled?(resource)
users_almost_there_path(email: resource.email)
diff --git a/app/graphql/types/projects/branch_rule_type.rb b/app/graphql/types/projects/branch_rule_type.rb
index 866cff0f439..1b0a080bd5d 100644
--- a/app/graphql/types/projects/branch_rule_type.rb
+++ b/app/graphql/types/projects/branch_rule_type.rb
@@ -13,6 +13,13 @@ module Types
null: false,
description: 'Branch name, with wildcards, for the branch rules.'
+ field :is_default,
+ type: GraphQL::Types::Boolean,
+ null: false,
+ method: :default_branch?,
+ calls_gitaly: true,
+ description: "Check if this branch rule protects the project's default branch."
+
field :branch_protection,
type: Types::BranchRules::BranchProtectionType,
null: false,
diff --git a/app/models/ci/build_metadata.rb b/app/models/ci/build_metadata.rb
index 98d829e9253..6d412d49083 100644
--- a/app/models/ci/build_metadata.rb
+++ b/app/models/ci/build_metadata.rb
@@ -6,12 +6,14 @@ module Ci
class BuildMetadata < Ci::ApplicationRecord
BuildTimeout = Struct.new(:value, :source)
+ include Ci::Partitionable
include Presentable
include ChronicDurationAttribute
include Gitlab::Utils::StrongMemoize
self.table_name = 'ci_builds_metadata'
self.primary_key = 'id'
+ partitionable scope: :build
belongs_to :build, class_name: 'CommitStatus'
belongs_to :project
diff --git a/app/models/concerns/ci/partitionable.rb b/app/models/concerns/ci/partitionable.rb
index 710ee1ba64f..df803180e77 100644
--- a/app/models/concerns/ci/partitionable.rb
+++ b/app/models/concerns/ci/partitionable.rb
@@ -19,7 +19,32 @@ module Ci
extend ActiveSupport::Concern
include ::Gitlab::Utils::StrongMemoize
+ module Testing
+ InclusionError = Class.new(StandardError)
+
+ PARTITIONABLE_MODELS = %w[
+ CommitStatus
+ Ci::BuildMetadata
+ Ci::Stage
+ Ci::JobArtifact
+ Ci::PipelineVariable
+ Ci::Pipeline
+ ].freeze
+
+ def self.check_inclusion(klass)
+ return if PARTITIONABLE_MODELS.include?(klass.name)
+
+ raise Partitionable::Testing::InclusionError,
+ "#{klass} must be included in PARTITIONABLE_MODELS"
+
+ rescue InclusionError => e
+ Gitlab::ErrorTracking.track_and_raise_for_dev_exception(e)
+ end
+ end
+
included do
+ Partitionable::Testing.check_inclusion(self)
+
before_validation :set_partition_id, on: :create
validates :partition_id, presence: true
@@ -37,6 +62,8 @@ module Ci
def partitionable(scope:)
define_method(:partition_scope_value) do
strong_memoize(:partition_scope_value) do
+ next Ci::Pipeline.current_partition_value if respond_to?(:importing?) && importing?
+
record = scope.to_proc.call(self)
record.respond_to?(:partition_id) ? record.partition_id : record
end
diff --git a/app/models/protected_branch.rb b/app/models/protected_branch.rb
index b3a918d8952..dfd5c315f6e 100644
--- a/app/models/protected_branch.rb
+++ b/app/models/protected_branch.rb
@@ -95,6 +95,10 @@ class ProtectedBranch < ApplicationRecord
def self.downcase_humanized_name
name.underscore.humanize.downcase
end
+
+ def default_branch?
+ name == project.default_branch
+ end
end
ProtectedBranch.prepend_mod_with('ProtectedBranch')
diff --git a/app/services/merge_requests/merge_base_service.rb b/app/services/merge_requests/merge_base_service.rb
index 3e630d40b3d..2a3c1e8bc26 100644
--- a/app/services/merge_requests/merge_base_service.rb
+++ b/app/services/merge_requests/merge_base_service.rb
@@ -9,12 +9,12 @@ module MergeRequests
attr_reader :merge_request
# Overridden in EE.
- def hooks_validation_pass?(_merge_request)
+ def hooks_validation_pass?(merge_request, validate_squash_message: false)
true
end
# Overridden in EE.
- def hooks_validation_error(_merge_request)
+ def hooks_validation_error(merge_request, validate_squash_message: false)
# No-op
end
diff --git a/app/services/packages/rpm/repository_metadata/base_builder.rb b/app/services/packages/rpm/repository_metadata/base_builder.rb
index 9d76336d764..00d2f4fb0dc 100644
--- a/app/services/packages/rpm/repository_metadata/base_builder.rb
+++ b/app/services/packages/rpm/repository_metadata/base_builder.rb
@@ -11,7 +11,7 @@ module Packages
def build_empty_structure
Nokogiri::XML::Builder.new(encoding: 'UTF-8') do |xml|
- xml.public_send(self.class::ROOT_TAG, self.class::ROOT_ATTRIBUTES) # rubocop:disable GitlabSecurity/PublicSend
+ xml.method_missing(self.class::ROOT_TAG, self.class::ROOT_ATTRIBUTES)
end.to_xml
end
end
diff --git a/app/services/packages/rpm/repository_metadata/build_repomd_xml.rb b/app/services/packages/rpm/repository_metadata/build_repomd_xml.rb
index c6cfd77815d..84614196254 100644
--- a/app/services/packages/rpm/repository_metadata/build_repomd_xml.rb
+++ b/app/services/packages/rpm/repository_metadata/build_repomd_xml.rb
@@ -9,6 +9,7 @@ module Packages
xmlns: 'http://linux.duke.edu/metadata/repo',
'xmlns:rpm': 'http://linux.duke.edu/metadata/rpm'
}.freeze
+ ALLOWED_DATA_VALUE_KEYS = %i[checksum open-checksum location timestamp size open-size].freeze
# Expected `data` structure
#
@@ -48,9 +49,9 @@ module Packages
end
def build_file_info(info, xml)
- info.each do |key, attributes|
+ info.slice(*ALLOWED_DATA_VALUE_KEYS).each do |key, attributes|
value = attributes.delete(:value)
- xml.public_send(key, value, attributes) # rubocop:disable GitlabSecurity/PublicSend
+ xml.method_missing(key, value, attributes)
end
end
end
diff --git a/app/views/devise/registrations/new.html.haml b/app/views/devise/registrations/new.html.haml
index b6719834358..8a960602536 100644
--- a/app/views/devise/registrations/new.html.haml
+++ b/app/views/devise/registrations/new.html.haml
@@ -9,7 +9,7 @@
.signup-page
= render 'devise/shared/signup_box',
- url: registration_path(resource_name),
+ url: registration_path(resource_name, glm_tracking_params.to_hash),
button_text: _('Register'),
borderless: Feature.enabled?(:restyle_login_page, @project),
show_omniauth_providers: omniauth_enabled? && button_based_providers_enabled?
diff --git a/app/views/registrations/welcome/show.html.haml b/app/views/registrations/welcome/show.html.haml
index 911ba5e8042..5cff0f562e5 100644
--- a/app/views/registrations/welcome/show.html.haml
+++ b/app/views/registrations/welcome/show.html.haml
@@ -17,7 +17,9 @@
%p.gl-text-center= html_escape(_('%{gitlab_experience_text}. We won\'t share this information with anyone.')) % { gitlab_experience_text: gitlab_experience_text }
- else
%p.gl-text-center= html_escape(_('%{gitlab_experience_text}. Don\'t worry, this information isn\'t shared outside of your self-managed GitLab instance.')) % { gitlab_experience_text: gitlab_experience_text }
- = gitlab_ui_form_for(current_user, url: users_sign_up_welcome_path, html: { class: 'card gl-w-full! gl-p-5 js-users-signup-welcome', 'aria-live' => 'assertive' }) do |f|
+ = gitlab_ui_form_for(current_user,
+ url: users_sign_up_welcome_path(glm_tracking_params),
+ html: { class: 'card gl-w-full! gl-p-5 js-users-signup-welcome', 'aria-live' => 'assertive' }) do |f|
.devise-errors
= render 'devise/shared/error_messages', resource: current_user
.row
diff --git a/db/post_migrate/20220928225711_schedule_update_ci_pipeline_artifacts_locked_status.rb b/db/post_migrate/20220928225711_schedule_update_ci_pipeline_artifacts_locked_status.rb
new file mode 100644
index 00000000000..0d7a5dc4bec
--- /dev/null
+++ b/db/post_migrate/20220928225711_schedule_update_ci_pipeline_artifacts_locked_status.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+
+class ScheduleUpdateCiPipelineArtifactsLockedStatus < Gitlab::Database::Migration[2.0]
+ disable_ddl_transaction!
+
+ MIGRATION = 'UpdateCiPipelineArtifactsUnknownLockedStatus'
+ DELAY_INTERVAL = 2.minutes
+ BATCH_SIZE = 1_000
+ SUB_BATCH_SIZE = 500
+
+ restrict_gitlab_migration gitlab_schema: :gitlab_ci
+
+ def up
+ queue_batched_background_migration(
+ MIGRATION,
+ :ci_pipeline_artifacts,
+ :id,
+ job_interval: DELAY_INTERVAL,
+ batch_size: BATCH_SIZE,
+ sub_batch_size: SUB_BATCH_SIZE
+ )
+ end
+
+ def down
+ delete_batched_background_migration(MIGRATION, :ci_pipeline_artifacts, :id, [])
+ end
+end
diff --git a/db/schema_migrations/20220928225711 b/db/schema_migrations/20220928225711
new file mode 100644
index 00000000000..fd7a8b303d1
--- /dev/null
+++ b/db/schema_migrations/20220928225711
@@ -0,0 +1 @@
+5ec9b3f36a986cbb86c8005a4425307f0f4399a4a4030460e715370630cb9490 \ No newline at end of file
diff --git a/doc/administration/compliance.md b/doc/administration/compliance.md
index 5773b9be5f8..ad345461776 100644
--- a/doc/administration/compliance.md
+++ b/doc/administration/compliance.md
@@ -48,9 +48,9 @@ settings and automation to ensure that whatever a compliance team has configured
stays configured and working correctly. These features can help you automate
compliance:
-- [**Compliance frameworks**](../user/project/settings/index.md#compliance-frameworks) (for groups): Create a custom
+- [**Compliance frameworks**](../user/group/manage.md#compliance-frameworks) (for groups): Create a custom
compliance framework at the group level to describe the type of compliance requirements any child project needs to follow.
-- [**Compliance pipelines**](../user/project/settings/index.md#compliance-pipeline-configuration) (for groups): Define a
+- [**Compliance pipelines**](../user/group/manage.md#configure-a-compliance-pipeline) (for groups): Define a
pipeline configuration to run for any projects with a given compliance framework.
## Audit management
diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md
index e8318aed0d5..a1de8e1caba 100644
--- a/doc/api/graphql/reference/index.md
+++ b/doc/api/graphql/reference/index.md
@@ -10258,6 +10258,7 @@ List of branch rules for a project, grouped by branch name.
| ---- | ---- | ----------- |
| <a id="branchrulebranchprotection"></a>`branchProtection` | [`BranchProtection!`](#branchprotection) | Branch protections configured for this branch rule. |
| <a id="branchrulecreatedat"></a>`createdAt` | [`Time!`](#time) | Timestamp of when the branch rule was created. |
+| <a id="branchruleisdefault"></a>`isDefault` | [`Boolean!`](#boolean) | Check if this branch rule protects the project's default branch. |
| <a id="branchrulename"></a>`name` | [`String!`](#string) | Branch name, with wildcards, for the branch rules. |
| <a id="branchruleupdatedat"></a>`updatedAt` | [`Time!`](#time) | Timestamp of when the branch rule was last updated. |
diff --git a/doc/architecture/blueprints/ci_data_decay/pipeline_partitioning.md b/doc/architecture/blueprints/ci_data_decay/pipeline_partitioning.md
index e95e05ec5ea..f7f11d27155 100644
--- a/doc/architecture/blueprints/ci_data_decay/pipeline_partitioning.md
+++ b/doc/architecture/blueprints/ci_data_decay/pipeline_partitioning.md
@@ -359,14 +359,25 @@ We first create a unique index including the `(id, partition_id)`.
Then, we drop the primary key constraint and use the new index created to set
the new primary key constraint.
-We must set the primary key explicitly as `ActiveRecord` does not support composite primary keys.
+`ActiveRecord` [does not support](https://github.com/rails/rails/blob/6-1-stable/activerecord/lib/active_record/attribute_methods/primary_key.rb#L126)
+composite primary keys, so we must force it to treat the `id` column as a primary key:
```ruby
-class Model
+class Model < ApplicationRecord
self.primary_key = 'id'
end
```
+The application layer is now ignorant of the database structure and all of the
+existing queries from `ActiveRecord` continue to use the `id` column to access
+the data. There is some risk to this approach because it is possible to
+construct application code that results in duplicate models with the same `id`
+value, but on a different `partition_id`. To mitigate this risk we must ensure
+that all inserts use the database sequence to populate the `id` since they are
+[guaranteed](https://www.postgresql.org/docs/12/sql-createsequence.html#id-1.9.3.81.7)
+to allocate distinct values and rewrite the access patterns to include the
+`partition_id` value. Manually assigning the ids during inserts must be avoided.
+
### Foreign keys
Foreign keys must reference columns that either are a primary key or form a
diff --git a/doc/ci/yaml/index.md b/doc/ci/yaml/index.md
index 6075a646532..054a100574b 100644
--- a/doc/ci/yaml/index.md
+++ b/doc/ci/yaml/index.md
@@ -379,7 +379,7 @@ start. Jobs in the current stage are not stopped and continue to run.
- If a job does not specify a [`stage`](#stage), the job is assigned the `test` stage.
- If a stage is defined but no jobs use it, the stage is not visible in the pipeline,
- which can help [compliance pipeline configurations](../../user/project/settings/index.md#compliance-pipeline-configuration):
+ which can help [compliance pipeline configurations](../../user/group/manage.md#configure-a-compliance-pipeline):
- Stages can be defined in the compliance configuration but remain hidden if not used.
- The defined stages become visible when developers use them in job definitions.
diff --git a/doc/gitlab-basics/start-using-git.md b/doc/gitlab-basics/start-using-git.md
index 2afd9f66804..4e552411d54 100644
--- a/doc/gitlab-basics/start-using-git.md
+++ b/doc/gitlab-basics/start-using-git.md
@@ -147,10 +147,14 @@ between your computer and GitLab.
git clone https://gitlab.com/gitlab-tests/sample-project.git
```
-1. GitLab requests your username and password:
- - If you have 2FA enabled for your account, you must [clone using a token](#clone-using-a-token)
- with `read_repository` or `write_repository` permissions instead of your account's password.
- - If you don't have 2FA enabled, use your account's password.
+1. GitLab requests your username and password.
+
+ If you have enabled two-factor authentication (2FA) on your account, you cannot use your account password. Instead, you can do one of the following:
+
+ - [Clone using a token](#clone-using-a-token) with `read_repository` or `write_repository` permissions.
+ - Install [Git Credential Manager](../user/profile/account/two_factor_authentication.md#git-credential-manager).
+
+ If you have not enabled 2FA, use your account password.
1. To view the files, go to the new directory:
diff --git a/doc/user/admin_area/settings/continuous_integration.md b/doc/user/admin_area/settings/continuous_integration.md
index 403a2564da3..95e40d4acb6 100644
--- a/doc/user/admin_area/settings/continuous_integration.md
+++ b/doc/user/admin_area/settings/continuous_integration.md
@@ -244,7 +244,7 @@ To enable or disable the banner:
> [Moved](https://gitlab.com/gitlab-org/gitlab/-/issues/352316) from GitLab Premium to GitLab Ultimate in 15.0.
NOTE:
-An alternative [compliance solution](../../project/settings/index.md#compliance-pipeline-configuration)
+An alternative [compliance solution](../../group/manage.md#configure-a-compliance-pipeline)
is available. We recommend this alternative solution because it provides greater flexibility,
allowing required pipelines to be assigned to specific compliance framework labels.
diff --git a/doc/user/application_security/configuration/index.md b/doc/user/application_security/configuration/index.md
index 26805859ae8..7e39f1086b0 100644
--- a/doc/user/application_security/configuration/index.md
+++ b/doc/user/application_security/configuration/index.md
@@ -19,10 +19,17 @@ The Security Configuration page lists the following for the security testing and
- Whether or not it is available.
- A configuration button or a link to its configuration guide.
-The status of each security control is determined by the project's latest default branch
-[CI pipeline](../../../ci/pipelines/index.md).
-If a job with the expected security report artifact exists in the pipeline, the feature's status is
-_enabled_.
+The status of each security control is determined by the following process:
+
+1. Check for a [CI pipeline](../../../ci/pipelines/index.md) in the most recent commit on the default branch.
+1. If no CI pipelines exist, then consider all security scanners disabled. Show the **Not enabled** status.
+1. If a pipeline is found, then inspect the CI YAML for each job in the CI/CD pipeline. If a
+ job in the pipeline defines an [`artifacts:reports` keyword](../../../ci/yaml/artifacts_reports.md)
+ for a security scanner, then consider the security scanner enabled. Show the **Enabled** status.
+
+Failed pipelines and jobs are included in this process. If a scanner is configured but the job fails,
+that scanner is still considered enabled. This process also determines the scanners and statuses
+returned through [our API](../../../api/graphql/reference/index.md#securityscanners).
If the latest pipeline used [Auto DevOps](../../../topics/autodevops/index.md),
all security features are configured by default.
diff --git a/doc/user/application_security/get-started-security.md b/doc/user/application_security/get-started-security.md
index d8abc290b72..41dc35dd8ce 100644
--- a/doc/user/application_security/get-started-security.md
+++ b/doc/user/application_security/get-started-security.md
@@ -36,7 +36,7 @@ The following steps will help you get the most from GitLab application security
remediating existing vulnerabilities and preventing the introduction of new ones.
1. Enable other scan types such as [SAST](sast/index.md), [DAST](dast/index.md),
[Fuzz testing](coverage_fuzzing/index.md), or [Container Scanning](container_scanning/index.md).
-1. Use [Compliance Pipelines](../../user/project/settings/index.md#compliance-pipeline-configuration)
+1. Use [Compliance Pipelines](../group/manage.md#configure-a-compliance-pipeline)
or [Scan Execution Policies](policies/scan-execution-policies.md) to enforce required scan types
and ensure separation of duties between security and engineering.
1. Consider enabling [Review Apps](../../development/testing_guide/review_apps.md) to allow for DAST
diff --git a/doc/user/application_security/index.md b/doc/user/application_security/index.md
index 84540c570e5..ffd9bb1a783 100644
--- a/doc/user/application_security/index.md
+++ b/doc/user/application_security/index.md
@@ -446,7 +446,7 @@ Security and compliance teams must ensure that security scans:
GitLab provides two methods of accomplishing this, each with advantages and disadvantages.
-- [Compliance framework pipelines](../project/settings/index.md#compliance-pipeline-configuration)
+- [Compliance framework pipelines](../group/manage.md#configure-a-compliance-pipeline)
are recommended when:
- Scan execution enforcement is required for any scanner that uses a GitLab template, such as SAST IaC, DAST, Dependency Scanning,
diff --git a/doc/user/application_security/policies/scan-execution-policies.md b/doc/user/application_security/policies/scan-execution-policies.md
index 7b95848f978..ce0900aaf4e 100644
--- a/doc/user/application_security/policies/scan-execution-policies.md
+++ b/doc/user/application_security/policies/scan-execution-policies.md
@@ -15,7 +15,7 @@ with a long, random job name. In the unlikely event of a job name collision, the
any pre-existing job in the pipeline. If a policy is created at the group-level, it will apply to every child
project or sub-group. A group-level policy cannot be edited from a child project or sub-group.
-This feature has some overlap with [compliance framework pipelines](../../project/settings/index.md#compliance-pipeline-configuration),
+This feature has some overlap with [compliance framework pipelines](../../group/manage.md#configure-a-compliance-pipeline),
as we have not [unified the user experience for these two features](https://gitlab.com/groups/gitlab-org/-/epics/7312).
For details on the similarities and differences between these features, see
[Enforce scan execution](../index.md#enforce-scan-execution).
diff --git a/doc/user/compliance/compliance_report/index.md b/doc/user/compliance/compliance_report/index.md
index 621fe8a4bc5..ac4b20b5166 100644
--- a/doc/user/compliance/compliance_report/index.md
+++ b/doc/user/compliance/compliance_report/index.md
@@ -72,7 +72,7 @@ The following is a list of violations that are either:
When you select a row, a drawer is shown that provides further details about the merge
request:
-- Project name and [compliance framework label](../../project/settings/index.md#compliance-frameworks),
+- Project name and [compliance framework label](../../project/settings/index.md#add-a-compliance-framework-to-a-project),
if the project has one assigned.
- Link to the merge request.
- The merge request's branch path in the format `[source] into [target]`.
diff --git a/doc/user/group/manage.md b/doc/user/group/manage.md
index 27c1d3e459d..8a6cb42f247 100644
--- a/doc/user/group/manage.md
+++ b/doc/user/group/manage.md
@@ -376,6 +376,186 @@ To enable delayed deletion of projects in a group:
NOTE:
In GitLab 13.11 and above the group setting for delayed project deletion is inherited by subgroups. As discussed in [Cascading settings](../../development/cascading_settings.md) inheritance can be overridden, unless enforced by an ancestor.
+## Compliance frameworks **(PREMIUM)**
+
+> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/276221) in GitLab 13.9.
+> - [Feature flag removed](https://gitlab.com/gitlab-org/gitlab/-/issues/287779) in GitLab 13.12.
+
+You can create a compliance framework that is a label to identify that your project has certain compliance
+requirements or needs additional oversight. The label can optionally enforce
+[compliance pipeline configuration](#configure-a-compliance-pipeline) to the projects on which it is
+[applied](../project/settings/index.md#add-a-compliance-framework-to-a-project).
+
+Group owners can create, edit, and delete compliance frameworks:
+
+1. On the top bar, select **Main menu > Groups > View all groups** and find your group.
+1. On the left sidebar, select **Settings** > **General**.
+1. Expand the **Compliance frameworks** section.
+1. Create, edit, or delete compliance frameworks.
+
+### Configure a compliance pipeline **(ULTIMATE)**
+
+> - [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/3156) in GitLab 13.9, disabled behind `ff_evaluate_group_level_compliance_pipeline` [feature flag](../../administration/feature_flags.md).
+> - [Enabled by default](https://gitlab.com/gitlab-org/gitlab/-/issues/300324) in GitLab 13.11.
+> - [Feature flag removed](https://gitlab.com/gitlab-org/gitlab/-/issues/331231) in GitLab 14.2.
+
+Group owners can configure a compliance pipeline in a project separate to other projects. By default, the compliance
+pipeline configuration (`.gitlab-ci.yml` file) is run instead of the pipeline configuration of labeled projects.
+
+However, the compliance pipeline configuration can reference the `.gitlab-ci.yml` file of the labeled projects so that:
+
+- The compliance pipeline can also run jobs of labeled project pipelines. This allows for centralized control of
+ pipeline configuration.
+- Jobs and variables defined in the compliance pipeline can't be changed by variables in the labeled project's
+ `.gitlab-ci.yml` file.
+
+See [example configuration](#example-configuration) for help configuring a compliance pipeline that runs jobs from
+labeled project pipeline configuration.
+
+To configure a compliance pipeline:
+
+1. On the top bar, select **Main menu > Groups > View all groups** and find your group.
+1. On the left sidebar, select **Settings** > **General**.
+1. Expand the **Compliance frameworks** section.
+1. In **Compliance pipeline configuration (optional)**, add the path to the compliance framework configuration. Use the
+ `path/file.y[a]ml@group-name/project-name` format. For example:
+
+ - `.compliance-ci.yml@gitlab-org/gitlab`.
+ - `.compliance-ci.yaml@gitlab-org/gitlab`.
+
+This configuration is inherited by projects where the compliance framework label is
+[applied](../project/settings/index.md#add-a-compliance-framework-to-a-project). In projects with the applied compliance
+framework label, the compliance pipeline configuration is run instead of the labeled project's own pipeline configuration.
+
+The user running the pipeline in the labeled project must at least have the Reporter role on the compliance project.
+
+When used to enforce scan execution, this feature has some overlap with
+[scan execution policies](../application_security/policies/scan-execution-policies.md). We have not
+[unified the user experience for these two features](https://gitlab.com/groups/gitlab-org/-/epics/7312). For details on
+the similarities and differences between these features, see [Enforce scan execution](../application_security/index.md#enforce-scan-execution).
+
+#### Example configuration
+
+The following example `.compliance-gitlab-ci.yml` includes the `include` keyword to ensure labeled project pipeline
+configuration is also executed.
+
+```yaml
+# Allows compliance team to control the ordering and interweaving of stages/jobs.
+# Stages without jobs defined will remain hidden.
+stages:
+ - pre-compliance
+ - build
+ - test
+ - pre-deploy-compliance
+ - deploy
+ - post-compliance
+
+variables: # Can be overridden by setting a job-specific variable in project's local .gitlab-ci.yml
+ FOO: sast
+
+sast: # None of these attributes can be overridden by a project's local .gitlab-ci.yml
+ variables:
+ FOO: sast
+ image: ruby:2.6
+ stage: pre-compliance
+ rules:
+ - if: $CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS && $CI_PIPELINE_SOURCE == "push"
+ when: never
+ - when: always # or when: on_success
+ allow_failure: false
+ before_script:
+ - "# No before scripts."
+ script:
+ - echo "running $FOO"
+ after_script:
+ - "# No after scripts."
+
+sanity check:
+ image: ruby:2.6
+ stage: pre-deploy-compliance
+ rules:
+ - if: $CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS && $CI_PIPELINE_SOURCE == "push"
+ when: never
+ - when: always # or when: on_success
+ allow_failure: false
+ before_script:
+ - "# No before scripts."
+ script:
+ - echo "running $FOO"
+ after_script:
+ - "# No after scripts."
+
+audit trail:
+ image: ruby:2.7
+ stage: post-compliance
+ rules:
+ - if: $CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS && $CI_PIPELINE_SOURCE == "push"
+ when: never
+ - when: always # or when: on_success
+ allow_failure: false
+ before_script:
+ - "# No before scripts."
+ script:
+ - echo "running $FOO"
+ after_script:
+ - "# No after scripts."
+
+include: # Execute individual project's configuration (if project contains .gitlab-ci.yml)
+ project: '$CI_PROJECT_PATH'
+ file: '$CI_CONFIG_PATH'
+ ref: '$CI_COMMIT_REF_NAME' # Must be defined or MR pipelines always use the use default branch
+```
+
+### Ensure compliance jobs are always run
+
+Compliance pipelines [use GitLab CI/CD](../../ci/index.md) to give you an incredible amount of flexibility
+for defining any sort of compliance jobs you like. Depending on your goals, these jobs
+can be configured to be:
+
+- Modified by users.
+- Non-modifiable.
+
+Generally, if a value in a compliance job:
+
+- Is set, it cannot be changed or overridden by project-level configurations.
+- Is not set, a project-level configuration may set.
+
+Either might be wanted or not depending on your use case.
+
+There are a few best practices for ensuring that these jobs are always run exactly
+as you define them and that downstream, project-level pipeline configurations
+cannot change them:
+
+- Add [a `rules:when:always` block](../../ci/yaml/index.md#when) to each of your compliance jobs. This ensures they are
+ non-modifiable and are always run.
+- Explicitly set any [variables](../../ci/yaml/index.md#variables) the job references. This:
+ - Ensures that project-level pipeline configurations do not set them and alter their
+ behavior.
+ - Includes any jobs that drive the logic of your job.
+- Explicitly set the [container image](../../ci/yaml/index.md#image) to run the job in. This ensures that your script
+ steps execute in the correct environment.
+- Explicitly set any relevant GitLab pre-defined [job keywords](../../ci/yaml/index.md#job-keywords).
+ This ensures that your job uses the settings you intend and that they are not overridden by
+ project-level pipelines.
+
+### Avoid parent and child pipelines in GitLab 14.7 and earlier
+
+NOTE:
+This advice does not apply to GitLab 14.8 and later because [a fix](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/78878) added
+compatibility for combining compliance pipelines, and parent and child pipelines.
+
+Compliance pipelines start on the run of _every_ pipeline in a labeled project. This means that if a pipeline in the labeled project
+triggers a child pipeline, the compliance pipeline runs first. This can trigger the parent pipeline, instead of the child pipeline.
+
+Therefore, in projects with compliance frameworks, we recommend replacing
+[parent-child pipelines](../../ci/pipelines/downstream_pipelines.md#parent-child-pipelines) with the following:
+
+- Direct [`include`](../../ci/yaml/index.md#include) statements that provide the parent pipeline with child pipeline configuration.
+- Child pipelines placed in another project that are run using the [trigger API](../../ci/triggers/index.md) rather than the parent-child
+ pipeline feature.
+
+This alternative ensures the compliance pipeline does not re-start the parent pipeline.
+
## Disable email notifications
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/23585) in GitLab 12.2.
@@ -560,9 +740,7 @@ Changes to [group wikis](../project/wiki/group.md) do not appear in group activi
You can view the most recent actions taken in a group, either in your browser or in an RSS feed:
-1. On the top bar, select **Main menu > Groups > View all groups**.
-1. Select **Your Groups**.
-1. Find the group and select it.
+1. On the top bar, select **Main menu > Groups > View all groups** and find your group.
1. On the left sidebar, select **Group information > Activity**.
To view the activity feed in Atom format, select the
diff --git a/doc/user/permissions.md b/doc/user/permissions.md
index 9d02d9a7672..102abf2b427 100644
--- a/doc/user/permissions.md
+++ b/doc/user/permissions.md
@@ -166,7 +166,7 @@ The following table lists project permissions available for each role:
| [Projects](project/index.md):<br>Rename project | | | | ✓ | ✓ |
| [Projects](project/index.md):<br>Share (invite) projects with groups | | | | ✓ (*7*) | ✓ (*7*) |
| [Projects](project/index.md):<br>View 2FA status of members | | | | ✓ | ✓ |
-| [Projects](project/index.md):<br>Assign project to a [compliance framework](project/settings/index.md#compliance-frameworks) | | | | | ✓ |
+| [Projects](project/index.md):<br>Assign project to a [compliance framework](project/settings/index.md#add-a-compliance-framework-to-a-project) | | | | | ✓ |
| [Projects](project/index.md):<br>Archive project | | | | | ✓ |
| [Projects](project/index.md):<br>Change project visibility level | | | | | ✓ |
| [Projects](project/index.md):<br>Delete project | | | | | ✓ |
diff --git a/doc/user/profile/account/two_factor_authentication.md b/doc/user/profile/account/two_factor_authentication.md
index 7442268fc2c..3dc768f6606 100644
--- a/doc/user/profile/account/two_factor_authentication.md
+++ b/doc/user/profile/account/two_factor_authentication.md
@@ -452,11 +452,10 @@ This error occurs in the following scenarios:
[enforce 2FA for all users](../../../security/two_factor_authentication.md#enforce-2fa-for-all-users) setting.
- You do not have 2FA enabled, but an administrator has disabled the
[password authentication enabled for Git over HTTP(S)](../../admin_area/settings/sign_in_restrictions.md#password-authentication-enabled)
- setting. If LDAP is:
- - Configured, an [LDAP password](../../../administration/auth/ldap/index.md)
- or a [personal access token](../personal_access_tokens.md)
- must be used to authenticate Git requests over HTTP(S).
- - Not configured, you must use a [personal access token](../personal_access_tokens.md).
+ setting. You can authenticate Git requests:
+ - Over HTTP(S) using a [personal access token](../personal_access_tokens.md).
+ - In your browser using [Git Credential Manager](#git-credential-manager).
+ - If you have configured LDAP, over HTTP(S) using an [LDAP password](../../../administration/auth/ldap/index.md).
### Error: "invalid pin code"
diff --git a/doc/user/project/settings/index.md b/doc/user/project/settings/index.md
index d7f6ef8539f..1ee71fe1a7a 100644
--- a/doc/user/project/settings/index.md
+++ b/doc/user/project/settings/index.md
@@ -43,187 +43,21 @@ To assign topics to a project:
If you're an instance administrator, you can administer all project topics from the
[Admin Area's Topics page](../../admin_area/index.md#administering-topics).
-## Compliance frameworks **(PREMIUM)**
+## Add a compliance framework to a project **(PREMIUM)**
-> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/276221) in GitLab 13.9.
-> - [Feature flag removed](https://gitlab.com/gitlab-org/gitlab/-/issues/287779) in GitLab 13.12.
+[Compliance frameworks](../../group/manage.md#compliance-frameworks) can be assigned to projects within group that has a
+compliance framework using either:
-You can create a compliance framework label to identify that your project has certain compliance
-requirements or needs additional oversight. The label can optionally apply
-[compliance pipeline configuration](#compliance-pipeline-configuration).
-
-Group owners can create, edit, and delete compliance frameworks:
-
-1. On the top bar, select **Main menu > Groups** and find your group.
-1. On the left sidebar, select **Settings** > **General**.
-1. Expand the **Compliance frameworks** section.
-
-Compliance frameworks created can then be assigned to projects within the group using:
-
-- The GitLab UI, using the project settings page.
+- The GitLab UI:
+ 1. On the top bar, select **Main menu > Projects > View all projects** and find your project.
+ 1. On the left sidebar, select **Settings** > **General**.
+ 1. Expand the **Compliance frameworks** section.
+ 1. Select a compliance framework.
+ 1. Select **Save changes**.
- In [GitLab 14.2](https://gitlab.com/gitlab-org/gitlab/-/issues/333249) and later, using the
- [GraphQL API](../../../api/graphql/reference/index.md#mutationprojectsetcomplianceframework).
-
-NOTE:
-Creating compliance frameworks on subgroups with GraphQL causes the framework to be
-created on the root ancestor if the user has the correct permissions. The GitLab UI presents a
-read-only view to discourage this behavior.
-
-### Compliance pipeline configuration **(ULTIMATE)**
-
-> - [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/3156) in GitLab 13.9, disabled behind `ff_evaluate_group_level_compliance_pipeline` [feature flag](../../../administration/feature_flags.md).
-> - [Enabled by default](https://gitlab.com/gitlab-org/gitlab/-/issues/300324) in GitLab 13.11.
-> - [Feature flag removed](https://gitlab.com/gitlab-org/gitlab/-/issues/331231) in GitLab 14.2.
-
-Compliance framework pipelines allow group owners to define
-a compliance pipeline in a separate repository that gets
-executed in place of the local project's `.gitlab-ci.yml` file. As part of this pipeline, an
-`include` statement can reference the local project's `.gitlab-ci.yml` file. This way, the compliance
-pipeline jobs can run alongside the project-specific jobs any time the pipeline runs.
-Jobs and variables defined in the compliance
-pipeline can't be changed by variables in the local project's `.gitlab-ci.yml` file.
-
-When you set up the compliance framework, use the **Compliance pipeline configuration** box to link
-the compliance framework to specific CI/CD configuration. Use the
-`path/file.y[a]ml@group-name/project-name` format. For example:
-
-- `.compliance-ci.yml@gitlab-org/gitlab`.
-- `.compliance-ci.yaml@gitlab-org/gitlab`.
-
-This configuration is inherited by projects where the compliance framework label is applied. The
-result forces projects with the label to run the compliance CI/CD configuration in addition to
-the project's own CI/CD configuration. When a project with a compliance framework label executes a
-pipeline, it evaluates configuration in the following order:
-
-1. Compliance pipeline configuration.
-1. Project-specific pipeline configuration.
-
-The user running the pipeline in the project must at least have the Reporter role on the compliance
-project.
-
-Example `.compliance-gitlab-ci.yml`:
-
-```yaml
-# Allows compliance team to control the ordering and interweaving of stages/jobs.
-# Stages without jobs defined will remain hidden.
-stages:
- - pre-compliance
- - build
- - test
- - pre-deploy-compliance
- - deploy
- - post-compliance
-
-variables: # Can be overridden by setting a job-specific variable in project's local .gitlab-ci.yml
- FOO: sast
-
-sast: # None of these attributes can be overridden by a project's local .gitlab-ci.yml
- variables:
- FOO: sast
- image: ruby:2.6
- stage: pre-compliance
- rules:
- - if: $CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS && $CI_PIPELINE_SOURCE == "push"
- when: never
- - when: always # or when: on_success
- allow_failure: false
- before_script:
- - "# No before scripts."
- script:
- - echo "running $FOO"
- after_script:
- - "# No after scripts."
-
-sanity check:
- image: ruby:2.6
- stage: pre-deploy-compliance
- rules:
- - if: $CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS && $CI_PIPELINE_SOURCE == "push"
- when: never
- - when: always # or when: on_success
- allow_failure: false
- before_script:
- - "# No before scripts."
- script:
- - echo "running $FOO"
- after_script:
- - "# No after scripts."
-
-audit trail:
- image: ruby:2.6
- stage: post-compliance
- rules:
- - if: $CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS && $CI_PIPELINE_SOURCE == "push"
- when: never
- - when: always # or when: on_success
- allow_failure: false
- before_script:
- - "# No before scripts."
- script:
- - echo "running $FOO"
- after_script:
- - "# No after scripts."
-
-include: # Execute individual project's configuration (if project contains .gitlab-ci.yml)
- project: '$CI_PROJECT_PATH'
- file: '$CI_CONFIG_PATH'
- ref: '$CI_COMMIT_REF_NAME' # Must be defined or MR pipelines always use the use default branch
-```
-
-When used to enforce scan execution, this feature has some overlap with [scan execution policies](../../application_security/policies/scan-execution-policies.md),
-as we have not [unified the user experience for these two features](https://gitlab.com/groups/gitlab-org/-/epics/7312).
-For details on the similarities and differences between these features, see
-[Enforce scan execution](../../application_security/index.md#enforce-scan-execution).
-
-### Ensure compliance jobs are always run
-
-Compliance pipelines use GitLab CI/CD to give you an incredible amount of flexibility
-for defining any sort of compliance jobs you like. Depending on your goals, these jobs
-can be configured to be:
-
-- Modified by users.
-- Non-modifiable.
-
-At a high-level, if a value in a compliance job:
-
-- Is set, it cannot be changed or overridden by project-level configurations.
-- Is not set, a project-level configuration may set.
-
-Either might be wanted or not depending on your use case.
-
-There are a few best practices for ensuring that these jobs are always run exactly
-as you define them and that downstream, project-level pipeline configurations
-cannot change them:
-
-- Add a `rules:when:always` block to each of your compliance jobs. This ensures they are
- non-modifiable and are always run.
-- Explicitly set any variables the job references. This:
- - Ensures that project-level pipeline configurations do not set them and alter their
- behavior.
- - Includes any jobs that drive the logic of your job.
-- Explicitly set the container image file to run the job in. This ensures that your script
- steps execute in the correct environment.
-- Explicitly set any relevant GitLab pre-defined [job keywords](../../../ci/yaml/index.md#job-keywords).
- This ensures that your job uses the settings you intend and that they are not overridden by
- project-level pipelines.
-
-### Avoid parent and child pipelines in GitLab 14.7 and earlier
-
-NOTE:
-This advice does not apply to GitLab 14.8 and later because [a fix](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/78878) added
-compatibility for combining compliance pipelines, and parent and child pipelines.
-
-Compliance pipelines start on the run of _every_ pipeline in a relevant project. This means that if a pipeline in the relevant project
-triggers a child pipeline, the compliance pipeline runs first. This can trigger the parent pipeline, instead of the child pipeline.
-
-Therefore, in projects with compliance frameworks, we recommend replacing
-[parent-child pipelines](../../../ci/pipelines/downstream_pipelines.md#parent-child-pipelines) with the following:
-
-- Direct [`include`](../../../ci/yaml/index.md#include) statements that provide the parent pipeline with child pipeline configuration.
-- Child pipelines placed in another project that are run using the [trigger API](../../../ci/triggers/index.md) rather than the parent-child
- pipeline feature.
-
-This alternative ensures the compliance pipeline does not re-start the parent pipeline.
+ [GraphQL API](../../../api/graphql/reference/index.md#mutationprojectsetcomplianceframework). If you create
+ compliance frameworks on subgroups with GraphQL, the framework is created on the root ancestor if the user has the
+ correct permissions. The GitLab UI presents a read-only view to discourage this behavior.
## Configure project visibility, features, and permissions
diff --git a/doc/user/usage_quotas.md b/doc/user/usage_quotas.md
index 7f4bb84f0aa..2060a029833 100644
--- a/doc/user/usage_quotas.md
+++ b/doc/user/usage_quotas.md
@@ -26,30 +26,32 @@ Storage types that add to the total namespace storage are:
- Wiki
- Snippets
-If your total namespace storage exceeds the available namespace storage quota, all projects under the namespace are locked. A locked project will not be able to push to the repository, run pipelines and jobs, or build and push packages.
+If your total namespace storage exceeds the available namespace storage quota, all projects under the namespace are locked.
+A locked project cannot push to the repository, run pipelines and jobs, or build and push packages.
To prevent exceeding the namespace storage quota, you can:
-1. Reduce storage consumption by following the suggestions in the [Manage Your Storage Usage](#manage-your-storage-usage) section of this page.
-1. Apply for [GitLab for Education](https://about.gitlab.com/solutions/education/join/), [GitLab for Open Source](https://about.gitlab.com/solutions/open-source/join/), or [GitLab for Startups](https://about.gitlab.com/solutions/startups/) if you meet the eligibility requirements.
-1. Consider using a [self-managed instance](../subscriptions/self_managed/index.md) of GitLab which does not have these limits on the free tier.
-1. [Purchase additional storage](../subscriptions/gitlab_com/index.md#purchase-more-storage-and-transfer) units at $60/year for 10GB of storage.
-1. [Start a trial](https://about.gitlab.com/free-trial/) or [upgrade to GitLab Premium or Ultimate](https://about.gitlab.com/pricing) which include higher limits and features that enable growing teams to ship faster without sacrificing on quality.
-1. [Talk to an expert](https://page.gitlab.com/usage_limits_help.html) to learn more about your options and ask questions.
+- Reduce storage consumption by following the suggestions in the [Manage Your Storage Usage](#manage-your-storage-usage) section of this page.
+- Apply for [GitLab for Education](https://about.gitlab.com/solutions/education/join/), [GitLab for Open Source](https://about.gitlab.com/solutions/open-source/join/), or [GitLab for Startups](https://about.gitlab.com/solutions/startups/) if you meet the eligibility requirements.
+- Consider using a [self-managed instance](../subscriptions/self_managed/index.md) of GitLab which does not have these limits on the free tier.
+- [Purchase additional storage](../subscriptions/gitlab_com/index.md#purchase-more-storage-and-transfer) units at $60/year for 10GB of storage.
+- [Start a trial](https://about.gitlab.com/free-trial/) or [upgrade to GitLab Premium or Ultimate](https://about.gitlab.com/pricing) which include higher limits and features that enable growing teams to ship faster without sacrificing on quality.
+- [Talk to an expert](https://page.gitlab.com/usage_limits_help.html) to learn more about your options and ask questions.
### Namespace storage limit enforcement schedule
Storage limits for GitLab SaaS Free tier namespaces will not be enforced prior to 2022-10-19. Storage limits for GitLab SaaS Paid tier namespaces will not be enforced for prior to 2023-02-15. Enforcement will not occur until all storage types are accurately measured, including deduplication of forks for [Git](https://gitlab.com/gitlab-org/gitlab/-/issues/371671) and [LFS](https://gitlab.com/gitlab-org/gitlab/-/issues/370242).
-Impacted users are notified via email and in-app notifications at least 60 days prior to enforcement.
+Impacted users are notified by email and through in-app notifications at least 60 days prior to enforcement.
### Project storage limit
Projects on GitLab SaaS have a 10GB storage limit on their Git repository and LFS storage.
-Once namespace-level storage limits are enforced, the project limit will be removed. A namespace has either a namespace-level storage limit or a project-level storage limit, but not both.
+After namespace-level storage limits are enforced, the project limit is removed. A namespace has either a namespace-level storage limit or a project-level storage limit, but not both.
-When a project's repository and LFS reaches the quota, the project is locked. You cannot push changes to a locked project. To monitor the size of each
-repository in a namespace, including a breakdown for each project, you can
+When a project's repository and LFS reaches the quota, the project is locked.
+You cannot push changes to a locked project. To monitor the size of each
+repository in a namespace, including a breakdown for each project,
[view storage usage](#view-storage-usage). To allow a project's repository and LFS to exceed the free quota
you must purchase additional storage. For more details, see [Excess storage usage](#excess-storage-usage).
@@ -66,7 +68,7 @@ Prerequisites:
1. From the left sidebar, select **Settings > Usage Quotas**.
1. Select the **Storage** tab.
-The statistics are displayed. Select any title to view details. The information on this page
+Select any title to view details. The information on this page
is updated every 90 minutes.
If your namespace shows `'Not applicable.'`, push a commit to any project in the
diff --git a/lib/gitlab/background_migration/update_ci_pipeline_artifacts_unknown_locked_status.rb b/lib/gitlab/background_migration/update_ci_pipeline_artifacts_unknown_locked_status.rb
new file mode 100644
index 00000000000..84183753158
--- /dev/null
+++ b/lib/gitlab/background_migration/update_ci_pipeline_artifacts_unknown_locked_status.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module BackgroundMigration
+ # The `ci_pipeline_artifacts.locked` column was added in
+ # https://gitlab.com/gitlab-org/gitlab/-/merge_requests/97194 to
+ # speed up the finding of expired, pipeline artifacts. By default,
+ # the value is "unknown" (2), but the correct value should be the
+ # value of the associated `ci_pipelines.locked` value. This class
+ # does an UPDATE join to make the values match.
+ class UpdateCiPipelineArtifactsUnknownLockedStatus < BatchedMigrationJob
+ def perform
+ connection.exec_query(<<~SQL)
+ UPDATE ci_pipeline_artifacts
+ SET locked = ci_pipelines.locked
+ FROM ci_pipelines
+ WHERE ci_pipeline_artifacts.id BETWEEN #{start_id} AND #{end_id}
+ AND ci_pipeline_artifacts.locked = 2
+ AND ci_pipelines.id = ci_pipeline_artifacts.pipeline_id;
+ SQL
+ end
+ end
+ end
+end
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index fb3abbd6557..46084e22952 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -9847,7 +9847,7 @@ msgstr ""
msgid "ComplianceFrameworks|No compliance frameworks are set up yet"
msgstr ""
-msgid "ComplianceFrameworks|Required format: %{codeStart}path/file.y[a]ml@group-name/project-name%{codeEnd}. %{linkStart}Learn more.%{linkEnd}"
+msgid "ComplianceFrameworks|Required format: %{codeStart}path/file.y[a]ml@group-name/project-name%{codeEnd}. %{linkStart}What is a compliance pipeline configuration?%{linkEnd}"
msgstr ""
msgid "ComplianceFrameworks|Unable to save this compliance framework. Please try again"
@@ -19041,7 +19041,7 @@ msgstr ""
msgid "GroupSettings|Compliance frameworks"
msgstr ""
-msgid "GroupSettings|Configure compliance frameworks to make them available to projects in this group. %{linkStart}Learn more.%{linkEnd}"
+msgid "GroupSettings|Configure compliance frameworks to make them available to projects in this group. %{linkStart}What are compliance frameworks?%{linkEnd}"
msgstr ""
msgid "GroupSettings|Configure limits on the number of repositories users can download in a given time."
diff --git a/qa/qa/tools/ci/qa_changes.rb b/qa/qa/tools/ci/qa_changes.rb
index 846f4dc4769..784923714d6 100644
--- a/qa/qa/tools/ci/qa_changes.rb
+++ b/qa/qa/tools/ci/qa_changes.rb
@@ -18,9 +18,10 @@ module QA
/Dockerfile\.assets/
)
- def initialize(mr_diff, mr_labels)
+ def initialize(mr_diff, mr_labels, additional_group_spec_list)
@mr_diff = mr_diff
@mr_labels = mr_labels
+ @additional_group_spec_list = additional_group_spec_list
end
# Specific specs to run
@@ -80,6 +81,9 @@ module QA
# @return [Array]
attr_reader :mr_labels
+ # @return [Hash<String, Array<String>>]
+ attr_reader :additional_group_spec_list
+
# Are the changed files only qa specs?
#
# @return [Boolean] whether the changes files are only qa specs
@@ -101,6 +105,13 @@ module QA
mr_labels.find { |label| label =~ /^devops::/ }&.delete_prefix('devops::')
end
+ # Extract group name from MR labels
+ #
+ # @return [String] a group name
+ def group_name_from_mr_labels
+ mr_labels.find { |label| label =~ /^group::/ }&.delete_prefix('group::')
+ end
+
# Get qa spec directories for devops stage
#
# @return [Array] qa spec directories
@@ -108,7 +119,15 @@ module QA
devops_stage = devops_stage_from_mr_labels
return unless devops_stage
- Dir.glob("qa/specs/**/*/").select { |dir| dir =~ %r{\d+_#{devops_stage}/$} }
+ spec_dirs = stage_specs(devops_stage)
+
+ grp_name = group_name_from_mr_labels
+ return spec_dirs if grp_name.nil?
+
+ additional_grp_specs = additional_group_spec_list[grp_name]
+ return spec_dirs if additional_grp_specs.nil?
+
+ spec_dirs + stage_specs(*additional_grp_specs)
end
# Changes to gitlab dependencies
@@ -122,7 +141,15 @@ module QA
#
# @return [Array<String>]
def changed_files
- @changed_files ||= mr_diff.map { |change| change[:path] } # rubocop:disable Rails/Pluck
+ @changed_files ||= mr_diff.map { |change| change[:path] }
+ end
+
+ # Devops stage specs
+ #
+ # @param [Array<String>] devops_stages
+ # @return [Array]
+ def stage_specs(*devops_stages)
+ Dir.glob("qa/specs/**/*/").select { |dir| dir =~ %r{\d+_(#{devops_stages.join('|')})/$} }
end
end
end
diff --git a/qa/spec/tools/ci/qa_changes_spec.rb b/qa/spec/tools/ci/qa_changes_spec.rb
index a083ed79bdf..d93d3cd9258 100644
--- a/qa/spec/tools/ci/qa_changes_spec.rb
+++ b/qa/spec/tools/ci/qa_changes_spec.rb
@@ -1,9 +1,10 @@
# frozen_string_literal: true
RSpec.describe QA::Tools::Ci::QaChanges do
- subject(:qa_changes) { described_class.new(mr_diff, mr_labels) }
+ subject(:qa_changes) { described_class.new(mr_diff, mr_labels, additional_group_spec_list) }
let(:mr_labels) { [] }
+ let(:additional_group_spec_list) { [] }
before do
allow(File).to receive(:directory?).and_return(false)
@@ -75,6 +76,43 @@ RSpec.describe QA::Tools::Ci::QaChanges do
)
end
end
+
+ context "when configured to run tests from other stages" do
+ let(:additional_group_spec_list) do
+ {
+ 'foo' => %w[create],
+ 'bar' => %w[monitor verify]
+ }
+ end
+
+ context "with a single extra stage configured for the group name" do
+ let(:mr_labels) { %w[devops::manage group::foo] }
+
+ it ".qa_tests return specs for both devops stage and create stage" do
+ expect(qa_changes.qa_tests.split(" ")).to include(
+ "qa/specs/features/browser_ui/1_manage/",
+ "qa/specs/features/api/1_manage/",
+ "qa/specs/features/browser_ui/3_create/",
+ "qa/specs/features/api/3_create/"
+ )
+ end
+ end
+
+ context "with a multiple extra stages configured for the group name" do
+ let(:mr_labels) { %w[devops::manage group::bar] }
+
+ it ".qa_tests return specs for both devops stage and multiple other stages" do
+ expect(qa_changes.qa_tests.split(" ")).to include(
+ "qa/specs/features/browser_ui/1_manage/",
+ "qa/specs/features/api/1_manage/",
+ "qa/specs/features/browser_ui/8_monitor/",
+ "qa/specs/features/api/8_monitor/",
+ "qa/specs/features/browser_ui/4_verify/",
+ "qa/specs/features/api/4_verify/"
+ )
+ end
+ end
+ end
end
context "with quarantine changes" do
diff --git a/qa/tasks/ci.rake b/qa/tasks/ci.rake
index 44a794d9f94..435fe8ebb77 100644
--- a/qa/tasks/ci.rake
+++ b/qa/tasks/ci.rake
@@ -13,8 +13,10 @@ namespace :ci do
diff = mr_diff
labels = mr_labels
+ # Assign mapping of groups to tests in stages other than the groups defined stage
+ additional_group_spec_list = { 'gitaly' => %w[create] }
- qa_changes = QA::Tools::Ci::QaChanges.new(diff, labels)
+ qa_changes = QA::Tools::Ci::QaChanges.new(diff, labels, additional_group_spec_list)
logger = qa_changes.logger
logger.info("Analyzing merge request changes")
diff --git a/spec/controllers/groups/runners_controller_spec.rb b/spec/controllers/groups/runners_controller_spec.rb
index 77c62c0d930..1da868c6c4b 100644
--- a/spec/controllers/groups/runners_controller_spec.rb
+++ b/spec/controllers/groups/runners_controller_spec.rb
@@ -8,9 +8,11 @@ RSpec.describe Groups::RunnersController do
let_it_be(:project) { create(:project, group: group) }
let!(:runner) { create(:ci_runner, :group, groups: [group]) }
- let!(:runner_project) { create(:ci_runner, :project, projects: [project]) }
+ let!(:project_runner) { create(:ci_runner, :project, projects: [project]) }
+ let!(:instance_runner) { create(:ci_runner, :instance) }
- let(:params_runner_project) { { group_id: group, id: runner_project } }
+ let(:params_runner_project) { { group_id: group, id: project_runner } }
+ let(:params_runner_instance) { { group_id: group, id: instance_runner } }
let(:params) { { group_id: group, id: runner } }
before do
@@ -70,8 +72,33 @@ RSpec.describe Groups::RunnersController do
expect(response).to render_template(:show)
end
+ context 'when runners_finder_all_available is enabled' do
+ before do
+ stub_feature_flags(runners_finder_all_available: true)
+ end
+
+ it 'renders show with 200 status code instance runner' do
+ get :show, params: { group_id: group, id: instance_runner }
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response).to render_template(:show)
+ end
+ end
+
+ context 'when runners_finder_all_available is disabled' do
+ before do
+ stub_feature_flags(runners_finder_all_available: false)
+ end
+
+ it 'renders show with a 404 instance runner' do
+ get :show, params: { group_id: group, id: instance_runner }
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
+
it 'renders show with 200 status code project runner' do
- get :show, params: { group_id: group, id: runner_project }
+ get :show, params: { group_id: group, id: project_runner }
expect(response).to have_gitlab_http_status(:ok)
expect(response).to render_template(:show)
@@ -89,8 +116,14 @@ RSpec.describe Groups::RunnersController do
expect(response).to have_gitlab_http_status(:not_found)
end
+ it 'renders a 404 instance runner' do
+ get :show, params: { group_id: group, id: instance_runner }
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+
it 'renders a 404 project runner' do
- get :show, params: { group_id: group, id: runner_project }
+ get :show, params: { group_id: group, id: project_runner }
expect(response).to have_gitlab_http_status(:not_found)
end
@@ -103,15 +136,21 @@ RSpec.describe Groups::RunnersController do
group.add_owner(user)
end
- it 'renders show with 200 status code' do
+ it 'renders edit with 200 status code' do
get :edit, params: { group_id: group, id: runner }
expect(response).to have_gitlab_http_status(:ok)
expect(response).to render_template(:edit)
end
- it 'renders show with 200 status code project runner' do
- get :edit, params: { group_id: group, id: runner_project }
+ it 'renders a 404 instance runner' do
+ get :edit, params: { group_id: group, id: instance_runner }
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+
+ it 'renders edit with 200 status code project runner' do
+ get :edit, params: { group_id: group, id: project_runner }
expect(response).to have_gitlab_http_status(:ok)
expect(response).to render_template(:edit)
@@ -130,7 +169,7 @@ RSpec.describe Groups::RunnersController do
end
it 'renders a 404 project runner' do
- get :edit, params: { group_id: group, id: runner_project }
+ get :edit, params: { group_id: group, id: project_runner }
expect(response).to have_gitlab_http_status(:not_found)
end
@@ -154,15 +193,26 @@ RSpec.describe Groups::RunnersController do
expect(runner.reload.description).to eq(new_desc)
end
+ it 'does not update the instance runner' do
+ new_desc = instance_runner.description.swapcase
+
+ expect do
+ post :update, params: params_runner_instance.merge(runner: { description: new_desc } )
+ end.to not_change { instance_runner.ensure_runner_queue_value }
+ .and not_change { instance_runner.description }
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+
it 'updates the project runner, ticks the queue, and redirects project runner' do
- new_desc = runner_project.description.swapcase
+ new_desc = project_runner.description.swapcase
expect do
post :update, params: params_runner_project.merge(runner: { description: new_desc } )
- end.to change { runner_project.ensure_runner_queue_value }
+ end.to change { project_runner.ensure_runner_queue_value }
expect(response).to have_gitlab_http_status(:found)
- expect(runner_project.reload.description).to eq(new_desc)
+ expect(project_runner.reload.description).to eq(new_desc)
end
end
@@ -182,15 +232,26 @@ RSpec.describe Groups::RunnersController do
expect(runner.reload.description).to eq(old_desc)
end
+ it 'rejects the update and responds 404 instance runner' do
+ old_desc = instance_runner.description
+
+ expect do
+ post :update, params: params_runner_instance.merge(runner: { description: old_desc.swapcase } )
+ end.not_to change { instance_runner.ensure_runner_queue_value }
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ expect(instance_runner.reload.description).to eq(old_desc)
+ end
+
it 'rejects the update and responds 404 project runner' do
- old_desc = runner_project.description
+ old_desc = project_runner.description
expect do
post :update, params: params_runner_project.merge(runner: { description: old_desc.swapcase } )
- end.not_to change { runner_project.ensure_runner_queue_value }
+ end.not_to change { project_runner.ensure_runner_queue_value }
expect(response).to have_gitlab_http_status(:not_found)
- expect(runner_project.reload.description).to eq(old_desc)
+ expect(project_runner.reload.description).to eq(old_desc)
end
end
end
diff --git a/spec/factories/ci/build_metadata.rb b/spec/factories/ci/build_metadata.rb
index cfc86c4ef4b..a0a5305ef39 100644
--- a/spec/factories/ci/build_metadata.rb
+++ b/spec/factories/ci/build_metadata.rb
@@ -3,5 +3,9 @@
FactoryBot.define do
factory :ci_build_metadata, class: 'Ci::BuildMetadata' do
build { association(:ci_build, strategy: :build, metadata: instance) }
+
+ after(:build) do |metadata|
+ metadata.build&.valid?
+ end
end
end
diff --git a/spec/factories/ci/builds.rb b/spec/factories/ci/builds.rb
index 8c2edc8cd9f..9a3b2837ab8 100644
--- a/spec/factories/ci/builds.rb
+++ b/spec/factories/ci/builds.rb
@@ -424,79 +424,79 @@ FactoryBot.define do
trait :codequality_report do
after(:build) do |build|
- build.job_artifacts << create(:ci_job_artifact, :codequality, job: build)
+ build.job_artifacts << build(:ci_job_artifact, :codequality, job: build)
end
end
trait :sast_report do
after(:build) do |build|
- build.job_artifacts << create(:ci_job_artifact, :sast, job: build)
+ build.job_artifacts << build(:ci_job_artifact, :sast, job: build)
end
end
trait :secret_detection_report do
after(:build) do |build|
- build.job_artifacts << create(:ci_job_artifact, :secret_detection, job: build)
+ build.job_artifacts << build(:ci_job_artifact, :secret_detection, job: build)
end
end
trait :test_reports do
after(:build) do |build|
- build.job_artifacts << create(:ci_job_artifact, :junit, job: build)
+ build.job_artifacts << build(:ci_job_artifact, :junit, job: build)
end
end
trait :test_reports_with_attachment do
after(:build) do |build|
- build.job_artifacts << create(:ci_job_artifact, :junit_with_attachment, job: build)
+ build.job_artifacts << build(:ci_job_artifact, :junit_with_attachment, job: build)
end
end
trait :broken_test_reports do
after(:build) do |build|
- build.job_artifacts << create(:ci_job_artifact, :junit_with_corrupted_data, job: build)
+ build.job_artifacts << build(:ci_job_artifact, :junit_with_corrupted_data, job: build)
end
end
trait :test_reports_with_duplicate_failed_test_names do
after(:build) do |build|
- build.job_artifacts << create(:ci_job_artifact, :junit_with_duplicate_failed_test_names, job: build)
+ build.job_artifacts << build(:ci_job_artifact, :junit_with_duplicate_failed_test_names, job: build)
end
end
trait :test_reports_with_three_failures do
after(:build) do |build|
- build.job_artifacts << create(:ci_job_artifact, :junit_with_three_failures, job: build)
+ build.job_artifacts << build(:ci_job_artifact, :junit_with_three_failures, job: build)
end
end
trait :accessibility_reports do
after(:build) do |build|
- build.job_artifacts << create(:ci_job_artifact, :accessibility, job: build)
+ build.job_artifacts << build(:ci_job_artifact, :accessibility, job: build)
end
end
trait :coverage_reports do
after(:build) do |build|
- build.job_artifacts << create(:ci_job_artifact, :cobertura, job: build)
+ build.job_artifacts << build(:ci_job_artifact, :cobertura, job: build)
end
end
trait :codequality_reports do
after(:build) do |build|
- build.job_artifacts << create(:ci_job_artifact, :codequality, job: build)
+ build.job_artifacts << build(:ci_job_artifact, :codequality, job: build)
end
end
trait :codequality_reports_without_degradation do
after(:build) do |build|
- build.job_artifacts << create(:ci_job_artifact, :codequality_without_errors, job: build)
+ build.job_artifacts << build(:ci_job_artifact, :codequality_without_errors, job: build)
end
end
trait :terraform_reports do
after(:build) do |build|
- build.job_artifacts << create(:ci_job_artifact, :terraform, job: build)
+ build.job_artifacts << build(:ci_job_artifact, :terraform, job: build)
end
end
diff --git a/spec/factories/ci/pipelines.rb b/spec/factories/ci/pipelines.rb
index 5b20010ef7e..650b8647237 100644
--- a/spec/factories/ci/pipelines.rb
+++ b/spec/factories/ci/pipelines.rb
@@ -8,6 +8,7 @@ FactoryBot.define do
sha { 'b83d6e391c22777fca1ed3012fce84f633d7fed0' }
status { 'pending' }
add_attribute(:protected) { false }
+ partition_id { 1234 }
project
@@ -53,6 +54,7 @@ FactoryBot.define do
end
factory :ci_pipeline do
+ partition_id { 1234 }
transient { ci_ref_presence { true } }
before(:create) do |pipeline, evaluator|
diff --git a/spec/features/milestones/user_creates_milestone_spec.rb b/spec/features/milestones/user_creates_milestone_spec.rb
index dd377aa4a26..1ab231632fb 100644
--- a/spec/features/milestones/user_creates_milestone_spec.rb
+++ b/spec/features/milestones/user_creates_milestone_spec.rb
@@ -3,29 +3,100 @@
require 'spec_helper'
RSpec.describe "User creates milestone", :js do
- let_it_be(:user) { create(:user) }
- let_it_be(:project) { create(:project) }
+ let_it_be(:developer) { create(:user) }
+ let_it_be(:inherited_guest) { create(:user) }
+ let_it_be(:inherited_developer) { create(:user) }
+ let_it_be(:group) { create(:group, :public) }
+
+ shared_examples 'creates milestone' do
+ specify do
+ title = "v2.3"
+
+ fill_in("Title", with: title)
+ fill_in("Description", with: "# Description header")
+ click_button("Create milestone")
+
+ expect(page).to have_content(title)
+ .and have_content("Issues")
+ .and have_header_with_correct_id_and_link(1, "Description header", "description-header")
+
+ visit(activity_project_path(project))
+
+ expect(page).to have_content("#{user.name} #{user.to_reference} opened milestone")
+ end
+ end
+
+ shared_examples 'renders not found' do
+ specify do
+ expect(page).to have_title('Not Found')
+ expect(page).to have_content('Page Not Found')
+ end
+ end
+
+ before_all do
+ group.add_guest(inherited_guest)
+ group.add_developer(inherited_developer)
+ end
before do
- project.add_developer(user)
sign_in(user)
-
visit(new_project_milestone_path(project))
end
- it "creates milestone" do
- title = "v2.3"
+ context 'when project is public' do
+ let_it_be(:project) { create(:project, :public, group: group) }
+
+ context 'and issues and merge requests are private' do
+ before_all do
+ project.project_feature.update!(
+ issues_access_level: ProjectFeature::PRIVATE,
+ merge_requests_access_level: ProjectFeature::PRIVATE
+ )
+ end
+
+ context 'when user is an inherited member from the group' do
+ context 'and user is a guest' do
+ let(:user) { inherited_guest }
+
+ it_behaves_like 'renders not found'
+ end
+
+ context 'and user is a developer' do
+ let(:user) { inherited_developer }
+
+ it_behaves_like 'creates milestone'
+ end
+ end
+ end
+ end
+
+ context 'when project is private' do
+ let_it_be(:project) { create(:project, :private, group: group) }
+
+ context 'and user is a direct project member' do
+ before_all do
+ project.add_developer(developer)
+ end
+
+ context 'when user is a developer' do
+ let(:user) { developer }
+
+ it_behaves_like 'creates milestone'
+ end
+ end
- fill_in("Title", with: title)
- fill_in("Description", with: "# Description header")
- click_button("Create milestone")
+ context 'and user is an inherited member from the group' do
+ context 'when user is a guest' do
+ let(:user) { inherited_guest }
- expect(page).to have_content(title)
- .and have_content("Issues")
- .and have_header_with_correct_id_and_link(1, "Description header", "description-header")
+ it_behaves_like 'renders not found'
+ end
- visit(activity_project_path(project))
+ context 'when user is a developer' do
+ let(:user) { inherited_developer }
- expect(page).to have_content("#{user.name} #{user.to_reference} opened milestone")
+ it_behaves_like 'creates milestone'
+ end
+ end
end
end
diff --git a/spec/frontend/flash_spec.js b/spec/frontend/flash_spec.js
index e26c52f0bf7..a809bf248bf 100644
--- a/spec/frontend/flash_spec.js
+++ b/spec/frontend/flash_spec.js
@@ -285,6 +285,13 @@ describe('Flash', () => {
expect(document.querySelector('.gl-alert')).toBeNull();
});
+ it('does not crash if calling .dismiss() twice', () => {
+ alert = createAlert({ message: mockMessage });
+
+ alert.dismiss();
+ expect(() => alert.dismiss()).not.toThrow();
+ });
+
it('calls onDismiss when dismissed', () => {
const dismissHandler = jest.fn();
diff --git a/spec/graphql/types/branch_rule_type_spec.rb b/spec/graphql/types/branch_rule_type_spec.rb
index 277901f00bf..12a2c8dfe12 100644
--- a/spec/graphql/types/branch_rule_type_spec.rb
+++ b/spec/graphql/types/branch_rule_type_spec.rb
@@ -10,6 +10,7 @@ RSpec.describe GitlabSchema.types['BranchRule'] do
let(:fields) do
%i[
name
+ isDefault
branch_protection
created_at
updated_at
diff --git a/spec/lib/gitlab/background_migration/update_ci_pipeline_artifacts_unknown_locked_status_spec.rb b/spec/lib/gitlab/background_migration/update_ci_pipeline_artifacts_unknown_locked_status_spec.rb
new file mode 100644
index 00000000000..98939e15952
--- /dev/null
+++ b/spec/lib/gitlab/background_migration/update_ci_pipeline_artifacts_unknown_locked_status_spec.rb
@@ -0,0 +1,62 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::BackgroundMigration::UpdateCiPipelineArtifactsUnknownLockedStatus do
+ describe '#perform' do
+ let(:batch_table) { :ci_pipeline_artifacts }
+ let(:batch_column) { :id }
+
+ let(:sub_batch_size) { 1 }
+ let(:pause_ms) { 0 }
+ let(:connection) { Ci::ApplicationRecord.connection }
+
+ let(:namespaces) { table(:namespaces) }
+ let(:projects) { table(:projects) }
+ let(:pipelines) { table(:ci_pipelines, database: :ci) }
+ let(:pipeline_artifacts) { table(:ci_pipeline_artifacts, database: :ci) }
+
+ let(:namespace) { namespaces.create!(name: 'name', path: 'path') }
+ let(:project) do
+ projects
+ .create!(name: "project", path: "project", namespace_id: namespace.id, project_namespace_id: namespace.id)
+ end
+
+ let(:unlocked) { 0 }
+ let(:locked) { 1 }
+ let(:unknown) { 2 }
+
+ let(:unlocked_pipeline) { pipelines.create!(locked: unlocked) }
+ let(:locked_pipeline) { pipelines.create!(locked: locked) }
+
+ # rubocop:disable Layout/LineLength
+ let!(:locked_artifact) { pipeline_artifacts.create!(project_id: project.id, pipeline_id: locked_pipeline.id, size: 1024, file_type: 0, file_format: 'gzip', file: 'a.gz', locked: unknown) }
+ let!(:unlocked_artifact_1) { pipeline_artifacts.create!(project_id: project.id, pipeline_id: unlocked_pipeline.id, size: 2048, file_type: 1, file_format: 'raw', file: 'b', locked: unknown) }
+ let!(:unlocked_artifact_2) { pipeline_artifacts.create!(project_id: project.id, pipeline_id: unlocked_pipeline.id, size: 4096, file_type: 2, file_format: 'gzip', file: 'c.gz', locked: unknown) }
+ let!(:already_unlocked_artifact) { pipeline_artifacts.create!(project_id: project.id, pipeline_id: unlocked_pipeline.id, size: 8192, file_type: 3, file_format: 'raw', file: 'd', locked: unlocked) }
+ let!(:already_locked_artifact) { pipeline_artifacts.create!(project_id: project.id, pipeline_id: locked_pipeline.id, size: 8192, file_type: 3, file_format: 'raw', file: 'd', locked: locked) }
+ # rubocop:enable Layout/LineLength
+
+ subject do
+ described_class.new(
+ start_id: locked_artifact.id,
+ end_id: already_locked_artifact.id,
+ batch_table: batch_table,
+ batch_column: batch_column,
+ sub_batch_size: sub_batch_size,
+ pause_ms: pause_ms,
+ connection: connection
+ ).perform
+ end
+
+ it 'updates ci_pipeline_artifacts with unknown lock status' do
+ subject
+
+ expect(locked_artifact.reload.locked).to eq(locked)
+ expect(unlocked_artifact_1.reload.locked).to eq(unlocked)
+ expect(unlocked_artifact_2.reload.locked).to eq(unlocked)
+ expect(already_unlocked_artifact.reload.locked).to eq(unlocked)
+ expect(already_locked_artifact.reload.locked).to eq(locked)
+ end
+ end
+end
diff --git a/spec/migrations/20220928225711_schedule_update_ci_pipeline_artifacts_locked_status_spec.rb b/spec/migrations/20220928225711_schedule_update_ci_pipeline_artifacts_locked_status_spec.rb
new file mode 100644
index 00000000000..7e3f8caa966
--- /dev/null
+++ b/spec/migrations/20220928225711_schedule_update_ci_pipeline_artifacts_locked_status_spec.rb
@@ -0,0 +1,31 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require_migration!
+
+RSpec.describe ScheduleUpdateCiPipelineArtifactsLockedStatus, migration: :gitlab_ci do
+ let_it_be(:migration) { described_class::MIGRATION }
+
+ describe '#up' do
+ it 'schedules background jobs for each batch of ci_pipeline_artifacts' do
+ migrate!
+
+ expect(migration).to have_scheduled_batched_migration(
+ gitlab_schema: :gitlab_ci,
+ table_name: :ci_pipeline_artifacts,
+ column_name: :id,
+ batch_size: described_class::BATCH_SIZE,
+ sub_batch_size: described_class::SUB_BATCH_SIZE
+ )
+ end
+ end
+
+ describe '#down' do
+ it 'deletes all batched migration records' do
+ migrate!
+ schema_migrate_down!
+
+ expect(migration).not_to have_scheduled_batched_migration
+ end
+ end
+end
diff --git a/spec/models/ci/build_metadata_spec.rb b/spec/models/ci/build_metadata_spec.rb
index e904463a5ca..16cff72db64 100644
--- a/spec/models/ci/build_metadata_spec.rb
+++ b/spec/models/ci/build_metadata_spec.rb
@@ -14,8 +14,8 @@ RSpec.describe Ci::BuildMetadata do
status: 'success')
end
- let(:build) { create(:ci_build, pipeline: pipeline) }
- let(:metadata) { build.metadata }
+ let(:job) { create(:ci_build, pipeline: pipeline) }
+ let(:metadata) { job.metadata }
it_behaves_like 'having unique enum values'
@@ -35,7 +35,7 @@ RSpec.describe Ci::BuildMetadata do
context 'when project timeout is set' do
context 'when runner is assigned to the job' do
before do
- build.update!(runner: runner)
+ job.update!(runner: runner)
end
context 'when runner timeout is not set' do
@@ -59,13 +59,13 @@ RSpec.describe Ci::BuildMetadata do
context 'when job timeout is set' do
context 'when job timeout is higher than project timeout' do
- let(:build) { create(:ci_build, pipeline: pipeline, options: { job_timeout: 3000 }) }
+ let(:job) { create(:ci_build, pipeline: pipeline, options: { job_timeout: 3000 }) }
it_behaves_like 'sets timeout', 'job_timeout_source', 3000
end
context 'when job timeout is lower than project timeout' do
- let(:build) { create(:ci_build, pipeline: pipeline, options: { job_timeout: 1000 }) }
+ let(:job) { create(:ci_build, pipeline: pipeline, options: { job_timeout: 1000 }) }
it_behaves_like 'sets timeout', 'job_timeout_source', 1000
end
@@ -73,18 +73,18 @@ RSpec.describe Ci::BuildMetadata do
context 'when both runner and job timeouts are set' do
before do
- build.update!(runner: runner)
+ job.update!(runner: runner)
end
context 'when job timeout is higher than runner timeout' do
- let(:build) { create(:ci_build, pipeline: pipeline, options: { job_timeout: 3000 }) }
+ let(:job) { create(:ci_build, pipeline: pipeline, options: { job_timeout: 3000 }) }
let(:runner) { create(:ci_runner, maximum_timeout: 2100) }
it_behaves_like 'sets timeout', 'runner_timeout_source', 2100
end
context 'when job timeout is lower than runner timeout' do
- let(:build) { create(:ci_build, pipeline: pipeline, options: { job_timeout: 1900 }) }
+ let(:job) { create(:ci_build, pipeline: pipeline, options: { job_timeout: 1900 }) }
let(:runner) { create(:ci_runner, maximum_timeout: 2100) }
it_behaves_like 'sets timeout', 'job_timeout_source', 1900
@@ -135,20 +135,51 @@ RSpec.describe Ci::BuildMetadata do
describe 'set_cancel_gracefully' do
it 'sets cancel_gracefully' do
- build.set_cancel_gracefully
+ job.set_cancel_gracefully
- expect(build.cancel_gracefully?).to be true
+ expect(job.cancel_gracefully?).to be true
end
it 'returns false' do
- expect(build.cancel_gracefully?).to be false
+ expect(job.cancel_gracefully?).to be false
end
end
context 'loose foreign key on ci_builds_metadata.project_id' do
it_behaves_like 'cleanup by a loose foreign key' do
- let!(:parent) { create(:project) }
- let!(:model) { create(:ci_build_metadata, project: parent) }
+ let!(:parent) { project }
+ let!(:model) { metadata }
+ end
+ end
+
+ describe 'partitioning' do
+ context 'with job' do
+ let(:status) { build(:commit_status, partition_id: 123) }
+ let(:metadata) { build(:ci_build_metadata, build: status) }
+
+ it 'copies the partition_id from job' do
+ expect { metadata.valid? }.to change(metadata, :partition_id).to(123)
+ end
+
+ context 'when it is already set' do
+ let(:metadata) { build(:ci_build_metadata, build: status, partition_id: 125) }
+
+ it 'does not change the partition_id value' do
+ expect { metadata.valid? }.not_to change(metadata, :partition_id)
+ end
+ end
+ end
+
+ context 'without job' do
+ subject(:metadata) do
+ build(:ci_build_metadata, build: nil)
+ end
+
+ it { is_expected.to validate_presence_of(:partition_id) }
+
+ it 'does not change the partition_id value' do
+ expect { metadata.valid? }.not_to change(metadata, :partition_id)
+ end
end
end
end
diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb
index 41b817f231b..52142278c15 100644
--- a/spec/models/ci/build_spec.rb
+++ b/spec/models/ci/build_spec.rb
@@ -320,10 +320,10 @@ RSpec.describe Ci::Build do
let(:artifact_scope) { Ci::JobArtifact.where(file_type: 'archive') }
- let!(:build_1) { create(:ci_build, :artifacts) }
- let!(:build_2) { create(:ci_build, :codequality_reports) }
- let!(:build_3) { create(:ci_build, :test_reports) }
- let!(:build_4) { create(:ci_build, :artifacts) }
+ let!(:build_1) { create(:ci_build, :artifacts, pipeline: pipeline) }
+ let!(:build_2) { create(:ci_build, :codequality_reports, pipeline: pipeline) }
+ let!(:build_3) { create(:ci_build, :test_reports, pipeline: pipeline) }
+ let!(:build_4) { create(:ci_build, :artifacts, pipeline: pipeline) }
it 'returns artifacts matching the given scope' do
expect(builds).to contain_exactly(build_1, build_4)
diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb
index 3fc1883c1e6..8bf32527342 100644
--- a/spec/models/ci/pipeline_spec.rb
+++ b/spec/models/ci/pipeline_spec.rb
@@ -5488,7 +5488,7 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
end
describe 'partitioning' do
- let(:pipeline) { build(:ci_pipeline) }
+ let(:pipeline) { build(:ci_pipeline, partition_id: nil) }
before do
allow(described_class).to receive(:current_partition_value) { 123 }
diff --git a/spec/models/concerns/ci/partitionable_spec.rb b/spec/models/concerns/ci/partitionable_spec.rb
new file mode 100644
index 00000000000..d53501ccc3d
--- /dev/null
+++ b/spec/models/concerns/ci/partitionable_spec.rb
@@ -0,0 +1,26 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Ci::Partitionable do
+ describe 'partitionable models inclusion' do
+ let(:ci_model) { Class.new(Ci::ApplicationRecord) }
+
+ subject { ci_model.include(described_class) }
+
+ it 'raises an exception' do
+ expect { subject }
+ .to raise_error(/must be included in PARTITIONABLE_MODELS/)
+ end
+
+ context 'when is included in the models list' do
+ before do
+ stub_const("#{described_class}::Testing::PARTITIONABLE_MODELS", [ci_model.name])
+ end
+
+ it 'does not raise exceptions' do
+ expect { subject }.not_to raise_error
+ end
+ end
+ end
+end
diff --git a/spec/models/protected_branch_spec.rb b/spec/models/protected_branch_spec.rb
index 54a90ca6049..b88367b9ca2 100644
--- a/spec/models/protected_branch_spec.rb
+++ b/spec/models/protected_branch_spec.rb
@@ -435,4 +435,28 @@ RSpec.describe ProtectedBranch do
expect(described_class.downcase_humanized_name).to eq 'protected branch'
end
end
+
+ describe '.default_branch?' do
+ before do
+ allow(subject.project).to receive(:default_branch).and_return(branch)
+ end
+
+ context 'when the name matches the default branch' do
+ let(:branch) { subject.name }
+
+ it { is_expected.to be_default_branch }
+ end
+
+ context 'when the name does not match the default branch' do
+ let(:branch) { "#{subject.name}qwerty" }
+
+ it { is_expected.not_to be_default_branch }
+ end
+
+ context 'when a wildcard name matches the default branch' do
+ let(:branch) { "#{subject.name}*" }
+
+ it { is_expected.not_to be_default_branch }
+ end
+ end
end
diff --git a/spec/requests/api/graphql/project/branch_rules_spec.rb b/spec/requests/api/graphql/project/branch_rules_spec.rb
index 70fb37941e2..1aaf0e9edc7 100644
--- a/spec/requests/api/graphql/project/branch_rules_spec.rb
+++ b/spec/requests/api/graphql/project/branch_rules_spec.rb
@@ -21,27 +21,24 @@ RSpec.describe 'getting list of branch rules for a project' do
let(:branch_rules_data) { graphql_data_at('project', 'branchRules', 'edges') }
let(:variables) { { path: project.full_path } }
-
- let(:fields) do
- <<~QUERY
- pageInfo {
- hasNextPage
- hasPreviousPage
- }
- edges {
- cursor
- node {
- #{all_graphql_fields_for('branch_rules'.classify)}
- }
- }
- QUERY
- end
-
+ # fields must use let as the all_graphql_fields_for also configures some spies
+ let(:fields) { all_graphql_fields_for('BranchRule') }
let(:query) do
<<~GQL
query($path: ID!, $n: Int, $cursor: String) {
project(fullPath: $path) {
- branchRules(first: $n, after: $cursor) { #{fields} }
+ branchRules(first: $n, after: $cursor) {
+ pageInfo {
+ hasNextPage
+ hasPreviousPage
+ }
+ edges {
+ cursor
+ node {
+ #{fields}
+ }
+ }
+ }
}
}
GQL
@@ -55,7 +52,9 @@ RSpec.describe 'getting list of branch rules for a project' do
it_behaves_like 'a working graphql query'
- it { expect(branch_rules_data).to be_empty }
+ it 'hides branch rules data' do
+ expect(branch_rules_data).to be_empty
+ end
end
context 'when the user does have read_protected_branch abilities' do
@@ -66,12 +65,17 @@ RSpec.describe 'getting list of branch rules for a project' do
it_behaves_like 'a working graphql query'
- it 'includes a name' do
+ it 'returns branch rules data' do
expect(branch_rules_data.dig(0, 'node', 'name')).to be_present
- end
-
- it 'includes created_at and updated_at' do
+ expect(branch_rules_data.dig(0, 'node', 'isDefault')).to be(true).or be(false)
+ expect(branch_rules_data.dig(0, 'node', 'branchProtection')).to be_present
expect(branch_rules_data.dig(0, 'node', 'createdAt')).to be_present
+ expect(branch_rules_data.dig(0, 'node', 'updatedAt')).to be_present
+
+ expect(branch_rules_data.dig(1, 'node', 'name')).to be_present
+ expect(branch_rules_data.dig(1, 'node', 'isDefault')).to be(true).or be(false)
+ expect(branch_rules_data.dig(1, 'node', 'branchProtection')).to be_present
+ expect(branch_rules_data.dig(1, 'node', 'createdAt')).to be_present
expect(branch_rules_data.dig(1, 'node', 'updatedAt')).to be_present
end
@@ -82,16 +86,16 @@ RSpec.describe 'getting list of branch rules for a project' do
{ path: project.full_path, n: branch_rule_limit, cursor: last_cursor }
end
- it_behaves_like 'a working graphql query' do
- it 'only returns N branch_rules' do
- expect(branch_rules_data.size).to eq(branch_rule_limit)
- expect(has_next_page).to be_truthy
- expect(has_prev_page).to be_falsey
- post_graphql(query, current_user: current_user, variables: next_variables)
- expect(branch_rules_data.size).to eq(branch_rule_limit)
- expect(has_next_page).to be_falsey
- expect(has_prev_page).to be_truthy
- end
+ it_behaves_like 'a working graphql query'
+
+ it 'returns pagination information' do
+ expect(branch_rules_data.size).to eq(branch_rule_limit)
+ expect(has_next_page).to be_truthy
+ expect(has_prev_page).to be_falsey
+ post_graphql(query, current_user: current_user, variables: next_variables)
+ expect(branch_rules_data.size).to eq(branch_rule_limit)
+ expect(has_next_page).to be_falsey
+ expect(has_prev_page).to be_truthy
end
context 'when no limit is provided' do
diff --git a/spec/services/ci/job_artifacts/create_service_spec.rb b/spec/services/ci/job_artifacts/create_service_spec.rb
index a2259f9813b..030ba84951e 100644
--- a/spec/services/ci/job_artifacts/create_service_spec.rb
+++ b/spec/services/ci/job_artifacts/create_service_spec.rb
@@ -182,7 +182,8 @@ RSpec.describe Ci::JobArtifacts::CreateService do
end
context 'with job partitioning' do
- let(:job) { create(:ci_build, project: project, partition_id: 123) }
+ let(:pipeline) { create(:ci_pipeline, project: project, partition_id: 123) }
+ let(:job) { create(:ci_build, pipeline: pipeline) }
it 'sets partition_id on artifacts' do
expect { subject }.to change { Ci::JobArtifact.count }
diff --git a/spec/services/packages/rpm/repository_metadata/build_repomd_xml_spec.rb b/spec/services/packages/rpm/repository_metadata/build_repomd_xml_spec.rb
index 29b0f73e3c1..0843a983b7e 100644
--- a/spec/services/packages/rpm/repository_metadata/build_repomd_xml_spec.rb
+++ b/spec/services/packages/rpm/repository_metadata/build_repomd_xml_spec.rb
@@ -62,5 +62,25 @@ RSpec.describe Packages::Rpm::RepositoryMetadata::BuildRepomdXml do
end
end
end
+
+ context 'when data values has unexpected keys' do
+ let(:data) do
+ {
+ filelists: described_class::ALLOWED_DATA_VALUE_KEYS.each_with_object({}) do |key, result|
+ result[:"#{key}-wrong"] = { value: 'value' }
+ end
+ }
+ end
+
+ it 'ignores wrong keys' do
+ result = Nokogiri::XML::Document.parse(subject).remove_namespaces!
+
+ data.each do |tag_name, tag_attributes|
+ tag_attributes.each_key do |key|
+ expect(result.at("//repomd/data[@type=\"#{tag_name}\"]/#{key}")).to be_nil
+ end
+ end
+ end
+ end
end
end
diff --git a/spec/support/models/partitionable_check.rb b/spec/support/models/partitionable_check.rb
new file mode 100644
index 00000000000..2c09c1b3408
--- /dev/null
+++ b/spec/support/models/partitionable_check.rb
@@ -0,0 +1,46 @@
+# frozen_string_literal: true
+
+module PartitioningTesting
+ module CascadeCheck
+ extend ActiveSupport::Concern
+
+ included do
+ after_create :check_partition_cascade_value
+ end
+
+ def check_partition_cascade_value
+ raise 'Partition value not found' unless partition_scope_value
+ raise 'Default value detected' if partition_id == 100
+
+ return if partition_id == partition_scope_value
+
+ raise "partition_id was expected to equal #{partition_scope_value} but it was #{partition_id}."
+ end
+ end
+
+ module DefaultPartitionValue
+ extend ActiveSupport::Concern
+
+ class_methods do
+ def current_partition_value
+ current = super
+
+ if current == 100
+ 54321
+ else
+ current
+ end
+ end
+ end
+ end
+end
+
+Ci::Partitionable::Testing::PARTITIONABLE_MODELS.each do |klass|
+ model = klass.safe_constantize
+
+ if klass == 'Ci::Pipeline'
+ model.prepend(PartitioningTesting::DefaultPartitionValue)
+ else
+ model.include(PartitioningTesting::CascadeCheck)
+ end
+end
diff --git a/spec/views/registrations/welcome/show.html.haml_spec.rb b/spec/views/registrations/welcome/show.html.haml_spec.rb
index d9c5d348e15..99d87ac449b 100644
--- a/spec/views/registrations/welcome/show.html.haml_spec.rb
+++ b/spec/views/registrations/welcome/show.html.haml_spec.rb
@@ -11,6 +11,7 @@ RSpec.describe 'registrations/welcome/show' do
allow(view).to receive(:in_trial_flow?).and_return(false)
allow(view).to receive(:user_has_memberships?).and_return(false)
allow(view).to receive(:in_oauth_flow?).and_return(false)
+ allow(view).to receive(:glm_tracking_params).and_return({})
render
end