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
4 changes: 3 additions & 1 deletion lib/rubygems/commands/push_command.rb
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,9 @@ def platform_matches?(spec)
end

def ruby_matches?(spec)
!options[:ruby_abi] || spec.ruby_abi == options[:ruby_abi]
return true unless options[:ruby_abi]

Gem::ContentAddress.applicable?(spec) && spec.ruby_abi == options[:ruby_abi]
end

def send_push_request(name, args)
Expand Down
1 change: 1 addition & 0 deletions lib/rubygems/source.rb
Original file line number Diff line number Diff line change
Expand Up @@ -406,6 +406,7 @@ def content_addressable_metadata(name, rows)
requirements = compact_index_requirements(info_row)
platform = required_platform_from(requirements[:platform])
next unless platform
next unless requirements[:ruby]

ContentAddressableInfo.new(version, suffix, ruby_abi_from(requirements[:ruby]), platform)
end
Expand Down
94 changes: 93 additions & 1 deletion spec/install/gemfile/content_addressable_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@
end
end

it "falls back to the pure-ruby gem when the content-addressed fat gem requires a different Ruby ABI" do
it "falls back to the pure-ruby gem when the content-addressed gem requires a different Ruby ABI" do
simulate_platform "x86_64-linux" do
build_repo2 do
build_gem "mygem", "1.0" do |s|
Expand Down Expand Up @@ -242,4 +242,96 @@
expect(the_bundle).to include_gems "mygem 2.0 not_content_addressed"
end
end

it "falls back to the non-content-addressed gem when all content-addressed gems require a different Ruby ABI" do
Comment thread
jenshenny marked this conversation as resolved.
simulate_platform "x86_64-linux" do
build_repo2 do
build_gem "mygem", "1.0" do |s|
s.platform = Gem::Platform.new("x86_64-linux")
s.write "lib/mygem.rb", "MYGEM = '1.0 not_content_addressed'"
end
end

build_gem "mygem", "1.0", ruby_abi: mismatched_abi, path: gem_repo2("gems") do |s|
s.platform = Gem::Platform.new("x86_64-linux")
s.required_ruby_version = "~> #{mismatched_abi}.0"
s.write "lib/mygem.rb", "MYGEM = '1.0 content_addressed_mismatched_abi_1'"
end

second_mismatched_abi = "#{Gem.ruby_version.segments[0] + 2}.0"
build_gem "mygem", "1.0", ruby_abi: second_mismatched_abi, path: gem_repo2("gems") do |s|
s.platform = Gem::Platform.new("x86_64-linux")
s.required_ruby_version = "~> #{second_mismatched_abi}.0"
s.write "lib/mygem.rb", "MYGEM = '1.0 content_addressed_mismatched_abi_2'"
end

install_gemfile <<~G, artifice: "compact_index_v2", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo2.to_s }
source "https://gem.repo2"

gem "mygem"
G

expect(the_bundle).to include_gems "mygem 1.0 not_content_addressed"
end
end

it "installs a locked content-addressed gem in frozen mode" do
simulate_platform "x86_64-linux" do
build_repo2 do
build_gem "mygem", "1.0" do |s|
s.platform = Gem::Platform.new("x86_64-linux")
s.write "lib/mygem.rb", "MYGEM = '1.0 not_content_addressed'"
end
end

build_gem "mygem", "1.0", ruby_abi: current_abi, path: gem_repo2("gems") do |s|
s.platform = Gem::Platform.new("x86_64-linux")
s.required_ruby_version = "~> #{current_abi}.0"
s.write "lib/mygem.rb", "MYGEM = '1.0 content_addressed'"
end

install_gemfile <<~G, artifice: "compact_index_v2", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo2.to_s }
source "https://gem.repo2"

gem "mygem"
G

expect(the_bundle).to include_gems "mygem 1.0 content_addressed"

pristine_system_gems
bundle_config "frozen true"
bundle "install", artifice: "compact_index_v2", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo2.to_s }

expect(the_bundle).to include_gems "mygem 1.0 content_addressed"
end
end

it "fails when the downloaded content-addressed gem hash does not match the filename" do
simulate_platform "x86_64-linux" do
build_repo2 do
build_gem "mygem", "1.0" do |s|
s.platform = Gem::Platform.new("x86_64-linux")
s.write "lib/mygem.rb", "MYGEM = '1.0 not_content_addressed'"
end
end

build_gem "mygem", "1.0", ruby_abi: current_abi, path: gem_repo2("gems") do |s|
s.platform = Gem::Platform.new("x86_64-linux")
s.required_ruby_version = "~> #{current_abi}.0"
s.write "lib/mygem.rb", "MYGEM = '1.0 content_addressed'"
end

ca_gem = Dir[gem_repo2("gems", "mygem-1.0-[0-9a-f]*.gem")].first
non_ca_gem = gem_repo2("gems", "mygem-1.0-x86_64-linux.gem")
FileUtils.cp non_ca_gem, ca_gem

install_gemfile <<~G, artifice: "compact_index_v2", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo2.to_s }, raise_on_error: false
source "https://gem.repo2"

gem "mygem"
G

