Class: Dependabot::Clients::Azure

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

Defined Under Namespace

Classes: BadGateway, Forbidden, InternalServerError, NotFound, ServiceNotAvailable, Unauthorized

Constant Summary collapse

RETRYABLE_ERRORS =
[InternalServerError, BadGateway, ServiceNotAvailable].freeze
MAX_PR_DESCRIPTION_LENGTH =
3999

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(source, credentials, max_retries: 3) ⇒ Azure

Client #



42
43
44
45
46
47
# File 'lib/dependabot/clients/azure.rb', line 42

def initialize(source, credentials, max_retries: 3)
  @source = source
  @credentials = credentials
  @auth_header = auth_header_for(credentials&.fetch("token", nil))
  @max_retries = max_retries || 3
end

Class Method Details

.for_source(source:, credentials:) ⇒ Object

Constructor methods #



29
30
31
32
33
34
35
36
# File 'lib/dependabot/clients/azure.rb', line 29

def self.for_source(source:, credentials:)
  credential =
    credentials.
    select { |cred| cred["type"] == "git_source" }.
    find { |cred| cred["host"] == source.hostname }

  new(source, credential)
end

Instance Method Details

#branch(branch_name) ⇒ Object



122
123
124
125
126
127
128
129
# File 'lib/dependabot/clients/azure.rb', line 122

def branch(branch_name)
  response = get(source.api_endpoint +
    source.organization + "/" + source.project +
    "/_apis/git/repositories/" + source.unscoped_repo +
    "/refs?filter=heads/" + branch_name)

  JSON.parse(response.body).fetch("value").first
end

#commits(branch_name = nil) ⇒ Object



109
110
111
112
113
114
115
116
117
118
119
120
# File 'lib/dependabot/clients/azure.rb', line 109

def commits(branch_name = nil)
  commits_url = source.api_endpoint +
                source.organization + "/" + source.project +
                "/_apis/git/repositories/" + source.unscoped_repo +
                "/commits"

  commits_url += "?searchCriteria.itemVersion.version=" + branch_name unless branch_name.to_s.empty?

  response = get(commits_url)

  JSON.parse(response.body).fetch("value")
end

#create_commit(branch_name, base_commit, commit_message, files, author_details) ⇒ Object



142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
# File 'lib/dependabot/clients/azure.rb', line 142

def create_commit(branch_name, base_commit, commit_message, files,
                  author_details)
  content = {
    refUpdates: [
      { name: "refs/heads/" + branch_name, oldObjectId: base_commit }
    ],
    commits: [
      {
        comment: commit_message,
        author: author_details,
        changes: files.map do |file|
          {
            changeType: "edit",
            item: { path: file.path },
            newContent: {
              content: Base64.encode64(file.content),
              contentType: "base64encoded"
            }
          }
        end
      }.compact
    ]
  }

  post(source.api_endpoint + source.organization + "/" + source.project +
    "/_apis/git/repositories/" + source.unscoped_repo +
    "/pushes?api-version=5.0", content.to_json)
end

#create_pull_request(pr_name, source_branch, target_branch, pr_description, labels, work_item = nil) ⇒ Object

rubocop:disable Metrics/ParameterLists



172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
# File 'lib/dependabot/clients/azure.rb', line 172

def create_pull_request(pr_name, source_branch, target_branch,
                        pr_description, labels, work_item = nil)
  pr_description = truncate_pr_description(pr_description)

  content = {
    sourceRefName: "refs/heads/" + source_branch,
    targetRefName: "refs/heads/" + target_branch,
    title: pr_name,
    description: pr_description,
    labels: labels.map { |label| { name: label } },
    workItemRefs: [{ id: work_item }]
  }

  post(source.api_endpoint +
    source.organization + "/" + source.project +
    "/_apis/git/repositories/" + source.unscoped_repo +
    "/pullrequests?api-version=5.0", content.to_json)
end

#fetch_commit(_repo, branch) ⇒ Object

Raises:



49
50
51
52
53
54
55
56
57
58
# File 'lib/dependabot/clients/azure.rb', line 49

def fetch_commit(_repo, branch)
  response = get(source.api_endpoint +
    source.organization + "/" + source.project +
    "/_apis/git/repositories/" + source.unscoped_repo +
    "/stats/branches?name=" + branch)

  raise NotFound if response.status == 400

  JSON.parse(response.body).fetch("commit").fetch("commitId")
end

#fetch_default_branch(_repo) ⇒ Object



60
61
62
63
64
65
66
# File 'lib/dependabot/clients/azure.rb', line 60

def fetch_default_branch(_repo)
  response = get(source.api_endpoint +
    source.organization + "/" + source.project +
    "/_apis/git/repositories/" + source.unscoped_repo)

  JSON.parse(response.body).fetch("defaultBranch").gsub("refs/heads/", "")
end

