dependabot-uv 0.349.0 → 0.351.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/helpers/requirements.txt +1 -1
- data/lib/dependabot/uv/authed_url_builder.rb +3 -27
- data/lib/dependabot/uv/file_updater/lock_file_updater.rb +38 -27
- data/lib/dependabot/uv/language.rb +6 -76
- data/lib/dependabot/uv/language_version_manager.rb +6 -131
- data/lib/dependabot/uv/metadata_finder.rb +6 -210
- data/lib/dependabot/uv/name_normaliser.rb +3 -17
- data/lib/dependabot/uv/native_helpers.rb +4 -30
- data/lib/dependabot/uv/package.rb +27 -0
- data/lib/dependabot/uv/requirement.rb +4 -196
- data/lib/dependabot/uv/requirement_parser.rb +5 -53
- data/lib/dependabot/uv/update_checker/latest_version_finder.rb +10 -16
- data/lib/dependabot/uv/version.rb +4 -321
- metadata +19 -8
- data/lib/dependabot/uv/file_parser/setup_file_parser.rb +0 -194
- data/lib/dependabot/uv/package/package_details_fetcher.rb +0 -486
- data/lib/dependabot/uv/package/package_registry_finder.rb +0 -288
- data/lib/dependabot/uv/pipenv_runner.rb +0 -110
|
@@ -1,288 +0,0 @@
|
|
|
1
|
-
# typed: strict
|
|
2
|
-
# frozen_string_literal: true
|
|
3
|
-
|
|
4
|
-
require "toml-rb"
|
|
5
|
-
require "sorbet-runtime"
|
|
6
|
-
require "dependabot/dependency"
|
|
7
|
-
require "dependabot/dependency_file"
|
|
8
|
-
require "dependabot/credential"
|
|
9
|
-
require "dependabot/uv/update_checker"
|
|
10
|
-
require "dependabot/uv/authed_url_builder"
|
|
11
|
-
require "dependabot/errors"
|
|
12
|
-
|
|
13
|
-
module Dependabot
|
|
14
|
-
module Uv
|
|
15
|
-
module Package
|
|
16
|
-
class PackageRegistryFinder
|
|
17
|
-
extend T::Sig
|
|
18
|
-
|
|
19
|
-
PYPI_BASE_URL = "https://pypi.org/simple/"
|
|
20
|
-
ENVIRONMENT_VARIABLE_REGEX = /\$\{.+\}/
|
|
21
|
-
|
|
22
|
-
UrlsHash = T.type_alias { { main: T.nilable(String), extra: T::Array[String] } }
|
|
23
|
-
|
|
24
|
-
sig do
|
|
25
|
-
params(
|
|
26
|
-
dependency_files: T::Array[Dependabot::DependencyFile],
|
|
27
|
-
credentials: T::Array[Dependabot::Credential],
|
|
28
|
-
dependency: Dependabot::Dependency
|
|
29
|
-
).void
|
|
30
|
-
end
|
|
31
|
-
def initialize(dependency_files:, credentials:, dependency:)
|
|
32
|
-
@dependency_files = T.let(dependency_files, T::Array[Dependabot::DependencyFile])
|
|
33
|
-
@credentials = T.let(credentials, T::Array[Dependabot::Credential])
|
|
34
|
-
@dependency = T.let(dependency, Dependabot::Dependency)
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
sig { returns(T::Array[String]) }
|
|
38
|
-
def registry_urls
|
|
39
|
-
extra_index_urls =
|
|
40
|
-
config_variable_index_urls[:extra] +
|
|
41
|
-
pipfile_index_urls[:extra] +
|
|
42
|
-
requirement_file_index_urls[:extra] +
|
|
43
|
-
pip_conf_index_urls[:extra] +
|
|
44
|
-
pyproject_index_urls[:extra]
|
|
45
|
-
|
|
46
|
-
extra_index_urls = extra_index_urls.map do |url|
|
|
47
|
-
clean_check_and_remove_environment_variables(url)
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
# URL encode any `@` characters within registry URL creds.
|
|
51
|
-
# TODO: The test that fails if the `map` here is removed is likely a
|
|
52
|
-
# bug in Ruby's URI parser, and should be fixed there.
|
|
53
|
-
[main_index_url, *extra_index_urls].map do |url|
|
|
54
|
-
url.rpartition("@").tap { |a| a.first.gsub!("@", "%40") }.join
|
|
55
|
-
end.uniq
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
private
|
|
59
|
-
|
|
60
|
-
sig { returns(T::Array[Dependabot::DependencyFile]) }
|
|
61
|
-
attr_reader :dependency_files
|
|
62
|
-
|
|
63
|
-
sig { returns(T::Array[Dependabot::Credential]) }
|
|
64
|
-
attr_reader :credentials
|
|
65
|
-
|
|
66
|
-
sig { returns(String) }
|
|
67
|
-
def main_index_url
|
|
68
|
-
url =
|
|
69
|
-
config_variable_index_urls[:main] ||
|
|
70
|
-
pipfile_index_urls[:main] ||
|
|
71
|
-
requirement_file_index_urls[:main] ||
|
|
72
|
-
pip_conf_index_urls[:main] ||
|
|
73
|
-
pyproject_index_urls[:main] ||
|
|
74
|
-
PYPI_BASE_URL
|
|
75
|
-
|
|
76
|
-
clean_check_and_remove_environment_variables(url)
|
|
77
|
-
end
|
|
78
|
-
|
|
79
|
-
sig { returns(UrlsHash) }
|
|
80
|
-
def requirement_file_index_urls
|
|
81
|
-
urls = T.let({ main: nil, extra: [] }, UrlsHash)
|
|
82
|
-
|
|
83
|
-
requirements_files.each do |file|
|
|
84
|
-
content = file.content
|
|
85
|
-
next unless content
|
|
86
|
-
|
|
87
|
-
if content.match?(/^--index-url\s+['"]?([^\s'"]+)['"]?/)
|
|
88
|
-
match_result = content.match(/^--index-url\s+['"]?([^\s'"]+)['"]?/)
|
|
89
|
-
urls[:main] = match_result&.captures&.first&.strip
|
|
90
|
-
end
|
|
91
|
-
extra_urls = urls[:extra]
|
|
92
|
-
extra_urls +=
|
|
93
|
-
content
|
|
94
|
-
.scan(/^--extra-index-url\s+['"]?([^\s'"]+)['"]?/)
|
|
95
|
-
.flatten
|
|
96
|
-
.map(&:strip)
|
|
97
|
-
urls[:extra] = extra_urls
|
|
98
|
-
end
|
|
99
|
-
|
|
100
|
-
urls
|
|
101
|
-
end
|
|
102
|
-
|
|
103
|
-
sig { returns(UrlsHash) }
|
|
104
|
-
def pip_conf_index_urls
|
|
105
|
-
urls = T.let({ main: nil, extra: [] }, UrlsHash)
|
|
106
|
-
|
|
107
|
-
return urls unless pip_conf
|
|
108
|
-
|
|
109
|
-
pip_conf_file = pip_conf
|
|
110
|
-
return urls unless pip_conf_file
|
|
111
|
-
|
|
112
|
-
content = pip_conf_file.content
|
|
113
|
-
return urls unless content
|
|
114
|
-
|
|
115
|
-
if content.match?(/^index-url\s*=/x)
|
|
116
|
-
match_result = content.match(/^index-url\s*=\s*(.+)/)
|
|
117
|
-
urls[:main] = match_result&.captures&.first
|
|
118
|
-
end
|
|
119
|
-
extra_urls = urls[:extra]
|
|
120
|
-
extra_urls += content.scan(/^extra-index-url\s*=(.+)/).flatten
|
|
121
|
-
urls[:extra] = extra_urls
|
|
122
|
-
|
|
123
|
-
urls
|
|
124
|
-
end
|
|
125
|
-
|
|
126
|
-
sig { returns(UrlsHash) }
|
|
127
|
-
def pipfile_index_urls
|
|
128
|
-
urls = T.let({ main: nil, extra: [] }, UrlsHash)
|
|
129
|
-
begin
|
|
130
|
-
return urls unless pipfile
|
|
131
|
-
|
|
132
|
-
pipfile_file = pipfile
|
|
133
|
-
return urls unless pipfile_file
|
|
134
|
-
|
|
135
|
-
content = pipfile_file.content
|
|
136
|
-
return urls unless content
|
|
137
|
-
|
|
138
|
-
pipfile_object = TomlRB.parse(content)
|
|
139
|
-
|
|
140
|
-
urls[:main] = pipfile_object["source"]&.first&.fetch("url", nil)
|
|
141
|
-
|
|
142
|
-
pipfile_object["source"]&.each do |source|
|
|
143
|
-
urls[:extra] << source.fetch("url") if source["url"]
|
|
144
|
-
end
|
|
145
|
-
urls[:extra] = urls[:extra].uniq
|
|
146
|
-
|
|
147
|
-
urls
|
|
148
|
-
rescue TomlRB::ParseError, TomlRB::ValueOverwriteError
|
|
149
|
-
urls
|
|
150
|
-
end
|
|
151
|
-
end
|
|
152
|
-
|
|
153
|
-
# rubocop:disable Metrics/PerceivedComplexity
|
|
154
|
-
sig { returns(UrlsHash) }
|
|
155
|
-
def pyproject_index_urls
|
|
156
|
-
urls = T.let({ main: nil, extra: [] }, UrlsHash)
|
|
157
|
-
|
|
158
|
-
begin
|
|
159
|
-
return urls unless pyproject
|
|
160
|
-
|
|
161
|
-
pyproject_file = pyproject
|
|
162
|
-
return urls unless pyproject_file
|
|
163
|
-
|
|
164
|
-
pyproject_content = pyproject_file.content
|
|
165
|
-
return urls unless pyproject_content
|
|
166
|
-
|
|
167
|
-
sources =
|
|
168
|
-
TomlRB.parse(pyproject_content).dig("tool", "poetry", "source") ||
|
|
169
|
-
[]
|
|
170
|
-
|
|
171
|
-
sources.each do |source|
|
|
172
|
-
# If source is PyPI, skip it, and let it pick the default URI
|
|
173
|
-
next if source["name"].casecmp?("PyPI")
|
|
174
|
-
|
|
175
|
-
if @dependency.all_sources.include?(source["name"])
|
|
176
|
-
# If dependency has specified this source, use it
|
|
177
|
-
return { main: source["url"], extra: [] }
|
|
178
|
-
elsif source["default"]
|
|
179
|
-
urls[:main] = source["url"]
|
|
180
|
-
elsif source["priority"] != "explicit"
|
|
181
|
-
# if source is not explicit, add it to extra
|
|
182
|
-
urls[:extra] << source["url"]
|
|
183
|
-
end
|
|
184
|
-
end
|
|
185
|
-
urls[:extra] = urls[:extra].uniq
|
|
186
|
-
|
|
187
|
-
urls
|
|
188
|
-
rescue TomlRB::ParseError, TomlRB::ValueOverwriteError
|
|
189
|
-
urls
|
|
190
|
-
end
|
|
191
|
-
end
|
|
192
|
-
# rubocop:enable Metrics/PerceivedComplexity
|
|
193
|
-
|
|
194
|
-
sig { returns(UrlsHash) }
|
|
195
|
-
def config_variable_index_urls
|
|
196
|
-
urls = T.let({ main: nil, extra: [] }, UrlsHash)
|
|
197
|
-
|
|
198
|
-
index_url_creds = credentials
|
|
199
|
-
.select { |cred| cred["type"] == "python_index" }
|
|
200
|
-
|
|
201
|
-
if (main_cred = index_url_creds.find(&:replaces_base?))
|
|
202
|
-
urls[:main] = AuthedUrlBuilder.authed_url(credential: main_cred)
|
|
203
|
-
end
|
|
204
|
-
|
|
205
|
-
urls[:extra] =
|
|
206
|
-
index_url_creds
|
|
207
|
-
.reject(&:replaces_base?)
|
|
208
|
-
.map { |cred| AuthedUrlBuilder.authed_url(credential: cred) }
|
|
209
|
-
|
|
210
|
-
urls
|
|
211
|
-
end
|
|
212
|
-
|
|
213
|
-
sig { params(url: String).returns(String) }
|
|
214
|
-
def clean_check_and_remove_environment_variables(url)
|
|
215
|
-
url = url.strip.sub(%r{/+$}, "") + "/"
|
|
216
|
-
|
|
217
|
-
return authed_base_url(url) unless url.match?(ENVIRONMENT_VARIABLE_REGEX)
|
|
218
|
-
|
|
219
|
-
config_variable_urls =
|
|
220
|
-
[
|
|
221
|
-
config_variable_index_urls[:main],
|
|
222
|
-
*config_variable_index_urls[:extra]
|
|
223
|
-
]
|
|
224
|
-
.compact
|
|
225
|
-
.map { |u| u.strip.gsub(%r{/*$}, "") + "/" }
|
|
226
|
-
|
|
227
|
-
regexp = url
|
|
228
|
-
.sub(%r{(?<=://).+@}, "")
|
|
229
|
-
.sub(%r{https?://}, "")
|
|
230
|
-
.split(ENVIRONMENT_VARIABLE_REGEX)
|
|
231
|
-
.map { |part| Regexp.quote(part) }
|
|
232
|
-
.join(".+")
|
|
233
|
-
authed_url = config_variable_urls.find { |u| u.match?(regexp) }
|
|
234
|
-
return authed_url if authed_url
|
|
235
|
-
|
|
236
|
-
cleaned_url = url.gsub(%r{#{ENVIRONMENT_VARIABLE_REGEX}/?}o, "")
|
|
237
|
-
authed_url = authed_base_url(cleaned_url)
|
|
238
|
-
return authed_url if credential_for(cleaned_url)
|
|
239
|
-
|
|
240
|
-
raise PrivateSourceAuthenticationFailure, url
|
|
241
|
-
end
|
|
242
|
-
|
|
243
|
-
sig { params(base_url: String).returns(String) }
|
|
244
|
-
def authed_base_url(base_url)
|
|
245
|
-
cred = credential_for(base_url)
|
|
246
|
-
return base_url unless cred
|
|
247
|
-
|
|
248
|
-
AuthedUrlBuilder.authed_url(credential: cred).gsub(%r{/*$}, "") + "/"
|
|
249
|
-
end
|
|
250
|
-
|
|
251
|
-
sig { params(url: String).returns(T.nilable(Dependabot::Credential)) }
|
|
252
|
-
def credential_for(url)
|
|
253
|
-
credentials
|
|
254
|
-
.select { |c| c["type"] == "python_index" }
|
|
255
|
-
.find do |c|
|
|
256
|
-
cred_url = c.fetch("index-url").gsub(%r{/*$}, "") + "/"
|
|
257
|
-
cred_url.include?(url)
|
|
258
|
-
end
|
|
259
|
-
end
|
|
260
|
-
|
|
261
|
-
sig { returns(T.nilable(Dependabot::DependencyFile)) }
|
|
262
|
-
def pip_conf
|
|
263
|
-
dependency_files.find { |f| f.name == "pip.conf" }
|
|
264
|
-
end
|
|
265
|
-
|
|
266
|
-
sig { returns(T.nilable(Dependabot::DependencyFile)) }
|
|
267
|
-
def pipfile
|
|
268
|
-
dependency_files.find { |f| f.name == "Pipfile" }
|
|
269
|
-
end
|
|
270
|
-
|
|
271
|
-
sig { returns(T.nilable(Dependabot::DependencyFile)) }
|
|
272
|
-
def pyproject
|
|
273
|
-
dependency_files.find { |f| f.name == "pyproject.toml" }
|
|
274
|
-
end
|
|
275
|
-
|
|
276
|
-
sig { returns(T::Array[Dependabot::DependencyFile]) }
|
|
277
|
-
def requirements_files
|
|
278
|
-
dependency_files.select { |f| f.name.match?(/requirements/x) }
|
|
279
|
-
end
|
|
280
|
-
|
|
281
|
-
sig { returns(T::Array[Dependabot::DependencyFile]) }
|
|
282
|
-
def pip_compile_files
|
|
283
|
-
dependency_files.select { |f| f.name.end_with?(".in") }
|
|
284
|
-
end
|
|
285
|
-
end
|
|
286
|
-
end
|
|
287
|
-
end
|
|
288
|
-
end
|
|
@@ -1,110 +0,0 @@
|
|
|
1
|
-
# typed: strict
|
|
2
|
-
# frozen_string_literal: true
|
|
3
|
-
|
|
4
|
-
require "dependabot/shared_helpers"
|
|
5
|
-
require "dependabot/uv/file_parser"
|
|
6
|
-
require "json"
|
|
7
|
-
require "sorbet-runtime"
|
|
8
|
-
|
|
9
|
-
module Dependabot
|
|
10
|
-
module Uv
|
|
11
|
-
class PipenvRunner
|
|
12
|
-
extend T::Sig
|
|
13
|
-
|
|
14
|
-
sig do
|
|
15
|
-
params(
|
|
16
|
-
dependency: Dependabot::Dependency,
|
|
17
|
-
lockfile: T.nilable(Dependabot::DependencyFile),
|
|
18
|
-
language_version_manager: LanguageVersionManager
|
|
19
|
-
)
|
|
20
|
-
.void
|
|
21
|
-
end
|
|
22
|
-
def initialize(dependency:, lockfile:, language_version_manager:)
|
|
23
|
-
@dependency = dependency
|
|
24
|
-
@lockfile = lockfile
|
|
25
|
-
@language_version_manager = language_version_manager
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
sig { params(constraint: String).returns(String) }
|
|
29
|
-
def run_upgrade(constraint)
|
|
30
|
-
constraint = "" if constraint == "*"
|
|
31
|
-
command = "pyenv exec pipenv upgrade --verbose #{dependency_name}#{constraint}"
|
|
32
|
-
command << " --dev" if lockfile_section == "develop"
|
|
33
|
-
|
|
34
|
-
run(command, fingerprint: "pyenv exec pipenv upgrade --verbose <dependency_name><constraint>")
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
sig { params(constraint: String).returns(T.nilable(String)) }
|
|
38
|
-
def run_upgrade_and_fetch_version(constraint)
|
|
39
|
-
run_upgrade(constraint)
|
|
40
|
-
|
|
41
|
-
updated_lockfile = JSON.parse(File.read("Pipfile.lock"))
|
|
42
|
-
|
|
43
|
-
fetch_version_from_parsed_lockfile(updated_lockfile)
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
sig { params(command: String, fingerprint: T.nilable(String)).returns(String) }
|
|
47
|
-
def run(command, fingerprint: nil)
|
|
48
|
-
run_command(
|
|
49
|
-
"pyenv local #{language_version_manager.python_major_minor}",
|
|
50
|
-
fingerprint: "pyenv local <python_major_minor>"
|
|
51
|
-
)
|
|
52
|
-
|
|
53
|
-
run_command(command, fingerprint: fingerprint)
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
private
|
|
57
|
-
|
|
58
|
-
sig { returns(Dependabot::Dependency) }
|
|
59
|
-
attr_reader :dependency
|
|
60
|
-
|
|
61
|
-
sig { returns(T.nilable(Dependabot::DependencyFile)) }
|
|
62
|
-
attr_reader :lockfile
|
|
63
|
-
|
|
64
|
-
sig { returns(LanguageVersionManager) }
|
|
65
|
-
attr_reader :language_version_manager
|
|
66
|
-
|
|
67
|
-
sig { params(updated_lockfile: T::Hash[String, T.untyped]).returns(T.nilable(String)) }
|
|
68
|
-
def fetch_version_from_parsed_lockfile(updated_lockfile)
|
|
69
|
-
deps = updated_lockfile[lockfile_section] || {}
|
|
70
|
-
|
|
71
|
-
deps.dig(dependency_name, "version")
|
|
72
|
-
&.gsub(/^==/, "")
|
|
73
|
-
end
|
|
74
|
-
|
|
75
|
-
sig { params(command: String, fingerprint: T.nilable(String)).returns(String) }
|
|
76
|
-
def run_command(command, fingerprint: nil)
|
|
77
|
-
SharedHelpers.run_shell_command(command, env: pipenv_env_variables, fingerprint: fingerprint)
|
|
78
|
-
end
|
|
79
|
-
|
|
80
|
-
sig { returns(String) }
|
|
81
|
-
def lockfile_section
|
|
82
|
-
if dependency.requirements.any?
|
|
83
|
-
T.must(dependency.requirements.first)[:groups].first
|
|
84
|
-
else
|
|
85
|
-
Uv::FileParser::DEPENDENCY_GROUP_KEYS.each do |keys|
|
|
86
|
-
section = keys.fetch(:lockfile)
|
|
87
|
-
return section if JSON.parse(T.must(T.must(lockfile).content))[section].keys.any?(dependency_name)
|
|
88
|
-
end
|
|
89
|
-
end
|
|
90
|
-
end
|
|
91
|
-
|
|
92
|
-
sig { returns(String) }
|
|
93
|
-
def dependency_name
|
|
94
|
-
dependency.metadata[:original_name] || dependency.name
|
|
95
|
-
end
|
|
96
|
-
|
|
97
|
-
sig { returns(T::Hash[String, String]) }
|
|
98
|
-
def pipenv_env_variables
|
|
99
|
-
{
|
|
100
|
-
"PIPENV_YES" => "true", # Install new Python ver if needed
|
|
101
|
-
"PIPENV_MAX_RETRIES" => "3", # Retry timeouts
|
|
102
|
-
"PIPENV_NOSPIN" => "1", # Don't pollute logs with spinner
|
|
103
|
-
"PIPENV_TIMEOUT" => "600", # Set install timeout to 10 minutes
|
|
104
|
-
"PIP_DEFAULT_TIMEOUT" => "60", # Set pip timeout to 1 minute
|
|
105
|
-
"COLUMNS" => "250" # Avoid line wrapping
|
|
106
|
-
}
|
|
107
|
-
end
|
|
108
|
-
end
|
|
109
|
-
end
|
|
110
|
-
end
|