
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.
111 lines
4.2 KiB
Ruby
111 lines
4.2 KiB
Ruby
# 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
|