dependabot-common 0.213.0 → 0.215.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 23fa5c7ea872ca0849f22018af9b0811ad9044f03a4e7d59aa023b3dd80bd4e6
4
- data.tar.gz: cea778ebef75ccec5afcd3e5932af78d9711c51c4c864ea02d65930fce8ca4dc
3
+ metadata.gz: e671c74d96743b47570682e512064ebb84774d5b63ce1090278f34d0f6ea857d
4
+ data.tar.gz: 32956c64580c84d9592253981318c2e442511988e21887f5c3bb5757d95adb2f
5
5
  SHA512:
6
- metadata.gz: b14ad55cbabd2a49bd35c7f8012f95972515eead80a71363353a8978286e9d756fd97da5442cb3013acdebfb1a77c7f8f7c450bac553a2d6f52b12687fcf2d43
7
- data.tar.gz: 86cbba3afb724d1ee0b6c1fd0bb357a33c288263b3032e0ef029e099b5d7f7d8d5104526a8890d6233fdb8850b56ecd05cb0746bd8e6fe8fd84338db8caa28dd
6
+ metadata.gz: 35725326642881096654be6bf9e0278555078763c4003a676fcea4ae55115ecda3358866398f39ca5e0626853070e177b827f90d3f63b4701db1092a3bacc948
7
+ data.tar.gz: efbfb8bf7609d0ddae2e9294fedbcfd592b016c41cede8010760c8bb81d32ed005fafac040b98f9222310a92bec2c4ce9ec0c0dacfb459309ac85ea3e001538c
@@ -172,7 +172,8 @@ module Dependabot
172
172
 
173
173
  # rubocop:disable Metrics/ParameterLists
174
174
  def create_pull_request(pr_name, source_branch, target_branch,
175
- pr_description, labels, work_item = nil)
175
+ pr_description, labels,
176
+ reviewers = nil, assignees = nil, work_item = nil)
176
177
  pr_description = truncate_pr_description(pr_description)
177
178
 
178
179
  content = {
@@ -181,6 +182,7 @@ module Dependabot
181
182
  title: pr_name,
182
183
  description: pr_description,
183
184
  labels: labels.map { |label| { name: label } },
185
+ reviewers: pr_reviewers(reviewers, assignees),
184
186
  workItemRefs: [{ id: work_item }]
185
187
  }
186
188
 
@@ -324,6 +326,13 @@ module Dependabot
324
326
  message&.include?("TF401289")
325
327
  end
326
328
 
329
+ def pr_reviewers(reviewers, assignees)
330
+ return [] unless reviewers || assignees
331
+
332
+ pr_reviewers = reviewers&.map { |r_id| { id: r_id, isRequired: true } } || []
333
+ pr_reviewers + (assignees&.map { |r_id| { id: r_id, isRequired: false } } || [])
334
+ end
335
+
327
336
  attr_reader :auth_header
328
337
  attr_reader :credentials
329
338
  attr_reader :source
@@ -12,6 +12,8 @@ module Dependabot
12
12
 
13
13
  class Forbidden < StandardError; end
14
14
 
15
+ class TimedOut < StandardError; end
16
+
15
17
  #######################
16
18
  # Constructor methods #
17
19
  #######################
@@ -79,19 +81,24 @@ module Dependabot
79
81
  JSON.parse(response.body)
80
82
  end
81
83
 
82
- def pull_requests(repo, source_branch, target_branch)
83
- pr_path = "#{repo}/pullrequests"
84
- # Get pull requests with any status
85
- pr_path += "?status=OPEN&status=MERGED&status=DECLINED&status=SUPERSEDED"
84
+ def pull_requests(repo, source_branch, target_branch, status = %w(OPEN MERGED DECLINED SUPERSEDED))
85
+ pr_path = "#{repo}/pullrequests?"
86
+ # Get pull requests with given status
87
+ status.each { |n| pr_path += "status=#{n}&" }
86
88
  next_page_url = base_url + pr_path
87
89
  pull_requests = paginate({ "next" => next_page_url })
88
90
 
89
91
  pull_requests unless source_branch && target_branch
90
92
 
91
93
  pull_requests.select do |pr|
92
- pr_source_branch = pr.fetch("source").fetch("branch").fetch("name")
94
+ if source_branch.nil?
95
+ source_branch_matches = true
96
+ else
97
+ pr_source_branch = pr.fetch("source").fetch("branch").fetch("name")
98
+ source_branch_matches = pr_source_branch == source_branch
99
+ end
93
100
  pr_target_branch = pr.fetch("destination").fetch("branch").fetch("name")
94
- pr_source_branch == source_branch && pr_target_branch == target_branch
101
+ source_branch_matches && pr_target_branch == target_branch
95
102
  end
96
103
  end
97
104
 
@@ -106,8 +113,7 @@ module Dependabot
106
113
  }
107
114
 
108
115
  files.each do |file|
109
- absolute_path = file.name.start_with?("/") ? file.name : "/" + file.name
110
- parameters[absolute_path] = file.content
116
+ parameters[file.path] = file.content
111
117
  end
