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
1 change: 1 addition & 0 deletions .github/workflows/vendor-gems.yml
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ jobs:
with:
app-id: ${{ vars.BREW_COMMIT_APP_ID }}
private-key: ${{ secrets.BREW_COMMIT_APP_KEY }}
permission-contents: write

- name: Push to pull request
if: github.event_name == 'workflow_dispatch'
Expand Down
66 changes: 13 additions & 53 deletions Library/Homebrew/dev-cmd/update-portable-ruby.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,88 +4,48 @@
require "abstract_command"
require "formula"
require "utils/bottles"
require "utils/portable_ruby"

module Homebrew
module DevCmd
class UpdatePortableRuby < AbstractCommand
cmd_args do
description <<~EOS
Update the vendored portable Ruby version files, bottle checksums,
`utils/ruby.sh` and `Gemfile.lock` entries from the current
`portable-ruby` formula.
Update the vendored `portable-ruby` from the current `portable-ruby` formula:
write the version files and bottle checksums, run `brew vendor-install ruby`,
then sync `utils/ruby.sh`, vendored gems and RBI files to the bundler shipped
by the new ruby.
EOS
switch "-n", "--dry-run",
description: "Print what would be done rather than doing it."
switch "--skip-vendor-install",
description: "Do not run `brew vendor-install ruby`; skip the `utils/ruby.sh`, " \
"`Gemfile.lock` and RBI updates."

named_args :none

hide_from_man_page!
end

sig { override.void }
def run
formula = Homebrew.with_no_api_env { Formulary.factory("portable-ruby") }

version = formula.version.to_s
pkg_version = formula.pkg_version.to_s
vendor_dir = HOMEBREW_LIBRARY_PATH/"vendor"

write_file(vendor_dir/"portable-ruby-version", "#{pkg_version}\n")
write_file(HOMEBREW_LIBRARY_PATH/".ruby-version", "#{version}\n")
(vendor_dir/"portable-ruby-version").atomic_write("#{pkg_version}\n")
(HOMEBREW_LIBRARY_PATH/".ruby-version").atomic_write("#{version}\n")

formula.bottle_specification.checksums.each do |checksum|
tag_symbol = checksum.fetch("tag")
tag = Utils::Bottles::Tag.from_symbol(tag_symbol)
os = tag.linux? ? "linux" : "darwin"
path = vendor_dir/"portable-ruby-#{tag.standardized_arch}-#{os}"
write_file(path, "ruby_TAG=#{tag_symbol}\nruby_SHA=#{checksum.fetch("digest")}\n")
path.atomic_write("ruby_TAG=#{tag_symbol}\nruby_SHA=#{checksum.fetch("digest")}\n")
end

return if args.skip_vendor_install?

if args.dry_run?
ohai "brew vendor-install ruby"
ohai "Would update #{HOMEBREW_LIBRARY_PATH/"utils/ruby.sh"} and #{HOMEBREW_LIBRARY_PATH/"Gemfile.lock"} " \
"with the bundler version shipped by portable-ruby #{pkg_version}."
ohai "brew typecheck --update"
return
end

ohai "brew vendor-install ruby"
safe_system HOMEBREW_BREW_FILE, "vendor-install", "ruby"

bundler_dir = Pathname.glob(vendor_dir/"portable-ruby/#{pkg_version}/lib/ruby/gems/*/gems/bundler-*").first
odie "Cannot find vendored bundler for portable-ruby #{pkg_version}." if bundler_dir.nil?
bundler_version = bundler_dir.basename.to_s.delete_prefix("bundler-")

ruby_sh = HOMEBREW_LIBRARY_PATH/"utils/ruby.sh"
original = ruby_sh.read
updated = original.sub(/(?<=^export HOMEBREW_BUNDLER_VERSION=")[^"]+/, bundler_version)
if original != updated
ohai "Writing #{ruby_sh}"
ruby_sh.atomic_write(updated)
end

ohai "brew vendor-gems --no-commit --update=--ruby,--bundler=#{bundler_version}"
safe_system HOMEBREW_BREW_FILE, "vendor-gems", "--no-commit", "--update=--ruby,--bundler=#{bundler_version}"

ohai "brew typecheck --update"
bundler_version = Utils::PortableRuby.sync_bundler_version!(pkg_version)
safe_system HOMEBREW_BREW_FILE, "vendor-gems", "--no-commit",
"--update=--ruby,--bundler=#{bundler_version}"
safe_system HOMEBREW_BREW_FILE, "typecheck", "--update"
end

private

sig { params(path: Pathname, contents: String).void }
def write_file(path, contents)
if args.dry_run?
ohai "Write #{path}:"
puts contents
else
ohai "Writing #{path}"
path.atomic_write(contents)
end
end
end
end
end

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Library/Homebrew/test_bot.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
require "tap"
require "utils"
require "utils/bottles"
require "utils/portable_ruby"

module Homebrew
module TestBot
Expand Down
66 changes: 63 additions & 3 deletions Library/Homebrew/test_bot/formulae.rb
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ def run!(args:)
sorted_formulae.each do |f|
verify_local_bottles
if testing_portable_ruby?
portable_formula!(f)
portable_formula!(f, args:)
else
formula!(f, args:)
end
Expand Down Expand Up @@ -754,8 +754,8 @@ def formula!(formula_name, args:)
end
end

