dependabot-common 0.215.0 → 0.216.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (35) hide show
  1. checksums.yaml +4 -4
  2. data/lib/dependabot/clients/azure.rb +69 -0
  3. data/lib/dependabot/clients/bitbucket.rb +3 -1
  4. data/lib/dependabot/clients/github_with_retries.rb +6 -0
  5. data/lib/dependabot/config/file_fetcher.rb +1 -1
  6. data/lib/dependabot/config/ignore_condition.rb +20 -13
  7. data/lib/dependabot/dependency.rb +63 -2
  8. data/lib/dependabot/dependency_file.rb +1 -1
  9. data/lib/dependabot/errors.rb +1 -1
  10. data/lib/dependabot/file_fetchers/base.rb +15 -4
  11. data/lib/dependabot/file_parsers/base/dependency_set.rb +19 -12
  12. data/lib/dependabot/file_parsers/base.rb +0 -2
  13. data/lib/dependabot/git_metadata_fetcher.rb +30 -25
  14. data/lib/dependabot/group_rule.rb +11 -0
  15. data/lib/dependabot/metadata_finders/README.md +1 -1
  16. data/lib/dependabot/metadata_finders/base/changelog_finder.rb +53 -11
  17. data/lib/dependabot/metadata_finders/base/commits_finder.rb +40 -3
  18. data/lib/dependabot/metadata_finders/base/release_finder.rb +1 -1
  19. data/lib/dependabot/pull_request_creator/branch_namer/group_rule_strategy.rb +28 -0
  20. data/lib/dependabot/pull_request_creator/branch_namer/solo_strategy.rb +208 -0
  21. data/lib/dependabot/pull_request_creator/branch_namer.rb +28 -179
  22. data/lib/dependabot/pull_request_creator/labeler.rb +5 -1
  23. data/lib/dependabot/pull_request_creator/message_builder/link_and_mention_sanitizer.rb +3 -1
  24. data/lib/dependabot/pull_request_creator/message_builder/metadata_presenter.rb +1 -1
  25. data/lib/dependabot/pull_request_creator/message_builder.rb +22 -81
  26. data/lib/dependabot/pull_request_creator.rb +1 -0
  27. data/lib/dependabot/security_advisory.rb +1 -1
  28. data/lib/dependabot/shared_helpers.rb +16 -3
  29. data/lib/dependabot/simple_instrumentor.rb +19 -0
  30. data/lib/dependabot/source.rb +7 -7
  31. data/lib/dependabot/version.rb +18 -1
  32. data/lib/dependabot.rb +1 -0
  33. metadata +46 -62
  34. data/lib/dependabot/notifications.rb +0 -18
  35. data/lib/rubygems_version_patch.rb +0 -14
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e671c74d96743b47570682e512064ebb84774d5b63ce1090278f34d0f6ea857d
4
- data.tar.gz: 32956c64580c84d9592253981318c2e442511988e21887f5c3bb5757d95adb2f
3
+ metadata.gz: 645d08a2a5cfd122e1ec0bb4302d7c4e80bd0348e0c0e52d28dc210d196b963b
4
+ data.tar.gz: 25d81216a7b48d60b332d241e06f3a7dcc384b06ea3f5fac1dcc02949da226dc
5
5
  SHA512:
6
- metadata.gz: 35725326642881096654be6bf9e0278555078763c4003a676fcea4ae55115ecda3358866398f39ca5e0626853070e177b827f90d3f63b4701db1092a3bacc948
7
- data.tar.gz: efbfb8bf7609d0ddae2e9294fedbcfd592b016c41cede8010760c8bb81d32ed005fafac040b98f9222310a92bec2c4ce9ec0c0dacfb459309ac85ea3e001538c
6
+ metadata.gz: 8ef726592da7c2ff04784322801d12902ab42613e53b084687acdbfb479982e60a5303cb6e7f249e5525547a7b7681af48c3afe9df16cfda435b771e5e3f84ad
7
+ data.tar.gz: ef3a1010b2dd5736c9595754b1eb1cf1cca4cd7eecdfae551b97a2d84c71952ccad43bedafab0f9b6cea07892afbff41b766d9d8941ae10bcd7eedb434970aa3
@@ -192,6 +192,32 @@ module Dependabot
192
192
  "/pullrequests?api-version=5.0", content.to_json)
