
Without this, we sometimes mistakenly assign runners to dependents jobs when we don't have to.
113 lines
4.3 KiB
Ruby
113 lines
4.3 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
|
|
|
|
# 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
|