dependabot-bazel 0.349.0 → 0.350.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: ed0dbfa97a75fd1406bca951209c70fd4a6662af02ea56ee4358a4a35775a49a
4
- data.tar.gz: 06572e1e787cd4a06b8a87ca8e9ba64a217ba05a521a7b43734f37f77619a724
3
+ metadata.gz: 89e2aed09f75955bca16702b04e8fdda1daa176158f51b14b83d1d7134071b32
4
+ data.tar.gz: d4e6d299e8d7f66a35ef8dd53dc90b6d072cd7c4b7f26b3d1c5154ecb9434722
5
5
  SHA512:
6
- metadata.gz: 2389b544675a0ea95032e4b7011bb61344c1becd2f238b009d8cc206f8d12c11f32a9707edc05cf4d525738821a0f118b7129f9c72e6777266c832eda6d12aec
7
- data.tar.gz: a1c6fea7689e7d7518bba8f42f6b5a6c57a17446b8f6f8e349338f4822d4442430ccf77d680da7bfd09c60e2d27caa2e9783461bcb048f32ebb7bf5a6ef6b8f0
6
+ metadata.gz: 33a3bae8546312ada4aa162f6b4c8b31141f6d87b5d859bb0b87f7e3357297c9fe4eb2fd52714657aa5f951911fbcf19ebada4fd92a58dcfc5549dff5c6a0120
7
+ data.tar.gz: 90609272d6e1707e079c13d3a2648c9dd85892207193dbb926fa8644fbfcd511deacc60b4c16b57dfd022b7b9445fe09fb5057d9e264d0f0695d686cabc5048d
@@ -0,0 +1,116 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ require "dependabot/bazel/file_fetcher"
5
+ require "sorbet-runtime"
6
+
7
+ module Dependabot
8
+ module Bazel
9
+ class FileFetcher < Dependabot::FileFetchers::Base
10
+ # Fetches .bzl files and their dependencies recursively.
11
+ # Handles load() and Label() statements to build a complete dependency graph.
12
+ class BzlFileFetcher
13
+ extend T::Sig
14
+
15
+ sig do
16
+ params(
17
+ module_file: DependencyFile,
18
+ fetcher: FileFetcher
19
+ ).void
20
+ end
21
+ def initialize(module_file:, fetcher:)
22
+ @module_file = module_file
23
+ @fetcher = fetcher
24
+ @visited_bzl_files = T.let(Set.new, T::Set[String])
25
+ end
26
+
27
+ sig { returns(T::Array[DependencyFile]) }
28
+ def fetch_bzl_files
29
+ content = T.must(@module_file.content)
30
+ bzl_file_paths = extract_bzl_file_paths(content)
31
+ fetch_bzl_files_recursively(bzl_file_paths)
32
+ end
33
+
34
+ private
35
+
36
+ sig { returns(DependencyFile) }
37
+ attr_reader :module_file
38
+
39
+ sig { returns(FileFetcher) }
40
+ attr_reader :fetcher
41
+
42
+ sig { returns(T::Set[String]) }
43
+ attr_reader :visited_bzl_files
44
+
45
+ # Fetches .bzl files recursively, following their load() and Label() dependencies.
46
+ sig { params(paths: T::Array[String]).returns(T::Array[DependencyFile]) }
47
+ def fetch_bzl_files_recursively(paths)
48
+ files = T.let([], T::Array[DependencyFile])
49
+
50
+ paths.each do |path|
51
+ next if visited_bzl_files.include?(path)
52
+
53
+ fetched_file = fetcher.send(:fetch_file_if_present, path)
54
+ next unless fetched_file
55
+
56
+ files << fetched_file
57
+ visited_bzl_files.add(path)
58
+
59
+ if path.end_with?(".bzl")
60
+ bzl_deps = extract_bzl_load_dependencies(fetched_file.content, path)
61
+ files += fetch_bzl_files_recursively(bzl_deps)
62
+ end
63
+ end
64
+
65
+ files
66
+ end
67
+
68
+ # Extracts .bzl file paths from use_extension() and use_repo_rule() calls.
69
+ # Only extracts workspace-relative paths (//...) and filters out external Bazel
70
+ # repositories (@repo//...) since those files don't exist in the current repository.
71
+ sig { params(content: String).returns(T::Array[String]) }
72
+ def extract_bzl_file_paths(content)
73
+ extract_use_extension_paths(content) + extract_use_repo_rule_paths(content)
74
+ end
75
+
76
+ sig { params(content: String).returns(T::Array[String]) }
77
+ def extract_use_extension_paths(content)
78
+ content.scan(%r{use_extension\s*\(\s*"//([^"]+)"}).filter_map do |match|
79
+ path = T.must(match[0]).tr(":", "/").sub(%r{^/}, "")
80
+ path if path.end_with?(".bzl")
81
+ end
82
+ end
83
+
84
+ sig { params(content: String).returns(T::Array[String]) }
85
+ def extract_use_repo_rule_paths(content)
86
+ content.scan(%r{use_repo_rule\s*\(\s*"//([^"]+)"}).filter_map do |match|
87
+ path = T.must(match[0]).tr(":", "/").sub(%r{^/}, "")
88
+ path if path.end_with?(".bzl")
89
+ end
90
+ end
91
+
92
+ # Extracts file dependencies from load() and Label() statements.
93
+ # Only extracts workspace-relative (//...) and file-relative (:...) paths.
94
+ # External Bazel repositories (@repo//...) are excluded since those files
95
+ # exist in different repositories, not the current one being analyzed.
96
+ sig { params(content: String, file_path: String).returns(T::Array[String]) }
97
+ def extract_bzl_load_dependencies(content, file_path)
98
+ paths = []
99
+ file_dir = File.dirname(file_path)
100
+
101
+ content.scan(%r{load\s*\(\s*"(//[^"]+|:[^"]+)"}) do |match|
102
+ path = PathConverter.label_to_path(match[0], context_dir: file_dir)
103
+ paths << path unless path.empty?
104
+ end
105
+
106
+ content.scan(%r{Label\s*\(\s*"(//[^"]+|:[^"]+)"\)}) do |match|
107
+ path = PathConverter.label_to_path(match[0], context_dir: file_dir)
108
+ paths << path unless path.empty?
109
+ end
110
+
111
+ paths
112
+ end
113
+ end
114
+ end
115
+ end
116
+ end
@@ -0,0 +1,79 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ require "dependabot/bazel/file_fetcher"
5
+ require "sorbet-runtime"
6
+
7
+ module Dependabot
8
+ module Bazel
9
+ class FileFetcher < Dependabot::FileFetchers::Base
10
+ # Fetches entire directory trees, typically for local_path_override directories.
11
+ # Includes BUILD files to make directories valid Bazel packages.
12
+ class DirectoryTreeFetcher
13
+ extend T::Sig
14
+
15
+ SKIP_DIRECTORIES = T.let(%w(.git .bazel-* bazel-* node_modules .github).freeze, T::Array[String])
16
+
17
+ sig { params(fetcher: FileFetcher).void }
18
+ def initialize(fetcher:)
19
+ @fetcher = fetcher
20
+ end
21
+
22
+ sig { params(directory: String).returns(T::Array[DependencyFile]) }
23
+ def fetch_directory_tree(directory)
24
+ return [] if should_skip_directory?(directory)
25
+
26
+ files = T.let([], T::Array[DependencyFile])
27
+
28
+ begin
29
+ repo_contents = @fetcher.send(:repo_contents, dir: directory)
30
+
31
+ repo_contents.each do |item|
32
+ path = item.path
33
+ next if path.nil? || should_skip_directory?(path)
34
+
35
+ case item.type
36
+ when "file"
37
+ fetched_file = @fetcher.send(:fetch_file_if_present, path)
38
+ files << fetched_file if fetched_file
39
+ when "dir"
40
+ files += fetch_directory_tree(path)
41
+ end
42
+ end
43
+ rescue Octokit::NotFound, Dependabot::RepoNotFound, Dependabot::DependencyFileNotFound => e
44
+ Dependabot.logger.warn("Skipping inaccessible directory '#{directory}': #{e.message}")
45
+ end
46
+
47
+ files
48
+ end
49
+
50
+ sig { params(directories: T::Set[String]).returns(T::Array[DependencyFile]) }
51
+ def fetch_build_files_for_directories(directories)
52
+ files = T.let([], T::Array[DependencyFile])
53
+ directories.each do |dir|
54
+ build_file = @fetcher.send(:fetch_file_if_present, "#{dir}/BUILD") ||
55
+ @fetcher.send(:fetch_file_if_present, "#{dir}/BUILD.bazel")
56
+ files << build_file if build_file
57
+ end
58
+ files
59
+ end
60
+
61
+ private
62
+
63
+ sig { returns(FileFetcher) }
64
+ attr_reader :fetcher
65
+
66
+ sig { params(dirname: String).returns(T::Boolean) }
67
+ def should_skip_directory?(dirname)
68
+ SKIP_DIRECTORIES.any? do |skip_pattern|
69
+ if skip_pattern.end_with?("*")
70
+ dirname.start_with?(skip_pattern.chomp("*"))
71
+ else
72
+ dirname == skip_pattern || dirname.end_with?("/#{skip_pattern}")
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,58 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ require "dependabot/bazel/file_fetcher"
5
+ require "sorbet-runtime"
6
+
7
+ module Dependabot
8
+ module Bazel
9
+ class FileFetcher < Dependabot::FileFetchers::Base
10
+ # Fetches downloader configuration files referenced in .bazelrc.
11
+ # Parses .bazelrc for --downloader_config flags and fetches those files.
12
+ class DownloaderConfigFetcher
13
+ extend T::Sig
14
+
15
+ sig { params(fetcher: FileFetcher).void }
16
+ def initialize(fetcher:)
17
+ @fetcher = fetcher
18
+ end
19
+
20
+ sig { returns(T::Array[DependencyFile]) }
21
+ def fetch_downloader_config_files
22
+ bazelrc_file = @fetcher.send(:fetch_file_if_present, ".bazelrc")
23
+ return [] unless bazelrc_file
24
+
25
+ config_paths = extract_downloader_config_paths(bazelrc_file)
26
+ files = T.let([], T::Array[DependencyFile])
27
+
28
+ config_paths.each do |path|
29
+ fetched_file = @fetcher.send(:fetch_file_if_present, path)
30
+ files << fetched_file if fetched_file
31
+ rescue Dependabot::DependencyFileNotFound
32
+ Dependabot.logger.warn(
33
+ "Downloader config file '#{path}' referenced in .bazelrc but not found in repository"
34
+ )
35
+ end
36
+
37
+ files
38
+ end
39
+
40
+ private
41
+
42
+ sig { returns(FileFetcher) }
43
+ attr_reader :fetcher
44
+
45
+ sig { params(bazelrc_file: DependencyFile).returns(T::Array[String]) }
46
+ def extract_downloader_config_paths(bazelrc_file)
47
+ content = T.must(bazelrc_file.content)
48
+ extract_downloader_config_flags(content).reject(&:empty?).uniq
49
+ end
50
+
51
+ sig { params(content: String).returns(T::Array[String]) }
52
+ def extract_downloader_config_flags(content)
53
+ content.scan(/--downloader_config[=\s]+(\S+)/).flatten
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,83 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ require "dependabot/bazel/file_fetcher"
5
+ require "dependabot/bazel/file_fetcher/path_converter"
6
+ require "sorbet-runtime"
7
+
8
+ module Dependabot
9
+ module Bazel
10
+ class FileFetcher < Dependabot::FileFetchers::Base
11
+ # Extracts file and directory paths referenced in MODULE.bazel files.
12
+ # Handles attributes like lock_file, requirements_lock, patches, and local_path_override.
13
+ class ModulePathExtractor
14
+ extend T::Sig
15
+
16
+ sig { params(module_file: DependencyFile).void }
17
+ def initialize(module_file:)
18
+ @module_file = module_file
19
+ end
20
+
21
+ sig { returns([T::Array[String], T::Array[String]]) }
22
+ def extract_paths
23
+ content = T.must(@module_file.content)
24
+ file_paths = extract_file_attribute_paths(content)
25
+ directory_paths = extract_directory_paths(content)
26
+ [file_paths.uniq, directory_paths.uniq]
27
+ end
28
+
29
+ private
30
+
31
+ sig { returns(DependencyFile) }
32
+ attr_reader :module_file
33
+
34
+ # Extracts file paths from lock_file, requirements_lock, and patches attributes.
35
+ sig { params(content: String).returns(T::Array[String]) }
36
+ def extract_file_attribute_paths(content)
37
+ (
38
+ extract_lock_file_paths(content) +
39
+ extract_requirements_lock_paths(content) +
40
+ extract_patches_paths(content)
41
+ ).compact
42
+ end
43
+
44
+ sig { params(content: String).returns(T::Array[String]) }
45
+ def extract_lock_file_paths(content)
46
+ content.scan(/lock_file\s*=\s*"([^"]+)"/).map { |match| PathConverter.label_to_path(T.must(match[0])) }
47
+ end
48
+
49
+ sig { params(content: String).returns(T::Array[String]) }
50
+ def extract_requirements_lock_paths(content)
51
+ content.scan(/requirements_lock\s*=\s*"([^"]+)"/).map do |match|
52
+ PathConverter.label_to_path(T.must(match[0]))
53
+ end
54
+ end
55
+
56
+ sig { params(content: String).returns(T::Array[String]) }
57
+ def extract_patches_paths(content)
58
+ patches = []
59
+ content.scan(/patches\s*=\s*\[([^\]]+)\]/) do |match|
60
+ match[0].scan(/"([^"]+)"/) { |file| patches << PathConverter.label_to_path(file[0]) }
61
+ end
62
+ content.scan(/patches\s*=\s*"([^"]+)"/) do |match|
63
+ patches << PathConverter.label_to_path(match[0])
64
+ end
65
+ patches
66
+ end
67
+
68
+ # Extracts directory paths from local_path_override attributes.
69
+ sig { params(content: String).returns(T::Array[String]) }
70
+ def extract_directory_paths(content)
71
+ extract_local_path_override_paths(content)
72
+ .reject { |path| PathConverter.should_filter_path?(path) }
73
+ .map { |path| PathConverter.normalize_path(path) }
74
+ end
75
+
76
+ sig { params(content: String).returns(T::Array[String]) }
77
+ def extract_local_path_override_paths(content)
78
+ content.scan(/local_path_override\s*\([^)]*path\s*=\s*"([^"]+)"[^)]*\)/m).flatten
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,78 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ require "dependabot/bazel/file_fetcher"
5
+ require "sorbet-runtime"
6
+
7
+ module Dependabot
8
+ module Bazel
9
+ class FileFetcher < Dependabot::FileFetchers::Base
10
+ # Converts Bazel label syntax to filesystem paths
11
+ class PathConverter
12
+ extend T::Sig
13
+
14
+ sig { params(label: String, context_dir: T.nilable(String)).returns(String) }
15
+ def self.label_to_path(label, context_dir: nil)
16
+ path = strip_external_repo_prefix(label)
17
+ return resolve_relative_path(path, context_dir) if relative_path?(path)
18
+
19
+ convert_absolute_label_to_path(path)
20
+ end
21
+
22
+ sig { params(path: String).returns(T::Boolean) }
23
+ def self.should_filter_path?(path)
24
+ absolute_path_or_url?(path)
25
+ end
26
+
27
+ sig { params(path: String).returns(String) }
28
+ def self.normalize_path(path)
29
+ remove_leading_dot_slash(path)
30
+ end
31
+
32
+ sig { params(label: String).returns(String) }
33
+ private_class_method def self.strip_external_repo_prefix(label)
34
+ label.sub(%r{^@[^/]+//}, "")
35
+ end
36
+
37
+ sig { params(path: String).returns(T::Boolean) }
38
+ private_class_method def self.relative_path?(path)
39
+ path.start_with?(":")
40
+ end
41
+
42
+ sig { params(path: String, context_dir: T.nilable(String)).returns(String) }
43
+ private_class_method def self.resolve_relative_path(path, context_dir)
44
+ relative_file = path.sub(/^:/, "")
45
+ return relative_file if context_dir.nil? || context_dir == "."
46
+
47
+ "#{context_dir}/#{relative_file}"
48
+ end
49
+
50
+ sig { params(path: String).returns(String) }
51
+ private_class_method def self.convert_absolute_label_to_path(path)
52
+ path_with_slashes = replace_colon_separator_with_slash(path)
53
+ remove_leading_slashes(path_with_slashes)
54
+ end
55
+
56
+ sig { params(path: String).returns(String) }
57
+ private_class_method def self.replace_colon_separator_with_slash(path)
58
+ path.tr(":", "/")
59
+ end
60
+
61
+ sig { params(path: String).returns(String) }
62
+ private_class_method def self.remove_leading_slashes(path)
63
+ path.sub(%r{^/+}, "")
64
+ end
65
+
66
+ sig { params(path: String).returns(T::Boolean) }
67
+ private_class_method def self.absolute_path_or_url?(path)
68
+ path.start_with?("http://", "https://", "/", "@")
69
+ end
70
+
71
+ sig { params(path: String).returns(String) }
72
+ private_class_method def self.remove_leading_dot_slash(path)
73
+ path.sub(%r{^\./}, "")
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
@@ -9,12 +9,17 @@ module Dependabot
9
9
  class FileFetcher < Dependabot::FileFetchers::Base
10
10
  extend T::Sig
11
11
 
12
+ require_relative "file_fetcher/path_converter"
13
+ require_relative "file_fetcher/bzl_file_fetcher"
14
+ require_relative "file_fetcher/module_path_extractor"
15
+ require_relative "file_fetcher/directory_tree_fetcher"
16
+ require_relative "file_fetcher/downloader_config_fetcher"
17
+
12
18
  WORKSPACE_FILES = T.let(%w(WORKSPACE WORKSPACE.bazel).freeze, T::Array[String])
13
19
  MODULE_FILE = T.let("MODULE.bazel", String)
14
20
  CONFIG_FILES = T.let(
15
21
  %w(.bazelrc MODULE.bazel.lock .bazelversion maven_install.json BUILD BUILD.bazel).freeze, T::Array[String]
16
22
  )
17
- SKIP_DIRECTORIES = T.let(%w(.git .bazel-* bazel-* node_modules .github).freeze, T::Array[String])
18
23
 
19
24
  sig { override.returns(String) }
20
25
  def self.required_files_message
@@ -28,21 +33,12 @@ module Dependabot
28
33
 
29
34
  sig { override.returns(T::Array[DependencyFile]) }
30
35
  def fetch_files
31
- unless allow_beta_ecosystems?
32
- raise Dependabot::DependencyFileNotFound.new(
33
- nil,
34
- "Bazel support is currently in beta. To enable it, add `enable_beta_ecosystems: true` to the" \
35
- " top-level of your `dependabot.yml`. See " \
36
- "https://docs.github.com/en/code-security/dependabot/working-with-dependabot" \
37
- "/dependabot-options-reference#enable-beta-ecosystems for details."
38
- )
39
- end
40
-
41
36
  fetched_files = T.let([], T::Array[DependencyFile])
42
37
  fetched_files += workspace_files
43
38
  fetched_files += module_files
44
39
  fetched_files += config_files
45
40
  fetched_files += referenced_files_from_modules
41
+ fetched_files += downloader_config_files
46
42
 
47
43
  return fetched_files if fetched_files.any?
48
44
 
@@ -128,86 +124,72 @@ module Dependabot
128
124
  nil
129
125
  end
130
126
 
131
- sig { params(dirname: String).returns(T::Boolean) }
132
- def should_skip_directory?(dirname)
133
- SKIP_DIRECTORIES.any? { |skip_dir| dirname.start_with?(skip_dir) }
134
- end
135
-
136
- # Fetches files referenced in MODULE.bazel files via lock_file and requirements_lock attributes.
137
- # Also fetches BUILD/BUILD.bazel files from directories containing referenced files, as these
138
- # are required by Bazel to recognize the directories as valid packages.
139
- #
140
- # This method handles Bazel label syntax and converts it to filesystem paths:
141
- # - "@repo//path:file.json" -> "path/file.json"
142
- # - "//path:file.json" -> "path/file.json"
143
- # - "@repo//:file.json" -> "file.json"
144
- #
145
- # @return [Array<DependencyFile>] referenced files and their associated BUILD files
127
+ # Fetches files referenced in MODULE.bazel and their associated BUILD files.
128
+ # Bazel requires BUILD files to recognize directories as valid packages.
146
129
  sig { returns(T::Array[DependencyFile]) }
147
130
  def referenced_files_from_modules
148
131
  files = T.let([], T::Array[DependencyFile])
149
132
  directories_with_files = T.let(Set.new, T::Set[String])
133
+ local_override_directories = T.let(Set.new, T::Set[String])
134
+ tree_fetcher = DirectoryTreeFetcher.new(fetcher: self)
150
135
 
151
136
  module_files.each do |module_file|
152
- referenced_paths = extract_referenced_paths(module_file)
137
+ extractor = ModulePathExtractor.new(module_file: module_file)
138
+ file_paths, directory_paths = extractor.extract_paths
153
139
 
154
- referenced_paths.each do |path|
155
- fetched_file = fetch_file_if_present(path)
156
- next unless fetched_file
140
+ bzl_fetcher = BzlFileFetcher.new(module_file: module_file, fetcher: self)
141
+ bzl_files = bzl_fetcher.fetch_bzl_files
157
142
 
158
- files << fetched_file
159
- # Track directories that contain referenced files so we can fetch their BUILD files.
160
- # Exclude root directory (.) as BUILD files there are already handled by config_files.
161
- dir = File.dirname(path)
143
+ bzl_files.each do |file|
144
+ dir = File.dirname(file.name)
162
145
  directories_with_files.add(dir) unless dir == "."
163
146
  end
164
- end
165
147
 
166
- # Fetch BUILD or BUILD.bazel files for directories that contain referenced files.
167
- # These BUILD files are required for Bazel to recognize directories as valid packages.
168
- directories_with_files.each do |dir|
169
- build_file = fetch_file_if_present("#{dir}/BUILD") || fetch_file_if_present("#{dir}/BUILD.bazel")
170
- files << build_file if build_file
148
+ files += bzl_files
149
+ files += fetch_paths_and_track_directories(file_paths, directories_with_files)
150
+
151
+ directory_paths.each { |dir| local_override_directories.add(dir) unless dir == "." }
171
152
  end
172
153
 
154
+ files += tree_fetcher.fetch_build_files_for_directories(directories_with_files)
155
+ files += fetch_local_override_directory_trees(local_override_directories)
156
+
173
157
  files
174
158
  end
175
159
 
176
- # Extracts file paths from lock_file and requirements_lock attributes in MODULE.bazel content.
177
- # Converts Bazel label syntax to filesystem paths.
178
- #
179
- # Bazel labels can have several formats:
180
- # - "@repo//path:file" - external or self-referential repository with path
181
- # - "//path:file" - main repository reference with path
182
- # - "@repo//:file" - external or self-referential repository root file
183
- # - "//:file" - main repository root file
184
- #
185
- # The method:
186
- # 1. Strips optional @repo prefix
187
- # 2. Converts colon separators to forward slashes (path:file -> path/file)
188
- # 3. Removes leading slashes to create relative paths
189
- #
190
- # @param module_file [DependencyFile] the MODULE.bazel file to parse
191
- # @return [Array<String>] unique relative file paths referenced in the module
192
- sig { params(module_file: DependencyFile).returns(T::Array[String]) }
193
- def extract_referenced_paths(module_file)
194
- content = T.must(module_file.content)
195
- paths = []
196
-
197
- # Match lock_file attributes with optional @repo prefix: "(?:@[^"\/]+)?\/\/([^"]+)"
198
- # Capture group 1: everything after // (e.g., "tools/jol:file.json" or ":file.json")
199
- content.scan(%r{lock_file\s*=\s*"(?:@[^"/]+)?//([^"]+)"}) do |match|
200
- path = match[0].tr(":", "/").sub(%r{^/}, "")
201
- paths << path
202
- end
160
+ # Fetches files and tracks their directories for BUILD file resolution.
161
+ sig do
162
+ params(
163
+ paths: T::Array[String],
164
+ directories: T::Set[String]
165
+ ).returns(T::Array[DependencyFile])
166
+ end
167
+ def fetch_paths_and_track_directories(paths, directories)
168
+ files = T.let([], T::Array[DependencyFile])
169
+ paths.each do |path|
170
+ fetched_file = fetch_file_if_present(path)
171
+ next unless fetched_file
203
172
 
204
- # Match requirements_lock attributes with optional @repo prefix
205
- content.scan(%r{requirements_lock\s*=\s*"(?:@[^"/]+)?//([^"]+)"}) do |match|
206
- path = match[0].tr(":", "/").sub(%r{^/}, "")
207
- paths << path
173
+ files << fetched_file
174
+ dir = File.dirname(path)
175
+ directories.add(dir) unless dir == "."
208
176
  end
177
+ files
178
+ end
209
179
 
210
- paths.uniq
180
+ # Fetches complete directory trees for local module overrides.
181
+ sig { params(directories: T::Set[String]).returns(T::Array[DependencyFile]) }
182
+ def fetch_local_override_directory_trees(directories)
183
+ tree_fetcher = DirectoryTreeFetcher.new(fetcher: self)
184
+ files = T.let([], T::Array[DependencyFile])
185
+ directories.each { |dir| files += tree_fetcher.fetch_directory_tree(dir) }
186
+ files
187
+ end
188
+
189
+ sig { returns(T::Array[DependencyFile]) }
190
+ def downloader_config_files
191
+ config_fetcher = DownloaderConfigFetcher.new(fetcher: self)
192
+ config_fetcher.fetch_downloader_config_files
211
193
  end
212
194
  end
213
195
  end
@@ -169,8 +169,7 @@ module Dependabot
169
169
  name = func_call.arguments["name"]
170
170
  version = func_call.arguments["version"]
171
171
 
172
- # Ensure name and version are strings
173
- next unless name.is_a?(String) && version.is_a?(String)
172
+ next unless name.is_a?(String) && version.is_a?(String) && !name.empty? && !version.empty?
174
173
 
175
174
  dependency_set << Dependabot::Dependency.new(
176
175
  name: name,
@@ -9,13 +9,72 @@ module Dependabot
9
9
  class Version < Dependabot::Version
10
10
  extend T::Sig
11
11
 
12
- # Bazel uses semantic versioning with hyphens for pre-release versions (e.g., "1.7.0-rc4")
13
- # Dependabot::Version normalizes these to dot notation with "pre" prefix (e.g., "1.7.0.pre.rc4")
14
- # We need to preserve the original format for Bazel Central Registry compatibility
12
+ sig { override.params(version: VersionParameter).void }
13
+ def initialize(version)
14
+ @original_version = T.let(version.to_s, String)
15
+ @bcr_suffix = T.let(parse_bcr_suffix(@original_version), T.nilable(Integer))
16
+
17
+ base_version = remove_bcr_suffix(@original_version)
18
+ super(base_version)
19
+
20
+ @original_version = version.to_s
21
+ end
22
+
15
23
  sig { override.returns(String) }
16
24
  def to_s
17
25
  @original_version
18
26
  end
27
+
28
+ sig { returns(T.nilable(Integer)) }
29
+ attr_reader :bcr_suffix
30
+
31
+ sig { override.params(other: T.untyped).returns(T.nilable(Integer)) }
32
+ def <=>(other)
33
+ other_bazel = convert_to_bazel_version(other)
34
+ return nil unless other_bazel
35
+
36
+ base_comparison = super(other_bazel)
37
+ return base_comparison unless base_comparison&.zero?
38
+
39
+ compare_bcr_suffixes(@bcr_suffix, other_bazel.bcr_suffix)
40
+ end
41
+
42
+ private
43
+
44
+ sig { params(version_string: String).returns(T.nilable(Integer)) }
45
+ def parse_bcr_suffix(version_string)
46
+ match = version_string.match(/\.bcr\.(\d+)$/)
47
+ match ? T.must(match[1]).to_i : nil
48
+ end
49
+
50
+ sig { params(version_string: String).returns(String) }
51
+ def remove_bcr_suffix(version_string)
52
+ version_string.sub(/\.bcr\.\d+$/, "")
53
+ end
54
+
55
+ sig { params(other: T.untyped).returns(T.nilable(Dependabot::Bazel::Version)) }
56
+ def convert_to_bazel_version(other)
57
+ case other
58
+ when Dependabot::Bazel::Version
59
+ other
60
+ when Gem::Version
61
+ T.cast(Dependabot::Bazel::Version.new(other.to_s), Dependabot::Bazel::Version)
62
+ when String
63
+ T.cast(Dependabot::Bazel::Version.new(other), Dependabot::Bazel::Version)
64
+ when Dependabot::Version
65
+ T.cast(Dependabot::Bazel::Version.new(other.to_s), Dependabot::Bazel::Version)
66
+ end
67
+ end
68
+
69
+ sig { params(ours: T.nilable(Integer), theirs: T.nilable(Integer)).returns(Integer) }
70
+ def compare_bcr_suffixes(ours, theirs)
71
+ return ours <=> theirs if ours && theirs
72
+
73
+ return 1 if ours
74
+ return -1 if theirs
75
+
76
+ 0
77
+ end
19
78
  end
20
79
  end
21
80
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dependabot-bazel
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.349.0
4
+ version: 0.350.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dependabot
@@ -15,14 +15,14 @@ dependencies:
15
15
  requirements:
16
16
  - - '='
17
17
  - !ruby/object:Gem::Version
18
- version: 0.349.0
18
+ version: 0.350.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.349.0
25
+ version: 0.350.0
26
26
  - !ruby/object:Gem::Dependency
27
27
  name: debug
28
28
  requirement: !ruby/object:Gem::Requirement
@@ -243,6 +243,11 @@ extra_rdoc_files: []
243
243
  files:
244
244
  - lib/dependabot/bazel.rb
245
245
  - lib/dependabot/bazel/file_fetcher.rb
246
+ - lib/dependabot/bazel/file_fetcher/bzl_file_fetcher.rb
247
+ - lib/dependabot/bazel/file_fetcher/directory_tree_fetcher.rb
248
+ - lib/dependabot/bazel/file_fetcher/downloader_config_fetcher.rb
249
+ - lib/dependabot/bazel/file_fetcher/module_path_extractor.rb
250
+ - lib/dependabot/bazel/file_fetcher/path_converter.rb
246
251
  - lib/dependabot/bazel/file_parser.rb
247
252
  - lib/dependabot/bazel/file_parser/starlark_parser.rb
248
253
  - lib/dependabot/bazel/file_updater.rb
@@ -263,7 +268,7 @@ licenses:
263
268
  - MIT
264
269
  metadata:
265
270
  bug_tracker_uri: https://github.com/dependabot/dependabot-core/issues
266
- changelog_uri: https://github.com/dependabot/dependabot-core/releases/tag/v0.349.0
271
+ changelog_uri: https://github.com/dependabot/dependabot-core/releases/tag/v0.350.0
267
272
  rdoc_options: []
268
273
  require_paths:
269
274
  - lib