#fetch_file_contents(commit, path) ⇒ Object



98
99
100
101
102
103
104
105
106
107
# File 'lib/dependabot/clients/azure.rb', line 98

def fetch_file_contents(commit, path)
  response = get(source.api_endpoint +
    source.organization + "/" + source.project +
    "/_apis/git/repositories/" + source.unscoped_repo +
    "/items?path=" + path +
    "&versionDescriptor.versionType=commit" \
    "&versionDescriptor.version=" + commit)

  response.body
end

#fetch_repo_contents(commit = nil, path = nil) ⇒ Object



68
69
70
71
72
73
74
75
76
77
# File 'lib/dependabot/clients/azure.rb', line 68

def fetch_repo_contents(commit = nil, path = nil)
  tree = fetch_repo_contents_treeroot(commit, path)

  response = get(source.api_endpoint +
    source.organization + "/" + source.project +
    "/_apis/git/repositories/" + source.unscoped_repo +
    "/trees/" + tree + "?recursive=false")

  JSON.parse(response.body).fetch("treeEntries")
end

#fetch_repo_contents_treeroot(commit = nil, path = nil) ⇒ Object



79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/dependabot/clients/azure.rb', line 79

def fetch_repo_contents_treeroot(commit = nil, path = nil)
  actual_path = path
  actual_path = "/" if path.to_s.empty?

  tree_url = source.api_endpoint +
             source.organization + "/" + source.project +
             "/_apis/git/repositories/" + source.unscoped_repo +
             "/items?path=" + actual_path

  unless commit.to_s.empty?
    tree_url += "&versionDescriptor.versionType=commit" \
                "&versionDescriptor.version=" + commit
  end

  tree_response = get(tree_url)

  JSON.parse(tree_response.body).fetch("objectId")
end

#get(url) ⇒ Object

rubocop:enable Metrics/ParameterLists

Raises:



216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
# File 'lib/dependabot/clients/azure.rb', line 216

def get(url)
  response = nil

  retry_connection_failures do
    response = Excon.get(
      url,
      user: credentials&.fetch("username", nil),
      password: credentials&.fetch("password", nil),
      idempotent: true,
      **SharedHelpers.excon_defaults(
        headers: auth_header
      )
    )

    raise InternalServerError if response.status == 500
    raise BadGateway if response.status == 502
    raise ServiceNotAvailable if response.status == 503
  end

  raise Unauthorized if response.status == 401
  raise Forbidden if response.status == 403
  raise NotFound if response.status == 404

  response
end

#post(url, json) ⇒ Object

Raises:



242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
# File 'lib/dependabot/clients/azure.rb', line 242

def post(url, json)
  response = nil

  retry_connection_failures do
    response = Excon.post(
      url,
      body: json,
      user: credentials&.fetch("username", nil),
      password: credentials&.fetch("password", nil),
      idempotent: true,
      **SharedHelpers.excon_defaults(
        headers: auth_header.merge(
          {
            "Content-Type" => "application/json"
          }
        )
      )
    )

    raise InternalServerError if response.status == 500
    raise BadGateway if response.status == 502
    raise ServiceNotAvailable if response.status == 503
  end

  raise Unauthorized if response.status == 401
  raise Forbidden if response.status == 403
  raise NotFound if response.status == 404

  response
end

#pull_request(pull_request_id) ⇒ Object



191
192
193
194
195
196
197
# File 'lib/dependabot/clients/azure.rb', line 191

def pull_request(pull_request_id)
  response = get(source.api_endpoint +
    source.organization + "/" + source.project +
    "/_apis/git/pullrequests/" + pull_request_id)

  JSON.parse(response.body)
end

#pull_requests(source_branch, target_branch) ⇒ Object



131
132
133
134
135
136
137
138
139
140
# File 'lib/dependabot/clients/azure.rb', line 131

def pull_requests(source_branch, target_branch)
  response = get(source.api_endpoint +
    source.organization + "/" + source.project +
    "/_apis/git/repositories/" + source.unscoped_repo +
    "/pullrequests?searchCriteria.status=all" \
    "&searchCriteria.sourceRefName=refs/heads/" + source_branch +
    "&searchCriteria.targetRefName=refs/heads/" + target_branch)

  JSON.parse(response.body).fetch("value")
end

#update_ref(branch_name, old_commit, new_commit) ⇒ Object



199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
# File 'lib/dependabot/clients/azure.rb', line 199

def update_ref(branch_name, old_commit, new_commit)
  content = [
    {
      name: "refs/heads/" + branch_name,
      oldObjectId: old_commit,
      newObjectId: new_commit
    }
  ]

  response = post(source.api_endpoint + source.organization + "/" + source.project +
                  "/_apis/git/repositories/" + source.unscoped_repo +
                  "/refs?api-version=5.0", content.to_json)

  JSON.parse(response.body).fetch("value").first
end