Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 18 additions & 1 deletion gradle/lib/dependabot/gradle/file_fetcher.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ class FileFetcher < Dependabot::FileFetchers::Base
SUPPORTED_SETTINGS_FILE_NAMES =
%w(settings.gradle settings.gradle.kts).freeze

# For now Gradle only supports libray .toml files in the main gradle folder
SUPPORTED_VERSION_CATALOG_FILE_PATH =
%w(/gradle/libs.versions.toml).freeze

def self.required_files_in?(filenames)
filenames.any? do |filename|
SUPPORTED_BUILD_FILE_NAMES.include?(filename)
Expand All @@ -33,7 +37,7 @@ def fetch_files
end

def all_buildfiles_in_build(root_dir)
files = [buildfile(root_dir), settings_file(root_dir)].compact
files = [buildfile(root_dir), settings_file(root_dir), version_catalog_file(root_dir)].compact
files += subproject_buildfiles(root_dir)
files += dependency_script_plugins(root_dir)
files + included_builds(root_dir).
Expand Down Expand Up @@ -82,6 +86,15 @@ def subproject_buildfiles(root_dir)
end
end

def version_catalog_file(root_dir)
return nil unless root_dir == "."

gradle_toml_file(root_dir)
rescue Dependabot::DependencyFileNotFound
# Catalog file is optional for Gradle
nil
end

# rubocop:disable Metrics/PerceivedComplexity
def dependency_script_plugins(root_dir)
return [] unless buildfile(root_dir)
Expand Down Expand Up @@ -127,6 +140,10 @@ def buildfile(dir)
file
end

def gradle_toml_file(dir)
find_first(dir, SUPPORTED_VERSION_CATALOG_FILE_PATH)
end

def settings_file(dir)
find_first(dir, SUPPORTED_SETTINGS_FILE_NAMES)
end
Expand Down
70 changes: 70 additions & 0 deletions gradle/lib/dependabot/gradle/file_parser.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# frozen_string_literal: true

require "toml-rb"

require "dependabot/dependency"
require "dependabot/file_parsers"
require "dependabot/file_parsers/base"
Expand Down Expand Up @@ -44,6 +46,9 @@ def parse
script_plugin_files.each do |plugin_file|
dependency_set += buildfile_dependencies(plugin_file)
end
version_catalog_file.each do |toml_file|
dependency_set += version_catalog_dependencies(toml_file)
end
dependency_set.dependencies
end

Expand All @@ -62,6 +67,65 @@ def self.find_includes(buildfile, dependency_files)

private

def version_catalog_dependencies(toml_file)
dependency_set = DependencySet.new
parsed_toml_file = parsed_toml_file(toml_file)
dependency_set += version_catalog_library_dependencies(parsed_toml_file, toml_file)
dependency_set += version_catalog_plugin_dependencies(parsed_toml_file, toml_file)
dependency_set
end

def version_catalog_library_dependencies(parsed_toml_file, toml_file)
dependencies_for_declarations(parsed_toml_file["libraries"], toml_file, :details_for_library_dependency)
end

def version_catalog_plugin_dependencies(parsed_toml_file, toml_file)
dependencies_for_declarations(parsed_toml_file["plugins"], toml_file, :details_for_plugin_dependency)
end

def dependencies_for_declarations(declarations, toml_file, details_getter)
dependency_set = DependencySet.new
return dependency_set unless declarations

declarations.each do |_mod, declaration|
group, name, version = send(details_getter, declaration)
Comment thread
deivid-rodriguez marked this conversation as resolved.

# Only support basic version and reference formats for now,
# refrain from updating anything else as it's likely to be a very deliberate choice.
next unless Gradle::Version.correct?(version) || (version.is_a?(Hash) && version.key?("ref"))

version_details = Gradle::Version.correct?(version) ? version : "$" + version["ref"]
details = { group: group, name: name, version: version_details }
dependency = dependency_from(details_hash: details, buildfile: toml_file)
next unless dependency

dependency_set << dependency
end
dependency_set
end

def details_for_library_dependency(declaration)
return declaration.split(":") if declaration.is_a?(String)

if declaration["module"]
[*declaration["module"].split(":"), declaration["version"]]
else
[declaration["group"], declaration["name"], declaration["version"]]
end
end
Comment thread
deivid-rodriguez marked this conversation as resolved.

def details_for_plugin_dependency(declaration)
return ["plugins", *declaration.split(":")] if declaration.is_a?(String)

["plugins", declaration["id"], declaration["version"]]
end

def parsed_toml_file(file)
TomlRB.parse(file.content)
rescue TomlRB::ParseError, TomlRB::ValueOverwriteError
Comment thread
deivid-rodriguez marked this conversation as resolved.
raise Dependabot::DependencyFileNotParseable, file.path
end

def map_value_regex(key)
/(?:^|\s|,|\()#{Regexp.quote(key)}(\s*=|:)\s*['"](?<value>[^'"]+)['"]/
end
Expand Down Expand Up @@ -314,6 +378,12 @@ def buildfiles
end
end

def version_catalog_file
@version_catalog_file ||= dependency_files.select do |f|
f.name.end_with?("libs.versions.toml")
end
end

def script_plugin_files
@script_plugin_files ||=
buildfiles.flat_map do |buildfile|
Expand Down
2 changes: 2 additions & 0 deletions gradle/lib/dependabot/gradle/file_updater.rb
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,8 @@ def original_buildfile_declarations(dependency, requirement)
if dependency.name.include?(":")
next false unless line.include?(dependency.name.split(":").first)
next false unless line.include?(dependency.name.split(":").last)
elsif requirement.fetch(:file).end_with?(".toml")
next false unless line.include?(dependency.name)
else
name_regex_value = /['"]#{Regexp.quote(dependency.name)}['"]/
name_regex = /(id|kotlin)(\s+#{name_regex_value}|\(#{name_regex_value}\))/
Expand Down
71 changes: 71 additions & 0 deletions gradle/spec/dependabot/gradle/file_fetcher_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,17 @@ def stub_content_request(path, fixture)
}]
end

def stub_no_content_request(path)
stub_request(:get, File.join(url, path)).
with(headers: { "Authorization" => "token token" }).
to_return(status: 404)
end

before { allow(file_fetcher_instance).to receive(:commit).and_return("sha") }

context "with a basic buildfile" do
before do
stub_no_content_request("gradle?ref=sha")
stub_content_request("?ref=sha", "contents_java.json")
stub_content_request("build.gradle?ref=sha", "contents_java_basic_buildfile.json")
end
Expand All @@ -52,6 +59,19 @@ def stub_content_request(path, fixture)
to match_array(%w(build.gradle))
end

context "with version catalog" do
before do
stub_content_request("gradle?ref=sha", "content_gradle_toml.json")
stub_content_request("gradle/libs.versions.toml?ref=sha", "libs_versions_toml.json")
end

it "fetches the toml file" do
expect(file_fetcher_instance.files.count).to eq(2)
expect(file_fetcher_instance.files.map(&:name)).
to match_array(%w(build.gradle gradle/libs.versions.toml))
end
end

context "with a settings.gradle" do
before do
stub_content_request("?ref=sha", "contents_java_with_settings.json")
Expand All @@ -78,6 +98,19 @@ def stub_content_request(path, fixture)
to match_array(%w(build.gradle settings.gradle))
end
end

context "whith versions catalog" do
before do
stub_content_request("gradle?ref=sha", "content_gradle_toml.json")
stub_content_request("gradle/libs.versions.toml?ref=sha", "libs_versions_toml.json")
end

it "fetches the main buildfile and subproject buildfile and version catalog" do
expect(file_fetcher_instance.files.count).to eq(4)
expect(file_fetcher_instance.files.map(&:name)).
to match_array(%w(build.gradle settings.gradle app/build.gradle gradle/libs.versions.toml))
end
end
end

context "with included builds" do
Expand All @@ -96,6 +129,18 @@ def stub_content_request(path, fixture)
expect(file_fetcher_instance.files.map(&:name)).
to match_array(%w(build.gradle buildSrc/build.gradle))
end

context "with version catalog" do
before do
stub_content_request("gradle?ref=sha", "content_gradle_toml.json")
stub_content_request("gradle/libs.versions.toml?ref=sha", "libs_versions_toml.json")
end

it "fetches all buildfiles and version catalog" do
expect(file_fetcher_instance.files.map(&:name)).
to match_array(%w(build.gradle buildSrc/build.gradle gradle/libs.versions.toml))
end
end
end

context "explicitly included" do
Expand Down Expand Up @@ -244,6 +289,18 @@ def stub_content_request(path, fixture)
expect(file_fetcher_instance.files.map(&:name)).
to match_array(%w(settings.gradle app/build.gradle))
end

context "with version catalog" do
before do
stub_content_request("gradle?ref=sha", "content_gradle_toml.json")
stub_content_request("gradle/libs.versions.toml?ref=sha", "libs_versions_toml.json")
end

it "fetches the main buildfile, subproject buildfile and version catalog" do
expect(file_fetcher_instance.files.map(&:name)).
to match_array(%w(settings.gradle app/build.gradle gradle/libs.versions.toml))
end
end
end

context "with kotlin" do
Expand Down Expand Up @@ -279,6 +336,7 @@ def stub_content_request(path, fixture)

context "with a script plugin" do
before do
stub_no_content_request("gradle?ref=sha")
stub_content_request("?ref=sha", "contents_java.json")
stub_content_request("build.gradle?ref=sha", "contents_java_buildfile_with_script_plugins.json")
stub_content_request("gradle/dependencies.gradle?ref=sha", "contents_java_simple_settings.json")
Expand All @@ -290,6 +348,18 @@ def stub_content_request(path, fixture)
to match_array(%w(build.gradle gradle/dependencies.gradle))
end

context "with version catalog" do
before do
stub_content_request("gradle?ref=sha", "content_gradle_toml.json")
stub_content_request("gradle/libs.versions.toml?ref=sha", "libs_versions_toml.json")
end

it "fetches the main buildfile, subproject buildfile and version catalog" do
expect(file_fetcher_instance.files.map(&:name)).
to match_array(%w(build.gradle gradle/dependencies.gradle gradle/libs.versions.toml))
end
end

context "that can't be found" do
before do
stub_content_request("?ref=sha", "contents_java.json")
Expand All @@ -311,6 +381,7 @@ def stub_content_request(path, fixture)

context "with no required manifest files" do
before do
stub_no_content_request("gradle?ref=sha")
stub_request(:get, url + "?ref=sha").
with(headers: { "Authorization" => "token token" }).
to_return(
Expand Down
Loading