Skip to content

Commit

Permalink
Added support for .tool-versions file in Gemfile for Ruby projects
Browse files Browse the repository at this point in the history
  • Loading branch information
kpumuk committed Feb 16, 2024
1 parent dfe50fe commit b115833
Show file tree
Hide file tree
Showing 21 changed files with 644 additions and 3 deletions.
11 changes: 8 additions & 3 deletions bundler/lib/dependabot/bundler/file_fetcher.rb
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ def fetch_files
fetched_files += child_gemfiles
fetched_files += gemspecs
fetched_files << ruby_version_file if ruby_version_file
fetched_files << tool_versions_file if tool_versions_file
fetched_files += path_gemspecs
fetched_files += require_relative_files(fetched_files)

Expand Down Expand Up @@ -99,9 +100,13 @@ def gemspec_directories
def ruby_version_file
return unless gemfile

@ruby_version_file ||=
fetch_file_if_present(".ruby-version")
&.tap { |f| f.support_file = true }
@ruby_version_file ||= fetch_support_file(".ruby-version")
end

def tool_versions_file
return unless gemfile

@tool_versions_file ||= fetch_support_file(".tool-versions")
end

def path_gemspecs
Expand Down
1 change: 1 addition & 0 deletions bundler/lib/dependabot/bundler/file_parser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,7 @@ def evaled_gemfiles
.reject { |f| f.name.end_with?(".specification") }
.reject { |f| f.name.end_with?(".lock") }
.reject { |f| f.name.end_with?(".ruby-version") }
.reject { |f| f.name.end_with?(".tool-versions") }
.reject { |f| f.name == "Gemfile" }
.reject { |f| f.name == "gems.rb" }
.reject { |f| f.name == "gems.locked" }
Expand Down
6 changes: 6 additions & 0 deletions bundler/lib/dependabot/bundler/file_parser/file_preparer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ def prepared_dependency_files
*evaled_gemfiles,
lockfile,
ruby_version_file,
tool_versions_file,
*imported_ruby_files,
*specification_files
].compact
Expand All @@ -50,6 +51,7 @@ def evaled_gemfiles
.reject { |f| f.name.end_with?(".specification") }
.reject { |f| f.name.end_with?(".lock") }
.reject { |f| f.name.end_with?(".ruby-version") }
.reject { |f| f.name.end_with?(".tool-versions") }
.reject { |f| f.name == "Gemfile" }
.reject { |f| f.name == "gems.rb" }
.reject { |f| f.name == "gems.locked" }
Expand All @@ -72,6 +74,10 @@ def ruby_version_file
dependency_files.find { |f| f.name == ".ruby-version" }
end

def tool_versions_file
dependency_files.find { |f| f.name == ".tool-versions" }
end

def imported_ruby_files
dependency_files
.select { |f| f.name.end_with?(".rb") }
Expand Down
1 change: 1 addition & 0 deletions bundler/lib/dependabot/bundler/file_updater.rb
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ def evaled_gemfiles
.reject { |f| f.name.end_with?(".specification") }
.reject { |f| f.name.end_with?(".lock") }
.reject { |f| f.name.end_with?(".ruby-version") }
.reject { |f| f.name.end_with?(".tool-versions") }
.reject { |f| f.name == "Gemfile" }
.reject { |f| f.name == "gems.rb" }
.reject { |f| f.name == "gems.locked" }
Expand Down
14 changes: 14 additions & 0 deletions bundler/lib/dependabot/bundler/file_updater/lockfile_updater.rb
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ def write_temporary_dependency_files

write_gemspecs(top_level_gemspecs)
write_ruby_version_file
write_tool_versions_file
write_gemspecs(path_gemspecs)
write_specification_files
write_imported_ruby_files
Expand All @@ -111,6 +112,14 @@ def write_ruby_version_file
File.write(path, ruby_version_file.content)
end

def write_tool_versions_file
return unless tool_versions_file

path = tool_versions_file.name
FileUtils.mkdir_p(Pathname.new(path).dirname)
File.write(path, tool_versions_file.content)
end

def write_gemspecs(files)
files.each do |file|
path = file.name
Expand Down Expand Up @@ -156,6 +165,10 @@ def ruby_version_file
dependency_files.find { |f| f.name == ".ruby-version" }
end

def tool_versions_file
dependency_files.find { |f| f.name == ".tool-versions" }
end

