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
82 changes: 82 additions & 0 deletions Library/Homebrew/dev-cmd/generate-bottle-ci-matrix.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# typed: strict
# frozen_string_literal: true

require "abstract_command"
require "formula"
require "utils/bottles"

module Homebrew
module DevCmd
class GenerateBottleCiMatrix < AbstractCommand
cmd_args do
description <<~EOS
Generate a GitHub Actions runner matrix for a dispatched bottle build.
For internal use in Homebrew taps.
EOS
comma_array "--runners",
description: "Build runner names as a comma-separated list."

named_args :formula, number: 1, without_api: true

hide_from_man_page!
end

sig { override.void }
def run
runners = args.runners.to_a.map(&:strip).reject(&:empty?)
raise UsageError, "`--runners` must specify at least one build runner." if runners.empty?

github_run_id = ENV.fetch("GITHUB_RUN_ID") do
raise UsageError, "The `$GITHUB_RUN_ID` environment variable must be set."
end
formula = args.named.to_formulae.fetch(0)
matrix = runners.map do |runner|
macos_runner_parts = runner.split("-", 2) if runner.match?(/\A\d+(?:\.\d+)?(?:-(?:arm64|x86_64))?\z/)
bottle_tag = if (runner.start_with?("ubuntu-") && runner.end_with?("-arm")) ||
runner.match?(/\Alinux-arm64(?:\z|-)/)
Utils::Bottles.tag(:arm64_linux)
elsif runner.start_with?("ubuntu", "linux")
Utils::Bottles.tag(:x86_64_linux)
elsif macos_runner_parts
Utils::Bottles::Tag.new(
system: MacOSVersion.new(macos_runner_parts.fetch(0)).to_sym,
arch: macos_runner_parts.fetch(1, "x86_64").to_sym,
)
end
if bottle_tag && formula.bottle_specification.tag?(bottle_tag, no_older_versions: true)
ofail "#{formula.name} already has a bottle for #{bottle_tag}!"
end

if macos_runner_parts
{
runner: "#{macos_runner_parts.fetch(0)}-#{macos_runner_parts.fetch(1, "x86_64")}-" \
"#{github_run_id}-dispatch",
cleanup: false,
}
elsif runner.start_with?("ubuntu-")
{
runner:,
container: {
image: "ghcr.io/homebrew/brew:main",
options: "--user=linuxbrew",
},
workdir: "/github/home",
cleanup: false,
}
elsif runner.match?(/\Alinux-(?:arm64|x86_64)\z/)
{ runner: "#{runner}-#{github_run_id}-dispatch", cleanup: false }
else
{ runner:, cleanup: true }
end
end
puts JSON.pretty_generate(matrix)

return unless (github_output = ENV.fetch("GITHUB_OUTPUT", nil))

File.open(github_output, "a") do |file|
file.puts "runners=#{JSON.generate(matrix)}"
end
end
end
end
end
10 changes: 8 additions & 2 deletions Library/Homebrew/dev-cmd/update-portable-ruby.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ class UpdatePortableRuby < AbstractCommand
then sync `utils/ruby.sh`, vendored gems and RBI files to the bundler shipped
by the new ruby.
EOS
switch "--print-target-version",
description: "Print the target portable Ruby package version without updating it."
named_args :none

hide_from_man_page!
Expand All @@ -27,12 +29,16 @@ class UpdatePortableRuby < AbstractCommand
sig { override.void }
def run
formula = Homebrew::API.with_no_api_env { Formulary.factory("portable-ruby") }
version = formula.version.to_s
pkg_version = formula.pkg_version.to_s
if args.print_target_version?
puts pkg_version
return
end

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")
(HOMEBREW_LIBRARY_PATH/".ruby-version").atomic_write("#{formula.version}\n")

formula.bottle_specification.checksums.each do |checksum|
tag_symbol = checksum.fetch("tag")
Expand Down

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

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

85 changes: 85 additions & 0 deletions Library/Homebrew/test/dev-cmd/generate-bottle-ci-matrix_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
# typed: strict
# frozen_string_literal: true

require "cmd/shared_examples/args_parse"
require "dev-cmd/generate-bottle-ci-matrix"

RSpec.describe Homebrew::DevCmd::GenerateBottleCiMatrix do
it_behaves_like "parseable arguments"

it "checks runners against their bottle tags" do
runners = {
"15" => Utils::Bottles::Tag.from_symbol(:x86_64_sequoia),
"15.4-arm64" => Utils::Bottles::Tag.from_symbol(:arm64_sequoia),
"ubuntu-latest" => Utils::Bottles.tag(:x86_64_linux),
"ubuntu-24.04-arm" => Utils::Bottles.tag(:arm64_linux),
"linux-arm64" => Utils::Bottles.tag(:arm64_linux),
"linux-x86_64" => Utils::Bottles.tag(:x86_64_linux),
"linux-self-hosted-1" => Utils::Bottles.tag(:x86_64_linux),
}
command = described_class.new(["--runners=#{runners.keys.join(",")}", "testball"])
bottle_specification = instance_double(BottleSpecification)
formula = instance_double(Formula, name: "testball", bottle_specification:)
tag_checks = []
allow(command.args.named).to receive(:to_formulae).and_return([formula])
allow(command).to receive(:puts)
allow(bottle_specification).to receive(:tag?) do |tag, no_older_versions:|
tag_checks << [tag, no_older_versions]
false
end
ENV["GITHUB_RUN_ID"] = "123"
ENV.delete("GITHUB_OUTPUT")

