diff --git a/app/graphql/mutations/ci/project_ci_cd_settings_update.rb b/app/graphql/mutations/ci/project_ci_cd_settings_update.rb
index 082c345adf6d5e7980761d3e9fc7a4fe2eb89f11..7df277641bf32e412f9b8ce407ec0890f181a176 100644
--- a/app/graphql/mutations/ci/project_ci_cd_settings_update.rb
+++ b/app/graphql/mutations/ci/project_ci_cd_settings_update.rb
@@ -6,6 +6,7 @@ class ProjectCiCdSettingsUpdate < BaseMutation
graphql_name 'ProjectCiCdSettingsUpdate'
include FindsProject
+ include Gitlab::Utils::StrongMemoize
authorize :admin_project
@@ -37,13 +38,11 @@ class ProjectCiCdSettingsUpdate < BaseMutation
description: 'CI/CD settings after mutation.'
def resolve(full_path:, **args)
- project = authorized_find!(full_path)
-
if args[:job_token_scope_enabled]
raise Gitlab::Graphql::Errors::ArgumentError, 'job_token_scope_enabled can only be set to false'
end
- settings = project.ci_cd_settings
+ settings = project(full_path).ci_cd_settings
settings.update(args)
{
@@ -51,6 +50,14 @@ def resolve(full_path:, **args)
errors: errors_on_object(settings)
}
end
+
+ private
+
+ def project(full_path)
+ strong_memoize_with(:project, full_path) do
+ authorized_find!(full_path)
+ end
+ end
end
end
end
diff --git a/app/graphql/types/ci/ci_cd_setting_type.rb b/app/graphql/types/ci/ci_cd_setting_type.rb
index 45ecbf5c084a2f24bb57ccfcc9e70e8a786749cb..8a49c5a6a951d08637b09bb9eaca33be6a66aaf7 100644
--- a/app/graphql/types/ci/ci_cd_setting_type.rb
+++ b/app/graphql/types/ci/ci_cd_setting_type.rb
@@ -29,6 +29,7 @@ class CiCdSettingType < BaseObject
null: true,
description: 'Whether merge pipelines are enabled.',
method: :merge_pipelines_enabled?
+ # TODO(Issue 422295): this is EE only and should be moved to the EE file
field :merge_trains_enabled,
GraphQL::Types::Boolean,
null: true,
@@ -41,3 +42,5 @@ class CiCdSettingType < BaseObject
end
end
end
+
+Types::Ci::CiCdSettingType.prepend_mod_with('Types::Ci::CiCdSettingType')
diff --git a/app/models/project_ci_cd_setting.rb b/app/models/project_ci_cd_setting.rb
index cc9003423be687496eb70cfff8ebd06b7b84961c..8d049b8d1b1e54665ab71ae1898575ca474f5a16 100644
--- a/app/models/project_ci_cd_setting.rb
+++ b/app/models/project_ci_cd_setting.rb
@@ -19,6 +19,7 @@ class ProjectCiCdSetting < ApplicationRecord
attribute :forward_deployment_enabled, default: true
attribute :separated_caches, default: true
+ validates :merge_trains_skip_train_allowed, inclusion: { in: [true, false] }
chronic_duration_attr :runner_token_expiration_interval_human_readable, :runner_token_expiration_interval
diff --git a/config/feature_flags/development/merge_trains_skip_train.yml b/config/feature_flags/development/merge_trains_skip_train.yml
new file mode 100644
index 0000000000000000000000000000000000000000..3d60acef4579598ff793ff1004fc57c3bb147581
--- /dev/null
+++ b/config/feature_flags/development/merge_trains_skip_train.yml
@@ -0,0 +1,8 @@
+---
+name: merge_trains_skip_train
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/129422
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/422111
+milestone: '16.4'
+type: development
+group: group::pipeline execution
+default_enabled: false
diff --git a/db/migrate/20230918194153_add_merge_immediately_to_ci_cd_settings.rb b/db/migrate/20230918194153_add_merge_immediately_to_ci_cd_settings.rb
new file mode 100644
index 0000000000000000000000000000000000000000..eee1ba6f78176c14beb813c5a04aadd56734e685
--- /dev/null
+++ b/db/migrate/20230918194153_add_merge_immediately_to_ci_cd_settings.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+class AddMergeImmediatelyToCiCdSettings < Gitlab::Database::Migration[2.1]
+ enable_lock_retries!
+
+ def up
+ add_column :project_ci_cd_settings, :merge_trains_skip_train_allowed, :boolean, default: false, null: false
+ end
+
+ def down
+ remove_column :project_ci_cd_settings, :merge_trains_skip_train_allowed
+ end
+end
diff --git a/db/schema_migrations/20230918194153 b/db/schema_migrations/20230918194153
new file mode 100644
index 0000000000000000000000000000000000000000..2fd045aa9edf53363f34ead83bb4eaea11e5faa6
--- /dev/null
+++ b/db/schema_migrations/20230918194153
@@ -0,0 +1 @@
+1ec3edbe609cd0142790b28c3b55e50ae36be4119f741ce970b36fd8a788a2ce
\ No newline at end of file
diff --git a/db/structure.sql b/db/structure.sql
index 622c5988d2ac947e571134e17269ac9598402c15..57d77b05a03a70804a1d6451338299bb7a68491f 100644
--- a/db/structure.sql
+++ b/db/structure.sql
@@ -21348,7 +21348,8 @@ CREATE TABLE project_ci_cd_settings (
separated_caches boolean DEFAULT true NOT NULL,
allow_fork_pipelines_to_run_in_parent_project boolean DEFAULT true NOT NULL,
inbound_job_token_scope_enabled boolean DEFAULT true NOT NULL,
- forward_deployment_rollback_allowed boolean DEFAULT true NOT NULL
+ forward_deployment_rollback_allowed boolean DEFAULT true NOT NULL,
+ merge_trains_skip_train_allowed boolean DEFAULT false NOT NULL
);
CREATE SEQUENCE project_ci_cd_settings_id_seq
diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md
index 17033349edfc5ba40a3ab63159740e911f4dcef3..040a11c649bfdca48d1677d9f32bc9bf90be2261 100644
--- a/doc/api/graphql/reference/index.md
+++ b/doc/api/graphql/reference/index.md
@@ -5543,6 +5543,7 @@ Input type: `ProjectCiCdSettingsUpdateInput`
| `keepLatestArtifact` | [`Boolean`](#boolean) | Indicates if the latest artifact should be kept for the project. |
| `mergePipelinesEnabled` | [`Boolean`](#boolean) | Indicates if merge pipelines are enabled for the project. |
| `mergeTrainsEnabled` | [`Boolean`](#boolean) | Indicates if merge trains are enabled for the project. |
+| `mergeTrainsSkipTrainAllowed` | [`Boolean`](#boolean) | Indicates whether an option is allowed to merge without refreshing the merge train. Ignored unless the `merge_trains_skip_train` feature flag is also enabled. |
#### Fields
@@ -23268,6 +23269,7 @@ four standard [pagination arguments](#connection-pagination-arguments):
| `keepLatestArtifact` | [`Boolean`](#boolean) | Whether to keep the latest builds artifacts. |
| `mergePipelinesEnabled` | [`Boolean`](#boolean) | Whether merge pipelines are enabled. |
| `mergeTrainsEnabled` | [`Boolean`](#boolean) | Whether merge trains are enabled. |
+| `mergeTrainsSkipTrainAllowed` | [`Boolean!`](#boolean) | Whether merge immediately is allowed for merge trains. |
| `project` | [`Project`](#project) | Project the CI/CD settings belong to. |
### `ProjectConversations`
diff --git a/ee/app/controllers/ee/projects/settings/merge_requests_controller.rb b/ee/app/controllers/ee/projects/settings/merge_requests_controller.rb
index 91f0eab2a7428093031867d3c612536c2412d804..1a0f79a0dc20ce80922f87ad43138c7d31203f51 100644
--- a/ee/app/controllers/ee/projects/settings/merge_requests_controller.rb
+++ b/ee/app/controllers/ee/projects/settings/merge_requests_controller.rb
@@ -50,6 +50,7 @@ def project_params_ee
attrs << %i[merge_pipelines_enabled] if allow_merge_pipelines_params?
attrs << %i[merge_trains_enabled] if allow_merge_trains_params?
+ attrs << %i[merge_trains_skip_train_allowed] if allow_merge_trains_params?
attrs << %i[only_allow_merge_if_all_status_checks_passed] if allow_external_status_checks?
attrs += merge_request_rules_params
diff --git a/ee/app/controllers/ee/projects_controller.rb b/ee/app/controllers/ee/projects_controller.rb
index cd299ca893199fa366687eac73618162605f74b0..e901a242fa2590224747d1ccb3f47a459f6b2e46 100644
--- a/ee/app/controllers/ee/projects_controller.rb
+++ b/ee/app/controllers/ee/projects_controller.rb
@@ -129,6 +129,7 @@ def project_params_ee
attrs << %i[merge_pipelines_enabled] if allow_merge_pipelines_params?
attrs << %i[merge_trains_enabled] if allow_merge_trains_params?
+ attrs << %i[merge_trains_skip_train_allowed] if allow_merge_trains_params?
attrs += merge_request_rules_params
diff --git a/ee/app/graphql/ee/mutations/ci/project_ci_cd_settings_update.rb b/ee/app/graphql/ee/mutations/ci/project_ci_cd_settings_update.rb
index 7a401485be50e9d46508deed6d856b7cf9c46eab..475f8655dca717460ba0187ba5bfac8831fe97a0 100644
--- a/ee/app/graphql/ee/mutations/ci/project_ci_cd_settings_update.rb
+++ b/ee/app/graphql/ee/mutations/ci/project_ci_cd_settings_update.rb
@@ -15,10 +15,19 @@ module ProjectCiCdSettingsUpdate
argument :merge_trains_enabled, GraphQL::Types::Boolean,
required: false,
description: 'Indicates if merge trains are enabled for the project.'
+
+ argument :merge_trains_skip_train_allowed, GraphQL::Types::Boolean,
+ required: false,
+ description: 'Indicates whether an option is allowed to merge without refreshing the merge train. ' \
+ 'Ignored unless the `merge_trains_skip_train` feature flag is also enabled.'
end
override :resolve
def resolve(full_path:, **args)
+ if ::Feature.disabled?(:merge_trains_skip_train, project(full_path))
+ args.delete(:merge_trains_skip_train_allowed)
+ end
+
super.tap do |result|
ci_cd_settings = result[:ci_cd_settings]
audit_project = ci_cd_settings.project
diff --git a/ee/app/graphql/ee/types/ci/ci_cd_setting_type.rb b/ee/app/graphql/ee/types/ci/ci_cd_setting_type.rb
new file mode 100644
index 0000000000000000000000000000000000000000..f1b96f5a361a91b8be90c9dc17121518d9560c79
--- /dev/null
+++ b/ee/app/graphql/ee/types/ci/ci_cd_setting_type.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+module EE
+ module Types
+ module Ci
+ module CiCdSettingType
+ extend ActiveSupport::Concern
+
+ prepended do
+ field :merge_trains_skip_train_allowed,
+ GraphQL::Types::Boolean,
+ null: false,
+ description: 'Whether merge immediately is allowed for merge trains.',
+ method: :merge_trains_skip_train_allowed?
+ end
+ end
+ end
+ end
+end
diff --git a/ee/app/models/ee/project.rb b/ee/app/models/ee/project.rb
index a05346d4b9dc53aa5ffb37f350d5d88d1c8b9148..3fde1178da93af54ddacd63c5643f4d362e5b9cc 100644
--- a/ee/app/models/ee/project.rb
+++ b/ee/app/models/ee/project.rb
@@ -333,6 +333,7 @@ def lock_for_confirmation!(id)
delegate :merge_pipelines_enabled, :merge_pipelines_enabled=, to: :ci_cd_settings, allow_nil: true
delegate :merge_trains_enabled, :merge_trains_enabled=, to: :ci_cd_settings, allow_nil: true
+ delegate :merge_trains_skip_train_allowed, :merge_trains_skip_train_allowed=, to: :ci_cd_settings, allow_nil: true
delegate :auto_rollback_enabled, :auto_rollback_enabled=, to: :ci_cd_settings, allow_nil: true
@@ -1111,6 +1112,12 @@ def merge_trains_enabled?
ci_cd_settings.merge_trains_enabled?
end
+ def merge_trains_skip_train_allowed?
+ return false unless ci_cd_settings
+
+ ci_cd_settings.merge_trains_skip_train_allowed?
+ end
+
def auto_rollback_enabled?
return false unless ci_cd_settings
diff --git a/ee/app/models/ee/project_ci_cd_setting.rb b/ee/app/models/ee/project_ci_cd_setting.rb
index 7884c3888dcf6181167543f5a43e2be9b35a826d..1d7e03009e1881730ebd172801074690c961d819 100644
--- a/ee/app/models/ee/project_ci_cd_setting.rb
+++ b/ee/app/models/ee/project_ci_cd_setting.rb
@@ -28,6 +28,10 @@ def auto_rollback_enabled?
super && project.feature_available?(:auto_rollback)
end
+ def merge_trains_skip_train_allowed?
+ merge_trains_skip_train_allowed && ::Feature.enabled?(:merge_trains_skip_train, project)
+ end
+
private
def merge_trains_disabled?(project)
diff --git a/ee/app/views/projects/settings/merge_requests/_merge_trains_settings.html.haml b/ee/app/views/projects/settings/merge_requests/_merge_trains_settings.html.haml
index fc12aba5e6c8d3ea5ae5c0155e399642a058738f..41c420a6ba3c41274580b746570e88a15898d5c7 100644
--- a/ee/app/views/projects/settings/merge_requests/_merge_trains_settings.html.haml
+++ b/ee/app/views/projects/settings/merge_requests/_merge_trains_settings.html.haml
@@ -7,3 +7,8 @@
s_('ProjectSettings|Enable merge trains'),
help_text: s_('ProjectSettings|Merge requests approved for merge are queued, and pipelines validate the combined results of the source and target branches before merge. %{link_start}What are merge trains?%{link_end}').html_safe % { link_start: merge_trains_help_link_start, link_end: ''.html_safe },
checkbox_options: { class: 'js-merge-options-merge-trains', data: { qa_selector: 'merge_trains_checkbox' } }
+ - if Feature.enabled?(:merge_trains_skip_train)
+ = form.gitlab_ui_checkbox_component :merge_trains_skip_train_allowed,
+ s_('ProjectSettings|Allow skipping the merge train'),
+ help_text: s_('ProjectSettings|Merge requests can be set to merge immediately without interrupting the merge train. Commits in earlier merge train pipelines might not get validated with immediately merged commits.'),
+ checkbox_options: { class: 'js-merge-options-merge-trains-skip-train-allowed' }
diff --git a/ee/lib/ee/api/entities/project.rb b/ee/lib/ee/api/entities/project.rb
index 346d27cf201d981d2c1498d560ce4ece7f3e0d1f..a2d62c4126395ab5794b5e8a753c3eca498be7fd 100644
--- a/ee/lib/ee/api/entities/project.rb
+++ b/ee/lib/ee/api/entities/project.rb
@@ -51,6 +51,7 @@ def preload_relation(projects_relation, options = {})
end
expose :merge_pipelines_enabled?, as: :merge_pipelines_enabled, if: ->(project, _) { project.feature_available?(:merge_pipelines) }
expose :merge_trains_enabled?, as: :merge_trains_enabled, if: ->(project, _) { project.feature_available?(:merge_pipelines) }
+ expose :merge_trains_skip_train_allowed?, as: :merge_trains_skip_train_allowed, if: ->(project, _) { project.feature_available?(:merge_pipelines) }
expose :only_allow_merge_if_all_status_checks_passed, if: ->(project, _) { project.feature_available?(:external_status_checks) }
expose :allow_pipeline_trigger_approve_deployment, documentation: { type: 'boolean' }, if: ->(project, _) { project.feature_available?(:protected_environments) }
end
diff --git a/ee/lib/ee/api/helpers/projects_helpers.rb b/ee/lib/ee/api/helpers/projects_helpers.rb
index eb4e53550ed127b2cf852b9c0f5f67e28d209269..d02515cef67ae9d25d5b07559b227bee1f01460c 100644
--- a/ee/lib/ee/api/helpers/projects_helpers.rb
+++ b/ee/lib/ee/api/helpers/projects_helpers.rb
@@ -42,6 +42,7 @@ module ProjectsHelpers
optional :merge_requests_template, type: String, desc: 'Default description for merge requests. Description is parsed with GitLab Flavored Markdown.'
optional :merge_pipelines_enabled, type: Grape::API::Boolean, desc: 'Enable merged results pipelines.'
optional :merge_trains_enabled, type: Grape::API::Boolean, desc: 'Enable merge trains.'
+ optional :merge_trains_skip_train_allowed, type: Grape::API::Boolean, desc: 'Allow merge train merge requests to be merged without waiting for pipelines to finish.'
end
end
@@ -65,6 +66,7 @@ def update_params_at_least_one_of
:merge_requests_template,
:merge_pipelines_enabled,
:merge_trains_enabled,
+ :merge_trains_skip_train_allowed,
:requirements_access_level
]
end
diff --git a/ee/lib/ee/api/projects.rb b/ee/lib/ee/api/projects.rb
index 2cf9a1cf754a02c1e17f868ea52d782e2f866bd7..56fe90eff7cf4a8cc65cf6b30801098f0a0f3024 100644
--- a/ee/lib/ee/api/projects.rb
+++ b/ee/lib/ee/api/projects.rb
@@ -127,6 +127,7 @@ def verify_merge_pipelines_attrs!(project, attrs)
attrs.delete(:merge_pipelines_enabled) unless project.feature_available?(:merge_pipelines)
attrs.delete(:merge_trains_enabled) unless project.feature_available?(:merge_trains)
+ attrs.delete(:merge_trains_skip_train_allowed) unless project.feature_available?(:merge_trains)
end
def check_audit_events_available!(project)
diff --git a/ee/spec/controllers/projects/settings/merge_requests_controller_spec.rb b/ee/spec/controllers/projects/settings/merge_requests_controller_spec.rb
index 1471a6ca55794eb7135569901521e6fe851ce094..3f2dbbfb6fae24b4259cee3480016cc746010961 100644
--- a/ee/spec/controllers/projects/settings/merge_requests_controller_spec.rb
+++ b/ee/spec/controllers/projects/settings/merge_requests_controller_spec.rb
@@ -129,6 +129,37 @@
end
end
+ context 'when merge_trains_skip_train_allowed param is specified' do
+ let(:params) { { merge_trains_skip_train_allowed: true } }
+ let(:ci_settings) { project.ci_cd_settings }
+
+ let(:request) do
+ put :update, params: { namespace_id: project.namespace, project_id: project, project: params }
+ end
+
+ before do
+ stub_licensed_features(merge_pipelines: true, merge_trains: true)
+ end
+
+ it 'updates the attribute' do
+ request
+
+ expect(ci_settings.merge_trains_skip_train_allowed).to be_truthy
+ end
+
+ context 'when license is not sufficient' do
+ before do
+ stub_licensed_features(merge_trains: false)
+ end
+
+ it 'does not update the attribute' do
+ request
+
+ expect(ci_settings.merge_trains_skip_train_allowed).to be_falsy
+ end
+ end
+ end
+
context 'when only_allow_merge_if_all_status_checks_passed param is specified' do
let(:params) { { project_setting_attributes: { only_allow_merge_if_all_status_checks_passed: true } } }
diff --git a/ee/spec/controllers/projects_controller_spec.rb b/ee/spec/controllers/projects_controller_spec.rb
index 4ffc59e1a9d7bd3dbe3297cbcc05ab9d441abfc3..4a062fc9222838d399a515cbcddf1abb7d011feb 100644
--- a/ee/spec/controllers/projects_controller_spec.rb
+++ b/ee/spec/controllers/projects_controller_spec.rb
@@ -422,6 +422,36 @@
end
end
+ context 'when merge_trains_skip_train_allowed param is specified' do
+ let(:params) { { merge_trains_skip_train_allowed: true } }
+
+ let(:request) do
+ put :update, params: { namespace_id: project.namespace, id: project, project: params }
+ end
+
+ before do
+ stub_licensed_features(merge_pipelines: true, merge_trains: true)
+ end
+
+ it 'updates the attribute' do
+ request
+
+ expect(project.merge_trains_skip_train_allowed).to be_truthy
+ end
+
+ context 'when license is not sufficient' do
+ before do
+ stub_licensed_features(merge_trains: false)
+ end
+
+ it 'does not update the attribute' do
+ request
+
+ expect(project.merge_trains_skip_train_allowed).to be_falsy
+ end
+ end
+ end
+
context 'when auto_rollback_enabled param is specified' do
let(:params) { { auto_rollback_enabled: true } }
diff --git a/ee/spec/features/projects/settings/merge_requests/merge_trains/enable_merge_trains_skip_train_allowed_spec.rb b/ee/spec/features/projects/settings/merge_requests/merge_trains/enable_merge_trains_skip_train_allowed_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..aea7dc4eb26a7087abcd0588fa0e3471e7105e55
--- /dev/null
+++ b/ee/spec/features/projects/settings/merge_requests/merge_trains/enable_merge_trains_skip_train_allowed_spec.rb
@@ -0,0 +1,51 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'Merge Trains Skip Train Setting', :js, feature_category: :merge_trains do
+ let_it_be_with_reload(:project) { create(:project) }
+ let_it_be(:user) { create(:user) }
+
+ before_all do
+ project.add_maintainer(user)
+ end
+
+ before do
+ stub_licensed_features(merge_pipelines: true, merge_trains: true)
+ sign_in(user)
+ end
+
+ context 'when visiting the project settings page' do
+ before do
+ visit project_settings_merge_requests_path(project)
+ wait_for_requests
+ end
+
+ it 'is unchecked by default' do
+ expect(find('#project_merge_trains_skip_train_allowed')).not_to be_checked
+ end
+
+ it 'can be enabled' do
+ page.within('#project-merge-options') do
+ check _('Allow skipping the merge train')
+ expect(find('#project_merge_trains_skip_train_allowed')).to be_checked
+ end
+
+ click_button('Save changes')
+
+ wait_for_requests
+
+ expect(project.ci_cd_settings.merge_trains_skip_train_allowed).to eq(true)
+ end
+ end
+
+ context 'when the feature flag is disabled' do
+ before do
+ stub_feature_flags(merge_trains_skip_train: false)
+ end
+
+ it 'does not show the checkbox' do
+ expect(page).not_to have_checked_field('#project_merge_trains_skip_train_allowed')
+ end
+ end
+end
diff --git a/ee/spec/graphql/ee/mutations/ci/project_ci_cd_settings_update_spec.rb b/ee/spec/graphql/ee/mutations/ci/project_ci_cd_settings_update_spec.rb
index d35d538414c777a87b3e7d2850633e66dffbdeef..62cdb81e697e9d65611c6bb0e7ee54c925741f64 100644
--- a/ee/spec/graphql/ee/mutations/ci/project_ci_cd_settings_update_spec.rb
+++ b/ee/spec/graphql/ee/mutations/ci/project_ci_cd_settings_update_spec.rb
@@ -15,8 +15,11 @@
before do
stub_licensed_features(merge_pipelines: true, merge_trains: true)
stub_feature_flags(disable_merge_trains: false)
- project.merge_pipelines_enabled = nil
- project.merge_trains_enabled = false
+ project.update!(
+ merge_pipelines_enabled: nil,
+ merge_trains_enabled: false,
+ merge_trains_skip_train_allowed: false
+ )
end
describe '#resolve' do
@@ -53,6 +56,18 @@
expect(project.merge_trains_enabled?).to eq(true)
end
end
+
+ context 'when merge_trains_skip_train_allowed is set to true' do
+ let(:mutation_params) do
+ {
+ merge_trains_skip_train_allowed: true
+ }
+ end
+
+ it 'updates the value' do
+ expect(project.ci_cd_settings.merge_trains_skip_train_allowed?).to eq(true)
+ end
+ end
end
describe 'when the inbound_job_token_scope parameter is not provided' do
diff --git a/ee/spec/models/ee/project_spec.rb b/ee/spec/models/ee/project_spec.rb
index 6d70b3e131fa9378920e402280d466c916d14e90..9b9b40f6a66d67b22f42b61d60dc3b59d055d567 100644
--- a/ee/spec/models/ee/project_spec.rb
+++ b/ee/spec/models/ee/project_spec.rb
@@ -92,7 +92,8 @@
# EE only
'auto_rollback_enabled' => '',
'merge_pipelines_enabled' => '',
- 'merge_trains_enabled' => ''
+ 'merge_trains_enabled' => '',
+ 'merge_trains_skip_train_allowed' => ''
}
end
diff --git a/ee/spec/models/project_ci_cd_setting_spec.rb b/ee/spec/models/project_ci_cd_setting_spec.rb
index 68db5d8d8545ebb4738c7cf49d30a188546097f0..81d4ce1e7094b2022e058e4a7943bd28b0f6e92c 100644
--- a/ee/spec/models/project_ci_cd_setting_spec.rb
+++ b/ee/spec/models/project_ci_cd_setting_spec.rb
@@ -9,6 +9,8 @@
stub_feature_flags(disable_merge_trains: false)
end
+ it { is_expected.to validate_inclusion_of(:merge_trains_skip_train_allowed).in_array([true, false]) }
+
describe '#merge_pipelines_enabled?' do
subject { project.merge_pipelines_enabled? }
diff --git a/ee/spec/requests/api/graphql/ci/ci_cd_setting_spec.rb b/ee/spec/requests/api/graphql/ci/ci_cd_setting_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..1469eb53fff5dcbef358d528dd39c5a3312a410c
--- /dev/null
+++ b/ee/spec/requests/api/graphql/ci/ci_cd_setting_spec.rb
@@ -0,0 +1,55 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'Getting Ci Cd Setting', feature_category: :continuous_integration do
+ include GraphqlHelpers
+
+ let_it_be_with_reload(:project) { create(:project, :repository) }
+ let_it_be(:owner) { project.first_owner }
+ let_it_be(:user) { create(:user) }
+
+ let(:fields) do
+ <<~QUERY
+ #{all_graphql_fields_for('ProjectCiCdSetting', max_depth: 1)}
+ QUERY
+ end
+
+ let(:query) do
+ graphql_query_for(
+ 'project',
+ { 'fullPath' => project.full_path },
+ query_graphql_field('ciCdSettings', {}, fields)
+ )
+ end
+
+ let(:settings_data) { graphql_data['project']['ciCdSettings'] }
+
+ context 'without permissions' do
+ before_all do
+ project.add_reporter(user)
+ end
+
+ before do
+ post_graphql(query, current_user: user)
+ end
+
+ it_behaves_like 'a working graphql query'
+
+ specify { expect(settings_data).to be nil }
+ end
+
+ context 'with project permissions' do
+ before do
+ post_graphql(query, current_user: owner)
+ end
+
+ let(:skip_train_setting) { project.ci_cd_settings.merge_trains_skip_train_allowed? }
+
+ it_behaves_like 'a working graphql query'
+
+ it 'fetches the settings data' do
+ expect(settings_data['mergeTrainsSkipTrainAllowed']).to eq skip_train_setting
+ end
+ end
+end
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 840af45c1fbdc85b2311926be4554b10a66b052a..860ec9cad7c7da1373e7804546cacb2f102570fb 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -36854,6 +36854,9 @@ msgstr ""
msgid "ProjectSettings|Allow anyone to pull from Package Registry"
msgstr ""
+msgid "ProjectSettings|Allow skipping the merge train"
+msgstr ""
+
msgid "ProjectSettings|Always show thumbs-up and thumbs-down emoji buttons on issues, merge requests, and snippets."
msgstr ""
@@ -37073,6 +37076,9 @@ msgstr ""
msgid "ProjectSettings|Merge requests approved for merge are queued, and pipelines validate the combined results of the source and target branches before merge. %{link_start}What are merge trains?%{link_end}"
msgstr ""
+msgid "ProjectSettings|Merge requests can be set to merge immediately without interrupting the merge train. Commits in earlier merge train pipelines might not get validated with immediately merged commits."
+msgstr ""
+
msgid "ProjectSettings|Merge suggestions"
msgstr ""
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index 6193a565d76fb4ac3db45dea20ec88b2c8966895..877da5e81ce3e31c1a4c3a561807196168ed3a6c 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -1158,6 +1158,7 @@
merge_pipelines_enabled
merge_trains_enabled
auto_rollback_enabled
+ merge_trains_skip_train_allowed
)
end
end
diff --git a/spec/requests/api/project_attributes.yml b/spec/requests/api/project_attributes.yml
index 677cb243a7cab02f383a0fd89f0d9ed85b1d98c1..d95f96c25d64140e8dd7ac748c1f9f57beeba49c 100644
--- a/spec/requests/api/project_attributes.yml
+++ b/spec/requests/api/project_attributes.yml
@@ -94,6 +94,7 @@ ci_cd_settings:
- id
- project_id
- merge_trains_enabled
+ - merge_trains_skip_train_allowed
- merge_pipelines_enabled
- auto_rollback_enabled
- inbound_job_token_scope_enabled
diff --git a/spec/support/rspec_order_todo.yml b/spec/support/rspec_order_todo.yml
index c2cb29064b69bfd28724ae448f1a014223b887d2..0c530f63e598cf948a0581bf0ae55f7c97e0c6a4 100644
--- a/spec/support/rspec_order_todo.yml
+++ b/spec/support/rspec_order_todo.yml
@@ -591,7 +591,6 @@
- './ee/spec/graphql/api/vulnerabilities_spec.rb'
- './ee/spec/graphql/ee/mutations/boards/issues/issue_move_list_spec.rb'
- './ee/spec/graphql/ee/mutations/boards/lists/create_spec.rb'
-- './ee/spec/graphql/ee/mutations/ci/project_ci_cd_settings_update_spec.rb'
- './ee/spec/graphql/ee/mutations/ci/runner/update_spec.rb'
- './ee/spec/graphql/ee/mutations/concerns/mutations/resolves_issuable_spec.rb'
- './ee/spec/graphql/ee/resolvers/board_list_issues_resolver_spec.rb'