diff --git a/config/feature_flags/gitlab_com_derisk/skip_reference_existence_check_for_child_pipelines.yml b/config/feature_flags/gitlab_com_derisk/skip_reference_existence_check_for_child_pipelines.yml new file mode 100644 index 0000000000000000000000000000000000000000..32e90b62996bde02b5f552e50c121da31a889ff9 --- /dev/null +++ b/config/feature_flags/gitlab_com_derisk/skip_reference_existence_check_for_child_pipelines.yml @@ -0,0 +1,10 @@ +--- +name: skip_reference_existence_check_for_child_pipelines +description: Skip reference existence check for child pipelines +feature_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/428852 +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/204279 +rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/569488 +milestone: '18.4' +group: group::pipeline execution +type: gitlab_com_derisk +default_enabled: false diff --git a/lib/gitlab/ci/pipeline/chain/validate/repository.rb b/lib/gitlab/ci/pipeline/chain/validate/repository.rb index 7977ce904431720919a3ae9b8404e9125cdf5fe7..3440063ad483032ca8577a5ef3ed12d75def86e4 100644 --- a/lib/gitlab/ci/pipeline/chain/validate/repository.rb +++ b/lib/gitlab/ci/pipeline/chain/validate/repository.rb @@ -9,7 +9,10 @@ class Repository < Chain::Base include Chain::Helpers def perform! - unless @command.branch_exists? || @command.tag_exists? || @command.merge_request_ref_exists? + unless skip_reference_check? || + @command.branch_exists? || + @command.tag_exists? || + @command.merge_request_ref_exists? return error('Reference not found') end @@ -25,6 +28,18 @@ def perform! def break? @pipeline.errors.any? end + + private + + def skip_reference_check? + # Like jobs, child pipelines rely on the parent pipeline's + # reference validation. This is safe because they use exactly the + # same reference and SHA, and, thanks to a keep-around reference + # to the associated commit, allows for execution even after the + # ref has been deleted from the repository. + @pipeline.parent_pipeline? && Feature.enabled?(:skip_reference_existence_check_for_child_pipelines, + project) + end end end end diff --git a/spec/lib/gitlab/ci/pipeline/chain/validate/repository_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/validate/repository_spec.rb index feedef18dcd23ffbbc447f6fdd8f755f924530cb..623914774a4230778e41c3931e0c6fb40274c029 100644 --- a/spec/lib/gitlab/ci/pipeline/chain/validate/repository_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/chain/validate/repository_spec.rb @@ -42,6 +42,42 @@ expect(pipeline.errors.to_a) .to include 'Reference not found' end + + context 'when child pipeline' do + let(:pipeline) { build_stubbed(:ci_pipeline, source: :parent_pipeline) } + + let(:command) do + Gitlab::Ci::Pipeline::Chain::Command.new( + project: project, current_user: user, origin_ref: 'something', checkout_sha: project.commit.id) + end + + it 'does not break the chain' do + expect(step.break?).to be false + end + + it 'does not append pipeline errors' do + expect(pipeline.errors).to be_empty + end + + it 'skips reference validation' do + expect(command).not_to receive(:branch_exists?) + expect(command).not_to receive(:tag_exists?) + expect(command).not_to receive(:merge_request_ref_exists?) + + step.perform! + end + + context 'when FF skip_reference_existence_check_for_child_pipelines is disabled' do + before_all do + stub_feature_flags(skip_reference_existence_check_for_child_pipelines: false) + end + + it 'breaks the chain and adds a "Reference not found" error' do + expect(step.break?).to be true + expect(pipeline.errors.to_a).to include 'Reference not found' + end + end + end end context 'when origin ref is a merge request ref' do