Skip to content

Commit 740bac5

Browse files
committed
feat: add appimage support for linux
1 parent 0ac4836 commit 740bac5

File tree

10 files changed

+151
-23
lines changed

10 files changed

+151
-23
lines changed

Library/Homebrew/cask/artifact.rb

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
# frozen_string_literal: true
33

44
require "cask/artifact/app"
5+
require "cask/artifact/appimage"
56
require "cask/artifact/artifact" # generic 'artifact' stanza
67
require "cask/artifact/audio_unit_plugin"
78
require "cask/artifact/binary"
@@ -53,5 +54,9 @@ module Artifact
5354
::Cask::Artifact::VstPlugin,
5455
::Cask::Artifact::Vst3Plugin,
5556
].freeze
57+
58+
LINUX_ONLY_ARTIFACTS = [
59+
::Cask::Artifact::AppImage,
60+
].freeze
5661
end
5762
end
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# typed: strict
2+
# frozen_string_literal: true
3+
4+
require "cask/artifact/symlinked"
5+
6+
module Cask
7+
module Artifact
8+
class AppImage < Symlinked
9+
sig { params(target: T.any(String, Pathname)).returns(Pathname) }
10+
def resolve_target(target)
11+
config.appimagedir/target
12+
end
13+
end
14+
end
15+
end

Library/Homebrew/cask/cask.rb

Lines changed: 53 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -164,17 +164,66 @@ def font?
164164
end
165165

166166
sig { returns(T::Boolean) }
167-
def supports_macos? = true
167+
def supports_macos?
168+
return true if font?
169+
170+
if @dsl.on_system_blocks_exist?
171+
any_loaded = false
172+
begin
173+
OnSystem::VALID_OS_ARCH_TAGS.each do |bottle_tag|
174+
next unless bottle_tag.macos?
175+
Homebrew::SimulateSystem.with_tag(bottle_tag) do
176+
refresh
177+
178+
any_loaded = true
179+
return false if artifacts.any? do |artifact|
180+
::Cask::Artifact::LINUX_ONLY_ARTIFACTS.include?(artifact.class)
181+
end
182+
end
183+
end
184+
rescue CaskInvalidError => e
185+
# Invalid cask for macOS
186+
odebug e.message
187+
ensure
188+
refresh
189+
end
190+
191+
return false if !any_loaded
192+
end
193+
194+
true
195+
end
168196

169197
sig { returns(T::Boolean) }
170198
def supports_linux?
171199
return true if font?
172200

173-
return false if artifacts.any? do |artifact|
174-
::Cask::Artifact::MACOS_ONLY_ARTIFACTS.include?(artifact.class)
201+
if @dsl.on_system_blocks_exist?
202+
any_loaded = false
203+
begin
204+
OnSystem::VALID_OS_ARCH_TAGS.each do |bottle_tag|
205+
next unless bottle_tag.linux?
206+
Homebrew::SimulateSystem.with_tag(bottle_tag) do
207+
refresh
208+
209+
any_loaded = true
210+
return false if artifacts.any? do |artifact|
211+
::Cask::Artifact::MACOS_ONLY_ARTIFACTS.include?(artifact.class)
212+
end
213+
end
214+
end
215+
rescue CaskInvalidError => e
216+
# Invalid cask for Linux
217+
218+
ensure
219+
refresh
220+
end
221+
222+
return false if !any_loaded
175223
end
176224

177-
@dsl.os.present?
225+
# Only assume Linux support if there is an explicit `on_system` stanza
226+
@dsl.on_system_blocks_exist?
178227
end
179228

180229
# The caskfile is needed during installation when there are

Library/Homebrew/cask/config.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ class Config
1616
DEFAULT_DIRS = T.let(
1717
{
1818
appdir: "/Applications",
19+
appimagedir: "~/Applications",
1920
keyboard_layoutdir: "/Library/Keyboard Layouts",
2021
colorpickerdir: "~/Library/ColorPickers",
2122
prefpanedir: "~/Library/PreferencePanes",
@@ -49,6 +50,7 @@ def self.from_args(args)
4950
args = T.unsafe(args)
5051
new(explicit: {
5152
appdir: args.appdir,
53+
appimagedir: args.appimagedir,
5254
keyboard_layoutdir: args.keyboard_layoutdir,
5355
colorpickerdir: args.colorpickerdir,
5456
prefpanedir: args.prefpanedir,

Library/Homebrew/cask/dsl.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ class DSL
3838
ORDINARY_ARTIFACT_CLASSES = [
3939
Artifact::Installer,
4040
Artifact::App,
41+
Artifact::AppImage,
4142
Artifact::Artifact,
4243
Artifact::AudioUnitPlugin,
4344
Artifact::Binary,

Library/Homebrew/cli/parser.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,10 @@ def self.global_cask_options
6666
description: "Target location for Applications " \
6767
"(default: `#{Cask::Config::DEFAULT_DIRS[:appdir]}`).",
6868
}],
69+
[:flag, "--appimagedir=", {
70+
description: "Target location for AppImages " \
71+
"(default: `#{Cask::Config::DEFAULT_DIRS[:appimagedir]}`).",
72+
}],
6973
[:flag, "--keyboard-layoutdir=", {
7074
description: "Target location for Keyboard Layouts " \
7175
"(default: `#{Cask::Config::DEFAULT_DIRS[:keyboard_layoutdir]}`).",

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

Lines changed: 46 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,8 @@ def run
6767
pr_url = args.named if args.url?
6868
syntax_only = args.syntax_only?
6969

70-
repository = ENV.fetch("GITHUB_REPOSITORY", nil)
70+
# repository = ENV.fetch("GITHUB_REPOSITORY", nil)
71+
repository = "homebrew/homebrew-cask" # For testing outside of GitHub Actions
7172
raise UsageError, "The `$GITHUB_REPOSITORY` environment variable must be set." if repository.blank?
7273

7374
tap = T.let(Tap.fetch(repository), Tap)
@@ -127,33 +128,62 @@ def run
127128

128129
sig { params(cask: Cask::Cask).returns(T::Hash[T::Hash[Symbol, T.any(Symbol, String)], Float]) }
129130
def filter_runners(cask)
130-
filtered_macos_runners = RUNNERS.select do |runner, _|
131-
runner[:symbol] != :linux &&
132-
cask.depends_on.macos.present? &&
133-
cask.depends_on.macos.allows?(MacOSVersion.from_symbol(T.must(runner[:symbol]).to_sym))
134-
end
135131

136-
filtered_runners = if filtered_macos_runners.any?
132+
filtered_runners = if cask.supports_macos?
133+
filtered_macos_runners = MACOS_RUNNERS.select do |runner, _|
134+
cask.depends_on.macos.present? &&
135+
cask.depends_on.macos.allows?(MacOSVersion.from_symbol(T.must(runner[:symbol]).to_sym))
136+
end
137+
138+
return MACOS_RUNNERS.dup unless filtered_macos_runners.any?
139+
137140
filtered_macos_runners
138-
else
139-
RUNNERS.dup
140141
end
141142

142-
filtered_runners = filtered_runners.merge(LINUX_RUNNERS) if cask.supports_linux?
143-
144143
archs = architectures(cask:)
144+
odebug "Architectures macOS: #{archs}" if archs.any?
145145
filtered_runners.select! do |runner, _|
146146
archs.include?(runner.fetch(:arch))
147147
end
148148

149+
odebug "Filtered runners: #{filtered_runners.keys.map { |r| r[:name] }}" if filtered_runners.any?
150+
if cask.supports_linux?
151+
filtered_linux_runners = LINUX_RUNNERS.dup
152+
153+
archs = architectures(cask:, os: :linux, arch: :intel)
154+
filtered_linux_runners.select! do |runner, _|
155+
archs.include?(runner.fetch(:arch))
156+
end
157+
158+
filtered_runners.merge!(filtered_linux_runners)
159+
end
160+
161+
odebug "Architectures linux: #{archs}" if archs.any?
162+
odebug "Filtered runners: #{filtered_runners.keys.map { |r| r[:name] }}" if filtered_runners.any?
163+
149164
filtered_runners
150165
end
151166

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?
167+
sig { params(cask: Cask::Cask, os: Symbol, arch: Symbol).returns(T::Array[Symbol]) }
168+
def architectures(cask:, os: :macos, arch: :arm)
169+
architectures = []
170+
begin
171+
tag = Utils::Bottles::Tag.new(system: os, arch: arch)
172+
Homebrew::SimulateSystem.with_tag(tag) do
173+
cask.refresh
174+
175+
if cask.depends_on.arch.blank?
176+
architectures = RUNNERS.keys.map { |r| r.fetch(:arch).to_sym }.uniq.sort
177+
next
178+
end
179+
180+
architectures = cask.depends_on.arch.map { |arch| arch[:type] }
181+
end
182+
rescue ::Cask::CaskInvalidError => e
183+
# Can't read cask for this system-arch combination.
184+
end
155185

156-
cask.depends_on.arch.map { |arch| arch[:type] }.uniq.sort
186+
return architectures
157187
end
158188

159189
sig {
@@ -255,7 +285,7 @@ def generate_matrix(tap, labels: [], cask_names: [], skip_install: false, new_ca
255285
native_runner_arch = arch == runner.fetch(:arch)
256286
# we don't need to run simulated archs on Linux
257287
next if runner.fetch(:symbol) == :linux && !native_runner_arch
258-
# we don't need to run simulated archs on macOS
288+
# we don't need to run simulated archs on macOS Sequoia
259289
next if runner.fetch(:symbol) == :sequoia && !native_runner_arch
260290

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

Library/Homebrew/extend/os/cask/installer.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@
22
# frozen_string_literal: true
33

44
require "extend/os/linux/cask/installer" if OS.linux?
5+
require "extend/os/mac/cask/installer" if OS.mac?

Library/Homebrew/extend/os/linux/cask/installer.rb

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,7 @@ module Installer
1111

1212
sig { void }
1313
def check_stanza_os_requirements
14-
return unless artifacts.any? do |artifact|
15-
::Cask::Artifact::MACOS_ONLY_ARTIFACTS.include?(artifact.class)
16-
end
14+
return unless @cask.supports_linux?
1715

1816
raise ::Cask::CaskError, "macOS is required for this software."
1917
end
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# typed: strict
2+
# frozen_string_literal: true
3+
4+
module OS
5+
module Mac
6+
module Cask
7+
module Installer
8+
extend T::Helpers
9+
10+
requires_ancestor { ::Cask::Installer }
11+
12+
sig { void }
13+
def check_stanza_os_requirements
14+
return unless @cask.supports_macos?
15+
16+
raise ::Cask::CaskError, "Linux is required for this software."
17+
end
18+
end
19+
end
20+
end
21+
end
22+
23+
Cask::Installer.prepend(OS::Mac::Cask::Installer)

0 commit comments

Comments
 (0)