dependabot-uv 0.397.0 → 0.398.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2a21c1fc9597ab5ac799394151df83043ad6c6d1b62fb408c0657a9a4e67c6bc
4
- data.tar.gz: 42f770b5019c335bba7129bb87d8a875502b07d2ecab72af74169d59a2327b29
3
+ metadata.gz: f460c106ded2b40fb9c7e91e53baee1a2959a640e47bafaece6b43b0f112b022
4
+ data.tar.gz: 281ce9fee19f90b0b703145445bb05c83d2cbc81eadbd3f3fb8eef3f07d79a0b
5
5
  SHA512:
6
- metadata.gz: aabd1f1f406790c8a4fa4b2fe0418d03993c49b54a86be43e749d352dcfccc5e791587461bfa4a4bfc63c142d789097f40a9b012769ec21705b8b3cb96728fe2
7
- data.tar.gz: abb80225b2a528c131fea6c1119a15bbba6c15e50c3df8990c0497ed2e3775b60d9bd984d4b8715cdc58cbac104eaf2f50673567b7c1cae28f1a1fd0f7f35dfd
6
+ metadata.gz: 836cbb9770876a0f0c0f3c02d599cf0948739d7ad2ea12701f1cb78fcf1051b3f78364db63f46e4b4a12e2080d41a1797912e20803a1cbf201aa8ab857038314
7
+ data.tar.gz: f3144802f8314d9998824fe31164dc4d47cb092eb7a8ed0764e221d455cf60301c37a76033c5cf963e919fe43514e9e33347c4c74eca10575514332583beeb5a
@@ -1,13 +1,13 @@
1
1
  pip==26.2.1
2
2
  pip-tools==7.6.1
3
- flake8==7.3.0
3
+ flake8==7.4.1
4
4
  hashin==1.0.5
5
5
  pipenv==2024.4.1
6
6
  plette==2.2.1
7
- poetry==2.4.3
7
+ poetry==2.5.1
8
8
  # TODO: Replace 3p package `tomli` with 3.11's new stdlib `tomllib` once we drop support for Python 3.10.
9
9
  tomli==2.4.1
10
- uv==0.12.15
10
+ uv==0.12.18
11
11
 
12
12
  # Some dependencies will only install if Cython is present
13
13
  Cython==3.3.0
@@ -1,4 +1,4 @@
1
- # typed: strict
1
+ # typed: strong
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require "sorbet-runtime"
@@ -7,7 +7,7 @@ require "dependabot/dependency"
7
7
  require "dependabot/dependency_graphers"
8
8
  require "dependabot/dependency_graphers/base"
9
9
  require "dependabot/uv/file_parser"
10
- require "toml-rb"
10
+ require "dependabot/uv/lockfile_document"
11
11
 
12
12
  module Dependabot
13
13
  module Uv
@@ -42,19 +42,17 @@ module Dependabot
42
42
  sig { returns(T::Hash[String, T::Array[String]]) }
43
43
  def package_relationships
44
44
  @package_relationships ||= T.let(
45
- package_relationships_from_lockfile(T.must(T.must(uv_lock).content)),
45
+ package_relationships_from_lockfile(T.must(uv_lock)),
46
46
  T.nilable(T::Hash[String, T::Array[String]])
47
47
  )
48
48
  end
49
49
 
50
50
  sig { void }
51
51
  def prepare_from_lockfile!
52
- lockfile = T.must(uv_lock)
53
- parsed = TomlRB.parse(lockfile.content)
54
- packages = T.cast(parsed.fetch("package", []), T::Array[T.untyped])
55
- manifest = parsed.fetch("manifest", {})
52
+ document = LockfileDocument.from_file(T.must(uv_lock))
53
+ packages = document.graph_packages
56
54
 
57
- root_names = root_package_names(packages, manifest)
55
+ root_names = root_package_names(packages, document.workspace_members.to_set)
58
56
  direct_runtime, direct_dev = direct_dependency_names(packages, root_names)
59
57
 
60
58
  @dependencies = packages.filter_map do |pkg|
@@ -73,14 +71,15 @@ module Dependabot
73
71
  @prepared = true
74
72
  end
75
73
 
76
- sig { params(lockfile_content: String).returns(T::Hash[String, T::Array[String]]) }
77
- def package_relationships_from_lockfile(lockfile_content)
78
- lockfile_packages(lockfile_content).each_with_object({}) do |package_data, rels|
79
- parent = lockfile_parent_name(package_data)
80
- next unless parent
74
+ sig { params(lockfile: Dependabot::DependencyFile).returns(T::Hash[String, T::Array[String]]) }
75
+ def package_relationships_from_lockfile(lockfile)
76
+ relationships = T.let({}, T::Hash[String, T::Array[String]])
77
+ LockfileDocument.from_file(lockfile).graph_packages.each_with_object(relationships) do |package, rels|
78
+ name = package.name
79
+ next unless name
81
80
 
