dependabot-common 0.237.0 → 0.239.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/dependabot/clients/codecommit.rb +1 -0
- data/lib/dependabot/config/file.rb +3 -3
- data/lib/dependabot/dependency.rb +137 -27
- data/lib/dependabot/dependency_file.rb +1 -1
- data/lib/dependabot/errors.rb +252 -7
- data/lib/dependabot/experiments.rb +9 -2
- data/lib/dependabot/file_updaters/base.rb +1 -1
- data/lib/dependabot/git_commit_checker.rb +6 -0
- data/lib/dependabot/git_metadata_fetcher.rb +58 -20
- data/lib/dependabot/git_ref.rb +71 -0
- data/lib/dependabot/metadata_finders.rb +9 -2
- data/lib/dependabot/pull_request_creator/branch_namer/base.rb +38 -5
- data/lib/dependabot/pull_request_creator/branch_namer/dependency_group_strategy.rb +10 -5
- data/lib/dependabot/pull_request_creator/branch_namer/solo_strategy.rb +15 -15
- data/lib/dependabot/pull_request_creator/branch_namer.rb +6 -3
- data/lib/dependabot/pull_request_creator/github.rb +1 -1
- data/lib/dependabot/pull_request_creator/message_builder/metadata_presenter.rb +1 -2
- data/lib/dependabot/pull_request_creator/message_builder.rb +55 -5
- data/lib/dependabot/pull_request_creator.rb +2 -1
- data/lib/dependabot/requirement.rb +20 -0
- data/lib/dependabot/utils.rb +5 -3
- data/lib/dependabot/workspace/base.rb +1 -1
- data/lib/dependabot.rb +1 -1
- metadata +21 -5
data/lib/dependabot/errors.rb
CHANGED
@@ -1,10 +1,224 @@
|
|
1
|
-
# typed:
|
1
|
+
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
require "sorbet-runtime"
|
5
5
|
require "dependabot/utils"
|
6
6
|
|
7
7
|
module Dependabot
|
8
|
+
extend T::Sig
|
9
|
+
|
10
|
+
# rubocop:disable Metrics/MethodLength
|
11
|
+
sig { params(error: StandardError).returns(T.nilable(T::Hash[Symbol, T.untyped])) }
|
12
|
+
def self.fetcher_error_details(error)
|
13
|
+
case error
|
14
|
+
when Dependabot::ToolVersionNotSupported
|
15
|
+
{
|
16
|
+
"error-type": "tool_version_not_supported",
|
17
|
+
"error-detail": {
|
18
|
+
"tool-name": error.tool_name,
|
19
|
+
"detected-version": error.detected_version,
|
20
|
+
"supported-versions": error.supported_versions
|
21
|
+
}
|
22
|
+
}
|
23
|
+
when Dependabot::BranchNotFound
|
24
|
+
{
|
25
|
+
"error-type": "branch_not_found",
|
26
|
+
"error-detail": { "branch-name": error.branch_name }
|
27
|
+
}
|
28
|
+
when Dependabot::DirectoryNotFound
|
29
|
+
{
|
30
|
+
"error-type": "directory_not_found",
|
31
|
+
"error-detail": { "directory-name": error.directory_name }
|
32
|
+
}
|
33
|
+
when Dependabot::RepoNotFound
|
34
|
+
# This happens if the repo gets removed after a job gets kicked off.
|
35
|
+
# This also happens when a configured personal access token is not authz'd to fetch files from the job repo.
|
36
|
+
{
|
37
|
+
"error-type": "job_repo_not_found",
|
38
|
+
"error-detail": { message: error.message }
|
39
|
+
}
|
40
|
+
when Dependabot::DependencyFileNotParseable
|
41
|
+
{
|
42
|
+
"error-type": "dependency_file_not_parseable",
|
43
|
+
"error-detail": {
|
44
|
+
message: error.message,
|
45
|
+
"file-path": error.file_path
|
46
|
+
}
|
47
|
+
}
|
48
|
+
when Dependabot::DependencyFileNotFound
|
49
|
+
{
|
50
|
+
"error-type": "dependency_file_not_found",
|
51
|
+
"error-detail": { "file-path": error.file_path }
|
52
|
+
}
|
53
|
+
when Dependabot::OutOfDisk
|
54
|
+
{
|
55
|
+
"error-type": "out_of_disk",
|
56
|
+
"error-detail": {}
|
57
|
+
}
|
58
|
+
when Dependabot::PathDependenciesNotReachable
|
59
|
+
{
|
60
|
+
"error-type": "path_dependencies_not_reachable",
|
61
|
+
"error-detail": { dependencies: error.dependencies }
|
62
|
+
}
|
63
|
+
when Octokit::Unauthorized
|
64
|
+
{ "error-type": "octokit_unauthorized" }
|
65
|
+
when Octokit::ServerError
|
66
|
+
# If we get a 500 from GitHub there's very little we can do about it,
|
67
|
+
# and responsibility for fixing it is on them, not us. As a result we
|
68
|
+
# quietly log these as errors
|
69
|
+
{ "error-type": "server_error" }
|
70
|
+
when *Octokit::RATE_LIMITED_ERRORS
|
71
|
+
# If we get a rate-limited error we let dependabot-api handle the
|
72
|
+
# retry by re-enqueing the update job after the reset
|
73
|
+
{
|
74
|
+
"error-type": "octokit_rate_limited",
|
75
|
+
"error-detail": {
|
76
|
+
"rate-limit-reset": T.cast(error, Octokit::Error).response_headers["X-RateLimit-Reset"]
|
77
|
+
}
|
78
|
+
}
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
sig { params(error: StandardError).returns(T.nilable(T::Hash[Symbol, T.untyped])) }
|
83
|
+
def self.parser_error_details(error)
|
84
|
+
case error
|
85
|
+
when Dependabot::DependencyFileNotEvaluatable
|
86
|
+
{
|
87
|
+
"error-type": "dependency_file_not_evaluatable",
|
88
|
+
"error-detail": { message: error.message }
|
89
|
+
}
|
90
|
+
when Dependabot::DependencyFileNotResolvable
|
91
|
+
{
|
92
|
+
"error-type": "dependency_file_not_resolvable",
|
93
|
+
"error-detail": { message: error.message }
|
94
|
+
}
|
95
|
+
when Dependabot::BranchNotFound
|
96
|
+
{
|
97
|
+
"error-type": "branch_not_found",
|
98
|
+
"error-detail": { "branch-name": error.branch_name }
|
99
|
+
}
|
100
|
+
when Dependabot::DependencyFileNotParseable
|
101
|
+
{
|
102
|
+
"error-type": "dependency_file_not_parseable",
|
103
|
+
"error-detail": {
|
104
|
+
message: error.message,
|
105
|
+
"file-path": error.file_path
|
106
|
+
}
|
107
|
+
}
|
108
|
+
when Dependabot::DependencyFileNotFound
|
109
|
+
{
|
110
|
+
"error-type": "dependency_file_not_found",
|
111
|
+
"error-detail": { "file-path": error.file_path }
|
112
|
+
}
|
113
|
+
when Dependabot::PathDependenciesNotReachable
|
114
|
+
{
|
115
|
+
"error-type": "path_dependencies_not_reachable",
|
116
|
+
"error-detail": { dependencies: error.dependencies }
|
117
|
+
}
|
118
|
+
when Dependabot::PrivateSourceAuthenticationFailure
|
119
|
+
{
|
120
|
+
"error-type": "private_source_authentication_failure",
|
121
|
+
"error-detail": { source: error.source }
|
122
|
+
}
|
123
|
+
when Dependabot::GitDependenciesNotReachable
|
124
|
+
{
|
125
|
+
"error-type": "git_dependencies_not_reachable",
|
126
|
+
"error-detail": { "dependency-urls": error.dependency_urls }
|
127
|
+
}
|
128
|
+
when Dependabot::NotImplemented
|
129
|
+
{
|
130
|
+
"error-type": "not_implemented",
|
131
|
+
"error-detail": {
|
132
|
+
message: error.message
|
133
|
+
}
|
134
|
+
}
|
135
|
+
when Octokit::ServerError
|
136
|
+
# If we get a 500 from GitHub there's very little we can do about it,
|
137
|
+
# and responsibility for fixing it is on them, not us. As a result we
|
138
|
+
# quietly log these as errors
|
139
|
+
{ "error-type": "server_error" }
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
sig { params(error: StandardError).returns(T.nilable(T::Hash[Symbol, T.untyped])) }
|
144
|
+
def self.updater_error_details(error)
|
145
|
+
case error
|
146
|
+
when Dependabot::DependencyFileNotResolvable
|
147
|
+
{
|
148
|
+
"error-type": "dependency_file_not_resolvable",
|
149
|
+
"error-detail": { message: error.message }
|
150
|
+
}
|
151
|
+
when Dependabot::DependencyFileNotEvaluatable
|
152
|
+
{
|
153
|
+
"error-type": "dependency_file_not_evaluatable",
|
154
|
+
"error-detail": { message: error.message }
|
155
|
+
}
|
156
|
+
when Dependabot::GitDependenciesNotReachable
|
157
|
+
{
|
158
|
+
"error-type": "git_dependencies_not_reachable",
|
159
|
+
"error-detail": { "dependency-urls": error.dependency_urls }
|
160
|
+
}
|
161
|
+
when Dependabot::MisconfiguredTooling
|
162
|
+
{
|
163
|
+
"error-type": "misconfigured_tooling",
|
164
|
+
"error-detail": { "tool-name": error.tool_name, message: error.tool_message }
|
165
|
+
}
|
166
|
+
when Dependabot::GitDependencyReferenceNotFound
|
167
|
+
{
|
168
|
+
"error-type": "git_dependency_reference_not_found",
|
169
|
+
"error-detail": { dependency: error.dependency }
|
170
|
+
}
|
171
|
+
when Dependabot::PrivateSourceAuthenticationFailure
|
172
|
+
{
|
173
|
+
"error-type": "private_source_authentication_failure",
|
174
|
+
"error-detail": { source: error.source }
|
175
|
+
}
|
176
|
+
when Dependabot::PrivateSourceTimedOut
|
177
|
+
{
|
178
|
+
"error-type": "private_source_timed_out",
|
179
|
+
"error-detail": { source: error.source }
|
180
|
+
}
|
181
|
+
when Dependabot::PrivateSourceCertificateFailure
|
182
|
+
{
|
183
|
+
"error-type": "private_source_certificate_failure",
|
184
|
+
"error-detail": { source: error.source }
|
185
|
+
}
|
186
|
+
when Dependabot::MissingEnvironmentVariable
|
187
|
+
{
|
188
|
+
"error-type": "missing_environment_variable",
|
189
|
+
"error-detail": {
|
190
|
+
"environment-variable": error.environment_variable
|
191
|
+
}
|
192
|
+
}
|
193
|
+
when Dependabot::GoModulePathMismatch
|
194
|
+
{
|
195
|
+
"error-type": "go_module_path_mismatch",
|
196
|
+
"error-detail": {
|
197
|
+
"declared-path": error.declared_path,
|
198
|
+
"discovered-path": error.discovered_path,
|
199
|
+
"go-mod": error.go_mod
|
200
|
+
}
|
201
|
+
}
|
202
|
+
when Dependabot::NotImplemented
|
203
|
+
{
|
204
|
+
"error-type": "not_implemented",
|
205
|
+
"error-detail": {
|
206
|
+
message: error.message
|
207
|
+
}
|
208
|
+
}
|
209
|
+
when *Octokit::RATE_LIMITED_ERRORS
|
210
|
+
# If we get a rate-limited error we let dependabot-api handle the
|
211
|
+
# retry by re-enqueing the update job after the reset
|
212
|
+
{
|
213
|
+
"error-type": "octokit_rate_limited",
|
214
|
+
"error-detail": {
|
215
|
+
"rate-limit-reset": T.cast(error, Octokit::Error).response_headers["X-RateLimit-Reset"]
|
216
|
+
}
|
217
|
+
}
|
218
|
+
end
|
219
|
+
end
|
220
|
+
# rubocop:enable Metrics/MethodLength
|
221
|
+
|
8
222
|
class DependabotError < StandardError
|
9
223
|
extend T::Sig
|
10
224
|
|
@@ -109,6 +323,31 @@ module Dependabot
|
|
109
323
|
# File level errors #
|
110
324
|
#####################
|
111
325
|
|
326
|
+
class MisconfiguredTooling < DependabotError
|
327
|
+
extend T::Sig
|
328
|
+
|
329
|
+
sig { returns(String) }
|
330
|
+
attr_reader :tool_name
|
331
|
+
|
332
|
+
sig { returns(String) }
|
333
|
+
attr_reader :tool_message
|
334
|
+
|
335
|
+
sig do
|
336
|
+
params(
|
337
|
+
tool_name: String,
|
338
|
+
tool_message: String
|
339
|
+
).void
|
340
|
+
end
|
341
|
+
def initialize(tool_name, tool_message)
|
342
|
+
@tool_name = tool_name
|
343
|
+
@tool_message = tool_message
|
344
|
+
|
345
|
+
msg = "Dependabot detected that #{tool_name} is misconfigured in this repository. " \
|
346
|
+
"Running `#{tool_name.downcase}` results in the following error: #{tool_message}"
|
347
|
+
super(msg)
|
348
|
+
end
|
349
|
+
end
|
350
|
+
|
112
351
|
class ToolVersionNotSupported < DependabotError
|
113
352
|
extend T::Sig
|
114
353
|
|
@@ -142,23 +381,28 @@ module Dependabot
|
|
142
381
|
class DependencyFileNotFound < DependabotError
|
143
382
|
extend T::Sig
|
144
383
|
|
145
|
-
sig { returns(String) }
|
384
|
+
sig { returns(T.nilable(String)) }
|
146
385
|
attr_reader :file_path
|
147
386
|
|
387
|
+
sig { params(file_path: T.nilable(String), msg: T.nilable(String)).void }
|
148
388
|
def initialize(file_path, msg = nil)
|
149
389
|
@file_path = file_path
|
150
390
|
super(msg || "#{file_path} not found")
|
151
391
|
end
|
152
392
|
|
153
|
-
sig { returns(String) }
|
393
|
+
sig { returns(T.nilable(String)) }
|
154
394
|
def file_name
|
155
|
-
|
395
|
+
return unless file_path
|
396
|
+
|
397
|
+
T.must(file_path).split("/").last
|
156
398
|
end
|
157
399
|
|
158
|
-
sig { returns(String) }
|
400
|
+
sig { returns(T.nilable(String)) }
|
159
401
|
def directory
|
160
402
|
# Directory should always start with a `/`
|
161
|
-
|
403
|
+
return unless file_path
|
404
|
+
|
405
|
+
T.must(T.must(file_path).split("/")[0..-2]).join("/").sub(%r{^/*}, "/")
|
162
406
|
end
|
163
407
|
end
|
164
408
|
|
@@ -200,8 +444,9 @@ module Dependabot
|
|
200
444
|
sig { returns(String) }
|
201
445
|
attr_reader :source
|
202
446
|
|
447
|
+
sig { params(source: T.nilable(String)).void }
|
203
448
|
def initialize(source)
|
204
|
-
@source = T.let(sanitize_source(source), String)
|
449
|
+
@source = T.let(sanitize_source(T.must(source)), String)
|
205
450
|
msg = "The following source could not be reached as it requires " \
|
206
451
|
"authentication (and any provided details were invalid or lacked " \
|
207
452
|
"the required permissions): #{@source}"
|
@@ -1,18 +1,25 @@
|
|
1
|
-
# typed:
|
1
|
+
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
+
require "sorbet-runtime"
|
5
|
+
|
4
6
|
module Dependabot
|
5
7
|
module Experiments
|
6
|
-
|
8
|
+
extend T::Sig
|
9
|
+
|
10
|
+
@experiments = T.let({}, T::Hash[T.any(String, Symbol), T.untyped])
|
7
11
|
|
12
|
+
sig { returns(T::Hash[T.any(String, Symbol), T.untyped]) }
|
8
13
|
def self.reset!
|
9
14
|
@experiments = {}
|
10
15
|
end
|
11
16
|
|
17
|
+
sig { params(name: T.any(String, Symbol), value: T.untyped).void }
|
12
18
|
def self.register(name, value)
|
13
19
|
@experiments[name.to_sym] = value
|
14
20
|
end
|
15
21
|
|
22
|
+
sig { params(name: T.any(String, Symbol)).returns(T::Boolean) }
|
16
23
|
def self.enabled?(name)
|
17
24
|
!!@experiments[name.to_sym]
|
18
25
|
end
|
@@ -73,7 +73,7 @@ module Dependabot
|
|
73
73
|
|
74
74
|
sig { params(file: Dependabot::DependencyFile, dependency: Dependabot::Dependency).returns(T::Boolean) }
|
75
75
|
def requirement_changed?(file, dependency)
|
76
|
-
changed_requirements = dependency.requirements - dependency.previous_requirements
|
76
|
+
changed_requirements = dependency.requirements - T.must(dependency.previous_requirements)
|
77
77
|
|
78
78
|
changed_requirements.any? { |f| f[:file] == file.name }
|
79
79
|
end
|
@@ -153,6 +153,12 @@ module Dependabot
|
|
153
153
|
@local_tag_for_pinned_sha = most_specific_version_tag_for_sha(ref) if pinned_ref_looks_like_commit_sha?
|
154
154
|
end
|
155
155
|
|
156
|
+
def version_for_pinned_sha
|
157
|
+
return unless local_tag_for_pinned_sha && version_class.correct?(local_tag_for_pinned_sha)
|
158
|
+
|
159
|
+
version_class.new(local_tag_for_pinned_sha)
|
160
|
+
end
|
161
|
+
|
156
162
|
def git_repo_reachable?
|
157
163
|
local_upload_pack
|
158
164
|
true
|
@@ -1,49 +1,73 @@
|
|
1
|
-
# typed:
|
1
|
+
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
require "excon"
|
5
5
|
require "open3"
|
6
|
+
require "sorbet-runtime"
|
7
|
+
|
6
8
|
require "dependabot/errors"
|
9
|
+
require "dependabot/git_ref"
|
7
10
|
|
8
11
|
module Dependabot
|
9
12
|
class GitMetadataFetcher
|
13
|
+
extend T::Sig
|
14
|
+
|
10
15
|
KNOWN_HOSTS = /github\.com|bitbucket\.org|gitlab.com/i
|
11
16
|
|
17
|
+
sig do
|
18
|
+
params(
|
19
|
+
url: String,
|
20
|
+
credentials: T::Array[T::Hash[String, String]]
|
21
|
+
)
|
22
|
+
.void
|
23
|
+
end
|
12
24
|
def initialize(url:, credentials:)
|
13
25
|
@url = url
|
14
26
|
@credentials = credentials
|
15
27
|
end
|
16
28
|
|
29
|
+
sig { returns(T.nilable(String)) }
|
17
30
|
def upload_pack
|
18
|
-
@upload_pack ||= fetch_upload_pack_for(url)
|
31
|
+
@upload_pack ||= T.let(fetch_upload_pack_for(url), T.nilable(String))
|
19
32
|
rescue Octokit::ClientError
|
20
33
|
raise Dependabot::GitDependenciesNotReachable, [url]
|
21
34
|
end
|
22
35
|
|
36
|
+
sig { returns(T::Array[GitRef]) }
|
23
37
|
def tags
|
24
38
|
return [] unless upload_pack
|
25
39
|
|
26
|
-
@tags ||=
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
40
|
+
@tags ||= T.let(
|
41
|
+
tags_for_upload_pack.map do |ref|
|
42
|
+
GitRef.new(
|
43
|
+
name: ref.name,
|
44
|
+
tag_sha: ref.ref_sha,
|
45
|
+
commit_sha: ref.commit_sha
|
46
|
+
)
|
47
|
+
end,
|
48
|
+
T.nilable(T::Array[GitRef])
|
49
|
+
)
|
33
50
|
end
|
34
51
|
|
52
|
+
sig { returns(T::Array[GitRef]) }
|
35
53
|
def tags_for_upload_pack
|
36
|
-
@tags_for_upload_pack ||=
|
54
|
+
@tags_for_upload_pack ||= T.let(
|
55
|
+
refs_for_upload_pack.select { |ref| ref.ref_type == RefType::Tag },
|
56
|
+
T.nilable(T::Array[GitRef])
|
57
|
+
)
|
37
58
|
end
|
38
59
|
|
60
|
+
sig { returns(T::Array[GitRef]) }
|
39
61
|
def refs_for_upload_pack
|
40
|
-
@refs_for_upload_pack ||= parse_refs_for_upload_pack
|
62
|
+
@refs_for_upload_pack ||= T.let(parse_refs_for_upload_pack, T.nilable(T::Array[GitRef]))
|
41
63
|
end
|
42
64
|
|
65
|
+
sig { returns(T::Array[String]) }
|
43
66
|
def ref_names
|
44
67
|
refs_for_upload_pack.map(&:name)
|
45
68
|
end
|
46
69
|
|
70
|
+
sig { params(ref: String).returns(T.nilable(String)) }
|
47
71
|
def head_commit_for_ref(ref)
|
48
72
|
if ref == "HEAD"
|
49
73
|
# Remove the opening clause of the upload pack as this isn't always
|
@@ -51,8 +75,8 @@ module Dependabot
|
|
51
75
|
# causes problems for our `sha_for_update_pack_line` logic. The format
|
52
76
|
# of this opening clause is documented at
|
53
77
|
# https://git-scm.com/docs/http-protocol#_smart_server_response
|
54
|
-
line = upload_pack.gsub(/^[0-9a-f]{4}# service=git-upload-pack/, "")
|
55
|
-
|
78
|
+
line = T.must(upload_pack).gsub(/^[0-9a-f]{4}# service=git-upload-pack/, "")
|
79
|
+
.lines.find { |l| l.include?(" HEAD") }
|
56
80
|
return sha_for_update_pack_line(line) if line
|
57
81
|
end
|
58
82
|
|
@@ -61,6 +85,7 @@ module Dependabot
|
|
61
85
|
&.commit_sha
|
62
86
|
end
|
63
87
|
|
88
|
+
sig { params(ref: String).returns(T.nilable(String)) }
|
64
89
|
def head_commit_for_ref_sha(ref)
|
65
90
|
refs_for_upload_pack
|
66
91
|
.find { |r| r.ref_sha == ref }
|
@@ -69,8 +94,13 @@ module Dependabot
|
|
69
94
|
|
70
95
|
private
|
71
96
|
|
72
|
-
|
97
|
+
sig { returns(String) }
|
98
|
+
attr_reader :url
|
99
|
+
|
100
|
+
sig { returns(T::Array[T::Hash[String, String]]) }
|
101
|
+
attr_reader :credentials
|
73
102
|
|
103
|
+
sig { params(uri: String).returns(String) }
|
74
104
|
def fetch_upload_pack_for(uri)
|
75
105
|
response = fetch_raw_upload_pack_for(uri)
|
76
106
|
return response.body if response.status == 200
|
@@ -97,6 +127,7 @@ module Dependabot
|
|
97
127
|
raise Dependabot::GitDependenciesNotReachable, [uri]
|
98
128
|
end
|
99
129
|
|
130
|
+
sig { params(uri: String).returns(Excon::Response) }
|
100
131
|
def fetch_raw_upload_pack_for(uri)
|
101
132
|
url = service_pack_uri(uri)
|
102
133
|
url = url.rpartition("@").tap { |a| a.first.gsub!("@", "%40") }.join
|
@@ -107,6 +138,7 @@ module Dependabot
|
|
107
138
|
)
|
108
139
|
end
|
109
140
|
|
141
|
+
sig { params(uri: String).returns(T.untyped) }
|
110
142
|
def fetch_raw_upload_pack_with_git_for(uri)
|
111
143
|
service_pack_uri = uri
|
112
144
|
service_pack_uri += ".git" unless service_pack_uri.end_with?(".git") || skip_git_suffix(uri)
|
@@ -129,11 +161,12 @@ module Dependabot
|
|
129
161
|
end
|
130
162
|
end
|
131
163
|
|
164
|
+
sig { returns(T::Array[GitRef]) }
|
132
165
|
def parse_refs_for_upload_pack
|
133
166
|
peeled_lines = []
|
134
167
|
|
135
|
-
result = upload_pack.lines.each_with_object({}) do |line, res|
|
136
|
-
full_ref_name = line.split.last
|
168
|
+
result = T.must(upload_pack).lines.each_with_object({}) do |line, res|
|
169
|
+
full_ref_name = T.must(line.split.last)
|
137
170
|
next unless full_ref_name.start_with?("refs/tags", "refs/heads")
|
138
171
|
|
139
172
|
(peeled_lines << line) && next if line.strip.end_with?("^{}")
|
@@ -141,10 +174,10 @@ module Dependabot
|
|
141
174
|
ref_name = full_ref_name.sub(%r{^refs/(tags|heads)/}, "").strip
|
142
175
|
sha = sha_for_update_pack_line(line)
|
143
176
|
|
144
|
-
res[ref_name] =
|
177
|
+
res[ref_name] = GitRef.new(
|
145
178
|
name: ref_name,
|
146
179
|
ref_sha: sha,
|
147
|
-
ref_type: full_ref_name.start_with?("refs/tags") ?
|
180
|
+
ref_type: full_ref_name.start_with?("refs/tags") ? RefType::Tag : RefType::Head,
|
148
181
|
commit_sha: sha
|
149
182
|
)
|
150
183
|
end
|
@@ -162,6 +195,7 @@ module Dependabot
|
|
162
195
|
result.values
|
163
196
|
end
|
164
197
|
|
198
|
+
sig { params(uri: String).returns(String) }
|
165
199
|
def service_pack_uri(uri)
|
166
200
|
service_pack_uri = uri_with_auth(uri)
|
167
201
|
service_pack_uri = service_pack_uri.gsub(%r{/$}, "")
|
@@ -169,6 +203,7 @@ module Dependabot
|
|
169
203
|
service_pack_uri + "/info/refs?service=git-upload-pack"
|
170
204
|
end
|
171
205
|
|
206
|
+
sig { params(uri: String).returns(T::Boolean) }
|
172
207
|
def skip_git_suffix(uri)
|
173
208
|
# TODO: Unlike the other providers (GitHub, GitLab, BitBucket), as of 2023-01-18 Azure DevOps does not support the
|
174
209
|
# ".git" suffix. It will return a 404.
|
@@ -188,6 +223,7 @@ module Dependabot
|
|
188
223
|
|
189
224
|
# Add in username and password if present in credentials.
|
190
225
|
# Credentials are never present for production Dependabot.
|
226
|
+
sig { params(uri: String).returns(String) }
|
191
227
|
def uri_with_auth(uri)
|
192
228
|
uri = SharedHelpers.scp_to_standard(uri)
|
193
229
|
uri = URI(uri)
|
@@ -196,7 +232,7 @@ module Dependabot
|
|
196
232
|
|
197
233
|
uri.scheme = "https" if uri.scheme != "http"
|
198
234
|
|
199
|
-
if !uri.password && cred
|
235
|
+
if !uri.password && cred && cred.fetch("username", nil) && cred.fetch("password", nil)
|
200
236
|
# URI doesn't have authentication details, but we have credentials
|
201
237
|
uri.user = URI.encode_www_form_component(cred["username"])
|
202
238
|
uri.password = URI.encode_www_form_component(cred["password"])
|
@@ -205,10 +241,12 @@ module Dependabot
|
|
205
241
|
uri.to_s
|
206
242
|
end
|
207
243
|
|
244
|
+
sig { params(line: String).returns(String) }
|
208
245
|
def sha_for_update_pack_line(line)
|
209
|
-
line.split.first.chars.last(40).join
|
246
|
+
T.must(line.split.first).chars.last(40).join
|
210
247
|
end
|
211
248
|
|
249
|
+
sig { returns(T::Hash[Symbol, T.untyped]) }
|
212
250
|
def excon_defaults
|
213
251
|
# Some git hosts are slow when returning a large number of tags
|
214
252
|
SharedHelpers.excon_defaults(read_timeout: 20)
|
@@ -0,0 +1,71 @@
|
|
1
|
+
# typed: strong
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "sorbet-runtime"
|
5
|
+
|
6
|
+
module Dependabot
|
7
|
+
class GitRef
|
8
|
+
extend T::Sig
|
9
|
+
|
10
|
+
sig { returns(String) }
|
11
|
+
attr_accessor :name
|
12
|
+
|
13
|
+
sig { returns(String) }
|
14
|
+
attr_accessor :commit_sha
|
15
|
+
|
16
|
+
sig { returns(T.nilable(String)) }
|
17
|
+
attr_reader :tag_sha
|
18
|
+
|
19
|
+
sig { returns(T.nilable(String)) }
|
20
|
+
attr_reader :ref_sha
|
21
|
+
|
22
|
+
sig { returns(T.nilable(RefType)) }
|
23
|
+
attr_reader :ref_type
|
24
|
+
|
25
|
+
sig do
|
26
|
+
params(
|
27
|
+
name: String,
|
28
|
+
commit_sha: String,
|
29
|
+
tag_sha: T.nilable(String),
|
30
|
+
ref_sha: T.nilable(String),
|
31
|
+
ref_type: T.nilable(RefType)
|
32
|
+
)
|
33
|
+
.void
|
34
|
+
end
|
35
|
+
def initialize(name:, commit_sha:, tag_sha: nil, ref_sha: nil, ref_type: nil)
|
36
|
+
@name = name
|
37
|
+
@commit_sha = commit_sha
|
38
|
+
@ref_sha = ref_sha
|
39
|
+
@tag_sha = tag_sha
|
40
|
+
@ref_type = ref_type
|
41
|
+
end
|
42
|
+
|
43
|
+
sig { params(other: BasicObject).returns(T::Boolean) }
|
44
|
+
def ==(other)
|
45
|
+
case other
|
46
|
+
when GitRef
|
47
|
+
to_h == other.to_h
|
48
|
+
else
|
49
|
+
false
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
sig { returns(T::Hash[Symbol, T.nilable(String)]) }
|
54
|
+
def to_h
|
55
|
+
{
|
56
|
+
name: name,
|
57
|
+
commit_sha: commit_sha,
|
58
|
+
tag_sha: tag_sha,
|
59
|
+
ref_sha: ref_sha,
|
60
|
+
ref_type: ref_type
|
61
|
+
}.compact
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
class RefType < T::Enum
|
66
|
+
enums do
|
67
|
+
Tag = new
|
68
|
+
Head = new
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -1,10 +1,16 @@
|
|
1
|
-
# typed:
|
1
|
+
# typed: strong
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
+
require "sorbet-runtime"
|
5
|
+
require "dependabot/metadata_finders/base"
|
6
|
+
|
4
7
|
module Dependabot
|
5
8
|
module MetadataFinders
|
6
|
-
|
9
|
+
extend T::Sig
|
10
|
+
|
11
|
+
@metadata_finders = T.let({}, T::Hash[String, T.class_of(Dependabot::MetadataFinders::Base)])
|
7
12
|
|
13
|
+
sig { params(package_manager: String).returns(T.class_of(Dependabot::MetadataFinders::Base)) }
|
8
14
|
def self.for_package_manager(package_manager)
|
9
15
|
metadata_finder = @metadata_finders[package_manager]
|
10
16
|
return metadata_finder if metadata_finder
|
@@ -12,6 +18,7 @@ module Dependabot
|
|
12
18
|
raise "Unsupported package_manager #{package_manager}"
|
13
19
|
end
|
14
20
|
|
21
|
+
sig { params(package_manager: String, metadata_finder: T.class_of(Dependabot::MetadataFinders::Base)).void }
|
15
22
|
def self.register(package_manager, metadata_finder)
|
16
23
|
@metadata_finders[package_manager] = metadata_finder
|
17
24
|
end
|