Skip to content

[Content Addressable] Update gem build flags to include --content-addressable when building a singe Ruby ABI gem - #9906

Open
OughtPuts wants to merge 1 commit into
ruby:masterfrom
Shopify:ho/gem-build-content-addressable-flag
Open

OughtPuts wants to merge 1 commit into
ruby:masterfrom
Shopify:ho/gem-build-content-addressable-flag

Conversation

@OughtPuts

@OughtPuts OughtPuts commented Sep 24, 2026 •

Copy link
Copy Markdown
Contributor

#9899

TLDR

Removes the responsibility of building a content addressable gem out of the --ruby-abi X.Y flag and into a new separate flag, --content-addressable.

Description

This PR:

  • Adds a --content-addressable flag option to BuildCommand
  • Maintains the --ruby-abi option in BuildCommand but this command is now only responsible for setting a ruby ABI at build time (instead of in the spec). All help text is rewritten to reflect this.
  • ruby_abi is no longer passed through to Gem::Package.build - the final argument is now a boolean representing whether or not --content-addressable is provided.
  • We still validate the platform, ruby version and rubygems version, but now only when --content-addressable is passed.
  • the ruby_abi_compatible? method in Gem::ContentAddress is now no longer used (we use Gem::ContentAddress.eligible? instead so this has been removed).
  • Other Gem::Package.build callsites were adjusted, e.g. builders.rb and helper.rb.

Tests

Relevant functionality tests:

  • Tests added / updated in clean_spec, content_addressable_spec, test_gem_commands_build_command, test_gem_content_address, test_gem_package.

Other test updates:

  • Changed the spec gate from >= 4.1.0.dev to >= 4.1.0.a to align with the production minimum.
  • Skip content-addressable integration specs on prerelease Rubies because their temporary test gems require the format ~> X.Y.0, which Ruby versions such as 4.1.0.dev do not satisfy, preventing the tests from reaching their intended assertions.
  • Removed Dir[File.join(@tempdir, "platformed_gem-2-*.gem")].first from test_content_addressable_produces_deterministic_content_address as this was hiding a small bug. We should return the relevant Dir from the lamda from the build each time and not take the first matching directory of the set.

Top Hatting:

The below has passed successfully for me locally ✅

  1. From a local gem directory (but specifying the rubygems changes present on this branch) Build a gem using the content addressable flag e.g. gem build --content-addressable. Confirm a content addressable gem is built e.g. ca_test-1.0.0-1234abcd.gem.

  2. Build a gem using the content addressable flag AND the ruby-abi flag because required_ruby_version is not present in the spec e.g. gem build --content-addressable --ruby-abi 4.0. Confirm a content addressable gem is built e.g. ca_test-2.0.0-1234abcd.gem. Confirm that the Ruby ABI version has been correctly set in the spec.

  3. Build a standard gem without either flag e.g. gem build. Confirm that a gem is built e.g. ca_test-3.0.0.gem

@kou kou left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1

Could you check the CI failure?

@jenshenny jenshenny left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is great work! I tested some cases with the flags and they work as intended.

One thing I noticed is that if a required_ruby_version is set already, --ruby-abi will silently overwrite it. I think a warning would make sense to tell the user that it's being overwritten.

Details
Command Gemspec Behaviour
--content-addressable arm64-darwin, RRV ~> 4.0.0 ✅ Built ca_test-1.0.0-7ef84327.gem. RGV raised to >= 4.1.0.a. Prints Platform: and Ruby ABI: 4.0 lines.
--content-addressable --ruby-abi 4.0 arm64-darwin, no RRV ✅ Built ca_test-2.0.0-44a55a27.gem. RRV ~> 4.0.0 written into spec.
(none) arm64-darwin, no RRV ✅ Built ca_test-3.0.0-arm64-darwin.gem. RRV/RGV left at >= 0. Standard "specify required_ruby_version" warning.

--ruby-abi alone

Command Gemspec Behaviour
--ruby-abi 4.0 no RRV ✅ Traditional filename ca_test-4.0.0-arm64-darwin.gem; RRV ~> 4.0.0 in spec. Not content-addressed.
--ruby-abi 4.0 RRV ~> 4.0.0 (matches) ✅ Same as above, no-op.
--ruby-abi 4.0 RRV ~> 3.3.0 (conflict) ✅ Built. RRV silently overwritten to ~> 4.0.0. No warning.
--ruby-abi 4.0 --content-addressable RRV ~> 3.3.0 (conflict) ✅ Built CA gem. RRV silently overwritten to ~> 4.0.0. No warning.
--ruby-abi 4.0 RRV >= 3.0 ✅ Built. RRV silently overwritten to ~> 4.0.0.
--ruby-abi 4.0.1 — ❌ OptionParser::InvalidArgument: Ruby ABI must be in X.Y format (at parse time).

