github_runner_matrix: filter incompatible testing formulae

`brew test-bot` can occasionally spend a long time doing nothing when
testing dependents.[^1] This is because it takes a long time to work out
that there is nothing to do.

To fix this, let's adjust `brew determine-test-runners` to pass on only
the list of formulae for which there is work to be done so that
`brew test-bot` doesn't need to waste time working this out.

[^1]: For example, it spends about 15 minutes doing nothing at https://github.com/Homebrew/homebrew-core/actions/runs/10500178069/job/29133091332?pr=180185
This commit is contained in:
Carlo Cabrera 2024-08-26 08:10:31 +08:00
parent 17e3bee3da
commit 3588f1b8c5
No known key found for this signature in database
GPG Key ID: C74D447FC549A1D0
3 changed files with 74 additions and 45 deletions

View File

@ -10,7 +10,15 @@ class GitHubRunnerMatrix
RunnerSpec = T.type_alias { T.any(LinuxRunnerSpec, MacOSRunnerSpec) } RunnerSpec = T.type_alias { T.any(LinuxRunnerSpec, MacOSRunnerSpec) }
private_constant :RunnerSpec private_constant :RunnerSpec
MacOSRunnerSpecHash = T.type_alias { { name: String, runner: String, timeout: Integer, cleanup: T::Boolean } } MacOSRunnerSpecHash = T.type_alias do
{
name: String,
runner: String,
timeout: Integer,
cleanup: T::Boolean,
testing_formulae: String,
}
end
private_constant :MacOSRunnerSpecHash private_constant :MacOSRunnerSpecHash
LinuxRunnerSpecHash = T.type_alias do LinuxRunnerSpecHash = T.type_alias do
@ -21,6 +29,7 @@ class GitHubRunnerMatrix
workdir: String, workdir: String,
timeout: Integer, timeout: Integer,
cleanup: T::Boolean, cleanup: T::Boolean,
testing_formulae: String,
} }
end end
private_constant :LinuxRunnerSpecHash private_constant :LinuxRunnerSpecHash
@ -49,6 +58,8 @@ class GitHubRunnerMatrix
@deleted_formulae = T.let(deleted_formulae, T::Array[String]) @deleted_formulae = T.let(deleted_formulae, T::Array[String])
@all_supported = T.let(all_supported, T::Boolean) @all_supported = T.let(all_supported, T::Boolean)
@dependent_matrix = T.let(dependent_matrix, T::Boolean) @dependent_matrix = T.let(dependent_matrix, T::Boolean)
@compatible_testing_formulae = T.let({}, T::Hash[GitHubRunner, T::Array[TestRunnerFormula]])
@formulae_with_untested_dependents = T.let({}, T::Hash[GitHubRunner, T::Array[TestRunnerFormula]])
@runners = T.let([], T::Array[GitHubRunner]) @runners = T.let([], T::Array[GitHubRunner])
generate_runners! generate_runners!
@ -102,6 +113,7 @@ class GitHubRunnerMatrix
raise "Unexpected arch: #{arch}" if VALID_ARCHES.exclude?(arch) raise "Unexpected arch: #{arch}" if VALID_ARCHES.exclude?(arch)
runner = GitHubRunner.new(platform:, arch:, spec:, macos_version:) runner = GitHubRunner.new(platform:, arch:, spec:, macos_version:)
runner.spec.testing_formulae += testable_formulae(runner)
runner.active = active_runner?(runner) runner.active = active_runner?(runner)
runner.freeze runner.freeze
end end
@ -185,47 +197,50 @@ class GitHubRunnerMatrix
@runners.freeze @runners.freeze
end end
sig { params(runner: GitHubRunner).returns(T::Array[String]) }
def testable_formulae(runner)
formulae = if @dependent_matrix
formulae_with_untested_dependents(runner)
else
compatible_testing_formulae(runner)
end
formulae.map(&:name)
end
sig { params(runner: GitHubRunner).returns(T::Boolean) } sig { params(runner: GitHubRunner).returns(T::Boolean) }
def active_runner?(runner) def active_runner?(runner)
if @dependent_matrix return true if @all_supported || @deleted_formulae.present?
formulae_have_untested_dependents?(runner)
elsif !@all_supported && @deleted_formulae.empty?
compatible_formulae = @testing_formulae.dup
testable_formulae(runner).present?
end
sig { params(runner: GitHubRunner).returns(T::Array[TestRunnerFormula]) }
def compatible_testing_formulae(runner)
@compatible_testing_formulae[runner] ||= begin
platform = runner.platform platform = runner.platform
arch = runner.arch arch = runner.arch
macos_version = runner.macos_version macos_version = runner.macos_version
compatible_formulae.select! do |formula| @testing_formulae.select do |formula|
next false if macos_version && !formula.compatible_with?(macos_version) next false if macos_version && !formula.compatible_with?(macos_version)
formula.public_send(:"#{platform}_compatible?") && formula.public_send(:"#{platform}_compatible?") &&
formula.public_send(:"#{arch}_compatible?") formula.public_send(:"#{arch}_compatible?")
end end
compatible_formulae.present?
else
true
end end
end end
sig { params(runner: GitHubRunner).returns(T::Boolean) } sig { params(runner: GitHubRunner).returns(T::Array[TestRunnerFormula]) }
def formulae_have_untested_dependents?(runner) def formulae_with_untested_dependents(runner)
@formulae_with_untested_dependents[runner] ||= begin
platform = runner.platform platform = runner.platform
arch = runner.arch arch = runner.arch
macos_version = runner.macos_version macos_version = runner.macos_version
@testing_formulae.any? do |formula| compatible_testing_formulae(runner).select 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.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:, arch:, macos_version: macos_version&.to_sym) compatible_dependents = formula.dependents(platform:, arch:, macos_version: macos_version&.to_sym)
.dup .select do |dependent_f|
compatible_dependents.select! do |dependent_f|
next false if macos_version && !dependent_f.compatible_with?(macos_version) next false if macos_version && !dependent_f.compatible_with?(macos_version)
dependent_f.public_send(:"#{platform}_compatible?") && dependent_f.public_send(:"#{platform}_compatible?") &&
@ -238,3 +253,4 @@ class GitHubRunnerMatrix
end end
end end
end end
end

