Class: GitHubRunnerMatrix

Inherits:
Object show all
Defined in:
github_runner_matrix.rb

Constant Summary collapse

NEWEST_HOMEBREW_CORE_MACOS_RUNNER =

This constant is part of a private API. This constant may only be used in the Homebrew/brew repository. Third parties should avoid using this constant if possible, as it may be removed or changed without warning.

When bumping newest runner, run e.g. git log -p --reverse -G "sha256 tahoe" on homebrew/core and tag the first commit with a bottle e.g. git tag 15-sequoia f42c4a659e4da887fc714f8f41cc26794a4bb320 to allow people to jump to specific commits based on their macOS version.

:tahoe
OLDEST_HOMEBREW_CORE_MACOS_RUNNER =
:sonoma
NEWEST_HOMEBREW_CORE_INTEL_MACOS_RUNNER =
:sonoma

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(testing_formulae, deleted_formulae, all_supported:, dependent_matrix:, dependent_shards: nil) ⇒ void

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

Parameters:



33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
# File 'github_runner_matrix.rb', line 33

def initialize(testing_formulae, deleted_formulae, all_supported:, dependent_matrix:, dependent_shards: nil)
  if all_supported && (testing_formulae.present? || deleted_formulae.present? || dependent_matrix)
    raise ArgumentError, "all_supported is mutually exclusive to other arguments"
  end

  @testing_formulae = testing_formulae
  @deleted_formulae = deleted_formulae
  @all_supported = all_supported
  @dependent_matrix = dependent_matrix
  @dependent_shards = T.let(dependent_shards || 1, Integer)
  @compatible_testing_formulae = T.let({}, T::Hash[GitHubRunner, T::Array[TestRunnerFormula]])
  @formulae_with_untested_dependents = T.let({}, T::Hash[GitHubRunner, T::Array[TestRunnerFormula]])

  # gracefully handle non-GitHub Actions environments
  @github_run_id = T.let(
    if ENV.key?("GITHUB_ACTIONS")
      ENV.fetch("GITHUB_RUN_ID")
    else
      ENV.fetch("GITHUB_RUN_ID", "")
    end, String
  )
  @linux_self_hosted = T.let(ENV.fetch("HOMEBREW_LINUX_SELF_HOSTED", "false") == "true", T::Boolean)
  @runner_timeout = T.let(
    if ENV.fetch("HOMEBREW_MACOS_LONG_TIMEOUT", "false") == "true"
      GITHUB_ACTIONS_LONG_TIMEOUT
    else
      GITHUB_ACTIONS_SHORT_TIMEOUT
    end, Integer
  )

  @runners = T.let([], T::Array[GitHubRunner])
  generate_runners!

  freeze
end

Instance Attribute Details

#runnersArray<GitHubRunner> (readonly)

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

Returns:



22
23
24
# File 'github_runner_matrix.rb', line 22

def runners
  @runners
end

Instance Method Details

#active_runner_specs_hashArray<RunnerSpecHash>

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

Returns:

  • (Array<RunnerSpecHash>)


70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
# File 'github_runner_matrix.rb', line 70

def active_runner_specs_hash
  specs = runners.select(&:active)
                 .map(&:spec)
                 .map(&:to_h)
  return specs if !@dependent_matrix || @dependent_shards == 1

  specs.flat_map do |spec|
    (1..@dependent_shards).map do |shard|
      spec.merge(
        name:                      "#{spec.fetch(:name)} shard #{shard}/#{@dependent_shards}",
        runner:                    spec.fetch(:runner).sub("-deps", "-deps#{shard}").to_s,
        formulae_dependents_shard: "#{shard}/#{@dependent_shards}",
      )
    end
  end
end

#generate_runners!void

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

This method returns an undefined value.



88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
# File 'github_runner_matrix.rb', line 88

def generate_runners!
  return if @runners.present?

  if !@all_supported || @linux_self_hosted
    VALID_ARCHES.each do |arch|
      @runners << create_runner(:linux, arch, linux_runner_spec(arch, self_hosted: @linux_self_hosted))
    end
  end

  # Portable Ruby logic
  if @testing_formulae.any? { |tf| tf.name.start_with?("portable-") }
    x86_64_spec = MacOSRunnerSpec.new(
      name:    "macOS 10.15-cross x86_64",
      runner:  "10.15-cross-#{@github_run_id}",
      timeout: GITHUB_ACTIONS_LONG_TIMEOUT,
      cleanup: true,
    )
    x86_64_macos_version = MacOSVersion.new("10.15")
    @runners << create_runner(:macos, :x86_64, x86_64_spec, x86_64_macos_version)

    # odisabled: remove support for Big Sur September (or later) 2027
    arm64_spec = MacOSRunnerSpec.new(
      name:    "macOS 11-cross arm64",
      runner:  "11-arm64-cross-#{@github_run_id}",
      timeout: GITHUB_ACTIONS_LONG_TIMEOUT,
      cleanup: true,
    )
    arm64_macos_version = MacOSVersion.new("11")
    @runners << create_runner(:macos, :arm64, arm64_spec, arm64_macos_version)
    return
  end

  # Use GitHub Actions macOS Runner for testing dependents if compatible with timeout.
  use_github_runner = ENV.fetch("HOMEBREW_MACOS_BUILD_ON_GITHUB_RUNNER", "false") == "true"
  use_github_runner ||= @dependent_matrix
  use_github_runner &&= @runner_timeout <= GITHUB_ACTIONS_RUNNER_TIMEOUT

  MacOSVersion::SYMBOLS.each_value do |version|
    macos_version = MacOSVersion.new(version)
    next unless runner_enabled?(macos_version)

    github_runner_available = macos_version.between?(OLDEST_GITHUB_ACTIONS_ARM_MACOS_RUNNER,
                                                     NEWEST_GITHUB_ACTIONS_ARM_MACOS_RUNNER)

    runner, timeout = if use_github_runner && github_runner_available
      ["macos-#{version}", GITHUB_ACTIONS_RUNNER_TIMEOUT]
    elsif macos_version >= :monterey
      ["#{version}-arm64#{ephemeral_suffix}", @runner_timeout]
    else
      ["#{version}-arm64", @runner_timeout]
    end

    # We test recursive dependents on ARM macOS, so they can be slower than our Intel runners.
    timeout *= 2 if @dependent_matrix && timeout < GITHUB_ACTIONS_RUNNER_TIMEOUT
    spec = MacOSRunnerSpec.new(
      name:    "macOS #{version}-arm64",
      runner:,
      timeout:,
      cleanup: !runner.end_with?(ephemeral_suffix),
    )
    @runners << create_runner(:macos, :arm64, spec, macos_version)

    skip_intel_runner = !@all_supported && macos_version > NEWEST_HOMEBREW_CORE_INTEL_MACOS_RUNNER
    skip_intel_runner &&= @dependent_matrix || @testing_formulae.none? do |testing_formula|
      bottle_spec = testing_formula.formula.bottle_specification
      bottle_spec.tag?(Utils::Bottles.tag(macos_version.to_sym), no_older_versions: true) &&
        !bottle_spec.tag?(Utils::Bottles.tag(:all), no_older_versions: true)
    end
    next if skip_intel_runner

    github_runner_available = macos_version.between?(OLDEST_GITHUB_ACTIONS_INTEL_MACOS_RUNNER,
                                                     NEWEST_GITHUB_ACTIONS_INTEL_MACOS_RUNNER)

    runner, timeout = if use_github_runner && github_runner_available
      ["macos-#{version}", GITHUB_ACTIONS_RUNNER_TIMEOUT]
    else
      ["#{version}-x86_64#{ephemeral_suffix}", @runner_timeout]
    end

    # macOS 12-x86_64 is usually slower.
    timeout += 30 if macos_version <= :monterey
    # The ARM runners are typically over twice as fast as the Intel runners.
    timeout *= 2 if !(use_github_runner && github_runner_available) && timeout < GITHUB_ACTIONS_LONG_TIMEOUT
    spec = MacOSRunnerSpec.new(
      name:    "macOS #{version}-x86_64",
      runner:,
      timeout:,
      cleanup: !runner.end_with?(ephemeral_suffix),
    )
    @runners << create_runner(:macos, :x86_64, spec, macos_version)
  end

  @runners.freeze
end