mirror of
https://github.com/Homebrew/brew.git
synced 2026-08-12 22:29:27 +04:00
Treat cask OS support as explicit data
- Align casks with formulae: platform support comes from `depends_on :macos`/`:linux`/`macos:` data rather than generation heuristics guessing intent from `os` stanzas, Linux checksums or `on_linux` blocks. - `Cask#to_hash_with_variations` now emits variations for every valid OS/arch tag whenever `on_system` blocks exist, matching `Formula#to_hash_with_variations`; the Linux-specific gate and its `sha256_set_for_linux?` and `on_linux_blocks_exist?` tracking are removed. macOS-only casks publish truthful Linux variations, e.g. a `null` `sha256`, instead of omitting them. - `Cask::Installer` gains a first-class unsupported-system error: API-loaded casks with no activatable artifact for the running system fail with "This cask is not available on macOS/Linux." instead of installing nothing. Audited casks always declare an activatable artifact for the systems they support, so missing artifacts in API data mean the system is unsupported. Source loads keep working for unaudited casks, e.g. naked containers. - A sweep of the full homebrew/cask generation pipeline (all casks, both Linux tags, including internal per-tag payloads) confirmed no cask needs new `depends_on` annotations and nothing regresses.
This commit is contained in:
@@ -616,8 +616,6 @@ module Cask
|
||||
if dsl!.on_system_blocks_exist?
|
||||
begin
|
||||
OnSystem::VALID_OS_ARCH_TAGS.each do |bottle_tag|
|
||||
next if bottle_tag.linux? && dsl!.os.nil? && !dsl!.sha256_set_for_linux? && !dsl!.on_linux_blocks_exist?
|
||||
|
||||
macos_requirements = [depends_on.macos, depends_on.maximum_macos].compact
|
||||
next if bottle_tag.macos? &&
|
||||
macos_requirements.present? &&
|
||||
|
||||
@@ -128,7 +128,6 @@ module Cask
|
||||
:no_autobump_message,
|
||||
:on_system_blocks_exist?,
|
||||
:on_os_blocks_exist?,
|
||||
:on_linux_blocks_exist?,
|
||||
:on_system_block_min_os,
|
||||
:depends_on_set_in_block?,
|
||||
*ORDINARY_ARTIFACT_CLASSES.map(&:dsl_key),
|
||||
@@ -233,13 +232,11 @@ module Cask
|
||||
@no_autobump_message = T.let(nil, T.nilable(T.any(String, Symbol)))
|
||||
@on_system_blocks_exist = T.let(false, T::Boolean)
|
||||
@on_os_blocks_exist = T.let(false, T::Boolean)
|
||||
@on_linux_blocks_exist = T.let(false, T::Boolean)
|
||||
@on_system_block_min_os = T.let(nil, T.nilable(MacOSVersion))
|
||||
@os = T.let(nil, T.nilable(String))
|
||||
@os_set_in_block = T.let(false, T::Boolean)
|
||||
@rename = T.let([], T::Array[DSL::Rename])
|
||||
@sha256 = T.let(nil, T.nilable(T.any(Checksum, Symbol)))
|
||||
@sha256_set_for_linux = T.let(false, T::Boolean)
|
||||
@sha256_set_in_block = T.let(false, T::Boolean)
|
||||
@staged_path = T.let(nil, T.nilable(Pathname))
|
||||
@token = T.let(cask.token, String)
|
||||
@@ -267,12 +264,6 @@ module Cask
|
||||
sig { returns(T::Boolean) }
|
||||
def on_os_blocks_exist? = @on_os_blocks_exist
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
def on_linux_blocks_exist? = @on_linux_blocks_exist
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
def sha256_set_for_linux? = @sha256_set_for_linux
|
||||
|
||||
# Specifies the cask's name.
|
||||
#
|
||||
# NOTE: Multiple names can be specified.
|
||||
@@ -560,7 +551,6 @@ module Cask
|
||||
if arm.present? || x86_64.present? || x86_64_linux.present? || arm64_linux.present?
|
||||
@on_system_blocks_exist = true
|
||||
end
|
||||
@sha256_set_for_linux = true if x86_64_linux.present? || arm64_linux.present?
|
||||
|
||||
val = arg || on_system_conditional(
|
||||
macos: on_arch_conditional(arm:, intel: x86_64),
|
||||
|
||||
@@ -358,6 +358,7 @@ on_request: true)
|
||||
sig { void }
|
||||
def check_requirements
|
||||
check_stanza_os_requirements
|
||||
check_supported_system
|
||||
check_macos_requirements
|
||||
check_arch_requirements
|
||||
end
|
||||
@@ -369,6 +370,20 @@ on_request: true)
|
||||
raise CaskError, "#{@cask}: This cask requires Linux."
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def check_supported_system
|
||||
# Audited casks always have an activatable artifact for the systems they
|
||||
# support, so API data without one means this system is unsupported.
|
||||
# Source loads keep working for unaudited casks, e.g. naked containers.
|
||||
return unless @cask.loaded_from_api?
|
||||
return if @cask.artifacts.any? do |artifact|
|
||||
artifact.respond_to?(:install_phase) || artifact.is_a?(Artifact::StageOnly)
|
||||
end
|
||||
|
||||
os_name = Homebrew::SimulateSystem.simulating_or_running_on_macos? ? "macOS" : "Linux"
|
||||
raise CaskError, "#{@cask}: This cask is not available on #{os_name}."
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def check_macos_requirements
|
||||
macos_requirement = [@cask.depends_on.macos, @cask.depends_on.maximum_macos].compact.find { !it.satisfied? }
|
||||
|
||||
@@ -88,7 +88,6 @@ module OnSystem
|
||||
base.define_method(:"on_#{base_os}") do |&block|
|
||||
@on_system_blocks_exist = T.let(true, T.nilable(TrueClass))
|
||||
@on_os_blocks_exist = T.let(true, T.nilable(TrueClass))
|
||||
@on_linux_blocks_exist = T.let(true, T.nilable(TrueClass)) if base_os == :linux
|
||||
|
||||
return unless OnSystem.os_condition_met? OnSystem.condition_from_method_name(T.must(__method__))
|
||||
|
||||
@@ -105,7 +104,6 @@ module OnSystem
|
||||
base.define_method(:on_system) do |linux, macos:, &block|
|
||||
@on_system_blocks_exist = T.let(true, T.nilable(TrueClass))
|
||||
@on_os_blocks_exist = T.let(true, T.nilable(TrueClass))
|
||||
@on_linux_blocks_exist = T.let(true, T.nilable(TrueClass))
|
||||
|
||||
raise ArgumentError, "The first argument to `on_system` must be `:linux`" if linux != :linux
|
||||
|
||||
|
||||
-3
@@ -159,9 +159,6 @@ class Cask::Cask
|
||||
sig { params(args: T.untyped, block: T.untyped).returns(T.untyped) }
|
||||
def no_autobump_message(*args, &block); end
|
||||
|
||||
sig { params(args: T.untyped, block: T.untyped).returns(T::Boolean) }
|
||||
def on_linux_blocks_exist?(*args, &block); end
|
||||
|
||||
sig { params(args: T.untyped, block: T.untyped).returns(T::Boolean) }
|
||||
def on_os_blocks_exist?(*args, &block); end
|
||||
|
||||
|
||||
@@ -757,6 +757,16 @@ RSpec.describe Cask::Cask, :cask do
|
||||
"url": "file://#{TEST_FIXTURE_DIR}/cask/caffeine/darwin/1.0.0/intel.zip",
|
||||
"version": "1.0.0",
|
||||
"sha256": "1866dfa833b123bb8fe7fa7185ebf24d28d300d0643d75798bc23730af734216"
|
||||
},
|
||||
"x86_64_linux": {
|
||||
"url": "file://#{TEST_FIXTURE_DIR}/cask/caffeine/darwin//intel.zip",
|
||||
"version": null,
|
||||
"sha256": null
|
||||
},
|
||||
"arm64_linux": {
|
||||
"url": "file://#{TEST_FIXTURE_DIR}/cask/caffeine/darwin-arm64//arm.zip",
|
||||
"version": null,
|
||||
"sha256": null
|
||||
}
|
||||
}
|
||||
JSON
|
||||
@@ -795,6 +805,13 @@ RSpec.describe Cask::Cask, :cask do
|
||||
"catalina": {
|
||||
"url": "file://#{TEST_FIXTURE_DIR}/cask/caffeine-intel.zip",
|
||||
"sha256": "8c62a2b791cf5f0da6066a0a4b6e85f62949cd60975da062df44adf887f4370b"
|
||||
},
|
||||
"x86_64_linux": {
|
||||
"url": "file://#{TEST_FIXTURE_DIR}/cask/caffeine-intel.zip",
|
||||
"sha256": null
|
||||
},
|
||||
"arm64_linux": {
|
||||
"sha256": null
|
||||
}
|
||||
}
|
||||
JSON
|
||||
|
||||
@@ -231,6 +231,41 @@ RSpec.describe Cask::Installer, :cask do
|
||||
end.to raise_error(Cask::CaskError, /\Awith-depends-on-arch: This cask depends on hardware architecture/)
|
||||
end
|
||||
|
||||
it "names the cask when it has nothing to install on this system" do
|
||||
no_artifacts_cask = Cask::Cask.new("with-no-artifacts", loaded_from_api: true) do
|
||||
version "1.0"
|
||||
sha256 :no_check
|
||||
url "https://brew.sh/x.zip"
|
||||
end
|
||||
expect do
|
||||
described_class.new(no_artifacts_cask).check_supported_system
|
||||
end.to raise_error(Cask::CaskError, "with-no-artifacts: This cask is not available on macOS.")
|
||||
end
|
||||
|
||||
it "treats uninstall-only artifacts as nothing to install" do
|
||||
zap_only_cask = Cask::Cask.new("with-zap-only", loaded_from_api: true) do
|
||||
version "1.0"
|
||||
sha256 :no_check
|
||||
url "https://brew.sh/x.zip"
|
||||
zap trash: "~/Library/Caches/brew-test"
|
||||
end
|
||||
expect do
|
||||
described_class.new(zap_only_cask).check_supported_system
|
||||
end.to raise_error(Cask::CaskError, "with-zap-only: This cask is not available on macOS.")
|
||||
end
|
||||
|
||||
it "does not treat stage_only casks as having nothing to install" do
|
||||
stage_only_cask = Cask::Cask.new("with-stage-only", loaded_from_api: true) do
|
||||
version "1.0"
|
||||
sha256 :no_check
|
||||
url "https://brew.sh/x.zip"
|
||||
stage_only true
|
||||
end
|
||||
expect do
|
||||
described_class.new(stage_only_cask).check_supported_system
|
||||
end.not_to raise_error
|
||||
end
|
||||
|
||||
it "installs fine if sha256 :no_check is used with --require-sha and --force" do
|
||||
no_checksum = Cask::CaskLoader.load(cask_path("no-checksum"))
|
||||
|
||||
@@ -385,7 +420,7 @@ RSpec.describe Cask::Installer, :cask do
|
||||
expect(Homebrew::API::Cask).to receive(:source_download_cask).once.and_return(source_caffeine)
|
||||
|
||||
caffeine = Cask::CaskLoader.load(path)
|
||||
expect(caffeine).to receive(:loaded_from_api?).once.and_return(true)
|
||||
allow(caffeine).to receive(:loaded_from_api?).and_return(true)
|
||||
expect(caffeine).to receive(:caskfile_only?).once.and_return(true)
|
||||
|
||||
described_class.new(caffeine).install
|
||||
@@ -511,7 +546,7 @@ RSpec.describe Cask::Installer, :cask do
|
||||
expect(Homebrew::API::Cask).to receive(:source_download_cask).twice.and_return(source_caffeine)
|
||||
|
||||
caffeine = Cask::CaskLoader.load(path)
|
||||
expect(caffeine).to receive(:loaded_from_api?).twice.and_return(true)
|
||||
allow(caffeine).to receive(:loaded_from_api?).and_return(true)
|
||||
expect(caffeine).to receive(:caskfile_only?).twice.and_return(true)
|
||||
expect(caffeine).to receive(:installed_caskfile).once.and_return(invalid_path)
|
||||
|
||||
@@ -693,6 +728,7 @@ RSpec.describe Cask::Installer, :cask do
|
||||
ENV["HOMEBREW_FORBIDDEN_CASKS"] = cask_name = "homebrew-forbidden-cask"
|
||||
cask = Cask::Cask.new(cask_name) do
|
||||
url "file://#{TEST_FIXTURE_DIR}/cask/container.tar.gz"
|
||||
app "Fake.app"
|
||||
end
|
||||
allow(cask).to receive_messages(loaded_from_api?: true, caskfile_only?: true)
|
||||
installer = described_class.new(cask)
|
||||
@@ -710,6 +746,7 @@ RSpec.describe Cask::Installer, :cask do
|
||||
url "https://example.com/source-cask.zip"
|
||||
version "0.9"
|
||||
sha256 "d7b9f4e8bf83608b71fe958a99f19f2e5e68bb2582965d32e41759c24f1aef97"
|
||||
app "Fake.app"
|
||||
end
|
||||
cask_struct = Homebrew::API::CaskStruct.new(
|
||||
sha256: "d7b9f4e8bf83608b71fe958a99f19f2e5e68bb2582965d32e41759c24f1aef97",
|
||||
@@ -755,6 +792,7 @@ RSpec.describe Cask::Installer, :cask do
|
||||
it "enqueues source API caskfiles before the main cask download" do
|
||||
cask = Cask::Cask.new("source-api-cask") do
|
||||
url "file://#{TEST_FIXTURE_DIR}/cask/container.tar.gz"
|
||||
app "Fake.app"
|
||||
end
|
||||
allow(cask).to receive_messages(loaded_from_api?: true, caskfile_only?: true, languages: ["en"])
|
||||
download_queue = instance_double(Homebrew::DownloadQueue)
|
||||
@@ -772,6 +810,7 @@ RSpec.describe Cask::Installer, :cask do
|
||||
it "leaves source API caskfiles in the main queue when their URL is known" do
|
||||
cask = Cask::Cask.new("source-api-cask") do
|
||||
url "file://#{TEST_FIXTURE_DIR}/cask/container.tar.gz"
|
||||
app "Fake.app"
|
||||
end
|
||||
allow(cask).to receive_messages(loaded_from_api?: true, caskfile_only?: true, languages: [])
|
||||
download_queue = instance_double(Homebrew::DownloadQueue)
|
||||
|
||||
Reference in New Issue
Block a user