193
193
  end
194
194
 
195
+ def autocomplete_pull_request(pull_request_id, auto_complete_set_by, merge_commit_message,
196
+ delete_source_branch = true, squash_merge = true, merge_strategy = "squash",
197
+ trans_work_items = true, ignore_config_ids = [])
198
+
199
+ content = {
200
+ autoCompleteSetBy: {
201
+ id: auto_complete_set_by
202
+ },
203
+ completionOptions: {
204
+ mergeCommitMessage: merge_commit_message,
205
+ deleteSourceBranch: delete_source_branch,
206
+ squashMerge: squash_merge,
207
+ mergeStrategy: merge_strategy,
208
+ transitionWorkItems: trans_work_items,
209
+ autoCompleteIgnoreConfigIds: ignore_config_ids
210
+ }
211
+ }
212
+
213
+ response = patch(source.api_endpoint +
214
+ source.organization + "/" + source.project +
215
+ "/_apis/git/repositories/" + source.unscoped_repo +
216
+ "/pullrequests/" + pull_request_id.to_s + "?api-version=5.1", content.to_json)
217
+
218
+ JSON.parse(response.body)
219
+ end
220
+
195
221
  def pull_request(pull_request_id)
196
222
  response = get(source.api_endpoint +
197
223
  source.organization + "/" + source.project +
@@ -217,6 +243,18 @@ module Dependabot
217
243
  end
218
244
  # rubocop:enable Metrics/ParameterLists
219
245
 
246
+ def compare(previous_tag, new_tag, type)
247
+ response = get(source.api_endpoint +
248
+ source.organization + "/" + source.project +
249
+ "/_apis/git/repositories/" + source.unscoped_repo +
250
+ "/commits?searchCriteria.itemVersion.versionType=#{type}" \
251
+ "&searchCriteria.itemVersion.version=#{previous_tag}" \
252
+ "&searchCriteria.compareVersion.versionType=#{type}" \
253
+ "&searchCriteria.compareVersion.version=#{new_tag}")
254
+
255
+ JSON.parse(response.body).fetch("value")
256
+ end
257
+
220
258
  def get(url)
221
259
  response = nil
222
260
 
@@ -279,6 +317,37 @@ module Dependabot
279
317
  response
280
318
  end
281
319
 
320
+ def patch(url, json)
321
+ response = nil
322
+
323
+ retry_connection_failures do
324
+ response = Excon.patch(
325
+ url,
326
+ body: json,
327
+ user: credentials&.fetch("username", nil),
328
+ password: credentials&.fetch("password", nil),
329
+ idempotent: true,
330
+ **SharedHelpers.excon_defaults(
331
+ headers: auth_header.merge(
332
+ {
333
+ "Content-Type" => "application/json"
334
+ }
335
+ )
336
+ )
337
+ )
338
+
339
+ raise InternalServerError if response.status == 500
340
+ raise BadGateway if response.status == 502
341
+ raise ServiceNotAvailable if response.status == 503
342
+ end
343
+
344
+ raise Unauthorized if response.status == 401
345
+ raise Forbidden if response.status == 403
346
+ raise NotFound if response.status == 404
347
+
348
+ response
349
+ end
350
+
282
351
  private
283
352
 
284
353
  def retry_connection_failures
@@ -171,6 +171,8 @@ module Dependabot
171
171
  base_url = "https://api.bitbucket.org/2.0/user?fields=uuid"
172
172
  response = get(base_url)
173
173
  JSON.parse(response.body).fetch("uuid")
174
+ rescue Unauthorized
175
+ [nil]
174
176
  end
175
177
 
176
178
  def default_reviewers(repo)
@@ -205,7 +207,7 @@ module Dependabot
205
207
 
206
208
  def get(url)
207
209
  response = Excon.get(
208
- url,
210
+ URI::DEFAULT_PARSER.escape(url),
209
211
  user: credentials&.fetch("username", nil),
210
212
  password: credentials&.fetch("password", nil),
211
213
  # Setting to false to prevent Excon retries, use BitbucketWithRetries for retries.
@@ -89,6 +89,12 @@ module Dependabot
89
89
  access_tokens << nil if access_tokens.empty?
90
90
  access_tokens.uniq!
91
91
 
92
+ # Explicitly set the proxy if one is set in the environment
93
+ # as Faraday's find_proxy is very slow.
94
+ Octokit.configure do |c|
95
+ c.proxy = ENV["HTTPS_PROXY"] if ENV["HTTPS_PROXY"]
96
+ end
97
+
92
98
  Octokit.middleware = Faraday::RackBuilder.new do |builder|
93
99
  builder.use Faraday::Retry::Middleware, exceptions: RETRYABLE_ERRORS, max: max_retries || 3
94
100
 
@@ -40,7 +40,7 @@ module Dependabot
40
40
  end
41
41
 
42
42
  unless self.class.required_files_in?(fetched_files.map(&:name))
43
- raise Dependabot::DependencyFileNotFound, self.class.required_files_message
43
+ raise Dependabot::DependencyFileNotFound.new(nil, self.class.required_files_message)
44
44
  end
45
45
 
46
46
  fetched_files
@@ -32,16 +32,19 @@ module Dependabot
32
32
  end
33
33
 
34
34
  def versions_by_type(dependency)
35
- return [] unless dependency.version
35
+ version = correct_version_for(dependency)
36
+ return [] unless version
37
+
38
+ semver = version.to_semver
36
39
 
37
40
  transformed_update_types.flat_map do |t|
38
41
  case t
39
42
  when PATCH_VERSION_TYPE
40
- ignore_patch(dependency.version)
43
+ ignore_patch(semver)
41
44
  when MINOR_VERSION_TYPE
42
- ignore_minor(dependency.version)
45
+ ignore_minor(semver)
43
46
  when MAJOR_VERSION_TYPE
44
- ignore_major(dependency.version)
47
+ ignore_major(semver)
45
48
  else
46
49
  []
47
50
  end
@@ -49,8 +52,6 @@ module Dependabot
49
52
  end
50
53
 
51
54
  def ignore_patch(version)
52
- return [] unless rubygems_compatible?(version)
53
-
54
55
  parts = version.split(".")
55
56
  version_parts = parts.fill(0, parts.length...2)
56
57
  upper_parts = version_parts.first(1) + [version_parts[1].to_i + 1]
@@ -61,8 +62,6 @@ module Dependabot
61
62
  end
62
63
 
63
64
  def ignore_minor(version)
64
- return [] unless rubygems_compatible?(version)
65
-
66
65
  parts = version.split(".")
67
66
  version_parts = parts.fill(0, parts.length...2)
68
67
  lower_parts = version_parts.first(1) + [version_parts[1].to_i + 1] + ["a"]
@@ -74,8 +73,6 @@ module Dependabot
74
73
  end
75
74
 
76
75
  def ignore_major(version)
77
- return [] unless rubygems_compatible?(version)
78
-
79
76
  version_parts = version.split(".")
80
77
  lower_parts = [version_parts[0].to_i + 1] + ["a"]
81
78
  lower_bound = ">= #{lower_parts.join('.')}"
@@ -83,10 +80,20 @@ module Dependabot
83
80
  [lower_bound]
84
81
  end
85
82
 
86
- def rubygems_compatible?(version)
87
- return false if version.nil? || version.empty?
83
+ def correct_version_for(dependency)
84
+ version = dependency.version
85
+ return if version.nil? || version.empty?
86
+
87
+ version_class = version_class_for(dependency.package_manager)
88
+ return unless version_class.correct?(version)
89
+
90
+ version_class.new(version)
91
+ end
88
92
 
89
- Gem::Version.correct?(version)
93
+ def version_class_for(package_manager)
94
+ Utils.version_class_for_package_manager(package_manager)
95
+ rescue StandardError
96
+ Dependabot::Version
90
97
  end
91
98
  end
92
99
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "rubygems_version_patch"
3
+ require "dependabot/version"
4
4
 
5
5
  module Dependabot
6
6
  class Dependency
@@ -110,6 +110,67 @@ module Dependabot
110
110
  display_name_builder.call(name)
111
111
  end
112
112
 
113
+ def humanized_previous_version
114
+ # If we don't have a previous version, we *may* still be able to figure
115
+ # one out if a ref was provided and has been changed (in which case the
116
+ # previous ref was essentially the version).
117
+ if previous_version.nil?
118
+ return ref_changed? ? previous_ref : nil
119
+ end
120
+
121
+ if previous_version.match?(/^[0-9a-f]{40}/)
122
+ return previous_ref if ref_changed? && previous_ref
123
+
124
+ "`#{previous_version[0..6]}`"
125
+ elsif version == previous_version &&
126
+ package_manager == "docker"
127
+ digest = docker_digest_from_reqs(previous_requirements)
128
+ "`#{digest.split(':').last[0..6]}`"
129
+ else
130
+ previous_version
131
+ end
132
+ end
133
+
134
+ def humanized_version
135
+ return if removed?
136
+
137
+ if version.match?(/^[0-9a-f]{40}/)
138
+ return new_ref if ref_changed? && new_ref
139
+
140
+ "`#{version[0..6]}`"
141
+ elsif version == previous_version &&
142
+ package_manager == "docker"
143
+ digest = docker_digest_from_reqs(requirements)
144
+ "`#{digest.split(':').last[0..6]}`"
145
+ else
146
+ version
147
+ end
148
+ end
149
+
150
+ def docker_digest_from_reqs(requirements)
151
+ requirements.
152
+ filter_map { |r| r.dig(:source, "digest") || r.dig(:source, :digest) }.
153
+ first
154
+ end
155
+
156
+ def previous_ref
157
+ previous_refs = previous_requirements.filter_map do |r|
158
+ r.dig(:source, "ref") || r.dig(:source, :ref)
159
+ end.uniq
160
+ return previous_refs.first if previous_refs.count == 1
161
+ end
162
+
163
+ def new_ref
164
+ new_refs = requirements.filter_map do |r|
165
+ r.dig(:source, "ref") || r.dig(:source, :ref)
166
+ end.uniq
167
+ return new_refs.first if new_refs.count == 1
168
+ end
169
+
170
+ def ref_changed?
171
+ previous_ref != new_ref
172
+ end
173
+
113
174
  # Returns all detected versions of the dependency. Only ecosystems that
114
175
  # support this feature will return more than the current version.
115
176
  def all_versions
@@ -135,7 +196,7 @@ module Dependabot
135
196
  end
136
197
 
137
198
  def eql?(other)
138
- self.==(other)
199
+ self == other
139
200
  end
140
201
 
141
202
  private
@@ -87,7 +87,7 @@ module Dependabot
87
87
  end
88
88
 
89
89
  def eql?(other)
90
- self.==(other)
90
+ self == other
91
91
  end
92
92
 
93
93
  def support_file?
@@ -80,7 +80,7 @@ module Dependabot
80
80
 
81
81
  def initialize(file_path, msg = nil)
82
82
  @file_path = file_path
83
- super(msg)
83
+ super("#{file_path} not found" || msg)
84
84
  end
85
85
 
86
86
  def file_name
@@ -95,11 +95,17 @@ module Dependabot
95
95
  rescue Dependabot::SharedHelpers::HelperSubprocessFailed => e
96
96
  if e.message.include?("fatal: Remote branch #{target_branch} not found in upstream origin")
97
97
  raise Dependabot::BranchNotFound, target_branch
98
+ elsif e.message.include?("No space left on device")
99
+ raise Dependabot::OutOfDisk
98
100
  end
99
101
 
100
102
  raise Dependabot::RepoNotFound, source
101
103
  end
102
104
 
105
+ def package_manager_version
106
+ nil
107
+ end
108
+
103
109
  private
104
110
 
105
111
  def fetch_file_if_present(filename, fetch_submodules: false)
@@ -498,7 +504,7 @@ module Dependabot
498
504
  _fetch_file_content_from_github(path, repo, commit)
499
505
  when "gitlab"
500
506
  tmp = gitlab_client.get_file(repo, path, commit).content
501
- Base64.decode64(tmp).force_encoding("UTF-8").encode
507
+ decode_binary_string(tmp)
502
508
  when "azure"
503
509
  azure_client.fetch_file_contents(commit, path)
504
510
  when "bitbucket"
@@ -534,7 +540,7 @@ module Dependabot
534
540
  # see https://github.blog/changelog/2022-05-03-increased-file-size-limit-when-retrieving-file-contents-via-rest-api/
535
541
  github_client.contents(repo, path: path, ref: commit, accept: "application/vnd.github.v3.raw")
536
542
  else
537
- Base64.decode64(tmp.content).force_encoding("UTF-8").encode
543
+ decode_binary_string(tmp.content)
538
544
  end
539
545
  rescue Octokit::Forbidden => e
540
546
  raise unless e.message.include?("too_large")
@@ -549,7 +555,7 @@ module Dependabot
549
555
  tmp = github_client.blob(repo, file_details.sha)
550
556
  return tmp.content if tmp.encoding == "utf-8"
551
557
 
552
- Base64.decode64(tmp.content).force_encoding("UTF-8").encode
558
+ decode_binary_string(tmp.content)
553
559
  end
554
560
  # rubocop:enable Metrics/AbcSize
555
561
 
@@ -607,7 +613,7 @@ module Dependabot
607
613
  CMD
608
614
  )
609
615
  rescue SharedHelpers::HelperSubprocessFailed => e
610
- raise unless GIT_SUBMODULE_ERROR_REGEX && e.message.downcase.include?("submodule")
616
+ raise unless e.message.match(GIT_SUBMODULE_ERROR_REGEX) && e.message.downcase.include?("submodule")
611
617
 
612
618
  submodule_cloning_failed = true
613
619
  match = e.message.match(GIT_SUBMODULE_ERROR_REGEX)
@@ -652,6 +658,11 @@ module Dependabot
652
658
  # rubocop:enable Metrics/MethodLength
653
659
  # rubocop:enable Metrics/PerceivedComplexity
654
660
  # rubocop:enable Metrics/BlockLength
661
+
662
+ def decode_binary_string(str)
663
+ bom = (+"\xEF\xBB\xBF").force_encoding(Encoding::BINARY)
664
+ Base64.decode64(str).delete_prefix(bom).force_encoding("UTF-8").encode
665
+ end
655
666
  end
656
667
  end
657
668
  end
@@ -117,19 +117,10 @@ module Dependabot
117
117
 
118
118
  # Produces a new dependency by merging the attributes of `old_dep` with those of
119
119
  # `new_dep`. Requirements and subdependency metadata will be combined and deduped.
120
- # The version of the combined dependency is determined by the logic below.
120
+ # The version of the combined dependency is determined by the
121
+ # `#combined_version` method below.
121
122
  def combined_dependency(old_dep, new_dep)
122
- version = if old_dep.top_level? # Prefer a direct dependency over a transitive one
123
- old_dep.version || new_dep.version
124
- elsif !version_class.correct?(new_dep.version)
125
- old_dep.version
126
- elsif !version_class.correct?(old_dep.version)
127
- new_dep.version
128
- elsif version_class.new(new_dep.version) > version_class.new(old_dep.version)
129
- old_dep.version
130
- else
131
- new_dep.version
132
- end
123
+ version = combined_version(old_dep, new_dep)
133
124
  requirements = (old_dep.requirements + new_dep.requirements).uniq
134
125
  subdependency_metadata = (
135
126
  (old_dep.subdependency_metadata || []) +
@@ -145,6 +136,22 @@ module Dependabot
145
136
  )
146
137
  end
147
138
 
139
+ def combined_version(old_dep, new_dep)
140
+ if old_dep.version.nil? ^ new_dep.version.nil?
141
+ [old_dep, new_dep].find(&:version).version
142
+ elsif old_dep.top_level? ^ new_dep.top_level? # Prefer a direct dependency over a transitive one
143
+ [old_dep, new_dep].find(&:top_level?).version
144
+ elsif !version_class.correct?(new_dep.version)
145
+ old_dep.version
146
+ elsif !version_class.correct?(old_dep.version)
147
+ new_dep.version
148
+ elsif version_class.new(new_dep.version) > version_class.new(old_dep.version)
149
+ old_dep.version
150
+ else
151
+ new_dep.version
152
+ end
153
+ end
154
+
148
155
  def version_class
149
156
  @version_class ||= Utils.version_class_for_package_manager(@combined.package_manager)
150
157
  end
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "dependabot/notifications"
4
-
5
3
  module Dependabot
6
4
  module FileParsers
7
5
  class Base
@@ -106,7 +106,7 @@ module Dependabot
106
106
 
107
107
  def fetch_raw_upload_pack_with_git_for(uri)
108
108
  service_pack_uri = uri
109
- service_pack_uri += ".git" unless service_pack_uri.end_with?(".git")
109
+ service_pack_uri += ".git" unless service_pack_uri.end_with?(".git") || skip_git_suffix(uri)
110
110
 
111
111
  env = { "PATH" => ENV.fetch("PATH", nil) }
112
112
  command = "git ls-remote #{service_pack_uri}"
@@ -158,40 +158,45 @@ module Dependabot
158
158
  def service_pack_uri(uri)
159
159
  service_pack_uri = uri_with_auth(uri)
160
160
  service_pack_uri = service_pack_uri.gsub(%r{/$}, "")
161
- service_pack_uri += ".git" unless service_pack_uri.end_with?(".git")
161
+ service_pack_uri += ".git" unless service_pack_uri.end_with?(".git") || skip_git_suffix(uri)
162
162
  service_pack_uri + "/info/refs?service=git-upload-pack"
163
163
  end
164
164
 
165
+ def skip_git_suffix(uri)
166
+ # TODO: Unlike the other providers (GitHub, GitLab, BitBucket), as of 2023-01-18 Azure DevOps does not support the
167
+ # ".git" suffix. It will return a 404.
168
+ # So skip adding ".git" if looks like an ADO URI.
169
+ # There's no access to the source object here, so have to check the URI instead.
170
+ # Even if we had the current source object, the URI may be for a dependency hosted elsewhere.
171
+ # Unfortunately as a consequence, urls pointing to Azure DevOps Server will not work.
172
+ # Only alternative is to remove the addition of ".git" suffix since the other providers
173
+ # (GitHub, GitLab, BitBucket) work with or without the suffix.
174
+ # That change has other ramifications, so it'd be better if Azure started supporting ".git"
175
+ # like all the other providers.
176
+ uri = "https://#{uri.split('git@').last.sub(%r{:/?}, '/')}" if uri.start_with?("git@")
177
+ uri = URI(uri)
178
+ hostname = uri.hostname.to_s
179
+ hostname == "dev.azure.com" || hostname.end_with?(".visualstudio.com")
180
+ end
181
+
182
+ # Add in username and password if present in credentials.
183
+ # Credentials are never present for production Dependabot.
165
184
  def uri_with_auth(uri)
166
- bare_uri =
167
- if uri.include?("git@") then uri.split("git@").last.sub(%r{:/?}, "/")
168
- else
169
- uri.sub(%r{.*?://}, "")
170
- end
185
+ # Handle SCP-style git URIs
186
+ uri = "https://#{uri.split('git@').last.sub(%r{:/?}, '/')}" if uri.start_with?("git@")
187
+ uri = URI(uri)
171
188
  cred = credentials.select { |c| c["type"] == "git_source" }.
172
- find { |c| bare_uri.start_with?(c["host"]) }
189
+ find { |c| uri.host == c["host"] }
173
190
 
174
- scheme = scheme_for_uri(uri)
191
+ uri.scheme = "https" if uri.scheme != "http"
175
192
 
176
- if bare_uri.match?(%r{[^/]+:[^/]+@})
177
- # URI already has authentication details
178
- "#{scheme}://#{bare_uri}"
179
- elsif cred&.fetch("username", nil) && cred&.fetch("password", nil)
193
+ if !uri.password && cred&.fetch("username", nil) && cred&.fetch("password", nil)
180
194
  # URI doesn't have authentication details, but we have credentials
181
- auth_string = "#{cred.fetch('username')}:#{cred.fetch('password')}"
182
- "#{scheme}://#{auth_string}@#{bare_uri}"
183
- else
184
- # No credentials, so just return the http(s) URI
185
- "#{scheme}://#{bare_uri}"
195
+ uri.user = URI.encode_www_form_component(cred["username"])
196
+ uri.password = URI.encode_www_form_component(cred["password"])
186
197
  end
187
- end
188
198
 
189
- def scheme_for_uri(uri)
190
- if uri.match?(%r{^http://})
191
- "http"
192
- else
193
- "https"
194
- end
199
+ uri.to_s
195
200
  end
196
201
 
197
202
  def sha_for_update_pack_line(line)
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dependabot
4
+ class GroupRule
5
+ attr_reader :name
6
+
7
+ def initialize(name)
8
+ @name = name
9
+ end
10
+ end
11
+ end
@@ -45,7 +45,7 @@ and implement the following methods:
45
45
 
46
46
  | Method | Description |
47
47
  |------------------------|-------------------------|
48
- | `#look_up_source` | Private method that returns a `Dependabot::Source` object. Generally the source details are extracted from a source code URL provided by the language's dependency registry, but sometimes it's already know from parsing the dependency file. |
48
+ | `#look_up_source` | Private method that returns a `Dependabot::Source` object. Generally the source details are extracted from a source code URL provided by the language's dependency registry, but sometimes it's already available from parsing the dependency file. |
49
49
 
50
50
  To ensure the above are implemented, you should include
51
51
  `it_behaves_like "a dependency metadata finder"` in your specs for the new
@@ -82,11 +82,12 @@ module Dependabot
82
82
  return unless suggested_changelog_url
83
83
 
84
84
  # TODO: Support other providers
85
- source = Source.from_url(suggested_changelog_url)
86
- return unless source&.provider == "github"
85
+ suggested_source = Source.from_url(suggested_changelog_url)
86
+ return unless suggested_source&.provider == "github"
87
87
 
88
- opts = { path: source.directory, ref: source.branch }.compact
89
- tmp_files = github_client.contents(source.repo, opts)
88
+ opts = { path: suggested_source.directory, ref: suggested_source.branch }.compact
89
+ suggested_source_client = github_client_for_source(suggested_source)
90
+ tmp_files = suggested_source_client.contents(suggested_source.repo, opts)
90
91
 
91
92
  filename = suggested_changelog_url.split("/").last.split("#").first
92
93
  @changelog_from_suggested_url =
@@ -161,12 +162,13 @@ module Dependabot
161
162
  @file_text ||= {}
162
163
 
163
164
  unless @file_text.key?(file.download_url)
164
- provider = Source.from_url(file.html_url).provider
165
+ file_source = Source.from_url(file.html_url)
165
166
  @file_text[file.download_url] =
166
- case provider
167
- when "github" then fetch_github_file(file)
167
+ case file_source.provider
168
+ when "github" then fetch_github_file(file_source, file)
168
169
  when "gitlab" then fetch_gitlab_file(file)
169
170
  when "bitbucket" then fetch_bitbucket_file(file)
171
+ when "azure" then fetch_azure_file(file)
170
172
  when "codecommit" then nil # TODO: git file from codecommit
171
173
  else raise "Unsupported provider '#{provider}'"
172
174
  end
@@ -177,9 +179,9 @@ module Dependabot
177
179
  @file_text[file.download_url].sub(/\n*\z/, "")
178
180
  end
179
181
 
180
- def fetch_github_file(file)
182
+ def fetch_github_file(file_source, file)
181
183
  # Hitting the download URL directly causes encoding problems
182
- raw_content = github_client.get(file.url).content
184
+ raw_content = github_client_for_source(file_source).get(file.url).content
183
185
  Base64.decode64(raw_content).force_encoding("UTF-8").encode
184
186
  end
185
187
 
@@ -196,6 +198,11 @@ module Dependabot
196
198
  force_encoding("UTF-8").encode
197
199
  end
198
200
 
201
+ def fetch_azure_file(file)
202
+ azure_client.get(file.download_url).body.
203
+ force_encoding("UTF-8").encode
204
+ end
205
+
199
206
  def upgrade_guide
200
207
  return unless source
201
208
 
@@ -220,7 +227,7 @@ module Dependabot
220
227
  when "github" then fetch_github_file_list(ref)
221
228
  when "bitbucket" then fetch_bitbucket_file_list
222
229
  when "gitlab" then fetch_gitlab_file_list
223
- when "azure" then [] # TODO: Fetch files from Azure
230
+ when "azure" then fetch_azure_file_list
224
231
  when "codecommit" then [] # TODO: Fetch Files from Codecommit
225
232
  else raise "Unexpected repo provider '#{source.provider}'"
226
233
  end
@@ -292,6 +299,29 @@ module Dependabot
292
299
  []
293
300
  end
294
301
 
302
+ def fetch_azure_file_list
303
+ azure_client.fetch_repo_contents.map do |entry|
304
+ type = case entry.fetch("gitObjectType")
305
+ when "blob" then "file"
306
+ when "tree" then "dir"
307
+ else entry.fetch("gitObjectType")
308
+ end
309
+
310
+ OpenStruct.new(
311
+ name: File.basename(entry.fetch("relativePath")),
312
+ type: type,
313
+ size: entry.fetch("size"),
314
+ path: entry.fetch("relativePath"),
315
+ html_url: "#{source.url}?path=/#{entry.fetch('relativePath')}",
316
+ download_url: entry.fetch("url")
317
+ )
318
+ end
319
+ rescue Dependabot::Clients::Azure::NotFound,
320
+ Dependabot::Clients::Azure::Unauthorized,
321
+ Dependabot::Clients::Azure::Forbidden
322
+ []
323
+ end
324
+
295
325
  def new_version
296
326
  return @new_version if defined?(@new_version)
297
327
 
@@ -346,7 +376,19 @@ module Dependabot
346
376
 
347
377
  def github_client
348
378
  @github_client ||= Dependabot::Clients::GithubWithRetries.
349
- for_github_dot_com(credentials: credentials)
379
+ for_source(source: source, credentials: credentials)
380
+ end
381
+
382
+ def azure_client
383
+ @azure_client ||= Dependabot::Clients::Azure.
384
+ for_source(source: source, credentials: credentials)
385
+ end
386
+
387
+ def github_client_for_source(client_source)
388
+ return github_client if client_source == source
389
+
390
+ Dependabot::Clients::GithubWithRetries.
391
+ for_source(source: client_source, credentials: credentials)
350
392
  end
351
393
 
352
394
  def bitbucket_client