Skip to content

Commit c9cba63

Browse files
committed
Executes a validation before the job to ensure that a branch called dependabot does not exist
If a branch named `dependabot` already exists, branches in the format `dependabot/<name>` cannot be created because Git does not allow a ref to be both a file and a directory Fixes #14193
1 parent 41c2f16 commit c9cba63

File tree

3 files changed

+71
-1
lines changed

3 files changed

+71
-1
lines changed

common/lib/dependabot/errors.rb

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,13 @@ def self.fetcher_error_details(error)
5252
message: error.message
5353
}
5454
}
55+
when Dependabot::RefNamespaceConflictError
56+
{
57+
"error-type": "file_fetcher_error",
58+
"error-detail": {
59+
message: error.message
60+
}
61+
}
5562
when Dependabot::DirectoryNotFound
5663
{
5764
"error-type": "directory_not_found",
@@ -469,6 +476,8 @@ class NotImplemented < DependabotError; end
469476

470477
class InvalidGitAuthToken < DependabotError; end
471478

479+
class RefNamespaceConflictError < DependabotError; end
480+
472481
#####################
473482
# Repo level errors #
474483
#####################

updater/lib/dependabot/file_fetcher_command.rb

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ def perform_job # rubocop:disable Metrics/AbcSize
3131
begin
3232
connectivity_check if ENV["ENABLE_CONNECTIVITY_CHECK"] == "1"
3333
validate_target_branch
34+
dependabot_ref_namespace_available?
3435
clone_repo_contents
3536
@base_commit_sha = file_fetcher.commit
3637
raise "base commit SHA not found" unless @base_commit_sha
@@ -211,6 +212,26 @@ def with_retries(max_retries: 2, &_block)
211212
end
212213
end
213214

215+
# Validates that the repository does not have a top-level branch named `dependabot`.
216+
sig { void }
217+
def dependabot_ref_namespace_available?
218+
dependabot_branch = "dependabot"
219+
begin
220+
branch_exists = git_metadata_fetcher.ref_names.include?(dependabot_branch)
221+
if branch_exists
222+
error_message = "Branch '#{dependabot_branch}' already exists and causes a Git ref namespace conflict. " \
223+
"Git can’t create `dependabot/...` branches while the branch `dependabot` exists." \
224+
"Please delete the '#{dependabot_branch}' branch and retry."
225+
raise Dependabot::RefNamespaceConflictError, error_message
226+
end
227+
rescue Dependabot::RefNamespaceConflictError
228+
# Re-raise so they aren't caught by the generic rescue
229+
raise
230+
rescue StandardError => e
231+
Dependabot.logger.warn("Could not validate the existence of the 'dependabot' branch: #{e.message}")
232+
end
233+
end
234+
214235
sig { void }
215236
def validate_target_branch
216237
return unless job.source.branch

updater/spec/dependabot/file_fetcher_command_spec.rb

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -264,7 +264,11 @@
264264

265265
it "falls back to existing validation and continues processing" do
266266
# Should not raise error during early validation, but log warning
267-
expect(Dependabot.logger).to receive(:warn).with(/Could not validate target branch early:/)
267+
expect(Dependabot.logger).to receive(:warn)
268+
.with(/Could not validate target branch early:/).ordered
269+
270+
expect(Dependabot.logger).to receive(:warn)
271+
.with(/Could not validate the existence of the 'dependabot' branch/).ordered
268272

269273
expect { perform_job }.not_to raise_error
270274
end
@@ -309,6 +313,42 @@
309313
end
310314
end
311315

316+
context "when the dependabot branch exists" do
317+
let(:job_definition) do
318+
job_def = JSON.parse(fixture("jobs/job_with_credentials.json"))
319+
job_def
320+
end
321+
322+
let(:git_metadata_fetcher) { double("GitMetadataFetcher") }
323+
324+
before do
325+
allow_any_instance_of(described_class)
326+
.to receive(:git_metadata_fetcher)
327+
.and_return(git_metadata_fetcher)
328+
329+
allow(git_metadata_fetcher).to receive_messages(
330+
ref_names: %w(main develop dependabot),
331+
upload_pack: nil
332+
)
333+
end
334+
335+
it "raises error with helpful message before file operations" do
336+
expect(api_client)
337+
.to receive(:record_update_job_error)
338+
.with(
339+
error_details: {
340+
message: "Branch 'dependabot' already exists and causes a Git ref namespace conflict. " \
341+
"Git can’t create `dependabot/...` branches while the branch `dependabot` exists." \
342+
"Please delete the 'dependabot' branch and retry."
343+
},
344+
error_type: "file_fetcher_error"
345+
)
346+
expect(api_client).to receive(:mark_job_as_processed)
347+
348+
expect { perform_job }.to output(/Error during file fetching; aborting/).to_stdout_from_any_process
349+
end
350+
end
351+
312352
context "when the fetcher raises a RepoNotFound error" do
313353
let(:provider) { job_definition.dig("job", "source", "provider") }
314354
let(:repo) { job_definition.dig("job", "source", "repo") }

0 commit comments

Comments
 (0)