From 89cd55c2879937428b1352cbbb154daa6a59159b Mon Sep 17 00:00:00 2001 From: Carlo Cabrera <30379873+carlocab@users.noreply.github.com> Date: Mon, 3 Apr 2023 20:36:45 +0800 Subject: [PATCH 01/39] dev-cmd/determine-test-runners: add command to set test runners This is based on feedback from Homebrew/homebrew-core#127236. --- .../dev-cmd/determine-test-runners.rb | 30 +++ .../os/dev-cmd/determine-test-runners.rb | 4 + .../linux/dev-cmd/determine-test-runners.rb | 197 ++++++++++++++++++ 3 files changed, 231 insertions(+) create mode 100755 Library/Homebrew/dev-cmd/determine-test-runners.rb create mode 100644 Library/Homebrew/extend/os/dev-cmd/determine-test-runners.rb create mode 100755 Library/Homebrew/extend/os/linux/dev-cmd/determine-test-runners.rb diff --git a/Library/Homebrew/dev-cmd/determine-test-runners.rb b/Library/Homebrew/dev-cmd/determine-test-runners.rb new file mode 100755 index 0000000000..4b1ade89d6 --- /dev/null +++ b/Library/Homebrew/dev-cmd/determine-test-runners.rb @@ -0,0 +1,30 @@ +# typed: true +# frozen_string_literal: true + +require "cli/parser" + +module Homebrew + module_function + + def determine_test_runners_args + Homebrew::CLI::Parser.new do + usage_banner <<~EOS + `determine-test-runners` [] + + Determines the runners used to test formulae or their dependents. + EOS + switch "--dependents", + description: "Determine runners for testing dependents." + + named_args min: 1, max: 2 + + hide_from_man_page! + end + end + + def determine_test_runners + odie "This command is supported only on Linux!" + end +end + +require "extend/os/dev-cmd/determine-test-runners" diff --git a/Library/Homebrew/extend/os/dev-cmd/determine-test-runners.rb b/Library/Homebrew/extend/os/dev-cmd/determine-test-runners.rb new file mode 100644 index 0000000000..9d6f300d7c --- /dev/null +++ b/Library/Homebrew/extend/os/dev-cmd/determine-test-runners.rb @@ -0,0 +1,4 @@ +# typed: strict +# frozen_string_literal: true + +require "extend/os/linux/dev-cmd/determine-test-runners" if OS.linux? diff --git a/Library/Homebrew/extend/os/linux/dev-cmd/determine-test-runners.rb b/Library/Homebrew/extend/os/linux/dev-cmd/determine-test-runners.rb new file mode 100755 index 0000000000..3705355c3e --- /dev/null +++ b/Library/Homebrew/extend/os/linux/dev-cmd/determine-test-runners.rb @@ -0,0 +1,197 @@ +# typed: false +# frozen_string_literal: true + +require "formula" + +class Formula + def macos_only? + requirements.any? { |r| r.is_a?(MacOSRequirement) && !r.version_specified? } + end + + def linux_only? + requirements.any?(LinuxRequirement) + end + + def x86_64_only? + requirements.any? { |r| r.is_a?(ArchRequirement) && (r.arch == :x86_64) } + end + + def arm64_only? + requirements.any? { |r| r.is_a?(ArchRequirement) && (r.arch == :arm64) } + end + + def versioned_macos_requirement + requirements.find { |r| r.is_a?(MacOSRequirement) && r.version_specified? } + end + + def compatible_with?(macos_version) + return true if versioned_macos_requirement.blank? + + macos_version.public_send(versioned_macos_requirement.comparator, versioned_macos_requirement.version) + end + + def dependents + @dependent_hash ||= {} + @dependent_hash[ENV["HOMEBREW_SIMULATE_MACOS_ON_LINUX"].present?] ||= with_env(HOMEBREW_STDERR: "1") do + Utils.safe_popen_read( + HOMEBREW_BREW_FILE, "uses", "--formulae", "--eval-all", "--include-build", "--include-test", name + ).split("\n").map { |dependent| Formula[dependent] }.freeze + end + + @dependent_hash[ENV["HOMEBREW_SIMULATE_MACOS_ON_LINUX"].present?] + end +end + +module Homebrew + module_function + + def formulae_have_untested_dependents?(testing_formulae, reject_platform:, reject_arch:, select_macos_version:) + testing_formulae.any? do |formula| + # If the formula has a platform/arch/macOS version requirement, then its + # dependents don't need to be tested if these requirements are not satisfied. + next false if reject_platform && formula.method("#{reject_platform}_only?").call + next false if reject_arch && formula.method("#{reject_arch}_only?").call + next false if select_macos_version && !formula.compatible_with?(select_macos_version) + + compatible_dependents = formula.dependents.dup + + compatible_dependents.reject! { |dependent_f| dependent_f.method("#{reject_arch}_only?").call } if reject_arch + + if reject_platform + compatible_dependents.reject! { |dependent_f| dependent_f.method("#{reject_platform}_only?").call } + end + + if select_macos_version + compatible_dependents.select! { |dependent_f| dependent_f.compatible_with?(select_macos_version) } + end + + (compatible_dependents - testing_formulae).present? + end + end + + def add_runner?(formulae, + dependents:, + deleted_formulae:, + reject_platform: nil, + reject_arch: nil, + select_macos_version: nil) + if dependents + formulae_have_untested_dependents?( + formulae, + reject_platform: reject_platform, + reject_arch: reject_arch, + select_macos_version: select_macos_version, + ) + else + return true if deleted_formulae.present? + + compatible_formulae = formulae.dup + compatible_formulae.reject! { |formula| formula.method("#{reject_platform}_only?").call } if reject_platform + compatible_formulae.reject! { |formula| formula.method("#{reject_arch}_only?").call } if reject_arch + compatible_formulae.select! { |formula| formula.compatible_with?(select_macos_version) } if select_macos_version + + compatible_formulae.present? + end + end + + def determine_test_runners + args = determine_test_runners_args.parse + testing_formulae = args.named.first.split(",") + testing_formulae.map! { |name| Formula[name] } + .freeze + deleted_formulae = args.named.second&.split(",") + + runners = [] + + linux_runner = ENV.fetch("HOMEBREW_LINUX_RUNNER") { raise "HOMEBREW_LINUX_RUNNER is not defined" } + linux_cleanup = ENV.fetch("HOMEBREW_LINUX_CLEANUP") { raise "HOMEBREW_LINUX_CLEANUP is not defined" } + + linux_runner_spec = { + runner: linux_runner, + container: { + image: "ghcr.io/homebrew/ubuntu22.04:master", + options: "--user=linuxbrew -e GITHUB_ACTIONS_HOMEBREW_SELF_HOSTED", + }, + workdir: "/github/home", + timeout: 4320, + cleanup: linux_cleanup == "true", + } + + with_env(HOMEBREW_SIMULATE_MACOS_ON_LINUX: nil) do + if add_runner?( + testing_formulae, + reject_platform: :macos, + reject_arch: :arm64, + deleted_formulae: deleted_formulae, + dependents: args.dependents?, + ) + runners << linux_runner_spec + end + end + + # TODO: `HOMEBREW_SIMULATE_MACOS_ON_LINUX` simulates the oldest version of macOS. + # Handle formulae that are dependents only on new versions of macOS. + with_env(HOMEBREW_SIMULATE_MACOS_ON_LINUX: "1") do + if add_runner?( + testing_formulae, + reject_platform: :linux, + deleted_formulae: deleted_formulae, + dependents: args.dependents?, + ) + add_intel_runners = add_runner?( + testing_formulae, + reject_platform: :linux, + reject_arch: :arm64, + deleted_formulae: deleted_formulae, + dependents: args.dependents?, + ) + add_m1_runners = add_runner?( + testing_formulae, + reject_platform: :linux, + reject_arch: :x86_64, + deleted_formulae: deleted_formulae, + dependents: args.dependents?, + ) + + MacOSVersions::SYMBOLS.each_value do |version| + macos_version = MacOS::Version.new(version) + next if macos_version.outdated_release? || macos_version.prerelease? + + unless add_runner?( + testing_formulae, + reject_platform: :linux, + select_macos_version: macos_version, + deleted_formulae: deleted_formulae, + dependents: args.dependents?, + ) + next # No formulae to test on this macOS version. + end + + ephemeral_suffix = "-#{ENV.fetch("GITHUB_RUN_ID")}-#{ENV.fetch("GITHUB_RUN_ATTEMPT")}" + runners << { runner: "#{macos_version}#{ephemeral_suffix}", cleanup: false } if add_intel_runners + + next unless add_m1_runners + + # Use bare metal runner when testing dependents on Monterey. + if macos_version >= :ventura || (macos_version >= :monterey && !args.dependents?) + runners << { runner: "#{macos_version}-arm64#{ephemeral_suffix}", cleanup: false } + elsif macos_version >= :big_sur + runners << { runner: "#{macos_version}-arm64", cleanup: true } + end + end + end + end + + if !args.dependents? && runners.blank? + # If there are no tests to run, add a runner that is meant to do nothing + # to support making the `tests` job a required status check. + runners << { runner: "ubuntu-latest", no_op: true } + end + + github_output = ENV.fetch("GITHUB_OUTPUT") { raise "GITHUB_OUTPUT is not defined" } + File.open(github_output, "a") do |f| + f.puts("runners=#{runners.to_json}") + f.puts("runners_present=#{runners.present?}") + end + end +end From 2c920fa6145d348df008cf70c7e63d7c1f6851f9 Mon Sep 17 00:00:00 2001 From: BrewTestBot <1589480+BrewTestBot@users.noreply.github.com> Date: Mon, 3 Apr 2023 12:43:09 +0000 Subject: [PATCH 02/39] Update manpage and completions. Autogenerated by the [sponsors-maintainers-man-completions](https://github.com/Homebrew/brew/blob/HEAD/.github/workflows/sponsors-maintainers-man-completions.yml) workflow. --- completions/bash/brew | 18 ++++++++++++++++++ completions/fish/brew.fish | 8 ++++++++ completions/internal_commands_list.txt | 1 + completions/zsh/_brew | 11 +++++++++++ 4 files changed, 38 insertions(+) diff --git a/completions/bash/brew b/completions/bash/brew index 2ef69567a1..f584352ef0 100644 --- a/completions/bash/brew +++ b/completions/bash/brew @@ -788,6 +788,23 @@ _brew_desc() { __brew_complete_casks } +_brew_determine_test_runners() { + local cur="${COMP_WORDS[COMP_CWORD]}" + case "${cur}" in + -*) + __brewcomp " + --debug + --dependents + --help + --quiet + --verbose + " + return + ;; + *) ;; + esac +} + _brew_developer() { local cur="${COMP_WORDS[COMP_CWORD]}" case "${cur}" in @@ -2628,6 +2645,7 @@ _brew() { create) _brew_create ;; deps) _brew_deps ;; desc) _brew_desc ;; + determine-test-runners) _brew_determine_test_runners ;; developer) _brew_developer ;; dispatch-build-bottle) _brew_dispatch_build_bottle ;; docs) _brew_docs ;; diff --git a/completions/fish/brew.fish b/completions/fish/brew.fish index 82f5637f28..188a4b9f94 100644 --- a/completions/fish/brew.fish +++ b/completions/fish/brew.fish @@ -613,6 +613,14 @@ __fish_brew_complete_arg 'desc; and not __fish_seen_argument -l cask -l casks' - __fish_brew_complete_arg 'desc; and not __fish_seen_argument -l formula -l formulae' -a '(__fish_brew_suggest_casks_all)' +__fish_brew_complete_cmd 'determine-test-runners' 'Determines the runners used to test formulae or their dependents' +__fish_brew_complete_arg 'determine-test-runners' -l debug -d 'Display any debugging information' +__fish_brew_complete_arg 'determine-test-runners' -l dependents -d 'Determine runners for testing dependents' +__fish_brew_complete_arg 'determine-test-runners' -l help -d 'Show this message' +__fish_brew_complete_arg 'determine-test-runners' -l quiet -d 'Make some output more quiet' +__fish_brew_complete_arg 'determine-test-runners' -l verbose -d 'Make some output more verbose' + + __fish_brew_complete_cmd 'developer' 'Control Homebrew\'s developer mode' __fish_brew_complete_sub_cmd 'developer' 'state' __fish_brew_complete_sub_cmd 'developer' 'on' diff --git a/completions/internal_commands_list.txt b/completions/internal_commands_list.txt index ce9b70c9c6..a35e46fe4b 100644 --- a/completions/internal_commands_list.txt +++ b/completions/internal_commands_list.txt @@ -30,6 +30,7 @@ contributions create deps desc +determine-test-runners developer dispatch-build-bottle docs diff --git a/completions/zsh/_brew b/completions/zsh/_brew index de508faa19..6e36809e5f 100644 --- a/completions/zsh/_brew +++ b/completions/zsh/_brew @@ -158,6 +158,7 @@ __brew_internal_commands() { 'create:Generate a formula or, with `--cask`, a cask for the downloadable file at URL and open it in the editor' 'deps:Show dependencies for formula' 'desc:Display formula'\''s name and one-line description' + 'determine-test-runners:Determines the runners used to test formulae or their dependents' 'developer:Control Homebrew'\''s developer mode' 'dispatch-build-bottle:Build bottles for these formulae with GitHub Actions' 'docs:Open Homebrew'\''s online documentation (https://docs' @@ -755,6 +756,16 @@ _brew_desc() { '*::cask:__brew_casks' } +# brew determine-test-runners +_brew_determine_test_runners() { + _arguments \ + '--debug[Display any debugging information]' \ + '--dependents[Determine runners for testing dependents]' \ + '--help[Show this message]' \ + '--quiet[Make some output more quiet]' \ + '--verbose[Make some output more verbose]' +} + # brew developer _brew_developer() { _arguments \ From dbdff020c0c8a1d2f9a1ff64180296fd835627e9 Mon Sep 17 00:00:00 2001 From: Carlo Cabrera <30379873+carlocab@users.noreply.github.com> Date: Mon, 3 Apr 2023 21:31:08 +0800 Subject: [PATCH 03/39] determine-test-runners: avoid reopening `Formula` --- .../linux/dev-cmd/determine-test-runners.rb | 24 ++++++++++++------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/Library/Homebrew/extend/os/linux/dev-cmd/determine-test-runners.rb b/Library/Homebrew/extend/os/linux/dev-cmd/determine-test-runners.rb index 3705355c3e..e3289be995 100755 --- a/Library/Homebrew/extend/os/linux/dev-cmd/determine-test-runners.rb +++ b/Library/Homebrew/extend/os/linux/dev-cmd/determine-test-runners.rb @@ -3,25 +3,33 @@ require "formula" -class Formula +class TestRunnerFormula + attr_reader :name, :formula + + def initialize(name) + @name = name + @formula = Formula[name] + freeze + end + def macos_only? - requirements.any? { |r| r.is_a?(MacOSRequirement) && !r.version_specified? } + formula.requirements.any? { |r| r.is_a?(MacOSRequirement) && !r.version_specified? } end def linux_only? - requirements.any?(LinuxRequirement) + formula.requirements.any?(LinuxRequirement) end def x86_64_only? - requirements.any? { |r| r.is_a?(ArchRequirement) && (r.arch == :x86_64) } + formula.requirements.any? { |r| r.is_a?(ArchRequirement) && (r.arch == :x86_64) } end def arm64_only? - requirements.any? { |r| r.is_a?(ArchRequirement) && (r.arch == :arm64) } + formula.requirements.any? { |r| r.is_a?(ArchRequirement) && (r.arch == :arm64) } end def versioned_macos_requirement - requirements.find { |r| r.is_a?(MacOSRequirement) && r.version_specified? } + formula.requirements.find { |r| r.is_a?(MacOSRequirement) && r.version_specified? } end def compatible_with?(macos_version) @@ -35,7 +43,7 @@ class Formula @dependent_hash[ENV["HOMEBREW_SIMULATE_MACOS_ON_LINUX"].present?] ||= with_env(HOMEBREW_STDERR: "1") do Utils.safe_popen_read( HOMEBREW_BREW_FILE, "uses", "--formulae", "--eval-all", "--include-build", "--include-test", name - ).split("\n").map { |dependent| Formula[dependent] }.freeze + ).split("\n").map { |dependent| TestRunnerFormula.new(dependent) }.freeze end @dependent_hash[ENV["HOMEBREW_SIMULATE_MACOS_ON_LINUX"].present?] @@ -97,7 +105,7 @@ module Homebrew def determine_test_runners args = determine_test_runners_args.parse testing_formulae = args.named.first.split(",") - testing_formulae.map! { |name| Formula[name] } + testing_formulae.map! { |name| TestRunnerFormula.new(name) } .freeze deleted_formulae = args.named.second&.split(",") From 45fdbbd1c1d85be39b747109c9489786b4fffbbb Mon Sep 17 00:00:00 2001 From: Carlo Cabrera <30379873+carlocab@users.noreply.github.com> Date: Mon, 3 Apr 2023 22:27:46 +0800 Subject: [PATCH 04/39] determine-test-runners: add type-checking --- .../dev-cmd/determine-test-runners.rb | 10 ++- .../linux/dev-cmd/determine-test-runners.rb | 86 ++++++++++++++----- 2 files changed, 69 insertions(+), 27 deletions(-) diff --git a/Library/Homebrew/dev-cmd/determine-test-runners.rb b/Library/Homebrew/dev-cmd/determine-test-runners.rb index 4b1ade89d6..d75f5e6e7a 100755 --- a/Library/Homebrew/dev-cmd/determine-test-runners.rb +++ b/Library/Homebrew/dev-cmd/determine-test-runners.rb @@ -1,12 +1,13 @@ -# typed: true +# typed: strict # frozen_string_literal: true require "cli/parser" module Homebrew - module_function + extend T::Sig - def determine_test_runners_args + sig { returns(Homebrew::CLI::Parser) } + def self.determine_test_runners_args Homebrew::CLI::Parser.new do usage_banner <<~EOS `determine-test-runners` [] @@ -22,7 +23,8 @@ module Homebrew end end - def determine_test_runners + sig { void } + def self.determine_test_runners odie "This command is supported only on Linux!" end end diff --git a/Library/Homebrew/extend/os/linux/dev-cmd/determine-test-runners.rb b/Library/Homebrew/extend/os/linux/dev-cmd/determine-test-runners.rb index e3289be995..bdfe8c17ca 100755 --- a/Library/Homebrew/extend/os/linux/dev-cmd/determine-test-runners.rb +++ b/Library/Homebrew/extend/os/linux/dev-cmd/determine-test-runners.rb @@ -1,72 +1,98 @@ -# typed: false +# typed: strict # frozen_string_literal: true require "formula" class TestRunnerFormula - attr_reader :name, :formula + extend T::Sig + sig { returns(String) } + attr_reader :name + + sig { returns(Formula) } + attr_reader :formula + + sig { params(name: String).void } def initialize(name) - @name = name - @formula = Formula[name] + @name = T.let(name, String) + @formula = T.let(Formula[name], Formula) + @dependent_hash = T.let({}, T::Hash[T::Boolean, T::Array[TestRunnerFormula]]) freeze end + sig { returns(T::Boolean) } def macos_only? formula.requirements.any? { |r| r.is_a?(MacOSRequirement) && !r.version_specified? } end + sig { returns(T::Boolean) } def linux_only? formula.requirements.any?(LinuxRequirement) end + sig { returns(T::Boolean) } def x86_64_only? formula.requirements.any? { |r| r.is_a?(ArchRequirement) && (r.arch == :x86_64) } end + sig { returns(T::Boolean) } def arm64_only? formula.requirements.any? { |r| r.is_a?(ArchRequirement) && (r.arch == :arm64) } end + sig { returns(T.nilable(MacOSRequirement)) } def versioned_macos_requirement formula.requirements.find { |r| r.is_a?(MacOSRequirement) && r.version_specified? } end + sig { params(macos_version: MacOS::Version).returns(T::Boolean) } def compatible_with?(macos_version) - return true if versioned_macos_requirement.blank? + # Assign to a variable to assist type-checking. + requirement = versioned_macos_requirement + return true if requirement.blank? - macos_version.public_send(versioned_macos_requirement.comparator, versioned_macos_requirement.version) + macos_version.public_send(requirement.comparator, requirement.version) end + sig { returns(T::Array[TestRunnerFormula]) } def dependents - @dependent_hash ||= {} @dependent_hash[ENV["HOMEBREW_SIMULATE_MACOS_ON_LINUX"].present?] ||= with_env(HOMEBREW_STDERR: "1") do Utils.safe_popen_read( HOMEBREW_BREW_FILE, "uses", "--formulae", "--eval-all", "--include-build", "--include-test", name ).split("\n").map { |dependent| TestRunnerFormula.new(dependent) }.freeze end - @dependent_hash[ENV["HOMEBREW_SIMULATE_MACOS_ON_LINUX"].present?] + T.must(@dependent_hash[ENV["HOMEBREW_SIMULATE_MACOS_ON_LINUX"].present?]) end end module Homebrew - module_function + extend T::Sig - def formulae_have_untested_dependents?(testing_formulae, reject_platform:, reject_arch:, select_macos_version:) + sig { + params( + testing_formulae: T::Array[TestRunnerFormula], + reject_platform: T.nilable(Symbol), + reject_arch: T.nilable(Symbol), + select_macos_version: T.nilable(MacOS::Version), + ).returns(T::Boolean) + } + def self.formulae_have_untested_dependents?(testing_formulae, reject_platform:, reject_arch:, select_macos_version:) testing_formulae.any? do |formula| # If the formula has a platform/arch/macOS version requirement, then its # dependents don't need to be tested if these requirements are not satisfied. - next false if reject_platform && formula.method("#{reject_platform}_only?").call - next false if reject_arch && formula.method("#{reject_arch}_only?").call + next false if reject_platform && formula.method("#{reject_platform}_only?".to_sym).call + next false if reject_arch && formula.method("#{reject_arch}_only?".to_sym).call next false if select_macos_version && !formula.compatible_with?(select_macos_version) compatible_dependents = formula.dependents.dup - compatible_dependents.reject! { |dependent_f| dependent_f.method("#{reject_arch}_only?").call } if reject_arch + if reject_arch + compatible_dependents.reject! { |dependent_f| dependent_f.method("#{reject_arch}_only?".to_sym).call } + end if reject_platform - compatible_dependents.reject! { |dependent_f| dependent_f.method("#{reject_platform}_only?").call } + compatible_dependents.reject! { |dependent_f| dependent_f.method("#{reject_platform}_only?".to_sym).call } end if select_macos_version @@ -77,12 +103,22 @@ module Homebrew end end - def add_runner?(formulae, - dependents:, - deleted_formulae:, - reject_platform: nil, - reject_arch: nil, - select_macos_version: nil) + sig { + params( + formulae: T::Array[TestRunnerFormula], + dependents: T::Boolean, + deleted_formulae: T.nilable(T::Array[String]), + reject_platform: T.nilable(Symbol), + reject_arch: T.nilable(Symbol), + select_macos_version: T.nilable(MacOS::Version), + ).returns(T::Boolean) + } + def self.add_runner?(formulae, + dependents:, + deleted_formulae:, + reject_platform: nil, + reject_arch: nil, + select_macos_version: nil) if dependents formulae_have_untested_dependents?( formulae, @@ -94,15 +130,19 @@ module Homebrew return true if deleted_formulae.present? compatible_formulae = formulae.dup - compatible_formulae.reject! { |formula| formula.method("#{reject_platform}_only?").call } if reject_platform - compatible_formulae.reject! { |formula| formula.method("#{reject_arch}_only?").call } if reject_arch + + compatible_formulae.reject! { |formula| formula.method("#{reject_arch}_only?".to_sym).call } if reject_arch compatible_formulae.select! { |formula| formula.compatible_with?(select_macos_version) } if select_macos_version + if reject_platform + compatible_formulae.reject! { |formula| formula.method("#{reject_platform}_only?".to_sym).call } + end compatible_formulae.present? end end - def determine_test_runners + sig { void } + def self.determine_test_runners args = determine_test_runners_args.parse testing_formulae = args.named.first.split(",") testing_formulae.map! { |name| TestRunnerFormula.new(name) } From ae63b234729e86d3e9e7d2b056316274669c0f1e Mon Sep 17 00:00:00 2001 From: Carlo Cabrera <30379873+carlocab@users.noreply.github.com> Date: Mon, 3 Apr 2023 22:42:00 +0800 Subject: [PATCH 05/39] determine-test-runners: add a test I'll add more substantial tests shortly, but let's start with this one. --- .../Homebrew/test/dev-cmd/determine-test-runners_spec.rb | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 Library/Homebrew/test/dev-cmd/determine-test-runners_spec.rb diff --git a/Library/Homebrew/test/dev-cmd/determine-test-runners_spec.rb b/Library/Homebrew/test/dev-cmd/determine-test-runners_spec.rb new file mode 100644 index 0000000000..2d4f935a5b --- /dev/null +++ b/Library/Homebrew/test/dev-cmd/determine-test-runners_spec.rb @@ -0,0 +1,8 @@ +# typed: false +# frozen_string_literal: true + +require "cmd/shared_examples/args_parse" + +describe "brew determine-test-runners" do + it_behaves_like "parseable arguments" +end From 4dc370ca249d5834587efa46db091c0c6162921e Mon Sep 17 00:00:00 2001 From: Carlo Cabrera <30379873+carlocab@users.noreply.github.com> Date: Tue, 4 Apr 2023 00:05:29 +0800 Subject: [PATCH 06/39] Add some more tests --- .../dev-cmd/determine-test-runners_spec.rb | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/Library/Homebrew/test/dev-cmd/determine-test-runners_spec.rb b/Library/Homebrew/test/dev-cmd/determine-test-runners_spec.rb index 2d4f935a5b..05b30dce94 100644 --- a/Library/Homebrew/test/dev-cmd/determine-test-runners_spec.rb +++ b/Library/Homebrew/test/dev-cmd/determine-test-runners_spec.rb @@ -1,8 +1,67 @@ # typed: false # frozen_string_literal: true +require "dev-cmd/determine-test-runners" require "cmd/shared_examples/args_parse" describe "brew determine-test-runners" do it_behaves_like "parseable arguments" + + let(:github_output) { "#{TEST_TMPDIR}/github_output" } + let(:runner_env) { + { + "HOMEBREW_LINUX_RUNNER" => "ubuntu-latest", + "HOMEBREW_LINUX_CLEANUP" => "false", + "GITHUB_RUN_ID" => "12345", + "GITHUB_RUN_ATTEMPT" => "1", + "GITHUB_OUTPUT" => github_output, + } + } + # TODO: Generate this dynamically based on our supported macOS versions. + let(:all_runners) { ["11", "11-arm64", "12", "12-arm64", "13", "13-arm64", "ubuntu-latest"] } + + after(:each) do + FileUtils.rm_f github_output + end + + it "fails without any arguments", :integration_test do + expect { brew "determine-test-runners" } + .to not_to_output.to_stdout + .and be_a_failure + end + + it "assigns all runners for formulae without any requirements", :integration_test, :needs_linux do + setup_test_formula "testball" + + ohai runner_env + expect { brew "determine-test-runners", "testball", runner_env } + .to not_to_output.to_stdout + .and not_to_output.to_stderr + .and be_a_success + + expect(File.read(github_output)).not_to be_empty + expect(get_runners(github_output)).to eq(all_runners) + end + + it "assigns all runners when there are deleted formulae", :integration_test, :needs_linux do + expect { brew "determine-test-runners", "", "testball", runner_env } + .to not_to_output.to_stdout + .and not_to_output.to_stderr + .and be_a_success + + expect(File.read(github_output)).not_to be_empty + expect(get_runners(github_output)).to eq(all_runners) + end +end + +def parse_runner_hash(file) + runner_line = File.open(file).first + json_text = runner_line[/runners=(.*)/, 1] + JSON.parse(json_text) +end + +def get_runners(file) + runner_hash = parse_runner_hash(file) + runner_hash.map { |item| item["runner"].delete_suffix("-12345-1") } + .sort end From d5dc9ee5e0d39969b95d0072a707a01f9a07b472 Mon Sep 17 00:00:00 2001 From: Carlo Cabrera <30379873+carlocab@users.noreply.github.com> Date: Tue, 4 Apr 2023 01:01:04 +0800 Subject: [PATCH 07/39] determine-test-runners: test `--dependents` --- .../test/dev-cmd/determine-test-runners_spec.rb | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/Library/Homebrew/test/dev-cmd/determine-test-runners_spec.rb b/Library/Homebrew/test/dev-cmd/determine-test-runners_spec.rb index 05b30dce94..aeee8fa6ef 100644 --- a/Library/Homebrew/test/dev-cmd/determine-test-runners_spec.rb +++ b/Library/Homebrew/test/dev-cmd/determine-test-runners_spec.rb @@ -33,7 +33,6 @@ describe "brew determine-test-runners" do it "assigns all runners for formulae without any requirements", :integration_test, :needs_linux do setup_test_formula "testball" - ohai runner_env expect { brew "determine-test-runners", "testball", runner_env } .to not_to_output.to_stdout .and not_to_output.to_stderr @@ -52,6 +51,20 @@ describe "brew determine-test-runners" do expect(File.read(github_output)).not_to be_empty expect(get_runners(github_output)).to eq(all_runners) end + + describe "--dependents" do + it "assigns no runners when a formula has no dependents", :integration_test, :needs_linux do + setup_test_formula "testball" + + expect { brew "determine-test-runners", "--dependents", "testball", runner_env } + .to not_to_output.to_stdout + .and not_to_output.to_stderr + .and be_a_success + + expect(File.read(github_output)).not_to be_empty + expect(get_runners(github_output)).to be_empty + end + end end def parse_runner_hash(file) From 140e751ec655ad9c04186dcfe18803ca6d47b757 Mon Sep 17 00:00:00 2001 From: Carlo Cabrera <30379873+carlocab@users.noreply.github.com> Date: Tue, 4 Apr 2023 01:12:05 +0800 Subject: [PATCH 08/39] Fix `brew style` --- .../dev-cmd/determine-test-runners_spec.rb | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/Library/Homebrew/test/dev-cmd/determine-test-runners_spec.rb b/Library/Homebrew/test/dev-cmd/determine-test-runners_spec.rb index aeee8fa6ef..4db72e6bdb 100644 --- a/Library/Homebrew/test/dev-cmd/determine-test-runners_spec.rb +++ b/Library/Homebrew/test/dev-cmd/determine-test-runners_spec.rb @@ -5,25 +5,25 @@ require "dev-cmd/determine-test-runners" require "cmd/shared_examples/args_parse" describe "brew determine-test-runners" do - it_behaves_like "parseable arguments" - - let(:github_output) { "#{TEST_TMPDIR}/github_output" } - let(:runner_env) { - { - "HOMEBREW_LINUX_RUNNER" => "ubuntu-latest", - "HOMEBREW_LINUX_CLEANUP" => "false", - "GITHUB_RUN_ID" => "12345", - "GITHUB_RUN_ATTEMPT" => "1", - "GITHUB_OUTPUT" => github_output, - } - } - # TODO: Generate this dynamically based on our supported macOS versions. - let(:all_runners) { ["11", "11-arm64", "12", "12-arm64", "13", "13-arm64", "ubuntu-latest"] } - - after(:each) do + after do FileUtils.rm_f github_output end + # TODO: Generate this dynamically based on our supported macOS versions. + let(:all_runners) { %w[11 11-arm64 12 12-arm64 13 13-arm64 ubuntu-latest] } + let(:github_output) { "#{TEST_TMPDIR}/github_output" } + let(:runner_env) do + { + "HOMEBREW_LINUX_RUNNER" => "ubuntu-latest", + "HOMEBREW_LINUX_CLEANUP" => "false", + "GITHUB_RUN_ID" => "12345", + "GITHUB_RUN_ATTEMPT" => "1", + "GITHUB_OUTPUT" => github_output, + } + end + + it_behaves_like "parseable arguments" + it "fails without any arguments", :integration_test do expect { brew "determine-test-runners" } .to not_to_output.to_stdout From 3dc66fae9f75782cb20e3243fb4a24c3573677df Mon Sep 17 00:00:00 2001 From: Carlo Cabrera <30379873+carlocab@users.noreply.github.com> Date: Tue, 4 Apr 2023 01:46:33 +0800 Subject: [PATCH 09/39] determine-test-runners: add non-trivial tests --- .../dev-cmd/determine-test-runners_spec.rb | 100 +++++++++++++++--- 1 file changed, 88 insertions(+), 12 deletions(-) diff --git a/Library/Homebrew/test/dev-cmd/determine-test-runners_spec.rb b/Library/Homebrew/test/dev-cmd/determine-test-runners_spec.rb index 4db72e6bdb..2464a99a35 100644 --- a/Library/Homebrew/test/dev-cmd/determine-test-runners_spec.rb +++ b/Library/Homebrew/test/dev-cmd/determine-test-runners_spec.rb @@ -10,11 +10,15 @@ describe "brew determine-test-runners" do end # TODO: Generate this dynamically based on our supported macOS versions. - let(:all_runners) { %w[11 11-arm64 12 12-arm64 13 13-arm64 ubuntu-latest] } + let(:linux_runner) { "ubuntu-22.04" } + let(:all_runners) { ["11", "11-arm64", "12", "12-arm64", "13", "13-arm64", linux_runner] } + let(:intel_runners) { ["11", "12", "13", linux_runner] } + let(:arm64_runners) { %w[11-arm64 12-arm64 13-arm64] } + let(:macos_runners) { all_runners - [linux_runner] } let(:github_output) { "#{TEST_TMPDIR}/github_output" } let(:runner_env) do { - "HOMEBREW_LINUX_RUNNER" => "ubuntu-latest", + "HOMEBREW_LINUX_RUNNER" => linux_runner, "HOMEBREW_LINUX_CLEANUP" => "false", "GITHUB_RUN_ID" => "12345", "GITHUB_RUN_ATTEMPT" => "1", @@ -52,18 +56,90 @@ describe "brew determine-test-runners" do expect(get_runners(github_output)).to eq(all_runners) end - describe "--dependents" do - it "assigns no runners when a formula has no dependents", :integration_test, :needs_linux do - setup_test_formula "testball" + it "assigns `ubuntu-latest` when there are no testing formulae and no deleted formulae", :integration_test, + :needs_linux do + expect { brew "determine-test-runners", "", runner_env } + .to not_to_output.to_stdout + .and not_to_output.to_stderr + .and be_a_success - expect { brew "determine-test-runners", "--dependents", "testball", runner_env } - .to not_to_output.to_stdout - .and not_to_output.to_stderr - .and be_a_success + expect(File.read(github_output)).not_to be_empty + expect(get_runners(github_output)).to eq(["ubuntu-latest"]) + end - expect(File.read(github_output)).not_to be_empty - expect(get_runners(github_output)).to be_empty - end + it "assigns only Intel runners when a formula `depends_on arch: :x86_64`", :integration_test, :needs_linux do + setup_test_formula "intel_depender", <<~RUBY + url "https://brew.sh/intel_depender-1.0.tar.gz" + depends_on arch: :x86_64 + RUBY + + expect { brew "determine-test-runners", "intel_depender", runner_env } + .to not_to_output.to_stdout + .and not_to_output.to_stderr + .and be_a_success + + expect(File.read(github_output)).not_to be_empty + expect(get_runners(github_output)).to eq(intel_runners) + end + + it "assigns only ARM64 runners when a formula `depends_on arch: :arm64`", :integration_test, :needs_linux do + setup_test_formula "fancy-m1-ml-framework", <<~RUBY + url "https://brew.sh/fancy-m1-ml-framework-1.0.tar.gz" + depends_on arch: :arm64 + RUBY + + expect { brew "determine-test-runners", "fancy-m1-ml-framework", runner_env } + .to not_to_output.to_stdout + .and not_to_output.to_stderr + .and be_a_success + + expect(File.read(github_output)).not_to be_empty + expect(get_runners(github_output)).to eq(arm64_runners) + end + + it "assigns only macOS runners when a formula `depends_on :macos`", :integration_test, :needs_linux do + setup_test_formula "xcode-helper", <<~RUBY + url "https://brew.sh/xcode-helper-1.0.tar.gz" + depends_on :macos + RUBY + + expect { brew "determine-test-runners", "xcode-helper", runner_env } + .to not_to_output.to_stdout + .and not_to_output.to_stderr + .and be_a_success + + expect(File.read(github_output)).not_to be_empty + expect(get_runners(github_output)).to eq(macos_runners) + end + + it "assigns only Linux runners when a formula `depends_on :linux`", :integration_test, :needs_linux do + setup_test_formula "linux-kernel-requirer", <<~RUBY + url "https://brew.sh/linux-kernel-requirer-1.0.tar.gz" + depends_on :linux + RUBY + + expect { brew "determine-test-runners", "linux-kernel-requirer", runner_env } + .to not_to_output.to_stdout + .and not_to_output.to_stderr + .and be_a_success + + expect(File.read(github_output)).not_to be_empty + expect(get_runners(github_output)).to eq([linux_runner]) + end + + it "assigns only compatible runners when there is a versioned macOS requirement", :integration_test, :needs_linux do + setup_test_formula "needs-macos-13", <<~RUBY + url "https://brew.sh/needs-macos-13-1.0.tar.gz" + depends_on macos: :ventura + RUBY + + expect { brew "determine-test-runners", "needs-macos-13", runner_env } + .to not_to_output.to_stdout + .and not_to_output.to_stderr + .and be_a_success + + expect(File.read(github_output)).not_to be_empty + expect(get_runners(github_output)).to eq(["13", "13-arm64", linux_runner]) end end From a2e97fe1ad87734e36c096c8b1442d27dc721f77 Mon Sep 17 00:00:00 2001 From: Carlo Cabrera <30379873+carlocab@users.noreply.github.com> Date: Tue, 4 Apr 2023 01:51:37 +0800 Subject: [PATCH 10/39] Improve type-checking style Co-authored-by: Douglas Eichelberger --- .../os/linux/dev-cmd/determine-test-runners.rb | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/Library/Homebrew/extend/os/linux/dev-cmd/determine-test-runners.rb b/Library/Homebrew/extend/os/linux/dev-cmd/determine-test-runners.rb index bdfe8c17ca..e4980ce09e 100755 --- a/Library/Homebrew/extend/os/linux/dev-cmd/determine-test-runners.rb +++ b/Library/Homebrew/extend/os/linux/dev-cmd/determine-test-runners.rb @@ -62,7 +62,7 @@ class TestRunnerFormula ).split("\n").map { |dependent| TestRunnerFormula.new(dependent) }.freeze end - T.must(@dependent_hash[ENV["HOMEBREW_SIMULATE_MACOS_ON_LINUX"].present?]) + @dependent_hash.fetch(ENV["HOMEBREW_SIMULATE_MACOS_ON_LINUX"].present?) end end @@ -81,18 +81,16 @@ module Homebrew testing_formulae.any? do |formula| # If the formula has a platform/arch/macOS version requirement, then its # dependents don't need to be tested if these requirements are not satisfied. - next false if reject_platform && formula.method("#{reject_platform}_only?".to_sym).call - next false if reject_arch && formula.method("#{reject_arch}_only?".to_sym).call + next false if reject_platform && formula.method(:"#{reject_platform}_only?").call + next false if reject_arch && formula.method(:"#{reject_arch}_only?").call next false if select_macos_version && !formula.compatible_with?(select_macos_version) compatible_dependents = formula.dependents.dup - if reject_arch - compatible_dependents.reject! { |dependent_f| dependent_f.method("#{reject_arch}_only?".to_sym).call } - end + compatible_dependents.reject! { |dependent_f| dependent_f.method(:"#{reject_arch}_only?").call } if reject_arch if reject_platform - compatible_dependents.reject! { |dependent_f| dependent_f.method("#{reject_platform}_only?".to_sym).call } + compatible_dependents.reject! { |dependent_f| dependent_f.method(:"#{reject_platform}_only?").call } end if select_macos_version @@ -131,11 +129,9 @@ module Homebrew compatible_formulae = formulae.dup - compatible_formulae.reject! { |formula| formula.method("#{reject_arch}_only?".to_sym).call } if reject_arch + compatible_formulae.reject! { |formula| formula.method(:"#{reject_arch}_only?").call } if reject_arch compatible_formulae.select! { |formula| formula.compatible_with?(select_macos_version) } if select_macos_version - if reject_platform - compatible_formulae.reject! { |formula| formula.method("#{reject_platform}_only?".to_sym).call } - end + compatible_formulae.reject! { |formula| formula.method(:"#{reject_platform}_only?").call } if reject_platform compatible_formulae.present? end From 3d7d4c60f70c49b7e6b5527234ac9c30beded6f0 Mon Sep 17 00:00:00 2001 From: Carlo Cabrera <30379873+carlocab@users.noreply.github.com> Date: Tue, 4 Apr 2023 02:13:35 +0800 Subject: [PATCH 11/39] determine-test-runners_spec: hardcode fewer things --- .../test/dev-cmd/determine-test-runners_spec.rb | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/Library/Homebrew/test/dev-cmd/determine-test-runners_spec.rb b/Library/Homebrew/test/dev-cmd/determine-test-runners_spec.rb index 2464a99a35..c3bddc98cb 100644 --- a/Library/Homebrew/test/dev-cmd/determine-test-runners_spec.rb +++ b/Library/Homebrew/test/dev-cmd/determine-test-runners_spec.rb @@ -12,16 +12,17 @@ describe "brew determine-test-runners" do # TODO: Generate this dynamically based on our supported macOS versions. let(:linux_runner) { "ubuntu-22.04" } let(:all_runners) { ["11", "11-arm64", "12", "12-arm64", "13", "13-arm64", linux_runner] } - let(:intel_runners) { ["11", "12", "13", linux_runner] } - let(:arm64_runners) { %w[11-arm64 12-arm64 13-arm64] } + let(:intel_runners) { all_runners.reject { |r| r.end_with? "-arm64" } } + let(:arm64_runners) { all_runners.select { |r| r.end_with? "-arm64" } } let(:macos_runners) { all_runners - [linux_runner] } let(:github_output) { "#{TEST_TMPDIR}/github_output" } + let(:ephemeral_suffix) { "-12345-1" } let(:runner_env) do { "HOMEBREW_LINUX_RUNNER" => linux_runner, "HOMEBREW_LINUX_CLEANUP" => "false", - "GITHUB_RUN_ID" => "12345", - "GITHUB_RUN_ATTEMPT" => "1", + "GITHUB_RUN_ID" => ephemeral_suffix.split("-").second, + "GITHUB_RUN_ATTEMPT" => ephemeral_suffix.split("-").third, "GITHUB_OUTPUT" => github_output, } end @@ -127,6 +128,7 @@ describe "brew determine-test-runners" do expect(get_runners(github_output)).to eq([linux_runner]) end + # TODO: Keep this updated to use the newest supported macOS version. it "assigns only compatible runners when there is a versioned macOS requirement", :integration_test, :needs_linux do setup_test_formula "needs-macos-13", <<~RUBY url "https://brew.sh/needs-macos-13-1.0.tar.gz" @@ -151,6 +153,6 @@ end def get_runners(file) runner_hash = parse_runner_hash(file) - runner_hash.map { |item| item["runner"].delete_suffix("-12345-1") } + runner_hash.map { |item| item["runner"].delete_suffix(ephemeral_suffix) } .sort end From 839b7d44cc014176f0bed4bc232f048125358436 Mon Sep 17 00:00:00 2001 From: Carlo Cabrera <30379873+carlocab@users.noreply.github.com> Date: Tue, 4 Apr 2023 02:28:56 +0800 Subject: [PATCH 12/39] determine-test-runners: test failure modes --- .../test/dev-cmd/determine-test-runners_spec.rb | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/Library/Homebrew/test/dev-cmd/determine-test-runners_spec.rb b/Library/Homebrew/test/dev-cmd/determine-test-runners_spec.rb index c3bddc98cb..520035f741 100644 --- a/Library/Homebrew/test/dev-cmd/determine-test-runners_spec.rb +++ b/Library/Homebrew/test/dev-cmd/determine-test-runners_spec.rb @@ -35,6 +35,22 @@ describe "brew determine-test-runners" do .and be_a_failure end + it "fails when the necessary environment variables are missing", :integration_test, :needs_linux do + setup_test_formula "testball" + + runner_env.each_key do |k| + next if ["GITHUB_RUN_ID", "GITHUB_RUN_ATTEMPT"].include? k + + runner_env_dup = runner_env.dup + runner_env_dup.delete(k) + + expect { brew "determine-test-runners", "testball", runner_env_dup } + .to not_to_output.to_stdout + .and output("Error: #{k} is not defined\n").to_stderr + .and be_a_failure + end + end + it "assigns all runners for formulae without any requirements", :integration_test, :needs_linux do setup_test_formula "testball" From 4cb50a38c7dca819347a4c53da512d86f25040af Mon Sep 17 00:00:00 2001 From: Carlo Cabrera <30379873+carlocab@users.noreply.github.com> Date: Tue, 4 Apr 2023 02:42:14 +0800 Subject: [PATCH 13/39] Improve versioned macOS test --- Library/Homebrew/test/dev-cmd/determine-test-runners_spec.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/Library/Homebrew/test/dev-cmd/determine-test-runners_spec.rb b/Library/Homebrew/test/dev-cmd/determine-test-runners_spec.rb index 520035f741..fafcb7a18d 100644 --- a/Library/Homebrew/test/dev-cmd/determine-test-runners_spec.rb +++ b/Library/Homebrew/test/dev-cmd/determine-test-runners_spec.rb @@ -158,6 +158,7 @@ describe "brew determine-test-runners" do expect(File.read(github_output)).not_to be_empty expect(get_runners(github_output)).to eq(["13", "13-arm64", linux_runner]) + expect(get_runners(github_output)).not_to eq(all_runners) end end From 7ff2382d74cccd236f975bd5b11767e547329f36 Mon Sep 17 00:00:00 2001 From: Carlo Cabrera <30379873+carlocab@users.noreply.github.com> Date: Tue, 4 Apr 2023 12:15:23 +0800 Subject: [PATCH 14/39] determine-test-runners: fix parallel tests Since these tests run in parallel, we want to minimise our reliance on environment variables. Also, we need to make sure that different examples write to different paths. --- .../dev-cmd/determine-test-runners_spec.rb | 65 +++++++++---------- 1 file changed, 32 insertions(+), 33 deletions(-) diff --git a/Library/Homebrew/test/dev-cmd/determine-test-runners_spec.rb b/Library/Homebrew/test/dev-cmd/determine-test-runners_spec.rb index fafcb7a18d..27576dd959 100644 --- a/Library/Homebrew/test/dev-cmd/determine-test-runners_spec.rb +++ b/Library/Homebrew/test/dev-cmd/determine-test-runners_spec.rb @@ -13,9 +13,10 @@ describe "brew determine-test-runners" do let(:linux_runner) { "ubuntu-22.04" } let(:all_runners) { ["11", "11-arm64", "12", "12-arm64", "13", "13-arm64", linux_runner] } let(:intel_runners) { all_runners.reject { |r| r.end_with? "-arm64" } } - let(:arm64_runners) { all_runners.select { |r| r.end_with? "-arm64" } } + let(:arm64_runners) { all_runners - intel_runners } let(:macos_runners) { all_runners - [linux_runner] } - let(:github_output) { "#{TEST_TMPDIR}/github_output" } + # We need to make sure we write to a different path for each example. + let(:github_output) { "#{TEST_TMPDIR}/github_output#{TestRunnerTestHelper.new.number}" } let(:ephemeral_suffix) { "-12345-1" } let(:runner_env) do { @@ -23,7 +24,6 @@ describe "brew determine-test-runners" do "HOMEBREW_LINUX_CLEANUP" => "false", "GITHUB_RUN_ID" => ephemeral_suffix.split("-").second, "GITHUB_RUN_ATTEMPT" => ephemeral_suffix.split("-").third, - "GITHUB_OUTPUT" => github_output, } end @@ -35,26 +35,10 @@ describe "brew determine-test-runners" do .and be_a_failure end - it "fails when the necessary environment variables are missing", :integration_test, :needs_linux do - setup_test_formula "testball" - - runner_env.each_key do |k| - next if ["GITHUB_RUN_ID", "GITHUB_RUN_ATTEMPT"].include? k - - runner_env_dup = runner_env.dup - runner_env_dup.delete(k) - - expect { brew "determine-test-runners", "testball", runner_env_dup } - .to not_to_output.to_stdout - .and output("Error: #{k} is not defined\n").to_stderr - .and be_a_failure - end - end - it "assigns all runners for formulae without any requirements", :integration_test, :needs_linux do setup_test_formula "testball" - expect { brew "determine-test-runners", "testball", runner_env } + expect { brew "determine-test-runners", "testball", runner_env.merge({ "GITHUB_OUTPUT" => github_output }) } .to not_to_output.to_stdout .and not_to_output.to_stderr .and be_a_success @@ -64,7 +48,7 @@ describe "brew determine-test-runners" do end it "assigns all runners when there are deleted formulae", :integration_test, :needs_linux do - expect { brew "determine-test-runners", "", "testball", runner_env } + expect { brew "determine-test-runners", "", "testball", runner_env.merge({ "GITHUB_OUTPUT" => github_output }) } .to not_to_output.to_stdout .and not_to_output.to_stderr .and be_a_success @@ -75,7 +59,7 @@ describe "brew determine-test-runners" do it "assigns `ubuntu-latest` when there are no testing formulae and no deleted formulae", :integration_test, :needs_linux do - expect { brew "determine-test-runners", "", runner_env } + expect { brew "determine-test-runners", "", runner_env.merge({ "GITHUB_OUTPUT" => github_output }) } .to not_to_output.to_stdout .and not_to_output.to_stderr .and be_a_success @@ -90,7 +74,7 @@ describe "brew determine-test-runners" do depends_on arch: :x86_64 RUBY - expect { brew "determine-test-runners", "intel_depender", runner_env } + expect { brew "determine-test-runners", "intel_depender", runner_env.merge({ "GITHUB_OUTPUT" => github_output }) } .to not_to_output.to_stdout .and not_to_output.to_stderr .and be_a_success @@ -105,7 +89,9 @@ describe "brew determine-test-runners" do depends_on arch: :arm64 RUBY - expect { brew "determine-test-runners", "fancy-m1-ml-framework", runner_env } + expect do + brew "determine-test-runners", "fancy-m1-ml-framework", runner_env.merge({ "GITHUB_OUTPUT" => github_output }) + end .to not_to_output.to_stdout .and not_to_output.to_stderr .and be_a_success @@ -120,7 +106,7 @@ describe "brew determine-test-runners" do depends_on :macos RUBY - expect { brew "determine-test-runners", "xcode-helper", runner_env } + expect { brew "determine-test-runners", "xcode-helper", runner_env.merge({ "GITHUB_OUTPUT" => github_output }) } .to not_to_output.to_stdout .and not_to_output.to_stderr .and be_a_success @@ -135,7 +121,9 @@ describe "brew determine-test-runners" do depends_on :linux RUBY - expect { brew "determine-test-runners", "linux-kernel-requirer", runner_env } + expect do + brew "determine-test-runners", "linux-kernel-requirer", runner_env.merge({ "GITHUB_OUTPUT" => github_output }) + end .to not_to_output.to_stdout .and not_to_output.to_stderr .and be_a_success @@ -151,7 +139,7 @@ describe "brew determine-test-runners" do depends_on macos: :ventura RUBY - expect { brew "determine-test-runners", "needs-macos-13", runner_env } + expect { brew "determine-test-runners", "needs-macos-13", runner_env.merge({ "GITHUB_OUTPUT" => github_output }) } .to not_to_output.to_stdout .and not_to_output.to_stderr .and be_a_success @@ -162,14 +150,25 @@ describe "brew determine-test-runners" do end end -def parse_runner_hash(file) +def get_runners(file) runner_line = File.open(file).first json_text = runner_line[/runners=(.*)/, 1] - JSON.parse(json_text) -end - -def get_runners(file) - runner_hash = parse_runner_hash(file) + runner_hash = JSON.parse(json_text) runner_hash.map { |item| item["runner"].delete_suffix(ephemeral_suffix) } .sort end + +class TestRunnerTestHelper + @instances = 0 + + class << self + attr_accessor :instances + end + + attr_reader :number + + def initialize + self.class.instances += 1 + @number = self.class.instances + end +end From e191dbfb9ebd6f577669c63e203af4894ac5369e Mon Sep 17 00:00:00 2001 From: Carlo Cabrera <30379873+carlocab@users.noreply.github.com> Date: Tue, 4 Apr 2023 13:20:03 +0800 Subject: [PATCH 15/39] Improve style --- .../os/linux/dev-cmd/determine-test-runners.rb | 12 ++++++------ .../test/dev-cmd/determine-test-runners_spec.rb | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Library/Homebrew/extend/os/linux/dev-cmd/determine-test-runners.rb b/Library/Homebrew/extend/os/linux/dev-cmd/determine-test-runners.rb index e4980ce09e..fc0b4315c5 100755 --- a/Library/Homebrew/extend/os/linux/dev-cmd/determine-test-runners.rb +++ b/Library/Homebrew/extend/os/linux/dev-cmd/determine-test-runners.rb @@ -81,16 +81,16 @@ module Homebrew testing_formulae.any? do |formula| # If the formula has a platform/arch/macOS version requirement, then its # dependents don't need to be tested if these requirements are not satisfied. - next false if reject_platform && formula.method(:"#{reject_platform}_only?").call - next false if reject_arch && formula.method(:"#{reject_arch}_only?").call + next false if reject_platform && formula.send(:"#{reject_platform}_only?") + next false if reject_arch && formula.send(:"#{reject_arch}_only?") next false if select_macos_version && !formula.compatible_with?(select_macos_version) compatible_dependents = formula.dependents.dup - compatible_dependents.reject! { |dependent_f| dependent_f.method(:"#{reject_arch}_only?").call } if reject_arch + compatible_dependents.reject! { |dependent_f| dependent_f.send(:"#{reject_arch}_only?")} if reject_arch if reject_platform - compatible_dependents.reject! { |dependent_f| dependent_f.method(:"#{reject_platform}_only?").call } + compatible_dependents.reject! { |dependent_f| dependent_f.send(:"#{reject_platform}_only?") } end if select_macos_version @@ -129,9 +129,9 @@ module Homebrew compatible_formulae = formulae.dup - compatible_formulae.reject! { |formula| formula.method(:"#{reject_arch}_only?").call } if reject_arch + compatible_formulae.reject! { |formula| formula.send(:"#{reject_platform}_only?") } if reject_platform + compatible_formulae.reject! { |formula| formula.send(:"#{reject_arch}_only?") } if reject_arch compatible_formulae.select! { |formula| formula.compatible_with?(select_macos_version) } if select_macos_version - compatible_formulae.reject! { |formula| formula.method(:"#{reject_platform}_only?").call } if reject_platform compatible_formulae.present? end diff --git a/Library/Homebrew/test/dev-cmd/determine-test-runners_spec.rb b/Library/Homebrew/test/dev-cmd/determine-test-runners_spec.rb index 27576dd959..1cea8aee8f 100644 --- a/Library/Homebrew/test/dev-cmd/determine-test-runners_spec.rb +++ b/Library/Homebrew/test/dev-cmd/determine-test-runners_spec.rb @@ -16,7 +16,7 @@ describe "brew determine-test-runners" do let(:arm64_runners) { all_runners - intel_runners } let(:macos_runners) { all_runners - [linux_runner] } # We need to make sure we write to a different path for each example. - let(:github_output) { "#{TEST_TMPDIR}/github_output#{TestRunnerTestHelper.new.number}" } + let(:github_output) { "#{TEST_TMPDIR}/github_output#{DetermineRunnerTestHelper.new.number}" } let(:ephemeral_suffix) { "-12345-1" } let(:runner_env) do { @@ -158,7 +158,7 @@ def get_runners(file) .sort end -class TestRunnerTestHelper +class DetermineRunnerTestHelper @instances = 0 class << self From 3d03d243a415b2cfafb3d4bcf71575da4b151d19 Mon Sep 17 00:00:00 2001 From: Carlo Cabrera <30379873+carlocab@users.noreply.github.com> Date: Tue, 4 Apr 2023 02:28:56 +0800 Subject: [PATCH 16/39] determine-test-runners: test failure modes (again) --- .../os/linux/dev-cmd/determine-test-runners.rb | 5 ++++- .../test/dev-cmd/determine-test-runners_spec.rb | 16 +++++++++++++++- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/Library/Homebrew/extend/os/linux/dev-cmd/determine-test-runners.rb b/Library/Homebrew/extend/os/linux/dev-cmd/determine-test-runners.rb index fc0b4315c5..f77bf20fd6 100755 --- a/Library/Homebrew/extend/os/linux/dev-cmd/determine-test-runners.rb +++ b/Library/Homebrew/extend/os/linux/dev-cmd/determine-test-runners.rb @@ -197,6 +197,9 @@ module Homebrew dependents: args.dependents?, ) + github_run_id = ENV.fetch("GITHUB_RUN_ID") { raise "GITHUB_RUN_ID is not defined" } + github_run_attempt = ENV.fetch("GITHUB_RUN_ATTEMPT") { raise "GITHUB_RUN_ATTEMPT is not defined" } + MacOSVersions::SYMBOLS.each_value do |version| macos_version = MacOS::Version.new(version) next if macos_version.outdated_release? || macos_version.prerelease? @@ -211,7 +214,7 @@ module Homebrew next # No formulae to test on this macOS version. end - ephemeral_suffix = "-#{ENV.fetch("GITHUB_RUN_ID")}-#{ENV.fetch("GITHUB_RUN_ATTEMPT")}" + ephemeral_suffix = "-#{github_run_id}-#{github_run_attempt}" runners << { runner: "#{macos_version}#{ephemeral_suffix}", cleanup: false } if add_intel_runners next unless add_m1_runners diff --git a/Library/Homebrew/test/dev-cmd/determine-test-runners_spec.rb b/Library/Homebrew/test/dev-cmd/determine-test-runners_spec.rb index 1cea8aee8f..d26c01b159 100644 --- a/Library/Homebrew/test/dev-cmd/determine-test-runners_spec.rb +++ b/Library/Homebrew/test/dev-cmd/determine-test-runners_spec.rb @@ -24,7 +24,7 @@ describe "brew determine-test-runners" do "HOMEBREW_LINUX_CLEANUP" => "false", "GITHUB_RUN_ID" => ephemeral_suffix.split("-").second, "GITHUB_RUN_ATTEMPT" => ephemeral_suffix.split("-").third, - } + }.freeze end it_behaves_like "parseable arguments" @@ -35,6 +35,20 @@ describe "brew determine-test-runners" do .and be_a_failure end + it "fails when the necessary environment variables are missing", :integration_test, :needs_linux do + setup_test_formula "testball" + + runner_env.each_key do |k| + runner_env_dup = runner_env.dup + runner_env_dup[k] = nil + + expect { brew "determine-test-runners", "testball", runner_env_dup } + .to not_to_output.to_stdout + .and output("Error: #{k} is not defined\n").to_stderr + .and be_a_failure + end + end + it "assigns all runners for formulae without any requirements", :integration_test, :needs_linux do setup_test_formula "testball" From c125079d0fe21383b275126f1a23a2ecb077016a Mon Sep 17 00:00:00 2001 From: Carlo Cabrera <30379873+carlocab@users.noreply.github.com> Date: Tue, 4 Apr 2023 13:54:35 +0800 Subject: [PATCH 17/39] determine-test-runners: allow usage on macOS without `--dependents` --- .../dev-cmd/determine-test-runners.rb | 206 +++++++++++++++++- .../linux/dev-cmd/determine-test-runners.rb | 199 +---------------- .../dev-cmd/determine-test-runners_spec.rb | 30 ++- 3 files changed, 226 insertions(+), 209 deletions(-) diff --git a/Library/Homebrew/dev-cmd/determine-test-runners.rb b/Library/Homebrew/dev-cmd/determine-test-runners.rb index d75f5e6e7a..b015e06e21 100755 --- a/Library/Homebrew/dev-cmd/determine-test-runners.rb +++ b/Library/Homebrew/dev-cmd/determine-test-runners.rb @@ -2,6 +2,59 @@ # frozen_string_literal: true require "cli/parser" +require "formula" + +class TestRunnerFormula + extend T::Sig + + sig { returns(String) } + attr_reader :name + + sig { returns(Formula) } + attr_reader :formula + + sig { params(name: String).void } + def initialize(name) + @name = T.let(name, String) + @formula = T.let(Formula[name], Formula) + @dependent_hash = T.let({}, T::Hash[T::Boolean, T::Array[TestRunnerFormula]]) + freeze + end + + sig { returns(T::Boolean) } + def macos_only? + formula.requirements.any? { |r| r.is_a?(MacOSRequirement) && !r.version_specified? } + end + + sig { returns(T::Boolean) } + def linux_only? + formula.requirements.any?(LinuxRequirement) + end + + sig { returns(T::Boolean) } + def x86_64_only? + formula.requirements.any? { |r| r.is_a?(ArchRequirement) && (r.arch == :x86_64) } + end + + sig { returns(T::Boolean) } + def arm64_only? + formula.requirements.any? { |r| r.is_a?(ArchRequirement) && (r.arch == :arm64) } + end + + sig { returns(T.nilable(MacOSRequirement)) } + def versioned_macos_requirement + formula.requirements.find { |r| r.is_a?(MacOSRequirement) && r.version_specified? } + end + + sig { params(macos_version: MacOS::Version).returns(T::Boolean) } + def compatible_with?(macos_version) + # Assign to a variable to assist type-checking. + requirement = versioned_macos_requirement + return true if requirement.blank? + + macos_version.public_send(requirement.comparator, requirement.version) + end +end module Homebrew extend T::Sig @@ -15,7 +68,7 @@ module Homebrew Determines the runners used to test formulae or their dependents. EOS switch "--dependents", - description: "Determine runners for testing dependents." + description: "Determine runners for testing dependents. (requires Linux)" named_args min: 1, max: 2 @@ -23,9 +76,158 @@ module Homebrew end end + sig { + params( + _testing_formulae: T::Array[TestRunnerFormula], + reject_platform: T.nilable(Symbol), + reject_arch: T.nilable(Symbol), + select_macos_version: T.nilable(MacOS::Version), + ).void + } + def self.formulae_have_untested_dependents?(_testing_formulae, reject_platform:, + reject_arch:, select_macos_version:) + odie "`--dependents` is supported only on Linux!" + end + + sig { + params( + formulae: T::Array[TestRunnerFormula], + dependents: T::Boolean, + deleted_formulae: T.nilable(T::Array[String]), + reject_platform: T.nilable(Symbol), + reject_arch: T.nilable(Symbol), + select_macos_version: T.nilable(MacOS::Version), + ).returns(T::Boolean) + } + def self.add_runner?(formulae, + dependents:, + deleted_formulae:, + reject_platform: nil, + reject_arch: nil, + select_macos_version: nil) + if dependents + formulae_have_untested_dependents?( + formulae, + reject_platform: reject_platform, + reject_arch: reject_arch, + select_macos_version: select_macos_version, + ) + else + return true if deleted_formulae.present? + + compatible_formulae = formulae.dup + + compatible_formulae.reject! { |formula| formula.send(:"#{reject_platform}_only?") } if reject_platform + compatible_formulae.reject! { |formula| formula.send(:"#{reject_arch}_only?") } if reject_arch + compatible_formulae.select! { |formula| formula.compatible_with?(select_macos_version) } if select_macos_version + + compatible_formulae.present? + end + end + sig { void } def self.determine_test_runners - odie "This command is supported only on Linux!" + args = determine_test_runners_args.parse + testing_formulae = args.named.first.split(",") + testing_formulae.map! { |name| TestRunnerFormula.new(name) } + .freeze + deleted_formulae = args.named.second&.split(",") + + runners = [] + + linux_runner = ENV.fetch("HOMEBREW_LINUX_RUNNER") { raise "HOMEBREW_LINUX_RUNNER is not defined" } + linux_cleanup = ENV.fetch("HOMEBREW_LINUX_CLEANUP") { raise "HOMEBREW_LINUX_CLEANUP is not defined" } + + linux_runner_spec = { + runner: linux_runner, + container: { + image: "ghcr.io/homebrew/ubuntu22.04:master", + options: "--user=linuxbrew -e GITHUB_ACTIONS_HOMEBREW_SELF_HOSTED", + }, + workdir: "/github/home", + timeout: 4320, + cleanup: linux_cleanup == "true", + } + + with_env(HOMEBREW_SIMULATE_MACOS_ON_LINUX: nil) do + if add_runner?( + testing_formulae, + reject_platform: :macos, + reject_arch: :arm64, + deleted_formulae: deleted_formulae, + dependents: args.dependents?, + ) + runners << linux_runner_spec + end + end + + # TODO: `HOMEBREW_SIMULATE_MACOS_ON_LINUX` simulates the oldest version of macOS. + # Handle formulae that are dependents only on new versions of macOS. + with_env(HOMEBREW_SIMULATE_MACOS_ON_LINUX: "1") do + if add_runner?( + testing_formulae, + reject_platform: :linux, + deleted_formulae: deleted_formulae, + dependents: args.dependents?, + ) + add_intel_runners = add_runner?( + testing_formulae, + reject_platform: :linux, + reject_arch: :arm64, + deleted_formulae: deleted_formulae, + dependents: args.dependents?, + ) + add_m1_runners = add_runner?( + testing_formulae, + reject_platform: :linux, + reject_arch: :x86_64, + deleted_formulae: deleted_formulae, + dependents: args.dependents?, + ) + + github_run_id = ENV.fetch("GITHUB_RUN_ID") { raise "GITHUB_RUN_ID is not defined" } + github_run_attempt = ENV.fetch("GITHUB_RUN_ATTEMPT") { raise "GITHUB_RUN_ATTEMPT is not defined" } + + MacOSVersions::SYMBOLS.each_value do |version| + macos_version = MacOS::Version.new(version) + next if macos_version.outdated_release? || macos_version.prerelease? + + unless add_runner?( + testing_formulae, + reject_platform: :linux, + select_macos_version: macos_version, + deleted_formulae: deleted_formulae, + dependents: args.dependents?, + ) + next # No formulae to test on this macOS version. + end + + ephemeral_suffix = "-#{github_run_id}-#{github_run_attempt}" + runners << { runner: "#{macos_version}#{ephemeral_suffix}", cleanup: false } if add_intel_runners + + next unless add_m1_runners + + # Use bare metal runner when testing dependents on Monterey. + if macos_version >= :ventura || (macos_version >= :monterey && !args.dependents?) + runners << { runner: "#{macos_version}-arm64#{ephemeral_suffix}", cleanup: false } + elsif macos_version >= :big_sur + runners << { runner: "#{macos_version}-arm64", cleanup: true } + end + end + end + end + + if !args.dependents? && runners.blank? + # If there are no tests to run, add a runner that is meant to do nothing + # to support making the `tests` job a required status check. + runners << { runner: "ubuntu-latest", no_op: true } + end + + github_output = ENV.fetch("GITHUB_OUTPUT") { raise "GITHUB_OUTPUT is not defined" } + File.open(github_output, "a") do |f| + f.puts("runners=#{runners.to_json}") + f.puts("runners_present=#{runners.present?}") + end end end diff --git a/Library/Homebrew/extend/os/linux/dev-cmd/determine-test-runners.rb b/Library/Homebrew/extend/os/linux/dev-cmd/determine-test-runners.rb index f77bf20fd6..2e2828e5de 100755 --- a/Library/Homebrew/extend/os/linux/dev-cmd/determine-test-runners.rb +++ b/Library/Homebrew/extend/os/linux/dev-cmd/determine-test-runners.rb @@ -1,59 +1,9 @@ # typed: strict # frozen_string_literal: true -require "formula" - class TestRunnerFormula extend T::Sig - sig { returns(String) } - attr_reader :name - - sig { returns(Formula) } - attr_reader :formula - - sig { params(name: String).void } - def initialize(name) - @name = T.let(name, String) - @formula = T.let(Formula[name], Formula) - @dependent_hash = T.let({}, T::Hash[T::Boolean, T::Array[TestRunnerFormula]]) - freeze - end - - sig { returns(T::Boolean) } - def macos_only? - formula.requirements.any? { |r| r.is_a?(MacOSRequirement) && !r.version_specified? } - end - - sig { returns(T::Boolean) } - def linux_only? - formula.requirements.any?(LinuxRequirement) - end - - sig { returns(T::Boolean) } - def x86_64_only? - formula.requirements.any? { |r| r.is_a?(ArchRequirement) && (r.arch == :x86_64) } - end - - sig { returns(T::Boolean) } - def arm64_only? - formula.requirements.any? { |r| r.is_a?(ArchRequirement) && (r.arch == :arm64) } - end - - sig { returns(T.nilable(MacOSRequirement)) } - def versioned_macos_requirement - formula.requirements.find { |r| r.is_a?(MacOSRequirement) && r.version_specified? } - end - - sig { params(macos_version: MacOS::Version).returns(T::Boolean) } - def compatible_with?(macos_version) - # Assign to a variable to assist type-checking. - requirement = versioned_macos_requirement - return true if requirement.blank? - - macos_version.public_send(requirement.comparator, requirement.version) - end - sig { returns(T::Array[TestRunnerFormula]) } def dependents @dependent_hash[ENV["HOMEBREW_SIMULATE_MACOS_ON_LINUX"].present?] ||= with_env(HOMEBREW_STDERR: "1") do @@ -87,12 +37,8 @@ module Homebrew compatible_dependents = formula.dependents.dup - compatible_dependents.reject! { |dependent_f| dependent_f.send(:"#{reject_arch}_only?")} if reject_arch - - if reject_platform - compatible_dependents.reject! { |dependent_f| dependent_f.send(:"#{reject_platform}_only?") } - end - + compatible_dependents.reject! { |dependent_f| dependent_f.send(:"#{reject_arch}_only?") } if reject_arch + compatible_dependents.reject! { |dependent_f| dependent_f.send(:"#{reject_platform}_only?") } if reject_platform if select_macos_version compatible_dependents.select! { |dependent_f| dependent_f.compatible_with?(select_macos_version) } end @@ -100,145 +46,4 @@ module Homebrew (compatible_dependents - testing_formulae).present? end end - - sig { - params( - formulae: T::Array[TestRunnerFormula], - dependents: T::Boolean, - deleted_formulae: T.nilable(T::Array[String]), - reject_platform: T.nilable(Symbol), - reject_arch: T.nilable(Symbol), - select_macos_version: T.nilable(MacOS::Version), - ).returns(T::Boolean) - } - def self.add_runner?(formulae, - dependents:, - deleted_formulae:, - reject_platform: nil, - reject_arch: nil, - select_macos_version: nil) - if dependents - formulae_have_untested_dependents?( - formulae, - reject_platform: reject_platform, - reject_arch: reject_arch, - select_macos_version: select_macos_version, - ) - else - return true if deleted_formulae.present? - - compatible_formulae = formulae.dup - - compatible_formulae.reject! { |formula| formula.send(:"#{reject_platform}_only?") } if reject_platform - compatible_formulae.reject! { |formula| formula.send(:"#{reject_arch}_only?") } if reject_arch - compatible_formulae.select! { |formula| formula.compatible_with?(select_macos_version) } if select_macos_version - - compatible_formulae.present? - end - end - - sig { void } - def self.determine_test_runners - args = determine_test_runners_args.parse - testing_formulae = args.named.first.split(",") - testing_formulae.map! { |name| TestRunnerFormula.new(name) } - .freeze - deleted_formulae = args.named.second&.split(",") - - runners = [] - - linux_runner = ENV.fetch("HOMEBREW_LINUX_RUNNER") { raise "HOMEBREW_LINUX_RUNNER is not defined" } - linux_cleanup = ENV.fetch("HOMEBREW_LINUX_CLEANUP") { raise "HOMEBREW_LINUX_CLEANUP is not defined" } - - linux_runner_spec = { - runner: linux_runner, - container: { - image: "ghcr.io/homebrew/ubuntu22.04:master", - options: "--user=linuxbrew -e GITHUB_ACTIONS_HOMEBREW_SELF_HOSTED", - }, - workdir: "/github/home", - timeout: 4320, - cleanup: linux_cleanup == "true", - } - - with_env(HOMEBREW_SIMULATE_MACOS_ON_LINUX: nil) do - if add_runner?( - testing_formulae, - reject_platform: :macos, - reject_arch: :arm64, - deleted_formulae: deleted_formulae, - dependents: args.dependents?, - ) - runners << linux_runner_spec - end - end - - # TODO: `HOMEBREW_SIMULATE_MACOS_ON_LINUX` simulates the oldest version of macOS. - # Handle formulae that are dependents only on new versions of macOS. - with_env(HOMEBREW_SIMULATE_MACOS_ON_LINUX: "1") do - if add_runner?( - testing_formulae, - reject_platform: :linux, - deleted_formulae: deleted_formulae, - dependents: args.dependents?, - ) - add_intel_runners = add_runner?( - testing_formulae, - reject_platform: :linux, - reject_arch: :arm64, - deleted_formulae: deleted_formulae, - dependents: args.dependents?, - ) - add_m1_runners = add_runner?( - testing_formulae, - reject_platform: :linux, - reject_arch: :x86_64, - deleted_formulae: deleted_formulae, - dependents: args.dependents?, - ) - - github_run_id = ENV.fetch("GITHUB_RUN_ID") { raise "GITHUB_RUN_ID is not defined" } - github_run_attempt = ENV.fetch("GITHUB_RUN_ATTEMPT") { raise "GITHUB_RUN_ATTEMPT is not defined" } - - MacOSVersions::SYMBOLS.each_value do |version| - macos_version = MacOS::Version.new(version) - next if macos_version.outdated_release? || macos_version.prerelease? - - unless add_runner?( - testing_formulae, - reject_platform: :linux, - select_macos_version: macos_version, - deleted_formulae: deleted_formulae, - dependents: args.dependents?, - ) - next # No formulae to test on this macOS version. - end - - ephemeral_suffix = "-#{github_run_id}-#{github_run_attempt}" - runners << { runner: "#{macos_version}#{ephemeral_suffix}", cleanup: false } if add_intel_runners - - next unless add_m1_runners - - # Use bare metal runner when testing dependents on Monterey. - if macos_version >= :ventura || (macos_version >= :monterey && !args.dependents?) - runners << { runner: "#{macos_version}-arm64#{ephemeral_suffix}", cleanup: false } - elsif macos_version >= :big_sur - runners << { runner: "#{macos_version}-arm64", cleanup: true } - end - end - end - end - - if !args.dependents? && runners.blank? - # If there are no tests to run, add a runner that is meant to do nothing - # to support making the `tests` job a required status check. - runners << { runner: "ubuntu-latest", no_op: true } - end - - github_output = ENV.fetch("GITHUB_OUTPUT") { raise "GITHUB_OUTPUT is not defined" } - File.open(github_output, "a") do |f| - f.puts("runners=#{runners.to_json}") - f.puts("runners_present=#{runners.present?}") - end - end end diff --git a/Library/Homebrew/test/dev-cmd/determine-test-runners_spec.rb b/Library/Homebrew/test/dev-cmd/determine-test-runners_spec.rb index d26c01b159..0aa6ee8927 100644 --- a/Library/Homebrew/test/dev-cmd/determine-test-runners_spec.rb +++ b/Library/Homebrew/test/dev-cmd/determine-test-runners_spec.rb @@ -35,7 +35,7 @@ describe "brew determine-test-runners" do .and be_a_failure end - it "fails when the necessary environment variables are missing", :integration_test, :needs_linux do + it "fails when the necessary environment variables are missing", :integration_test do setup_test_formula "testball" runner_env.each_key do |k| @@ -49,7 +49,7 @@ describe "brew determine-test-runners" do end end - it "assigns all runners for formulae without any requirements", :integration_test, :needs_linux do + it "assigns all runners for formulae without any requirements", :integration_test do setup_test_formula "testball" expect { brew "determine-test-runners", "testball", runner_env.merge({ "GITHUB_OUTPUT" => github_output }) } @@ -61,7 +61,7 @@ describe "brew determine-test-runners" do expect(get_runners(github_output)).to eq(all_runners) end - it "assigns all runners when there are deleted formulae", :integration_test, :needs_linux do + it "assigns all runners when there are deleted formulae", :integration_test do expect { brew "determine-test-runners", "", "testball", runner_env.merge({ "GITHUB_OUTPUT" => github_output }) } .to not_to_output.to_stdout .and not_to_output.to_stderr @@ -71,8 +71,7 @@ describe "brew determine-test-runners" do expect(get_runners(github_output)).to eq(all_runners) end - it "assigns `ubuntu-latest` when there are no testing formulae and no deleted formulae", :integration_test, - :needs_linux do + it "assigns `ubuntu-latest` when there are no testing formulae and no deleted formulae", :integration_test do expect { brew "determine-test-runners", "", runner_env.merge({ "GITHUB_OUTPUT" => github_output }) } .to not_to_output.to_stdout .and not_to_output.to_stderr @@ -82,7 +81,7 @@ describe "brew determine-test-runners" do expect(get_runners(github_output)).to eq(["ubuntu-latest"]) end - it "assigns only Intel runners when a formula `depends_on arch: :x86_64`", :integration_test, :needs_linux do + it "assigns only Intel runners when a formula `depends_on arch: :x86_64`", :integration_test do setup_test_formula "intel_depender", <<~RUBY url "https://brew.sh/intel_depender-1.0.tar.gz" depends_on arch: :x86_64 @@ -97,7 +96,7 @@ describe "brew determine-test-runners" do expect(get_runners(github_output)).to eq(intel_runners) end - it "assigns only ARM64 runners when a formula `depends_on arch: :arm64`", :integration_test, :needs_linux do + it "assigns only ARM64 runners when a formula `depends_on arch: :arm64`", :integration_test do setup_test_formula "fancy-m1-ml-framework", <<~RUBY url "https://brew.sh/fancy-m1-ml-framework-1.0.tar.gz" depends_on arch: :arm64 @@ -114,7 +113,7 @@ describe "brew determine-test-runners" do expect(get_runners(github_output)).to eq(arm64_runners) end - it "assigns only macOS runners when a formula `depends_on :macos`", :integration_test, :needs_linux do + it "assigns only macOS runners when a formula `depends_on :macos`", :integration_test do setup_test_formula "xcode-helper", <<~RUBY url "https://brew.sh/xcode-helper-1.0.tar.gz" depends_on :macos @@ -129,7 +128,7 @@ describe "brew determine-test-runners" do expect(get_runners(github_output)).to eq(macos_runners) end - it "assigns only Linux runners when a formula `depends_on :linux`", :integration_test, :needs_linux do + it "assigns only Linux runners when a formula `depends_on :linux`", :integration_test do setup_test_formula "linux-kernel-requirer", <<~RUBY url "https://brew.sh/linux-kernel-requirer-1.0.tar.gz" depends_on :linux @@ -147,7 +146,7 @@ describe "brew determine-test-runners" do end # TODO: Keep this updated to use the newest supported macOS version. - it "assigns only compatible runners when there is a versioned macOS requirement", :integration_test, :needs_linux do + it "assigns only compatible runners when there is a versioned macOS requirement", :integration_test do setup_test_formula "needs-macos-13", <<~RUBY url "https://brew.sh/needs-macos-13-1.0.tar.gz" depends_on macos: :ventura @@ -162,6 +161,17 @@ describe "brew determine-test-runners" do expect(get_runners(github_output)).to eq(["13", "13-arm64", linux_runner]) expect(get_runners(github_output)).not_to eq(all_runners) end + + describe "--dependents" do + it "fails on macOS", :integration_test, :needs_macos do + setup_test_formula "testball" + + expect { brew "determine-test-runners", "--dependents", "testball", runner_env.dup } + .to not_to_output.to_stdout + .and output("Error: `--dependents` is supported only on Linux!\n").to_stderr + .and be_a_failure + end + end end def get_runners(file) From 15061cec70ba29ba98cea7dd654c1182bd77ea50 Mon Sep 17 00:00:00 2001 From: Carlo Cabrera <30379873+carlocab@users.noreply.github.com> Date: Tue, 4 Apr 2023 14:54:49 +0800 Subject: [PATCH 18/39] Update completions. Generated with `brew generate-man-completions` --- completions/fish/brew.fish | 2 +- completions/zsh/_brew | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/completions/fish/brew.fish b/completions/fish/brew.fish index 188a4b9f94..c281613236 100644 --- a/completions/fish/brew.fish +++ b/completions/fish/brew.fish @@ -615,7 +615,7 @@ __fish_brew_complete_arg 'desc; and not __fish_seen_argument -l formula -l formu __fish_brew_complete_cmd 'determine-test-runners' 'Determines the runners used to test formulae or their dependents' __fish_brew_complete_arg 'determine-test-runners' -l debug -d 'Display any debugging information' -__fish_brew_complete_arg 'determine-test-runners' -l dependents -d 'Determine runners for testing dependents' +__fish_brew_complete_arg 'determine-test-runners' -l dependents -d 'Determine runners for testing dependents. (requires Linux)' __fish_brew_complete_arg 'determine-test-runners' -l help -d 'Show this message' __fish_brew_complete_arg 'determine-test-runners' -l quiet -d 'Make some output more quiet' __fish_brew_complete_arg 'determine-test-runners' -l verbose -d 'Make some output more verbose' diff --git a/completions/zsh/_brew b/completions/zsh/_brew index 6e36809e5f..7e1624332a 100644 --- a/completions/zsh/_brew +++ b/completions/zsh/_brew @@ -760,7 +760,7 @@ _brew_desc() { _brew_determine_test_runners() { _arguments \ '--debug[Display any debugging information]' \ - '--dependents[Determine runners for testing dependents]' \ + '--dependents[Determine runners for testing dependents. (requires Linux)]' \ '--help[Show this message]' \ '--quiet[Make some output more quiet]' \ '--verbose[Make some output more verbose]' From 506f6c81a73b64486fce4743b205ffc93014f32a Mon Sep 17 00:00:00 2001 From: Carlo Cabrera <30379873+carlocab@users.noreply.github.com> Date: Tue, 4 Apr 2023 16:19:46 +0800 Subject: [PATCH 19/39] Support `--dependents` flag on macOS --- .../dev-cmd/determine-test-runners.rb | 169 ++++++++++-------- .../os/dev-cmd/determine-test-runners.rb | 4 - .../linux/dev-cmd/determine-test-runners.rb | 49 ----- .../dev-cmd/determine-test-runners_spec.rb | 14 +- 4 files changed, 107 insertions(+), 129 deletions(-) delete mode 100644 Library/Homebrew/extend/os/dev-cmd/determine-test-runners.rb delete mode 100755 Library/Homebrew/extend/os/linux/dev-cmd/determine-test-runners.rb diff --git a/Library/Homebrew/dev-cmd/determine-test-runners.rb b/Library/Homebrew/dev-cmd/determine-test-runners.rb index b015e06e21..eb68453307 100755 --- a/Library/Homebrew/dev-cmd/determine-test-runners.rb +++ b/Library/Homebrew/dev-cmd/determine-test-runners.rb @@ -13,11 +13,11 @@ class TestRunnerFormula sig { returns(Formula) } attr_reader :formula - sig { params(name: String).void } - def initialize(name) - @name = T.let(name, String) - @formula = T.let(Formula[name], Formula) - @dependent_hash = T.let({}, T::Hash[T::Boolean, T::Array[TestRunnerFormula]]) + sig { params(formula: Formula).void } + def initialize(formula) + @formula = T.let(formula, Formula) + @name = T.let(formula.name, String) + @dependent_hash = T.let({}, T::Hash[Symbol, T::Array[TestRunnerFormula]]) freeze end @@ -54,6 +54,19 @@ class TestRunnerFormula macos_version.public_send(requirement.comparator, requirement.version) end + + sig { params(cache_key: Symbol).returns(T::Array[TestRunnerFormula]) } + def dependents(cache_key) + # TODO: Check that `--eval-all` or `HOMEBREW_EVAL_ALL` is set. + @dependent_hash[cache_key] ||= with_env(HOMEBREW_EVAL_ALL: "1") do + Formula.all + .select { |candidate_f| candidate_f.deps.map(&:name).include?(name) } + .map { |f| TestRunnerFormula.new(f) } + .freeze + end + + @dependent_hash.fetch(cache_key) + end end module Homebrew @@ -68,25 +81,39 @@ module Homebrew Determines the runners used to test formulae or their dependents. EOS switch "--dependents", - description: "Determine runners for testing dependents. (requires Linux)" + description: "Determine runners for testing dependents." named_args min: 1, max: 2 hide_from_man_page! end end - sig { params( - _testing_formulae: T::Array[TestRunnerFormula], + testing_formulae: T::Array[TestRunnerFormula], reject_platform: T.nilable(Symbol), reject_arch: T.nilable(Symbol), select_macos_version: T.nilable(MacOS::Version), - ).void + ).returns(T::Boolean) } - def self.formulae_have_untested_dependents?(_testing_formulae, reject_platform:, - reject_arch:, select_macos_version:) - odie "`--dependents` is supported only on Linux!" + def self.formulae_have_untested_dependents?(testing_formulae, reject_platform:, reject_arch:, select_macos_version:) + testing_formulae.any? do |formula| + # If the formula has a platform/arch/macOS version requirement, then its + # dependents don't need to be tested if these requirements are not satisfied. + next false if reject_platform && formula.send(:"#{reject_platform}_only?") + next false if reject_arch && formula.send(:"#{reject_arch}_only?") + next false if select_macos_version && !formula.compatible_with?(select_macos_version) + + compatible_dependents = formula.dependents(:"#{reject_platform}_#{reject_arch}_#{select_macos_version}").dup + + compatible_dependents.reject! { |dependent_f| dependent_f.send(:"#{reject_arch}_only?") } if reject_arch + compatible_dependents.reject! { |dependent_f| dependent_f.send(:"#{reject_platform}_only?") } if reject_platform + if select_macos_version + compatible_dependents.select! { |dependent_f| dependent_f.compatible_with?(select_macos_version) } + end + + (compatible_dependents - testing_formulae).present? + end end sig { @@ -128,8 +155,11 @@ module Homebrew sig { void } def self.determine_test_runners args = determine_test_runners_args.parse + + Formulary.enable_factory_cache! + testing_formulae = args.named.first.split(",") - testing_formulae.map! { |name| TestRunnerFormula.new(name) } + testing_formulae.map! { |name| TestRunnerFormula.new(Formula[name]) } .freeze deleted_formulae = args.named.second&.split(",") @@ -149,71 +179,68 @@ module Homebrew cleanup: linux_cleanup == "true", } - with_env(HOMEBREW_SIMULATE_MACOS_ON_LINUX: nil) do - if add_runner?( - testing_formulae, - reject_platform: :macos, - reject_arch: :arm64, - deleted_formulae: deleted_formulae, - dependents: args.dependents?, - ) - runners << linux_runner_spec - end + if args.dependents? + Homebrew::SimulateSystem.os = :linux + Homebrew::SimulateSystem.arch = :intel + Formulary.clear_cache end - # TODO: `HOMEBREW_SIMULATE_MACOS_ON_LINUX` simulates the oldest version of macOS. - # Handle formulae that are dependents only on new versions of macOS. - with_env(HOMEBREW_SIMULATE_MACOS_ON_LINUX: "1") do + if add_runner?( + testing_formulae, + reject_platform: :macos, + reject_arch: :arm64, + deleted_formulae: deleted_formulae, + dependents: args.dependents?, + ) + runners << linux_runner_spec + end + + github_run_id = ENV.fetch("GITHUB_RUN_ID") { raise "GITHUB_RUN_ID is not defined" } + github_run_attempt = ENV.fetch("GITHUB_RUN_ATTEMPT") { raise "GITHUB_RUN_ATTEMPT is not defined" } + ephemeral_suffix = "-#{github_run_id}-#{github_run_attempt}" + + MacOSVersions::SYMBOLS.each do |symbol, version| + macos_version = MacOS::Version.new(version) + next if macos_version.outdated_release? || macos_version.prerelease? + + if args.dependents? + Formulary.clear_cache + Homebrew::SimulateSystem.os = symbol + Homebrew::SimulateSystem.arch = :intel + end + if add_runner?( testing_formulae, - reject_platform: :linux, - deleted_formulae: deleted_formulae, - dependents: args.dependents?, + reject_platform: :linux, + reject_arch: :arm64, + select_macos_version: macos_version, + deleted_formulae: deleted_formulae, + dependents: args.dependents?, ) - add_intel_runners = add_runner?( - testing_formulae, - reject_platform: :linux, - reject_arch: :arm64, - deleted_formulae: deleted_formulae, - dependents: args.dependents?, - ) - add_m1_runners = add_runner?( - testing_formulae, - reject_platform: :linux, - reject_arch: :x86_64, - deleted_formulae: deleted_formulae, - dependents: args.dependents?, - ) + runners << { runner: "#{version}#{ephemeral_suffix}", cleanup: false } + end - github_run_id = ENV.fetch("GITHUB_RUN_ID") { raise "GITHUB_RUN_ID is not defined" } - github_run_attempt = ENV.fetch("GITHUB_RUN_ATTEMPT") { raise "GITHUB_RUN_ATTEMPT is not defined" } + if args.dependents? + Formulary.clear_cache + Homebrew::SimulateSystem.os = symbol + Homebrew::SimulateSystem.arch = :arm + end - MacOSVersions::SYMBOLS.each_value do |version| - macos_version = MacOS::Version.new(version) - next if macos_version.outdated_release? || macos_version.prerelease? + next unless add_runner?( + testing_formulae, + reject_platform: :linux, + reject_arch: :x86_64, + select_macos_version: macos_version, + deleted_formulae: deleted_formulae, + dependents: args.dependents?, + ) - unless add_runner?( - testing_formulae, - reject_platform: :linux, - select_macos_version: macos_version, - deleted_formulae: deleted_formulae, - dependents: args.dependents?, - ) - next # No formulae to test on this macOS version. - end - - ephemeral_suffix = "-#{github_run_id}-#{github_run_attempt}" - runners << { runner: "#{macos_version}#{ephemeral_suffix}", cleanup: false } if add_intel_runners - - next unless add_m1_runners - - # Use bare metal runner when testing dependents on Monterey. - if macos_version >= :ventura || (macos_version >= :monterey && !args.dependents?) - runners << { runner: "#{macos_version}-arm64#{ephemeral_suffix}", cleanup: false } - elsif macos_version >= :big_sur - runners << { runner: "#{macos_version}-arm64", cleanup: true } - end - end + runner_name = "#{version}-arm64" + # Use bare metal runner when testing dependents on Monterey. + if macos_version >= :ventura || (macos_version >= :monterey && !args.dependents?) + runners << { runner: "#{runner_name}#{ephemeral_suffix}", cleanup: false } + elsif macos_version >= :big_sur + runners << { runner: runner_name, cleanup: true } end end @@ -230,5 +257,3 @@ module Homebrew end end end - -require "extend/os/dev-cmd/determine-test-runners" diff --git a/Library/Homebrew/extend/os/dev-cmd/determine-test-runners.rb b/Library/Homebrew/extend/os/dev-cmd/determine-test-runners.rb deleted file mode 100644 index 9d6f300d7c..0000000000 --- a/Library/Homebrew/extend/os/dev-cmd/determine-test-runners.rb +++ /dev/null @@ -1,4 +0,0 @@ -# typed: strict -# frozen_string_literal: true - -require "extend/os/linux/dev-cmd/determine-test-runners" if OS.linux? diff --git a/Library/Homebrew/extend/os/linux/dev-cmd/determine-test-runners.rb b/Library/Homebrew/extend/os/linux/dev-cmd/determine-test-runners.rb deleted file mode 100755 index 2e2828e5de..0000000000 --- a/Library/Homebrew/extend/os/linux/dev-cmd/determine-test-runners.rb +++ /dev/null @@ -1,49 +0,0 @@ -# typed: strict -# frozen_string_literal: true - -class TestRunnerFormula - extend T::Sig - - sig { returns(T::Array[TestRunnerFormula]) } - def dependents - @dependent_hash[ENV["HOMEBREW_SIMULATE_MACOS_ON_LINUX"].present?] ||= with_env(HOMEBREW_STDERR: "1") do - Utils.safe_popen_read( - HOMEBREW_BREW_FILE, "uses", "--formulae", "--eval-all", "--include-build", "--include-test", name - ).split("\n").map { |dependent| TestRunnerFormula.new(dependent) }.freeze - end - - @dependent_hash.fetch(ENV["HOMEBREW_SIMULATE_MACOS_ON_LINUX"].present?) - end -end - -module Homebrew - extend T::Sig - - sig { - params( - testing_formulae: T::Array[TestRunnerFormula], - reject_platform: T.nilable(Symbol), - reject_arch: T.nilable(Symbol), - select_macos_version: T.nilable(MacOS::Version), - ).returns(T::Boolean) - } - def self.formulae_have_untested_dependents?(testing_formulae, reject_platform:, reject_arch:, select_macos_version:) - testing_formulae.any? do |formula| - # If the formula has a platform/arch/macOS version requirement, then its - # dependents don't need to be tested if these requirements are not satisfied. - next false if reject_platform && formula.send(:"#{reject_platform}_only?") - next false if reject_arch && formula.send(:"#{reject_arch}_only?") - next false if select_macos_version && !formula.compatible_with?(select_macos_version) - - compatible_dependents = formula.dependents.dup - - compatible_dependents.reject! { |dependent_f| dependent_f.send(:"#{reject_arch}_only?") } if reject_arch - compatible_dependents.reject! { |dependent_f| dependent_f.send(:"#{reject_platform}_only?") } if reject_platform - if select_macos_version - compatible_dependents.select! { |dependent_f| dependent_f.compatible_with?(select_macos_version) } - end - - (compatible_dependents - testing_formulae).present? - end - end -end diff --git a/Library/Homebrew/test/dev-cmd/determine-test-runners_spec.rb b/Library/Homebrew/test/dev-cmd/determine-test-runners_spec.rb index 0aa6ee8927..6f6d870f47 100644 --- a/Library/Homebrew/test/dev-cmd/determine-test-runners_spec.rb +++ b/Library/Homebrew/test/dev-cmd/determine-test-runners_spec.rb @@ -163,13 +163,19 @@ describe "brew determine-test-runners" do end describe "--dependents" do - it "fails on macOS", :integration_test, :needs_macos do + it "assignes no runners for formulae with no dependents", :integration_test do setup_test_formula "testball" - expect { brew "determine-test-runners", "--dependents", "testball", runner_env.dup } + expect do + brew "determine-test-runners", "--dependents", "testball", + runner_env.merge({ "GITHUB_OUTPUT" => github_output }) + end .to not_to_output.to_stdout - .and output("Error: `--dependents` is supported only on Linux!\n").to_stderr - .and be_a_failure + .and not_to_output.to_stderr + .and be_a_success + + expect(File.read(github_output)).not_to be_empty + expect(get_runners(github_output)).to eq([]) end end end From fdbb7f37c250b6fc39d0621ed7c0ac2969fc1f01 Mon Sep 17 00:00:00 2001 From: Carlo Cabrera <30379873+carlocab@users.noreply.github.com> Date: Tue, 4 Apr 2023 16:21:05 +0800 Subject: [PATCH 20/39] Update completions. Generated with `brew generate-man-completions` --- completions/fish/brew.fish | 2 +- completions/zsh/_brew | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/completions/fish/brew.fish b/completions/fish/brew.fish index c281613236..188a4b9f94 100644 --- a/completions/fish/brew.fish +++ b/completions/fish/brew.fish @@ -615,7 +615,7 @@ __fish_brew_complete_arg 'desc; and not __fish_seen_argument -l formula -l formu __fish_brew_complete_cmd 'determine-test-runners' 'Determines the runners used to test formulae or their dependents' __fish_brew_complete_arg 'determine-test-runners' -l debug -d 'Display any debugging information' -__fish_brew_complete_arg 'determine-test-runners' -l dependents -d 'Determine runners for testing dependents. (requires Linux)' +__fish_brew_complete_arg 'determine-test-runners' -l dependents -d 'Determine runners for testing dependents' __fish_brew_complete_arg 'determine-test-runners' -l help -d 'Show this message' __fish_brew_complete_arg 'determine-test-runners' -l quiet -d 'Make some output more quiet' __fish_brew_complete_arg 'determine-test-runners' -l verbose -d 'Make some output more verbose' diff --git a/completions/zsh/_brew b/completions/zsh/_brew index 7e1624332a..6e36809e5f 100644 --- a/completions/zsh/_brew +++ b/completions/zsh/_brew @@ -760,7 +760,7 @@ _brew_desc() { _brew_determine_test_runners() { _arguments \ '--debug[Display any debugging information]' \ - '--dependents[Determine runners for testing dependents. (requires Linux)]' \ + '--dependents[Determine runners for testing dependents]' \ '--help[Show this message]' \ '--quiet[Make some output more quiet]' \ '--verbose[Make some output more verbose]' From 575cb0263cb1428c63b9428bbd913ffde83bd317 Mon Sep 17 00:00:00 2001 From: Carlo Cabrera <30379873+carlocab@users.noreply.github.com> Date: Tue, 4 Apr 2023 22:34:17 +0800 Subject: [PATCH 21/39] determine-test-runners: fix generic OS test failures Without this, testing on the generic OS results in errors like Error: uninitialized constant Homebrew::MacOS --- Library/Homebrew/os.rb | 1 + Library/Homebrew/os/generic.rb | 8 ++++++++ Library/Homebrew/os/linux.rb | 2 -- Library/Homebrew/os/mac.rb | 2 -- 4 files changed, 9 insertions(+), 4 deletions(-) create mode 100644 Library/Homebrew/os/generic.rb diff --git a/Library/Homebrew/os.rb b/Library/Homebrew/os.rb index da59954324..97f849c131 100644 --- a/Library/Homebrew/os.rb +++ b/Library/Homebrew/os.rb @@ -55,6 +55,7 @@ module OS LINUX_PREFERRED_GCC_COMPILER_FORMULA = "gcc@11" # https://packages.ubuntu.com/jammy/gcc LINUX_PREFERRED_GCC_RUNTIME_FORMULA = "gcc" + require "os/generic" if OS.mac? require "os/mac" # Don't tell people to report issues on unsupported configurations. diff --git a/Library/Homebrew/os/generic.rb b/Library/Homebrew/os/generic.rb new file mode 100644 index 0000000000..516c74764b --- /dev/null +++ b/Library/Homebrew/os/generic.rb @@ -0,0 +1,8 @@ +# typed: strict +# frozen_string_literal: true + +module OS + module Mac + ::MacOS = OS::Mac + end +end diff --git a/Library/Homebrew/os/linux.rb b/Library/Homebrew/os/linux.rb index 2b5df8cfc3..a48836ded1 100644 --- a/Library/Homebrew/os/linux.rb +++ b/Library/Homebrew/os/linux.rb @@ -54,8 +54,6 @@ module OS module Mac module_function - ::MacOS = OS::Mac - raise "Loaded OS::Linux on generic OS!" if ENV["HOMEBREW_TEST_GENERIC_OS"] def version diff --git a/Library/Homebrew/os/mac.rb b/Library/Homebrew/os/mac.rb index b52a965130..f8e0f3bba7 100644 --- a/Library/Homebrew/os/mac.rb +++ b/Library/Homebrew/os/mac.rb @@ -13,8 +13,6 @@ module OS module_function - ::MacOS = OS::Mac - raise "Loaded OS::Mac on generic OS!" if ENV["HOMEBREW_TEST_GENERIC_OS"] VERSION = ENV.fetch("HOMEBREW_MACOS_VERSION").chomp.freeze From a4ebb96fe60205d9181ab9eff1fe2bfad23676ab Mon Sep 17 00:00:00 2001 From: Carlo Cabrera <30379873+carlocab@users.noreply.github.com> Date: Tue, 4 Apr 2023 23:10:51 +0800 Subject: [PATCH 22/39] Remove `MacOS` definition from generic OS We don't want to define `MacOS` in the generic OS, so let's make sure we refer to it as `OS::Mac` in the places we need it. This reverts commit 575cb0263cb1428c63b9428bbd913ffde83bd317. --- Library/Homebrew/dev-cmd/determine-test-runners.rb | 8 ++++---- Library/Homebrew/extend/on_system.rb | 4 ++-- Library/Homebrew/os.rb | 1 - Library/Homebrew/os/generic.rb | 8 -------- Library/Homebrew/os/linux.rb | 2 ++ Library/Homebrew/os/mac.rb | 2 ++ Library/Homebrew/requirements/macos_requirement.rb | 8 ++++---- 7 files changed, 14 insertions(+), 19 deletions(-) delete mode 100644 Library/Homebrew/os/generic.rb diff --git a/Library/Homebrew/dev-cmd/determine-test-runners.rb b/Library/Homebrew/dev-cmd/determine-test-runners.rb index eb68453307..19ae02829d 100755 --- a/Library/Homebrew/dev-cmd/determine-test-runners.rb +++ b/Library/Homebrew/dev-cmd/determine-test-runners.rb @@ -46,7 +46,7 @@ class TestRunnerFormula formula.requirements.find { |r| r.is_a?(MacOSRequirement) && r.version_specified? } end - sig { params(macos_version: MacOS::Version).returns(T::Boolean) } + sig { params(macos_version: OS::Mac::Version).returns(T::Boolean) } def compatible_with?(macos_version) # Assign to a variable to assist type-checking. requirement = versioned_macos_requirement @@ -93,7 +93,7 @@ module Homebrew testing_formulae: T::Array[TestRunnerFormula], reject_platform: T.nilable(Symbol), reject_arch: T.nilable(Symbol), - select_macos_version: T.nilable(MacOS::Version), + select_macos_version: T.nilable(OS::Mac::Version), ).returns(T::Boolean) } def self.formulae_have_untested_dependents?(testing_formulae, reject_platform:, reject_arch:, select_macos_version:) @@ -123,7 +123,7 @@ module Homebrew deleted_formulae: T.nilable(T::Array[String]), reject_platform: T.nilable(Symbol), reject_arch: T.nilable(Symbol), - select_macos_version: T.nilable(MacOS::Version), + select_macos_version: T.nilable(OS::Mac::Version), ).returns(T::Boolean) } def self.add_runner?(formulae, @@ -200,7 +200,7 @@ module Homebrew ephemeral_suffix = "-#{github_run_id}-#{github_run_attempt}" MacOSVersions::SYMBOLS.each do |symbol, version| - macos_version = MacOS::Version.new(version) + macos_version = OS::Mac::Version.new(version) next if macos_version.outdated_release? || macos_version.prerelease? if args.dependents? diff --git a/Library/Homebrew/extend/on_system.rb b/Library/Homebrew/extend/on_system.rb index 5d4c581f44..2fc253904c 100644 --- a/Library/Homebrew/extend/on_system.rb +++ b/Library/Homebrew/extend/on_system.rb @@ -28,13 +28,13 @@ module OnSystem return false if Homebrew::SimulateSystem.simulating_or_running_on_linux? - base_os = MacOS::Version.from_symbol(os_name) + base_os = OS::Mac::Version.from_symbol(os_name) current_os = if Homebrew::SimulateSystem.current_os == :macos # Assume the oldest macOS version when simulating a generic macOS version # Version::NULL is always treated as less than any other version. Version::NULL else - MacOS::Version.from_symbol(Homebrew::SimulateSystem.current_os) + OS::Mac::Version.from_symbol(Homebrew::SimulateSystem.current_os) end return current_os >= base_os if or_condition == :or_newer diff --git a/Library/Homebrew/os.rb b/Library/Homebrew/os.rb index 97f849c131..da59954324 100644 --- a/Library/Homebrew/os.rb +++ b/Library/Homebrew/os.rb @@ -55,7 +55,6 @@ module OS LINUX_PREFERRED_GCC_COMPILER_FORMULA = "gcc@11" # https://packages.ubuntu.com/jammy/gcc LINUX_PREFERRED_GCC_RUNTIME_FORMULA = "gcc" - require "os/generic" if OS.mac? require "os/mac" # Don't tell people to report issues on unsupported configurations. diff --git a/Library/Homebrew/os/generic.rb b/Library/Homebrew/os/generic.rb deleted file mode 100644 index 516c74764b..0000000000 --- a/Library/Homebrew/os/generic.rb +++ /dev/null @@ -1,8 +0,0 @@ -# typed: strict -# frozen_string_literal: true - -module OS - module Mac - ::MacOS = OS::Mac - end -end diff --git a/Library/Homebrew/os/linux.rb b/Library/Homebrew/os/linux.rb index a48836ded1..2b5df8cfc3 100644 --- a/Library/Homebrew/os/linux.rb +++ b/Library/Homebrew/os/linux.rb @@ -54,6 +54,8 @@ module OS module Mac module_function + ::MacOS = OS::Mac + raise "Loaded OS::Linux on generic OS!" if ENV["HOMEBREW_TEST_GENERIC_OS"] def version diff --git a/Library/Homebrew/os/mac.rb b/Library/Homebrew/os/mac.rb index f8e0f3bba7..b52a965130 100644 --- a/Library/Homebrew/os/mac.rb +++ b/Library/Homebrew/os/mac.rb @@ -13,6 +13,8 @@ module OS module_function + ::MacOS = OS::Mac + raise "Loaded OS::Mac on generic OS!" if ENV["HOMEBREW_TEST_GENERIC_OS"] VERSION = ENV.fetch("HOMEBREW_MACOS_VERSION").chomp.freeze diff --git a/Library/Homebrew/requirements/macos_requirement.rb b/Library/Homebrew/requirements/macos_requirement.rb index addba7473d..6aa4f90fb0 100644 --- a/Library/Homebrew/requirements/macos_requirement.rb +++ b/Library/Homebrew/requirements/macos_requirement.rb @@ -23,9 +23,9 @@ class MacOSRequirement < Requirement def initialize(tags = [], comparator: ">=") @version = begin if comparator == "==" && tags.first.respond_to?(:map) - tags.first.map { |s| MacOS::Version.from_symbol(s) } + tags.first.map { |s| OS::Mac::Version.from_symbol(s) } else - MacOS::Version.from_symbol(tags.first) unless tags.empty? + OS::Mac::Version.from_symbol(tags.first) unless tags.empty? end rescue MacOSVersionError => e if DISABLED_MACOS_VERSIONS.include?(e.version) @@ -43,7 +43,7 @@ class MacOSRequirement < Requirement end # Otherwise fallback to the oldest allowed if comparator is >=. - MacOS::Version.new(HOMEBREW_MACOS_OLDEST_ALLOWED) if comparator == ">=" + OS::Mac::Version.new(HOMEBREW_MACOS_OLDEST_ALLOWED) if comparator == ">=" end @comparator = comparator @@ -56,7 +56,7 @@ class MacOSRequirement < Requirement satisfy(build_env: false) do T.bind(self, MacOSRequirement) - next Array(@version).any? { |v| MacOS.version.public_send(@comparator, v) } if OS.mac? && version_specified? + next Array(@version).any? { |v| OS::Mac.version.public_send(@comparator, v) } if OS.mac? && version_specified? next true if OS.mac? next true if @version From 5194e5c65ed1588966670c4fdbc1adfe1e08cb44 Mon Sep 17 00:00:00 2001 From: Carlo Cabrera <30379873+carlocab@users.noreply.github.com> Date: Tue, 4 Apr 2023 23:58:32 +0800 Subject: [PATCH 23/39] Check for `--eval-all` before using `HOMEBREW_EVAL_ALL` --- .../dev-cmd/determine-test-runners.rb | 32 ++++++++++++++----- .../dev-cmd/determine-test-runners_spec.rb | 2 +- 2 files changed, 25 insertions(+), 9 deletions(-) diff --git a/Library/Homebrew/dev-cmd/determine-test-runners.rb b/Library/Homebrew/dev-cmd/determine-test-runners.rb index 19ae02829d..6059022a90 100755 --- a/Library/Homebrew/dev-cmd/determine-test-runners.rb +++ b/Library/Homebrew/dev-cmd/determine-test-runners.rb @@ -13,11 +13,15 @@ class TestRunnerFormula sig { returns(Formula) } attr_reader :formula - sig { params(formula: Formula).void } - def initialize(formula) + sig { returns(T::Boolean) } + attr_reader :eval_all + + sig { params(formula: Formula, eval_all: T::Boolean).void } + def initialize(formula, eval_all: Homebrew::EnvConfig.eval_all?) @formula = T.let(formula, Formula) @name = T.let(formula.name, String) @dependent_hash = T.let({}, T::Hash[Symbol, T::Array[TestRunnerFormula]]) + @eval_all = T.let(eval_all, T::Boolean) freeze end @@ -57,11 +61,16 @@ class TestRunnerFormula sig { params(cache_key: Symbol).returns(T::Array[TestRunnerFormula]) } def dependents(cache_key) - # TODO: Check that `--eval-all` or `HOMEBREW_EVAL_ALL` is set. - @dependent_hash[cache_key] ||= with_env(HOMEBREW_EVAL_ALL: "1") do - Formula.all + formula_selector, eval_all_env = if eval_all || Homebrew::EnvConfig.eval_all? + [:all, "1"] + else + [:installed, nil] + end + + @dependent_hash[cache_key] ||= with_env(HOMEBREW_EVAL_ALL: eval_all_env) do + Formula.send(formula_selector) .select { |candidate_f| candidate_f.deps.map(&:name).include?(name) } - .map { |f| TestRunnerFormula.new(f) } + .map { |f| TestRunnerFormula.new(f, eval_all: eval_all) } .freeze end @@ -80,8 +89,11 @@ module Homebrew Determines the runners used to test formulae or their dependents. EOS + switch "--eval-all", + description: "Evaluate all available formulae, whether installed or not, to determine testing " \ + "dependents." switch "--dependents", - description: "Determine runners for testing dependents." + description: "Determine runners for testing dependents. Requires `--eval-all` or `HOMEBREW_EVAL_ALL`." named_args min: 1, max: 2 @@ -156,10 +168,14 @@ module Homebrew def self.determine_test_runners args = determine_test_runners_args.parse + eval_all = args.eval_all? || Homebrew::EnvConfig.eval_all? + + odie "`--dependents` requires `--eval-all` or `HOMEBREW_EVAL_ALL`!" if args.dependents? && !eval_all + Formulary.enable_factory_cache! testing_formulae = args.named.first.split(",") - testing_formulae.map! { |name| TestRunnerFormula.new(Formula[name]) } + testing_formulae.map! { |name| TestRunnerFormula.new(Formula[name], eval_all: eval_all) } .freeze deleted_formulae = args.named.second&.split(",") diff --git a/Library/Homebrew/test/dev-cmd/determine-test-runners_spec.rb b/Library/Homebrew/test/dev-cmd/determine-test-runners_spec.rb index 6f6d870f47..f6da916c26 100644 --- a/Library/Homebrew/test/dev-cmd/determine-test-runners_spec.rb +++ b/Library/Homebrew/test/dev-cmd/determine-test-runners_spec.rb @@ -167,7 +167,7 @@ describe "brew determine-test-runners" do setup_test_formula "testball" expect do - brew "determine-test-runners", "--dependents", "testball", + brew "determine-test-runners", "--eval-all", "--dependents", "testball", runner_env.merge({ "GITHUB_OUTPUT" => github_output }) end .to not_to_output.to_stdout From 26726adc21a1db155f3dbfca54be1fa688d2cea0 Mon Sep 17 00:00:00 2001 From: Carlo Cabrera <30379873+carlocab@users.noreply.github.com> Date: Wed, 5 Apr 2023 01:09:56 +0800 Subject: [PATCH 24/39] determine-test-runners: refactor This supports extraction into separate modules/files. --- .../dev-cmd/determine-test-runners.rb | 168 ++++++++++-------- 1 file changed, 91 insertions(+), 77 deletions(-) diff --git a/Library/Homebrew/dev-cmd/determine-test-runners.rb b/Library/Homebrew/dev-cmd/determine-test-runners.rb index 6059022a90..a21cf98c98 100755 --- a/Library/Homebrew/dev-cmd/determine-test-runners.rb +++ b/Library/Homebrew/dev-cmd/determine-test-runners.rb @@ -30,21 +30,41 @@ class TestRunnerFormula formula.requirements.any? { |r| r.is_a?(MacOSRequirement) && !r.version_specified? } end + sig { returns(T::Boolean) } + def macos_compatible? + !linux_only? + end + sig { returns(T::Boolean) } def linux_only? formula.requirements.any?(LinuxRequirement) end + sig { returns(T::Boolean) } + def linux_compatible? + !macos_only? + end + sig { returns(T::Boolean) } def x86_64_only? formula.requirements.any? { |r| r.is_a?(ArchRequirement) && (r.arch == :x86_64) } end + sig { returns(T::Boolean) } + def x86_64_compatible? + !arm64_only? + end + sig { returns(T::Boolean) } def arm64_only? formula.requirements.any? { |r| r.is_a?(ArchRequirement) && (r.arch == :arm64) } end + sig { returns(T::Boolean) } + def arm64_compatible? + !x86_64_only? + end + sig { returns(T.nilable(MacOSRequirement)) } def versioned_macos_requirement formula.requirements.find { |r| r.is_a?(MacOSRequirement) && r.version_specified? } @@ -59,22 +79,39 @@ class TestRunnerFormula macos_version.public_send(requirement.comparator, requirement.version) end - sig { params(cache_key: Symbol).returns(T::Array[TestRunnerFormula]) } - def dependents(cache_key) - formula_selector, eval_all_env = if eval_all || Homebrew::EnvConfig.eval_all? - [:all, "1"] - else - [:installed, nil] - end + SIMULATE_SYSTEM_SYMBOLS = T.let({ arm64: :arm, x86_64: :intel }.freeze, T::Hash[Symbol, Symbol]) - @dependent_hash[cache_key] ||= with_env(HOMEBREW_EVAL_ALL: eval_all_env) do - Formula.send(formula_selector) - .select { |candidate_f| candidate_f.deps.map(&:name).include?(name) } - .map { |f| TestRunnerFormula.new(f, eval_all: eval_all) } - .freeze - end + sig { + params( + platform: Symbol, + arch: Symbol, + macos_version: T.nilable(Symbol), + ).returns(T::Array[TestRunnerFormula]) + } + def dependents(platform:, arch:, macos_version:) + cache_key = :"#{platform}_#{arch}_#{macos_version}" - @dependent_hash.fetch(cache_key) + @dependent_hash.fetch(cache_key) do + all = eval_all || Homebrew::EnvConfig.eval_all? + formula_selector, eval_all_env = if all + [:all, "1"] + else + [:installed, nil] + end + + with_env(HOMEBREW_EVAL_ALL: eval_all_env) do + Formulary.clear_cache + Homebrew::SimulateSystem.arch = SIMULATE_SYSTEM_SYMBOLS.fetch(arch) + Homebrew::SimulateSystem.os = macos_version || platform + + Formula.send(formula_selector) + .select { |candidate_f| candidate_f.deps.map(&:name).include?(name) } + .map { |f| TestRunnerFormula.new(f, eval_all: all) } + .freeze + ensure + Homebrew::SimulateSystem.clear + end + end end end @@ -100,29 +137,29 @@ module Homebrew hide_from_man_page! end end + sig { params( - testing_formulae: T::Array[TestRunnerFormula], - reject_platform: T.nilable(Symbol), - reject_arch: T.nilable(Symbol), - select_macos_version: T.nilable(OS::Mac::Version), + testing_formulae: T::Array[TestRunnerFormula], + platform: Symbol, + arch: Symbol, + macos_version: T.nilable(OS::Mac::Version), ).returns(T::Boolean) } - def self.formulae_have_untested_dependents?(testing_formulae, reject_platform:, reject_arch:, select_macos_version:) + def self.formulae_have_untested_dependents?(testing_formulae, platform:, arch:, macos_version:) testing_formulae.any? do |formula| # If the formula has a platform/arch/macOS version requirement, then its # dependents don't need to be tested if these requirements are not satisfied. - next false if reject_platform && formula.send(:"#{reject_platform}_only?") - next false if reject_arch && formula.send(:"#{reject_arch}_only?") - next false if select_macos_version && !formula.compatible_with?(select_macos_version) + next false unless formula.send(:"#{platform}_compatible?") + next false unless formula.send(:"#{arch}_compatible?") + next false if macos_version && !formula.compatible_with?(macos_version) - compatible_dependents = formula.dependents(:"#{reject_platform}_#{reject_arch}_#{select_macos_version}").dup + compatible_dependents = formula.dependents(platform: platform, arch: arch, macos_version: macos_version&.to_sym) + .dup - compatible_dependents.reject! { |dependent_f| dependent_f.send(:"#{reject_arch}_only?") } if reject_arch - compatible_dependents.reject! { |dependent_f| dependent_f.send(:"#{reject_platform}_only?") } if reject_platform - if select_macos_version - compatible_dependents.select! { |dependent_f| dependent_f.compatible_with?(select_macos_version) } - end + compatible_dependents.select! { |dependent_f| dependent_f.send(:"#{platform}_compatible?") } + compatible_dependents.select! { |dependent_f| dependent_f.send(:"#{arch}_compatible?") } + compatible_dependents.select! { |dependent_f| dependent_f.compatible_with?(macos_version) } if macos_version (compatible_dependents - testing_formulae).present? end @@ -130,35 +167,30 @@ module Homebrew sig { params( - formulae: T::Array[TestRunnerFormula], - dependents: T::Boolean, - deleted_formulae: T.nilable(T::Array[String]), - reject_platform: T.nilable(Symbol), - reject_arch: T.nilable(Symbol), - select_macos_version: T.nilable(OS::Mac::Version), + formulae: T::Array[TestRunnerFormula], + dependents: T::Boolean, + deleted_formulae: T.nilable(T::Array[String]), + platform: Symbol, + arch: Symbol, + macos_version: T.nilable(OS::Mac::Version), ).returns(T::Boolean) } - def self.add_runner?(formulae, - dependents:, - deleted_formulae:, - reject_platform: nil, - reject_arch: nil, - select_macos_version: nil) + def self.add_runner?(formulae, dependents:, deleted_formulae:, platform:, arch:, macos_version: nil) if dependents formulae_have_untested_dependents?( formulae, - reject_platform: reject_platform, - reject_arch: reject_arch, - select_macos_version: select_macos_version, + platform: platform, + arch: arch, + macos_version: macos_version, ) else return true if deleted_formulae.present? compatible_formulae = formulae.dup - compatible_formulae.reject! { |formula| formula.send(:"#{reject_platform}_only?") } if reject_platform - compatible_formulae.reject! { |formula| formula.send(:"#{reject_arch}_only?") } if reject_arch - compatible_formulae.select! { |formula| formula.compatible_with?(select_macos_version) } if select_macos_version + compatible_formulae.select! { |formula| formula.send(:"#{platform}_compatible?") } + compatible_formulae.select! { |formula| formula.send(:"#{arch}_compatible?") } + compatible_formulae.select! { |formula| formula.compatible_with?(macos_version) } if macos_version compatible_formulae.present? end @@ -195,16 +227,10 @@ module Homebrew cleanup: linux_cleanup == "true", } - if args.dependents? - Homebrew::SimulateSystem.os = :linux - Homebrew::SimulateSystem.arch = :intel - Formulary.clear_cache - end - if add_runner?( testing_formulae, - reject_platform: :macos, - reject_arch: :arm64, + platform: :linux, + arch: :x86_64, deleted_formulae: deleted_formulae, dependents: args.dependents?, ) @@ -215,40 +241,28 @@ module Homebrew github_run_attempt = ENV.fetch("GITHUB_RUN_ATTEMPT") { raise "GITHUB_RUN_ATTEMPT is not defined" } ephemeral_suffix = "-#{github_run_id}-#{github_run_attempt}" - MacOSVersions::SYMBOLS.each do |symbol, version| + MacOSVersions::SYMBOLS.each_value do |version| macos_version = OS::Mac::Version.new(version) next if macos_version.outdated_release? || macos_version.prerelease? - if args.dependents? - Formulary.clear_cache - Homebrew::SimulateSystem.os = symbol - Homebrew::SimulateSystem.arch = :intel - end - if add_runner?( testing_formulae, - reject_platform: :linux, - reject_arch: :arm64, - select_macos_version: macos_version, - deleted_formulae: deleted_formulae, - dependents: args.dependents?, + platform: :macos, + arch: :x86_64, + macos_version: macos_version, + deleted_formulae: deleted_formulae, + dependents: args.dependents?, ) runners << { runner: "#{version}#{ephemeral_suffix}", cleanup: false } end - if args.dependents? - Formulary.clear_cache - Homebrew::SimulateSystem.os = symbol - Homebrew::SimulateSystem.arch = :arm - end - next unless add_runner?( testing_formulae, - reject_platform: :linux, - reject_arch: :x86_64, - select_macos_version: macos_version, - deleted_formulae: deleted_formulae, - dependents: args.dependents?, + platform: :macos, + arch: :arm64, + macos_version: macos_version, + deleted_formulae: deleted_formulae, + dependents: args.dependents?, ) runner_name = "#{version}-arm64" From 409ec342368019ce700ae7fe9f66c35d46380359 Mon Sep 17 00:00:00 2001 From: Carlo Cabrera <30379873+carlocab@users.noreply.github.com> Date: Wed, 5 Apr 2023 01:11:14 +0800 Subject: [PATCH 25/39] Update completions. Generated with `brew generate-man-completions` --- completions/bash/brew | 1 + completions/fish/brew.fish | 3 ++- completions/zsh/_brew | 3 ++- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/completions/bash/brew b/completions/bash/brew index f584352ef0..cffe80a828 100644 --- a/completions/bash/brew +++ b/completions/bash/brew @@ -795,6 +795,7 @@ _brew_determine_test_runners() { __brewcomp " --debug --dependents + --eval-all --help --quiet --verbose diff --git a/completions/fish/brew.fish b/completions/fish/brew.fish index 188a4b9f94..ce988bbec3 100644 --- a/completions/fish/brew.fish +++ b/completions/fish/brew.fish @@ -615,7 +615,8 @@ __fish_brew_complete_arg 'desc; and not __fish_seen_argument -l formula -l formu __fish_brew_complete_cmd 'determine-test-runners' 'Determines the runners used to test formulae or their dependents' __fish_brew_complete_arg 'determine-test-runners' -l debug -d 'Display any debugging information' -__fish_brew_complete_arg 'determine-test-runners' -l dependents -d 'Determine runners for testing dependents' +__fish_brew_complete_arg 'determine-test-runners' -l dependents -d 'Determine runners for testing dependents. Requires `--eval-all` or `HOMEBREW_EVAL_ALL`' +__fish_brew_complete_arg 'determine-test-runners' -l eval-all -d 'Evaluate all available formulae, whether installed or not, to determine testing dependents' __fish_brew_complete_arg 'determine-test-runners' -l help -d 'Show this message' __fish_brew_complete_arg 'determine-test-runners' -l quiet -d 'Make some output more quiet' __fish_brew_complete_arg 'determine-test-runners' -l verbose -d 'Make some output more verbose' diff --git a/completions/zsh/_brew b/completions/zsh/_brew index 6e36809e5f..a39be70255 100644 --- a/completions/zsh/_brew +++ b/completions/zsh/_brew @@ -760,7 +760,8 @@ _brew_desc() { _brew_determine_test_runners() { _arguments \ '--debug[Display any debugging information]' \ - '--dependents[Determine runners for testing dependents]' \ + '--dependents[Determine runners for testing dependents. Requires `--eval-all` or `HOMEBREW_EVAL_ALL`]' \ + '--eval-all[Evaluate all available formulae, whether installed or not, to determine testing dependents]' \ '--help[Show this message]' \ '--quiet[Make some output more quiet]' \ '--verbose[Make some output more verbose]' From 49e8d088ae4d18db60ddcf11bb654864a38a9c6b Mon Sep 17 00:00:00 2001 From: Carlo Cabrera <30379873+carlocab@users.noreply.github.com> Date: Wed, 5 Apr 2023 01:18:11 +0800 Subject: [PATCH 26/39] Move `TestRunnerFormula` class into a separate file --- .../dev-cmd/determine-test-runners.rb | 113 +---------------- Library/Homebrew/test_runner_formula.rb | 115 ++++++++++++++++++ 2 files changed, 116 insertions(+), 112 deletions(-) create mode 100644 Library/Homebrew/test_runner_formula.rb diff --git a/Library/Homebrew/dev-cmd/determine-test-runners.rb b/Library/Homebrew/dev-cmd/determine-test-runners.rb index a21cf98c98..8dac2d9e0d 100755 --- a/Library/Homebrew/dev-cmd/determine-test-runners.rb +++ b/Library/Homebrew/dev-cmd/determine-test-runners.rb @@ -2,118 +2,7 @@ # frozen_string_literal: true require "cli/parser" -require "formula" - -class TestRunnerFormula - extend T::Sig - - sig { returns(String) } - attr_reader :name - - sig { returns(Formula) } - attr_reader :formula - - sig { returns(T::Boolean) } - attr_reader :eval_all - - sig { params(formula: Formula, eval_all: T::Boolean).void } - def initialize(formula, eval_all: Homebrew::EnvConfig.eval_all?) - @formula = T.let(formula, Formula) - @name = T.let(formula.name, String) - @dependent_hash = T.let({}, T::Hash[Symbol, T::Array[TestRunnerFormula]]) - @eval_all = T.let(eval_all, T::Boolean) - freeze - end - - sig { returns(T::Boolean) } - def macos_only? - formula.requirements.any? { |r| r.is_a?(MacOSRequirement) && !r.version_specified? } - end - - sig { returns(T::Boolean) } - def macos_compatible? - !linux_only? - end - - sig { returns(T::Boolean) } - def linux_only? - formula.requirements.any?(LinuxRequirement) - end - - sig { returns(T::Boolean) } - def linux_compatible? - !macos_only? - end - - sig { returns(T::Boolean) } - def x86_64_only? - formula.requirements.any? { |r| r.is_a?(ArchRequirement) && (r.arch == :x86_64) } - end - - sig { returns(T::Boolean) } - def x86_64_compatible? - !arm64_only? - end - - sig { returns(T::Boolean) } - def arm64_only? - formula.requirements.any? { |r| r.is_a?(ArchRequirement) && (r.arch == :arm64) } - end - - sig { returns(T::Boolean) } - def arm64_compatible? - !x86_64_only? - end - - sig { returns(T.nilable(MacOSRequirement)) } - def versioned_macos_requirement - formula.requirements.find { |r| r.is_a?(MacOSRequirement) && r.version_specified? } - end - - sig { params(macos_version: OS::Mac::Version).returns(T::Boolean) } - def compatible_with?(macos_version) - # Assign to a variable to assist type-checking. - requirement = versioned_macos_requirement - return true if requirement.blank? - - macos_version.public_send(requirement.comparator, requirement.version) - end - - SIMULATE_SYSTEM_SYMBOLS = T.let({ arm64: :arm, x86_64: :intel }.freeze, T::Hash[Symbol, Symbol]) - - sig { - params( - platform: Symbol, - arch: Symbol, - macos_version: T.nilable(Symbol), - ).returns(T::Array[TestRunnerFormula]) - } - def dependents(platform:, arch:, macos_version:) - cache_key = :"#{platform}_#{arch}_#{macos_version}" - - @dependent_hash.fetch(cache_key) do - all = eval_all || Homebrew::EnvConfig.eval_all? - formula_selector, eval_all_env = if all - [:all, "1"] - else - [:installed, nil] - end - - with_env(HOMEBREW_EVAL_ALL: eval_all_env) do - Formulary.clear_cache - Homebrew::SimulateSystem.arch = SIMULATE_SYSTEM_SYMBOLS.fetch(arch) - Homebrew::SimulateSystem.os = macos_version || platform - - Formula.send(formula_selector) - .select { |candidate_f| candidate_f.deps.map(&:name).include?(name) } - .map { |f| TestRunnerFormula.new(f, eval_all: all) } - .freeze - ensure - Homebrew::SimulateSystem.clear - end - end - end -end +require "test_runner_formula" module Homebrew extend T::Sig diff --git a/Library/Homebrew/test_runner_formula.rb b/Library/Homebrew/test_runner_formula.rb new file mode 100644 index 0000000000..215af2eee6 --- /dev/null +++ b/Library/Homebrew/test_runner_formula.rb @@ -0,0 +1,115 @@ +# typed: strict +# frozen_string_literal: true + +require "formula" + +class TestRunnerFormula + extend T::Sig + + sig { returns(String) } + attr_reader :name + + sig { returns(Formula) } + attr_reader :formula + + sig { returns(T::Boolean) } + attr_reader :eval_all + + sig { params(formula: Formula, eval_all: T::Boolean).void } + def initialize(formula, eval_all: Homebrew::EnvConfig.eval_all?) + @formula = T.let(formula, Formula) + @name = T.let(formula.name, String) + @dependent_hash = T.let({}, T::Hash[Symbol, T::Array[TestRunnerFormula]]) + @eval_all = T.let(eval_all, T::Boolean) + freeze + end + + sig { returns(T::Boolean) } + def macos_only? + formula.requirements.any? { |r| r.is_a?(MacOSRequirement) && !r.version_specified? } + end + + sig { returns(T::Boolean) } + def macos_compatible? + !linux_only? + end + + sig { returns(T::Boolean) } + def linux_only? + formula.requirements.any?(LinuxRequirement) + end + + sig { returns(T::Boolean) } + def linux_compatible? + !macos_only? + end + + sig { returns(T::Boolean) } + def x86_64_only? + formula.requirements.any? { |r| r.is_a?(ArchRequirement) && (r.arch == :x86_64) } + end + + sig { returns(T::Boolean) } + def x86_64_compatible? + !arm64_only? + end + + sig { returns(T::Boolean) } + def arm64_only? + formula.requirements.any? { |r| r.is_a?(ArchRequirement) && (r.arch == :arm64) } + end + + sig { returns(T::Boolean) } + def arm64_compatible? + !x86_64_only? + end + + sig { returns(T.nilable(MacOSRequirement)) } + def versioned_macos_requirement + formula.requirements.find { |r| r.is_a?(MacOSRequirement) && r.version_specified? } + end + + sig { params(macos_version: OS::Mac::Version).returns(T::Boolean) } + def compatible_with?(macos_version) + # Assign to a variable to assist type-checking. + requirement = versioned_macos_requirement + return true if requirement.blank? + + macos_version.public_send(requirement.comparator, requirement.version) + end + + SIMULATE_SYSTEM_SYMBOLS = T.let({ arm64: :arm, x86_64: :intel }.freeze, T::Hash[Symbol, Symbol]) + + sig { + params( + platform: Symbol, + arch: Symbol, + macos_version: T.nilable(Symbol), + ).returns(T::Array[TestRunnerFormula]) + } + def dependents(platform:, arch:, macos_version:) + cache_key = :"#{platform}_#{arch}_#{macos_version}" + + @dependent_hash.fetch(cache_key) do + all = eval_all || Homebrew::EnvConfig.eval_all? + formula_selector, eval_all_env = if all + [:all, "1"] + else + [:installed, nil] + end + + with_env(HOMEBREW_EVAL_ALL: eval_all_env) do + Formulary.clear_cache + Homebrew::SimulateSystem.arch = SIMULATE_SYSTEM_SYMBOLS.fetch(arch) + Homebrew::SimulateSystem.os = macos_version || platform + + Formula.send(formula_selector) + .select { |candidate_f| candidate_f.deps.map(&:name).include?(name) } + .map { |f| TestRunnerFormula.new(f, eval_all: all) } + .freeze + ensure + Homebrew::SimulateSystem.clear + end + end + end +end From 868f9395b6bb04247453a2b3f4b135a1215208c8 Mon Sep 17 00:00:00 2001 From: Carlo Cabrera <30379873+carlocab@users.noreply.github.com> Date: Wed, 5 Apr 2023 03:52:26 +0800 Subject: [PATCH 27/39] Extract code to `GitHubRunnerMatrix` class The `GitHubRunnerMatrix` takes a list of testing formulae, deleted formulae, and the available runners and constructs the matrix of active runners for any given test job. --- .../dev-cmd/determine-test-runners.rb | 129 +++++------------- Library/Homebrew/github_runner_matrix.rb | 110 +++++++++++++++ 2 files changed, 145 insertions(+), 94 deletions(-) create mode 100644 Library/Homebrew/github_runner_matrix.rb diff --git a/Library/Homebrew/dev-cmd/determine-test-runners.rb b/Library/Homebrew/dev-cmd/determine-test-runners.rb index 8dac2d9e0d..92b2ebc98d 100755 --- a/Library/Homebrew/dev-cmd/determine-test-runners.rb +++ b/Library/Homebrew/dev-cmd/determine-test-runners.rb @@ -3,6 +3,7 @@ require "cli/parser" require "test_runner_formula" +require "github_runner_matrix" module Homebrew extend T::Sig @@ -29,59 +30,17 @@ module Homebrew sig { params( - testing_formulae: T::Array[TestRunnerFormula], - platform: Symbol, + version: String, arch: Symbol, - macos_version: T.nilable(OS::Mac::Version), - ).returns(T::Boolean) + ephemeral: T::Boolean, + ephemeral_suffix: T.nilable(String), + ).returns(T::Hash[Symbol, T.any(String, T::Boolean)]) } - def self.formulae_have_untested_dependents?(testing_formulae, platform:, arch:, macos_version:) - testing_formulae.any? do |formula| - # If the formula has a platform/arch/macOS version requirement, then its - # dependents don't need to be tested if these requirements are not satisfied. - next false unless formula.send(:"#{platform}_compatible?") - next false unless formula.send(:"#{arch}_compatible?") - next false if macos_version && !formula.compatible_with?(macos_version) - - compatible_dependents = formula.dependents(platform: platform, arch: arch, macos_version: macos_version&.to_sym) - .dup - - compatible_dependents.select! { |dependent_f| dependent_f.send(:"#{platform}_compatible?") } - compatible_dependents.select! { |dependent_f| dependent_f.send(:"#{arch}_compatible?") } - compatible_dependents.select! { |dependent_f| dependent_f.compatible_with?(macos_version) } if macos_version - - (compatible_dependents - testing_formulae).present? - end - end - - sig { - params( - formulae: T::Array[TestRunnerFormula], - dependents: T::Boolean, - deleted_formulae: T.nilable(T::Array[String]), - platform: Symbol, - arch: Symbol, - macos_version: T.nilable(OS::Mac::Version), - ).returns(T::Boolean) - } - def self.add_runner?(formulae, dependents:, deleted_formulae:, platform:, arch:, macos_version: nil) - if dependents - formulae_have_untested_dependents?( - formulae, - platform: platform, - arch: arch, - macos_version: macos_version, - ) - else - return true if deleted_formulae.present? - - compatible_formulae = formulae.dup - - compatible_formulae.select! { |formula| formula.send(:"#{platform}_compatible?") } - compatible_formulae.select! { |formula| formula.send(:"#{arch}_compatible?") } - compatible_formulae.select! { |formula| formula.compatible_with?(macos_version) } if macos_version - - compatible_formulae.present? + def self.runner_spec(version, arch:, ephemeral:, ephemeral_suffix: nil) + case arch + when :arm64 then { runner: "#{version}-arm64#{ephemeral_suffix}", clean: !ephemeral } + when :x86_64 then { runner: "#{version}#{ephemeral_suffix}", clean: !ephemeral } + else raise "Unexpected arch: #{arch}" end end @@ -96,14 +55,15 @@ module Homebrew Formulary.enable_factory_cache! testing_formulae = args.named.first.split(",") - testing_formulae.map! { |name| TestRunnerFormula.new(Formula[name], eval_all: eval_all) } + testing_formulae.map! { |name| TestRunnerFormula.new(Formulary.factory(name), eval_all: eval_all) } .freeze deleted_formulae = args.named.second&.split(",") - runners = [] - - linux_runner = ENV.fetch("HOMEBREW_LINUX_RUNNER") { raise "HOMEBREW_LINUX_RUNNER is not defined" } - linux_cleanup = ENV.fetch("HOMEBREW_LINUX_CLEANUP") { raise "HOMEBREW_LINUX_CLEANUP is not defined" } + linux_runner = ENV.fetch("HOMEBREW_LINUX_RUNNER") { raise "HOMEBREW_LINUX_RUNNER is not defined" } + linux_cleanup = ENV.fetch("HOMEBREW_LINUX_CLEANUP") { raise "HOMEBREW_LINUX_CLEANUP is not defined" } + github_run_id = ENV.fetch("GITHUB_RUN_ID") { raise "GITHUB_RUN_ID is not defined" } + github_run_attempt = ENV.fetch("GITHUB_RUN_ATTEMPT") { raise "GITHUB_RUN_ATTEMPT is not defined" } + github_output = ENV.fetch("GITHUB_OUTPUT") { raise "GITHUB_OUTPUT is not defined" } linux_runner_spec = { runner: linux_runner, @@ -115,61 +75,42 @@ module Homebrew timeout: 4320, cleanup: linux_cleanup == "true", } - - if add_runner?( - testing_formulae, - platform: :linux, - arch: :x86_64, - deleted_formulae: deleted_formulae, - dependents: args.dependents?, - ) - runners << linux_runner_spec - end - - github_run_id = ENV.fetch("GITHUB_RUN_ID") { raise "GITHUB_RUN_ID is not defined" } - github_run_attempt = ENV.fetch("GITHUB_RUN_ATTEMPT") { raise "GITHUB_RUN_ATTEMPT is not defined" } ephemeral_suffix = "-#{github_run_id}-#{github_run_attempt}" + available_runners = [] + available_runners << { platform: :linux, arch: :x86_64, runner_spec: linux_runner_spec, macos_version: nil } + MacOSVersions::SYMBOLS.each_value do |version| macos_version = OS::Mac::Version.new(version) next if macos_version.outdated_release? || macos_version.prerelease? - if add_runner?( - testing_formulae, - platform: :macos, - arch: :x86_64, - macos_version: macos_version, - deleted_formulae: deleted_formulae, - dependents: args.dependents?, - ) - runners << { runner: "#{version}#{ephemeral_suffix}", cleanup: false } - end + spec = runner_spec(version, arch: :x86_64, ephemeral: true, ephemeral_suffix: ephemeral_suffix) + available_runners << { platform: :macos, arch: :x86_64, runner_spec: spec, macos_version: macos_version } - next unless add_runner?( - testing_formulae, - platform: :macos, - arch: :arm64, - macos_version: macos_version, - deleted_formulae: deleted_formulae, - dependents: args.dependents?, - ) - - runner_name = "#{version}-arm64" - # Use bare metal runner when testing dependents on Monterey. - if macos_version >= :ventura || (macos_version >= :monterey && !args.dependents?) - runners << { runner: "#{runner_name}#{ephemeral_suffix}", cleanup: false } + # Use bare metal runner when testing dependents on ARM64 Monterey. + if (macos_version >= :ventura && args.dependents?) || macos_version >= :monterey + spec = runner_spec(version, arch: :arm64, ephemeral: true, ephemeral_suffix: ephemeral_suffix) + available_runners << { platform: :macos, arch: :arm64, runner_spec: spec, macos_version: macos_version } elsif macos_version >= :big_sur - runners << { runner: runner_name, cleanup: true } + spec = runner_spec(version, arch: :arm64, ephemeral: false) + available_runners << { platform: :macos, arch: :arm64, runner_spec: spec, macos_version: macos_version } end end + runner_matrix = GitHubRunnerMatrix.new( + available_runners, + testing_formulae, + deleted_formulae, + dependent_matrix: args.dependents?, + ) + runners = runner_matrix.active_runners + if !args.dependents? && runners.blank? # If there are no tests to run, add a runner that is meant to do nothing # to support making the `tests` job a required status check. runners << { runner: "ubuntu-latest", no_op: true } end - github_output = ENV.fetch("GITHUB_OUTPUT") { raise "GITHUB_OUTPUT is not defined" } File.open(github_output, "a") do |f| f.puts("runners=#{runners.to_json}") f.puts("runners_present=#{runners.present?}") diff --git a/Library/Homebrew/github_runner_matrix.rb b/Library/Homebrew/github_runner_matrix.rb new file mode 100644 index 0000000000..8217a30c1e --- /dev/null +++ b/Library/Homebrew/github_runner_matrix.rb @@ -0,0 +1,110 @@ +# typed: strict +# frozen_string_literal: true + +require "test_runner_formula" + +class GitHubRunnerMatrix + extend T::Sig + + # FIXME: sig { returns(T::Array[RunnerSpec]) } + sig { returns(T::Array[RunnerHashValue]) } + attr_reader :active_runners + + # FIXME: Enable cop again when https://github.com/sorbet/sorbet/issues/3532 is fixed. + # rubocop:disable Style/MutableConstant + RunnerSpec = T.type_alias do + T.any( + T::Hash[Symbol, T.any(String, T::Hash[Symbol, String], Integer, T::Boolean)], # Linux + T::Hash[Symbol, T.any(String, T::Boolean)], # macOS + ) + end + private_constant :RunnerSpec + RunnerHashValue = T.type_alias { T.any(Symbol, RunnerSpec, T.nilable(OS::Mac::Version)) } + private_constant :RunnerHashValue + # rubocop:enable Style/MutableConstant + + sig { + params( + available_runners: T::Array[T::Hash[Symbol, RunnerHashValue]], + testing_formulae: T::Array[TestRunnerFormula], + deleted_formulae: T.nilable(T::Array[String]), + dependent_matrix: T::Boolean, + ).void + } + def initialize(available_runners, testing_formulae, deleted_formulae, dependent_matrix:) + @available_runners = T.let(available_runners, T::Array[T::Hash[Symbol, RunnerHashValue]]) + @testing_formulae = T.let(testing_formulae, T::Array[TestRunnerFormula]) + @deleted_formulae = T.let(deleted_formulae, T.nilable(T::Array[String])) + @dependent_matrix = T.let(dependent_matrix, T::Boolean) + # FIXME: Should have type `RunnerSpec`, but Sorbet can't infer that that's correct. + @active_runners = T.let([], T::Array[RunnerHashValue]) + + generate_runners! + + freeze + end + + sig { void } + def generate_runners! + @available_runners.each do |runner| + @active_runners << runner.fetch(:runner_spec) if add_runner?(runner) + end + end + + sig { params(runner: T::Hash[Symbol, RunnerHashValue]).returns([Symbol, Symbol, T.nilable(OS::Mac::Version)]) } + def unpack_runner(runner) + platform = runner.fetch(:platform) + raise "Unexpected platform: #{platform}" if !platform.is_a?(Symbol) || [:macos, :linux].exclude?(platform) + + arch = runner.fetch(:arch) + raise "Unexpected arch: #{arch}" if !arch.is_a?(Symbol) || [:arm64, :x86_64].exclude?(arch) + + macos_version = runner.fetch(:macos_version) + if !macos_version.nil? && !macos_version.is_a?(OS::Mac::Version) + raise "Unexpected macos_version: #{macos_version}" + end + + [platform, arch, macos_version] + end + + sig { params(runner: T::Hash[Symbol, RunnerHashValue]).returns(T::Boolean) } + def add_runner?(runner) + if @dependent_matrix + formulae_have_untested_dependents?(runner) + else + return true if @deleted_formulae.present? + + compatible_formulae = @testing_formulae.dup + + platform, arch, macos_version = unpack_runner(runner) + + compatible_formulae.select! { |formula| formula.send(:"#{platform}_compatible?") } + compatible_formulae.select! { |formula| formula.send(:"#{arch}_compatible?") } + compatible_formulae.select! { |formula| formula.compatible_with?(macos_version) } if macos_version + + compatible_formulae.present? + end + end + + sig { params(runner: T::Hash[Symbol, RunnerHashValue]).returns(T::Boolean) } + def formulae_have_untested_dependents?(runner) + platform, arch, macos_version = unpack_runner(runner) + + @testing_formulae.any? do |formula| + # If the formula has a platform/arch/macOS version requirement, then its + # dependents don't need to be tested if these requirements are not satisfied. + next false unless formula.send(:"#{platform}_compatible?") + next false unless formula.send(:"#{arch}_compatible?") + next false if macos_version.present? && !formula.compatible_with?(macos_version) + + compatible_dependents = formula.dependents(platform: platform, arch: arch, macos_version: macos_version&.to_sym) + .dup + + compatible_dependents.select! { |dependent_f| dependent_f.send(:"#{platform}_compatible?") } + compatible_dependents.select! { |dependent_f| dependent_f.send(:"#{arch}_compatible?") } + compatible_dependents.select! { |dependent_f| dependent_f.compatible_with?(macos_version) } if macos_version + + (compatible_dependents - @testing_formulae).present? + end + end +end From 0c5654083160a16e470d9526653775f5ba01ae83 Mon Sep 17 00:00:00 2001 From: Carlo Cabrera <30379873+carlocab@users.noreply.github.com> Date: Wed, 5 Apr 2023 04:25:28 +0800 Subject: [PATCH 28/39] Fix `--dependents` breakage Without this, we sometimes mistakenly assign runners to dependents jobs when we don't have to. --- Library/Homebrew/github_runner_matrix.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Library/Homebrew/github_runner_matrix.rb b/Library/Homebrew/github_runner_matrix.rb index 8217a30c1e..f32d238628 100644 --- a/Library/Homebrew/github_runner_matrix.rb +++ b/Library/Homebrew/github_runner_matrix.rb @@ -104,7 +104,9 @@ class GitHubRunnerMatrix compatible_dependents.select! { |dependent_f| dependent_f.send(:"#{arch}_compatible?") } compatible_dependents.select! { |dependent_f| dependent_f.compatible_with?(macos_version) } if macos_version - (compatible_dependents - @testing_formulae).present? + # These arrays will generally have been generated by different Formulary caches, + # so we can only compare them by name and not directly. + (compatible_dependents.map(&:name) - @testing_formulae.map(&:name)).present? end end end From fc0c375cff16971f651cded03d879314a9f45fa9 Mon Sep 17 00:00:00 2001 From: Carlo Cabrera <30379873+carlocab@users.noreply.github.com> Date: Wed, 5 Apr 2023 23:58:19 +0800 Subject: [PATCH 29/39] Update completions. Generated with `brew generate-man-completions` --- completions/fish/brew.fish | 10 +++++----- completions/zsh/_brew | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/completions/fish/brew.fish b/completions/fish/brew.fish index ce988bbec3..f694cd1f8c 100644 --- a/completions/fish/brew.fish +++ b/completions/fish/brew.fish @@ -649,7 +649,7 @@ __fish_brew_complete_arg 'dispatch-build-bottle' -l workflow -d 'Dispatch specif __fish_brew_complete_arg 'dispatch-build-bottle' -a '(__fish_brew_suggest_formulae_all)' -__fish_brew_complete_cmd 'docs' 'Open Homebrew\'s online documentation (https://docs' +__fish_brew_complete_cmd 'docs' 'Open Homebrew\'s online documentation (https://docs.brew.sh) in a browser' __fish_brew_complete_arg 'docs' -l debug -d 'Display any debugging information' __fish_brew_complete_arg 'docs' -l help -d 'Show this message' __fish_brew_complete_arg 'docs' -l quiet -d 'Make some output more quiet' @@ -738,14 +738,14 @@ __fish_brew_complete_arg 'formula' -l verbose -d 'Make some output more verbose' __fish_brew_complete_arg 'formula' -a '(__fish_brew_suggest_formulae_all)' -__fish_brew_complete_cmd 'generate-cask-api' 'Generates Cask API data files for formulae' +__fish_brew_complete_cmd 'generate-cask-api' 'Generates Cask API data files for 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 help -d 'Show this message' __fish_brew_complete_arg 'generate-cask-api' -l quiet -d 'Make some output more quiet' __fish_brew_complete_arg 'generate-cask-api' -l verbose -d 'Make some output more verbose' -__fish_brew_complete_cmd 'generate-formula-api' 'Generates Formula API data files for formulae' +__fish_brew_complete_cmd 'generate-formula-api' 'Generates Formula API data files for formulae.brew.sh' __fish_brew_complete_arg 'generate-formula-api' -l debug -d 'Display any debugging information' __fish_brew_complete_arg 'generate-formula-api' -l help -d 'Show this message' __fish_brew_complete_arg 'generate-formula-api' -l quiet -d 'Make some output more quiet' @@ -1090,7 +1090,7 @@ __fish_brew_complete_arg 'missing' -l verbose -d 'Make some output more verbose' __fish_brew_complete_arg 'missing' -a '(__fish_brew_suggest_formulae_all)' -__fish_brew_complete_cmd 'nodenv-sync' 'Create symlinks for Homebrew\'s installed NodeJS versions in ~/' +__fish_brew_complete_cmd 'nodenv-sync' 'Create symlinks for Homebrew\'s installed NodeJS versions in ~/.nodenv/versions' __fish_brew_complete_arg 'nodenv-sync' -l debug -d 'Display any debugging information' __fish_brew_complete_arg 'nodenv-sync' -l help -d 'Show this message' __fish_brew_complete_arg 'nodenv-sync' -l quiet -d 'Make some output more quiet' @@ -1228,7 +1228,7 @@ __fish_brew_complete_arg 'prof' -l verbose -d 'Make some output more verbose' __fish_brew_complete_arg 'prof' -a '(__fish_brew_suggest_commands)' -__fish_brew_complete_cmd 'rbenv-sync' 'Create symlinks for Homebrew\'s installed Ruby versions in ~/' +__fish_brew_complete_cmd 'rbenv-sync' 'Create symlinks for Homebrew\'s installed Ruby versions in ~/.rbenv/versions' __fish_brew_complete_arg 'rbenv-sync' -l debug -d 'Display any debugging information' __fish_brew_complete_arg 'rbenv-sync' -l help -d 'Show this message' __fish_brew_complete_arg 'rbenv-sync' -l quiet -d 'Make some output more quiet' diff --git a/completions/zsh/_brew b/completions/zsh/_brew index a39be70255..7f79fb28e7 100644 --- a/completions/zsh/_brew +++ b/completions/zsh/_brew @@ -161,15 +161,15 @@ __brew_internal_commands() { 'determine-test-runners:Determines the runners used to test formulae or their dependents' 'developer:Control Homebrew'\''s developer mode' 'dispatch-build-bottle:Build bottles for these formulae with GitHub Actions' - 'docs:Open Homebrew'\''s online documentation (https://docs' + 'docs:Open Homebrew'\''s online documentation (https://docs.brew.sh) in a browser' 'doctor:Check your system for potential problems' 'edit:Open a formula or cask in the editor set by `EDITOR` or `HOMEBREW_EDITOR`, or open the Homebrew repository for editing if no formula is provided' 'extract:Look through repository history to find the most recent version of formula and create a copy in tap' 'fetch:Download a bottle (if available) or source packages for formulae and binaries for casks' 'formula:Display the path where formula is located' 'formulae:List all locally installable formulae including short names' - 'generate-cask-api:Generates Cask API data files for formulae' - 'generate-formula-api:Generates Formula API data files for formulae' + 'generate-cask-api:Generates Cask API data files for formulae.brew.sh' + 'generate-formula-api:Generates Formula API data files for formulae.brew.sh' 'generate-man-completions:Generate Homebrew'\''s manpages and shell completions' 'gist-logs:Upload logs for a failed build of formula to a new Gist' 'home:Open a formula or cask'\''s homepage in a browser, or open Homebrew'\''s own homepage if no argument is provided' @@ -185,7 +185,7 @@ __brew_internal_commands() { 'log:Show the `git log` for formula or cask, or show the log for the Homebrew repository if no formula or cask is provided' 'migrate:Migrate renamed packages to new names, where formula are old names of packages' 'missing:Check the given formula kegs for missing dependencies' - 'nodenv-sync:Create symlinks for Homebrew'\''s installed NodeJS versions in ~/' + 'nodenv-sync:Create symlinks for Homebrew'\''s installed NodeJS versions in ~/.nodenv/versions' 'options:Show install options specific to formula' 'outdated:List installed casks and formulae that have an updated version available' 'pin:Pin the specified formula, preventing them from being upgraded when issuing the `brew upgrade` formula command' @@ -196,7 +196,7 @@ __brew_internal_commands() { 'pr-pull:Download and publish bottles, and apply the bottle commit from a pull request with artifacts generated by GitHub Actions' 'pr-upload:Apply the bottle commit and publish bottles to a host' 'prof:Run Homebrew with a Ruby profiler' - 'rbenv-sync:Create symlinks for Homebrew'\''s installed Ruby versions in ~/' + 'rbenv-sync:Create symlinks for Homebrew'\''s installed Ruby versions in ~/.rbenv/versions' 'readall:Import all items from the specified tap, or from all installed taps if none is provided' 'reinstall:Uninstall and then reinstall a formula or cask using the same options it was originally installed with, plus any appended options specific to a formula' 'release:Create a new draft Homebrew/brew release with the appropriate version number and release notes' From 87373ff12abfea1145302011581bd22b0d5edaad Mon Sep 17 00:00:00 2001 From: Carlo Cabrera <30379873+carlocab@users.noreply.github.com> Date: Thu, 6 Apr 2023 02:13:15 +0800 Subject: [PATCH 30/39] Move more code out of `dev-cmd` --- .../dev-cmd/determine-test-runners.rb | 70 +------ Library/Homebrew/github_runner_matrix.rb | 193 +++++++++++++----- .../dev-cmd/determine-test-runners_spec.rb | 1 - Library/Homebrew/test_runner_formula.rb | 1 + 4 files changed, 153 insertions(+), 112 deletions(-) diff --git a/Library/Homebrew/dev-cmd/determine-test-runners.rb b/Library/Homebrew/dev-cmd/determine-test-runners.rb index 92b2ebc98d..40eec0e817 100755 --- a/Library/Homebrew/dev-cmd/determine-test-runners.rb +++ b/Library/Homebrew/dev-cmd/determine-test-runners.rb @@ -28,22 +28,6 @@ module Homebrew end end - sig { - params( - version: String, - arch: Symbol, - ephemeral: T::Boolean, - ephemeral_suffix: T.nilable(String), - ).returns(T::Hash[Symbol, T.any(String, T::Boolean)]) - } - def self.runner_spec(version, arch:, ephemeral:, ephemeral_suffix: nil) - case arch - when :arm64 then { runner: "#{version}-arm64#{ephemeral_suffix}", clean: !ephemeral } - when :x86_64 then { runner: "#{version}#{ephemeral_suffix}", clean: !ephemeral } - else raise "Unexpected arch: #{arch}" - end - end - sig { void } def self.determine_test_runners args = determine_test_runners_args.parse @@ -52,65 +36,21 @@ module Homebrew odie "`--dependents` requires `--eval-all` or `HOMEBREW_EVAL_ALL`!" if args.dependents? && !eval_all - Formulary.enable_factory_cache! - testing_formulae = args.named.first.split(",") testing_formulae.map! { |name| TestRunnerFormula.new(Formulary.factory(name), eval_all: eval_all) } .freeze - deleted_formulae = args.named.second&.split(",") + deleted_formulae = args.named.second&.split(",").freeze - linux_runner = ENV.fetch("HOMEBREW_LINUX_RUNNER") { raise "HOMEBREW_LINUX_RUNNER is not defined" } - linux_cleanup = ENV.fetch("HOMEBREW_LINUX_CLEANUP") { raise "HOMEBREW_LINUX_CLEANUP is not defined" } - github_run_id = ENV.fetch("GITHUB_RUN_ID") { raise "GITHUB_RUN_ID is not defined" } - github_run_attempt = ENV.fetch("GITHUB_RUN_ATTEMPT") { raise "GITHUB_RUN_ATTEMPT is not defined" } - github_output = ENV.fetch("GITHUB_OUTPUT") { raise "GITHUB_OUTPUT is not defined" } - - linux_runner_spec = { - runner: linux_runner, - container: { - image: "ghcr.io/homebrew/ubuntu22.04:master", - options: "--user=linuxbrew -e GITHUB_ACTIONS_HOMEBREW_SELF_HOSTED", - }, - workdir: "/github/home", - timeout: 4320, - cleanup: linux_cleanup == "true", - } - ephemeral_suffix = "-#{github_run_id}-#{github_run_attempt}" - - available_runners = [] - available_runners << { platform: :linux, arch: :x86_64, runner_spec: linux_runner_spec, macos_version: nil } - - MacOSVersions::SYMBOLS.each_value do |version| - macos_version = OS::Mac::Version.new(version) - next if macos_version.outdated_release? || macos_version.prerelease? - - spec = runner_spec(version, arch: :x86_64, ephemeral: true, ephemeral_suffix: ephemeral_suffix) - available_runners << { platform: :macos, arch: :x86_64, runner_spec: spec, macos_version: macos_version } - - # Use bare metal runner when testing dependents on ARM64 Monterey. - if (macos_version >= :ventura && args.dependents?) || macos_version >= :monterey - spec = runner_spec(version, arch: :arm64, ephemeral: true, ephemeral_suffix: ephemeral_suffix) - available_runners << { platform: :macos, arch: :arm64, runner_spec: spec, macos_version: macos_version } - elsif macos_version >= :big_sur - spec = runner_spec(version, arch: :arm64, ephemeral: false) - available_runners << { platform: :macos, arch: :arm64, runner_spec: spec, macos_version: macos_version } - end - end - - runner_matrix = GitHubRunnerMatrix.new( - available_runners, - testing_formulae, - deleted_formulae, - dependent_matrix: args.dependents?, - ) - runners = runner_matrix.active_runners + runner_matrix = GitHubRunnerMatrix.new(testing_formulae, deleted_formulae, dependent_matrix: args.dependents?) + runners = runner_matrix.active_runner_specs_hash if !args.dependents? && runners.blank? # If there are no tests to run, add a runner that is meant to do nothing # to support making the `tests` job a required status check. - runners << { runner: "ubuntu-latest", no_op: true } + runners << { name: "macOS 13-arm64", runner: "ubuntu-latest", no_op: true } end + github_output = ENV.fetch("GITHUB_OUTPUT") File.open(github_output, "a") do |f| f.puts("runners=#{runners.to_json}") f.puts("runners_present=#{runners.present?}") diff --git a/Library/Homebrew/github_runner_matrix.rb b/Library/Homebrew/github_runner_matrix.rb index f32d238628..7f408b270f 100644 --- a/Library/Homebrew/github_runner_matrix.rb +++ b/Library/Homebrew/github_runner_matrix.rb @@ -3,72 +3,169 @@ require "test_runner_formula" +class LinuxRunnerSpec < T::Struct + extend T::Sig + + const :name, String + const :runner, String + const :container, T::Hash[Symbol, String] + const :workdir, String + const :timeout, Integer + const :cleanup, T::Boolean + + sig { + returns({ + name: String, + runner: String, + container: T::Hash[Symbol, String], + workdir: String, + timeout: Integer, + cleanup: T::Boolean, + }) + } + def to_h + { + name: name, + runner: runner, + container: container, + workdir: workdir, + timeout: timeout, + cleanup: cleanup, + } + end +end + +class MacOSRunnerSpec < T::Struct + extend T::Sig + + const :name, String + const :runner, String + const :cleanup, T::Boolean + + sig { returns({ name: String, runner: String, cleanup: T::Boolean }) } + def to_h + { + name: name, + runner: runner, + cleanup: cleanup, + } + end +end + +class GitHubRunner < T::Struct + const :platform, Symbol + const :arch, Symbol + const :spec, T.any(LinuxRunnerSpec, MacOSRunnerSpec) + const :macos_version, T.nilable(OS::Mac::Version) +end + class GitHubRunnerMatrix extend T::Sig - # FIXME: sig { returns(T::Array[RunnerSpec]) } - sig { returns(T::Array[RunnerHashValue]) } - attr_reader :active_runners - # FIXME: Enable cop again when https://github.com/sorbet/sorbet/issues/3532 is fixed. # rubocop:disable Style/MutableConstant - RunnerSpec = T.type_alias do - T.any( - T::Hash[Symbol, T.any(String, T::Hash[Symbol, String], Integer, T::Boolean)], # Linux - T::Hash[Symbol, T.any(String, T::Boolean)], # macOS - ) - end + MaybeStringArray = T.type_alias { T.nilable(T::Array[String]) } + private_constant :MaybeStringArray + + RunnerSpec = T.type_alias { T.any(LinuxRunnerSpec, MacOSRunnerSpec) } private_constant :RunnerSpec - RunnerHashValue = T.type_alias { T.any(Symbol, RunnerSpec, T.nilable(OS::Mac::Version)) } - private_constant :RunnerHashValue # rubocop:enable Style/MutableConstant + sig { returns(T::Array[GitHubRunner]) } + attr_reader :available_runners + sig { params( - available_runners: T::Array[T::Hash[Symbol, RunnerHashValue]], - testing_formulae: T::Array[TestRunnerFormula], - deleted_formulae: T.nilable(T::Array[String]), - dependent_matrix: T::Boolean, + testing_formulae: T::Array[TestRunnerFormula], + deleted_formulae: MaybeStringArray, + dependent_matrix: T::Boolean, ).void } - def initialize(available_runners, testing_formulae, deleted_formulae, dependent_matrix:) - @available_runners = T.let(available_runners, T::Array[T::Hash[Symbol, RunnerHashValue]]) + def initialize(testing_formulae, deleted_formulae, dependent_matrix:) @testing_formulae = T.let(testing_formulae, T::Array[TestRunnerFormula]) - @deleted_formulae = T.let(deleted_formulae, T.nilable(T::Array[String])) + @deleted_formulae = T.let(deleted_formulae, MaybeStringArray) @dependent_matrix = T.let(dependent_matrix, T::Boolean) - # FIXME: Should have type `RunnerSpec`, but Sorbet can't infer that that's correct. - @active_runners = T.let([], T::Array[RunnerHashValue]) - generate_runners! + @available_runners = T.let([], T::Array[GitHubRunner]) + generate_available_runners! + + @active_runners = T.let( + @available_runners.select { |runner| active_runner?(runner) }, + T::Array[GitHubRunner], + ) freeze end + sig { returns(T::Array[T::Hash[Symbol, T.untyped]]) } + def active_runner_specs_hash + @active_runners.map(&:spec) + .map(&:to_h) + end + + sig { returns(LinuxRunnerSpec) } + def linux_runner_spec + linux_runner = ENV.fetch("HOMEBREW_LINUX_RUNNER") + linux_cleanup = ENV.fetch("HOMEBREW_LINUX_CLEANUP") + + LinuxRunnerSpec.new( + name: "Linux", + runner: linux_runner, + container: { + image: "ghcr.io/homebrew/ubuntu22.04:master", + options: "--user=linuxbrew -e GITHUB_ACTIONS_HOMEBREW_SELF_HOSTED", + }, + workdir: "/github/home", + timeout: 4320, + cleanup: linux_cleanup == "true", + ) + end + sig { void } - def generate_runners! - @available_runners.each do |runner| - @active_runners << runner.fetch(:runner_spec) if add_runner?(runner) + def generate_available_runners! + @available_runners << GitHubRunner.new(platform: :linux, arch: :x86_64, spec: linux_runner_spec) + + github_run_id = ENV.fetch("GITHUB_RUN_ID") + github_run_attempt = ENV.fetch("GITHUB_RUN_ATTEMPT") + ephemeral_suffix = "-#{github_run_id}-#{github_run_attempt}" + + MacOSVersions::SYMBOLS.each_value do |version| + macos_version = OS::Mac::Version.new(version) + next if macos_version.outdated_release? || macos_version.prerelease? + + spec = MacOSRunnerSpec.new( + name: "macOS #{version}-x86_64", + runner: "#{version}#{ephemeral_suffix}", + cleanup: false, + ) + @available_runners << GitHubRunner.new( + platform: :macos, + arch: :x86_64, + spec: spec, + macos_version: macos_version, + ) + + next unless macos_version >= :big_sur + + # Use bare metal runner when testing dependents on ARM64 Monterey. + runner, cleanup = if (macos_version >= :ventura && @dependent_matrix) || macos_version >= :monterey + ["#{version}-arm64#{ephemeral_suffix}", false] + else + ["#{version}-arm64", true] + end + + spec = MacOSRunnerSpec.new(name: "macOS #{version}-arm64", runner: runner, cleanup: cleanup) + @available_runners << GitHubRunner.new( + platform: :macos, + arch: :arm64, + spec: spec, + macos_version: macos_version, + ) end end - sig { params(runner: T::Hash[Symbol, RunnerHashValue]).returns([Symbol, Symbol, T.nilable(OS::Mac::Version)]) } - def unpack_runner(runner) - platform = runner.fetch(:platform) - raise "Unexpected platform: #{platform}" if !platform.is_a?(Symbol) || [:macos, :linux].exclude?(platform) - - arch = runner.fetch(:arch) - raise "Unexpected arch: #{arch}" if !arch.is_a?(Symbol) || [:arm64, :x86_64].exclude?(arch) - - macos_version = runner.fetch(:macos_version) - if !macos_version.nil? && !macos_version.is_a?(OS::Mac::Version) - raise "Unexpected macos_version: #{macos_version}" - end - - [platform, arch, macos_version] - end - - sig { params(runner: T::Hash[Symbol, RunnerHashValue]).returns(T::Boolean) } - def add_runner?(runner) + sig { params(runner: GitHubRunner).returns(T::Boolean) } + def active_runner?(runner) if @dependent_matrix formulae_have_untested_dependents?(runner) else @@ -76,7 +173,9 @@ class GitHubRunnerMatrix compatible_formulae = @testing_formulae.dup - platform, arch, macos_version = unpack_runner(runner) + platform = runner.platform + arch = runner.arch + macos_version = runner.macos_version compatible_formulae.select! { |formula| formula.send(:"#{platform}_compatible?") } compatible_formulae.select! { |formula| formula.send(:"#{arch}_compatible?") } @@ -86,9 +185,11 @@ class GitHubRunnerMatrix end end - sig { params(runner: T::Hash[Symbol, RunnerHashValue]).returns(T::Boolean) } + sig { params(runner: GitHubRunner).returns(T::Boolean) } def formulae_have_untested_dependents?(runner) - platform, arch, macos_version = unpack_runner(runner) + platform = runner.platform + arch = runner.arch + macos_version = runner.macos_version @testing_formulae.any? do |formula| # If the formula has a platform/arch/macOS version requirement, then its diff --git a/Library/Homebrew/test/dev-cmd/determine-test-runners_spec.rb b/Library/Homebrew/test/dev-cmd/determine-test-runners_spec.rb index f6da916c26..3a0e2ddf30 100644 --- a/Library/Homebrew/test/dev-cmd/determine-test-runners_spec.rb +++ b/Library/Homebrew/test/dev-cmd/determine-test-runners_spec.rb @@ -44,7 +44,6 @@ describe "brew determine-test-runners" do expect { brew "determine-test-runners", "testball", runner_env_dup } .to not_to_output.to_stdout - .and output("Error: #{k} is not defined\n").to_stderr .and be_a_failure end end diff --git a/Library/Homebrew/test_runner_formula.rb b/Library/Homebrew/test_runner_formula.rb index 215af2eee6..f623b4f13d 100644 --- a/Library/Homebrew/test_runner_formula.rb +++ b/Library/Homebrew/test_runner_formula.rb @@ -17,6 +17,7 @@ class TestRunnerFormula sig { params(formula: Formula, eval_all: T::Boolean).void } def initialize(formula, eval_all: Homebrew::EnvConfig.eval_all?) + Formulary.enable_factory_cache! @formula = T.let(formula, Formula) @name = T.let(formula.name, String) @dependent_hash = T.let({}, T::Hash[Symbol, T::Array[TestRunnerFormula]]) From d1b782fd68a666266367acfa3916326fb89a9f2b Mon Sep 17 00:00:00 2001 From: Carlo Cabrera <30379873+carlocab@users.noreply.github.com> Date: Thu, 6 Apr 2023 03:33:34 +0800 Subject: [PATCH 31/39] github_runner_matrix: simplify --- Library/Homebrew/github_runner_matrix.rb | 69 ++++++++++++++++-------- 1 file changed, 48 insertions(+), 21 deletions(-) diff --git a/Library/Homebrew/github_runner_matrix.rb b/Library/Homebrew/github_runner_matrix.rb index 7f408b270f..72204791d0 100644 --- a/Library/Homebrew/github_runner_matrix.rb +++ b/Library/Homebrew/github_runner_matrix.rb @@ -57,6 +57,7 @@ class GitHubRunner < T::Struct const :arch, Symbol const :spec, T.any(LinuxRunnerSpec, MacOSRunnerSpec) const :macos_version, T.nilable(OS::Mac::Version) + prop :active, T::Boolean, default: false end class GitHubRunnerMatrix @@ -69,6 +70,24 @@ class GitHubRunnerMatrix RunnerSpec = T.type_alias { T.any(LinuxRunnerSpec, MacOSRunnerSpec) } private_constant :RunnerSpec + + MacOSRunnerSpecHash = T.type_alias { { name: String, runner: String, cleanup: T::Boolean } } + private_constant :MacOSRunnerSpecHash + + LinuxRunnerSpecHash = T.type_alias do + { + name: String, + runner: String, + container: T::Hash[Symbol, String], + workdir: String, + timeout: Integer, + cleanup: T::Boolean, + } + end + private_constant :LinuxRunnerSpecHash + + RunnerSpecHash = T.type_alias { T.any(LinuxRunnerSpecHash, MacOSRunnerSpecHash) } + private_constant :RunnerSpecHash # rubocop:enable Style/MutableConstant sig { returns(T::Array[GitHubRunner]) } @@ -89,18 +108,14 @@ class GitHubRunnerMatrix @available_runners = T.let([], T::Array[GitHubRunner]) generate_available_runners! - @active_runners = T.let( - @available_runners.select { |runner| active_runner?(runner) }, - T::Array[GitHubRunner], - ) - freeze end - sig { returns(T::Array[T::Hash[Symbol, T.untyped]]) } + sig { returns(T::Array[RunnerSpecHash]) } def active_runner_specs_hash - @active_runners.map(&:spec) - .map(&:to_h) + @available_runners.select(&:active) + .map(&:spec) + .map(&:to_h) end sig { returns(LinuxRunnerSpec) } @@ -121,9 +136,31 @@ class GitHubRunnerMatrix ) end + VALID_PLATFORMS = T.let([:macos, :linux].freeze, T::Array[Symbol]) + VALID_ARCHES = T.let([:arm64, :x86_64].freeze, T::Array[Symbol]) + + sig { + params( + platform: Symbol, + arch: Symbol, + spec: RunnerSpec, + macos_version: T.nilable(OS::Mac::Version), + ).returns(GitHubRunner) + } + def create_runner(platform, arch, spec, macos_version = nil) + raise "Unexpected platform: #{platform}" if VALID_PLATFORMS.exclude?(platform) + raise "Unexpected arch: #{arch}" if VALID_ARCHES.exclude?(arch) + + runner = GitHubRunner.new(platform: platform, arch: arch, spec: spec, macos_version: macos_version) + runner.active = active_runner?(runner) + runner.freeze + end + sig { void } def generate_available_runners! - @available_runners << GitHubRunner.new(platform: :linux, arch: :x86_64, spec: linux_runner_spec) + return if @available_runners.present? + + @available_runners << create_runner(:linux, :x86_64, linux_runner_spec) github_run_id = ENV.fetch("GITHUB_RUN_ID") github_run_attempt = ENV.fetch("GITHUB_RUN_ATTEMPT") @@ -138,12 +175,7 @@ class GitHubRunnerMatrix runner: "#{version}#{ephemeral_suffix}", cleanup: false, ) - @available_runners << GitHubRunner.new( - platform: :macos, - arch: :x86_64, - spec: spec, - macos_version: macos_version, - ) + @available_runners << create_runner(:macos, :x86_64, spec, macos_version) next unless macos_version >= :big_sur @@ -155,12 +187,7 @@ class GitHubRunnerMatrix end spec = MacOSRunnerSpec.new(name: "macOS #{version}-arm64", runner: runner, cleanup: cleanup) - @available_runners << GitHubRunner.new( - platform: :macos, - arch: :arm64, - spec: spec, - macos_version: macos_version, - ) + @available_runners << create_runner(:macos, :arm64, spec, macos_version) end end From f9cc563e91a4b299facb5d77beeeb0dac895bfcf Mon Sep 17 00:00:00 2001 From: Carlo Cabrera <30379873+carlocab@users.noreply.github.com> Date: Thu, 6 Apr 2023 20:49:55 +0800 Subject: [PATCH 32/39] test_runner_formula: add tests --- .../Homebrew/test/test_runner_formula_spec.rb | 411 ++++++++++++++++++ 1 file changed, 411 insertions(+) create mode 100644 Library/Homebrew/test/test_runner_formula_spec.rb diff --git a/Library/Homebrew/test/test_runner_formula_spec.rb b/Library/Homebrew/test/test_runner_formula_spec.rb new file mode 100644 index 0000000000..280044f99b --- /dev/null +++ b/Library/Homebrew/test/test_runner_formula_spec.rb @@ -0,0 +1,411 @@ +# typed: false +# frozen_string_literal: true + +require "test_runner_formula" +require "test/support/fixtures/testball" + +describe TestRunnerFormula do + let(:testball) { Testball.new } + let(:xcode_helper) { setup_test_formula("xcode-helper", [:macos]) } + let(:linux_kernel_requirer) { setup_test_formula("linux-kernel-requirer", [:linux]) } + let(:old_non_portable_software) { setup_test_formula("old-non-portable-software", [arch: :x86_64]) } + let(:fancy_new_software) { setup_test_formula("fancy-new-software", [arch: :arm64]) } + let(:needs_modern_compiler) { setup_test_formula("needs-modern-compiler", [macos: :ventura]) } + + describe "#initialize" do + it "enables the Formulary factory cache" do + described_class.new(testball) + expect(Formulary.factory_cached?).to be(true) + end + end + + describe "#name" do + it "returns the wrapped Formula's name" do + expect(described_class.new(testball).name).to eq(testball.name) + end + end + + describe "#eval_all" do + it "is false by default" do + expect(described_class.new(testball).eval_all).to be(false) + end + + it "can be instantiated to be `true`" do + expect(described_class.new(testball, eval_all: true).eval_all).to be(true) + end + + it "takes the value of `HOMEBREW_EVAL_ALL` at instantiation time if not specified" do + allow(Homebrew::EnvConfig).to receive(:eval_all?).and_return(true) + expect(described_class.new(testball).eval_all).to be(true) + + allow(Homebrew::EnvConfig).to receive(:eval_all?).and_return(false) + expect(described_class.new(testball).eval_all).to be(false) + end + end + + describe "#formula" do + it "returns the wrapped Formula" do + expect(described_class.new(testball).formula).to eq(testball) + end + end + + describe "#macos_only?" do + context "when a formula requires macOS" do + it "returns true" do + expect(described_class.new(xcode_helper).macos_only?).to be(true) + end + end + + context "when a formula does not require macOS" do + it "returns false" do + expect(described_class.new(testball).macos_only?).to be(false) + expect(described_class.new(linux_kernel_requirer).macos_only?).to be(false) + expect(described_class.new(old_non_portable_software).macos_only?).to be(false) + expect(described_class.new(fancy_new_software).macos_only?).to be(false) + end + end + + context "when a formula requires only a minimum version of macOS" do + it "returns false" do + expect(described_class.new(needs_modern_compiler).macos_only?).to be(false) + end + end + end + + describe "#macos_compatible?" do + context "when a formula is compatible with macOS" do + it "returns true" do + expect(described_class.new(testball).macos_compatible?).to be(true) + expect(described_class.new(xcode_helper).macos_compatible?).to be(true) + expect(described_class.new(old_non_portable_software).macos_compatible?).to be(true) + expect(described_class.new(fancy_new_software).macos_compatible?).to be(true) + end + end + + context "when a formula requires only a minimum version of macOS" do + it "returns false" do + expect(described_class.new(needs_modern_compiler).macos_compatible?).to be(true) + end + end + + context "when a formula is not compatible with macOS" do + it "returns false" do + expect(described_class.new(linux_kernel_requirer).macos_compatible?).to be(false) + end + end + end + + describe "#linux_only?" do + context "when a formula requires Linux" do + it "returns true" do + expect(described_class.new(linux_kernel_requirer).linux_only?).to be(true) + end + end + + context "when a formula does not require Linux" do + it "returns false" do + expect(described_class.new(testball).linux_only?).to be(false) + expect(described_class.new(xcode_helper).linux_only?).to be(false) + expect(described_class.new(old_non_portable_software).linux_only?).to be(false) + expect(described_class.new(fancy_new_software).linux_only?).to be(false) + expect(described_class.new(needs_modern_compiler).linux_only?).to be(false) + end + end + end + + describe "#linux_compatible?" do + context "when a formula is compatible with Linux" do + it "returns true" do + expect(described_class.new(testball).linux_compatible?).to be(true) + expect(described_class.new(linux_kernel_requirer).linux_compatible?).to be(true) + expect(described_class.new(old_non_portable_software).linux_compatible?).to be(true) + expect(described_class.new(fancy_new_software).linux_compatible?).to be(true) + expect(described_class.new(needs_modern_compiler).linux_compatible?).to be(true) + end + end + + context "when a formula is not compatible with Linux" do + it "returns false" do + expect(described_class.new(xcode_helper).linux_compatible?).to be(false) + end + end + end + + describe "#x86_64_only?" do + context "when a formula requires an Intel architecture" do + it "returns true" do + expect(described_class.new(old_non_portable_software).x86_64_only?).to be(true) + end + end + + context "when a formula requires a non-Intel architecture" do + it "returns false" do + expect(described_class.new(fancy_new_software).x86_64_only?).to be(false) + end + end + + context "when a formula does not require a specfic architecture" do + it "returns false" do + expect(described_class.new(testball).x86_64_only?).to be(false) + expect(described_class.new(xcode_helper).x86_64_only?).to be(false) + expect(described_class.new(linux_kernel_requirer).x86_64_only?).to be(false) + expect(described_class.new(needs_modern_compiler).x86_64_only?).to be(false) + end + end + end + + describe "#x86_64_compatible?" do + context "when a formula is compatible with the Intel architecture" do + it "returns true" do + expect(described_class.new(testball).x86_64_compatible?).to be(true) + expect(described_class.new(xcode_helper).x86_64_compatible?).to be(true) + expect(described_class.new(linux_kernel_requirer).x86_64_compatible?).to be(true) + expect(described_class.new(old_non_portable_software).x86_64_compatible?).to be(true) + expect(described_class.new(needs_modern_compiler).x86_64_compatible?).to be(true) + end + end + + context "when a formula is not compatible with the Intel architecture" do + it "returns false" do + expect(described_class.new(fancy_new_software).x86_64_compatible?).to be(false) + end + end + end + + describe "#arm64_only?" do + context "when a formula requires an ARM64 architecture" do + it "returns true" do + expect(described_class.new(fancy_new_software).arm64_only?).to be(true) + end + end + + context "when a formula requires a non-ARM64 architecture" do + it "returns false" do + expect(described_class.new(old_non_portable_software).arm64_only?).to be(false) + end + end + + context "when a formula does not require a specific architecture" do + it "returns false" do + expect(described_class.new(testball).arm64_only?).to be(false) + expect(described_class.new(xcode_helper).arm64_only?).to be(false) + expect(described_class.new(linux_kernel_requirer).arm64_only?).to be(false) + expect(described_class.new(needs_modern_compiler).arm64_only?).to be(false) + end + end + end + + describe "#arm64_compatible?" do + context "when a formula is compatible with an ARM64 architecture" do + it "returns true" do + expect(described_class.new(testball).arm64_compatible?).to be(true) + expect(described_class.new(xcode_helper).arm64_compatible?).to be(true) + expect(described_class.new(linux_kernel_requirer).arm64_compatible?).to be(true) + expect(described_class.new(fancy_new_software).arm64_compatible?).to be(true) + expect(described_class.new(needs_modern_compiler).arm64_compatible?).to be(true) + end + end + + context "when a formula is not compatible with an ARM64 architecture" do + it "returns false" do + expect(described_class.new(old_non_portable_software).arm64_compatible?).to be(false) + end + end + end + + describe "#versioned_macos_requirement" do + let(:requirement) { described_class.new(needs_modern_compiler).versioned_macos_requirement } + + it "returns a MacOSRequirement with a specified version" do + expect(requirement).to be_a(MacOSRequirement) + expect(requirement.version_specified?).to be(true) + end + + context "when a formula has an unversioned MacOSRequirement" do + it "returns nil" do + expect(described_class.new(xcode_helper).versioned_macos_requirement).to be_nil + end + end + + context "when a formula has no declared MacOSRequirement" do + it "returns nil" do + expect(described_class.new(testball).versioned_macos_requirement).to be_nil + expect(described_class.new(linux_kernel_requirer).versioned_macos_requirement).to be_nil + expect(described_class.new(old_non_portable_software).versioned_macos_requirement).to be_nil + expect(described_class.new(fancy_new_software).versioned_macos_requirement).to be_nil + end + end + end + + describe "#dependents" do + let(:current_system) do + current_arch = case Homebrew::SimulateSystem.current_arch + when :arm then :arm64 + when :intel then :x86_64 + end + + current_platform = case Homebrew::SimulateSystem.current_os + when :generic then :linux + else Homebrew::SimulateSystem.current_os + end + + { + platform: current_platform, + arch: current_arch, + macos_version: nil, + } + end + + context "when a formula has no dependents" do + it "returns an empty array" do + expect(described_class.new(testball).dependents(current_system)).to eq([]) + expect(described_class.new(xcode_helper).dependents(current_system)).to eq([]) + expect(described_class.new(linux_kernel_requirer).dependents(current_system)).to eq([]) + expect(described_class.new(old_non_portable_software).dependents(current_system)).to eq([]) + expect(described_class.new(fancy_new_software).dependents(current_system)).to eq([]) + expect(described_class.new(needs_modern_compiler).dependents(current_system)).to eq([]) + end + end + + context "when a formula has dependents" do + let(:testball_user) { setup_test_formula("testball_user", ["testball"]) } + let(:recursive_testball_dependent) { setup_test_formula("recursive_testball_dependent", ["testball_user"]) } + + it "returns an array of direct dependents" do + allow(Formula).to receive(:all).and_return([testball_user, recursive_testball_dependent]) + + expect( + described_class.new(testball, eval_all: true).dependents(current_system).map(&:name), + ).to eq(["testball_user"]) + + expect( + described_class.new(testball_user, eval_all: true).dependents(current_system).map(&:name), + ).to eq(["recursive_testball_dependent"]) + end + + context "when called with arguments" do + after do + Homebrew::SimulateSystem.clear + end + + let(:testball_user_intel) { setup_test_formula("testball_user-intel", intel: ["testball"]) } + let(:testball_user_arm) { setup_test_formula("testball_user-arm", arm: ["testball"]) } + let(:testball_user_macos) { setup_test_formula("testball_user-macos", macos: ["testball"]) } + let(:testball_user_linux) { setup_test_formula("testball_user-linux", linux: ["testball"]) } + let(:testball_user_ventura) do + setup_test_formula("testball_user-ventura", ventura: ["testball"]) + end + let(:testball_and_dependents) do + [ + testball_user, + testball_user_intel, + testball_user_arm, + testball_user_macos, + testball_user_linux, + testball_user_ventura, + ] + end + + context "when given { platform: :linux, arch: :x86_64 }" do + before do + Homebrew::SimulateSystem.os = :linux + Homebrew::SimulateSystem.arch = :intel + end + + it "returns only the dependents for the requested platform and architecture" do + allow(Formula).to receive(:all).and_return(testball_and_dependents) + + expect( + described_class.new(testball, eval_all: true).dependents( + platform: :linux, arch: :x86_64, macos_version: nil, + ).map(&:name).sort, + ).to eq(["testball_user", "testball_user-intel", "testball_user-linux"].sort) + end + end + + context "when given { platform: :macos, arch: :x86_64 }" do + before do + Homebrew::SimulateSystem.os = :macos + Homebrew::SimulateSystem.arch = :intel + end + + it "returns only the dependents for the requested platform and architecture" do + allow(Formula).to receive(:all).and_return(testball_and_dependents) + + expect( + described_class.new(testball, eval_all: true).dependents( + platform: :macos, arch: :x86_64, macos_version: nil, + ).map(&:name).sort, + ).to eq(["testball_user", "testball_user-intel", "testball_user-macos"].sort) + end + end + + context "when given `{ platform: :macos, arch: :arm64 }`" do + before do + Homebrew::SimulateSystem.os = :macos + Homebrew::SimulateSystem.arch = :arm + end + + it "returns only the dependents for the requested platform and architecture" do + allow(Formula).to receive(:all).and_return(testball_and_dependents) + + expect( + described_class.new(testball, eval_all: true).dependents( + platform: :macos, arch: :arm64, macos_version: nil, + ).map(&:name).sort, + ).to eq(["testball_user", "testball_user-arm", "testball_user-macos"].sort) + end + end + + context "when given `{ platform: :macos, arch: :x86_64, macos_version: :mojave }`" do + before do + Homebrew::SimulateSystem.os = :mojave + Homebrew::SimulateSystem.arch = :intel + end + + it "returns only the dependents for the requested platform and architecture" do + allow(Formula).to receive(:all).and_return(testball_and_dependents) + + expect( + described_class.new(testball, eval_all: true).dependents( + platform: :macos, arch: :x86_64, macos_version: :mojave, + ).map(&:name).sort, + ).to eq(["testball_user", "testball_user-intel", "testball_user-macos"].sort) + end + end + + context "when given `{ platform: :macos, arch: :arm64, macos_version: :ventura }`" do + before do + Homebrew::SimulateSystem.os = :ventura + Homebrew::SimulateSystem.arch = :arm + end + + it "returns only the dependents for the requested platform and architecture" do + allow(Formula).to receive(:all).and_return(testball_and_dependents) + + expect( + described_class.new(testball, eval_all: true).dependents( + platform: :macos, arch: :arm64, macos_version: :ventura, + ).map(&:name).sort, + ).to eq(%w[testball_user testball_user-arm testball_user-macos testball_user-ventura].sort) + end + end + end + end + end +end + +def setup_test_formula(name, dependencies = [], **kwargs) + formula name do + url "https://brew.sh/#{name}-1.0.tar.gz" + dependencies.each { |dependency| depends_on dependency } + + kwargs.each do |k, v| + send(:"on_#{k}") do + v.each do |dep| + depends_on dep + end + end + end + end +end From 5ecd24ba20295a350d526d8015af8dd93273f50b Mon Sep 17 00:00:00 2001 From: Carlo Cabrera <30379873+carlocab@users.noreply.github.com> Date: Fri, 7 Apr 2023 01:47:11 +0800 Subject: [PATCH 33/39] Prefer `public_send` to `send` --- Library/Homebrew/github_runner_matrix.rb | 12 ++++++------ Library/Homebrew/test_runner_formula.rb | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Library/Homebrew/github_runner_matrix.rb b/Library/Homebrew/github_runner_matrix.rb index 72204791d0..f01f9c0f6b 100644 --- a/Library/Homebrew/github_runner_matrix.rb +++ b/Library/Homebrew/github_runner_matrix.rb @@ -204,8 +204,8 @@ class GitHubRunnerMatrix arch = runner.arch macos_version = runner.macos_version - compatible_formulae.select! { |formula| formula.send(:"#{platform}_compatible?") } - compatible_formulae.select! { |formula| formula.send(:"#{arch}_compatible?") } + compatible_formulae.select! { |formula| formula.public_send(:"#{platform}_compatible?") } + compatible_formulae.select! { |formula| formula.public_send(:"#{arch}_compatible?") } compatible_formulae.select! { |formula| formula.compatible_with?(macos_version) } if macos_version compatible_formulae.present? @@ -221,15 +221,15 @@ class GitHubRunnerMatrix @testing_formulae.any? do |formula| # If the formula has a platform/arch/macOS version requirement, then its # dependents don't need to be tested if these requirements are not satisfied. - next false unless formula.send(:"#{platform}_compatible?") - next false unless formula.send(:"#{arch}_compatible?") + next false unless formula.public_send(:"#{platform}_compatible?") + next false unless formula.public_send(:"#{arch}_compatible?") next false if macos_version.present? && !formula.compatible_with?(macos_version) compatible_dependents = formula.dependents(platform: platform, arch: arch, macos_version: macos_version&.to_sym) .dup - compatible_dependents.select! { |dependent_f| dependent_f.send(:"#{platform}_compatible?") } - compatible_dependents.select! { |dependent_f| dependent_f.send(:"#{arch}_compatible?") } + compatible_dependents.select! { |dependent_f| dependent_f.public_send(:"#{platform}_compatible?") } + compatible_dependents.select! { |dependent_f| dependent_f.public_send(:"#{arch}_compatible?") } compatible_dependents.select! { |dependent_f| dependent_f.compatible_with?(macos_version) } if macos_version # These arrays will generally have been generated by different Formulary caches, diff --git a/Library/Homebrew/test_runner_formula.rb b/Library/Homebrew/test_runner_formula.rb index f623b4f13d..b3e2dded65 100644 --- a/Library/Homebrew/test_runner_formula.rb +++ b/Library/Homebrew/test_runner_formula.rb @@ -104,7 +104,7 @@ class TestRunnerFormula Homebrew::SimulateSystem.arch = SIMULATE_SYSTEM_SYMBOLS.fetch(arch) Homebrew::SimulateSystem.os = macos_version || platform - Formula.send(formula_selector) + Formula.public_send(formula_selector) .select { |candidate_f| candidate_f.deps.map(&:name).include?(name) } .map { |f| TestRunnerFormula.new(f, eval_all: all) } .freeze From 3d5218892d10c8b79aab81d5ee9cf85a88ce05f5 Mon Sep 17 00:00:00 2001 From: Carlo Cabrera <30379873+carlocab@users.noreply.github.com> Date: Fri, 7 Apr 2023 19:10:28 +0800 Subject: [PATCH 34/39] github_runner_matrix: split into separate files --- Library/Homebrew/github_runner.rb | 35 ++++++++++ Library/Homebrew/github_runner_matrix.rb | 84 +++++------------------- Library/Homebrew/linux_runner_spec.rb | 34 ++++++++++ Library/Homebrew/macos_runner_spec.rb | 19 ++++++ 4 files changed, 104 insertions(+), 68 deletions(-) create mode 100644 Library/Homebrew/github_runner.rb create mode 100644 Library/Homebrew/linux_runner_spec.rb create mode 100644 Library/Homebrew/macos_runner_spec.rb diff --git a/Library/Homebrew/github_runner.rb b/Library/Homebrew/github_runner.rb new file mode 100644 index 0000000000..0dbf845cdc --- /dev/null +++ b/Library/Homebrew/github_runner.rb @@ -0,0 +1,35 @@ +# typed: strict +# frozen_string_literal: true + +require "linux_runner_spec" +require "macos_runner_spec" + +class GitHubRunner < T::Struct + extend T::Sig + + const :platform, Symbol + const :arch, Symbol + const :spec, T.any(LinuxRunnerSpec, MacOSRunnerSpec) + const :macos_version, T.nilable(OS::Mac::Version) + prop :active, T::Boolean, default: false + + sig { returns(T::Boolean) } + def macos? + platform == :macos + end + + sig { returns(T::Boolean) } + def linux? + platform == :linux + end + + sig { returns(T::Boolean) } + def x86_64? + arch == :x86_64 + end + + sig { returns(T::Boolean) } + def arm64? + arch == :arm64 + end +end diff --git a/Library/Homebrew/github_runner_matrix.rb b/Library/Homebrew/github_runner_matrix.rb index f01f9c0f6b..11b9677d8d 100644 --- a/Library/Homebrew/github_runner_matrix.rb +++ b/Library/Homebrew/github_runner_matrix.rb @@ -2,63 +2,7 @@ # frozen_string_literal: true require "test_runner_formula" - -class LinuxRunnerSpec < T::Struct - extend T::Sig - - const :name, String - const :runner, String - const :container, T::Hash[Symbol, String] - const :workdir, String - const :timeout, Integer - const :cleanup, T::Boolean - - sig { - returns({ - name: String, - runner: String, - container: T::Hash[Symbol, String], - workdir: String, - timeout: Integer, - cleanup: T::Boolean, - }) - } - def to_h - { - name: name, - runner: runner, - container: container, - workdir: workdir, - timeout: timeout, - cleanup: cleanup, - } - end -end - -class MacOSRunnerSpec < T::Struct - extend T::Sig - - const :name, String - const :runner, String - const :cleanup, T::Boolean - - sig { returns({ name: String, runner: String, cleanup: T::Boolean }) } - def to_h - { - name: name, - runner: runner, - cleanup: cleanup, - } - end -end - -class GitHubRunner < T::Struct - const :platform, Symbol - const :arch, Symbol - const :spec, T.any(LinuxRunnerSpec, MacOSRunnerSpec) - const :macos_version, T.nilable(OS::Mac::Version) - prop :active, T::Boolean, default: false -end +require "github_runner" class GitHubRunnerMatrix extend T::Sig @@ -91,7 +35,7 @@ class GitHubRunnerMatrix # rubocop:enable Style/MutableConstant sig { returns(T::Array[GitHubRunner]) } - attr_reader :available_runners + attr_reader :runners sig { params( @@ -105,19 +49,21 @@ class GitHubRunnerMatrix @deleted_formulae = T.let(deleted_formulae, MaybeStringArray) @dependent_matrix = T.let(dependent_matrix, T::Boolean) - @available_runners = T.let([], T::Array[GitHubRunner]) - generate_available_runners! + @runners = T.let([], T::Array[GitHubRunner]) + generate_runners! freeze end sig { returns(T::Array[RunnerSpecHash]) } def active_runner_specs_hash - @available_runners.select(&:active) - .map(&:spec) - .map(&:to_h) + runners.select(&:active) + .map(&:spec) + .map(&:to_h) end + private + sig { returns(LinuxRunnerSpec) } def linux_runner_spec linux_runner = ENV.fetch("HOMEBREW_LINUX_RUNNER") @@ -157,10 +103,10 @@ class GitHubRunnerMatrix end sig { void } - def generate_available_runners! - return if @available_runners.present? + def generate_runners! + return if @runners.present? - @available_runners << create_runner(:linux, :x86_64, linux_runner_spec) + @runners << create_runner(:linux, :x86_64, linux_runner_spec) github_run_id = ENV.fetch("GITHUB_RUN_ID") github_run_attempt = ENV.fetch("GITHUB_RUN_ATTEMPT") @@ -175,7 +121,7 @@ class GitHubRunnerMatrix runner: "#{version}#{ephemeral_suffix}", cleanup: false, ) - @available_runners << create_runner(:macos, :x86_64, spec, macos_version) + @runners << create_runner(:macos, :x86_64, spec, macos_version) next unless macos_version >= :big_sur @@ -187,8 +133,10 @@ class GitHubRunnerMatrix end spec = MacOSRunnerSpec.new(name: "macOS #{version}-arm64", runner: runner, cleanup: cleanup) - @available_runners << create_runner(:macos, :arm64, spec, macos_version) + @runners << create_runner(:macos, :arm64, spec, macos_version) end + + @runners.freeze end sig { params(runner: GitHubRunner).returns(T::Boolean) } diff --git a/Library/Homebrew/linux_runner_spec.rb b/Library/Homebrew/linux_runner_spec.rb new file mode 100644 index 0000000000..533eb3034b --- /dev/null +++ b/Library/Homebrew/linux_runner_spec.rb @@ -0,0 +1,34 @@ +# typed: strict +# frozen_string_literal: true + +class LinuxRunnerSpec < T::Struct + extend T::Sig + + const :name, String + const :runner, String + const :container, T::Hash[Symbol, String] + const :workdir, String + const :timeout, Integer + const :cleanup, T::Boolean + + sig { + returns({ + name: String, + runner: String, + container: T::Hash[Symbol, String], + workdir: String, + timeout: Integer, + cleanup: T::Boolean, + }) + } + def to_h + { + name: name, + runner: runner, + container: container, + workdir: workdir, + timeout: timeout, + cleanup: cleanup, + } + end +end diff --git a/Library/Homebrew/macos_runner_spec.rb b/Library/Homebrew/macos_runner_spec.rb new file mode 100644 index 0000000000..eb3b5ff6c4 --- /dev/null +++ b/Library/Homebrew/macos_runner_spec.rb @@ -0,0 +1,19 @@ +# typed: strict +# frozen_string_literal: true + +class MacOSRunnerSpec < T::Struct + extend T::Sig + + const :name, String + const :runner, String + const :cleanup, T::Boolean + + sig { returns({ name: String, runner: String, cleanup: T::Boolean }) } + def to_h + { + name: name, + runner: runner, + cleanup: cleanup, + } + end +end From 4c33d85172078980343511ef096ff9c3cd68246b Mon Sep 17 00:00:00 2001 From: Carlo Cabrera <30379873+carlocab@users.noreply.github.com> Date: Fri, 7 Apr 2023 19:31:24 +0800 Subject: [PATCH 35/39] test_runner_formula: update tests - also test `#compatible_with?` - check that we're not simulating a system after `#dependents` is called --- .../Homebrew/test/test_runner_formula_spec.rb | 94 +++++++++++++++---- 1 file changed, 75 insertions(+), 19 deletions(-) diff --git a/Library/Homebrew/test/test_runner_formula_spec.rb b/Library/Homebrew/test/test_runner_formula_spec.rb index 280044f99b..54fcd36477 100644 --- a/Library/Homebrew/test/test_runner_formula_spec.rb +++ b/Library/Homebrew/test/test_runner_formula_spec.rb @@ -237,6 +237,45 @@ describe TestRunnerFormula do end end + describe "#compatible_with?" do + context "when a formula has a versioned MacOSRequirement" do + context "when passed a compatible macOS version" do + it "returns true" do + expect(described_class.new(needs_modern_compiler).compatible_with?(OS::Mac::Version.new("13"))) + .to be(true) + end + end + + context "when passed an incompatible macOS version" do + it "returns false" do + expect(described_class.new(needs_modern_compiler).compatible_with?(OS::Mac::Version.new("11"))) + .to be(false) + end + end + end + + context "when a formula has an unversioned MacOSRequirement" do + it "returns true" do + MacOSVersions::SYMBOLS.each_value do |v| + version = OS::Mac::Version.new(v) + expect(described_class.new(xcode_helper).compatible_with?(version)).to be(true) + end + end + end + + context "when a formula has no declared MacOSRequirement" do + it "returns true" do + MacOSVersions::SYMBOLS.each_value do |v| + version = OS::Mac::Version.new(v) + expect(described_class.new(testball).compatible_with?(version)).to be(true) + expect(described_class.new(linux_kernel_requirer).compatible_with?(version)).to be(true) + expect(described_class.new(old_non_portable_software).compatible_with?(version)).to be(true) + expect(described_class.new(fancy_new_software).compatible_with?(version)).to be(true) + end + end + end + end + describe "#dependents" do let(:current_system) do current_arch = case Homebrew::SimulateSystem.current_arch @@ -264,6 +303,9 @@ describe TestRunnerFormula do expect(described_class.new(old_non_portable_software).dependents(current_system)).to eq([]) expect(described_class.new(fancy_new_software).dependents(current_system)).to eq([]) expect(described_class.new(needs_modern_compiler).dependents(current_system)).to eq([]) + + expect(Homebrew::SimulateSystem.os).to be_nil + expect(Homebrew::SimulateSystem.arch).to be_nil end end @@ -281,13 +323,12 @@ describe TestRunnerFormula do expect( described_class.new(testball_user, eval_all: true).dependents(current_system).map(&:name), ).to eq(["recursive_testball_dependent"]) + + expect(Homebrew::SimulateSystem.os).to be_nil + expect(Homebrew::SimulateSystem.arch).to be_nil end context "when called with arguments" do - after do - Homebrew::SimulateSystem.clear - end - let(:testball_user_intel) { setup_test_formula("testball_user-intel", intel: ["testball"]) } let(:testball_user_arm) { setup_test_formula("testball_user-arm", arm: ["testball"]) } let(:testball_user_macos) { setup_test_formula("testball_user-macos", macos: ["testball"]) } @@ -320,6 +361,9 @@ describe TestRunnerFormula do platform: :linux, arch: :x86_64, macos_version: nil, ).map(&:name).sort, ).to eq(["testball_user", "testball_user-intel", "testball_user-linux"].sort) + + expect(Homebrew::SimulateSystem.os).to be_nil + expect(Homebrew::SimulateSystem.arch).to be_nil end end @@ -337,6 +381,9 @@ describe TestRunnerFormula do platform: :macos, arch: :x86_64, macos_version: nil, ).map(&:name).sort, ).to eq(["testball_user", "testball_user-intel", "testball_user-macos"].sort) + + expect(Homebrew::SimulateSystem.os).to be_nil + expect(Homebrew::SimulateSystem.arch).to be_nil end end @@ -354,6 +401,9 @@ describe TestRunnerFormula do platform: :macos, arch: :arm64, macos_version: nil, ).map(&:name).sort, ).to eq(["testball_user", "testball_user-arm", "testball_user-macos"].sort) + + expect(Homebrew::SimulateSystem.os).to be_nil + expect(Homebrew::SimulateSystem.arch).to be_nil end end @@ -371,6 +421,9 @@ describe TestRunnerFormula do platform: :macos, arch: :x86_64, macos_version: :mojave, ).map(&:name).sort, ).to eq(["testball_user", "testball_user-intel", "testball_user-macos"].sort) + + expect(Homebrew::SimulateSystem.os).to be_nil + expect(Homebrew::SimulateSystem.arch).to be_nil end end @@ -388,24 +441,27 @@ describe TestRunnerFormula do platform: :macos, arch: :arm64, macos_version: :ventura, ).map(&:name).sort, ).to eq(%w[testball_user testball_user-arm testball_user-macos testball_user-ventura].sort) + + expect(Homebrew::SimulateSystem.os).to be_nil + expect(Homebrew::SimulateSystem.arch).to be_nil + end + end + end + end + end + + def setup_test_formula(name, dependencies = [], **kwargs) + formula name do + url "https://brew.sh/#{name}-1.0.tar.gz" + dependencies.each { |dependency| depends_on dependency } + + kwargs.each do |k, v| + send(:"on_#{k}") do + v.each do |dep| + depends_on dep end end end end end end - -def setup_test_formula(name, dependencies = [], **kwargs) - formula name do - url "https://brew.sh/#{name}-1.0.tar.gz" - dependencies.each { |dependency| depends_on dependency } - - kwargs.each do |k, v| - send(:"on_#{k}") do - v.each do |dep| - depends_on dep - end - end - end - end -end From fdc113d85cfce724cdee2adc0ef9b6315f0266ad Mon Sep 17 00:00:00 2001 From: Carlo Cabrera <30379873+carlocab@users.noreply.github.com> Date: Fri, 7 Apr 2023 22:21:08 +0800 Subject: [PATCH 36/39] os/mac/version: add `unsupported_release?` helper --- Library/Homebrew/github_runner_matrix.rb | 2 +- Library/Homebrew/os/mac/version.rb | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/Library/Homebrew/github_runner_matrix.rb b/Library/Homebrew/github_runner_matrix.rb index 11b9677d8d..e2ce5671f2 100644 --- a/Library/Homebrew/github_runner_matrix.rb +++ b/Library/Homebrew/github_runner_matrix.rb @@ -114,7 +114,7 @@ class GitHubRunnerMatrix MacOSVersions::SYMBOLS.each_value do |version| macos_version = OS::Mac::Version.new(version) - next if macos_version.outdated_release? || macos_version.prerelease? + next if macos_version.unsupported_release? spec = MacOSRunnerSpec.new( name: "macOS #{version}-x86_64", diff --git a/Library/Homebrew/os/mac/version.rb b/Library/Homebrew/os/mac/version.rb index d407e882ac..ce8ebb6880 100644 --- a/Library/Homebrew/os/mac/version.rb +++ b/Library/Homebrew/os/mac/version.rb @@ -72,6 +72,11 @@ module OS self >= HOMEBREW_MACOS_NEWEST_UNSUPPORTED end + sig { returns(T::Boolean) } + def unsupported_release? + outdated_release? || prerelease? + end + # For {OS::Mac::Version} compatibility. sig { returns(T::Boolean) } def requires_nehalem_cpu? From 5b8ead7bc80312c6759c03638c022c34365c9a47 Mon Sep 17 00:00:00 2001 From: Carlo Cabrera <30379873+carlocab@users.noreply.github.com> Date: Fri, 7 Apr 2023 22:21:46 +0800 Subject: [PATCH 37/39] Add more tests Also, remove most integration tests, because they're slow. --- .../dev-cmd/determine-test-runners_spec.rb | 158 +--------- .../test/github_runner_matrix_spec.rb | 288 ++++++++++++++++++ Library/Homebrew/test/github_runner_spec.rb | 34 +++ .../Homebrew/test/linux_runner_spec_spec.rb | 29 ++ .../Homebrew/test/macos_runner_spec_spec.rb | 20 ++ 5 files changed, 386 insertions(+), 143 deletions(-) create mode 100644 Library/Homebrew/test/github_runner_matrix_spec.rb create mode 100644 Library/Homebrew/test/github_runner_spec.rb create mode 100644 Library/Homebrew/test/linux_runner_spec_spec.rb create mode 100644 Library/Homebrew/test/macos_runner_spec_spec.rb diff --git a/Library/Homebrew/test/dev-cmd/determine-test-runners_spec.rb b/Library/Homebrew/test/dev-cmd/determine-test-runners_spec.rb index 3a0e2ddf30..769a66a65b 100644 --- a/Library/Homebrew/test/dev-cmd/determine-test-runners_spec.rb +++ b/Library/Homebrew/test/dev-cmd/determine-test-runners_spec.rb @@ -9,12 +9,7 @@ describe "brew determine-test-runners" do FileUtils.rm_f github_output end - # TODO: Generate this dynamically based on our supported macOS versions. let(:linux_runner) { "ubuntu-22.04" } - let(:all_runners) { ["11", "11-arm64", "12", "12-arm64", "13", "13-arm64", linux_runner] } - let(:intel_runners) { all_runners.reject { |r| r.end_with? "-arm64" } } - let(:arm64_runners) { all_runners - intel_runners } - let(:macos_runners) { all_runners - [linux_runner] } # We need to make sure we write to a different path for each example. let(:github_output) { "#{TEST_TMPDIR}/github_output#{DetermineRunnerTestHelper.new.number}" } let(:ephemeral_suffix) { "-12345-1" } @@ -26,28 +21,23 @@ describe "brew determine-test-runners" do "GITHUB_RUN_ATTEMPT" => ephemeral_suffix.split("-").third, }.freeze end + let(:all_runners) do + out = [] + MacOSVersions::SYMBOLS.each_value do |v| + macos_version = OS::Mac::Version.new(v) + next if macos_version.unsupported_release? + + out << v + out << "#{v}-arm64" + end + + out << linux_runner + + out + end it_behaves_like "parseable arguments" - it "fails without any arguments", :integration_test do - expect { brew "determine-test-runners" } - .to not_to_output.to_stdout - .and be_a_failure - end - - it "fails when the necessary environment variables are missing", :integration_test do - setup_test_formula "testball" - - runner_env.each_key do |k| - runner_env_dup = runner_env.dup - runner_env_dup[k] = nil - - expect { brew "determine-test-runners", "testball", runner_env_dup } - .to not_to_output.to_stdout - .and be_a_failure - end - end - it "assigns all runners for formulae without any requirements", :integration_test do setup_test_formula "testball" @@ -57,125 +47,7 @@ describe "brew determine-test-runners" do .and be_a_success expect(File.read(github_output)).not_to be_empty - expect(get_runners(github_output)).to eq(all_runners) - end - - it "assigns all runners when there are deleted formulae", :integration_test do - expect { brew "determine-test-runners", "", "testball", runner_env.merge({ "GITHUB_OUTPUT" => github_output }) } - .to not_to_output.to_stdout - .and not_to_output.to_stderr - .and be_a_success - - expect(File.read(github_output)).not_to be_empty - expect(get_runners(github_output)).to eq(all_runners) - end - - it "assigns `ubuntu-latest` when there are no testing formulae and no deleted formulae", :integration_test do - expect { brew "determine-test-runners", "", runner_env.merge({ "GITHUB_OUTPUT" => github_output }) } - .to not_to_output.to_stdout - .and not_to_output.to_stderr - .and be_a_success - - expect(File.read(github_output)).not_to be_empty - expect(get_runners(github_output)).to eq(["ubuntu-latest"]) - end - - it "assigns only Intel runners when a formula `depends_on arch: :x86_64`", :integration_test do - setup_test_formula "intel_depender", <<~RUBY - url "https://brew.sh/intel_depender-1.0.tar.gz" - depends_on arch: :x86_64 - RUBY - - expect { brew "determine-test-runners", "intel_depender", runner_env.merge({ "GITHUB_OUTPUT" => github_output }) } - .to not_to_output.to_stdout - .and not_to_output.to_stderr - .and be_a_success - - expect(File.read(github_output)).not_to be_empty - expect(get_runners(github_output)).to eq(intel_runners) - end - - it "assigns only ARM64 runners when a formula `depends_on arch: :arm64`", :integration_test do - setup_test_formula "fancy-m1-ml-framework", <<~RUBY - url "https://brew.sh/fancy-m1-ml-framework-1.0.tar.gz" - depends_on arch: :arm64 - RUBY - - expect do - brew "determine-test-runners", "fancy-m1-ml-framework", runner_env.merge({ "GITHUB_OUTPUT" => github_output }) - end - .to not_to_output.to_stdout - .and not_to_output.to_stderr - .and be_a_success - - expect(File.read(github_output)).not_to be_empty - expect(get_runners(github_output)).to eq(arm64_runners) - end - - it "assigns only macOS runners when a formula `depends_on :macos`", :integration_test do - setup_test_formula "xcode-helper", <<~RUBY - url "https://brew.sh/xcode-helper-1.0.tar.gz" - depends_on :macos - RUBY - - expect { brew "determine-test-runners", "xcode-helper", runner_env.merge({ "GITHUB_OUTPUT" => github_output }) } - .to not_to_output.to_stdout - .and not_to_output.to_stderr - .and be_a_success - - expect(File.read(github_output)).not_to be_empty - expect(get_runners(github_output)).to eq(macos_runners) - end - - it "assigns only Linux runners when a formula `depends_on :linux`", :integration_test do - setup_test_formula "linux-kernel-requirer", <<~RUBY - url "https://brew.sh/linux-kernel-requirer-1.0.tar.gz" - depends_on :linux - RUBY - - expect do - brew "determine-test-runners", "linux-kernel-requirer", runner_env.merge({ "GITHUB_OUTPUT" => github_output }) - end - .to not_to_output.to_stdout - .and not_to_output.to_stderr - .and be_a_success - - expect(File.read(github_output)).not_to be_empty - expect(get_runners(github_output)).to eq([linux_runner]) - end - - # TODO: Keep this updated to use the newest supported macOS version. - it "assigns only compatible runners when there is a versioned macOS requirement", :integration_test do - setup_test_formula "needs-macos-13", <<~RUBY - url "https://brew.sh/needs-macos-13-1.0.tar.gz" - depends_on macos: :ventura - RUBY - - expect { brew "determine-test-runners", "needs-macos-13", runner_env.merge({ "GITHUB_OUTPUT" => github_output }) } - .to not_to_output.to_stdout - .and not_to_output.to_stderr - .and be_a_success - - expect(File.read(github_output)).not_to be_empty - expect(get_runners(github_output)).to eq(["13", "13-arm64", linux_runner]) - expect(get_runners(github_output)).not_to eq(all_runners) - end - - describe "--dependents" do - it "assignes no runners for formulae with no dependents", :integration_test do - setup_test_formula "testball" - - expect do - brew "determine-test-runners", "--eval-all", "--dependents", "testball", - runner_env.merge({ "GITHUB_OUTPUT" => github_output }) - end - .to not_to_output.to_stdout - .and not_to_output.to_stderr - .and be_a_success - - expect(File.read(github_output)).not_to be_empty - expect(get_runners(github_output)).to eq([]) - end + expect(get_runners(github_output).sort).to eq(all_runners.sort) end end diff --git a/Library/Homebrew/test/github_runner_matrix_spec.rb b/Library/Homebrew/test/github_runner_matrix_spec.rb new file mode 100644 index 0000000000..37e8260f9d --- /dev/null +++ b/Library/Homebrew/test/github_runner_matrix_spec.rb @@ -0,0 +1,288 @@ +# typed: false +# frozen_string_literal: true + +require "github_runner_matrix" +require "test/support/fixtures/testball" + +describe GitHubRunnerMatrix do + before do + allow(ENV).to receive(:fetch).with("HOMEBREW_LINUX_RUNNER").and_return("ubuntu-latest") + allow(ENV).to receive(:fetch).with("HOMEBREW_LINUX_CLEANUP").and_return("false") + allow(ENV).to receive(:fetch).with("GITHUB_RUN_ID").and_return("12345") + allow(ENV).to receive(:fetch).with("GITHUB_RUN_ATTEMPT").and_return("1") + end + + let(:newest_supported_macos) do + MacOSVersions::SYMBOLS.find { |_, v| !OS::Mac::Version.new(v).prerelease? } + end + + let(:testball) { TestRunnerFormula.new(Testball.new) } + let(:testball_depender) { setup_test_runner_formula("testball-depender", ["testball"]) } + let(:testball_depender_linux) { setup_test_runner_formula("testball-depender-linux", ["testball", :linux]) } + let(:testball_depender_macos) { setup_test_runner_formula("testball-depender-macos", ["testball", :macos]) } + let(:testball_depender_intel) do + setup_test_runner_formula("testball-depender-intel", ["testball", { arch: :x86_64 }]) + end + let(:testball_depender_arm) { setup_test_runner_formula("testball-depender-arm", ["testball", { arch: :arm64 }]) } + let(:testball_depender_newest) do + symbol, = newest_supported_macos + setup_test_runner_formula("testball-depender-newest", ["testball", { macos: symbol }]) + end + + describe "#active_runner_specs_hash" do + it "returns an object that responds to `#to_json`" do + expect( + described_class.new([], ["deleted"], dependent_matrix: false) + .active_runner_specs_hash + .respond_to?(:to_json), + ).to be(true) + end + end + + describe "#generate_runners!" do + it "is idempotent" do + matrix = described_class.new([], [], dependent_matrix: false) + runners = matrix.runners.dup + matrix.send(:generate_runners!) + + expect(matrix.runners).to eq(runners) + end + end + + context "when there are no testing formulae and no deleted formulae" do + it "activates no test runners" do + expect(described_class.new([], [], dependent_matrix: false).runners.any?(&:active)) + .to be(false) + end + + it "activates no dependent runners" do + expect(described_class.new([], [], dependent_matrix: true).runners.any?(&:active)) + .to be(false) + end + end + + context "when there are testing formulae and no deleted formulae" do + context "when it is a matrix for the `tests` job" do + context "when testing formulae have no requirements" do + it "activates all runners" do + expect(described_class.new([testball], [], dependent_matrix: false).runners.all?(&:active)) + .to be(true) + end + end + + context "when testing formulae require Linux" do + it "activates only the Linux runner" do + runner_matrix = described_class.new([testball_depender_linux], [], dependent_matrix: false) + + expect(runner_matrix.runners.all?(&:active)).to be(false) + expect(runner_matrix.runners.any?(&:active)).to be(true) + expect(get_runner_names(runner_matrix)).to eq(["Linux"]) + end + end + + context "when testing formulae require macOS" do + it "activates only the macOS runners" do + runner_matrix = described_class.new([testball_depender_macos], [], dependent_matrix: false) + + expect(runner_matrix.runners.all?(&:active)).to be(false) + expect(runner_matrix.runners.any?(&:active)).to be(true) + expect(get_runner_names(runner_matrix)).to eq(get_runner_names(runner_matrix, :macos?)) + end + end + + context "when testing formulae require Intel" do + it "activates only the Intel runners" do + runner_matrix = described_class.new([testball_depender_intel], [], dependent_matrix: false) + + expect(runner_matrix.runners.all?(&:active)).to be(false) + expect(runner_matrix.runners.any?(&:active)).to be(true) + expect(get_runner_names(runner_matrix)).to eq(get_runner_names(runner_matrix, :x86_64?)) + end + end + + context "when testing formulae require ARM" do + it "activates only the ARM runners" do + runner_matrix = described_class.new([testball_depender_arm], [], dependent_matrix: false) + + expect(runner_matrix.runners.all?(&:active)).to be(false) + expect(runner_matrix.runners.any?(&:active)).to be(true) + expect(get_runner_names(runner_matrix)).to eq(get_runner_names(runner_matrix, :arm64?)) + end + end + + context "when testing formulae require a macOS version" do + it "activates the Linux runner and suitable macOS runners" do + _, v = newest_supported_macos + runner_matrix = described_class.new([testball_depender_newest], [], dependent_matrix: false) + + expect(runner_matrix.runners.all?(&:active)).to be(false) + expect(runner_matrix.runners.any?(&:active)).to be(true) + expect(get_runner_names(runner_matrix).sort).to eq(["Linux", "macOS #{v}-arm64", "macOS #{v}-x86_64"]) + end + end + end + + context "when it is a matrix for the `test_deps` job" do + context "when testing formulae have no dependents" do + it "activates no runners" do + allow(Homebrew::EnvConfig).to receive(:eval_all?).and_return(true) + allow(Formula).to receive(:all).and_return([testball].map(&:formula)) + + expect(described_class.new([testball], [], dependent_matrix: true).runners.any?(&:active)) + .to be(false) + end + end + + context "when testing formulae have dependents" do + context "when dependents have no requirements" do + it "activates all runners" do + allow(Homebrew::EnvConfig).to receive(:eval_all?).and_return(true) + allow(Formula).to receive(:all).and_return([testball, testball_depender].map(&:formula)) + + expect(described_class.new([testball], [], dependent_matrix: true).runners.all?(&:active)) + .to be(true) + end + end + + context "when dependents require Linux" do + it "activates only Linux runners" do + allow(Homebrew::EnvConfig).to receive(:eval_all?).and_return(true) + allow(Formula).to receive(:all).and_return([testball, testball_depender_linux].map(&:formula)) + + runner_matrix = described_class.new([testball], [], dependent_matrix: true) + expect(runner_matrix.runners.all?(&:active)).to be(false) + expect(runner_matrix.runners.any?(&:active)).to be(true) + expect(get_runner_names(runner_matrix)).to eq(get_runner_names(runner_matrix, :linux?)) + end + end + + context "when dependents require macOS" do + it "activates only macOS runners" do + allow(Homebrew::EnvConfig).to receive(:eval_all?).and_return(true) + allow(Formula).to receive(:all).and_return([testball, testball_depender_macos].map(&:formula)) + + runner_matrix = described_class.new([testball], [], dependent_matrix: true) + expect(runner_matrix.runners.all?(&:active)).to be(false) + expect(runner_matrix.runners.any?(&:active)).to be(true) + expect(get_runner_names(runner_matrix)).to eq(get_runner_names(runner_matrix, :macos?)) + end + end + + context "when dependents require an Intel architecture" do + it "activates only Intel runners" do + allow(Homebrew::EnvConfig).to receive(:eval_all?).and_return(true) + allow(Formula).to receive(:all).and_return([testball, testball_depender_intel].map(&:formula)) + + runner_matrix = described_class.new([testball], [], dependent_matrix: true) + expect(runner_matrix.runners.all?(&:active)).to be(false) + expect(runner_matrix.runners.any?(&:active)).to be(true) + expect(get_runner_names(runner_matrix)).to eq(get_runner_names(runner_matrix, :x86_64?)) + end + end + + context "when dependents require an ARM architecture" do + it "activates only ARM runners" do + allow(Homebrew::EnvConfig).to receive(:eval_all?).and_return(true) + allow(Formula).to receive(:all).and_return([testball, testball_depender_arm].map(&:formula)) + + runner_matrix = described_class.new([testball], [], dependent_matrix: true) + expect(runner_matrix.runners.all?(&:active)).to be(false) + expect(runner_matrix.runners.any?(&:active)).to be(true) + expect(get_runner_names(runner_matrix)).to eq(get_runner_names(runner_matrix, :arm64?)) + end + end + end + end + end + + context "when there are deleted formulae" do + context "when it is a matrix for the `tests` job" do + it "activates all runners" do + expect(described_class.new([], ["deleted"], dependent_matrix: false).runners.all?(&:active)) + .to be(true) + end + end + + context "when it is a matrix for the `test_deps` job" do + context "when there are no testing formulae" do + it "activates no runners" do + expect(described_class.new([], ["deleted"], dependent_matrix: true).runners.any?(&:active)) + .to be(false) + end + end + + context "when there are testing formulae with no dependents" do + it "activates no runners" do + testing_formulae = [testball] + runner_matrix = described_class.new(testing_formulae, ["deleted"], dependent_matrix: true) + + allow(Homebrew::EnvConfig).to receive(:eval_all?).and_return(true) + allow(Formula).to receive(:all).and_return(testing_formulae.map(&:formula)) + + expect(runner_matrix.runners.none?(&:active)).to be(true) + end + end + + context "when there are testing formulae with dependents" do + context "when dependent formulae have no requirements" do + it "activates the applicable runners" do + allow(Homebrew::EnvConfig).to receive(:eval_all?).and_return(true) + allow(Formula).to receive(:all).and_return([testball, testball_depender].map(&:formula)) + + testing_formulae = [testball] + expect(described_class.new(testing_formulae, ["deleted"], dependent_matrix: true).runners.all?(&:active)) + .to be(true) + end + end + + context "when dependent formulae have requirements" do + context "when dependent formulae require Linux" do + it "activates the applicable runners" do + allow(Homebrew::EnvConfig).to receive(:eval_all?).and_return(true) + allow(Formula).to receive(:all).and_return([testball, testball_depender_linux].map(&:formula)) + + testing_formulae = [testball] + matrix = described_class.new(testing_formulae, ["deleted"], dependent_matrix: true) + expect(get_runner_names(matrix)).to eq(["Linux"]) + end + end + + context "when dependent formulae require macOS" do + it "activates the applicable runners" do + allow(Homebrew::EnvConfig).to receive(:eval_all?).and_return(true) + allow(Formula).to receive(:all).and_return([testball, testball_depender_macos].map(&:formula)) + + testing_formulae = [testball] + matrix = described_class.new(testing_formulae, ["deleted"], dependent_matrix: true) + expect(get_runner_names(matrix)).to eq(get_runner_names(matrix, :macos?)) + end + end + end + end + end + end + + def get_runner_names(runner_matrix, predicate = :active) + runner_matrix.runners + .select(&predicate) + .map(&:spec) + .map(&:name) + end + + def setup_test_runner_formula(name, dependencies = [], **kwargs) + f = formula name do + url "https://brew.sh/#{name}-1.0.tar.gz" + dependencies.each { |dependency| depends_on dependency } + + kwargs.each do |k, v| + send(:"on_#{k}") do + v.each do |dep| + depends_on dep + end + end + end + end + + TestRunnerFormula.new(f) + end +end diff --git a/Library/Homebrew/test/github_runner_spec.rb b/Library/Homebrew/test/github_runner_spec.rb new file mode 100644 index 0000000000..04cfa8d595 --- /dev/null +++ b/Library/Homebrew/test/github_runner_spec.rb @@ -0,0 +1,34 @@ +# typed: false +# frozen_string_literal: true + +require "github_runner" + +describe GitHubRunner do + let(:runner) do + spec = MacOSRunnerSpec.new(name: "macOS 11-arm64", runner: "11-arm64", cleanup: true) + version = OS::Mac::Version.new("11") + described_class.new(platform: :macos, arch: :arm64, spec: spec, macos_version: version) + end + + it "has immutable attributes" do + [:platform, :arch, :spec, :macos_version].each do |attribute| + expect(runner.respond_to?("#{attribute}=")).to be(false) + end + end + + it "is inactive by default" do + expect(runner.active).to be(false) + end + + describe "#macos?" do + it "returns true if the runner is a macOS runner" do + expect(runner.macos?).to be(true) + end + end + + describe "#linux?" do + it "returns false if the runner is a macOS runner" do + expect(runner.linux?).to be(false) + end + end +end diff --git a/Library/Homebrew/test/linux_runner_spec_spec.rb b/Library/Homebrew/test/linux_runner_spec_spec.rb new file mode 100644 index 0000000000..0f3add6594 --- /dev/null +++ b/Library/Homebrew/test/linux_runner_spec_spec.rb @@ -0,0 +1,29 @@ +# typed: false +# frozen_string_literal: true + +require "linux_runner_spec" + +describe LinuxRunnerSpec do + let(:spec) do + described_class.new( + name: "Linux", + runner: "ubuntu-latest", + container: { image: "ghcr.io/homebrew/ubuntu22.04:master", options: "--user=linuxbrew" }, + workdir: "/github/home", + timeout: 360, + cleanup: false, + ) + end + + it "has immutable attributes" do + [:name, :runner, :container, :workdir, :timeout, :cleanup].each do |attribute| + expect(spec.respond_to?("#{attribute}=")).to be(false) + end + end + + describe "#to_h" do + it "returns an object that responds to `#to_json`" do + expect(spec.to_h.respond_to?(:to_json)).to be(true) + end + end +end diff --git a/Library/Homebrew/test/macos_runner_spec_spec.rb b/Library/Homebrew/test/macos_runner_spec_spec.rb new file mode 100644 index 0000000000..3c56c72805 --- /dev/null +++ b/Library/Homebrew/test/macos_runner_spec_spec.rb @@ -0,0 +1,20 @@ +# typed: false +# frozen_string_literal: true + +require "macos_runner_spec" + +describe MacOSRunnerSpec do + let(:spec) { described_class.new(name: "macOS 11-arm64", runner: "11-arm64", cleanup: true) } + + it "has immutable attributes" do + [:name, :runner, :cleanup].each do |attribute| + expect(spec.respond_to?("#{attribute}=")).to be(false) + end + end + + describe "#to_h" do + it "returns an object that responds to `#to_json`" do + expect(spec.to_h.respond_to?(:to_json)).to be(true) + end + end +end From 959e2432a9c534d98ae35d59f28b8c74370f7c80 Mon Sep 17 00:00:00 2001 From: Carlo Cabrera <30379873+carlocab@users.noreply.github.com> Date: Fri, 7 Apr 2023 22:27:08 +0800 Subject: [PATCH 38/39] Update completions. Generated with `brew generate-man-completions` --- completions/bash/brew | 1 - completions/fish/brew.fish | 1 - completions/zsh/_brew | 1 - docs/Manpage.md | 5 +++-- manpages/brew.1 | 10 ++++++---- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/completions/bash/brew b/completions/bash/brew index cffe80a828..893ce249f4 100644 --- a/completions/bash/brew +++ b/completions/bash/brew @@ -350,7 +350,6 @@ _brew_audit() { --cask --debug --display-cop-names - --display-failures-only --display-filename --eval-all --except diff --git a/completions/fish/brew.fish b/completions/fish/brew.fish index f694cd1f8c..7d28d645f8 100644 --- a/completions/fish/brew.fish +++ b/completions/fish/brew.fish @@ -332,7 +332,6 @@ __fish_brew_complete_arg 'audit' -l audit-debug -d 'Enable debugging and profili __fish_brew_complete_arg 'audit' -l cask -d 'Treat all named arguments as casks' __fish_brew_complete_arg 'audit' -l debug -d 'Display any debugging information' __fish_brew_complete_arg 'audit' -l display-cop-names -d 'Include the RuboCop cop name for each violation in the output' -__fish_brew_complete_arg 'audit' -l display-failures-only -d 'Only display casks that fail the audit. This is the default for formulae' __fish_brew_complete_arg 'audit' -l display-filename -d 'Prefix every line of output with the file or formula name being audited, to make output easy to grep' __fish_brew_complete_arg 'audit' -l eval-all -d 'Evaluate all available formulae and casks, whether installed or not, to audit them. Implied if `HOMEBREW_EVAL_ALL` is set' __fish_brew_complete_arg 'audit' -l except -d 'Specify a comma-separated method list to skip running the methods named `audit_`method' diff --git a/completions/zsh/_brew b/completions/zsh/_brew index 7f79fb28e7..f97070d420 100644 --- a/completions/zsh/_brew +++ b/completions/zsh/_brew @@ -420,7 +420,6 @@ _brew_audit() { '--audit-debug[Enable debugging and profiling of audit methods]' \ '--debug[Display any debugging information]' \ '(--skip-style --only-cops --except-cops)--display-cop-names[Include the RuboCop cop name for each violation in the output]' \ - '--display-failures-only[Only display casks that fail the audit. This is the default for formulae]' \ '--display-filename[Prefix every line of output with the file or formula name being audited, to make output easy to grep]' \ '--eval-all[Evaluate all available formulae and casks, whether installed or not, to audit them. Implied if `HOMEBREW_EVAL_ALL` is set]' \ '(--only)--except[Specify a comma-separated method list to skip running the methods named `audit_`method]' \ diff --git a/docs/Manpage.md b/docs/Manpage.md index d6924ee0cc..f5992e5b97 100644 --- a/docs/Manpage.md +++ b/docs/Manpage.md @@ -920,8 +920,6 @@ non-zero status if any errors are found. Include the RuboCop cop name for each violation in the output. * `--display-filename`: Prefix every line of output with the file or formula name being audited, to make output easy to grep. -* `--display-failures-only`: - Only display casks that fail the audit. This is the default for formulae. * `--skip-style`: Skip running non-RuboCop style checks. Useful if you plan on running `brew style` separately. Enabled by default unless a formula is specified by name. * `-D`, `--audit-debug`: @@ -2270,6 +2268,9 @@ example, run `export HOMEBREW_NO_INSECURE_REDIRECT=1` rather than just - `HOMEBREW_PRY`
If set, use Pry for the `brew irb` command. +- `HOMEBREW_UPGRADE_GREEDY` +
If set, pass `--greedy` to all cask upgrade commands. + - `HOMEBREW_SIMULATE_MACOS_ON_LINUX`
If set, running Homebrew on Linux will simulate certain macOS code paths. This is useful when auditing macOS formulae while on Linux. diff --git a/manpages/brew.1 b/manpages/brew.1 index 12c21ddb93..e62500261b 100644 --- a/manpages/brew.1 +++ b/manpages/brew.1 @@ -1287,10 +1287,6 @@ Include the RuboCop cop name for each violation in the output\. Prefix every line of output with the file or formula name being audited, to make output easy to grep\. . .TP -\fB\-\-display\-failures\-only\fR -Only display casks that fail the audit\. This is the default for formulae\. -. -.TP \fB\-\-skip\-style\fR Skip running non\-RuboCop style checks\. Useful if you plan on running \fBbrew style\fR separately\. Enabled by default unless a formula is specified by name\. . @@ -3341,6 +3337,12 @@ If set, \fBbrew install \fR will use this URL to download PyPI package If set, use Pry for the \fBbrew irb\fR command\. . .TP +\fBHOMEBREW_UPGRADE_GREEDY\fR +. +.br +If set, pass \fB\-\-greedy\fR to all cask upgrade commands\. +. +.TP \fBHOMEBREW_SIMULATE_MACOS_ON_LINUX\fR . .br From 8963335eea028a0d7d8a9485bd8ff6802bc85494 Mon Sep 17 00:00:00 2001 From: Carlo Cabrera <30379873+carlocab@users.noreply.github.com> Date: Sat, 8 Apr 2023 01:33:53 +0800 Subject: [PATCH 39/39] determine-test-runners: remove fake runner Based on discussion from Homebrew/homebrew-core#127236. --- Library/Homebrew/dev-cmd/determine-test-runners.rb | 6 ------ 1 file changed, 6 deletions(-) diff --git a/Library/Homebrew/dev-cmd/determine-test-runners.rb b/Library/Homebrew/dev-cmd/determine-test-runners.rb index 40eec0e817..c3903988fe 100755 --- a/Library/Homebrew/dev-cmd/determine-test-runners.rb +++ b/Library/Homebrew/dev-cmd/determine-test-runners.rb @@ -44,12 +44,6 @@ module Homebrew runner_matrix = GitHubRunnerMatrix.new(testing_formulae, deleted_formulae, dependent_matrix: args.dependents?) runners = runner_matrix.active_runner_specs_hash - if !args.dependents? && runners.blank? - # If there are no tests to run, add a runner that is meant to do nothing - # to support making the `tests` job a required status check. - runners << { name: "macOS 13-arm64", runner: "ubuntu-latest", no_op: true } - end - github_output = ENV.fetch("GITHUB_OUTPUT") File.open(github_output, "a") do |f| f.puts("runners=#{runners.to_json}")