From 71331b52131429144fda817dfb98a72a42cd6113 Mon Sep 17 00:00:00 2001 From: Hordur Freyr Yngvason Date: Mon, 8 Sep 2025 16:21:51 -0400 Subject: [PATCH] Add FF to skip reference existence check for child pipelines Adds a feature flagged fix for a bug in which child pipelines on deleted refs would fail with a `Reference not found` error, despite jobs starting just fine within the same parent pipeline. The change is behind the feature flag `skip_reference_existence_check_for_child_pipelines`, disabled by default. A common example is any sort of pipeline on a merged MR, where the associated reference has frequently been deleted (whether it be the source branch or a `refs/merge-requests/*` ref). The change here is to handle child pipelines like similar to jobs, and simply skip the reference existence check for child pipelines, because the parent pipeline will already have performed it and preserved the associated commit with a keep-around reference (i.e. `refs/keep-around/`). See https://gitlab.com/gitlab-org/gitlab/-/issues/428852 --- ...ce_existence_check_for_child_pipelines.yml | 10 ++++++ .../ci/pipeline/chain/validate/repository.rb | 17 ++++++++- .../chain/validate/repository_spec.rb | 36 +++++++++++++++++++ 3 files changed, 62 insertions(+), 1 deletion(-) create mode 100644 config/feature_flags/gitlab_com_derisk/skip_reference_existence_check_for_child_pipelines.yml 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 00000000000000..32e90b62996bde --- /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 7977ce90443172..3440063ad48303 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 feedef18dcd23f..623914774a4230 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 -- GitLab