82
- rels[parent] ||= []
83
- rels[parent].concat(lockfile_child_names(package_data))
81
+ parent = normalised_dependency_name(name)
82
+ (rels[parent] ||= []).concat(lockfile_child_names(package))
84
83
  end
85
84
  rescue StandardError => e
86
85
  errored_fetching_subdependencies!
@@ -89,48 +88,15 @@ module Dependabot
89
88
  {}
90
89
  end
91
90
 
92
- sig { params(lockfile_content: String).returns(T::Array[T.untyped]) }
93
- def lockfile_packages(lockfile_content)
94
- parsed = TomlRB.parse(lockfile_content)
95
- T.cast(parsed.fetch("package", []), T::Array[T.untyped])
96
- end
97
-
98
- sig { params(package_data: T.untyped).returns(T.nilable(String)) }
99
- def lockfile_parent_name(package_data)
100
- return unless package_data.is_a?(Hash)
101
-
102
- package_name = package_data["name"]
103
- return unless package_name.is_a?(String)
104
-
105
- normalised_dependency_name(package_name)
106
- end
107
-
108
91
  # Mirrors uv's `create_dependencies` (crates/uv-resolver/src/lock/export/cyclonedx_json.rs),
109
92
  # which chains a package's `dependencies`, `optional-dependencies`, and
110
93
  # `dev-dependencies` when building the SBOM dependency graph.
111
- sig { params(package_data: T.untyped).returns(T::Array[String]) }
112
- def lockfile_child_names(package_data)
113
- return [] unless package_data.is_a?(Hash)
114
-
115
- names = T.let([], T::Array[String])
116
- collect_dep_names(package_data["dependencies"], names)
117
- collect_dep_names_from_groups(package_data["optional-dependencies"], names)
118
- collect_dep_names_from_groups(package_data["dev-dependencies"], names)
94
+ sig { params(package: LockfileDocument::GraphPackage).returns(T::Array[String]) }
95
+ def lockfile_child_names(package)
96
+ names = package.dependencies + package.optional_dependencies + package.dev_dependencies
119
97
  names.map { |name| normalised_dependency_name(name) }.uniq
120
98
  end
121
99
 
122
- sig { params(dependency_data: T.untyped).returns(T.nilable(String)) }
123
- def lockfile_dependency_name(dependency_data)
124
- if dependency_data.is_a?(Hash)
125
- name = dependency_data["name"]
126
- return name if name.is_a?(String)
127
- end
128
-
129
- return dependency_data if dependency_data.is_a?(String)
130
-
131
- nil
132
- end
133
-
134
100
  # Identifies the workspace member packages whose `dependencies`,
135
101
  # `optional-dependencies`, and `dev-dependencies` arrays describe the
136
102
  # project's direct deps.
@@ -144,32 +110,17 @@ module Dependabot
144
110
  # Fallback for single-member workspaces (which omit `[manifest] members`):
145
111
  # match packages whose `source` is a local variant — `virtual`, `editable`,
146
112
  # or `directory` — per the `SourceWire` enum in the same file.
147
- sig { params(packages: T::Array[T.untyped], manifest: T.untyped).returns(T::Set[String]) }
148
- def root_package_names(packages, manifest)
149
- declared = declared_workspace_members(manifest)
113
+ sig do
114
+ params(packages: T::Array[LockfileDocument::GraphPackage], declared: T::Set[String]).returns(T::Set[String])
115
+ end
116
+ def root_package_names(packages, declared)
150
117
  return declared unless declared.empty?
151
118
 
152
119
  packages.each_with_object(Set.new) do |pkg, set|
153
- next unless pkg.is_a?(Hash)
154
-
155
- source = pkg["source"]
156
- next unless source.is_a?(Hash)
157
- next unless source.key?("virtual") || source.key?("editable") || source.key?("directory")
120
+ next unless pkg.local_source
158
121
 
159
- name = pkg["name"]
160
- set << name if name.is_a?(String)
161
- end
162
- end
163
-
164
- sig { params(manifest: T.untyped).returns(T::Set[String]) }
165
- def declared_workspace_members(manifest)
166
- return Set.new unless manifest.is_a?(Hash)
167
-
168
- members = manifest["members"]
169
- return Set.new unless members.is_a?(Array)
170
-
171
- members.each_with_object(Set.new) do |name, set|
172
- set << name if name.is_a?(String)
122
+ name = pkg.name
123
+ set << name if name
173
124
  end
174
125
  end
175
126
 
@@ -180,7 +131,7 @@ module Dependabot
180
131
  # semantics because the dependency graph reports what *could* be installed, not what was
