dependabot-common 0.390.0 → 0.392.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 (38) hide show
  1. checksums.yaml +4 -4
  2. data/lib/dependabot/clients/azure.rb +1 -1
  3. data/lib/dependabot/command_helpers.rb +11 -3
  4. data/lib/dependabot/config/file_fetcher.rb +2 -2
  5. data/lib/dependabot/config/ignore_condition.rb +1 -1
  6. data/lib/dependabot/config/update_config.rb +1 -1
  7. data/lib/dependabot/dependency_group.rb +16 -0
  8. data/lib/dependabot/dependency_requirement.rb +80 -14
  9. data/lib/dependabot/ecosystem.rb +8 -8
  10. data/lib/dependabot/errors.rb +8 -1
  11. data/lib/dependabot/file_fetchers/base.rb +1 -1
  12. data/lib/dependabot/file_updaters/artifact_updater.rb +1 -1
  13. data/lib/dependabot/file_updaters/base.rb +2 -2
  14. data/lib/dependabot/metadata_finders/base/changelog_finder.rb +4 -7
  15. data/lib/dependabot/metadata_finders/base/changelog_pruner.rb +3 -3
  16. data/lib/dependabot/metadata_finders/base/commits_finder.rb +6 -5
  17. data/lib/dependabot/metadata_finders/base/release_finder.rb +24 -16
  18. data/lib/dependabot/metadata_finders/base.rb +1 -1
  19. data/lib/dependabot/package/package_details.rb +2 -2
  20. data/lib/dependabot/package/package_language.rb +3 -3
  21. data/lib/dependabot/package/package_latest_version_finder.rb +4 -3
  22. data/lib/dependabot/package/package_release.rb +11 -11
  23. data/lib/dependabot/pull_request_creator/branch_name_template.rb +5 -14
  24. data/lib/dependabot/pull_request_creator/branch_namer/solo_strategy.rb +18 -16
  25. data/lib/dependabot/pull_request_creator/labeler.rb +8 -15
  26. data/lib/dependabot/pull_request_creator/message_builder/issue_linker.rb +21 -2
  27. data/lib/dependabot/pull_request_creator/message_builder/link_and_mention_sanitizer.rb +53 -13
  28. data/lib/dependabot/pull_request_creator/message_builder/metadata_presenter.rb +20 -13
  29. data/lib/dependabot/pull_request_creator/message_builder.rb +13 -13
  30. data/lib/dependabot/pull_request_creator/pr_name_prefixer.rb +10 -17
  31. data/lib/dependabot/pull_request_updater/github.rb +1 -1
  32. data/lib/dependabot/shared_helpers.rb +30 -7
  33. data/lib/dependabot/source.rb +1 -4
  34. data/lib/dependabot/update_checkers/base.rb +3 -3
  35. data/lib/dependabot/version.rb +2 -5
  36. data/lib/dependabot/workspace/git.rb +2 -2
  37. data/lib/dependabot.rb +1 -1
  38. metadata +2 -2
@@ -11,20 +11,11 @@ module Dependabot
11
11
  class BranchNameTemplate
12
12
  extend T::Sig
13
13
 
14
- class Error < Dependabot::DependabotError; end
15
-
16
- SOLO_PLACEHOLDERS = T.let(
17
- %w(prefix package_manager directory target_branch dependency version name).freeze,
18
- T::Array[String]
19
- )
20
- GROUP_PLACEHOLDERS = T.let(
21
- %w(prefix package_manager directory target_branch group_name name).freeze,
22
- T::Array[String]
23
- )
24
- MULTI_ECO_PLACEHOLDERS = T.let(
25
- %w(prefix target_branch group_name name).freeze,
26
- T::Array[String]
27
- )
14
+ class Error < Dependabot::BranchNameFormattingError; end
15
+
16
+ SOLO_PLACEHOLDERS = %w(prefix package_manager directory target_branch dependency version name).freeze
17
+ GROUP_PLACEHOLDERS = %w(prefix package_manager directory target_branch group_name name).freeze
18
+ MULTI_ECO_PLACEHOLDERS = %w(prefix target_branch group_name name).freeze
28
19
 
29
20
  # ---------------------------------------------------------------
30
21
  # 1. validate_template(template, strategy:)