expect(err).to include("content address mismatch")
end
end
end
152 changes: 151 additions & 1 deletion test/rubygems/test_gem_commands_build_command.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ def setup
@cmd = Gem::Commands::BuildCommand.new
end

def test_handle_options
def test_handle_options_force_strict_platform
@cmd.handle_options %w[--force --strict]

assert @cmd.options[:force]
Expand Down Expand Up @@ -120,6 +120,156 @@ def test_execute
util_test_build_gem @gem
end

def test_ruby_abi_rejects_invalid_format
gem = util_spec "platformed_gem" do |s|
s.license = "AGPL-3.0-only"
s.files = ["README.md"]
s.platform = "arm64-darwin"
end

gemspec_file = File.join(@tempdir, gem.spec_name)
File.open gemspec_file, "w" do |gs|
gs.write gem.to_ruby
end

["3", "3.4.1", "abc", "3.x"].each do |invalid|
@cmd.handle_options [gemspec_file, "--ruby-abi", invalid]
error = assert_raise(ArgumentError) do
use_ui @ui do
Dir.chdir @tempdir do
@cmd.execute
end
end
end
assert_match(/Ruby ABI must be in X\.Y format/, error.message)
end
end

def test_ruby_abi_rejects_ruby_platform
gem = util_spec "some_gem" do |s|
s.license = "AGPL-3.0-only"
s.files = ["README.md"]
end

gemspec_file = File.join(@tempdir, gem.spec_name)
File.open gemspec_file, "w" do |gs|
gs.write gem.to_ruby
end

@cmd.handle_options [gemspec_file, "--ruby-abi", "3.4"]
error = assert_raise(ArgumentError) do
use_ui @ui do
Dir.chdir @tempdir do
@cmd.execute
end
end
end
assert_match(/no platform or a Ruby platform has been set/, error.message)
end

def test_ruby_abi_rejects_mismatched_required_ruby_version
gem = util_spec "platformed_gem" do |s|
s.license = "AGPL-3.0-only"
s.files = ["README.md"]
s.platform = "arm64-darwin"
s.required_ruby_version = "~> 3.3.0"
end

gemspec_file = File.join(@tempdir, gem.spec_name)
File.open gemspec_file, "w" do |gs|
gs.write gem.to_ruby
end

@cmd.handle_options [gemspec_file, "--ruby-abi", "3.4"]
error = assert_raise(ArgumentError) do
use_ui @ui do
Dir.chdir @tempdir do
@cmd.execute
end
end
end
assert_match(/Cannot build gem for Ruby ABI 3\.4 because required_ruby_version/, error.message)
end

def test_ruby_abi_defaults_required_ruby_version_when_unset
gem = util_spec "platformed_gem" do |s|
s.license = "AGPL-3.0-only"
s.files = ["README.md"]
s.platform = "arm64-darwin"
end

gemspec_file = File.join(@tempdir, gem.spec_name)
File.open gemspec_file, "w" do |gs|
gs.write gem.to_ruby
end

@cmd.handle_options [gemspec_file, "--ruby-abi", "3.4"]
use_ui @ui do
Dir.chdir @tempdir do
@cmd.execute
end
end

files = Dir[File.join(@tempdir, "platformed_gem-2-*.gem")]
assert_equal 1, files.size
spec = Gem::Package.new(files.first).spec
assert_equal Gem::Requirement.new("~> 3.4.0"), spec.required_ruby_version
Comment thread
jenshenny marked this conversation as resolved.
end

def test_ruby_abi_produces_deterministic_content_address
gemspec = lambda do
gem = util_spec "platformed_gem" do |s|
s.license = "AGPL-3.0-only"
s.files = ["README.md"]
s.platform = "arm64-darwin"
s.required_ruby_version = "~> 3.4.0"
end

gemspec_file = File.join(@tempdir, gem.spec_name)
File.open gemspec_file, "w" do |gs|
gs.write gem.to_ruby
end

@cmd.handle_options [gemspec_file, "--ruby-abi", "3.4"]
use_ui @ui do
Dir.chdir @tempdir do
@cmd.execute
end
end

Dir[File.join(@tempdir, "platformed_gem-2-*.gem")].first
end

first_build = gemspec.call
second_build = gemspec.call

assert_equal File.basename(first_build), File.basename(second_build)
end

def test_ruby_abi_with_output_raises
gem = util_spec "platformed_gem" do |s|
s.license = "AGPL-3.0-only"
s.files = ["README.md"]
s.platform = "arm64-darwin"
s.required_ruby_version = "~> 3.4.0"
end

gemspec_file = File.join(@tempdir, gem.spec_name)
File.open gemspec_file, "w" do |gs|
gs.write gem.to_ruby
end

@cmd.handle_options [gemspec_file, "--ruby-abi", "3.4", "--output", "test.gem"]
error = assert_raise(ArgumentError) do
use_ui @ui do
Dir.chdir @tempdir do
@cmd.execute
end
end
end
assert_match(/Cannot specify both a Ruby ABI and an output file name/, error.message)
end

def test_execute_platform
gemspec_file = File.join(@tempdir, @gem.spec_name)

Expand Down
Loading
Loading