181
132
  # selected for a particular sync.
182
133
  sig do
183
- params(packages: T::Array[T.untyped], root_names: T::Set[String])
134
+ params(packages: T::Array[LockfileDocument::GraphPackage], root_names: T::Set[String])
184
135
  .returns([T::Set[String], T::Set[String]])
185
136
  end
186
137
  def direct_dependency_names(packages, root_names)
@@ -188,47 +139,29 @@ module Dependabot
188
139
  dev = T.let(Set.new, T::Set[String])
189
140
 
190
141
  packages.each do |pkg|
191
- next unless pkg.is_a?(Hash) && root_names.include?(pkg["name"])
142
+ name = pkg.name
143
+ next unless name && root_names.include?(name)
192
144
 
193
- collect_dep_names(pkg["dependencies"], runtime)
194
- collect_dep_names_from_groups(pkg["optional-dependencies"], runtime)
195
- collect_dep_names_from_groups(pkg["dev-dependencies"], dev)
145
+ runtime.merge(pkg.dependencies)
146
+ runtime.merge(pkg.optional_dependencies)
147
+ dev.merge(pkg.dev_dependencies)
196
148
  end
197
149
 
198
150
  [runtime, dev]
199
151
  end
200
152
 
201
- sig { params(entries: T.untyped, collection: T.any(T::Set[String], T::Array[String])).void }
202
- def collect_dep_names(entries, collection)
203
- return unless entries.is_a?(Array)
204
-
205
- entries.each do |entry|
206
- name = lockfile_dependency_name(entry)
207
- collection << name if name.is_a?(String)
208
- end
209
- end
210
-
211
- sig { params(groups: T.untyped, collection: T.any(T::Set[String], T::Array[String])).void }
212
- def collect_dep_names_from_groups(groups, collection)
213
- return unless groups.is_a?(Hash)
214
-
215
- groups.each_value { |entries| collect_dep_names(entries, collection) }
216
- end
217
-
218
153
  sig do
219
154
  params(
220
- pkg: T.untyped,
155
+ pkg: LockfileDocument::GraphPackage,
221
156
  root_names: T::Set[String],
222
157
  direct_runtime: T::Set[String],
223
158
  direct_dev: T::Set[String]
224
159
  ).returns(T.nilable(Dependabot::Dependency))
225
160
  end
226
161
  def build_dependency(pkg, root_names, direct_runtime, direct_dev)
227
- return unless pkg.is_a?(Hash)
228
-
229
- name = pkg["name"]
230
- version = pkg["version"]
231
- return unless name.is_a?(String) && version.is_a?(String)
162
+ name = pkg.name
163
+ version = pkg.version
164
+ return unless name && version
232
165
 
233
166
  # Root project packages get requirements: [] (indirect, runtime) to
234
167
  # match the prior FileParser-derived behaviour where uv.lock packages