@@ -1,4 +1,4 @@
1
- # typed: strict
1
+ # typed: strong
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require "digest"
@@ -88,14 +88,14 @@ module Dependabot
88
88
  def updating_a_property?
89
89
  T.must(dependencies.first)
90
90
  .requirements
91
- .any? { |r| r.dig(:metadata, :property_name) }
91
+ .any? { |r| r.metadata_string("property_name") }
92
92
  end
93
93
 
94
94
  sig { returns(T::Boolean) }
95
95
  def updating_a_dependency_set?
96
96
  T.must(dependencies.first)
97
97
  .requirements
98
- .any? { |r| r.dig(:metadata, :dependency_set) }
98
+ .any? { |r| r.metadata_string_hash("dependency_set") }
99
99
  end
100
100
 
101
101
  sig { returns(String) }
@@ -103,8 +103,8 @@ module Dependabot
103
103
  @property_name ||=
104
104
  T.let(
105
105
  T.must(dependencies.first).requirements
106
- .find { |r| r.dig(:metadata, :property_name) }
107
- &.dig(:metadata, :property_name),
106
+ .find { |r| r.metadata_string("property_name") }
107
+ &.metadata_string("property_name"),
108
108
  T.nilable(String)
109
109
  )
110
110
 
@@ -118,9 +118,9 @@ module Dependabot
118
118
  @dependency_set ||=
119
119
  T.let(
120
120
  T.must(dependencies.first).requirements
121
- .find { |r| r.dig(:metadata, :dependency_set) }
122
- &.dig(:metadata, :dependency_set),
123
- T.nilable(T::Hash[String, String])
121
+ .find { |r| r.metadata_string_hash("dependency_set") }
122
+ &.metadata_string_hash("dependency_set"),
123
+ T.nilable(T::Hash[Symbol, String])
124
124
  )
125
125
 
126
126
  raise "No dependency set!" unless @dependency_set
@@ -172,9 +172,11 @@ module Dependabot
172
172
  T.must(dependency.version)[0..6]
173
173
  elsif dependency.version == dependency.previous_version &&
174
174
  package_manager == "docker"
175
- dependency.requirements
176
- .filter_map { |r| r.dig(:source, "digest") || r.dig(:source, :digest) }
177
- .first.split(":").last[0..6]
175
+ T.must(
176
+ dependency.requirements
177
+ .filter_map { |r| r.source_string("digest") }
178
+ .first
179
+ ).split(":").last&.[](0..6)
178
180
  else
179
181
  dependency.version
180
182
  end
@@ -183,7 +185,7 @@ module Dependabot
183
185
  sig { params(dependency: Dependabot::Dependency).returns(T.nilable(String)) }
184
186
  def previous_ref(dependency)
185
187
  previous_refs = T.must(dependency.previous_requirements).filter_map do |r|
186
- r.dig(:source, "ref") || r.dig(:source, :ref)
188
+ r.source_string("ref")
187
189
  end.uniq
188
190
  previous_refs.first if previous_refs.one?
189
191
  end
@@ -191,7 +193,7 @@ module Dependabot
191
193
  sig { params(dependency: Dependabot::Dependency).returns(T.nilable(String)) }
192
194
  def new_ref(dependency)
193
195
  new_refs = dependency.requirements.filter_map do |r|
194
- r.dig(:source, "ref") || r.dig(:source, :ref)
196
+ r.source_string("ref")
195
197
  end.uniq
196
198
  new_refs.first if new_refs.one?
197
199
  end
@@ -208,10 +210,10 @@ module Dependabot
208
210
  dependency.requirements - T.must(dependency.previous_requirements)
209
211
 
210
212
  gemspec =
211
- updated_reqs.find { |r| r[:file].match?(%r{^[^/]*\.gemspec$}) }
212
- return gemspec[:requirement] if gemspec
213
+ updated_reqs.find { |r| T.must(r.file).match?(%r{^[^/]*\.gemspec$}) }
214
+ return gemspec.requirement_string if gemspec
213
215
 
214
- updated_reqs.first&.fetch(:requirement)
216
+ updated_reqs.first&.requirement_string
215
217
  end
216
218
 
217
219
  # TODO: Bring this in line with existing library checks that we do in the
