diff --git a/common/lib/dependabot/errors.rb b/common/lib/dependabot/errors.rb index 8597d4fe8c9..467fe30b875 100644 --- a/common/lib/dependabot/errors.rb +++ b/common/lib/dependabot/errors.rb @@ -220,6 +220,11 @@ def self.updater_error_details(error) "file-path": error.file_path } } + when Dependabot::DependencyFileNotSupported + { + "error-type": "dependency_file_not_supported", + "error-detail": { message: error.message } + } when Dependabot::GitDependenciesNotReachable { "error-type": "git_dependencies_not_reachable", @@ -616,6 +621,8 @@ class DependencyFileNotEvaluatable < DependabotError; end class DependencyFileNotResolvable < DependabotError; end + class DependencyFileNotSupported < DependabotError; end + class BadRequirementError < Gem::Requirement::BadRequirementError; end ####################### diff --git a/updater/lib/dependabot/api_client.rb b/updater/lib/dependabot/api_client.rb index fa879c888bf..eb1898cdae9 100644 --- a/updater/lib/dependabot/api_client.rb +++ b/updater/lib/dependabot/api_client.rb @@ -5,6 +5,8 @@ require "dependabot/job" require "dependabot/opentelemetry" require "sorbet-runtime" +require "dependabot/errors" +require "debug" # Provides a client to access the internal Dependabot Service's API # @@ -22,6 +24,7 @@ class ApiClient extend T::Sig MAX_REQUEST_RETRIES = 3 + INVALID_REQUEST_MSG = /The request contains invalid or unauthorized changes/ sig { params(base_url: String, job_id: T.any(String, Integer), job_token: String).void } def initialize(base_url, job_id, job_token) @@ -41,7 +44,12 @@ def create_pull_request(dependency_change, base_commit_sha) api_url = "#{base_url}/update_jobs/#{job_id}/create_pull_request" data = create_pull_request_data(dependency_change, base_commit_sha) response = http_client.post(api_url, json: { data: data }) - raise ApiError, response.body if response.code >= 400 + + if response.code >= 400 && dependency_file_not_supported_error?(response.body.to_s) + raise Dependabot::DependencyFileNotSupported, response.body.to_s + elsif response.code >= 400 + raise ApiError, response.body + end rescue HTTP::ConnectionError, OpenSSL::SSL::SSLError retry_count ||= 0 retry_count += 1 @@ -416,5 +424,15 @@ def create_pull_request_data(dependency_change, base_commit_sha) data["pr-body"] = dependency_change.pr_message.pr_message data end + + sig { params(response: String).returns(T::Boolean) } + def dependency_file_not_supported_error?(response) + body = JSON.parse(response) + + return false unless body.is_a?(Hash) + return false unless body["errors"] + + INVALID_REQUEST_MSG.match? body["errors"].first["detail"] + end end end diff --git a/updater/spec/dependabot/api_client_spec.rb b/updater/spec/dependabot/api_client_spec.rb index 45fcc686ab3..2de61e64949 100644 --- a/updater/spec/dependabot/api_client_spec.rb +++ b/updater/spec/dependabot/api_client_spec.rb @@ -214,6 +214,22 @@ end) end end + + context "when API returns a 400 Bad Request" do + let(:body) do + '{"errors":[{"status":400,"title":"Bad Request","detail":"The request contains invalid or unauthorized changes"}]}' + end + + before do + stub_request(:post, create_pull_request_url).to_return(status: 400, body: body) + end + + it "raises the correct error" do + expect do + client.create_pull_request(dependency_change, base_commit) + end.to raise_error(Dependabot::DependencyFileNotSupported) + end + end end describe "update_pull_request" do