View File

@ -8,6 +8,7 @@ class LinuxRunnerSpec < T::Struct
const :workdir, String const :workdir, String
const :timeout, Integer const :timeout, Integer
const :cleanup, T::Boolean const :cleanup, T::Boolean
prop :testing_formulae, T::Array[String], default: []
sig { sig {
returns({ returns({
@ -17,6 +18,7 @@ class LinuxRunnerSpec < T::Struct
workdir: String, workdir: String,
timeout: Integer, timeout: Integer,
cleanup: T::Boolean, cleanup: T::Boolean,
testing_formulae: String,
}) })
} }
def to_h def to_h
@ -27,6 +29,7 @@ class LinuxRunnerSpec < T::Struct
workdir:, workdir:,
timeout:, timeout:,
cleanup:, cleanup:,
testing_formulae: testing_formulae.join(","),
} }
end end
end end

View File

@ -6,14 +6,24 @@ class MacOSRunnerSpec < T::Struct
const :runner, String const :runner, String
const :timeout, Integer const :timeout, Integer
const :cleanup, T::Boolean const :cleanup, T::Boolean
prop :testing_formulae, T::Array[String], default: []
sig { returns({ name: String, runner: String, timeout: Integer, cleanup: T::Boolean }) } sig {
returns({
name: String,
runner: String,
timeout: Integer,
cleanup: T::Boolean,
testing_formulae: String,
})
}
def to_h def to_h
{ {
name:, name:,
runner:, runner:,
timeout:, timeout:,
cleanup:, cleanup:,
testing_formulae: testing_formulae.join(","),
} }
end end
end end