--content-addressable validation errors

Command Gemspec Behaviour
--content-addressable no RRV ❌ ArgumentError: ...required_ruby_version is set to >= 0. Please set required_ruby_version to "~> X.Y.0"...
--content-addressable RRV >= 3.0 ❌ Same error, with >= 3.0.
--content-addressable no platform, RRV ~> 4.0.0 ❌ ArgumentError: ...no platform or a Ruby platform has been set
--content-addressable --ruby-abi 4.0 no platform, no RRV ❌ Same platform error (platform checked before RRV).
--content-addressable --output foo.gem eligible ❌ ArgumentError: Cannot specify an output file name for a content-addressable gem...

required_rubygems_version handling

Command Gemspec Behaviour
--content-addressable RGV >= 3.0 ✅ Built. WARNING: required_rubygems_version was changed from ">= 3.0" to ">= 4.1.0.a"... RGV raised in spec.
--content-addressable RGV < 4.0 ❌ ArgumentError: Cannot build gem for Ruby ABI 4.0 because required_rubygems_version is set to < 4.0...

--platform interaction

Command Gemspec Behaviour
--content-addressable --platform arm64-darwin no platform ✅ Built CA gem, Platform: arm64-darwin (flag filled the gap).
--content-addressable --platform arm64-darwin x86_64-linux ✅ Built CA gem, Platform: x86_64-linux — gemspec wins, flag silently ignored. Opposite precedence to --ruby-abi.

@@ -38,40 +38,9 @@ def test_handle_options_force_strict_platform
end

def test_options_ruby_abi

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can consolidate test_ruby_abi_sets_required_ruby_version to this test here.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in c32efe2

@jenshenny

Copy link
Copy Markdown
Contributor

One thing I noticed is that if a required_ruby_version is set already, --ruby-abi will silently overwrite it. I think a warning would make sense to tell the user that it's being overwritten.

Added a commit to add a message

   $ gem build ca_test.gemspec --ruby-abi 4.0                                                                                                                                                                     
   INFO:  required_ruby_version was changed from "~> 3.3.0" to "~> 4.0.0" for this build because --ruby-abi 4.0 was given.                                                                                        
     Successfully built RubyGem                                                                                                                                                                                   
     Name: ca_test                                                                                                                                                                                                
     Version: 5.0.0                                                                                                                                                                                               
     File: ca_test-5.0.0-arm64-darwin.gem 

This PR lgtm ✅

@jenshenny
jenshenny force-pushed the ho/gem-build-content-addressable-flag branch 2 times, most recently from 32dc841 to 1a2d80a Compare September 28, 2026 20:48

@kou kou left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1

Comment thread lib/rubygems/package.rb Outdated
# MINIMUM_RUBYGEMS_VERSION. Content-addressable builds are incompatible
# with +file_name+.

def self.build(spec, skip_validation = false, strict_validation = false, file_name = nil, content_addressable = false)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not a strong opinion: content_addressable: false (keyword argument) may be better for further extension in the future.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, I made it a keyword argument.

Comment thread lib/rubygems/commands/build_command.rb Outdated
Comment on lines +117 to +118
alert "required_ruby_version was changed from \"#{existing}\" to \"#{requirement}\" for this build " \
"because --ruby-abi #{ruby_abi} was given."

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not a strong opinion: The similar message for required_rubygems_version uses alert_warning not alert:

alert_warning \
"required_rubygems_version was changed from \"#{existing}\" to \"#{normalized}\" for this build " \
"because content addressable gems can only be installed by RubyGems #{MINIMUM_RUBYGEMS_VERSION}."

We may want to use the same method (alert or alert_warning) for both cases.

@jenshenny jenshenny Sep 30, 2026 •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

True, I made both messages alert_warning since it's clear that it is warning the user that some parts of the gemspec are being modified.

Comment thread lib/rubygems/package.rb Outdated
raise ArgumentError, "Ruby ABI must be in X.Y format"
elsif !Gem::ContentAddress.platform_eligible?(@spec.platform)
unless Gem::ContentAddress.platform_eligible?(@spec.platform)
raise ArgumentError, "Cannot build a gem scoped to a single Ruby ABI as no platform or a Ruby platform has been set"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not a strong opinion: We may be able to improve this message too. For example: "a gem scoped to a single Ruby ABI" -> "a content-addressable gem"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, I improved the message as suggested 👍

@jenshenny
jenshenny force-pushed the ho/gem-build-content-addressable-flag branch from 1a2d80a to 6d64581 Compare September 30, 2026 02:23
@jenshenny
jenshenny force-pushed the ho/gem-build-content-addressable-flag branch from 6d64581 to 26c7509 Compare September 30, 2026 02:41

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants