Skip to content

Support Content Addressable File Naming - gem build Flow#9703

Open
OughtPuts wants to merge 4 commits into
ruby:masterfrom
Shopify:ho/ca-changes-gem-build
Open

Support Content Addressable File Naming - gem build Flow#9703
OughtPuts wants to merge 4 commits into
ruby:masterfrom
Shopify:ho/ca-changes-gem-build

Conversation

@OughtPuts

Copy link
Copy Markdown
Contributor

TL;DR

As part of the work to speed up bundle install, this PR adjusts gem build to support using content-addressable naming at build-time for a gem which only supports a single Ruby ABI.

Summary

This PR introduces changes to the gem build process to assign a content-addressable file name during the build of a gem that only supports one Ruby ABI (a skinny gem).

The bulk of the changes are in Gem::Package. In self.build, we now check if the ruby_abi flag has been set when the gem build command is run. Only if it has do we first validate the Ruby ABI value (and compare this to the required_ruby_version for compatibility etc.) and then build a content addressable file if the validation passes without raising. If the user has passed a file name value at time of running gem build, we raise, as we would not expect a content addressable gem to have a custom file name (it instead has a SHA value based on the file contents). As part of the CA file name compilation, we build the gem first in memory (using StringIO.new), generate the SHA value from the file contents, and then write the file including the SHA value as the filename.

There is also a minor change to package_task which ensures that we are eventually transferring the correct file output of the build (as now the name could take three different forms - standard, platformed or content addressed), and a helper method in specification.rb which helps compare the Ruby ABI to the required_ruby_version provided.

Testing

Tests written in test_gem_commands_build_command, test_gem_package, test_gem_package_task, and test_gem_specification to cover new behaviour.

Tophat

Within IRB terminal, paste this script so you can see the output of using the Gem::Package.build command in different scenarios:

require "rubygems/package"
  require "tmpdir"

  Dir.mktmpdir do |dir|
    Dir.chdir(dir) do
      Dir.mkdir("lib")
      File.write("lib/code.rb", "# hello\n")

      def spec(name, ruby_version: "~> 3.4.0", platform: "arm64-darwin")
        Gem::Specification.new(name, "1") do |s|
          s.summary = name
          s.authors = ["reviewer"]
          s.files = ["lib/code.rb"]
          s.platform = platform
          s.required_ruby_version = ruby_version if ruby_version
        end
      end

      puts "\n1. Explicit ruby_abi builds content-addressed filename"
      Gem::Package.build(spec("skinny"), false, false, nil, "3.4")
      p Dir["skinny-1-*.gem"]

      puts "\n2. No ruby_abi keeps normal platform filename"
      Gem::Package.build(spec("normal"))
      p Dir["normal-1-*.gem"]

      puts "\n3. Missing required_ruby_version is filled from ruby_abi"
      defaulted = spec("defaulted", ruby_version: nil)
      Gem::Package.build(defaulted, false, false, nil, "3.4")
      p Dir["defaulted-1-*.gem"]
      p defaulted.required_ruby_version

      puts "\n4. Conflicting required_ruby_version raises"
      begin
        Gem::Package.build(spec("bad", ruby_version: "~> 3.5.0"), false, false, nil, "3.4")
      rescue ArgumentError => e
        puts e.message
      end

      puts "\nBuilt files:"
      puts Dir["*.gem"].sort
    end
  end

To try the gem build command locally against this branch, you can run it through the RubyGems checkout rather than your installed RubyGems. From a test gem directory with a platformed gemspec, use: ruby --disable-gems -I/path/to/rubygems/lib /path/to/rubygems/exe/gem build *.gemspec --ruby-abi 3.4. As an example, for my checkout that is: ruby --disable-gems -I/Users/harrietoughton/rubygems/lib /Users/harrietoughton/rubygems/exe/gem build *.gemspec --ruby-abi 3.4.

The single Ruby ABI built file should be named <name>-<version>-<sha10>.gem rather than <name>-<version>-<platform>.gem. It's also worth testing out that you receive the expected behaviour with a wrong format ruby ABI (e.g. --ruby-abi 3), with a mismatched Ruby ABI and required_ruby_version and when a gem is platformed with no Ruby ABI and when it is neither platformed nor intended for a single ABI.

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.

1 participant