@@ -0,0 +1,111 @@
1
+ # typed: strong
2
+ # frozen_string_literal: true
3
+
4
+ require "sorbet-runtime"
5
+ require "toml-rb"
6
+
7
+ require "dependabot/dependency_file"
8
+
9
+ module Dependabot
10
+ module Uv
11
+ class LockfileDocument
12
+ extend T::Sig
13
+
14
+ LOCAL_SOURCE_KEYS = %w(virtual editable directory).freeze
15
+
16
+ class GraphPackage < T::ImmutableStruct
17
+ const :name, T.nilable(String)
18
+ const :version, T.nilable(String)
19
+ const :local_source, T::Boolean
20
+ const :dependencies, T::Array[String]
21
+ const :optional_dependencies, T::Array[String]
22
+ const :dev_dependencies, T::Array[String]
23
+ end
24
+
25
+ sig { params(file: Dependabot::DependencyFile).returns(LockfileDocument) }
26
+ def self.from_file(file)
27
+ new(data: T.cast(TomlRB.parse(T.must(file.content)), Object), context: file.path)
28
+ end
29
+
30
+ sig { params(data: Object, context: String).void }
31
+ def initialize(data:, context:)
32
+ @context = context
33
+ @data = T.let(object_hash(data), T::Hash[String, Object])
34
+ end
35
+
36
+ sig { returns(T::Array[String]) }
37
+ def workspace_members
38
+ manifest = @data["manifest"]
39
+ return [] unless manifest.is_a?(Hash)
40
+
41
+ members = T.cast(manifest["members"], Object)
42
+ return [] unless members.is_a?(Array)
43
+
44
+ members.filter_map { |member| string_or_nil(T.cast(member, Object)) }
45
+ end
46
+
47
+ sig { returns(T::Array[GraphPackage]) }
48
+ def graph_packages
49
+ package_entries.filter_map do |entry|
50
+ next unless entry.is_a?(Hash)
51
+
52
+ package = object_hash(entry)
53
+ source = package["source"]
54
+ GraphPackage.new(
55
+ name: string_or_nil(package["name"]),
56
+ version: string_or_nil(package["version"]),
57
+ local_source: source.is_a?(Hash) && LOCAL_SOURCE_KEYS.any? { |key| source.key?(key) },
58
+ dependencies: dependency_names(package["dependencies"]),
59
+ optional_dependencies: grouped_dependency_names(package["optional-dependencies"]),
60
+ dev_dependencies: grouped_dependency_names(package["dev-dependencies"])
61
+ )
62
+ end
63
+ end
64
+
65
+ private
66
+
67
+ sig { returns(T::Array[Object]) }
68
+ def package_entries
69
+ packages = @data.fetch("package", [])
70
+ raise TypeError, "#{@context} package must be an array" unless packages.is_a?(Array)
71
+
72
+ packages.map { |entry| T.cast(entry, Object) }
73
+ end
74
+
75
+ sig { params(value: Object).returns(T::Hash[String, Object]) }
76
+ def object_hash(value)
77
+ raise TypeError, "#{@context} must be an object" unless value.is_a?(Hash)
78
+
79
+ value.to_h do |raw_key, raw_value|
80
+ key = T.cast(raw_key, Object)
81
+ raise TypeError, "#{@context} keys must be strings" unless key.is_a?(String)
82
+
83
+ [key, T.cast(raw_value, Object)]
84
+ end
85
+ end
86
+
87
+ sig { params(value: Object).returns(T.nilable(String)) }
88
+ def string_or_nil(value)
89
+ value if value.is_a?(String)
90
+ end
91
+
92
+ sig { params(entries: Object).returns(T::Array[String]) }
93
+ def dependency_names(entries)
94
+ return [] unless entries.is_a?(Array)
95
+
96
+ entries.filter_map do |raw_entry|
97
+ entry = T.cast(raw_entry, Object)
98
+ name = entry.is_a?(Hash) ? T.cast(entry["name"], Object) : entry
99
+ string_or_nil(name)
100
+ end
101
+ end
102
+
103
+ sig { params(groups: Object).returns(T::Array[String]) }
104
+ def grouped_dependency_names(groups)
105
+ return [] unless groups.is_a?(Hash)
106
+
107
+ groups.values.flat_map { |entries| dependency_names(T.cast(entries, Object)) }
108
+ end
109
+ end
110
+ end
111
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dependabot-uv
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.397.0
4
+ version: 0.398.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dependabot
@@ -15,28 +15,28 @@ dependencies:
15
15
  requirements:
16
16
  - - '='
17
17
  - !ruby/object:Gem::Version
18
- version: 0.397.0
18
+ version: 0.398.0
19
19
  type: :runtime
20
20
  prerelease: false
21
21
  version_requirements: !ruby/object:Gem::Requirement
22
22
  requirements:
23
23
  - - '='
24
24
  - !ruby/object:Gem::Version
25
- version: 0.397.0
25
+ version: 0.398.0
26
26
  - !ruby/object:Gem::Dependency
27
27
  name: dependabot-python
28
28
  requirement: !ruby/object:Gem::Requirement
29
29
  requirements:
30
30
  - - '='
31
31
  - !ruby/object:Gem::Version
32
- version: 0.397.0
32
+ version: 0.398.0
33
33
  type: :runtime
34
34
  prerelease: false
35
35
  version_requirements: !ruby/object:Gem::Requirement
36
36
  requirements:
37
37
  - - '='
38
38
  - !ruby/object:Gem::Version
39
- version: 0.397.0
39
+ version: 0.398.0
40
40
  - !ruby/object:Gem::Dependency
41
41
  name: debug
42
42
  requirement: !ruby/object:Gem::Requirement
@@ -280,6 +280,7 @@ files:
280
280
  - lib/dependabot/uv/file_updater/version_config_parser.rb
281
281
  - lib/dependabot/uv/language.rb
282
282
  - lib/dependabot/uv/language_version_manager.rb
283
+ - lib/dependabot/uv/lockfile_document.rb
283
284
  - lib/dependabot/uv/metadata_finder.rb
284
285
  - lib/dependabot/uv/name_normaliser.rb
285
286
  - lib/dependabot/uv/native_helpers.rb
@@ -301,7 +302,7 @@ licenses:
301
302
  - MIT
302
303
  metadata:
303
304
  bug_tracker_uri: https://github.com/dependabot/dependabot-core/issues
304
- changelog_uri: https://github.com/dependabot/dependabot-core/releases/tag/v0.397.0
305
+ changelog_uri: https://github.com/dependabot/dependabot-core/releases/tag/v0.398.0
305
306
  rdoc_options: []
306
307
  require_paths:
307
308
  - lib