112
118
 
113
119
  body = encode_form_parameters(parameters)
@@ -144,6 +150,23 @@ module Dependabot
144
150
  end
145
151
  # rubocop:enable Metrics/ParameterLists
146
152
 
153
+ def decline_pull_request(repo, pr_id, comment = nil)
154
+ # https://developer.atlassian.com/cloud/bitbucket/rest/api-group-pullrequests/
155
+ decline_path = "#{repo}/pullrequests/#{pr_id}/decline"
156
+ post(base_url + decline_path, "")
157
+
158
+ comment = "Dependabot declined the pull request." if comment.nil?
159
+
160
+ content = {
161
+ content: {
162
+ raw: comment
163
+ }
164
+ }
165
+
166
+ comment_path = "#{repo}/pullrequests/#{pr_id}/comments"
167
+ post(base_url + comment_path, content.to_json)
168
+ end
169
+
147
170
  def current_user
148
171
  base_url = "https://api.bitbucket.org/2.0/user?fields=uuid"
149
172
  response = get(base_url)
@@ -205,6 +228,14 @@ module Dependabot
205
228
  end
206
229
 
207
230
  def post(url, body, content_type = "application/json")
231
+ headers = auth_header
232
+
233
+ headers = if body.empty?
234
+ headers.merge({ "Accept" => "application/json" })
235
+ else
236
+ headers.merge({ "Content-Type" => content_type })
237
+ end
238
+
208
239
  response = Excon.post(
209
240
  url,
210
241
  body: body,
@@ -212,16 +243,13 @@ module Dependabot
212
243
  password: credentials&.fetch("password", nil),
213
244
  idempotent: false,
214
245
  **SharedHelpers.excon_defaults(
215
- headers: auth_header.merge(
216
- {
217
- "Content-Type" => content_type
218
- }
219
- )
246
+ headers: headers
220
247
  )
221
248
  )
222
249
  raise Unauthorized if response.status == 401
223
250
  raise Forbidden if response.status == 403
224
251
  raise NotFound if response.status == 404
252
+ raise TimedOut if response.status == 555
225
253
 
226
254
  response
227
255
  end
@@ -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,7 +89,16 @@ module Dependabot
84
89
  access_tokens << nil if access_tokens.empty?
85
90
  access_tokens.uniq!
86
91
 
87
- @max_retries = max_retries || 3
92
+ Octokit.middleware = Faraday::RackBuilder.new do |builder|
93
+ builder.use Faraday::Retry::Middleware, exceptions: RETRYABLE_ERRORS, max: max_retries || 3
94
+
95
+ Octokit::Default::MIDDLEWARE.handlers.each do |handler|
96
+ next if handler.klass == Faraday::Retry::Middleware
97
+
98
+ builder.use handler.klass
99
+ end
100
+ end
101
+
88
102
  @clients = access_tokens.map do |token|
89
103
  Octokit::Client.new(args.merge(access_token: token))
90
104
  end
@@ -95,13 +109,11 @@ module Dependabot
95
109
  client = untried_clients.pop
96
110
 
97
111
  begin
98
- retry_connection_failures do
99
- if client.respond_to?(method_name)
100
- mutatable_args = args.map(&:dup)
101
- client.public_send(method_name, *mutatable_args, &block)
102
- else
103
- super
104
- end
112
+ if client.respond_to?(method_name)
113
+ mutatable_args = args.map(&:dup)
114
+ client.public_send(method_name, *mutatable_args, &block)
115
+ else
116
+ super
105
117
  end
106
118
  rescue Octokit::NotFound, Octokit::Unauthorized, Octokit::Forbidden
107
119
  raise unless (client = untried_clients.pop)
@@ -113,17 +125,6 @@ module Dependabot
113
125
  def respond_to_missing?(method_name, include_private = false)
114
126
  @clients.first.respond_to?(method_name) || super
115
127
  end
116
-
117
- def retry_connection_failures
118
- retry_attempt = 0
119
-
120
- begin
121
- yield
122
- rescue *RETRYABLE_ERRORS
123
- retry_attempt += 1
124
- retry_attempt <= @max_retries ? retry : raise
125
- end
126
- end
127
128
  end
128
129
  end
129
130
  end
@@ -67,6 +67,10 @@ module Dependabot
67
67
  @removed
68
68
  end
69
69
 
70
+ def numeric_version
71
+ @numeric_version ||= version_class.new(version) if version && version_class.correct?(version)
72
+ end
73
+
70
74
  def to_h
