Class: Dependabot::Clients::GithubWithRetries

Inherits:
Object
  • Object
show all
Defined in:
lib/dependabot/clients/github_with_retries.rb

Constant Summary collapse

DEFAULT_OPEN_TIMEOUT_IN_SECONDS =
2
DEFAULT_READ_TIMEOUT_IN_SECONDS =
5
DEFAULT_CLIENT_ARGS =
{
  connection_options: {
    request: {
      open_timeout: open_timeout_in_seconds,
      timeout: read_timeout_in_seconds
    }
  }
}.freeze
RETRYABLE_ERRORS =
[
  Faraday::ConnectionFailed,
  Faraday::TimeoutError,
  Octokit::InternalServerError,
  Octokit::BadGateway
].freeze

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(max_retries: 3, **args) ⇒ GithubWithRetries

Proxying #



84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
# File 'lib/dependabot/clients/github_with_retries.rb', line 84

def initialize(max_retries: 3, **args)
  args = DEFAULT_CLIENT_ARGS.merge(args)

  access_tokens = args.delete(:access_tokens) || []
  access_tokens << args[:access_token] if args[:access_token]
  access_tokens << nil if access_tokens.empty?
  access_tokens.uniq!

  Octokit.middleware = Faraday::RackBuilder.new do |builder|
    builder.use Faraday::Retry::Middleware, exceptions: RETRYABLE_ERRORS, max: max_retries || 3

    Octokit::Default::MIDDLEWARE.handlers.each do |handler|
      next if handler.klass == Faraday::Retry::Middleware

      builder.use handler.klass
    end
  end

  @clients = access_tokens.map do |token|
    Octokit::Client.new(args.merge(access_token: token))
  end
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(method_name, *args, &block) ⇒ Object



107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
# File 'lib/dependabot/clients/github_with_retries.rb', line 107

def method_missing(method_name, *args, &block)
  untried_clients = @clients.dup
  client = untried_clients.pop

  begin
    if client.respond_to?(method_name)
      mutatable_args = args.map(&:dup)
      client.public_send(method_name, *mutatable_args, &block)
    else
      super
    end
  rescue Octokit::NotFound, Octokit::Unauthorized, Octokit::Forbidden
    raise unless (client = untried_clients.pop)

    retry
  end
end

Class Method Details

.for_github_dot_com(credentials:) ⇒ Object



53
54
55
56
57
58
59
60
61
62
# File 'lib/dependabot/clients/github_with_retries.rb', line 53

def self.for_github_dot_com(credentials:)
  access_tokens =
    credentials.
    select { |cred| cred["type"] == "git_source" }.
    select { |cred| cred["host"] == "github.com" }.
    select { |cred| cred["password"] }.
    map { |cred| cred.fetch("password") }

  new(access_tokens: access_tokens)
end

.for_source(source:, credentials:) ⇒ Object

Constructor methods #



39
40
41
42
43
44
45
46
47
48
49
50
51
# File 'lib/dependabot/clients/github_with_retries.rb', line 39

def self.for_source(source:, credentials:)
  access_tokens =
    credentials.
    select { |cred| cred["type"] == "git_source" }.
    select { |cred| cred["host"] == source.hostname }.
    select { |cred| cred["password"] }.
    map { |cred| cred.fetch("password") }

  new(
    access_tokens: access_tokens,
    api_endpoint: source.api_endpoint
  )
end

.open_timeout_in_secondsObject



11
12
13
# File 'lib/dependabot/clients/github_with_retries.rb', line 11

def self.open_timeout_in_seconds
  ENV.fetch("DEPENDABOT_OPEN_TIMEOUT_IN_SECONDS", DEFAULT_OPEN_TIMEOUT_IN_SECONDS).to_i
end

.read_timeout_in_secondsObject



15
16
17
# File 'lib/dependabot/clients/github_with_retries.rb', line 15

def self.read_timeout_in_seconds
  ENV.fetch("DEPENDABOT_READ_TIMEOUT_IN_SECONDS", DEFAULT_READ_TIMEOUT_IN_SECONDS).to_i
end

Instance Method Details

#fetch_commit(repo, branch) ⇒ Object

VCS Interface #

Raises:

  • (Octokit::NotFound)


68
69
70
71
72
73
74
# File 'lib/dependabot/clients/github_with_retries.rb', line 68

def fetch_commit(repo, branch)
  response = ref(repo, "heads/#{branch}")

  raise Octokit::NotFound if response.is_a?(Array)

  response.object.sha
end

#fetch_default_branch(repo) ⇒ Object



76
77
78
# File 'lib/dependabot/clients/github_with_retries.rb', line 76

def fetch_default_branch(repo)
  repository(repo).default_branch
end

#respond_to_missing?(method_name, include_private = false) ⇒ Boolean

Returns:

  • (Boolean)


125
126
127
# File 'lib/dependabot/clients/github_with_retries.rb', line 125

def respond_to_missing?(method_name, include_private = false)
  @clients.first.respond_to?(method_name) || super
end