@@ -165,43 +165,36 @@ module Dependabot
165
165
  )
166
166
  end
167
167
 
168
- # rubocop:disable Metrics/PerceivedComplexity
169
168
  sig { params(dep: Dependabot::Dependency).returns(T.nilable(String)) }
170
169
  def version(dep)
171
170
  return dep.version if version_class.correct?(dep.version)
172
171
 
173
- source = dep.requirements.find { |r| r.fetch(:source) }&.fetch(:source)
174
- type = source&.fetch("type", nil) || source&.fetch(:type)
175
- return dep.version unless type == "git"
172
+ source = dep.requirements.find(&:source)
173
+ return dep.version if source.nil?
174
+ return dep.version unless source.source_string("type") == "git"
176
175
 
177
- ref = source.fetch("ref", nil) || source.fetch(:ref)
178
- version_from_ref = ref&.gsub(/^v/, "")
176
+ version_from_ref = source.source_string("ref")&.gsub(/^v/, "")
179
177
  return dep.version unless version_from_ref
180
178
  return dep.version unless version_class.correct?(version_from_ref)
181
179
 
182
180
  version_from_ref
183
181
  end
184
- # rubocop:enable Metrics/PerceivedComplexity
185
182
 
186
- # rubocop:disable Metrics/PerceivedComplexity
187
183
  sig { params(dep: Dependabot::Dependency).returns(T.nilable(String)) }
188
184
  def previous_version(dep)
189
185
  version_str = dep.previous_version
190
186
  return version_str if version_class.correct?(version_str)
191
187
 
192
- source = T.must(dep.previous_requirements)
193
- .find { |r| r.fetch(:source) }&.fetch(:source)
194
- type = source&.fetch("type", nil) || source&.fetch(:type)
195
- return version_str unless type == "git"
188
+ source = T.must(dep.previous_requirements).find(&:source)
189
+ return version_str if source.nil?
190
+ return version_str unless source.source_string("type") == "git"
196
191
 
197
- ref = source.fetch("ref", nil) || source.fetch(:ref)
198
- version_from_ref = ref&.gsub(/^v/, "")
192
+ version_from_ref = source.source_string("ref")&.gsub(/^v/, "")
199
193
  return version_str unless version_from_ref
200
194
  return version_str unless version_class.correct?(version_from_ref)
201
195
 
202
196
  version_from_ref
203
197
  end
204
- # rubocop:enable Metrics/PerceivedComplexity
205
198
 
206
199
  sig { returns(T.nilable(T::Array[String])) }
207
200
  def create_default_dependencies_label_if_required
@@ -2,6 +2,7 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require "sorbet-runtime"
5
+ require "uri"
5
6
  require "dependabot/pull_request_creator/message_builder"
6
7
 
7
8
  module Dependabot
@@ -53,19 +54,37 @@ module Dependabot
53
54
  &.fetch("repo", nil)
54
55
 
55
56
  source = if repo
56
- "https://github.com/#{repo}"
57
+ "#{qualified_reference_base_url}/#{repo}"
57
58
  elsif source_url
58
59
  source_url
59
60
  end
60
61
 
61
62
  if source
62
- "[#{repo ? (repo + tag) : tag}](#{source}/issues/#{number})"
63
+ "[#{repo ? (repo + tag) : tag}](#{source}#{issue_path(source)}/#{number})"
63
64
  else
64
65
  issue_link
65
66
  end
66
67
  end
67
68
  end
68
69
  end
70
+
71
+ private
72
+
73
+ sig { params(source: String).returns(String) }
74
+ def issue_path(source)
75
+ URI.parse(source).host&.downcase == "gitlab.com" ? "/-/work_items" : "/issues"
76
+ rescue URI::InvalidURIError
77
+ "/issues"
78
+ end
79
+
80
+ sig { returns(String) }
81
+ def qualified_reference_base_url
82
+ return "https://github.com" unless source_url
83
+
84
+ URI.parse(T.must(source_url)).host&.downcase == "gitlab.com" ? "https://gitlab.com" : "https://github.com"
85
+ rescue URI::InvalidURIError
86
+ "https://github.com"
87
+ end
69
88
  end
70
89
  end
71
90
  end
@@ -4,6 +4,7 @@
4
4
  require "commonmarker"
5
5
  require "sorbet-runtime"
6
6
  require "strscan"
7
+ require "uri"
7
8
  require "dependabot/pull_request_creator/message_builder"
8
9
 
9
10
  module Dependabot
@@ -13,6 +14,9 @@ module Dependabot
13
14
  extend T::Sig
14
15
 
15
16
  GITHUB_USERNAME = /[a-z0-9]+(-[a-z0-9]+)*/i
17
+ GITLAB_USERNAME = /[a-z0-9](?:[a-z0-9_.-]*[a-z0-9_-])?/i
18
+ GITHUB_HOST = "github.com"
19
+ GITLAB_HOST = "gitlab.com"
16
20
  GITHUB_REF_REGEX = %r{
17
21
  (?:https?://)?
18
22
  github\.com/(?<repo>#{GITHUB_USERNAME}/[^/\s]+)/
@@ -20,9 +24,6 @@ module Dependabot
20
24
  }x
21
25
  # [^/\s#]+ means one or more characters not matching (^) the class /, whitespace (\s), or #
22
26
  GITHUB_NWO_REGEX = %r{(?<repo>#{GITHUB_USERNAME}/[^/\s#]+)#(?<number>\d+)}
23
- MENTION_REGEX = %r{(?<![A-Za-z0-9`~])@#{GITHUB_USERNAME}/?}
24
- # regex to match a team mention on github
25
- TEAM_MENTION_REGEX = %r{(?<![A-Za-z0-9`~])@(?<org>#{GITHUB_USERNAME})/(?<team>#{GITHUB_USERNAME})/?}
26
27
  # End of string
27
28
  EOS_REGEX = /\z/
28
29
 
@@ -49,9 +50,18 @@ module Dependabot
49
50
  sig { returns(T.nilable(String)) }
50
51
  attr_reader :github_redirection_service
51
52
 
52
- sig { params(github_redirection_service: T.nilable(String)).void }
53
- def initialize(github_redirection_service:)
53
+ sig { returns(T.nilable(String)) }
54
+ attr_reader :metadata_source_url
55
+
56
+ sig do
57
+ params(
58
+ github_redirection_service: T.nilable(String),
59
+ metadata_source_url: T.nilable(String)
60
+ ).void
61
+ end
62
+ def initialize(github_redirection_service:, metadata_source_url: nil)
54
63
  @github_redirection_service = github_redirection_service
64
+ @metadata_source_url = metadata_source_url
55
65
  end
56
66
 
57
67
  sig { params(text: String, unsafe: T::Boolean, format_html: T::Boolean).returns(String) }
@@ -90,7 +100,7 @@ module Dependabot
90
100
  def sanitize_mentions(doc)
91
101
  doc.walk do |node|
92
102
  if node.type == :text &&
93
- node.string_content.match?(MENTION_REGEX)
103
+ node.string_content.match?(mention_regex)
94
104
  nodes = if parent_node_link?(node)
95
105
  build_mention_link_text_nodes(node.string_content)
96
106
  else
@@ -111,10 +121,10 @@ module Dependabot
111
121
  def sanitize_team_mentions(doc)
112
122
  doc.walk do |node|
113
123
  if node.type == :text &&
114
- node.string_content.match?(TEAM_MENTION_REGEX)
124
+ node.string_content.match?(team_mention_regex)
115
125
  if parent_node_link?(node)
116
126
  # Preserve text node formatting while preventing notifications with zero-width space
117
- node.string_content = node.string_content.gsub(TEAM_MENTION_REGEX) do |match|
127
+ node.string_content = node.string_content.gsub(team_mention_regex) do |match|
118
128
  insert_zero_width_space_in_mention(match)
119
129
  end
120
130
  else
@@ -189,9 +199,9 @@ module Dependabot
189
199
  scan = StringScanner.new(text)
190
200
 
191
201
  until scan.eos?
192
- line = scan.scan_until(MENTION_REGEX) ||
202
+ line = scan.scan_until(mention_regex) ||
193
203
  scan.scan_until(EOS_REGEX)
194
- line_match = T.must(line).match(MENTION_REGEX)
204
+ line_match = T.must(line).match(mention_regex)
195
205
  mention = line_match&.to_s
196
206
  text_node = Commonmarker::Node.new(:text)
197
207
 
@@ -199,7 +209,7 @@ module Dependabot
199
209
  text_node.string_content = line_match.pre_match
200
210
  nodes << text_node
201
211
  nodes << create_link_node(
202
- "https://github.com/#{mention.tr('@', '')}", mention.to_s
212
+ "#{mention_profile_base_url}/#{mention.delete_prefix('@')}", mention.to_s
203
213
  )
204
214
  else
205
215
  text_node.string_content = line
@@ -216,9 +226,9 @@ module Dependabot
216
226
 
217
227
  scan = StringScanner.new(text)
218
228
  until scan.eos?
219
- line = scan.scan_until(TEAM_MENTION_REGEX) ||
229
+ line = scan.scan_until(team_mention_regex) ||
220
230
  scan.scan_until(EOS_REGEX)
221
- line_match = T.must(line).match(TEAM_MENTION_REGEX)
231
+ line_match = T.must(line).match(team_mention_regex)
222
232
  mention = line_match&.to_s
223
233
  text_node = Commonmarker::Node.new(:text)
224
234
 
@@ -235,6 +245,36 @@ module Dependabot
235
245
  nodes
236
246
  end
237
247
 
248
+ sig { returns(Regexp) }
249
+ def mention_regex
250
+ %r{(?<![A-Za-z0-9`~])@#{mention_username_regex}/?}
251
+ end
252
+
253
+ sig { returns(Regexp) }
254
+ def team_mention_regex
255
+ username_regex = mention_username_regex
256
+ %r{(?<![A-Za-z0-9`~])@(?<org>#{username_regex})/(?<team>#{username_regex})/?}
257
+ end
258
+
259
+ sig { returns(Regexp) }
260
+ def mention_username_regex
261
+ metadata_source_host == GITLAB_HOST ? GITLAB_USERNAME : GITHUB_USERNAME
262
+ end
263
+
264
+ sig { returns(String) }
265
+ def mention_profile_base_url
266
+ "https://#{metadata_source_host == GITLAB_HOST ? GITLAB_HOST : GITHUB_HOST}"
267
+ end
268
+
269
+ sig { returns(T.nilable(String)) }
270
+ def metadata_source_host
271
+ return unless metadata_source_url
272
+
273
+ URI.parse(T.must(metadata_source_url)).host&.downcase
274
+ rescue URI::InvalidURIError
275
+ nil
276
+ end
277
+
238
278
  sig { params(text: String).returns(T::Array[Commonmarker::Node]) }
239
279
  def build_mention_link_text_nodes(text)
240
280
  code_node = Commonmarker::Node.new(:code)
@@ -85,13 +85,11 @@ module Dependabot
85
85
  def vulnerabilities_cascade
86
86
  return "" unless vulnerabilities_fixed&.any?
87
87
 
88
- msg = ""
89
- T.must(vulnerabilities_fixed).each do |v|
90
- msg += serialized_vulnerability_details(v)
88
+ msg = T.must(vulnerabilities_fixed).map do |vulnerability|
89
+ details = sanitize_template_tags(serialized_vulnerability_details(vulnerability))
90
+ sanitize_links_and_mentions(details, metadata_source_url: vulnerability["source_url"])
91
91
  end
92
-
93
- msg = sanitize_template_tags(msg)
94
- msg = sanitize_links_and_mentions(msg)
92
+ msg = msg.join
95
93
 
96
94
  build_details_tag(summary: "Vulnerabilities fixed", body: msg)
97
95
  end
@@ -109,7 +107,7 @@ module Dependabot
109
107
  base_url: source_url + "/blob/HEAD/"
110
108
  )
111
109
  msg = sanitize_template_tags(msg)
112
- msg = sanitize_links_and_mentions(msg)
110
+ msg = sanitize_links_and_mentions(msg, metadata_source_url: releases_url)
113
111
 
114
112
  build_details_tag(summary: "Release notes", body: msg)
115
113
  end
@@ -125,7 +123,7 @@ module Dependabot
125
123
  msg = link_issues(text: msg)
126
124
  msg = fix_relative_links(text: msg, base_url: changelog_url)
127
125
  msg = sanitize_template_tags(msg)
128
- msg = sanitize_links_and_mentions(msg)
126
+ msg = sanitize_links_and_mentions(msg, metadata_source_url: changelog_url)
129
127
 
130
128
  build_details_tag(summary: "Changelog", body: msg)
131
129
  end
@@ -141,7 +139,7 @@ module Dependabot
141
139
  msg = link_issues(text: msg)
142
140
  msg = fix_relative_links(text: msg, base_url: upgrade_guide_url)
143
141
  msg = sanitize_template_tags(msg)
144
- msg = sanitize_links_and_mentions(msg)
142
+ msg = sanitize_links_and_mentions(msg, metadata_source_url: upgrade_guide_url)
145
143
 
146
144
  build_details_tag(summary: "Upgrade guide", body: msg)
147
145
  end
@@ -170,7 +168,7 @@ module Dependabot
170
168
  "- See full diff in [compare view](#{commits_url})\n"
171
169
  end
172
170
  msg = link_issues(text: msg)
173
- msg = sanitize_links_and_mentions(msg)
171
+ msg = sanitize_links_and_mentions(msg, metadata_source_url: commits_url)
174
172
 
175
173
  build_details_tag(summary: "Commits", body: msg)
176
174
  end
@@ -317,10 +315,19 @@ module Dependabot
317
315
  !%w(bitbucket codecommit).include?(source.provider)
318
316
  end
319
317
 
320
- sig { params(text: String, unsafe: T::Boolean).returns(String) }
321
- def sanitize_links_and_mentions(text, unsafe: false)
318
+ sig do
319
+ params(
320
+ text: String,
321
+ unsafe: T::Boolean,
322
+ metadata_source_url: T.nilable(String)
323
+ ).returns(String)
324
+ end
325
+ def sanitize_links_and_mentions(text, unsafe: false, metadata_source_url: source_url)
322
326
  LinkAndMentionSanitizer
323
- .new(github_redirection_service: github_redirection_service)
327
+ .new(
328
+ github_redirection_service: github_redirection_service,
329
+ metadata_source_url: metadata_source_url
330
+ )
324
331
  .sanitize_links_and_mentions(text: text, unsafe: unsafe, format_html: source_provider_supports_html?)
325
332
  end
326
333
 
@@ -636,14 +636,14 @@ module Dependabot
636
636
  def updating_a_property?
637
637
  T.must(dependencies.first)
638
638
  .requirements
639
- .any? { |r| r.dig(:metadata, :property_name) }
639
+ .any? { |r| r.metadata_string("property_name") }
640
640
  end
641
641
 
642
642
  sig { returns(T::Boolean) }
643
643
  def updating_a_dependency_set?
644
644
  T.must(dependencies.first)
645
645
  .requirements
646
- .any? { |r| r.dig(:metadata, :dependency_set) }
646
+ .any? { |r| r.metadata_string_hash("dependency_set") }
647
647
  end
648
648
 
649
649
  sig { returns(T::Boolean) }
@@ -663,8 +663,8 @@ module Dependabot
663
663
  T.let(
664
664
  dependencies.first
665
665
  &.requirements
666
- &.find { |r| r.dig(:metadata, :property_name) }
667
- &.dig(:metadata, :property_name),
666
+ &.find { |r| r.metadata_string("property_name") }
667
+ &.metadata_string("property_name"),
668
668
  T.nilable(String)
669
669
  )
670
670
 
@@ -679,9 +679,9 @@ module Dependabot
679
679
  T.let(
680
680
  dependencies.first
681
681
  &.requirements
682
- &.find { |r| r.dig(:metadata, :dependency_set) }
683
- &.dig(:metadata, :dependency_set),
684
- T.nilable(T.nilable(T::Hash[Symbol, String]))
682
+ &.find { |r| r.metadata_string_hash("dependency_set") }
683
+ &.metadata_string_hash("dependency_set"),
684
+ T.nilable(T::Hash[Symbol, String])
685
685
  )
686
686
 
687
687
  raise "No dependency set!" unless @dependency_set
@@ -910,10 +910,10 @@ module Dependabot
910
910
  T.must(dependency.previous_requirements) - dependency.requirements
911
911
 
912
912
  gemspec =
913
- old_reqs.find { |r| r[:file].match?(%r{^[^/]*\.gemspec$}) }
914
- return gemspec.fetch(:requirement) if gemspec
913
+ old_reqs.find { |r| T.must(r.file).match?(%r{^[^/]*\.gemspec$}) }
914
+ return gemspec.requirement_string if gemspec
915
915
 
916
- req = T.must(old_reqs.first).fetch(:requirement)
916
+ req = T.must(old_reqs.first).requirement_string
917
917
  return req if req
918
918
 
919
919
  dependency.previous_ref if dependency.ref_changed?
@@ -925,10 +925,10 @@ module Dependabot
925
925
  dependency.requirements - T.must(dependency.previous_requirements)
926
926
 
927
927
  gemspec =
928
- updated_reqs.find { |r| r[:file].match?(%r{^[^/]*\.gemspec$}) }
929
- return gemspec.fetch(:requirement) if gemspec
928
+ updated_reqs.find { |r| T.must(r.file).match?(%r{^[^/]*\.gemspec$}) }
929
+ return T.must(gemspec.requirement_string) if gemspec
930
930
 
931
- req = T.must(updated_reqs.first).fetch(:requirement)
931
+ req = T.must(updated_reqs.first).requirement_string
932
932
  return req if req
933
933
  return T.must(dependency.new_ref) if dependency.ref_changed? && dependency.new_ref
934
934
 
@@ -16,23 +16,16 @@ module Dependabot
16
16
  class PrNamePrefixer # rubocop:disable Metrics/ClassLength
17
17
  extend T::Sig
18
18
 
19
- ANGULAR_PREFIXES = T.let(
20
- %w(build chore ci docs feat fix perf refactor style test).freeze,
21
- T::Array[String]
22
- )
23
- ESLINT_PREFIXES = T.let(
24
- %w(Breaking Build Chore Docs Fix New Update Upgrade).freeze,
25
- T::Array[String]
26
- )
27
- GITMOJI_PREFIXES = T.let(
28
- %w(alien ambulance apple arrow_down arrow_up art beers bento bookmark boom bug building_construction bulb
29
- busts_in_silhouette camera_flash card_file_box chart_with_upwards_trend checkered_flag children_crossing
30
- clown_face construction construction_worker egg fire globe_with_meridians green_apple green_heart hankey
31
- heavy_minus_sign heavy_plus_sign iphone lipstick lock loud_sound memo mute ok_hand package page_facing_up
32
- pencil2 penguin pushpin recycle rewind robot rocket rotating_light see_no_evil sparkles speech_balloon tada
33
- truck twisted_rightwards_arrows whale wheelchair white_check_mark wrench zap).freeze,
34
- T::Array[String]
35
- )
19
+ ANGULAR_PREFIXES = %w(build chore ci docs feat fix perf refactor style test).freeze
20
+ ESLINT_PREFIXES = %w(Breaking Build Chore Docs Fix New Update Upgrade).freeze
21
+ GITMOJI_PREFIXES = %w(
22
+ alien ambulance apple arrow_down arrow_up art beers bento bookmark boom bug building_construction
23
+ bulb busts_in_silhouette camera_flash card_file_box chart_with_upwards_trend checkered_flag children_crossing
24
+ clown_face construction construction_worker egg fire globe_with_meridians green_apple green_heart hankey
25
+ heavy_minus_sign heavy_plus_sign iphone lipstick lock loud_sound memo mute ok_hand package page_facing_up
26
+ pencil2 penguin pushpin recycle rewind robot rocket rotating_light see_no_evil sparkles speech_balloon tada
27
+ truck twisted_rightwards_arrows whale wheelchair white_check_mark wrench zap
28
+ ).freeze
36
29
 
37
30
  sig do
38
31
  params(
@@ -70,7 +70,7 @@ module Dependabot
70
70
  @pull_request_number = pull_request_number
71
71
  @author_details = author_details
72
72
  @signature_key = signature_key
73
- @commit_message = T.let(commit_message, T.nilable(String))
73
+ @commit_message = commit_message
74
74
  end
75
75
 
76
76
  sig { returns(T.nilable(Sawyer::Resource)) }
@@ -23,6 +23,8 @@ module Dependabot
23
23
  module SharedHelpers # rubocop:disable Metrics/ModuleLength
24
24
  extend T::Sig
25
25
 
26
+ Command = T.type_alias { T.any(String, T::Array[String]) }
27
+ CommandArguments = T.type_alias { T::Array[T.any(String, T::Array[String])] }
26
28
  USER_AGENT = T.let(
27
29
  "dependabot-core/#{Dependabot::VERSION} " \
28
30
  "#{Excon::USER_AGENT} ruby/#{RUBY_VERSION} " \
@@ -125,6 +127,26 @@ module Dependabot
125
127
  CommandHelpers.escape_command(command)
126
128
  end
127
129
 
130
+ sig do
131
+ params(command: Command, allow_unsafe_shell_command: T::Boolean)
132
+ .returns([CommandArguments, String])
133
+ end
134
+ def self.prepare_command(command, allow_unsafe_shell_command:)
135
+ unless command.is_a?(Array)
136
+ prepared_command = allow_unsafe_shell_command ? command : escape_command(command)
137
+ return [[prepared_command], prepared_command]
138
+ end
139
+
140
+ raise ArgumentError, "command must not be empty" if command.empty?
141
+ if allow_unsafe_shell_command
142
+ raise ArgumentError, "allow_unsafe_shell_command cannot be used with an argument vector"
143
+ end
144
+
145
+ executable = T.must(command.first)
146
+ [[[executable, executable], *command.drop(1)], Shellwords.join(command)]
147
+ end
148
+ private_class_method :prepare_command
149
+
128
150
  # rubocop:disable Metrics/MethodLength
129
151
  # rubocop:disable Metrics/AbcSize
130
152
  sig do
@@ -461,10 +483,9 @@ module Dependabot
461
483
  safe_directories
462
484
  end
463
485
 
464
- # rubocop:disable Metrics/PerceivedComplexity
465
486
  sig do
466
487
  params(
467
- command: String,
488
+ command: Command,
468
489
  allow_unsafe_shell_command: T::Boolean,
469
490
  cwd: T.nilable(String),
470
491
  env: T.nilable(T::Hash[String, String]),
@@ -485,14 +506,17 @@ module Dependabot
485
506
  output_observer: nil
486
507
  )
487
508
  start = Time.now
488
- cmd = allow_unsafe_shell_command ? command : escape_command(command)
509
+ command_args, command_for_output = prepare_command(
510
+ command,
511
+ allow_unsafe_shell_command: allow_unsafe_shell_command
512
+ )
489
513
 
490
- puts cmd if ENV["DEBUG_HELPERS"] == "true"
514
+ puts command_for_output if ENV["DEBUG_HELPERS"] == "true"
491
515
 
492
516
  opts = {}
493
517
  opts[:chdir] = cwd if cwd
494
518
 
495
- env_cmd = [env || {}, cmd, opts].compact
519
+ env_cmd = [env || {}, *command_args, opts].compact
496
520
  kwargs = {
497
521
  stderr_to_stdout: stderr_to_stdout,
498
522
  timeout: timeout
@@ -511,7 +535,7 @@ module Dependabot
511
535
  return stdout || "" if process&.success?
512
536
 
513
537
  error_context = {
514
- command: cmd,
538
+ command: command_for_output,
515
539
  fingerprint: fingerprint,
516
540
  time_taken: time_taken,
517
541
  process_exit_value: process.to_s
@@ -524,7 +548,6 @@ module Dependabot
524
548
  error_context: error_context
525
549
  )
526
550
  end
527
- # rubocop:enable Metrics/PerceivedComplexity
528
551
 
529
552
  sig { params(stderr: T.nilable(String), error_context: T::Hash[Symbol, String]).void }
530
553
  def self.check_out_of_disk_memory_error(stderr, error_context)
@@ -63,10 +63,7 @@ module Dependabot
63
63
  (?:#{CODECOMMIT_SOURCE})
64
64
  /x
65
65
 
66
- IGNORED_PROVIDER_HOSTS = T.let(
67
- %w(gitbox.apache.org svn.apache.org fuchsia.googlesource.com).freeze,
68
- T::Array[String]
69
- )
66
+ IGNORED_PROVIDER_HOSTS = %w(gitbox.apache.org svn.apache.org fuchsia.googlesource.com).freeze
70
67
 
71
68
  sig { returns(String) }
72
69
  attr_accessor :provider