Skip to content

Commit 589bfc7

Browse files
authored
Merge pull request #21380 from Homebrew/feat/cask/arch_ci
Make Cask CI generate correctly for partial arch dependencies
2 parents 730d145 + 8a783d7 commit 589bfc7

5 files changed

Lines changed: 361 additions & 15 deletions

File tree

Library/Homebrew/cask/artifact.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,5 +52,7 @@ module Artifact
5252
::Cask::Artifact::VstPlugin,
5353
::Cask::Artifact::Vst3Plugin,
5454
].freeze
55+
56+
LINUX_ONLY_ARTIFACTS = T.let([].freeze, T::Array[T.untyped])
5557
end
5658
end

Library/Homebrew/cask/cask.rb

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -168,11 +168,41 @@ def supports_macos? = true
168168
def supports_linux?
169169
return true if font?
170170

171-
return false if artifacts.any? do |artifact|
172-
::Cask::Artifact::MACOS_ONLY_ARTIFACTS.include?(artifact.class)
171+
# Cache the os value before contains_os_specific_artifacts? refreshes the cask
172+
# (the refresh clears @dsl.os in generic/non-OS-specific contexts)
173+
os_value = @dsl.os
174+
175+
return false if contains_os_specific_artifacts?
176+
177+
os_value.present?
178+
end
179+
180+
sig { returns(T::Boolean) }
181+
def contains_os_specific_artifacts?
182+
return false unless @dsl.on_system_blocks_exist?
183+
184+
any_loaded = T.let(false, T::Boolean)
185+
@contains_os_specific_artifacts ||= begin
186+
OnSystem::VALID_OS_ARCH_TAGS.each do |bottle_tag|
187+
Homebrew::SimulateSystem.with_tag(bottle_tag) do
188+
refresh
189+
190+
any_loaded = true if artifacts.any? do |artifact|
191+
(bottle_tag.linux? && ::Cask::Artifact::MACOS_ONLY_ARTIFACTS.include?(artifact.class)) ||
192+
(bottle_tag.macos? && ::Cask::Artifact::LINUX_ONLY_ARTIFACTS.include?(artifact.class))
193+
end
194+
end
195+
rescue CaskInvalidError
196+
# Invalid for this OS/arch tag; treat as having no OS-specific artifacts.
197+
next
198+
ensure
199+
refresh
200+
end
201+
202+
any_loaded
173203
end
174204

175-
@dsl.os.present?
205+
@contains_os_specific_artifacts
176206
end
177207

178208
# The caskfile is needed during installation when there are

Library/Homebrew/dev-cmd/generate-cask-ci-matrix.rb

Lines changed: 34 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -139,21 +139,43 @@ def filter_runners(cask)
139139
RUNNERS.dup
140140
end
141141

142-
filtered_runners = filtered_runners.merge(LINUX_RUNNERS) if cask.supports_linux?
143-
144-
archs = architectures(cask:)
142+
macos_archs = architectures(cask:, os: :macos)
145143
filtered_runners.select! do |runner, _|
146-
archs.include?(runner.fetch(:arch))
144+
macos_archs.include?(runner.fetch(:arch))
147145
end
148146

149-
filtered_runners
147+
return filtered_runners unless cask.supports_linux?
148+
149+
linux_archs = architectures(cask:, os: :linux)
150+
linux_runners = LINUX_RUNNERS.select do |runner, _|
151+
linux_archs.include?(runner.fetch(:arch))
152+
end
153+
154+
filtered_runners.merge(linux_runners)
150155
end
151156