71
75
  {
72
76
  "name" => name,
@@ -136,6 +140,10 @@ module Dependabot
136
140
 
137
141
  private
138
142
 
143
+ def version_class
144
+ Utils.version_class_for_package_manager(package_manager)
145
+ end
146
+
139
147
  def check_values
140
148
  raise ArgumentError, "blank strings must not be provided as versions" if [version, previous_version].any?("")
141
149
 
@@ -5,7 +5,7 @@ require "pathname"
5
5
  module Dependabot
6
6
  class DependencyFile
7
7
  attr_accessor :name, :content, :directory, :type, :support_file,
8
- :symlink_target, :content_encoding, :operation
8
+ :symlink_target, :content_encoding, :operation, :mode
9
9
 
10
10
  class ContentEncoding
11
11
  UTF_8 = "utf-8"
@@ -20,7 +20,8 @@ module Dependabot
20
20
 
21
21
  def initialize(name:, content:, directory: "/", type: "file",
22
22
  support_file: false, symlink_target: nil,
23
- content_encoding: ContentEncoding::UTF_8, deleted: false, operation: Operation::UPDATE)
23
+ content_encoding: ContentEncoding::UTF_8, deleted: false,
24
+ operation: Operation::UPDATE, mode: nil)
24
25
  @name = name
25
26
  @content = content
26
27
  @directory = clean_directory(directory)
@@ -40,6 +41,12 @@ module Dependabot
40
41
  # support_file flag instead)
41
42
  @type = type
42
43
 
44
+ begin
45
+ @mode = File.stat((symlink_target || path).sub(%r{^/}, "")).mode.to_s(8)
46
+ rescue StandardError
47
+ @mode = mode
48
+ end
49
+
43
50
  return unless (type == "symlink") ^ symlink_target
44
51
 
45
52
  raise "Symlinks must specify a target!" unless symlink_target
@@ -55,7 +62,8 @@ module Dependabot
55
62
  "support_file" => support_file,
56
63
  "content_encoding" => content_encoding,
57
64
  "deleted" => deleted,
58
- "operation" => operation
65
+ "operation" => operation,
66
+ "mode" => mode
59
67
  }
60
68
 
61
69
  details["symlink_target"] = symlink_target if symlink_target
@@ -26,6 +26,12 @@ module Dependabot
26
26
  Dependabot::Clients::CodeCommit::NotFound
27
27
  ].freeze
28
28
 
29
+ GIT_SUBMODULE_INACCESSIBLE_ERROR =
30
+ /^fatal: unable to access '(?<url>.*)': The requested URL returned error: (?<code>\d+)$/
31
+ GIT_SUBMODULE_CLONE_ERROR =
32
+ /^fatal: clone of '(?<url>.*)' into submodule path '.*' failed$/
33
+ GIT_SUBMODULE_ERROR_REGEX = /(#{GIT_SUBMODULE_INACCESSIBLE_ERROR})|(#{GIT_SUBMODULE_CLONE_ERROR})/
34
+
29
35
  def self.required_files_in?(_filename_array)
30
36
  raise NotImplementedError
31
37
  end
@@ -592,11 +598,26 @@ module Dependabot
592
598
  " --no-recurse-submodules"
593
599
  end
594
600
  clone_options << " --branch #{source.branch} --single-branch" if source.branch
595
- SharedHelpers.run_shell_command(
596
- <<~CMD
597
- git clone #{clone_options.string} #{source.url} #{path}
598
- CMD
599
- )
601
+
602
+ submodule_cloning_failed = false
603
+ begin
604
+ SharedHelpers.run_shell_command(
605
+ <<~CMD
606
+ git clone #{clone_options.string} #{source.url} #{path}
607
+ CMD
608
+ )
609
+ rescue SharedHelpers::HelperSubprocessFailed => e
610
+ raise unless GIT_SUBMODULE_ERROR_REGEX && e.message.downcase.include?("submodule")
611
+
612
+ submodule_cloning_failed = true
613
+ match = e.message.match(GIT_SUBMODULE_ERROR_REGEX)
614
+ url = match.named_captures["url"]
615
+ code = match.named_captures["code"]
616
+
617
+ # Submodules might be in the repo but unrelated to dependencies,
618
+ # so ignoring this error to try the update anyway since the base repo exists.
619
+ Dependabot.logger.error("Cloning of submodule failed: #{url} error: #{code || 'unknown'}")
620
+ end
600
621
 
601
622
  if source.commit
602
623
  # This code will only be called for testing. Production will never pass a commit
@@ -604,7 +625,7 @@ module Dependabot
604
625
  Dir.chdir(path) do
605
626
  fetch_options = StringIO.new
606
627
  fetch_options << "--depth 1"
607
- fetch_options << if recurse_submodules_when_cloning?
628
+ fetch_options << if recurse_submodules_when_cloning? && !submodule_cloning_failed
608
629
  " --recurse-submodules=on-demand"
609
630
  else
610
631
  " --no-recurse-submodules"
@@ -614,7 +635,7 @@ module Dependabot
614
635
 
615
636
  reset_options = StringIO.new
616
637
  reset_options << "--hard"
617
- reset_options << if recurse_submodules_when_cloning?
638
+ reset_options << if recurse_submodules_when_cloning? && !submodule_cloning_failed
618
639
  " --recurse-submodules"
619
640
  else
620
641
  " --no-recurse-submodules"
@@ -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|
@@ -23,13 +23,12 @@ module Dependabot
23
23
 
24
24
  def initialize(dependency:, credentials:,
25
25
  ignored_versions: [], raise_on_ignored: false,
26
- requirement_class: nil, version_class: nil)
26
+ consider_version_branches_pinned: false)
27
27
  @dependency = dependency
