Move more code out of dev-cmd
				
					
				
			This commit is contained in:
		
							parent
							
								
									fc0c375cff
								
							
						
					
					
						commit
						87373ff12a
					
				@ -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?}")
 | 
			
		||||
 | 
			
		||||
@ -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
 | 
			
		||||
 | 
			
		||||
@ -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
 | 
			
		||||
 | 
			
		||||
@ -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]])
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user