dependabot-common 0.214.0 → 0.216.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) 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 +12 -1
  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/file_updaters/vendor_updater.rb +2 -1
  14. data/lib/dependabot/git_commit_checker.rb +5 -5
  15. data/lib/dependabot/git_metadata_fetcher.rb +30 -25
  16. data/lib/dependabot/group_rule.rb +11 -0
  17. data/lib/dependabot/metadata_finders/README.md +1 -1
  18. data/lib/dependabot/metadata_finders/base/changelog_finder.rb +53 -11
  19. data/lib/dependabot/metadata_finders/base/commits_finder.rb +40 -3
  20. data/lib/dependabot/metadata_finders/base/release_finder.rb +1 -1
  21. data/lib/dependabot/pull_request_creator/branch_namer/group_rule_strategy.rb +28 -0
  22. data/lib/dependabot/pull_request_creator/branch_namer/solo_strategy.rb +208 -0
  23. data/lib/dependabot/pull_request_creator/branch_namer.rb +28 -179
  24. data/lib/dependabot/pull_request_creator/labeler.rb +5 -1
  25. data/lib/dependabot/pull_request_creator/message_builder/link_and_mention_sanitizer.rb +3 -1
  26. data/lib/dependabot/pull_request_creator/message_builder/metadata_presenter.rb +1 -1
  27. data/lib/dependabot/pull_request_creator/message_builder.rb +22 -81
  28. data/lib/dependabot/pull_request_creator.rb +1 -0
  29. data/lib/dependabot/security_advisory.rb +1 -1
  30. data/lib/dependabot/shared_helpers.rb +22 -7
  31. data/lib/dependabot/simple_instrumentor.rb +19 -0
  32. data/lib/dependabot/source.rb +7 -7
  33. data/lib/dependabot/version.rb +18 -1
  34. data/lib/dependabot.rb +1 -0
  35. metadata +46 -62
  36. data/lib/dependabot/notifications.rb +0 -18
  37. data/lib/rubygems_version_patch.rb +0 -14
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1fab7bc375e03e37b65e94e3e05e7c9ad945d982295b6c9a030083ed038dbaf3
4
- data.tar.gz: 9924a1980357d1833988bd19a8119a05162a28cc20cbdf157626744ef93be0ba
3
+ metadata.gz: 645d08a2a5cfd122e1ec0bb4302d7c4e80bd0348e0c0e52d28dc210d196b963b
4
+ data.tar.gz: 25d81216a7b48d60b332d241e06f3a7dcc384b06ea3f5fac1dcc02949da226dc
5
5
  SHA512:
6
- metadata.gz: 88069b0acbe42180a064046ab55cba565da0f9a3e00d40582a3fb406aee9e3b357cf8e3bdf34dc84da959c931c1519a87e02022e994baf947193f7d6fb949be8
7
- data.tar.gz: 785cddd10adbed3413e7fdfcbfcc9b5fa00fde7a55f6e171db0c62f4e4a9f17000dc06d7e4c75d312fda6c7801e9fa6428642ea2b3473dc1b4d7f14e0a660bff
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.
@@ -6,16 +6,21 @@ module Dependabot
6
6
  module Clients
7
7
  class GithubWithRetries
8
8
  DEFAULT_OPEN_TIMEOUT_IN_SECONDS = 2
9
+ DEFAULT_READ_TIMEOUT_IN_SECONDS = 5
9
10
 
10
11
  def self.open_timeout_in_seconds
11
12
  ENV.fetch("DEPENDABOT_OPEN_TIMEOUT_IN_SECONDS", DEFAULT_OPEN_TIMEOUT_IN_SECONDS).to_i
12
13
  end
13
14
 
15
+ def self.read_timeout_in_seconds
16
+ ENV.fetch("DEPENDABOT_READ_TIMEOUT_IN_SECONDS", DEFAULT_READ_TIMEOUT_IN_SECONDS).to_i
17
+ end
18
+
14
19
  DEFAULT_CLIENT_ARGS = {
15
20
  connection_options: {
16
21
  request: {
17
22
  open_timeout: open_timeout_in_seconds,
18
- timeout: 5
23
+ timeout: read_timeout_in_seconds
19
24
  }
20
25
  }
21
26
  }.freeze
@@ -84,6 +89,12 @@ module Dependabot
84
89
  access_tokens << nil if access_tokens.empty?
85
90
  access_tokens.uniq!
86
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
+
87
98
  Octokit.middleware = Faraday::RackBuilder.new do |builder|
88
99
  builder.use Faraday::Retry::Middleware, exceptions: RETRYABLE_ERRORS, max: max_retries || 3
89
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
@@ -23,7 +23,8 @@ module Dependabot
23
23
  # rubocop:enable Performance/DeletePrefix
24
24
 
25
25
  status = SharedHelpers.run_shell_command(
26
- "git status --untracked-files all --porcelain v1 #{relative_dir}"
26
+ "git status --untracked-files all --porcelain v1 #{relative_dir}",
27
+ fingerprint: "git status --untracked-files all --porcelain v1 <relative_dir>"
27
28
  )
28
29
  changed_paths = status.split("\n").map(&:split)
29
30
  changed_paths.map do |type, path|
@@ -99,12 +99,10 @@ module Dependabot
99
99
  local_repo_git_metadata_fetcher.head_commit_for_ref(name)
100
100
  end
101
101
 
102
- def local_tag_for_latest_version_matching_existing_precision
103
- max_local_tag_for_current_precision(allowed_version_tags)
104
- end
105
-
106
102
  def local_ref_for_latest_version_matching_existing_precision
107
- max_local_tag_for_current_precision(allowed_version_refs)
103
+ allowed_refs = local_tag_for_pinned_sha ? allowed_version_tags : allowed_version_refs
104
+
105
+ max_local_tag_for_current_precision(allowed_refs)
108
106
  end
109
107
 
110
108
  def local_tag_for_latest_version
@@ -151,6 +149,8 @@ module Dependabot
151
149
  end
152
150
 
153
151
  def local_tag_for_pinned_sha
152
+ return unless pinned_ref_looks_like_commit_sha?
153
+
154
154
  commit_sha = dependency_source_details.fetch(:ref)
155
155
  most_specific_version_tag_for_sha(commit_sha)
156
156
  end
@@ -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