dependabot-common 0.212.0 → 0.213.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.
- checksums.yaml +4 -4
- data/lib/dependabot/clients/bitbucket.rb +8 -1
- data/lib/dependabot/config/file.rb +1 -1
- data/lib/dependabot/dependency.rb +19 -2
- data/lib/dependabot/errors.rb +3 -3
- data/lib/dependabot/experiments.rb +19 -0
- data/lib/dependabot/file_fetchers/base.rb +143 -80
- data/lib/dependabot/file_parsers/base/dependency_set.rb +106 -41
- data/lib/dependabot/git_commit_checker.rb +23 -11
- data/lib/dependabot/git_metadata_fetcher.rb +1 -1
- data/lib/dependabot/pull_request_creator/branch_namer.rb +15 -4
- data/lib/dependabot/pull_request_creator/labeler.rb +6 -6
- data/lib/dependabot/pull_request_creator/message_builder/issue_linker.rb +5 -5
- data/lib/dependabot/pull_request_creator/message_builder/link_and_mention_sanitizer.rb +33 -5
- data/lib/dependabot/pull_request_creator/message_builder/metadata_presenter.rb +1 -3
- data/lib/dependabot/pull_request_creator/message_builder.rb +78 -6
- data/lib/dependabot/pull_request_creator/pr_name_prefixer.rb +3 -2
- data/lib/dependabot/pull_request_creator.rb +6 -3
- data/lib/dependabot/pull_request_updater/azure.rb +1 -1
- data/lib/dependabot/pull_request_updater/github.rb +15 -12
- data/lib/dependabot/pull_request_updater.rb +2 -1
- data/lib/dependabot/source.rb +9 -9
- data/lib/dependabot/update_checkers/base.rb +1 -1
- data/lib/dependabot/version.rb +1 -1
- metadata +16 -57
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 23fa5c7ea872ca0849f22018af9b0811ad9044f03a4e7d59aa023b3dd80bd4e6
|
|
4
|
+
data.tar.gz: cea778ebef75ccec5afcd3e5932af78d9711c51c4c864ea02d65930fce8ca4dc
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b14ad55cbabd2a49bd35c7f8012f95972515eead80a71363353a8978286e9d756fd97da5442cb3013acdebfb1a77c7f8f7c450bac553a2d6f52b12687fcf2d43
|
|
7
|
+
data.tar.gz: 86cbba3afb724d1ee0b6c1fd0bb357a33c288263b3032e0ef029e099b5d7f7d8d5104526a8890d6233fdb8850b56ecd05cb0746bd8e6fe8fd84338db8caa28dd
|
|
@@ -144,7 +144,14 @@ module Dependabot
|
|
|
144
144
|
end
|
|
145
145
|
# rubocop:enable Metrics/ParameterLists
|
|
146
146
|
|
|
147
|
+
def current_user
|
|
148
|
+
base_url = "https://api.bitbucket.org/2.0/user?fields=uuid"
|
|
149
|
+
response = get(base_url)
|
|
150
|
+
JSON.parse(response.body).fetch("uuid")
|
|
151
|
+
end
|
|
152
|
+
|
|
147
153
|
def default_reviewers(repo)
|
|
154
|
+
current_uuid = current_user
|
|
148
155
|
path = "#{repo}/default-reviewers?pagelen=100&fields=values.uuid,next"
|
|
149
156
|
reviewers_url = base_url + path
|
|
150
157
|
|
|
@@ -153,7 +160,7 @@ module Dependabot
|
|
|
153
160
|
reviewer_data = []
|
|
154
161
|
|
|
155
162
|
default_reviewers.each do |reviewer|
|
|
156
|
-
reviewer_data.append({ uuid: reviewer.fetch("uuid") })
|
|
163
|
+
reviewer_data.append({ uuid: reviewer.fetch("uuid") }) unless current_uuid == reviewer.fetch("uuid")
|
|
157
164
|
end
|
|
158
165
|
|
|
159
166
|
reviewer_data
|
|
@@ -71,7 +71,7 @@ module Dependabot
|
|
|
71
71
|
commit_message = cfg&.dig(:"commit-message") || {}
|
|
72
72
|
Dependabot::Config::UpdateConfig::CommitMessageOptions.new(
|
|
73
73
|
prefix: commit_message[:prefix],
|
|
74
|
-
prefix_development: commit_message[:"prefix-development"],
|
|
74
|
+
prefix_development: commit_message[:"prefix-development"] || commit_message[:prefix],
|
|
75
75
|
include: commit_message[:include]
|
|
76
76
|
)
|
|
77
77
|
end
|
|
@@ -37,11 +37,11 @@ module Dependabot
|
|
|
37
37
|
|
|
38
38
|
attr_reader :name, :version, :requirements, :package_manager,
|
|
39
39
|
:previous_version, :previous_requirements,
|
|
40
|
-
:subdependency_metadata
|
|
40
|
+
:subdependency_metadata, :metadata
|
|
41
41
|
|
|
42
42
|
def initialize(name:, requirements:, package_manager:, version: nil,
|
|
43
43
|
previous_version: nil, previous_requirements: nil,
|
|
44
|
-
subdependency_metadata: [], removed: false)
|
|
44
|
+
subdependency_metadata: [], removed: false, metadata: {})
|
|
45
45
|
@name = name
|
|
46
46
|
@version = version
|
|
47
47
|
@requirements = requirements.map { |req| symbolize_keys(req) }
|
|
@@ -54,6 +54,7 @@ module Dependabot
|
|
|
54
54
|
map { |h| symbolize_keys(h) }
|
|
55
55
|
end
|
|
56
56
|
@removed = removed
|
|
57
|
+
@metadata = symbolize_keys(metadata || {})
|
|
57
58
|
|
|
58
59
|
check_values
|
|
59
60
|
end
|
|
@@ -105,6 +106,22 @@ module Dependabot
|
|
|
105
106
|
display_name_builder.call(name)
|
|
106
107
|
end
|
|
107
108
|
|
|
109
|
+
# Returns all detected versions of the dependency. Only ecosystems that
|
|
110
|
+
# support this feature will return more than the current version.
|
|
111
|
+
def all_versions
|
|
112
|
+
all_versions = metadata[:all_versions]
|
|
113
|
+
return [version].compact unless all_versions
|
|
114
|
+
|
|
115
|
+
all_versions.filter_map(&:version)
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# This dependency is being indirectly updated by an update to another
|
|
119
|
+
# dependency. We don't need to try and update it ourselves but want to
|
|
120
|
+
# surface it to the user in the PR.
|
|
121
|
+
def informational_only?
|
|
122
|
+
metadata[:information_only]
|
|
123
|
+
end
|
|
124
|
+
|
|
108
125
|
def ==(other)
|
|
109
126
|
other.instance_of?(self.class) && to_h == other.to_h
|
|
110
127
|
end
|
data/lib/dependabot/errors.rb
CHANGED
|
@@ -4,9 +4,9 @@ require "dependabot/utils"
|
|
|
4
4
|
|
|
5
5
|
module Dependabot
|
|
6
6
|
class DependabotError < StandardError
|
|
7
|
-
BASIC_AUTH_REGEX = %r{://(?<auth>[^:]*:[^@%\s]+(@|%40))}
|
|
7
|
+
BASIC_AUTH_REGEX = %r{://(?<auth>[^:]*:[^@%\s]+(@|%40))}
|
|
8
8
|
# Remove any path segment from fury.io sources
|
|
9
|
-
FURY_IO_PATH_REGEX = %r{fury\.io/(?<path>.+)}
|
|
9
|
+
FURY_IO_PATH_REGEX = %r{fury\.io/(?<path>.+)}
|
|
10
10
|
|
|
11
11
|
def initialize(message = nil)
|
|
12
12
|
super(sanitize_message(message))
|
|
@@ -18,7 +18,7 @@ module Dependabot
|
|
|
18
18
|
return message unless message.is_a?(String)
|
|
19
19
|
|
|
20
20
|
path_regex =
|
|
21
|
-
Regexp.escape(Utils::BUMP_TMP_DIR_PATH) + "
|
|
21
|
+
Regexp.escape(Utils::BUMP_TMP_DIR_PATH) + "\\/" +
|
|
22
22
|
Regexp.escape(Utils::BUMP_TMP_FILE_PREFIX) + "[a-zA-Z0-9-]*"
|
|
23
23
|
|
|
24
24
|
message = message.gsub(/#{path_regex}/, "dependabot_tmp_dir").strip
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Dependabot
|
|
4
|
+
module Experiments
|
|
5
|
+
@experiments = {}
|
|
6
|
+
|
|
7
|
+
def self.reset!
|
|
8
|
+
@experiments = {}
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def self.register(name, value)
|
|
12
|
+
@experiments[name.to_sym] = value
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def self.enabled?(name)
|
|
16
|
+
!!@experiments[name.to_sym]
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "stringio"
|
|
3
4
|
require "dependabot/config"
|
|
4
5
|
require "dependabot/dependency_file"
|
|
5
6
|
require "dependabot/source"
|
|
@@ -69,6 +70,7 @@ module Dependabot
|
|
|
69
70
|
end
|
|
70
71
|
|
|
71
72
|
def commit
|
|
73
|
+
return cloned_commit if cloned_commit
|
|
72
74
|
return source.commit if source.commit
|
|
73
75
|
|
|
74
76
|
branch = target_branch || default_branch_for_repo
|
|
@@ -84,7 +86,11 @@ module Dependabot
|
|
|
84
86
|
def clone_repo_contents
|
|
85
87
|
@clone_repo_contents ||=
|
|
86
88
|
_clone_repo_contents(target_directory: repo_contents_path)
|
|
87
|
-
rescue Dependabot::SharedHelpers::HelperSubprocessFailed
|
|
89
|
+
rescue Dependabot::SharedHelpers::HelperSubprocessFailed => e
|
|
90
|
+
if e.message.include?("fatal: Remote branch #{target_branch} not found in upstream origin")
|
|
91
|
+
raise Dependabot::BranchNotFound, target_branch
|
|
92
|
+
end
|
|
93
|
+
|
|
88
94
|
raise Dependabot::RepoNotFound, source
|
|
89
95
|
end
|
|
90
96
|
|
|
@@ -168,6 +174,97 @@ module Dependabot
|
|
|
168
174
|
end
|
|
169
175
|
end
|
|
170
176
|
|
|
177
|
+
def cloned_commit
|
|
178
|
+
return if repo_contents_path.nil? || !File.directory?(File.join(repo_contents_path, ".git"))
|
|
179
|
+
|
|
180
|
+
SharedHelpers.with_git_configured(credentials: credentials) do
|
|
181
|
+
Dir.chdir(repo_contents_path) do
|
|
182
|
+
return SharedHelpers.run_shell_command("git rev-parse HEAD")&.strip
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
def default_branch_for_repo
|
|
188
|
+
@default_branch_for_repo ||= client_for_provider.
|
|
189
|
+
fetch_default_branch(repo)
|
|
190
|
+
rescue *CLIENT_NOT_FOUND_ERRORS
|
|
191
|
+
raise Dependabot::RepoNotFound, source
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
def update_linked_paths(repo, path, commit, github_response)
|
|
195
|
+
case github_response.type
|
|
196
|
+
when "submodule"
|
|
197
|
+
sub_source = Source.from_url(github_response.submodule_git_url)
|
|
198
|
+
return unless sub_source
|
|
199
|
+
|
|
200
|
+
@linked_paths[path] = {
|
|
201
|
+
repo: sub_source.repo,
|
|
202
|
+
provider: sub_source.provider,
|
|
203
|
+
commit: github_response.sha,
|
|
204
|
+
path: "/"
|
|
205
|
+
}
|
|
206
|
+
when "symlink"
|
|
207
|
+
updated_path = File.join(File.dirname(path), github_response.target)
|
|
208
|
+
@linked_paths[path] = {
|
|
209
|
+
repo: repo,
|
|
210
|
+
provider: "github",
|
|
211
|
+
commit: commit,
|
|
212
|
+
path: Pathname.new(updated_path).cleanpath.to_path
|
|
213
|
+
}
|
|
214
|
+
end
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
def recurse_submodules_when_cloning?
|
|
218
|
+
false
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
def client_for_provider
|
|
222
|
+
case source.provider
|
|
223
|
+
when "github" then github_client
|
|
224
|
+
when "gitlab" then gitlab_client
|
|
225
|
+
when "azure" then azure_client
|
|
226
|
+
when "bitbucket" then bitbucket_client
|
|
227
|
+
when "codecommit" then codecommit_client
|
|
228
|
+
else raise "Unsupported provider '#{source.provider}'."
|
|
229
|
+
end
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
def github_client
|
|
233
|
+
@github_client ||=
|
|
234
|
+
Dependabot::Clients::GithubWithRetries.for_source(
|
|
235
|
+
source: source,
|
|
236
|
+
credentials: credentials
|
|
237
|
+
)
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
def gitlab_client
|
|
241
|
+
@gitlab_client ||=
|
|
242
|
+
Dependabot::Clients::GitlabWithRetries.for_source(
|
|
243
|
+
source: source,
|
|
244
|
+
credentials: credentials
|
|
245
|
+
)
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
def azure_client
|
|
249
|
+
@azure_client ||=
|
|
250
|
+
Dependabot::Clients::Azure.
|
|
251
|
+
for_source(source: source, credentials: credentials)
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
def bitbucket_client
|
|
255
|
+
# TODO: When self-hosted Bitbucket is supported this should use
|
|
256
|
+
# `Bitbucket.for_source`
|
|
257
|
+
@bitbucket_client ||=
|
|
258
|
+
Dependabot::Clients::BitbucketWithRetries.
|
|
259
|
+
for_bitbucket_dot_org(credentials: credentials)
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
def codecommit_client
|
|
263
|
+
@codecommit_client ||=
|
|
264
|
+
Dependabot::Clients::CodeCommit.
|
|
265
|
+
for_source(source: source, credentials: credentials)
|
|
266
|
+
end
|
|
267
|
+
|
|
171
268
|
#################################################
|
|
172
269
|
# INTERNAL METHODS (not for use by sub-classes) #
|
|
173
270
|
#################################################
|
|
@@ -254,29 +351,6 @@ module Dependabot
|
|
|
254
351
|
end
|
|
255
352
|
end
|
|
256
353
|
|
|
257
|
-
def update_linked_paths(repo, path, commit, github_response)
|
|
258
|
-
case github_response.type
|
|
259
|
-
when "submodule"
|
|
260
|
-
sub_source = Source.from_url(github_response.submodule_git_url)
|
|
261
|
-
return unless sub_source
|
|
262
|
-
|
|
263
|
-
@linked_paths[path] = {
|
|
264
|
-
repo: sub_source.repo,
|
|
265
|
-
provider: sub_source.provider,
|
|
266
|
-
commit: github_response.sha,
|
|
267
|
-
path: "/"
|
|
268
|
-
}
|
|
269
|
-
when "symlink"
|
|
270
|
-
updated_path = File.join(File.dirname(path), github_response.target)
|
|
271
|
-
@linked_paths[path] = {
|
|
272
|
-
repo: repo,
|
|
273
|
-
provider: "github",
|
|
274
|
-
commit: commit,
|
|
275
|
-
path: Pathname.new(updated_path).cleanpath.to_path
|
|
276
|
-
}
|
|
277
|
-
end
|
|
278
|
-
end
|
|
279
|
-
|
|
280
354
|
def _build_github_file_struct(file)
|
|
281
355
|
OpenStruct.new(
|
|
282
356
|
name: file.name,
|
|
@@ -473,13 +547,6 @@ module Dependabot
|
|
|
473
547
|
end
|
|
474
548
|
# rubocop:enable Metrics/AbcSize
|
|
475
549
|
|
|
476
|
-
def default_branch_for_repo
|
|
477
|
-
@default_branch_for_repo ||= client_for_provider.
|
|
478
|
-
fetch_default_branch(repo)
|
|
479
|
-
rescue *CLIENT_NOT_FOUND_ERRORS
|
|
480
|
-
raise Dependabot::RepoNotFound, source
|
|
481
|
-
end
|
|
482
|
-
|
|
483
550
|
# Update the @linked_paths hash by exploiting a side-effect of
|
|
484
551
|
# recursively calling `repo_contents` for each directory up the tree
|
|
485
552
|
# until a submodule or symlink is found
|
|
@@ -504,6 +571,10 @@ module Dependabot
|
|
|
504
571
|
max_by(&:length)
|
|
505
572
|
end
|
|
506
573
|
|
|
574
|
+
# rubocop:disable Metrics/AbcSize
|
|
575
|
+
# rubocop:disable Metrics/MethodLength
|
|
576
|
+
# rubocop:disable Metrics/PerceivedComplexity
|
|
577
|
+
# rubocop:disable Metrics/BlockLength
|
|
507
578
|
def _clone_repo_contents(target_directory:)
|
|
508
579
|
SharedHelpers.with_git_configured(credentials: credentials) do
|
|
509
580
|
path = target_directory || File.join("tmp", source.repo)
|
|
@@ -512,62 +583,54 @@ module Dependabot
|
|
|
512
583
|
return path if Dir.exist?(File.join(path, ".git"))
|
|
513
584
|
|
|
514
585
|
FileUtils.mkdir_p(path)
|
|
515
|
-
|
|
586
|
+
|
|
587
|
+
clone_options = StringIO.new
|
|
588
|
+
clone_options << "--no-tags --depth 1"
|
|
589
|
+
clone_options << if recurse_submodules_when_cloning?
|
|
590
|
+
" --recurse-submodules --shallow-submodules"
|
|
591
|
+
else
|
|
592
|
+
" --no-recurse-submodules"
|
|
593
|
+
end
|
|
594
|
+
clone_options << " --branch #{source.branch} --single-branch" if source.branch
|
|
516
595
|
SharedHelpers.run_shell_command(
|
|
517
596
|
<<~CMD
|
|
518
|
-
git clone
|
|
597
|
+
git clone #{clone_options.string} #{source.url} #{path}
|
|
519
598
|
CMD
|
|
520
599
|
)
|
|
521
|
-
path
|
|
522
|
-
end
|
|
523
|
-
end
|
|
524
600
|
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
end
|
|
551
|
-
|
|
552
|
-
def azure_client
|
|
553
|
-
@azure_client ||=
|
|
554
|
-
Dependabot::Clients::Azure.
|
|
555
|
-
for_source(source: source, credentials: credentials)
|
|
556
|
-
end
|
|
557
|
-
|
|
558
|
-
def bitbucket_client
|
|
559
|
-
# TODO: When self-hosted Bitbucket is supported this should use
|
|
560
|
-
# `Bitbucket.for_source`
|
|
561
|
-
@bitbucket_client ||=
|
|
562
|
-
Dependabot::Clients::BitbucketWithRetries.
|
|
563
|
-
for_bitbucket_dot_org(credentials: credentials)
|
|
564
|
-
end
|
|
601
|
+
if source.commit
|
|
602
|
+
# This code will only be called for testing. Production will never pass a commit
|
|
603
|
+
# since Dependabot always wants to use the latest commit on a branch.
|
|
604
|
+
Dir.chdir(path) do
|
|
605
|
+
fetch_options = StringIO.new
|
|
606
|
+
fetch_options << "--depth 1"
|
|
607
|
+
fetch_options << if recurse_submodules_when_cloning?
|
|
608
|
+
" --recurse-submodules=on-demand"
|
|
609
|
+
else
|
|
610
|
+
" --no-recurse-submodules"
|
|
611
|
+
end
|
|
612
|
+
# Need to fetch the commit due to the --depth 1 above.
|
|
613
|
+
SharedHelpers.run_shell_command("git fetch #{fetch_options.string} origin #{source.commit}")
|
|
614
|
+
|
|
615
|
+
reset_options = StringIO.new
|
|
616
|
+
reset_options << "--hard"
|
|
617
|
+
reset_options << if recurse_submodules_when_cloning?
|
|
618
|
+
" --recurse-submodules"
|
|
619
|
+
else
|
|
620
|
+
" --no-recurse-submodules"
|
|
621
|
+
end
|
|
622
|
+
# Set HEAD to this commit so later calls so git reset HEAD will work.
|
|
623
|
+
SharedHelpers.run_shell_command("git reset #{reset_options.string} #{source.commit}")
|
|
624
|
+
end
|
|
625
|
+
end
|
|
565
626
|
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
Dependabot::Clients::CodeCommit.
|
|
569
|
-
for_source(source: source, credentials: credentials)
|
|
627
|
+
path
|
|
628
|
+
end
|
|
570
629
|
end
|
|
630
|
+
# rubocop:enable Metrics/AbcSize
|
|
631
|
+
# rubocop:enable Metrics/MethodLength
|
|
632
|
+
# rubocop:enable Metrics/PerceivedComplexity
|
|
633
|
+
# rubocop:enable Metrics/BlockLength
|
|
571
634
|
end
|
|
572
635
|
end
|
|
573
636
|
end
|
|
@@ -14,34 +14,42 @@ module Dependabot
|
|
|
14
14
|
raise ArgumentError, "must be an array of Dependency objects"
|
|
15
15
|
end
|
|
16
16
|
|
|
17
|
-
@dependencies = dependencies
|
|
18
17
|
@case_sensitive = case_sensitive
|
|
18
|
+
@dependencies = Hash.new { |hsh, key| hsh[key] = DependencySlot.new }
|
|
19
|
+
dependencies.each { |dep| self << dep }
|
|
19
20
|
end
|
|
20
21
|
|
|
21
|
-
|
|
22
|
+
def dependencies
|
|
23
|
+
@dependencies.values.filter_map(&:combined)
|
|
24
|
+
end
|
|
22
25
|
|
|
23
26
|
def <<(dep)
|
|
24
27
|
raise ArgumentError, "must be a Dependency object" unless dep.is_a?(Dependency)
|
|
25
28
|
|
|
26
|
-
|
|
29
|
+
@dependencies[key_for_dependency(dep)] << dep
|
|
30
|
+
self
|
|
31
|
+
end
|
|
27
32
|
|
|
28
|
-
|
|
33
|
+
def +(other)
|
|
34
|
+
raise ArgumentError, "must be a DependencySet" unless other.is_a?(DependencySet)
|
|
29
35
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
dependencies << dep
|
|
36
|
+
other_names = other.dependencies.map(&:name)
|
|
37
|
+
other_names.each do |name|
|
|
38
|
+
all_versions = other.all_versions_for_name(name)
|
|
39
|
+
all_versions.each { |dep| self << dep }
|
|
35
40
|
end
|
|
36
41
|
|
|
37
42
|
self
|
|
38
43
|
end
|
|
39
44
|
|
|
40
|
-
def
|
|
41
|
-
|
|
45
|
+
def all_versions_for_name(name)
|
|
46
|
+
key = key_for_name(name)
|
|
47
|
+
@dependencies.key?(key) ? @dependencies[key].all_versions : []
|
|
48
|
+
end
|
|
42
49
|
|
|
43
|
-
|
|
44
|
-
|
|
50
|
+
def dependency_for_name(name)
|
|
51
|
+
key = key_for_name(name)
|
|
52
|
+
@dependencies.key?(key) ? @dependencies[key].combined : nil
|
|
45
53
|
end
|
|
46
54
|
|
|
47
55
|
private
|
|
@@ -50,41 +58,98 @@ module Dependabot
|
|
|
50
58
|
@case_sensitive
|
|
51
59
|
end
|
|
52
60
|
|
|
53
|
-
def
|
|
54
|
-
|
|
61
|
+
def key_for_name(name)
|
|
62
|
+
case_sensitive? ? name : name.downcase
|
|
63
|
+
end
|
|
55
64
|
|
|
56
|
-
|
|
65
|
+
def key_for_dependency(dep)
|
|
66
|
+
key_for_name(dep.name)
|
|
57
67
|
end
|
|
58
68
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
69
|
+
# There can only be one entry per dependency name in a `DependencySet`. Each entry
|
|
70
|
+
# is assigned a `DependencySlot`.
|
|
71
|
+
#
|
|
72
|
+
# In some ecosystems (like `npm_and_yarn`), however, multiple versions of a
|
|
73
|
+
# dependency may be encountered and added to the set. The `DependencySlot` retains
|
|
74
|
+
# all added versions and presents a single unified dependency for the entry
|
|
75
|
+
# that combines the attributes of these versions.
|
|
76
|
+
#
|
|
77
|
+
# The combined dependency is accessible via `DependencySet#dependencies` or
|
|
78
|
+
# `DependencySet#dependency_for_name`. The list of individual versions of the
|
|
79
|
+
# dependency is accessible via `DependencySet#all_versions_for_name`.
|
|
80
|
+
class DependencySlot
|
|
81
|
+
attr_reader :all_versions, :combined
|
|
82
|
+
|
|
83
|
+
def initialize
|
|
84
|
+
@all_versions = []
|
|
85
|
+
@combined = nil
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def <<(dep)
|
|
89
|
+
return self if @all_versions.include?(dep)
|
|
90
|
+
|
|
91
|
+
@combined = if @combined
|
|
92
|
+
combined_dependency(@combined, dep)
|
|
93
|
+
else
|
|
94
|
+
Dependency.new(
|
|
95
|
+
name: dep.name,
|
|
96
|
+
version: dep.version,
|
|
97
|
+
requirements: dep.requirements,
|
|
98
|
+
package_manager: dep.package_manager,
|
|
99
|
+
subdependency_metadata: dep.subdependency_metadata
|
|
100
|
+
)
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
index_of_same_version =
|
|
104
|
+
@all_versions.find_index { |other| other.version == dep.version }
|
|
105
|
+
|
|
106
|
+
if index_of_same_version.nil?
|
|
107
|
+
@all_versions << dep
|
|
71
108
|
else
|
|
72
|
-
|
|
109
|
+
same_version = @all_versions[index_of_same_version]
|
|
110
|
+
@all_versions[index_of_same_version] = combined_dependency(same_version, dep)
|
|
73
111
|
end
|
|
74
112
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
113
|
+
self
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
private
|
|
117
|
+
|
|
118
|
+
# Produces a new dependency by merging the attributes of `old_dep` with those of
|
|
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.
|
|
121
|
+
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
|
|
133
|
+
requirements = (old_dep.requirements + new_dep.requirements).uniq
|
|
134
|
+
subdependency_metadata = (
|
|
135
|
+
(old_dep.subdependency_metadata || []) +
|
|
136
|
+
(new_dep.subdependency_metadata || [])
|
|
137
|
+
).uniq
|
|
138
|
+
|
|
139
|
+
Dependency.new(
|
|
140
|
+
name: old_dep.name,
|
|
141
|
+
version: version,
|
|
142
|
+
requirements: requirements,
|
|
143
|
+
package_manager: old_dep.package_manager,
|
|
144
|
+
subdependency_metadata: subdependency_metadata
|
|
145
|
+
)
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def version_class
|
|
149
|
+
@version_class ||= Utils.version_class_for_package_manager(@combined.package_manager)
|
|
150
|
+
end
|
|
87
151
|
end
|
|
152
|
+
private_constant :DependencySlot
|
|
88
153
|
end
|
|
89
154
|
end
|
|
90
155
|
end
|
|
@@ -19,7 +19,7 @@ module Dependabot
|
|
|
19
19
|
|
|
|
20
20
|
[0-9]+\.[0-9]+(?:\.[a-z0-9\-]+)*
|
|
21
21
|
)$
|
|
22
|
-
/ix
|
|
22
|
+
/ix
|
|
23
23
|
|
|
24
24
|
def initialize(dependency:, credentials:,
|
|
25
25
|
ignored_versions: [], raise_on_ignored: false,
|
|
@@ -49,8 +49,14 @@ module Dependabot
|
|
|
49
49
|
return true if branch
|
|
50
50
|
return true if dependency.version&.start_with?(ref)
|
|
51
51
|
|
|
52
|
-
#
|
|
53
|
-
|
|
52
|
+
# If the specified `ref` is actually a tag, we're pinned
|
|
53
|
+
return true if local_upload_pack.match?(%r{ refs/tags/#{ref}$})
|
|
54
|
+
|
|
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}$})
|
|
57
|
+
|
|
58
|
+
# Otherwise, assume we're pinned
|
|
59
|
+
true
|
|
54
60
|
end
|
|
55
61
|
|
|
56
62
|
def pinned_ref_looks_like_version?
|
|
@@ -61,6 +67,10 @@ module Dependabot
|
|
|
61
67
|
|
|
62
68
|
def pinned_ref_looks_like_commit_sha?
|
|
63
69
|
ref = dependency_source_details.fetch(:ref)
|
|
70
|
+
ref_looks_like_commit_sha?(ref)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def ref_looks_like_commit_sha?(ref)
|
|
64
74
|
return false unless ref&.match?(/^[0-9a-f]{6,40}$/)
|
|
65
75
|
|
|
66
76
|
return false unless pinned?
|
|
@@ -365,17 +375,19 @@ module Dependabot
|
|
|
365
375
|
def listing_tags
|
|
366
376
|
return [] unless listing_source_url
|
|
367
377
|
|
|
368
|
-
|
|
378
|
+
@listing_tags ||= begin
|
|
379
|
+
tags = listing_repo_git_metadata_fetcher.tags
|
|
369
380
|
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
381
|
+
if dependency_source_details&.fetch(:ref, nil)&.start_with?("tags/")
|
|
382
|
+
tags = tags.map do |tag|
|
|
383
|
+
tag.dup.tap { |t| t.name = "tags/#{tag.name}" }
|
|
384
|
+
end
|
|
373
385
|
end
|
|
374
|
-
end
|
|
375
386
|
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
387
|
+
tags
|
|
388
|
+
rescue GitDependenciesNotReachable
|
|
389
|
+
[]
|
|
390
|
+
end
|
|
379
391
|
end
|
|
380
392
|
|
|
381
393
|
def listing_upload_pack
|