diff --git a/jh/app/models/jh/merge_request.rb b/jh/app/models/jh/merge_request.rb index 1213aac99b2df7203b6908ad914594dd3086182d..7f3fd1da147255e22e12fa445cccfa351fa69a7d 100644 --- a/jh/app/models/jh/merge_request.rb +++ b/jh/app/models/jh/merge_request.rb @@ -14,5 +14,29 @@ module MergeRequest include ContentValidateable validates :title, :description, content_validation: true, if: :with_project_should_validate_content? end + + override :mergeable_ci_state? + def mergeable_ci_state? + return super unless ::MergeRequests::MonorepoService.monorepo_enabled?(self) + return super if diff_head_pipeline + + # The following code logic refers to the Upstream code "MergeRequest#mergeable_ci_state?" + return true unless only_allow_merge_if_pipeline_succeeds? || + (auto_merge_strategy == ::AutoMergeService::STRATEGY_MERGE_WHEN_CHECKS_PASS && has_ci_enabled?) + + return false unless mono_central_pipeline + + return true if project.allow_merge_on_skipped_pipeline?(inherit_group_setting: true) && + mono_central_pipeline.skipped? + + mono_central_pipeline.success? + end + + private + + def mono_central_pipeline + monorepo_service = ::MergeRequests::MonorepoService.new(project.root_ancestor, source_branch) + monorepo_service.central_pipeline(filter_by_precise_time_range: false) + end end end diff --git a/jh/app/services/merge_requests/monorepo_service.rb b/jh/app/services/merge_requests/monorepo_service.rb index e267d245ce5a597ee094dfeb76a274ce9a4209fa..39c8ea545483c6d64fad11c02f71dda42014bdd6 100644 --- a/jh/app/services/merge_requests/monorepo_service.rb +++ b/jh/app/services/merge_requests/monorepo_service.rb @@ -127,13 +127,14 @@ def cancel_lease! Gitlab::ExclusiveLease.cancel(lease_key, lease_value) end - def central_pipeline + # "filter_by_precise_time_range" is used to solve the problem of circular references. + def central_pipeline(filter_by_precise_time_range: true) return if ci_central_project.nil? pipelines = ::Ci::PipelinesFinder.new( ci_central_project, @root_group.owners.allow_cross_joins_across_databases(url: "https://jihulab.com/gitlab-cn/gitlab/-/issues/4002").first, - central_pipeline_finder_params + central_pipeline_finder_params(filter_by_precise_time_range) ).execute pipelines.find do |pipeline| @@ -213,10 +214,11 @@ def ci_central_project @ci_central_project ||= @root_group.projects.find_by_path(CI_CENTRAL_PROJECT_NAME) end - def central_pipeline_finder_params + def central_pipeline_finder_params(filter_by_precise_time_range) finder_params = { updated_after: merge_requests.first.created_at, ref: CI_CENTRAL_PROJECT_DEFAULT_BRANCH } - if merge_requests_list_status == :merged || merge_requests_list_status == :closed + if filter_by_precise_time_range && + (merge_requests_list_status == :merged || merge_requests_list_status == :closed) finder_params[:updated_before] = merged_at end diff --git a/jh/spec/models/jh/merge_request_spec.rb b/jh/spec/models/jh/merge_request_spec.rb index 000502bb63078ce1b2bd20f2a752d4725ff83405..8f47b44a63955a7cfe078ae8b88146e5cc947c24 100644 --- a/jh/spec/models/jh/merge_request_spec.rb +++ b/jh/spec/models/jh/merge_request_spec.rb @@ -7,4 +7,54 @@ it_behaves_like "content validation with project", :merge_request, :title it_behaves_like "content validation with project", :merge_request, :description end + + describe '#mergeable_ci_state?' do + let(:mono_central_pipeline) { build(:ci_empty_pipeline) } + let(:merge_request) { build(:merge_request, source_project: project) } + let(:project) { build(:project, :repository, only_allow_merge_if_pipeline_succeeds: true) } + + context 'when monorepo is enabled' do + before do + allow(::MergeRequests::MonorepoService).to receive(:monorepo_enabled?).and_return(true) + end + + context 'when no diff_head_pipeline is associated' do + before do + allow_any_instance_of(::MergeRequests::MonorepoService) + .to receive(:central_pipeline) + .and_return(mono_central_pipeline) + end + + context 'and has no mono_central_pipeline associated' do + let(:mono_central_pipeline) { nil } + + it { expect(merge_request.mergeable_ci_state?).to be_falsey } + end + + context 'and a failed mono_central_pipeline is associated' do + before do + mono_central_pipeline.status = 'failed' + end + + it { expect(merge_request.mergeable_ci_state?).to be_falsey } + end + + context 'and a successful mono_central_pipeline is associated' do + before do + mono_central_pipeline.status = 'success' + end + + it { expect(merge_request.mergeable_ci_state?).to be_truthy } + end + + context 'and a skipped mono_central_pipeline is associated' do + before do + mono_central_pipeline.status = 'skipped' + end + + it { expect(merge_request.mergeable_ci_state?).to be_falsey } + end + end + end + end end