Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
a99d0fa692
commit
d83bbccfcd
|
|
@ -1 +1 @@
|
|||
4c15523cf680c107c5aa2b8268674cd0345a6b78
|
||||
7397b0e1c1a4a8fc0290347da1ddf7ac11547a18
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
@ -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(() => {});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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?
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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')
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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?
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -0,0 +1 @@
|
|||
5ec9b3f36a986cbb86c8005a4425307f0f4399a4a4030460e715370630cb9490
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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. |
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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).
|
||||
|
|
|
|||
|
|
@ -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]`.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 | | | | | ✓ |
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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."
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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|
|
||||
|
|
|
|||
|
|
@ -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) }
|
||||
|
||||
fill_in("Title", with: title)
|
||||
fill_in("Description", with: "# Description header")
|
||||
click_button("Create milestone")
|
||||
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
|
||||
|
||||
expect(page).to have_content(title)
|
||||
.and have_content("Issues")
|
||||
.and have_header_with_correct_id_and_link(1, "Description header", "description-header")
|
||||
context 'when user is an inherited member from the group' do
|
||||
context 'and user is a guest' do
|
||||
let(:user) { inherited_guest }
|
||||
|
||||
visit(activity_project_path(project))
|
||||
it_behaves_like 'renders not found'
|
||||
end
|
||||
|
||||
expect(page).to have_content("#{user.name} #{user.to_reference} opened milestone")
|
||||
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
|
||||
|
||||
context 'and user is an inherited member from the group' do
|
||||
context 'when user is a guest' do
|
||||
let(:user) { inherited_guest }
|
||||
|
||||
it_behaves_like 'renders not found'
|
||||
end
|
||||
|
||||
context 'when user is a developer' do
|
||||
let(:user) { inherited_developer }
|
||||
|
||||
it_behaves_like 'creates milestone'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ RSpec.describe GitlabSchema.types['BranchRule'] do
|
|||
let(:fields) do
|
||||
%i[
|
||||
name
|
||||
isDefault
|
||||
branch_protection
|
||||
created_at
|
||||
updated_at
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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 }
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 }
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in New Issue