152-
sig { params(cask: Cask::Cask).returns(T::Array[Symbol]) }
153-
def architectures(cask:)
154-
return RUNNERS.keys.map { |r| r.fetch(:arch).to_sym }.uniq.sort if cask.depends_on.arch.blank?
157+
private
158+
159+
sig { params(cask: Cask::Cask, os: Symbol).returns(T::Array[Symbol]) }
160+
def architectures(cask:, os:)
161+
architectures = T.let([], T::Array[Symbol])
162+
[:arm, :intel].each do |arch|
163+
tag = Utils::Bottles::Tag.new(system: os, arch: arch)
164+
Homebrew::SimulateSystem.with_tag(tag) do
165+
cask.refresh
166+
167+
if cask.depends_on.arch.blank?
168+
architectures = RUNNERS.keys.map { |r| r.fetch(:arch).to_sym }.uniq.sort
169+
next
170+
end
171+
172+
architectures = cask.depends_on.arch.map { |arch| arch[:type] }
173+
end
174+
rescue ::Cask::CaskInvalidError
175+
# Can't read cask for this system-arch combination.
176+
end
155177

156-
cask.depends_on.arch.map { |arch| arch[:type] }.uniq.sort
178+
architectures
157179
end
158180

159181
sig {
@@ -251,11 +273,11 @@ def generate_matrix(tap, labels: [], cask_names: [], skip_install: false, new_ca
251273
cask = Cask::CaskLoader.load(path.expand_path)
252274

253275
runners, multi_os = runners(cask:)
254-
runners.product(architectures(cask:)).filter_map do |runner, arch|
276+
runners.product(architectures(cask:, os: :macos)).filter_map do |runner, arch|
255277
native_runner_arch = arch == runner.fetch(:arch)
256-
# we don't need to run simulated archs on Linux
278+
# we don't need to run simulated archs on Linux or macOS Sequoia
279+
# because they exist as real GitHub hosted runner
257280
next if runner.fetch(:symbol) == :linux && !native_runner_arch
258-
# we don't need to run simulated archs on macOS
259281
next if runner.fetch(:symbol) == :sequoia && !native_runner_arch
260282

261283
# If it's just a single OS test then we can just use the two real arch runners.

Library/Homebrew/test/cask/cask_spec.rb

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,68 @@
299299
end
300300
end
301301

302+
describe "#contains_os_specific_artifacts?" do
303+
it "returns false when there are no OSes defined" do
304+
cask = described_class.new("test-no-os") do
305+
version "0.0.1,2"
306+
307+
url "https://brew.sh/test-0.0.1.dmg"
308+
name "Test"
309+
desc "Test cask"
310+
homepage "https://brew.sh"
311+
end
312+
313+
expect(cask.contains_os_specific_artifacts?).to be false
314+
end
315+
316+
it "returns false when there are no artifacts" do
317+
cask = described_class.new("test-os-no-artifacts") do
318+
os macos: "mac", linux: "Linux"
319+
version "0.0.1,2"
320+
321+
url "https://brew.sh/test-0.0.1.dmg"
322+
name "Test"
323+
desc "Test cask"
324+
homepage "https://brew.sh"
325+
end
326+
327+
expect(cask.contains_os_specific_artifacts?).to be false
328+
end
329+
330+
it "returns false when there are scoped app" do
331+
cask = described_class.new("test-macos-app-artifact") do
332+
version "0.0.1,2"
333+
334+
url "https://brew.sh/test-0.0.1.dmg"
335+
name "Test"
336+
desc "Test cask"
337+
homepage "https://brew.sh"
338+
339+
on_macos do
340+
app "Test.app"
341+
end
342+
end
343+
344+
expect(cask.contains_os_specific_artifacts?).to be false
345+
end
346+
347+
it "returns true when there are unscoped app artifacts" do
348+
cask = described_class.new("test-os-app-artifact") do
349+
os macos: "mac", linux: "Linux"
350+
version "0.0.1,2"
351+
352+
url "https://brew.sh/test-0.0.1.dmg"
353+
name "Test"
354+
desc "Test cask"
355+
homepage "https://brew.sh"
356+
357+
app "Test.app"
358+
end
359+
360+
expect(cask.contains_os_specific_artifacts?).to be true
361+
end
362+
end
363+
302364
describe "#to_h" do
303365
let(:expected_json) { (TEST_FIXTURE_DIR/"cask/everything.json").read.strip }
304366

0 commit comments

Comments
 (0)