sig { params(formula_name: String).void }
def portable_formula!(formula_name)
sig { params(formula_name: String, args: Homebrew::Cmd::TestBotCmd::Args).void }
def portable_formula!(formula_name, args:)
test_header(:Formulae, method: "portable_formula!(#{formula_name})")

install_ca_certificates_if_needed
Expand Down Expand Up @@ -788,6 +788,66 @@ def portable_formula!(formula_name)
test "brew", "test", formula_name
test "brew", "linkage", formula_name
test "brew", "bottle", "--skip-relocation", "--json", "--no-rebuild", formula_name

# We only do full testing on `portable-ruby` itself.
return if formula_name != "portable-ruby"
return if args.dry_run?

bottle_file = bottle_glob(formula_name).first
if bottle_file.nil?
failed formula_name, "no bottle file found for portable-ruby validation"
return
end
Comment thread
p-linnane marked this conversation as resolved.

filename = bottle_file.basename.to_s
_, tag_string, = Utils::Bottles.extname_tag_rebuild(filename)
if tag_string.blank?
failed formula_name, "could not parse bottle filename #{filename}"
return
end

pkg_version = filename.delete_prefix("portable-ruby--")
.sub(/\.#{Regexp.escape(tag_string)}\.bottle.*\.tar\.gz\z/, "")
Comment thread
p-linnane marked this conversation as resolved.
if pkg_version.empty?
failed formula_name, "could not parse portable-ruby version from #{filename}"
return
end

tag_symbol = tag_string.to_sym
bottle_tag = Utils::Bottles::Tag.from_symbol(tag_symbol)
sha256 = bottle_file.sha256
version = pkg_version.split("_").first.to_s

vendor_dir = HOMEBREW_LIBRARY_PATH/"vendor"
(vendor_dir/"portable-ruby-version").atomic_write("#{pkg_version}\n")
(HOMEBREW_LIBRARY_PATH/".ruby-version").atomic_write("#{version}\n")
os = bottle_tag.linux? ? "linux" : "darwin"
platform_file = vendor_dir/"portable-ruby-#{bottle_tag.standardized_arch}-#{os}"
platform_file.atomic_write("ruby_TAG=#{tag_symbol}\nruby_SHA=#{sha256}\n")

# Seed `HOMEBREW_CACHE` so `brew vendor-install ruby` finds the just-built
# bottle locally instead of trying to download it.
HOMEBREW_CACHE.mkpath
FileUtils.cp(bottle_file, HOMEBREW_CACHE/"portable-ruby-#{pkg_version}.#{tag_symbol}.bottle.tar.gz")
Comment thread
p-linnane marked this conversation as resolved.
Comment thread
p-linnane marked this conversation as resolved.

test "brew", "vendor-install", "ruby"

bundler_version = Utils::PortableRuby.sync_bundler_version!(pkg_version)
test "brew", "vendor-gems", "--no-commit", "--update=--ruby,--bundler=#{bundler_version}"
test "brew", "typecheck", "--update"

# Run the checks that gate a Homebrew/brew pull request.
test "brew", "style"
test "brew", "typecheck"
test "brew", "install-bundler-gems", "--groups=all"
test "brew", "vendor-gems", "--non-bundler-gems", "--no-commit"
test "brew", "tests", "--online", "--coverage"
test "brew", "tests", "--generic", "--coverage"
test "brew", "update-test"
test "brew", "update-test", "--to-tag"
test "brew", "update-test", "--commit=HEAD"
test "brew", "test-bot", "--only-formulae", "--only-json-tab", "--test-default-formula",
env: { "GITHUB_ACTIONS" => nil }
Comment thread
p-linnane marked this conversation as resolved.
end

sig { params(formula_name: String).void }
Expand Down
29 changes: 29 additions & 0 deletions Library/Homebrew/utils/portable_ruby.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# typed: strict
# frozen_string_literal: true

require "utils/output"

module Utils
# Helper functions for the vendored portable-ruby.
module PortableRuby
extend Utils::Output::Mixin

Comment thread
p-linnane marked this conversation as resolved.
# Syncs `HOMEBREW_BUNDLER_VERSION` in `utils/ruby.sh` with the bundler shipped
# by the portable-ruby unpacked at `pkg_version`.
sig { params(pkg_version: String).returns(String) }
def self.sync_bundler_version!(pkg_version)
unpacked = HOMEBREW_LIBRARY_PATH/"vendor/portable-ruby/#{pkg_version}"
bundler_dir = Pathname.glob(unpacked/"lib/ruby/gems/*/gems/bundler-*").first
odie "Cannot find vendored bundler for portable-ruby #{pkg_version}." if bundler_dir.nil?

bundler_version = bundler_dir.basename.to_s.delete_prefix("bundler-")

ruby_sh = HOMEBREW_LIBRARY_PATH/"utils/ruby.sh"
original = ruby_sh.read
updated = original.sub(/(?<=^export HOMEBREW_BUNDLER_VERSION=")[^"]+/, bundler_version)
ruby_sh.atomic_write(updated) if original != updated

bundler_version
end
end
end
Loading