def post_process_lockfile(lockfile_body)
lockfile_body = reorder_git_dependencies(lockfile_body)
replace_lockfile_ending(lockfile_body)
Expand Down Expand Up @@ -266,6 +279,7 @@ def evaled_gemfiles
.reject { |f| f.name.end_with?(".specification") }
.reject { |f| f.name.end_with?(".lock") }
.reject { |f| f.name.end_with?(".ruby-version") }
.reject { |f| f.name.end_with?(".tool-versions") }
.reject { |f| f.name == "Gemfile" }
.reject { |f| f.name == "gems.rb" }
.reject { |f| f.name == "gems.locked" }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ def prepared_dependency_files
files += [
lockfile,
ruby_version_file,
tool_versions_file,
*imported_ruby_files,
*specification_files
].compact
Expand Down Expand Up @@ -128,6 +129,7 @@ def evaled_gemfiles
.reject { |f| f.name.end_with?(".specification") }
.reject { |f| f.name.end_with?(".lock") }
.reject { |f| f.name.end_with?(".ruby-version") }
.reject { |f| f.name.end_with?(".tool-versions") }
.reject { |f| f.name == "Gemfile" }
.reject { |f| f.name == "gems.rb" }
.reject { |f| f.name == "gems.locked" }
Expand All @@ -151,6 +153,10 @@ def ruby_version_file
dependency_files.find { |f| f.name == ".ruby-version" }
end

def tool_versions_file
dependency_files.find { |f| f.name == ".tool-versions" }
end

def path_gemspecs
all = dependency_files.select { |f| f.name.end_with?(".gemspec") }
all - top_level_gemspecs
Expand Down
42 changes: 42 additions & 0 deletions bundler/spec/dependabot/bundler/file_fetcher_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,14 @@
body: fixture("github", "ruby_version_content.json"),
headers: { "content-type" => "application/json" }
)

stub_request(:get, File.join(url, ".tool-versions?ref=sha"))
.with(headers: { "Authorization" => "token token" })
.to_return(
status: 200,
body: fixture("github", "tool_versions_content.json"),
headers: { "content-type" => "application/json" }
)
end

context "with a directory" do
Expand Down Expand Up @@ -162,6 +170,40 @@
end
end

context "with a .tool-versions file" do
before do
stub_request(:get, url + "?ref=sha")
.with(headers: { "Authorization" => "token token" })
.to_return(
status: 200,
body: fixture("github", "contents_ruby_tool_versions.json"),
headers: { "content-type" => "application/json" }
)

stub_request(:get, url + "Gemfile?ref=sha")
.with(headers: { "Authorization" => "token token" })
.to_return(
status: 200,
body: fixture("github", "gemfile_with_ruby_tool_versions_content.json"),
headers: { "content-type" => "application/json" }
)

stub_request(:get, url + "Gemfile.lock?ref=sha")
.with(headers: { "Authorization" => "token token" })
.to_return(
status: 200,
body: fixture("github", "gemfile_lock_content.json"),
headers: { "content-type" => "application/json" }
)
end

it "fetches the tool-versions file" do
expect(file_fetcher_instance.files.count).to eq(3)
expect(file_fetcher_instance.files.map(&:name))
.to include(".tool-versions")
end
end

context "with a gems.rb rather than a Gemfile" do
before do
stub_request(:get, url + "?ref=sha")
Expand Down
10 changes: 10 additions & 0 deletions bundler/spec/dependabot/bundler/file_parser/file_preparer_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,16 @@
its(:content) { is_expected.to eq("2.2.0\n") }
end

describe "the updated tool versions file" do
subject do
prepared_dependency_files.find { |f| f.name == ".tool-versions" }
end

let(:dependency_files) { bundler_project_dependency_files("tool_versions_file") }

its(:content) { is_expected.to eq("ruby 2.2.0\n") }
end

describe "the updated .specification file" do
subject do
prepared_dependency_files.find { |f| f.name == "plugins/example/.specification" }
Expand Down
18 changes: 18 additions & 0 deletions bundler/spec/dependabot/bundler/file_updater_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -532,6 +532,24 @@
end
end

context "given a Gemfile that loads a .tool-versions file" do
let(:project_name) { "tool_versions_file" }
let(:updater) do
described_class.new(
dependency_files: dependency_files,
dependencies: [dependency],
credentials: [{
"type" => "git_source",
"host" => "github.com"
}]
)
end

it "locks the updated gem to the latest version" do
expect(file.content).to include "business (1.5.0)"
end
end

context "when the Gemfile.lock didn't have a BUNDLED WITH line" do
let(:project_name) { "no_bundled_with" }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,12 @@
its([:version]) { is_expected.to eq(Gem::Version.new("1.5.0")) }
end

context "given a Gemfile that loads a .tool-versions file" do
let(:dependency_files) { bundler_project_dependency_files("tool_versions_file") }

its([:version]) { is_expected.to eq(Gem::Version.new("1.5.0")) }
end

context "with a gemspec and a Gemfile" do
let(:dependency_files) { bundler_project_dependency_files("gemfile_small_example") }

Expand Down

0 comments on commit b115833

Please sign in to comment.