28
28
  @credentials = credentials
29
29
  @ignored_versions = ignored_versions
30
30
  @raise_on_ignored = raise_on_ignored
31
- @requirement_class = requirement_class
32
- @version_class = version_class
31
+ @consider_version_branches_pinned = consider_version_branches_pinned
33
32
  end
34
33
 
35
34
  def git_dependency?
@@ -52,17 +51,17 @@ module Dependabot
52
51
  # If the specified `ref` is actually a tag, we're pinned
53
52
  return true if local_upload_pack.match?(%r{ refs/tags/#{ref}$})
54
53
 
55
- # If the specified `ref` is actually a branch, we're NOT pinned
56
- return false if local_upload_pack.match?(%r{ refs/heads/#{ref}$})
54
+ # Assume we're pinned unless the specified `ref` is actually a branch
55
+ return true unless local_upload_pack.match?(%r{ refs/heads/#{ref}$})
57
56
 
58
- # Otherwise, assume we're pinned
59
- true
57
+ # TODO: Research whether considering branches that look like versions pinned makes sense for all ecosystems
58
+ @consider_version_branches_pinned && version_tag?(ref)
60
59
  end
61
60
 
62
61
  def pinned_ref_looks_like_version?
63
62
  return false unless pinned?
64
63
 
65
- dependency_source_details.fetch(:ref).match?(VERSION_REGEX)
64
+ version_tag?(dependency_source_details.fetch(:ref))
66
65
  end
67
66
 
68
67
  def pinned_ref_looks_like_commit_sha?
@@ -70,6 +69,11 @@ module Dependabot
70
69
  ref_looks_like_commit_sha?(ref)
71
70
  end
72
71
 
72
+ def head_commit_for_pinned_ref
73
+ ref = dependency_source_details.fetch(:ref)
74
+ local_repo_git_metadata_fetcher.head_commit_for_ref_sha(ref)
75
+ end
76
+
73
77
  def ref_looks_like_commit_sha?(ref)
74
78
  return false unless ref&.match?(/^[0-9a-f]{6,40}$/)
75
79
 
@@ -85,13 +89,8 @@ module Dependabot
85
89
  def head_commit_for_current_branch
86
90
  ref = ref_or_branch || "HEAD"
87
91
 
88
- if pinned?
89
- return dependency.version ||
90
- local_repo_git_metadata_fetcher.head_commit_for_ref(ref)
91
- end
92
-
93
- sha = local_repo_git_metadata_fetcher.head_commit_for_ref(ref)
94
- return sha if sha
92
+ sha = head_commit_for_local_branch(ref)
93
+ return sha if pinned? || sha
95
94
 
96
95
  raise Dependabot::GitDependencyReferenceNotFound, dependency.name
97
96
  end
@@ -100,75 +99,43 @@ module Dependabot
100
99
  local_repo_git_metadata_fetcher.head_commit_for_ref(name)
101
100
  end
102
101
 
103
- def local_tags_for_latest_version_commit_sha
104
- tags = allowed_version_tags
105
- max_tag = max_version_tag(tags)
106
-
107
- return [] unless max_tag
102
+ def local_ref_for_latest_version_matching_existing_precision
103
+ allowed_refs = local_tag_for_pinned_sha ? allowed_version_tags : allowed_version_refs
108
104
 
109
- tags.
110
- select { |t| t.commit_sha == max_tag.commit_sha }.
111
- map do |t|
112
- version = t.name.match(VERSION_REGEX).named_captures.fetch("version")
113
- {
114
- tag: t.name,
115
- version: version_class.new(version),
116
- commit_sha: t.commit_sha,
117
- tag_sha: t.tag_sha
118
- }
119
- end
105
+ max_local_tag_for_current_precision(allowed_refs)
120
106
  end
121
107
 
122
108
  def local_tag_for_latest_version
123
- tag = max_version_tag(allowed_version_tags)
124
-
125
- return unless tag
109
+ max_local_tag(allowed_version_tags)
110
+ end
126
111
 
127
- version = tag.name.match(VERSION_REGEX).named_captures.fetch("version")
128
- {
129
- tag: tag.name,
130
- version: version_class.new(version),
131
- commit_sha: tag.commit_sha,
132
- tag_sha: tag.tag_sha
133
- }
112
+ def local_tags_for_allowed_versions_matching_existing_precision
113
+ select_matching_existing_precision(allowed_version_tags).map { |t| to_local_tag(t) }
134
114
  end
135
115
 
136
- def max_version_tag(tags)
137
- tags.
138
- max_by do |t|
139
- version = t.name.match(VERSION_REGEX).named_captures.
140
- fetch("version")
141
- version_class.new(version)
142
- end
116
+ def local_tags_for_allowed_versions
117
+ allowed_version_tags.map { |t| to_local_tag(t) }
143
118
  end
144
119
 
145
120
  def allowed_version_tags
146
- tags =
147
- local_tags.
148
- select { |t| version_tag?(t.name) && matches_existing_prefix?(t.name) }
149
- filtered = tags.
150
- reject { |t| tag_included_in_ignore_requirements?(t) }
151
- if @raise_on_ignored && filter_lower_versions(filtered).empty? && filter_lower_versions(tags).any?
152
- raise Dependabot::AllVersionsIgnored
153
- end
121
+ allowed_versions(local_tags)
122
+ end
154
123
 
155
- filtered.
156
- reject { |t| tag_is_prerelease?(t) && !wants_prerelease? }
124
+ def allowed_version_refs
125
+ allowed_versions(local_refs)
157
126
  end
158
127
 
159
128
  def current_version
160
129
  return unless dependency.version && version_tag?(dependency.version)
161
130
 
162
- version = dependency.version.match(VERSION_REGEX).named_captures.fetch("version")
163
- version_class.new(version)
131
+ version_from_ref(dependency.version)
164
132
  end
165
133
 
166
134
  def filter_lower_versions(tags)
167
135
  return tags unless current_version
168
136
 
169
137
  versions = tags.map do |t|
170
- version = t.name.match(VERSION_REGEX).named_captures.fetch("version")
171
- version_class.new(version)
138
+ version_from_tag(t)
172
139
  end
173
140
 
174
141
  versions.select do |version|
@@ -176,15 +143,16 @@ module Dependabot
176
143
  end
177
144
  end
178
145
 
179
- def local_tag_for_pinned_version
180
- return unless pinned?
146
+ def most_specific_tag_equivalent_to_pinned_ref
147
+ commit_sha = head_commit_for_local_branch(dependency_source_details.fetch(:ref))
148
+ most_specific_version_tag_for_sha(commit_sha)
149
+ end
181
150
 
182
- ref = dependency_source_details.fetch(:ref)
183
- tags = local_tags.select { |t| t.commit_sha == ref && version_class.correct?(t.name) }.
184
- sort_by { |t| version_class.new(t.name) }
185
- return if tags.empty?
151
+ def local_tag_for_pinned_sha
152
+ return unless pinned_ref_looks_like_commit_sha?
186
153
 
187
- tags[-1].name
154
+ commit_sha = dependency_source_details.fetch(:ref)
155
+ most_specific_version_tag_for_sha(commit_sha)
188
156
  end
189
157
 
190
158
  def git_repo_reachable?
@@ -198,6 +166,49 @@ module Dependabot
198
166
 
199
167
  attr_reader :dependency, :credentials, :ignored_versions
200
168
 
169
+ def max_local_tag_for_current_precision(tags)
170
+ max_local_tag(select_matching_existing_precision(tags))
171
+ end
172
+
173
+ def max_local_tag(tags)
174
+ max_version_tag = tags.max_by { |t| version_from_tag(t) }
175
+
176
+ to_local_tag(max_version_tag)
177
+ end
178
+
179
+ # Find the latest version with the same precision as the pinned version.
180
+ def select_matching_existing_precision(tags)
181
+ current_precision = precision(dependency.version)
182
+
183
+ tags.select { |tag| precision(scan_version(tag.name)) == current_precision }
184
+ end
185
+
186
+ def precision(version)
187
+ version.split(".").length
188
+ end
189
+
190
+ def most_specific_version_tag_for_sha(commit_sha)
191
+ tags = local_tags.select { |t| t.commit_sha == commit_sha && version_class.correct?(t.name) }.
192
+ sort_by { |t| version_class.new(t.name) }
193
+ return if tags.empty?
194
+
195
+ tags[-1].name
196
+ end
197
+
198
+ def allowed_versions(local_tags)
199
+ tags =
200
+ local_tags.
201
+ select { |t| version_tag?(t.name) && matches_existing_prefix?(t.name) }
202
+ filtered = tags.
203
+ reject { |t| tag_included_in_ignore_requirements?(t) }
204
+ if @raise_on_ignored && filter_lower_versions(filtered).empty? && filter_lower_versions(tags).any?
205
+ raise Dependabot::AllVersionsIgnored
206
+ end
207
+
208
+ filtered.
209
+ reject { |t| tag_is_prerelease?(t) && !wants_prerelease? }
210
+ end
211
+
201
212
  def pinned_ref_in_release?(version)
202
213
  raise "Not a git dependency!" unless git_dependency?
203
214
 
@@ -236,9 +247,15 @@ module Dependabot
236
247
  local_repo_git_metadata_fetcher.upload_pack
237
248
  end
238
249
 
250
+ def local_refs
251
+ handle_tag_prefix(local_repo_git_metadata_fetcher.refs_for_upload_pack)
252
+ end
253
+
239
254
  def local_tags
240
- tags = local_repo_git_metadata_fetcher.tags
255
+ handle_tag_prefix(local_repo_git_metadata_fetcher.tags_for_upload_pack)
256
+ end
241
257
 
258
+ def handle_tag_prefix(tags)
242
259
  if dependency_source_details&.fetch(:ref, nil)&.start_with?("tags/")
243
260
  tags = tags.map do |tag|
244
261
  tag.dup.tap { |t| t.name = "tags/#{tag.name}" }
@@ -341,6 +358,18 @@ module Dependabot
341
358
  tag.gsub(VERSION_REGEX, "").gsub(/v$/i, "")
342
359
  end
343
360
 
361
+ def to_local_tag(tag)
362
+ return unless tag
363
+
364
+ version = version_from_tag(tag)
365
+ {
366
+ tag: tag.name,
367
+ version: version,
368
+ commit_sha: tag.commit_sha,
369
+ tag_sha: tag.ref_sha
370
+ }
371
+ end
372
+
344
373
  def listing_source_url
345
374
  @listing_source_url ||=
346
375
  begin
@@ -404,31 +433,37 @@ module Dependabot
404
433
  return false unless dependency_source_details&.fetch(:ref, nil)
405
434
  return false unless pinned_ref_looks_like_version?
406
435
 
407
- version = dependency_source_details.fetch(:ref).match(VERSION_REGEX).
408
- named_captures.fetch("version")
409
- version_class.new(version).prerelease?
436
+ version = version_from_ref(dependency_source_details.fetch(:ref))
437
+ version.prerelease?
410
438
  end
411
439
 
412
440
  def tag_included_in_ignore_requirements?(tag)
413
- version = tag.name.match(VERSION_REGEX).named_captures.fetch("version")
414
- ignore_requirements.any? { |r| r.satisfied_by?(version_class.new(version)) }
441
+ version = version_from_tag(tag)
442
+ ignore_requirements.any? { |r| r.satisfied_by?(version) }
415
443
  end
416
444
 
417
445
  def tag_is_prerelease?(tag)
418
- version = tag.name.match(VERSION_REGEX).named_captures.fetch("version")
419
- version_class.new(version).prerelease?
446
+ version_from_tag(tag).prerelease?
420
447
  end
421
448
 
422
- def version_class
423
- return @version_class if @version_class
449
+ def version_from_tag(tag)
450
+ version_from_ref(tag.name)
451
+ end
424
452
 
425
- Utils.version_class_for_package_manager(dependency.package_manager)
453
+ def version_from_ref(name)
454
+ version_class.new(scan_version(name))
426
455
  end
427
456
 
428
- def requirement_class
429
- return @requirement_class if @requirement_class
457
+ def scan_version(name)
458
+ name.match(VERSION_REGEX).named_captures.fetch("version")
459
+ end
430
460
 
431
- Utils.requirement_class_for_package_manager(dependency.package_manager)
461
+ def version_class
462
+ @version_class ||= Utils.version_class_for_package_manager(dependency.package_manager)
463
+ end
464
+
465
+ def requirement_class
466
+ @requirement_class ||= Utils.requirement_class_for_package_manager(dependency.package_manager)
432
467
  end
433
468
 
434
469
  def local_repo_git_metadata_fetcher
@@ -22,7 +22,21 @@ module Dependabot
22
22
  def tags
23
23
  return [] unless upload_pack
24
24
 
25
- @tags ||= tags_for_upload_pack
25
+ @tags ||= tags_for_upload_pack.map do |ref|
26
+ OpenStruct.new(
27
+ name: ref.name,
28
+ tag_sha: ref.ref_sha,
29
+ commit_sha: ref.commit_sha
30
+ )
31
+ end
32
+ end
33
+
34
+ def tags_for_upload_pack
35
+ @tags_for_upload_pack ||= refs_for_upload_pack.select { |ref| ref.ref_type == :tag }
36
+ end
37
+
38
+ def refs_for_upload_pack
39
+ @refs_for_upload_pack ||= parse_refs_for_upload_pack
26
40
  end
27
41
 
28
42
  def ref_names
@@ -44,6 +58,12 @@ module Dependabot
44
58
  commit_sha
45
59
  end
46
60
 
61
+ def head_commit_for_ref_sha(ref)
62
+ refs_for_upload_pack.
63
+ find { |r| r.ref_sha == ref }&.
64
+ commit_sha
65
+ end
66
+
47
67
  private
48
68
 
49
69
  attr_reader :url, :credentials
@@ -102,22 +122,6 @@ module Dependabot
102
122
  end
103
123
  end
104
124
 
105
- def tags_for_upload_pack
106
- refs_for_upload_pack.
107
- select { |ref| ref.ref_type == :tag }.
108
- map do |ref|
109
- OpenStruct.new(
110
- name: ref.name,
111
- tag_sha: ref.ref_sha,
112
- commit_sha: ref.commit_sha
113
- )
114
- end
115
- end
116
-
117
- def refs_for_upload_pack
118
- @refs_for_upload_pack ||= parse_refs_for_upload_pack
119
- end
120
-
121
125
  def parse_refs_for_upload_pack
122
126
  peeled_lines = []
123
127
 
@@ -8,11 +8,11 @@ module Dependabot
8
8
  class Azure
9
9
  attr_reader :source, :branch_name, :base_commit, :credentials,
10
10
  :files, :commit_message, :pr_description, :pr_name,
11
- :author_details, :labeler, :work_item
11
+ :author_details, :labeler, :reviewers, :assignees, :work_item
12
12
 
13
13
  def initialize(source:, branch_name:, base_commit:, credentials:,
14
14
  files:, commit_message:, pr_description:, pr_name:,
15
- author_details:, labeler:, work_item: nil)
15
+ author_details:, labeler:, reviewers: nil, assignees: nil, work_item: nil)
16
16
  @source = source
17
17
  @branch_name = branch_name
18
18
  @base_commit = base_commit
@@ -23,6 +23,8 @@ module Dependabot
23
23
  @pr_name = pr_name
24
24
  @author_details = author_details
25
25
  @labeler = labeler
26
+ @reviewers = reviewers
27
+ @assignees = assignees
26
28
  @work_item = work_item
27
29
  end
28
30
 
@@ -79,6 +81,8 @@ module Dependabot
79
81
  source.branch || default_branch,
80
82
  pr_description,
81
83
  labeler.labels_for_pr,
84
+ reviewers,
85
+ assignees,
82
86
  work_item
83
87
  )
84
88
  end
@@ -193,7 +193,7 @@ module Dependabot
193
193
  {
194
194
  path: (file.symlink_target ||
195
195
  file.path).sub(%r{^/}, ""),
196
- mode: "100644",
196
+ mode: (file.mode || "100644"),
197
197
  type: "blob"
198
198
  }.merge(content)
199
199
  end
@@ -176,6 +176,8 @@ module Dependabot
176
176
  pr_name: message.pr_name,
177
177
  author_details: author_details,
178
178
  labeler: labeler,
179
+ reviewers: reviewers,
180
+ assignees: assignees,
179
181
  work_item: provider_metadata&.fetch(:work_item, nil)
180
182
  )
181
183
  end
@@ -59,12 +59,12 @@ module Dependabot
59
59
  super(message)
60
60
  @error_class = error_class || ""
61
61
  @error_context = error_context
62
- @command = error_context[:command]
62
+ @fingerprint = error_context[:fingerprint] || error_context[:command]
63
63
  @trace = trace
64
64
  end
65
65
 
66
66
  def raven_context
67
- { fingerprint: [@command], extra: @error_context }
67
+ { fingerprint: [@fingerprint], extra: @error_context.except(:stderr_output, :fingerprint) }
68
68
  end
69
69
  end
70
70
 
@@ -190,7 +190,8 @@ module Dependabot
190
190
  run_shell_command(
191
191
  "git config --global credential.helper " \
192
192
  "'!#{credential_helper_path} --file #{Dir.pwd}/git.store'",
193
- allow_unsafe_shell_command: true
193
+ allow_unsafe_shell_command: true,
194
+ fingerprint: "git config --global credential.helper '<helper_command>'"
194
195
  )
195
196
 
196
197
  # see https://github.blog/2022-04-12-git-security-vulnerability-announced/
@@ -295,7 +296,7 @@ module Dependabot
295
296
  FileUtils.mv(backup_path, GIT_CONFIG_GLOBAL_PATH)
296
297
  end
297
298
 
298
- def self.run_shell_command(command, allow_unsafe_shell_command: false, env: {})
299
+ def self.run_shell_command(command, allow_unsafe_shell_command: false, env: {}, fingerprint: nil)
299
300
  start = Time.now
300
301
  cmd = allow_unsafe_shell_command ? command : escape_command(command)
301
302
  stdout, process = Open3.capture2e(env || {}, cmd)
@@ -307,6 +308,7 @@ module Dependabot
307
308
 
308
309
  error_context = {
309
310
  command: cmd,
311
+ fingerprint: fingerprint,
310
312
  time_taken: time_taken,
311
313
  process_exit_value: process.to_s
312
314
  }
@@ -137,8 +137,7 @@ module Dependabot
137
137
  # Can't (currently) detect whether git dependencies are vulnerable
138
138
  return false if existing_version_is_sha?
139
139
 
140
- version = version_class.new(dependency.version)
141
- security_advisories.any? { |a| a.vulnerable?(version) }
140
+ active_advisories.any?
142
141
  end
143
142
 
144
143
  def ignore_requirements
@@ -147,6 +146,10 @@ module Dependabot
147
146
 
148
147
  private
149
148
 
149
+ def active_advisories
150
+ security_advisories.select { |a| a.vulnerable?(current_version) }
151
+ end
152
+
150
153
  def latest_version_resolvable_with_full_unlock?
151
154
  raise NotImplementedError
152
155
  end
@@ -235,7 +238,7 @@ module Dependabot
235
238
  # this case we treat the version as up-to-date so that it's ignored.
236
239
  return true if latest_version.to_s.match?(/^[0-9a-f]{40}$/)
237
240
 
238
- latest_version <= version_class.new(dependency.version)
241
+ latest_version <= current_version
239
242
  end
240
243
 
241
244
  def numeric_version_can_update?(requirements_to_unlock:)
@@ -244,7 +247,7 @@ module Dependabot
244
247
  case requirements_to_unlock&.to_sym
245
248
  when :none
246
249
  new_version = latest_resolvable_version_with_no_unlock
247
- new_version && new_version > version_class.new(dependency.version)
250
+ new_version && new_version > current_version
248
251
  when :own
249
252
  preferred_version_resolvable_with_unlock?
250
253
  when :all
@@ -259,7 +262,7 @@ module Dependabot
259
262
 
260
263
  if existing_version_is_sha?
261
264
  return false if new_version.to_s.start_with?(dependency.version)
262
- elsif new_version <= version_class.new(dependency.version)
265
+ elsif new_version <= current_version
263
266
  return false
264
267
  end
265
268
 
@@ -275,6 +278,10 @@ module Dependabot
275
278
  changed_requirements.none?
276
279
  end
277
280
 
281
+ def current_version
282
+ @current_version ||= dependency.numeric_version
283
+ end
284
+
278
285
  def can_compare_requirements?
279
286
  version_from_requirements &&
280
287
  latest_version &&
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Dependabot
4
- VERSION = "0.213.0"
4
+ VERSION = "0.215.0"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dependabot-common
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.213.0
4
+ version: 0.215.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dependabot
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-10-31 00:00:00.000000000 Z
11
+ date: 2022-12-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -119,6 +119,9 @@ dependencies:
119
119
  - - "~>"
120
120
  - !ruby/object:Gem::Version
121
121
  version: '0.75'
122
+ - - "<"
123
+ - !ruby/object:Gem::Version
124
+ version: '0.94'
122
125
  type: :runtime
123
126
  prerelease: false
124
127
  version_requirements: !ruby/object:Gem::Requirement
@@ -126,6 +129,9 @@ dependencies:
126
129
  - - "~>"
127
130
  - !ruby/object:Gem::Version
128
131
  version: '0.75'
132
+ - - "<"
133
+ - !ruby/object:Gem::Version
134
+ version: '0.94'
129
135
  - !ruby/object:Gem::Dependency
130
136
  name: faraday
131
137
  requirement: !ruby/object:Gem::Requirement
@@ -140,6 +146,20 @@ dependencies:
140
146
  - - '='
141
147
  - !ruby/object:Gem::Version
142
148
  version: 2.6.0
149
+ - !ruby/object:Gem::Dependency
150
+ name: faraday-retry
151
+ requirement: !ruby/object:Gem::Requirement
152
+ requirements:
153
+ - - '='
154
+ - !ruby/object:Gem::Version
155
+ version: 2.0.0
156
+ type: :runtime
157
+ prerelease: false
158
+ version_requirements: !ruby/object:Gem::Requirement
159
+ requirements:
160
+ - - '='
161
+ - !ruby/object:Gem::Version
162
+ version: 2.0.0
143
163
  - !ruby/object:Gem::Dependency
144
164
  name: gitlab
145
165
  requirement: !ruby/object:Gem::Requirement
@@ -262,14 +282,14 @@ dependencies:
262
282
  requirements:
263
283
  - - "~>"
264
284
  - !ruby/object:Gem::Version
265
- version: 3.13.0
285
+ version: 4.0.0
266
286
  type: :development
267
287
  prerelease: false
268
288
  version_requirements: !ruby/object:Gem::Requirement
269
289
  requirements:
270
290
  - - "~>"
271
291
  - !ruby/object:Gem::Version
272
- version: 3.13.0
292
+ version: 4.0.0
273
293
  - !ruby/object:Gem::Dependency
274
294
  name: rake
275
295
  requirement: !ruby/object:Gem::Requirement
@@ -318,14 +338,14 @@ dependencies:
318
338
  requirements:
319
339
  - - "~>"
320
340
  - !ruby/object:Gem::Version
321
- version: 1.37.1
341
+ version: 1.39.0
322
342
  type: :development
323
343
  prerelease: false
324
344
  version_requirements: !ruby/object:Gem::Requirement
325
345
  requirements:
326
346
  - - "~>"
327
347
  - !ruby/object:Gem::Version
328
- version: 1.37.1
348
+ version: 1.39.0
329
349
  - !ruby/object:Gem::Dependency
330
350
  name: rubocop-performance
331
351
  requirement: !ruby/object:Gem::Requirement
@@ -503,7 +523,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
503
523
  requirements:
504
524
  - - ">="
505
525
  - !ruby/object:Gem::Version
506
- version: 3.3.22
526
+ version: 3.3.7
507
527
  requirements: []
508
528
  rubygems_version: 3.3.7
509
529
  signing_key: