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
@@ -22,7 +22,6 @@ module Dependabot
22
22
 
23
23
  def commits_url
24
24
  return unless source
25
- return if source.provider == "azure" # TODO: Fetch Azure commits
26
25
  return if source.provider == "codecommit" # TODO: Fetch Codecommit commits
27
26
 
28
27
  path =
@@ -30,6 +29,7 @@ module Dependabot
30
29
  when "github" then github_compare_path(new_tag, previous_tag)
31
30
  when "bitbucket" then bitbucket_compare_path(new_tag, previous_tag)
32
31
  when "gitlab" then gitlab_compare_path(new_tag, previous_tag)
32
+ when "azure" then azure_compare_path(new_tag, previous_tag)
33
33
  else raise "Unexpected source provider '#{source.provider}'"
34
34
  end
35
35
 
@@ -44,7 +44,7 @@ module Dependabot
44
44
  when "github" then fetch_github_commits
45
45
  when "bitbucket" then fetch_bitbucket_commits
46
46
  when "gitlab" then fetch_gitlab_commits
47
- when "azure" then [] # TODO: Fetch Azure commits
47
+ when "azure" then fetch_azure_commits
48
48
  when "codecommit" then [] # TODO: Fetch Codecommit commits
49
49
  else raise "Unexpected source provider '#{source.provider}'"
50
50
  end
@@ -216,6 +216,18 @@ module Dependabot
216
216
  end
217
217
  end
218
218
 
219
+ def azure_compare_path(new_tag, previous_tag)
220
+ # GC for commits, GT for tags, and GB for branches
221
+ type = git_sha?(new_tag) ? "GC" : "GT"
222
+ if new_tag && previous_tag
223
+ "branchCompare?baseVersion=#{type}#{previous_tag}&targetVersion=#{type}#{new_tag}"
224
+ elsif new_tag
225
+ "commits?itemVersion=#{type}#{new_tag}"
226
+ else
227
+ "commits"
228
+ end
229
+ end
230
+
219
231
  def fetch_github_commits
220
232
  commits =
221
233
  begin
@@ -282,6 +294,26 @@ module Dependabot
282
294
  []
283
295
  end
284
296
 
297
+ def fetch_azure_commits
298
+ type = git_sha?(new_tag) ? "commit" : "tag"
299
+ azure_client.
300
+ compare(previous_tag, new_tag, type).
301
+ map do |commit|
302
+ {
303
+ message: commit["comment"],
304
+ sha: commit["commitId"],
305
+ html_url: commit["remoteUrl"]
306
+ }
307
+ end
308
+ rescue Dependabot::Clients::Azure::NotFound,
309
+ Dependabot::Clients::Azure::Unauthorized,
310
+ Dependabot::Clients::Azure::Forbidden,
311
+ Excon::Error::Server,
312
+ Excon::Error::Socket,
313
+ Excon::Error::Timeout
314
+ []
315
+ end
316
+
285
317
  def gitlab_client
286
318
  @gitlab_client ||= Dependabot::Clients::GitlabWithRetries.
287
319
  for_gitlab_dot_com(credentials: credentials)
@@ -289,7 +321,12 @@ module Dependabot
289
321
 
290
322
  def github_client
291
323
  @github_client ||= Dependabot::Clients::GithubWithRetries.
292
- for_github_dot_com(credentials: credentials)
324
+ for_source(source: source, credentials: credentials)
325
+ end
326
+
327
+ def azure_client
328
+ @azure_client ||= Dependabot::Clients::Azure.
329
+ for_source(source: source, credentials: credentials)
293
330
  end
294
331
 
295
332
  def bitbucket_client
@@ -300,7 +300,7 @@ module Dependabot
300
300
 
301
301
  def github_client
302
302
  @github_client ||= Dependabot::Clients::GithubWithRetries.
303
- for_github_dot_com(credentials: credentials)
303
+ for_source(source: source, credentials: credentials)
304
304
  end
305
305
  end
306
306
  end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dependabot
4
+ class PullRequestCreator
5
+ class BranchNamer
6
+ class GroupRuleStrategy
7
+ def initialize(dependencies:, files:, target_branch:, group_rule:,
8
+ separator: "/", prefix: "dependabot", max_length: nil)
9
+ @dependencies = dependencies
10
+ @files = files
11
+ @target_branch = target_branch
12
+ @group_rule = group_rule
13
+ @separator = separator
14
+ @prefix = prefix
15
+ @max_length = max_length
16
+ end
17
+
18
+ def new_branch_name
19
+ group_rule.name
20
+ end
21
+
22
+ private
23
+
24
+ attr_reader :group_rule
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,208 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "digest"
4
+
5
+ require "dependabot/metadata_finders"
6
+
7
+ module Dependabot
8
+ class PullRequestCreator
9
+ class BranchNamer
10
+ class SoloStrategy
11
+ attr_reader :dependencies, :files, :target_branch, :separator, :prefix, :max_length
12
+
13
+ def initialize(dependencies:, files:, target_branch:, separator: "/",
14
+ prefix: "dependabot", max_length: nil)
15
+ @dependencies = dependencies
16
+ @files = files
17
+ @target_branch = target_branch
18
+ @separator = separator
19
+ @prefix = prefix
20
+ @max_length = max_length
21
+ end
22
+
23
+ def new_branch_name
24
+ @name ||=
25
+ begin
26
+ dependency_name_part =
27
+ if dependencies.count > 1 && updating_a_property?
28
+ property_name
29
+ elsif dependencies.count > 1 && updating_a_dependency_set?
30
+ dependency_set.fetch(:group)
31
+ else
32
+ dependencies.
33
+ map(&:name).
34
+ join("-and-").
35
+ tr(":[]", "-").
36
+ tr("@", "")
37
+ end
38
+
39
+ "#{dependency_name_part}-#{branch_version_suffix}"
40
+ end
41
+
42
+ # Some users need branch names without slashes
43
+ sanitized_name = sanitize_ref(File.join(prefixes, @name).gsub("/", separator))
44
+
45
+ # Shorten the ref in case users refs have length limits
46
+ if @max_length && (sanitized_name.length > @max_length)
47
+ sha = Digest::SHA1.hexdigest(sanitized_name)[0, @max_length]
48
+ sanitized_name[[@max_length - sha.size, 0].max..] = sha
49
+ end
50
+
51
+ sanitized_name
52
+ end
53
+
54
+ private
55
+
56
+ def prefixes
57
+ [
58
+ prefix,
59
+ package_manager,
60
+ files.first.directory.tr(" ", "-"),
61
+ target_branch
62
+ ].compact
63
+ end
64
+
65
+ def package_manager
66
+ dependencies.first.package_manager
67
+ end
68
+
69
+ def updating_a_property?
70
+ dependencies.first.
71
+ requirements.
72
+ any? { |r| r.dig(:metadata, :property_name) }
73
+ end
74
+
75
+ def updating_a_dependency_set?
76
+ dependencies.first.
77
+ requirements.
78
+ any? { |r| r.dig(:metadata, :dependency_set) }
79
+ end
80
+
81
+ def property_name
82
+ @property_name ||= dependencies.first.requirements.
83
+ find { |r| r.dig(:metadata, :property_name) }&.
84
+ dig(:metadata, :property_name)
85
+
86
+ raise "No property name!" unless @property_name
87
+
88
+ @property_name
89
+ end
90
+
91
+ def dependency_set
92
+ @dependency_set ||= dependencies.first.requirements.
93
+ find { |r| r.dig(:metadata, :dependency_set) }&.
94
+ dig(:metadata, :dependency_set)
95
+
96
+ raise "No dependency set!" unless @dependency_set
97
+
98
+ @dependency_set
99
+ end
100
+
101
+ def branch_version_suffix
102
+ dep = dependencies.first
103
+
104
+ if dep.removed?
105
+ "-removed"
106
+ elsif library? && ref_changed?(dep) && new_ref(dep)
107
+ new_ref(dep)
108
+ elsif library?
109
+ sanitized_requirement(dep)
110
+ else
111
+ new_version(dep)
112
+ end
113
+ end
114
+
115
+ def sanitized_requirement(dependency)
116
+ new_library_requirement(dependency).
117
+ delete(" ").
118
+ gsub("!=", "neq-").
119
+ gsub(">=", "gte-").
120
+ gsub("<=", "lte-").
121
+ gsub("~>", "tw-").
122
+ gsub("^", "tw-").
123
+ gsub("||", "or-").
124
+ gsub("~", "approx-").
125
+ gsub("~=", "tw-").
126
+ gsub(/==*/, "eq-").
127
+ gsub(">", "gt-").
128
+ gsub("<", "lt-").
129
+ gsub("*", "star").
130
+ gsub(",", "-and-")
131
+ end
132
+
133
+ def new_version(dependency)
134
+ # Version looks like a git SHA and we could be updating to a specific
135
+ # ref in which case we return that otherwise we return a shorthand sha
136
+ if dependency.version.match?(/^[0-9a-f]{40}$/)
137
+ return new_ref(dependency) if ref_changed?(dependency) && new_ref(dependency)
138
+
139
+ dependency.version[0..6]
140
+ elsif dependency.version == dependency.previous_version &&
141
+ package_manager == "docker"
142
+ dependency.requirements.
143
+ filter_map { |r| r.dig(:source, "digest") || r.dig(:source, :digest) }.
144
+ first.split(":").last[0..6]
145
+ else
146
+ dependency.version
147
+ end
148
+ end
149
+
150
+ def previous_ref(dependency)
151
+ previous_refs = dependency.previous_requirements.filter_map do |r|
152
+ r.dig(:source, "ref") || r.dig(:source, :ref)
153
+ end.uniq
154
+ return previous_refs.first if previous_refs.count == 1
155
+ end
156
+
157
+ def new_ref(dependency)
158
+ new_refs = dependency.requirements.filter_map do |r|
159
+ r.dig(:source, "ref") || r.dig(:source, :ref)
160
+ end.uniq
161
+ return new_refs.first if new_refs.count == 1
162
+ end
163
+
164
+ def ref_changed?(dependency)
165
+ # We could go from multiple previous refs (nil) to a single new ref
166
+ previous_ref(dependency) != new_ref(dependency)
167
+ end
168
+
169
+ def new_library_requirement(dependency)
170
+ updated_reqs =
171
+ dependency.requirements - dependency.previous_requirements
172
+
173
+ gemspec =
174
+ updated_reqs.find { |r| r[:file].match?(%r{^[^/]*\.gemspec$}) }
175
+ return gemspec[:requirement] if gemspec
176
+
177
+ updated_reqs.first[:requirement]
178
+ end
179
+
180
+ # TODO: Bring this in line with existing library checks that we do in the
181
+ # update checkers, which are also overriden by passing an explicit
182
+ # `requirements_update_strategy`.
183
+ #
184
+ # TODO re-use in MessageBuilder
185
+ def library?
186
+ dependencies.any? { |d| !d.appears_in_lockfile? }
187
+ end
188
+
189
+ def requirements_changed?(dependency)
190
+ (dependency.requirements - dependency.previous_requirements).any?
191
+ end
192
+
193
+ def sanitize_ref(ref)
194
+ # This isn't a complete implementation of git's ref validation, but it
195
+ # covers most cases that crop up. Its list of allowed characters is a
196
+ # bit stricter than git's, but that's for cosmetic reasons.
197
+ ref.
198
+ # Remove forbidden characters (those not already replaced elsewhere)
199
+ gsub(%r{[^A-Za-z0-9/\-_.(){}]}, "").
200
+ # Slashes can't be followed by periods
201
+ gsub(%r{/\.}, "/dot-").squeeze(".").squeeze("/").
202
+ # Trailing periods are forbidden
203
+ sub(/\.$/, "")
204
+ end
205
+ end
206
+ end
207
+ end
208
+ end
@@ -4,203 +4,52 @@ require "digest"
4
4
 
5
5
  require "dependabot/metadata_finders"
6
6
  require "dependabot/pull_request_creator"
7
+ require "dependabot/pull_request_creator/branch_namer/solo_strategy"
7
8
 
8
9
  module Dependabot
9
10
  class PullRequestCreator
10
11
  class BranchNamer
11
- attr_reader :dependencies, :files, :target_branch, :separator, :prefix, :max_length
12
+ attr_reader :dependencies, :files, :target_branch, :separator, :prefix, :max_length, :group_rule
12
13
 
13
- def initialize(dependencies:, files:, target_branch:, separator: "/",
14
- prefix: "dependabot", max_length: nil)
14
+ def initialize(dependencies:, files:, target_branch:, group_rule: nil,
15
+ separator: "/", prefix: "dependabot", max_length: nil)
15
16
  @dependencies = dependencies
16
17
  @files = files
17
18
  @target_branch = target_branch
19
+ @group_rule = group_rule
18
20
  @separator = separator
19
21
  @prefix = prefix
20
22
  @max_length = max_length
21
23
  end
22
24
 
23
25
  def new_branch_name
24
- @name ||=
25
- begin
26
- dependency_name_part =
27
- if dependencies.count > 1 && updating_a_property?
28
- property_name
29
- elsif dependencies.count > 1 && updating_a_dependency_set?
30
- dependency_set.fetch(:group)
31
- else
32
- dependencies.
33
- map(&:name).
34
- join("-and-").
35
- tr(":[]", "-").
36
- tr("@", "")
37
- end
38
-
39
- "#{dependency_name_part}-#{branch_version_suffix}"
40
- end
41
-
42
- # Some users need branch names without slashes
43
- sanitized_name = sanitize_ref(File.join(prefixes, @name).gsub("/", separator))
44
-
45
- # Shorten the ref in case users refs have length limits
46
- if @max_length && (sanitized_name.length > @max_length)
47
- sha = Digest::SHA1.hexdigest(sanitized_name)[0, @max_length]
48
- sanitized_name[[@max_length - sha.size, 0].max..] = sha
49
- end
50
-
51
- sanitized_name
26
+ strategy.new_branch_name
52
27
  end
53
28
 
54
29
  private
55
30
 
56
- def prefixes
57
- [
58
- prefix,
59
- package_manager,
60
- files.first.directory.tr(" ", "-"),
61
- target_branch
62
- ].compact
63
- end
64
-
65
- def package_manager
66
- dependencies.first.package_manager
67
- end
68
-
69
- def updating_a_property?
70
- dependencies.first.
71
- requirements.
72
- any? { |r| r.dig(:metadata, :property_name) }
73
- end
74
-
75
- def updating_a_dependency_set?
76
- dependencies.first.
77
- requirements.
78
- any? { |r| r.dig(:metadata, :dependency_set) }
79
- end
80
-
81
- def property_name
82
- @property_name ||= dependencies.first.requirements.
83
- find { |r| r.dig(:metadata, :property_name) }&.
84
- dig(:metadata, :property_name)
85
-
86
- raise "No property name!" unless @property_name
87
-
88
- @property_name
89
- end
90
-
91
- def dependency_set
92
- @dependency_set ||= dependencies.first.requirements.
93
- find { |r| r.dig(:metadata, :dependency_set) }&.
94
- dig(:metadata, :dependency_set)
95
-
96
- raise "No dependency set!" unless @dependency_set
97
-
98
- @dependency_set
99
- end
100
-
101
- def branch_version_suffix
102
- dep = dependencies.first
103
-
104
- if dep.removed?
105
- "-removed"
106
- elsif library? && ref_changed?(dep) && new_ref(dep)
107
- new_ref(dep)
108
- elsif library?
109
- sanitized_requirement(dep)
110
- else
111
- new_version(dep)
112
- end
113
- end
114
-
115
- def sanitized_requirement(dependency)
116
- new_library_requirement(dependency).
117
- delete(" ").
118
- gsub("!=", "neq-").
119
- gsub(">=", "gte-").
120
- gsub("<=", "lte-").
121
- gsub("~>", "tw-").
122
- gsub("^", "tw-").
123
- gsub("||", "or-").
124
- gsub("~", "approx-").
125
- gsub("~=", "tw-").
126
- gsub(/==*/, "eq-").
127
- gsub(">", "gt-").
128
- gsub("<", "lt-").
129
- gsub("*", "star").
130
- gsub(",", "-and-")
131
- end
132
-
133
- def new_version(dependency)
134
- # Version looks like a git SHA and we could be updating to a specific
135
- # ref in which case we return that otherwise we return a shorthand sha
136
- if dependency.version.match?(/^[0-9a-f]{40}$/)
137
- return new_ref(dependency) if ref_changed?(dependency) && new_ref(dependency)
138
-
139
- dependency.version[0..6]
140
- elsif dependency.version == dependency.previous_version &&
141
- package_manager == "docker"
142
- dependency.requirements.
143
- filter_map { |r| r.dig(:source, "digest") || r.dig(:source, :digest) }.
144
- first.split(":").last[0..6]
145
- else
146
- dependency.version
147
- end
148
- end
149
-
150
- def previous_ref(dependency)
151
- previous_refs = dependency.previous_requirements.filter_map do |r|
152
- r.dig(:source, "ref") || r.dig(:source, :ref)
153
- end.uniq
154
- return previous_refs.first if previous_refs.count == 1
155
- end
156
-
157
- def new_ref(dependency)
158
- new_refs = dependency.requirements.filter_map do |r|
159
- r.dig(:source, "ref") || r.dig(:source, :ref)
160
- end.uniq
161
- return new_refs.first if new_refs.count == 1
162
- end
163
-
164
- def ref_changed?(dependency)
165
- # We could go from multiple previous refs (nil) to a single new ref
166
- previous_ref(dependency) != new_ref(dependency)
167
- end
168
-
169
- def new_library_requirement(dependency)
170
- updated_reqs =
171
- dependency.requirements - dependency.previous_requirements
172
-
173
- gemspec =
174
- updated_reqs.find { |r| r[:file].match?(%r{^[^/]*\.gemspec$}) }
175
- return gemspec[:requirement] if gemspec
176
-
177
- updated_reqs.first[:requirement]
178
- end
179
-
180
- # TODO: Bring this in line with existing library checks that we do in the
181
- # update checkers, which are also overriden by passing an explicit
182
- # `requirements_update_strategy`.
183
- #
184
- # TODO re-use in MessageBuilder
185
- def library?
186
- dependencies.any? { |d| !d.appears_in_lockfile? }
187
- end
188
-
189
- def requirements_changed?(dependency)
190
- (dependency.requirements - dependency.previous_requirements).any?
191
- end
192
-
193
- def sanitize_ref(ref)
194
- # This isn't a complete implementation of git's ref validation, but it
195
- # covers most cases that crop up. Its list of allowed characters is a
196
- # bit stricter than git's, but that's for cosmetic reasons.
197
- ref.
198
- # Remove forbidden characters (those not already replaced elsewhere)
199
- gsub(%r{[^A-Za-z0-9/\-_.(){}]}, "").
200
- # Slashes can't be followed by periods
201
- gsub(%r{/\.}, "/dot-").squeeze(".").squeeze("/").
202
- # Trailing periods are forbidden
203
- sub(/\.$/, "")
31
+ def strategy
32
+ @strategy ||=
33
+ if group_rule.nil?
34
+ SoloStrategy.new(
35
+ dependencies: dependencies,
36
+ files: files,
37
+ target_branch: target_branch,
38
+ separator: separator,
39
+ prefix: prefix,
40
+ max_length: max_length
41
+ )
42
+ else
43
+ GroupRuleStrategy.new(
44
+ dependencies: dependencies,
45
+ files: files,
46
+ target_branch: target_branch,
47
+ group_rule: group_rule,
48
+ separator: separator,
49
+ prefix: prefix,
50
+ max_length: max_length
51
+ )
52
+ end
204
53
  end
205
54
  end
206
55
  end
@@ -174,7 +174,11 @@ module Dependabot
174
174
  end
175
175
 
176
176
  def default_labels_for_pr
177
- if custom_labels then custom_labels & labels
177
+ if custom_labels
178
+ # Azure does not have centralised labels
179
+ return custom_labels if source.provider == "azure"
180
+
181
+ custom_labels & labels
178
182
  else
179
183
  [
180
184
  default_dependencies_label,
@@ -34,7 +34,7 @@ module Dependabot
34
34
  @github_redirection_service = github_redirection_service
35
35
  end
36
36
 
37
- def sanitize_links_and_mentions(text:, unsafe: false)
37
+ def sanitize_links_and_mentions(text:, unsafe: false, format_html: true)
38
38
  doc = CommonMarker.render_doc(
39
39
  text, :LIBERAL_HTML_TAG, COMMONMARKER_EXTENSIONS
40
40
  )
@@ -45,6 +45,8 @@ module Dependabot
45
45
  sanitize_nwo_text(doc)
46
46
 
47
47
  mode = unsafe ? :UNSAFE : :DEFAULT
48
+ return doc.to_commonmark([mode] + COMMONMARKER_OPTIONS) unless format_html
49
+
48
50
  doc.to_html(([mode] + COMMONMARKER_OPTIONS), COMMONMARKER_EXTENSIONS)
49
51
  end
50
52
 
@@ -247,7 +247,7 @@ module Dependabot
247
247
  def sanitize_links_and_mentions(text, unsafe: false)
248
248
  LinkAndMentionSanitizer.
249
249
  new(github_redirection_service: github_redirection_service).
250
- sanitize_links_and_mentions(text: text, unsafe: unsafe)
250
+ sanitize_links_and_mentions(text: text, unsafe: unsafe, format_html: source_provider_supports_html?)
251
251
  end
252
252
 
253
253
  def sanitize_template_tags(text)