dependabot-bazel 0.344.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 +7 -0
- data/lib/dependabot/bazel/file_fetcher.rb +108 -0
- data/lib/dependabot/bazel/file_parser/starlark_parser.rb +359 -0
- data/lib/dependabot/bazel/file_parser.rb +358 -0
- data/lib/dependabot/bazel/file_updater/bzlmod_file_updater.rb +99 -0
- data/lib/dependabot/bazel/file_updater/declaration_parser.rb +78 -0
- data/lib/dependabot/bazel/file_updater/workspace_file_updater.rb +203 -0
- data/lib/dependabot/bazel/file_updater.rb +116 -0
- data/lib/dependabot/bazel/language.rb +26 -0
- data/lib/dependabot/bazel/metadata_finder.rb +28 -0
- data/lib/dependabot/bazel/package_manager.rb +43 -0
- data/lib/dependabot/bazel/requirement.rb +62 -0
- data/lib/dependabot/bazel/update_checker/registry_client.rb +133 -0
- data/lib/dependabot/bazel/update_checker/requirements_updater.rb +35 -0
- data/lib/dependabot/bazel/update_checker.rb +186 -0
- data/lib/dependabot/bazel/version.rb +24 -0
- data/lib/dependabot/bazel.rb +21 -0
- metadata +283 -0
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "dependabot/bazel/file_updater"
|
|
5
|
+
|
|
6
|
+
module Dependabot
|
|
7
|
+
module Bazel
|
|
8
|
+
class FileUpdater < Dependabot::FileUpdaters::Base
|
|
9
|
+
class WorkspaceFileUpdater
|
|
10
|
+
extend T::Sig
|
|
11
|
+
|
|
12
|
+
sig do
|
|
13
|
+
params(
|
|
14
|
+
dependency_files: T::Array[Dependabot::DependencyFile],
|
|
15
|
+
dependencies: T::Array[Dependabot::Dependency],
|
|
16
|
+
credentials: T::Array[Dependabot::Credential]
|
|
17
|
+
).void
|
|
18
|
+
end
|
|
19
|
+
def initialize(dependency_files:, dependencies:, credentials:)
|
|
20
|
+
@dependency_files = dependency_files
|
|
21
|
+
@dependencies = dependencies
|
|
22
|
+
@credentials = credentials
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
sig { returns(T::Array[Dependabot::DependencyFile]) }
|
|
26
|
+
def updated_workspace_files
|
|
27
|
+
workspace_files.filter_map do |file|
|
|
28
|
+
updated_content = update_file_content(file)
|
|
29
|
+
next if updated_content == T.must(file.content)
|
|
30
|
+
|
|
31
|
+
file.dup.tap { |f| f.content = updated_content }
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
private
|
|
36
|
+
|
|
37
|
+
sig { returns(T::Array[Dependabot::DependencyFile]) }
|
|
38
|
+
attr_reader :dependency_files
|
|
39
|
+
|
|
40
|
+
sig { returns(T::Array[Dependabot::Dependency]) }
|
|
41
|
+
attr_reader :dependencies
|
|
42
|
+
|
|
43
|
+
sig { returns(T::Array[Dependabot::Credential]) }
|
|
44
|
+
attr_reader :credentials
|
|
45
|
+
|
|
46
|
+
sig { returns(T::Array[Dependabot::DependencyFile]) }
|
|
47
|
+
def workspace_files
|
|
48
|
+
@workspace_files ||= T.let(
|
|
49
|
+
dependency_files.select do |f|
|
|
50
|
+
f.name == "WORKSPACE" || f.name.end_with?("WORKSPACE.bazel")
|
|
51
|
+
end,
|
|
52
|
+
T.nilable(T::Array[Dependabot::DependencyFile])
|
|
53
|
+
)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
sig { params(file: Dependabot::DependencyFile).returns(String) }
|
|
57
|
+
def update_file_content(file)
|
|
58
|
+
content = T.must(file.content).dup
|
|
59
|
+
|
|
60
|
+
dependencies.each do |dependency|
|
|
61
|
+
content = update_dependency_in_content(content, dependency)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
content
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
sig { params(content: String, dependency: Dependabot::Dependency).returns(String) }
|
|
68
|
+
def update_dependency_in_content(content, dependency)
|
|
69
|
+
return content unless dependency.package_manager == "bazel"
|
|
70
|
+
|
|
71
|
+
case dependency_type(dependency)
|
|
72
|
+
when :http_archive
|
|
73
|
+
update_http_archive_declaration(content, dependency)
|
|
74
|
+
when :git_repository
|
|
75
|
+
update_git_repository_declaration(content, dependency)
|
|
76
|
+
else
|
|
77
|
+
content
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
sig { params(dependency: Dependabot::Dependency).returns(Symbol) }
|
|
82
|
+
def dependency_type(dependency)
|
|
83
|
+
return :http_archive if dependency.requirements.any? { |req| req.dig(:source, :type) == "http_archive" }
|
|
84
|
+
return :git_repository if dependency.requirements.any? { |req| req.dig(:source, :type) == "git_repository" }
|
|
85
|
+
|
|
86
|
+
:unknown
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
sig { params(content: String, dependency: Dependabot::Dependency).returns(String) }
|
|
90
|
+
def update_http_archive_declaration(content, dependency)
|
|
91
|
+
new_version = dependency.version
|
|
92
|
+
return content unless new_version
|
|
93
|
+
|
|
94
|
+
escaped_name = Regexp.escape(dependency.name)
|
|
95
|
+
|
|
96
|
+
http_archive_pattern = /http_archive\s*\(([^)]+?)\)/mx
|
|
97
|
+
|
|
98
|
+
content.gsub(http_archive_pattern) do |match|
|
|
99
|
+
function_content = T.must(Regexp.last_match(1))
|
|
100
|
+
|
|
101
|
+
if /name\s*=\s*["']#{escaped_name}["']/.match?(function_content)
|
|
102
|
+
updated_function_content = update_http_archive_attributes(function_content, dependency)
|
|
103
|
+
"http_archive(#{updated_function_content})"
|
|
104
|
+
else
|
|
105
|
+
match
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
rescue Dependabot::DependencyFileNotResolvable => e
|
|
109
|
+
raise e
|
|
110
|
+
rescue StandardError => e
|
|
111
|
+
raise Dependabot::DependencyFileNotResolvable,
|
|
112
|
+
"Failed to update http_archive for #{dependency.name}: #{e.message}"
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
sig { params(function_content: String, dependency: Dependabot::Dependency).returns(String) }
|
|
116
|
+
def update_http_archive_attributes(function_content, dependency)
|
|
117
|
+
updated_content = function_content.dup
|
|
118
|
+
|
|
119
|
+
updated_content = update_archive_url(updated_content, dependency) if /url\s*=/.match?(updated_content)
|
|
120
|
+
|
|
121
|
+
updated_content = update_archive_urls_array(updated_content, dependency) if /urls\s*=/.match?(updated_content)
|
|
122
|
+
|
|
123
|
+
updated_content
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
sig { params(content: String, dependency: Dependabot::Dependency).returns(String) }
|
|
127
|
+
def update_archive_url(content, dependency)
|
|
128
|
+
old_version = dependency.previous_version
|
|
129
|
+
new_version = dependency.version
|
|
130
|
+
return content unless old_version && new_version
|
|
131
|
+
|
|
132
|
+
content.gsub(/url\s*=\s*["']([^"']+)["']/) do
|
|
133
|
+
url = T.must(Regexp.last_match(1))
|
|
134
|
+
updated_url = transform_version_in_url(url, old_version, new_version)
|
|
135
|
+
"url = \"#{updated_url}\""
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
sig { params(content: String, dependency: Dependabot::Dependency).returns(String) }
|
|
140
|
+
def update_archive_urls_array(content, dependency)
|
|
141
|
+
old_version = dependency.previous_version
|
|
142
|
+
new_version = dependency.version
|
|
143
|
+
return content unless old_version && new_version
|
|
144
|
+
|
|
145
|
+
content.gsub(/urls\s*=\s*\[(.*?)\]/m) do
|
|
146
|
+
urls_content = T.must(Regexp.last_match(1))
|
|
147
|
+
updated_urls_content = urls_content.gsub(/["']([^"']+)["']/) do
|
|
148
|
+
url = T.must(Regexp.last_match(1))
|
|
149
|
+
updated_url = transform_version_in_url(url, old_version, new_version)
|
|
150
|
+
"\"#{updated_url}\""
|
|
151
|
+
end
|
|
152
|
+
"urls = [#{updated_urls_content}]"
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
sig { params(url: String, old_version: String, new_version: String).returns(String) }
|
|
157
|
+
def transform_version_in_url(url, old_version, new_version)
|
|
158
|
+
return url.gsub(old_version, new_version) if url.include?(old_version)
|
|
159
|
+
|
|
160
|
+
return url.gsub("v#{old_version}", "v#{new_version}") if url.include?("v#{old_version}")
|
|
161
|
+
|
|
162
|
+
old_with_v = "v#{old_version}"
|
|
163
|
+
return url.gsub(old_with_v, "v#{new_version}") if url.include?(old_with_v)
|
|
164
|
+
|
|
165
|
+
url
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
sig { params(content: String, dependency: Dependabot::Dependency).returns(String) }
|
|
169
|
+
def update_git_repository_declaration(content, dependency)
|
|
170
|
+
new_version = dependency.version
|
|
171
|
+
return content unless new_version
|
|
172
|
+
|
|
173
|
+
escaped_name = Regexp.escape(dependency.name)
|
|
174
|
+
|
|
175
|
+
git_repo_pattern = /git_repository\s*\(([^)]+?)\)/mx
|
|
176
|
+
|
|
177
|
+
content.gsub(git_repo_pattern) do |match|
|
|
178
|
+
function_content = T.must(Regexp.last_match(1))
|
|
179
|
+
|
|
180
|
+
if /name\s*=\s*["']#{escaped_name}["']/.match?(function_content)
|
|
181
|
+
updated_function_content = if /tag\s*=/.match?(function_content)
|
|
182
|
+
function_content.gsub(
|
|
183
|
+
/tag\s*=\s*["'][^"']*["']/,
|
|
184
|
+
"tag = \"#{new_version}\""
|
|
185
|
+
)
|
|
186
|
+
elsif /commit\s*=/.match?(function_content)
|
|
187
|
+
function_content.gsub(
|
|
188
|
+
/commit\s*=\s*["'][^"']*["']/,
|
|
189
|
+
"commit = \"#{new_version}\""
|
|
190
|
+
)
|
|
191
|
+
else
|
|
192
|
+
function_content
|
|
193
|
+
end
|
|
194
|
+
"git_repository(#{updated_function_content})"
|
|
195
|
+
else
|
|
196
|
+
match
|
|
197
|
+
end
|
|
198
|
+
end
|
|
199
|
+
end
|
|
200
|
+
end
|
|
201
|
+
end
|
|
202
|
+
end
|
|
203
|
+
end
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "dependabot/file_updaters"
|
|
5
|
+
require "dependabot/file_updaters/base"
|
|
6
|
+
|
|
7
|
+
module Dependabot
|
|
8
|
+
module Bazel
|
|
9
|
+
class FileUpdater < Dependabot::FileUpdaters::Base
|
|
10
|
+
extend T::Sig
|
|
11
|
+
|
|
12
|
+
require_relative "file_updater/bzlmod_file_updater"
|
|
13
|
+
require_relative "file_updater/workspace_file_updater"
|
|
14
|
+
require_relative "file_updater/declaration_parser"
|
|
15
|
+
|
|
16
|
+
sig { returns(T::Array[Regexp]) }
|
|
17
|
+
def self.updated_files_regex
|
|
18
|
+
[
|
|
19
|
+
/^MODULE\.bazel$/,
|
|
20
|
+
%r{^(?:.*/)?MODULE\.bazel$},
|
|
21
|
+
/^WORKSPACE$/,
|
|
22
|
+
%r{^(?:.*/)?WORKSPACE\.bazel$},
|
|
23
|
+
%r{^(?:.*/)?BUILD$},
|
|
24
|
+
%r{^(?:.*/)?BUILD\.bazel$}
|
|
25
|
+
]
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
sig { override.returns(T::Array[Dependabot::DependencyFile]) }
|
|
29
|
+
def updated_dependency_files
|
|
30
|
+
updated_files = T.let([], T::Array[Dependabot::DependencyFile])
|
|
31
|
+
|
|
32
|
+
dependencies.each do |dependency|
|
|
33
|
+
if bzlmod_dependency?(dependency)
|
|
34
|
+
updated_files.concat(update_bzlmod_dependency(dependency))
|
|
35
|
+
elsif workspace_dependency?(dependency)
|
|
36
|
+
updated_files.concat(update_workspace_dependency(dependency))
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
updated_files.uniq
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
private
|
|
44
|
+
|
|
45
|
+
sig { override.void }
|
|
46
|
+
def check_required_files
|
|
47
|
+
return if module_files.any? || workspace_files.any?
|
|
48
|
+
|
|
49
|
+
raise Dependabot::DependencyFileNotFound.new(
|
|
50
|
+
nil,
|
|
51
|
+
"No MODULE.bazel or WORKSPACE file found!"
|
|
52
|
+
)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
sig { returns(T::Array[Dependabot::DependencyFile]) }
|
|
56
|
+
def module_files
|
|
57
|
+
@module_files ||= T.let(
|
|
58
|
+
dependency_files.select { |f| f.name.end_with?("MODULE.bazel") },
|
|
59
|
+
T.nilable(T::Array[Dependabot::DependencyFile])
|
|
60
|
+
)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
sig { returns(T::Array[Dependabot::DependencyFile]) }
|
|
64
|
+
def workspace_files
|
|
65
|
+
@workspace_files ||= T.let(
|
|
66
|
+
dependency_files.select do |f|
|
|
67
|
+
f.name == "WORKSPACE" || f.name.end_with?("WORKSPACE.bazel")
|
|
68
|
+
end,
|
|
69
|
+
T.nilable(T::Array[Dependabot::DependencyFile])
|
|
70
|
+
)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
sig { params(dependency: Dependabot::Dependency).returns(T::Boolean) }
|
|
74
|
+
def bzlmod_dependency?(dependency)
|
|
75
|
+
dependency.requirements.any? { |req| req[:file]&.end_with?("MODULE.bazel") }
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
sig { params(dependency: Dependabot::Dependency).returns(T::Boolean) }
|
|
79
|
+
def workspace_dependency?(dependency)
|
|
80
|
+
dependency.requirements.any? do |req|
|
|
81
|
+
req[:file] == "WORKSPACE" || req[:file]&.end_with?("WORKSPACE.bazel")
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
sig { params(dependency: Dependabot::Dependency).returns(T::Array[Dependabot::DependencyFile]) }
|
|
86
|
+
def update_bzlmod_dependency(dependency)
|
|
87
|
+
bzlmod_updater = BzlmodFileUpdater.new(
|
|
88
|
+
dependency_files: dependency_files,
|
|
89
|
+
dependencies: [dependency],
|
|
90
|
+
credentials: credentials
|
|
91
|
+
)
|
|
92
|
+
bzlmod_updater.updated_module_files
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
sig { params(dependency: Dependabot::Dependency).returns(T::Array[Dependabot::DependencyFile]) }
|
|
96
|
+
def update_workspace_dependency(dependency)
|
|
97
|
+
workspace_updater = WorkspaceFileUpdater.new(
|
|
98
|
+
dependency_files: dependency_files,
|
|
99
|
+
dependencies: [dependency],
|
|
100
|
+
credentials: credentials
|
|
101
|
+
)
|
|
102
|
+
workspace_updater.updated_workspace_files
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
sig { params(file: Dependabot::DependencyFile).returns(T::Array[Dependabot::Dependency]) }
|
|
106
|
+
def relevant_dependencies_for_file(file)
|
|
107
|
+
dependencies.select do |dependency|
|
|
108
|
+
dependency.package_manager == "bazel" &&
|
|
109
|
+
dependency.requirements.any? { |req| req[:file] == file.name }
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
Dependabot::FileUpdaters.register("bazel", Dependabot::Bazel::FileUpdater)
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# typed: strong
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "sorbet-runtime"
|
|
5
|
+
require "dependabot/bazel/version"
|
|
6
|
+
require "dependabot/bazel/requirement"
|
|
7
|
+
require "dependabot/ecosystem"
|
|
8
|
+
|
|
9
|
+
module Dependabot
|
|
10
|
+
module Bazel
|
|
11
|
+
LANGUAGE = "bazel"
|
|
12
|
+
|
|
13
|
+
class Language < Dependabot::Ecosystem::VersionManager
|
|
14
|
+
extend T::Sig
|
|
15
|
+
|
|
16
|
+
sig { params(raw_version: String, requirement: T.nilable(Requirement)).void }
|
|
17
|
+
def initialize(raw_version, requirement = nil)
|
|
18
|
+
super(
|
|
19
|
+
name: LANGUAGE,
|
|
20
|
+
version: Version.new(raw_version),
|
|
21
|
+
requirement: requirement
|
|
22
|
+
)
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# typed: strong
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
# NOTE: This file was scaffolded automatically but is OPTIONAL.
|
|
5
|
+
# If you don't need custom metadata finding logic (changelogs, release notes, etc.),
|
|
6
|
+
# you can safely delete this file and remove the require from lib/dependabot/bazel.rb
|
|
7
|
+
|
|
8
|
+
require "dependabot/metadata_finders"
|
|
9
|
+
require "dependabot/metadata_finders/base"
|
|
10
|
+
|
|
11
|
+
module Dependabot
|
|
12
|
+
module Bazel
|
|
13
|
+
class MetadataFinder < Dependabot::MetadataFinders::Base
|
|
14
|
+
extend T::Sig
|
|
15
|
+
|
|
16
|
+
private
|
|
17
|
+
|
|
18
|
+
sig { override.returns(T.nilable(Dependabot::Source)) }
|
|
19
|
+
def look_up_source
|
|
20
|
+
# TODO: Implement custom source lookup logic if needed
|
|
21
|
+
# Otherwise, delete this file and the require in the main registration file
|
|
22
|
+
nil
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
Dependabot::MetadataFinders.register("bazel", Dependabot::Bazel::MetadataFinder)
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# typed: strong
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "sorbet-runtime"
|
|
5
|
+
require "dependabot/bazel/version"
|
|
6
|
+
require "dependabot/ecosystem"
|
|
7
|
+
require "dependabot/bazel/requirement"
|
|
8
|
+
|
|
9
|
+
module Dependabot
|
|
10
|
+
module Bazel
|
|
11
|
+
ECOSYSTEM = "bazel"
|
|
12
|
+
PACKAGE_MANAGER = "bazel"
|
|
13
|
+
|
|
14
|
+
# Keep versions in ascending order
|
|
15
|
+
SUPPORTED_BAZEL_VERSIONS = T.let([Version.new("6"), Version.new("7")].freeze, T::Array[Dependabot::Version])
|
|
16
|
+
|
|
17
|
+
# Currently, we don't support any deprecated versions of Bazel
|
|
18
|
+
# When a version is going to be unsupported, it will be added here for a while to give users time to upgrade
|
|
19
|
+
DEPRECATED_BAZEL_VERSIONS = T.let([].freeze, T::Array[Dependabot::Version])
|
|
20
|
+
|
|
21
|
+
class PackageManager < Dependabot::Ecosystem::VersionManager
|
|
22
|
+
extend T::Sig
|
|
23
|
+
|
|
24
|
+
sig do
|
|
25
|
+
params(
|
|
26
|
+
detected_version: String,
|
|
27
|
+
raw_version: T.nilable(String),
|
|
28
|
+
requirement: T.nilable(Requirement)
|
|
29
|
+
).void
|
|
30
|
+
end
|
|
31
|
+
def initialize(detected_version:, raw_version: nil, requirement: nil)
|
|
32
|
+
super(
|
|
33
|
+
name: PACKAGE_MANAGER,
|
|
34
|
+
detected_version: Version.new(detected_version),
|
|
35
|
+
version: raw_version ? Version.new(raw_version) : nil,
|
|
36
|
+
deprecated_versions: DEPRECATED_BAZEL_VERSIONS,
|
|
37
|
+
supported_versions: SUPPORTED_BAZEL_VERSIONS,
|
|
38
|
+
requirement: requirement
|
|
39
|
+
)
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "sorbet-runtime"
|
|
5
|
+
require "dependabot/requirement"
|
|
6
|
+
require "dependabot/utils"
|
|
7
|
+
|
|
8
|
+
module Dependabot
|
|
9
|
+
module Bazel
|
|
10
|
+
class Requirement < Dependabot::Requirement
|
|
11
|
+
extend T::Sig
|
|
12
|
+
|
|
13
|
+
# Bazel dependencies typically use exact versions, not version ranges
|
|
14
|
+
# This class exists for consistency with Dependabot patterns but
|
|
15
|
+
# may not be heavily used since Bazel tends to pin exact versions
|
|
16
|
+
|
|
17
|
+
sig { params(requirement_string: String).returns(String) }
|
|
18
|
+
def self.normalize_requirement(requirement_string)
|
|
19
|
+
# Handle exact version specifications (most common in Bazel)
|
|
20
|
+
return requirement_string if requirement_string.match?(/^[<>=~]/)
|
|
21
|
+
|
|
22
|
+
# For bare version strings, treat as exact match
|
|
23
|
+
return "= #{requirement_string}" if requirement_string.match?(/^\d+(\.\d+)*(-[\w\d.]+)?(\+[\w\d.]+)?$/)
|
|
24
|
+
|
|
25
|
+
requirement_string
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# This abstract method must be implemented
|
|
29
|
+
sig do
|
|
30
|
+
override
|
|
31
|
+
.params(requirement_string: T.nilable(String))
|
|
32
|
+
.returns(T::Array[Dependabot::Requirement])
|
|
33
|
+
end
|
|
34
|
+
def self.requirements_array(requirement_string)
|
|
35
|
+
# For Bazel, most requirements are simple exact versions
|
|
36
|
+
return [] if requirement_string.nil? || requirement_string.strip.empty?
|
|
37
|
+
|
|
38
|
+
normalized = normalize_requirement(requirement_string)
|
|
39
|
+
[new(normalized)]
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
sig { override.params(version: Gem::Version).returns(T::Boolean) }
|
|
43
|
+
def satisfied_by?(version)
|
|
44
|
+
# For Bazel versions, delegate to the base class
|
|
45
|
+
# but ensure we're working with proper version objects
|
|
46
|
+
bazel_version = case version
|
|
47
|
+
when Dependabot::Bazel::Version
|
|
48
|
+
version
|
|
49
|
+
else
|
|
50
|
+
Dependabot::Bazel::Version.new(version.to_s)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
super(bazel_version)
|
|
54
|
+
rescue ArgumentError
|
|
55
|
+
false
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
Dependabot::Utils
|
|
62
|
+
.register_requirement_class("bazel", Dependabot::Bazel::Requirement)
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "json"
|
|
5
|
+
require "base64"
|
|
6
|
+
require "sorbet-runtime"
|
|
7
|
+
require "dependabot/shared_helpers"
|
|
8
|
+
require "dependabot/clients/github_with_retries"
|
|
9
|
+
require "dependabot/dependency"
|
|
10
|
+
require "dependabot/errors"
|
|
11
|
+
|
|
12
|
+
module Dependabot
|
|
13
|
+
module Bazel
|
|
14
|
+
class UpdateChecker
|
|
15
|
+
class RegistryClient
|
|
16
|
+
extend T::Sig
|
|
17
|
+
|
|
18
|
+
GITHUB_REPO = T.let("bazelbuild/bazel-central-registry", String)
|
|
19
|
+
RAW_BASE = T.let("https://raw.githubusercontent.com/#{GITHUB_REPO}/main".freeze, String)
|
|
20
|
+
|
|
21
|
+
sig { params(credentials: T::Array[Dependabot::Credential]).void }
|
|
22
|
+
def initialize(credentials:)
|
|
23
|
+
@credentials = credentials
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
sig { params(module_name: String).returns(T::Array[String]) }
|
|
27
|
+
def all_module_versions(module_name)
|
|
28
|
+
contents = T.unsafe(github_client).contents(GITHUB_REPO, path: "modules/#{module_name}")
|
|
29
|
+
return [] unless contents.is_a?(Array)
|
|
30
|
+
|
|
31
|
+
versions = contents.filter_map do |item|
|
|
32
|
+
next unless item[:type] == "dir"
|
|
33
|
+
|
|
34
|
+
item[:name]
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
versions.sort_by { |v| version_sort_key(v) }
|
|
38
|
+
rescue Octokit::NotFound
|
|
39
|
+
Dependabot.logger.info("Module '#{module_name}' not found in registry")
|
|
40
|
+
[]
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
sig { params(module_name: String).returns(T.nilable(String)) }
|
|
44
|
+
def latest_module_version(module_name)
|
|
45
|
+
versions = all_module_versions(module_name)
|
|
46
|
+
return nil if versions.empty?
|
|
47
|
+
|
|
48
|
+
versions.max_by { |v| version_sort_key(v) }
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
sig { params(module_name: String).returns(T.nilable(T::Hash[String, T.untyped])) }
|
|
52
|
+
def get_metadata(module_name)
|
|
53
|
+
versions = all_module_versions(module_name)
|
|
54
|
+
return nil if versions.empty?
|
|
55
|
+
|
|
56
|
+
{
|
|
57
|
+
"name" => module_name,
|
|
58
|
+
"versions" => versions,
|
|
59
|
+
"latest_version" => latest_module_version(module_name)
|
|
60
|
+
}
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
sig { params(module_name: String, version: String).returns(T.nilable(T::Hash[String, T.untyped])) }
|
|
64
|
+
def get_source(module_name, version)
|
|
65
|
+
file_path = "modules/#{module_name}/#{version}/source.json"
|
|
66
|
+
|
|
67
|
+
begin
|
|
68
|
+
content = T.unsafe(github_client).contents(GITHUB_REPO, path: file_path)
|
|
69
|
+
return nil unless content
|
|
70
|
+
|
|
71
|
+
decoded_content = Base64.decode64(content.content)
|
|
72
|
+
JSON.parse(decoded_content)
|
|
73
|
+
rescue StandardError => e
|
|
74
|
+
Dependabot.logger.warn("Failed to get source for #{module_name}@#{version}: #{e.message}")
|
|
75
|
+
nil
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
sig { params(module_name: String, version: String).returns(T.nilable(String)) }
|
|
80
|
+
def get_module_bazel(module_name, version)
|
|
81
|
+
file_path = "modules/#{module_name}/#{version}/MODULE.bazel"
|
|
82
|
+
|
|
83
|
+
begin
|
|
84
|
+
content = T.unsafe(github_client).contents(GITHUB_REPO, path: file_path)
|
|
85
|
+
return nil unless content
|
|
86
|
+
|
|
87
|
+
Base64.decode64(content.content)
|
|
88
|
+
rescue StandardError => e
|
|
89
|
+
Dependabot.logger.warn("Failed to get MODULE.bazel for #{module_name}@#{version}: #{e.message}")
|
|
90
|
+
nil
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
sig { params(module_name: String, version: String).returns(T::Boolean) }
|
|
95
|
+
def module_version_exists?(module_name, version)
|
|
96
|
+
!get_source(module_name, version).nil?
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
sig { params(module_name: String, version: String).returns(T.nilable(Time)) }
|
|
100
|
+
def get_version_release_date(module_name, version)
|
|
101
|
+
file_path = "modules/#{module_name}/#{version}/MODULE.bazel"
|
|
102
|
+
|
|
103
|
+
commits = begin
|
|
104
|
+
T.unsafe(github_client).commits("bazelbuild/bazel-central-registry", path: file_path, per_page: 1)
|
|
105
|
+
rescue StandardError => e
|
|
106
|
+
Dependabot.logger.warn("Failed to get release date for #{module_name} #{version}: #{e.message}")
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
return nil unless commits&.any?
|
|
110
|
+
|
|
111
|
+
commits.first.commit.committer.date
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
private
|
|
115
|
+
|
|
116
|
+
sig { returns(Dependabot::Clients::GithubWithRetries) }
|
|
117
|
+
def github_client
|
|
118
|
+
@github_client ||= T.let(
|
|
119
|
+
Dependabot::Clients::GithubWithRetries.for_github_dot_com(credentials: @credentials),
|
|
120
|
+
T.nilable(Dependabot::Clients::GithubWithRetries)
|
|
121
|
+
)
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
sig { params(version: String).returns(T::Array[Integer]) }
|
|
125
|
+
def version_sort_key(version)
|
|
126
|
+
cleaned = version.gsub(/^v/, "")
|
|
127
|
+
parts = cleaned.split(".")
|
|
128
|
+
parts.map { |part| part.match?(/^\d+$/) ? part.to_i : 0 }
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# typed: strong
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
module Dependabot
|
|
5
|
+
module Bazel
|
|
6
|
+
class UpdateChecker
|
|
7
|
+
class RequirementsUpdater
|
|
8
|
+
extend T::Sig
|
|
9
|
+
|
|
10
|
+
sig { params(requirements: T::Array[T::Hash[Symbol, T.untyped]], latest_version: String).void }
|
|
11
|
+
def initialize(requirements:, latest_version:)
|
|
12
|
+
@requirements = requirements
|
|
13
|
+
@latest_version = latest_version
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
sig { returns(T::Array[T::Hash[Symbol, T.untyped]]) }
|
|
17
|
+
def updated_requirements
|
|
18
|
+
@requirements.map do |requirement|
|
|
19
|
+
updated_requirement = requirement.dup
|
|
20
|
+
updated_requirement[:requirement] = @latest_version
|
|
21
|
+
updated_requirement
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
private
|
|
26
|
+
|
|
27
|
+
sig { returns(T::Array[T::Hash[Symbol, T.untyped]]) }
|
|
28
|
+
attr_reader :requirements
|
|
29
|
+
|
|
30
|
+
sig { returns(String) }
|
|
31
|
+
attr_reader :latest_version
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|