command.run

expect(tag_checks).to eq(runners.values.map { |tag| [tag, true] })
end

it "fails for an existing Linux ARM bottle" do
command = described_class.new(["--runners=linux-arm64", "testball"])
bottle_specification = instance_double(BottleSpecification, tag?: true)
formula = instance_double(Formula, name: "testball", bottle_specification:)
allow(command.args.named).to receive(:to_formulae).and_return([formula])
ENV["GITHUB_RUN_ID"] = "123"
ENV.delete("GITHUB_OUTPUT")

expect { command.run }
.to change(Homebrew, :failed?).from(false).to(true)
.and output(/testball already has a bottle for arm64_linux/).to_stderr
end

it "generates the dispatched bottle runner matrix", :integration_test do
setup_test_formula "testball"

mktmpdir do |path|
github_output = path/"github-output"
runners = "15-arm64,ubuntu-24.04-arm,linux-arm64,linux-x86_64,custom-runner"

expect do
expect do
brew "generate-bottle-ci-matrix", "--runners=#{runners}", "testball",
"GITHUB_OUTPUT" => github_output.to_s,
"GITHUB_RUN_ID" => "123"
end.to be_a_success
end.to output(/"runner": "linux-arm64-123-dispatch"/).to_stdout

expect(JSON.parse(github_output.read.delete_prefix("runners="))).to eq(
[
{ "runner" => "15-arm64-123-dispatch", "cleanup" => false },
{
"runner" => "ubuntu-24.04-arm",
"container" => {
"image" => "ghcr.io/homebrew/brew:main",
"options" => "--user=linuxbrew",
},
"workdir" => "/github/home",
"cleanup" => false,
},
{ "runner" => "linux-arm64-123-dispatch", "cleanup" => false },
{ "runner" => "linux-x86_64-123-dispatch", "cleanup" => false },
{ "runner" => "custom-runner", "cleanup" => true },
],
)
end
end
end
8 changes: 8 additions & 0 deletions Library/Homebrew/test/dev-cmd/update-portable-ruby_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,12 @@

RSpec.describe Homebrew::DevCmd::UpdatePortableRuby do
it_behaves_like "parseable arguments"

it "prints the target package version without updating portable Ruby" do
formula = instance_double(Formula, pkg_version: PkgVersion.parse("4.0.6_2"))
allow(Formulary).to receive(:factory).with("portable-ruby").and_return(formula)

expect { described_class.new(["--print-target-version"]).run }
.to output("4.0.6_2\n").to_stdout
end
end
22 changes: 21 additions & 1 deletion completions/bash/brew
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ __brew_complete_commands() {
fi
if [[ -n ${HOMEBREW_DEVELOPER:-} ]]
then
maintainer_cmds="advisory-match determine-test-runners dispatch-build-bottle formula-analytics generate-advisories-api generate-analytics-api generate-cask-api generate-cask-ci-matrix generate-formula-api generate-internal-api generate-vulns-advisories pr-automerge pr-publish pr-pull pr-upload release update-license-data update-maintainers update-portable-ruby update-report update-sponsors vendor-install"
maintainer_cmds="advisory-match determine-test-runners dispatch-build-bottle formula-analytics generate-advisories-api generate-analytics-api generate-bottle-ci-matrix generate-cask-api generate-cask-ci-matrix generate-formula-api generate-internal-api generate-vulns-advisories pr-automerge pr-publish pr-pull pr-upload release update-license-data update-maintainers update-portable-ruby update-report update-sponsors vendor-install"
fi
user_aliases="$(__brew_list_aliases)"
while IFS= read -r line
Expand Down Expand Up @@ -1709,6 +1709,24 @@ _brew_generate_analytics_api() {
esac
}

_brew_generate_bottle_ci_matrix() {
local cur="${COMP_WORDS[COMP_CWORD]}"
case "${cur}" in
-*)
__brewcomp "
--debug
--help
--quiet
--runners
--verbose
"
return
;;
*) ;;
esac
__brew_complete_formulae
}

_brew_generate_cask_api() {
local cur="${COMP_WORDS[COMP_CWORD]}"
case "${cur}" in
Expand Down Expand Up @@ -3423,6 +3441,7 @@ _brew_update_portable_ruby() {
__brewcomp "
--debug
--help
--print-target-version
--quiet
--verbose
"
Expand Down Expand Up @@ -3835,6 +3854,7 @@ _brew() {
formulae) _brew_formulae ;;
generate-advisories-api) _brew_generate_advisories_api ;;
generate-analytics-api) _brew_generate_analytics_api ;;
generate-bottle-ci-matrix) _brew_generate_bottle_ci_matrix ;;
generate-cask-api) _brew_generate_cask_api ;;
generate-cask-ci-matrix) _brew_generate_cask_ci_matrix ;;
generate-cask-token) _brew_generate_cask_token ;;
Expand Down
10 changes: 10 additions & 0 deletions completions/fish/brew.fish
Original file line number Diff line number Diff line change
Expand Up @@ -1152,6 +1152,15 @@ __fish_brew_complete_arg 'generate-analytics-api' -l quiet -d 'Make some output
__fish_brew_complete_arg 'generate-analytics-api' -l verbose -d 'Make some output more verbose'


complete -f -c brew -n 'not __fish_brew_command; and set -q HOMEBREW_DEVELOPER' -a 'generate-bottle-ci-matrix' -d 'Generate a GitHub Actions runner matrix for a dispatched bottle build'
__fish_brew_complete_arg 'generate-bottle-ci-matrix' -l debug -d 'Display any debugging information'
__fish_brew_complete_arg 'generate-bottle-ci-matrix' -l help -d 'Show this message'
__fish_brew_complete_arg 'generate-bottle-ci-matrix' -l quiet -d 'Make some output more quiet'
__fish_brew_complete_arg 'generate-bottle-ci-matrix' -l runners -d 'Build runner names as a comma-separated list'
__fish_brew_complete_arg 'generate-bottle-ci-matrix' -l verbose -d 'Make some output more verbose'
__fish_brew_complete_arg 'generate-bottle-ci-matrix' -a '(__fish_brew_suggest_formulae_all)'


complete -f -c brew -n 'not __fish_brew_command; and set -q HOMEBREW_DEVELOPER' -a 'generate-cask-api' -d 'Generate `homebrew/cask` API data files for https://formulae.brew.sh'
__fish_brew_complete_arg 'generate-cask-api' -l debug -d 'Display any debugging information'
__fish_brew_complete_arg 'generate-cask-api' -l dry-run -d 'Generate API data without writing it to files'
Expand Down Expand Up @@ -2158,6 +2167,7 @@ __fish_brew_complete_arg 'update-perl-resources' -a '(__fish_brew_suggest_formul
complete -f -c brew -n 'not __fish_brew_command; and set -q HOMEBREW_DEVELOPER' -a 'update-portable-ruby' -d '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'
__fish_brew_complete_arg 'update-portable-ruby' -l debug -d 'Display any debugging information'
__fish_brew_complete_arg 'update-portable-ruby' -l help -d 'Show this message'
__fish_brew_complete_arg 'update-portable-ruby' -l print-target-version -d 'Print the target portable Ruby package version without updating it'
__fish_brew_complete_arg 'update-portable-ruby' -l quiet -d 'Make some output more quiet'
__fish_brew_complete_arg 'update-portable-ruby' -l verbose -d 'Make some output more verbose'

Expand Down
14 changes: 14 additions & 0 deletions completions/zsh/_brew
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,7 @@ __brew_internal_commands() {
'formula-analytics:Query Homebrew'\''s analytics'
'generate-advisories-api:Generate advisory API data for https://formulae.brew.sh from a checkout of https://github.com/Homebrew/advisory-database'
'generate-analytics-api:Generates analytics API data files for https://formulae.brew.sh'
'generate-bottle-ci-matrix:Generate a GitHub Actions runner matrix for a dispatched bottle build'
'generate-cask-api:Generate `homebrew/cask` API data files for https://formulae.brew.sh'
'generate-cask-ci-matrix:Generate a GitHub Actions matrix for a given pull request URL or list of cask names'
'generate-formula-api:Generate `homebrew/core` API data files for https://formulae.brew.sh'
Expand Down Expand Up @@ -1509,6 +1510,18 @@ _brew_generate_analytics_api() {
'--verbose[Make some output more verbose]'
}

# brew generate-bottle-ci-matrix
_brew_generate_bottle_ci_matrix() {
_arguments \
'--debug[Display any debugging information]' \
'--help[Show this message]' \
'--quiet[Make some output more quiet]' \
'--runners[Build runner names as a comma-separated list]' \
'--verbose[Make some output more verbose]' \
- formula \
'*:formula:__brew_formulae'
}

# brew generate-cask-api
_brew_generate_cask_api() {
_arguments \
Expand Down Expand Up @@ -2767,6 +2780,7 @@ _brew_update_portable_ruby() {
_arguments \
'--debug[Display any debugging information]' \
'--help[Show this message]' \
'--print-target-version[Print the target portable Ruby package version without updating it]' \
'--quiet[Make some output more quiet]' \
'--verbose[Make some output more verbose]'
}
Expand Down
Loading