brew/Library/Homebrew/extend/os/linux/dev-cmd/determine-test-runners.rb
2023-04-03 20:36:45 +08:00

198 lines
6.9 KiB
Ruby
Executable File

# 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