mirror of
https://github.com/Homebrew/brew.git
synced 2026-08-12 22:29:27 +04:00
sorbet: add strict type signatures to cask files
Add Sorbet `sig` annotations to the cask-related files that were
already marked `# typed: strict` but lacked complete signatures.
Key changes:
- Add `sig` and `T.let` declarations to cask/cask.rb, cask/dsl.rb,
cask/cask_loader.rb, cask/installer.rb, cask/exceptions.rb,
cask/quarantine.rb, and cask/dsl/caveats.rb
- Use `T::Sig::WithoutRuntime.sig` for `DSL#url`, `DSL#set_unique_stanza`,
and `CaskLoader.{path,load,for}` to avoid Sorbet runtime wrappers
interfering with `caller_locations` (used by `URL#unversioned?`) and
RSpec mocking of class methods
- Fix nil guards using raise-unless patterns instead of `T.must`
- Avoid `T.unsafe`; use `T.untyped` only as hash value types
- Fix cascade type errors in callers (audit.rb, info.rb, upgrade.rb,
livecheck, bump-cask-pr.rb, etc.) that now see explicit nilable types
on `Cask#tap`, `CaskLoader.load`, and related methods
This commit is contained in:
@@ -70,7 +70,8 @@ module Homebrew
|
||||
const :auto_updates, T::Boolean, default: false
|
||||
const :caveats_rosetta, T::Boolean, default: false
|
||||
const :conflicts_with_args, T::Hash[Symbol, T::Array[String]], default: {}
|
||||
const :container_args, T::Hash[Symbol, T.any(Symbol, T.anything)], default: {}
|
||||
const :container_args, { nested: T.nilable(String), type: T.nilable(Symbol) },
|
||||
default: { nested: nil, type: nil }
|
||||
const :depends_on_args, DependsOnArgs, default: {}
|
||||
const :deprecate_args, T::Hash[Symbol, T.nilable(T.any(String, Symbol))], default: {}
|
||||
const :desc, T.nilable(String)
|
||||
|
||||
@@ -17,7 +17,7 @@ class String
|
||||
# @!visibility private
|
||||
sig { params(config: T.nilable(T::Hash[Symbol, T.untyped])).returns(Cask::Cask) }
|
||||
def c(config: nil)
|
||||
Cask::CaskLoader.load(self, config:)
|
||||
Cask::CaskLoader.load(self, config: Cask::Config.new(**config))
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -501,7 +501,7 @@ module Cask
|
||||
url = cask.url
|
||||
return if url.nil?
|
||||
|
||||
return if !cask.tap.official? && !signing?
|
||||
return if !cask.tap&.official? && !signing?
|
||||
return if cask.deprecated? && cask.deprecation_reason != :fails_gatekeeper_check
|
||||
|
||||
unless Quarantine.available?
|
||||
@@ -575,7 +575,7 @@ module Cask
|
||||
#{result.merged_output}
|
||||
EOS
|
||||
|
||||
if cask.tap.official?
|
||||
if cask.tap&.official?
|
||||
signing_failure_message += <<~EOS
|
||||
The homebrew/cask tap requires all casks to be signed and notarized by Apple.
|
||||
Please contact the upstream developer and ask them to sign and notarize their software.
|
||||
@@ -1165,7 +1165,7 @@ module Cask
|
||||
|
||||
sig { void }
|
||||
def audit_conflicts_with
|
||||
return if !cask.tap.official? || cask.conflicts_with.nil?
|
||||
return if !cask.tap&.official? || cask.conflicts_with.nil?
|
||||
|
||||
Homebrew.with_no_api_env do
|
||||
nonexisting_conflicting_casks = cask.conflicts_with.fetch(:cask, Set.new) - core_cask_tokens
|
||||
@@ -1177,8 +1177,7 @@ module Cask
|
||||
|
||||
sig { void }
|
||||
def audit_denylist
|
||||
return unless cask.tap
|
||||
return unless cask.tap.official?
|
||||
return unless cask.tap&.official?
|
||||
return unless (reason = Denylist.reason(cask.token))
|
||||
|
||||
add_error "#{cask.token} is not allowed: #{reason}"
|
||||
@@ -1187,9 +1186,8 @@ module Cask
|
||||
sig { void }
|
||||
def audit_reverse_migration
|
||||
return unless new_cask?
|
||||
return unless cask.tap
|
||||
return unless cask.tap.official?
|
||||
return unless cask.tap.tap_migrations.key?(cask.token)
|
||||
return unless cask.tap&.official?
|
||||
return unless cask.tap&.tap_migrations&.key?(cask.token)
|
||||
|
||||
add_error "#{cask.token} is listed in tap_migrations.json"
|
||||
end
|
||||
@@ -1254,11 +1252,11 @@ module Cask
|
||||
|
||||
sig { void }
|
||||
def audit_cask_path
|
||||
return unless cask.tap.core_cask_tap?
|
||||
return unless (tap = cask.tap)&.core_cask_tap?
|
||||
|
||||
expected_path = cask.tap.new_cask_path(cask.token)
|
||||
expected_path = tap.new_cask_path(cask.token)
|
||||
|
||||
return if cask.sourcefile_path.to_s.end_with?(expected_path)
|
||||
return if cask.sourcefile_path.to_s.end_with?(expected_path.to_s)
|
||||
|
||||
add_error "Cask should be located in '#{expected_path}'"
|
||||
end
|
||||
|
||||
@@ -116,12 +116,14 @@ module Cask
|
||||
sig { params(languages: T::Array[String]).returns(::Cask::Audit) }
|
||||
def audit_languages(languages)
|
||||
original_config = cask.config
|
||||
localized_config = original_config.merge(Config.new(explicit: { languages: }))
|
||||
cask.config = localized_config
|
||||
begin
|
||||
localized_config = original_config.merge(Config.new(explicit: { languages: }))
|
||||
cask.config = localized_config
|
||||
|
||||
audit_cask_instance(cask)
|
||||
ensure
|
||||
cask.config = original_config
|
||||
audit_cask_instance(cask)
|
||||
ensure
|
||||
cask.config = original_config
|
||||
end
|
||||
end
|
||||
|
||||
sig { params(cask: ::Cask::Cask).returns(::Cask::Audit) }
|
||||
|
||||
+137
-51
@@ -1,4 +1,4 @@
|
||||
# typed: true # rubocop:todo Sorbet/StrictSigil
|
||||
# typed: strict
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "bundle_version"
|
||||
@@ -21,16 +21,34 @@ module Cask
|
||||
# The token of this {Cask}.
|
||||
#
|
||||
# @api internal
|
||||
sig { returns(String) }
|
||||
attr_reader :token
|
||||
|
||||
# The configuration of this {Cask}.
|
||||
#
|
||||
# @api internal
|
||||
sig { returns(Config) }
|
||||
attr_reader :config
|
||||
|
||||
attr_reader :sourcefile_path, :source, :default_config, :loader
|
||||
attr_accessor :download, :allow_reassignment
|
||||
sig { returns(T.nilable(Pathname)) }
|
||||
attr_reader :sourcefile_path
|
||||
|
||||
sig { returns(T.nilable(String)) }
|
||||
attr_reader :source
|
||||
|
||||
sig { returns(Config) }
|
||||
attr_reader :default_config
|
||||
|
||||
sig { returns(T.nilable(CaskLoader::ILoader)) }
|
||||
attr_reader :loader
|
||||
|
||||
sig { returns(T.nilable(Pathname)) }
|
||||
attr_accessor :download
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
attr_accessor :allow_reassignment
|
||||
|
||||
sig { params(eval_all: T::Boolean).returns(T::Array[Cask]) }
|
||||
def self.all(eval_all: false)
|
||||
if !eval_all && !Homebrew::EnvConfig.eval_all?
|
||||
raise ArgumentError, "Cask::Cask#all cannot be used without `--eval-all` or `HOMEBREW_EVAL_ALL=1`"
|
||||
@@ -48,8 +66,15 @@ module Cask
|
||||
end
|
||||
end
|
||||
|
||||
def tap
|
||||
return super if block_given? # Object#tap
|
||||
# This collides with Kernel#tap, complicating the type signature.
|
||||
# Overload sigs are not supported by Sorbet, otherwise we would use:
|
||||
# sig { params(blk: T.proc.params(arg0: Cask).void).returns(T.self_type) }
|
||||
# sig { params(blk: NilClass).returns(T.nilable(Tap)) }
|
||||
# Using a union type would require casts or type guards at call sites,
|
||||
# so T.untyped is used as the return type instead.
|
||||
sig { params(blk: T.nilable(T.proc.params(arg0: Cask).void)).returns(T.untyped) }
|
||||
def tap(&blk)
|
||||
return super if block_given? # Kernel#tap
|
||||
|
||||
@tap
|
||||
end
|
||||
@@ -83,15 +108,16 @@ module Cask
|
||||
@loader = loader
|
||||
# Sorbet has trouble with bound procs assigned to instance variables:
|
||||
# https://github.com/sorbet/sorbet/issues/6843
|
||||
instance_variable_set(:@block, block)
|
||||
@block = T.let(block, T.untyped)
|
||||
|
||||
@default_config = config || Config.new
|
||||
@default_config = T.let(config || Config.new, Config)
|
||||
|
||||
self.config = if config_path.exist?
|
||||
Config.from_json(File.read(config_path), ignore_invalid_keys: true)
|
||||
else
|
||||
@default_config
|
||||
end
|
||||
@config = T.let(if config_path.exist?
|
||||
Config.from_json(File.read(config_path), ignore_invalid_keys: true)
|
||||
else
|
||||
@default_config
|
||||
end, Config)
|
||||
refresh
|
||||
end
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
@@ -106,28 +132,33 @@ module Cask
|
||||
# An old name for the cask.
|
||||
sig { returns(T::Array[String]) }
|
||||
def old_tokens
|
||||
@old_tokens ||= if (tap = self.tap)
|
||||
Tap.tap_migration_oldnames(tap, token) +
|
||||
tap.cask_reverse_renames.fetch(token, [])
|
||||
else
|
||||
[]
|
||||
end
|
||||
@old_tokens ||= T.let(
|
||||
if (t = tap)
|
||||
Tap.tap_migration_oldnames(t, token) +
|
||||
t.cask_reverse_renames.fetch(token, [])
|
||||
else
|
||||
[]
|
||||
end,
|
||||
T.nilable(T::Array[String]),
|
||||
)
|
||||
end
|
||||
|
||||
sig { params(config: Config).void }
|
||||
def config=(config)
|
||||
@config = config
|
||||
|
||||
refresh
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def refresh
|
||||
@dsl = DSL.new(self)
|
||||
@dsl = T.let(DSL.new(self), T.nilable(DSL))
|
||||
@contains_os_specific_artifacts = nil
|
||||
return unless @block
|
||||
|
||||
@dsl.instance_eval(&@block)
|
||||
@dsl.add_implicit_macos_dependency
|
||||
@dsl.language_eval
|
||||
dsl!.instance_eval(&@block)
|
||||
dsl!.add_implicit_macos_dependency
|
||||
dsl!.language_eval
|
||||
rescue NoMethodError => e
|
||||
raise CaskInvalidError.new(token, e.message), e.backtrace
|
||||
end
|
||||
@@ -135,7 +166,7 @@ module Cask
|
||||
def_delegators :@dsl, *::Cask::DSL::DSL_METHODS
|
||||
|
||||
sig { returns(DSL::Caveats) }
|
||||
def caveats_object = @dsl.caveats_object
|
||||
def caveats_object = dsl!.caveats_object
|
||||
|
||||
sig { params(caskroom_path: Pathname).returns(T::Array[[String, String]]) }
|
||||
def timestamped_versions(caskroom_path: self.caskroom_path)
|
||||
@@ -150,16 +181,18 @@ module Cask
|
||||
# The fully-qualified token of this {Cask}.
|
||||
#
|
||||
# @api internal
|
||||
sig { returns(String) }
|
||||
def full_token
|
||||
return token if tap.nil?
|
||||
return token if tap.core_cask_tap?
|
||||
return token if (t = tap).nil?
|
||||
return token if t.core_cask_tap?
|
||||
|
||||
"#{tap.name}/#{token}"
|
||||
"#{t.name}/#{token}"
|
||||
end
|
||||
|
||||
# Alias for {#full_token}.
|
||||
#
|
||||
# @api internal
|
||||
sig { returns(String) }
|
||||
def full_name = full_token
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
@@ -178,12 +211,12 @@ module Cask
|
||||
|
||||
# Cache the os value before contains_os_specific_artifacts? refreshes the cask
|
||||
# (the refresh clears @dsl.os in generic/non-OS-specific contexts)
|
||||
os_value = @dsl.os
|
||||
os_value = dsl!.os
|
||||
|
||||
return false if contains_os_specific_artifacts?
|
||||
|
||||
# Casks with OS-specific blocks rely on the os stanza for Linux support
|
||||
return os_value.present? if @dsl.on_os_blocks_exist?
|
||||
return os_value.present? if dsl!.on_os_blocks_exist?
|
||||
|
||||
# Platform-agnostic casks: reject macOS-only artifacts and manual installers
|
||||
artifacts.none? do |a|
|
||||
@@ -194,7 +227,7 @@ module Cask
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
def contains_os_specific_artifacts?
|
||||
return false unless @dsl.on_system_blocks_exist?
|
||||
return false unless @dsl&.on_system_blocks_exist?
|
||||
|
||||
return @contains_os_specific_artifacts unless @contains_os_specific_artifacts.nil?
|
||||
|
||||
@@ -221,10 +254,12 @@ module Cask
|
||||
|
||||
# The caskfile is needed during installation when there are
|
||||
# `*flight` blocks or the cask has multiple languages
|
||||
sig { returns(T::Boolean) }
|
||||
def caskfile_only?
|
||||
languages.any? || artifacts.any?(Artifact::AbstractFlightBlock)
|
||||
end
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
def uninstall_flight_blocks?
|
||||
artifacts.any? do |artifact|
|
||||
case artifact
|
||||
@@ -287,33 +322,42 @@ module Cask
|
||||
bundle_version&.version
|
||||
end
|
||||
|
||||
sig { returns(Tab) }
|
||||
def tab
|
||||
Tab.for_cask(self)
|
||||
end
|
||||
|
||||
sig { returns(Pathname) }
|
||||
def config_path
|
||||
metadata_main_container_path/"config.json"
|
||||
end
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
def checksumable?
|
||||
return false if (url = self.url).nil?
|
||||
|
||||
DownloadStrategyDetector.detect(url.to_s, url.using) <= AbstractFileDownloadStrategy
|
||||
DownloadStrategyDetector.detect(url.to_s, url.using) <= AbstractFileDownloadStrategy || false
|
||||
end
|
||||
|
||||
sig { returns(Pathname) }
|
||||
def download_sha_path
|
||||
metadata_main_container_path/"LATEST_DOWNLOAD_SHA256"
|
||||
end
|
||||
|
||||
sig { returns(String) }
|
||||
def new_download_sha
|
||||
require "cask/installer"
|
||||
|
||||
# Call checksumable? before hashing
|
||||
@new_download_sha ||= Installer.new(self, verify_download_integrity: false)
|
||||
.download(quiet: true)
|
||||
.instance_eval { |x| Digest::SHA256.file(x).hexdigest }
|
||||
@new_download_sha ||= T.let(
|
||||
Installer.new(self, verify_download_integrity: false)
|
||||
.download(quiet: true)
|
||||
.instance_eval { |x| Digest::SHA256.file(x).hexdigest },
|
||||
T.nilable(String),
|
||||
)
|
||||
end
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
def outdated_download_sha?
|
||||
return true unless checksumable?
|
||||
|
||||
@@ -323,17 +367,25 @@ module Cask
|
||||
|
||||
sig { returns(Pathname) }
|
||||
def caskroom_path
|
||||
@caskroom_path ||= Caskroom.path.join(token)
|
||||
@caskroom_path ||= T.let(Caskroom.path.join(token), T.nilable(Pathname))
|
||||
end
|
||||
|
||||
# Check if the installed cask is outdated.
|
||||
#
|
||||
# @api internal
|
||||
sig {
|
||||
params(greedy: T::Boolean, greedy_latest: T.nilable(T::Boolean), greedy_auto_updates: T.nilable(T::Boolean))
|
||||
.returns(T::Boolean)
|
||||
}
|
||||
def outdated?(greedy: false, greedy_latest: false, greedy_auto_updates: false)
|
||||
!outdated_version(greedy:, greedy_latest:,
|
||||
greedy_auto_updates:).nil?
|
||||
end
|
||||
|
||||
sig {
|
||||
params(greedy: T::Boolean, greedy_latest: T.nilable(T::Boolean), greedy_auto_updates: T.nilable(T::Boolean))
|
||||
.returns(T.nilable(String))
|
||||
}
|
||||
def outdated_version(greedy: false, greedy_latest: false, greedy_auto_updates: false)
|
||||
# special case: tap version is not available
|
||||
return if version.nil?
|
||||
@@ -355,6 +407,15 @@ module Cask
|
||||
installed_version
|
||||
end
|
||||
|
||||
sig {
|
||||
params(
|
||||
greedy: T::Boolean,
|
||||
verbose: T::Boolean,
|
||||
json: T::Boolean,
|
||||
greedy_latest: T::Boolean,
|
||||
greedy_auto_updates: T::Boolean,
|
||||
).returns(T.any(String, T::Hash[Symbol, T.untyped]))
|
||||
}
|
||||
def outdated_info(greedy, verbose, json, greedy_latest, greedy_auto_updates)
|
||||
return token if !verbose && !json
|
||||
|
||||
@@ -372,28 +433,37 @@ module Cask
|
||||
end
|
||||
end
|
||||
|
||||
sig { returns(T.nilable(String)) }
|
||||
def ruby_source_path
|
||||
return @ruby_source_path if defined?(@ruby_source_path)
|
||||
|
||||
return unless sourcefile_path
|
||||
return unless tap
|
||||
return unless (sfp = sourcefile_path)
|
||||
return unless (t = tap)
|
||||
|
||||
@ruby_source_path = sourcefile_path.relative_path_from(tap.path)
|
||||
@ruby_source_path = T.let(sfp.relative_path_from(t.path).to_s, T.nilable(String))
|
||||
end
|
||||
|
||||
sig { returns(T::Hash[Symbol, String]) }
|
||||
sig { returns(T::Hash[Symbol, T.nilable(String)]) }
|
||||
def ruby_source_checksum
|
||||
@ruby_source_checksum ||= {
|
||||
sha256: Digest::SHA256.file(sourcefile_path).hexdigest,
|
||||
}.freeze
|
||||
@ruby_source_checksum ||= T.let(
|
||||
begin
|
||||
sfp = sourcefile_path
|
||||
{
|
||||
sha256: sfp ? Digest::SHA256.file(sfp).hexdigest : nil,
|
||||
}.freeze
|
||||
end,
|
||||
T.nilable(T::Hash[Symbol, T.nilable(String)]),
|
||||
)
|
||||
end
|
||||
|
||||
sig { returns(T::Array[String]) }
|
||||
def languages
|
||||
@languages ||= @dsl.languages
|
||||
@languages ||= T.let(dsl!.languages, T.nilable(T::Array[String]))
|
||||
end
|
||||
|
||||
sig { returns(T.nilable(String)) }
|
||||
def tap_git_head
|
||||
@tap_git_head ||= tap&.git_head
|
||||
@tap_git_head ||= T.let(tap&.git_head, T.nilable(String))
|
||||
rescue TapUnavailableError
|
||||
nil
|
||||
end
|
||||
@@ -417,15 +487,18 @@ module Cask
|
||||
"#<Cask #{token}#{sourcefile_path&.to_s&.prepend(" ")}>"
|
||||
end
|
||||
|
||||
sig { returns(Integer) }
|
||||
def hash
|
||||
token.hash
|
||||
end
|
||||
|
||||
sig { params(other: T.untyped).returns(T::Boolean) }
|
||||
def eql?(other)
|
||||
instance_of?(other.class) && token == other.token
|
||||
end
|
||||
alias == eql?
|
||||
|
||||
sig { returns(T::Hash[String, T.untyped]) }
|
||||
def to_h
|
||||
{
|
||||
"token" => token,
|
||||
@@ -474,9 +547,10 @@ module Cask
|
||||
}
|
||||
end
|
||||
|
||||
HASH_KEYS_TO_SKIP = %w[outdated installed versions].freeze
|
||||
HASH_KEYS_TO_SKIP = T.let(%w[outdated installed versions].freeze, T::Array[String])
|
||||
private_constant :HASH_KEYS_TO_SKIP
|
||||
|
||||
sig { returns(T::Hash[String, T.untyped]) }
|
||||
def to_hash_with_variations
|
||||
if loaded_from_internal_api?
|
||||
raise UsageError, "Cannot call #to_hash_with_variations on casks loaded from the internal API"
|
||||
@@ -489,13 +563,13 @@ module Cask
|
||||
hash = to_h
|
||||
variations = {}
|
||||
|
||||
if @dsl.on_system_blocks_exist?
|
||||
if dsl!.on_system_blocks_exist?
|
||||
begin
|
||||
OnSystem::VALID_OS_ARCH_TAGS.each do |bottle_tag|
|
||||
next if bottle_tag.linux? && @dsl.os.nil?
|
||||
next if bottle_tag.linux? && dsl!.os.nil?
|
||||
next if bottle_tag.macos? &&
|
||||
depends_on.macos &&
|
||||
!@dsl.depends_on_set_in_block? &&
|
||||
!dsl!.depends_on_set_in_block? &&
|
||||
!depends_on.macos.allows?(bottle_tag.to_macos_version)
|
||||
|
||||
Homebrew::SimulateSystem.with_tag(bottle_tag) do
|
||||
@@ -519,6 +593,7 @@ module Cask
|
||||
hash
|
||||
end
|
||||
|
||||
sig { params(uninstall_only: T::Boolean).returns(T::Array[T::Hash[Symbol, T.untyped]]) }
|
||||
def artifacts_list(uninstall_only: false)
|
||||
artifacts.filter_map do |artifact|
|
||||
case artifact
|
||||
@@ -539,6 +614,7 @@ module Cask
|
||||
end
|
||||
end
|
||||
|
||||
sig { params(uninstall_only: T::Boolean).returns(T::Array[T::Hash[Symbol, T.untyped]]) }
|
||||
def rename_list(uninstall_only: false)
|
||||
rename.filter_map do |rename|
|
||||
{ from: rename.from, to: rename.to }
|
||||
@@ -559,10 +635,18 @@ module Cask
|
||||
|
||||
sig { returns(T.nilable(Homebrew::BundleVersion)) }
|
||||
def bundle_version
|
||||
@bundle_version ||= if (bundle = artifacts.find { |a| a.is_a?(Artifact::App) }&.target) &&
|
||||
(plist = Pathname("#{bundle}/Contents/Info.plist")) && plist.exist? && plist.readable?
|
||||
Homebrew::BundleVersion.from_info_plist(plist)
|
||||
end
|
||||
@bundle_version ||= T.let(
|
||||
if (bundle = artifacts.find { |a| a.is_a?(Artifact::App) }&.target) &&
|
||||
(plist = Pathname("#{bundle}/Contents/Info.plist")) && plist.exist? && plist.readable?
|
||||
Homebrew::BundleVersion.from_info_plist(plist)
|
||||
end,
|
||||
T.nilable(Homebrew::BundleVersion),
|
||||
)
|
||||
end
|
||||
|
||||
sig { returns(DSL) }
|
||||
def dsl!
|
||||
@dsl || raise("unexpected nil @dsl")
|
||||
end
|
||||
|
||||
sig { returns(T.nilable(Artifact::App)) }
|
||||
@@ -622,6 +706,7 @@ module Cask
|
||||
build_comparisons.include?(-1)
|
||||
end
|
||||
|
||||
sig { params(hash: T::Hash[String, T.untyped]).returns(T::Hash[String, T.untyped]) }
|
||||
def api_to_local_hash(hash)
|
||||
hash["token"] = token
|
||||
hash["installed"] = installed_version
|
||||
@@ -629,6 +714,7 @@ module Cask
|
||||
hash
|
||||
end
|
||||
|
||||
sig { returns(T.nilable(T::Hash[Symbol, T.untyped])) }
|
||||
def url_specs
|
||||
url&.specs.dup.tap do |url_specs|
|
||||
case url_specs&.dig(:user_agent)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# typed: true # rubocop:todo Sorbet/StrictSigil
|
||||
# typed: strict
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "cask/cache"
|
||||
@@ -39,9 +39,16 @@ module Cask
|
||||
sig { returns(String) }
|
||||
attr_reader :content
|
||||
|
||||
sig { returns(T.nilable(Tap)) }
|
||||
sig { overridable.returns(T.nilable(Tap)) }
|
||||
attr_reader :tap
|
||||
|
||||
sig { void }
|
||||
def initialize
|
||||
@content = T.let("", String)
|
||||
@tap = T.let(nil, T.nilable(Tap))
|
||||
@config = T.let(nil, T.nilable(Config))
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
sig {
|
||||
@@ -68,12 +75,15 @@ module Cask
|
||||
content = ref.to_str
|
||||
|
||||
# Cache compiled regex
|
||||
@regex ||= begin
|
||||
token = /(?:"[^"]*"|'[^']*')/
|
||||
curly = /\(\s*#{token.source}\s*\)\s*\{.*\}/
|
||||
do_end = /\s+#{token.source}\s+do(?:\s*;\s*|\s+).*end/
|
||||
/\A\s*cask(?:#{curly.source}|#{do_end.source})\s*\Z/m
|
||||
end
|
||||
@regex ||= T.let(
|
||||
begin
|
||||
token = /(?:"[^"]*"|'[^']*')/
|
||||
curly = /\(\s*#{token.source}\s*\)\s*\{.*\}/
|
||||
do_end = /\s+#{token.source}\s+do(?:\s*;\s*|\s+).*end/
|
||||
/\A\s*cask(?:#{curly.source}|#{do_end.source})\s*\Z/m
|
||||
end,
|
||||
T.nilable(Regexp),
|
||||
)
|
||||
|
||||
return unless content.match?(@regex)
|
||||
|
||||
@@ -84,10 +94,11 @@ module Cask
|
||||
def initialize(content, tap: T.unsafe(nil))
|
||||
super()
|
||||
|
||||
@content = content.dup.force_encoding("UTF-8")
|
||||
@tap = tap
|
||||
@content = T.let(content.dup.force_encoding("UTF-8"), String)
|
||||
@tap = T.let(tap, T.nilable(Tap))
|
||||
end
|
||||
|
||||
sig { override.params(config: T.nilable(Config)).returns(Cask) }
|
||||
def load(config:)
|
||||
@config = config
|
||||
|
||||
@@ -122,11 +133,15 @@ module Cask
|
||||
def self.invalid_path?(pathname, valid_extnames: %w[.rb .json])
|
||||
return true if valid_extnames.exclude?(pathname.extname)
|
||||
|
||||
@invalid_basenames ||= %w[INSTALL_RECEIPT.json sbom.spdx.json].freeze
|
||||
@invalid_basenames ||= T.let(%w[INSTALL_RECEIPT.json sbom.spdx.json].freeze, T.nilable(T::Array[String]))
|
||||
@invalid_basenames.include?(pathname.basename.to_s)
|
||||
end
|
||||
|
||||
attr_reader :token, :path
|
||||
sig { returns(String) }
|
||||
attr_reader :token
|
||||
|
||||
sig { returns(Pathname) }
|
||||
attr_reader :path
|
||||
|
||||
sig { params(path: T.any(Pathname, String), token: String).void }
|
||||
def initialize(path, token: T.unsafe(nil))
|
||||
@@ -134,10 +149,10 @@ module Cask
|
||||
|
||||
path = Pathname(path).expand_path
|
||||
|
||||
@token = path.basename(path.extname).basename(".internal").to_s
|
||||
@path = path
|
||||
@tap = Tap.from_path(path) || Homebrew::API.tap_from_source_download(path)
|
||||
@from_installed_caskfile = false
|
||||
@token = T.let(path.basename(path.extname).basename(".internal").to_s, String)
|
||||
@path = T.let(path, Pathname)
|
||||
@tap = T.let(Tap.from_path(path) || Homebrew::API.tap_from_source_download(path), T.nilable(Tap))
|
||||
@from_installed_caskfile = T.let(false, T::Boolean)
|
||||
end
|
||||
|
||||
sig { override.params(config: T.nilable(Config)).returns(Cask) }
|
||||
@@ -172,7 +187,7 @@ module Cask
|
||||
end
|
||||
|
||||
begin
|
||||
instance_eval(content, path).tap do |cask|
|
||||
instance_eval(content, path.to_s).tap do |cask|
|
||||
raise CaskUnreadableError.new(token, "'#{path}' does not contain a cask.") unless cask.is_a?(Cask)
|
||||
end
|
||||
rescue NameError, ArgumentError, ScriptError => e
|
||||
@@ -192,6 +207,13 @@ module Cask
|
||||
|
||||
private
|
||||
|
||||
sig {
|
||||
override.params(
|
||||
header_token: String,
|
||||
options: T.untyped,
|
||||
block: T.nilable(T.proc.bind(DSL).void),
|
||||
).returns(Cask)
|
||||
}
|
||||
def cask(header_token, **options, &block)
|
||||
raise CaskTokenMismatchError.new(token, header_token) if token != header_token
|
||||
|
||||
@@ -209,10 +231,13 @@ module Cask
|
||||
return if Homebrew::EnvConfig.forbid_packages_from_paths?
|
||||
|
||||
# Cache compiled regex
|
||||
@uri_regex ||= begin
|
||||
uri_regex = ::URI::RFC2396_PARSER.make_regexp
|
||||
Regexp.new("\\A#{uri_regex.source}\\Z", uri_regex.options)
|
||||
end
|
||||
@uri_regex ||= T.let(
|
||||
begin
|
||||
uri_regex = ::URI::RFC2396_PARSER.make_regexp
|
||||
Regexp.new("\\A#{uri_regex.source}\\Z", uri_regex.options)
|
||||
end,
|
||||
T.nilable(Regexp),
|
||||
)
|
||||
|
||||
uri = ref.to_s
|
||||
return unless uri.match?(@uri_regex)
|
||||
@@ -223,15 +248,23 @@ module Cask
|
||||
new(uri)
|
||||
end
|
||||
|
||||
attr_reader :url, :name
|
||||
sig { returns(URI::Generic) }
|
||||
attr_reader :url
|
||||
|
||||
sig { returns(String) }
|
||||
attr_reader :name
|
||||
|
||||
sig { params(url: T.any(URI::Generic, String)).void }
|
||||
def initialize(url)
|
||||
@url = URI(url)
|
||||
@name = File.basename(T.must(@url.path))
|
||||
@url = T.let(URI(url), URI::Generic)
|
||||
url_path = @url.path
|
||||
raise "unexpected nil url.path" unless url_path
|
||||
|
||||
@name = T.let(File.basename(url_path), String)
|
||||
super Cache.path/name
|
||||
end
|
||||
|
||||
sig { override.params(config: T.nilable(Config)).returns(Cask) }
|
||||
def load(config:)
|
||||
path.dirname.mkpath
|
||||
|
||||
@@ -255,7 +288,7 @@ module Cask
|
||||
|
||||
# Loads a cask from a specific tap.
|
||||
class FromTapLoader < FromPathLoader
|
||||
sig { returns(Tap) }
|
||||
sig { override.returns(Tap) }
|
||||
attr_reader :tap
|
||||
|
||||
sig {
|
||||
@@ -279,14 +312,18 @@ module Cask
|
||||
|
||||
sig { params(tapped_token: String).void }
|
||||
def initialize(tapped_token)
|
||||
tap, token = Tap.with_cask_token(tapped_token)
|
||||
tap_with_token = Tap.with_cask_token(tapped_token)
|
||||
raise "unexpected nil Tap.with_cask_token" unless tap_with_token
|
||||
|
||||
tap, token = tap_with_token
|
||||
cask = CaskLoader.find_cask_in_tap(token, tap)
|
||||
super cask
|
||||
@tap = T.let(tap, Tap)
|
||||
end
|
||||
|
||||
sig { override.params(config: T.nilable(Config)).returns(Cask) }
|
||||
def load(config:)
|
||||
raise TapCaskUnavailableError.new(tap, token) unless T.must(tap).installed?
|
||||
raise TapCaskUnavailableError.new(tap, token) unless tap.installed?
|
||||
|
||||
super
|
||||
end
|
||||
@@ -298,7 +335,7 @@ module Cask
|
||||
|
||||
sig {
|
||||
params(ref: T.any(String, Pathname, Cask, URI::Generic), warn: T::Boolean)
|
||||
.returns(T.nilable(T.attached_class))
|
||||
.returns(T.nilable(FromInstanceLoader))
|
||||
}
|
||||
def self.try_new(ref, warn: false)
|
||||
new(ref) if ref.is_a?(Cask)
|
||||
@@ -309,6 +346,8 @@ module Cask
|
||||
@cask = cask
|
||||
end
|
||||
|
||||
# This is a false positive incompatibililty warning, due to Kernel#load being overridden.
|
||||
sig { override(allow_incompatible: true).params(config: T.nilable(Config)).returns(Cask) } # rubocop:disable Sorbet/AllowIncompatibleOverride
|
||||
def load(config:)
|
||||
@cask
|
||||
end
|
||||
@@ -329,7 +368,7 @@ module Cask
|
||||
|
||||
sig {
|
||||
params(ref: T.any(String, Pathname, Cask, URI::Generic), warn: T::Boolean)
|
||||
.returns(T.nilable(T.attached_class))
|
||||
.returns(T.nilable(FromAPILoader))
|
||||
}
|
||||
def self.try_new(ref, warn: false)
|
||||
return if Homebrew::EnvConfig.no_install_from_api?
|
||||
@@ -357,20 +396,25 @@ module Cask
|
||||
}
|
||||
def initialize(token, from_json: T.unsafe(nil), path: nil, from_installed_caskfile: false,
|
||||
from_internal_json: false)
|
||||
@token = token.sub(%r{^homebrew/(?:homebrew-)?cask/}i, "")
|
||||
@sourcefile_path = if path
|
||||
path
|
||||
elsif from_json
|
||||
from_internal_json ? Homebrew::API::Internal.cached_cask_json_file_path : Homebrew::API::Cask.cached_json_file_path
|
||||
else
|
||||
Homebrew::API.cached_cask_json_file_path
|
||||
end
|
||||
@path = path || CaskLoader.default_path(@token)
|
||||
@token = T.let(token.sub(%r{^homebrew/(?:homebrew-)?cask/}i, ""), String)
|
||||
@sourcefile_path = T.let(
|
||||
if path
|
||||
path
|
||||
elsif from_json
|
||||
from_internal_json ? Homebrew::API::Internal.cached_cask_json_file_path : Homebrew::API::Cask.cached_json_file_path
|
||||
else
|
||||
Homebrew::API.cached_cask_json_file_path
|
||||
end,
|
||||
Pathname,
|
||||
)
|
||||
@path = T.let(path || CaskLoader.default_path(@token), Pathname)
|
||||
@from_json = from_json
|
||||
@from_installed_caskfile = from_installed_caskfile
|
||||
@from_internal_json = from_internal_json
|
||||
end
|
||||
|
||||
# This is a false positive incompatibililty warning, due to Kernel#load being overridden.
|
||||
sig { override(allow_incompatible: true).params(config: T.nilable(Config)).returns(Cask) } # rubocop:disable Sorbet/AllowIncompatibleOverride
|
||||
def load(config:)
|
||||
if (api_source = from_json)
|
||||
if @from_internal_json
|
||||
@@ -387,6 +431,7 @@ module Cask
|
||||
|
||||
private
|
||||
|
||||
sig { params(config: T.nilable(Config)).returns(Cask) }
|
||||
def load_from_api(config:)
|
||||
api_source = Homebrew::API::Cask.all_casks.fetch(token)
|
||||
tap_git_head = api_source["tap_git_head"]
|
||||
@@ -397,6 +442,7 @@ module Cask
|
||||
load_from_struct(config:, cask_struct:, api_source:, tap_git_head:)
|
||||
end
|
||||
|
||||
sig { params(config: T.nilable(Config)).returns(Cask) }
|
||||
def load_from_internal_api(config:)
|
||||
cask_struct = Homebrew::API::Internal.cask_struct(token)
|
||||
api_source = Homebrew::API::Internal.cask_hashes.fetch(token)
|
||||
@@ -405,6 +451,7 @@ module Cask
|
||||
load_from_struct(config:, cask_struct:, api_source:, tap_git_head:, internal_api: true)
|
||||
end
|
||||
|
||||
sig { params(config: T.nilable(Config), api_source: T::Hash[String, T.untyped]).returns(Cask) }
|
||||
def load_from_json(config:, api_source:)
|
||||
tap_git_head = api_source["tap_git_head"]
|
||||
cask_struct = Homebrew::API::Cask::CaskStructGenerator.generate_cask_struct_hash(
|
||||
@@ -414,6 +461,7 @@ module Cask
|
||||
load_from_struct(config:, cask_struct:, api_source:, tap_git_head:)
|
||||
end
|
||||
|
||||
sig { params(config: T.nilable(Config), api_source: T::Hash[String, T.untyped]).returns(Cask) }
|
||||
def load_from_internal_json(config:, api_source:)
|
||||
api_source = api_source.dup
|
||||
tap_git_head = api_source.delete("tap_git_head")
|
||||
@@ -477,13 +525,15 @@ module Cask
|
||||
end
|
||||
end
|
||||
|
||||
container(**cask_struct.container_args) if cask_struct.container?
|
||||
if cask_struct.container?
|
||||
container(nested: cask_struct.container_args[:nested], type: cask_struct.container_args[:type])
|
||||
end
|
||||
|
||||
cask_struct.artifacts(appdir:).each do |key, args, kwargs, block|
|
||||
send(key, *args, **kwargs, &block)
|
||||
end
|
||||
|
||||
caveats cask_struct.caveats(appdir:) if cask_struct.caveats?
|
||||
caveats T.must(cask_struct.caveats(appdir:)) if cask_struct.caveats?
|
||||
|
||||
if cask_struct.caveats_rosetta
|
||||
caveats do
|
||||
@@ -556,7 +606,7 @@ module Cask
|
||||
|
||||
installed_tap = Cask.new(@token).tab.tap
|
||||
@tap = installed_tap if installed_tap
|
||||
@from_installed_caskfile = true
|
||||
@from_installed_caskfile = T.let(true, T::Boolean)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -579,17 +629,28 @@ module Cask
|
||||
super CaskLoader.default_path(token)
|
||||
end
|
||||
|
||||
sig { override.params(config: T.nilable(Config)).returns(Cask) }
|
||||
def load(config:)
|
||||
raise CaskUnavailableError.new(token, "No Cask with this name exists.")
|
||||
end
|
||||
end
|
||||
|
||||
# NOTE: Using `WithoutRuntime` to avoid Sorbet wrapping this method,
|
||||
# which would interfere with RSpec mocking of this class method.
|
||||
T::Sig::WithoutRuntime.sig { params(ref: T.any(String, Pathname, Cask, URI::Generic)).returns(Pathname) }
|
||||
def self.path(ref)
|
||||
self.for(ref, need_path: true).path
|
||||
T.cast(self.for(ref, need_path: true), T.any(FromAPILoader, FromPathLoader)).path
|
||||
end
|
||||
|
||||
# NOTE: Using `WithoutRuntime` to avoid Sorbet wrapping this method,
|
||||
# which would interfere with RSpec mocking of this class method.
|
||||
T::Sig::WithoutRuntime.sig {
|
||||
params(ref: T.any(String, Symbol, Pathname, Cask, URI::Generic), config: T.nilable(Config),
|
||||
warn: T::Boolean).returns(Cask)
|
||||
}
|
||||
def self.load(ref, config: nil, warn: true)
|
||||
self.for(ref, warn:).load(config:)
|
||||
normalized_ref = ref.is_a?(Symbol) ? ref.to_s : ref
|
||||
self.for(normalized_ref, warn:).load(config:)
|
||||
end
|
||||
|
||||
sig { params(tapped_token: String, warn: T::Boolean).returns(T.nilable([String, Tap, T.nilable(Symbol)])) }
|
||||
@@ -633,6 +694,12 @@ module Cask
|
||||
[token, tap, type]
|
||||
end
|
||||
|
||||
# NOTE: Using `WithoutRuntime` to avoid Sorbet wrapping this method,
|
||||
# which would interfere with RSpec mocking of this class method.
|
||||
T::Sig::WithoutRuntime.sig {
|
||||
params(ref: T.any(String, Pathname, Cask, URI::Generic), need_path: T::Boolean, warn: T::Boolean)
|
||||
.returns(ILoader)
|
||||
}
|
||||
def self.for(ref, need_path: false, warn: true)
|
||||
[
|
||||
FromInstanceLoader,
|
||||
@@ -650,6 +717,8 @@ module Cask
|
||||
return loader
|
||||
end
|
||||
end
|
||||
|
||||
raise CaskError, "No cask loader found for #{ref.inspect}"
|
||||
end
|
||||
|
||||
sig { params(ref: String, config: T.nilable(Config), warn: T::Boolean).returns(Cask) }
|
||||
@@ -678,10 +747,12 @@ module Cask
|
||||
loader.load(config:)
|
||||
end
|
||||
|
||||
sig { params(token: T.any(String, Symbol)).returns(Pathname) }
|
||||
def self.default_path(token)
|
||||
find_cask_in_tap(token.to_s.downcase, CoreCaskTap.instance)
|
||||
end
|
||||
|
||||
sig { params(token: String, tap: Tap).returns(Pathname) }
|
||||
def self.find_cask_in_tap(token, tap)
|
||||
filename = "#{token}.rb"
|
||||
|
||||
|
||||
+123
-21
@@ -1,4 +1,4 @@
|
||||
# typed: true # rubocop:todo Sorbet/StrictSigil
|
||||
# typed: strict
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "autobump_constants"
|
||||
@@ -65,14 +65,17 @@ module Cask
|
||||
Artifact::Zap,
|
||||
].freeze
|
||||
|
||||
ACTIVATABLE_ARTIFACT_CLASSES = (ORDINARY_ARTIFACT_CLASSES - [Artifact::StageOnly]).freeze
|
||||
ACTIVATABLE_ARTIFACT_CLASSES = T.let(
|
||||
(ORDINARY_ARTIFACT_CLASSES - [Artifact::StageOnly]).freeze,
|
||||
T::Array[T.class_of(Artifact::AbstractArtifact)],
|
||||
)
|
||||
|
||||
ARTIFACT_BLOCK_CLASSES = [
|
||||
Artifact::PreflightBlock,
|
||||
Artifact::PostflightBlock,
|
||||
].freeze
|
||||
|
||||
DSL_METHODS = Set.new([
|
||||
DSL_METHODS = T.let(Set.new([
|
||||
:arch,
|
||||
:artifacts,
|
||||
:auto_updates,
|
||||
@@ -117,14 +120,54 @@ module Cask
|
||||
*ORDINARY_ARTIFACT_CLASSES.map(&:dsl_key),
|
||||
*ACTIVATABLE_ARTIFACT_CLASSES.map(&:dsl_key),
|
||||
*ARTIFACT_BLOCK_CLASSES.flat_map { |klass| [klass.dsl_key, klass.uninstall_dsl_key] },
|
||||
]).freeze
|
||||
]).freeze, T::Set[Symbol])
|
||||
|
||||
include OnSystem::MacOSAndLinux
|
||||
|
||||
attr_reader :cask, :token, :no_autobump_message, :artifacts, :deprecation_date, :deprecation_reason,
|
||||
:deprecation_replacement_cask, :deprecation_replacement_formula, :deprecate_args,
|
||||
:disable_date, :disable_reason, :disable_replacement_cask,
|
||||
:disable_replacement_formula, :disable_args, :on_system_block_min_os
|
||||
sig { returns(Cask) }
|
||||
attr_reader :cask
|
||||
|
||||
sig { returns(String) }
|
||||
attr_reader :token
|
||||
|
||||
sig { returns(T.nilable(T.any(String, Symbol))) }
|
||||
attr_reader :no_autobump_message
|
||||
|
||||
sig { returns(ArtifactSet) }
|
||||
attr_reader :artifacts
|
||||
|
||||
sig { returns(T.nilable(Date)) }
|
||||
attr_reader :deprecation_date
|
||||
|
||||
sig { returns(T.nilable(T.any(String, Symbol))) }
|
||||
attr_reader :deprecation_reason
|
||||
|
||||
sig { returns(T.nilable(String)) }
|
||||
attr_reader :deprecation_replacement_cask
|
||||
|
||||
sig { returns(T.nilable(String)) }
|
||||
attr_reader :deprecation_replacement_formula
|
||||
|
||||
sig { returns(T.nilable(T::Hash[Symbol, T.nilable(T.any(String, Symbol))])) }
|
||||
attr_reader :deprecate_args
|
||||
|
||||
sig { returns(T.nilable(Date)) }
|
||||
attr_reader :disable_date
|
||||
|
||||
sig { returns(T.nilable(T.any(String, Symbol))) }
|
||||
attr_reader :disable_reason
|
||||
|
||||
sig { returns(T.nilable(String)) }
|
||||
attr_reader :disable_replacement_cask
|
||||
|
||||
sig { returns(T.nilable(String)) }
|
||||
attr_reader :disable_replacement_formula
|
||||
|
||||
sig { returns(T.nilable(T::Hash[Symbol, T.nilable(T.any(String, Symbol))])) }
|
||||
attr_reader :disable_args
|
||||
|
||||
sig { returns(T.nilable(MacOSVersion)) }
|
||||
attr_reader :on_system_block_min_os
|
||||
|
||||
sig { params(cask: Cask).void }
|
||||
def initialize(cask)
|
||||
@@ -167,6 +210,7 @@ module Cask
|
||||
@livecheck_defined = T.let(false, T::Boolean)
|
||||
@name = T.let([], T::Array[String])
|
||||
@no_autobump_defined = T.let(false, T::Boolean)
|
||||
@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_system_block_min_os = T.let(nil, T.nilable(MacOSVersion))
|
||||
@@ -212,6 +256,7 @@ module Cask
|
||||
# ```
|
||||
#
|
||||
# @api public
|
||||
sig { params(args: T.any(String, T::Array[String])).returns(T::Array[String]) }
|
||||
def name(*args)
|
||||
return @name if args.empty?
|
||||
|
||||
@@ -227,11 +272,21 @@ module Cask
|
||||
# ```
|
||||
#
|
||||
# @api public
|
||||
sig { params(description: T.nilable(String)).returns(T.nilable(String)) }
|
||||
def desc(description = nil)
|
||||
set_unique_stanza(:desc, description.nil?) { description }
|
||||
end
|
||||
|
||||
def set_unique_stanza(stanza, should_return)
|
||||
# NOTE: Using `WithoutRuntime` to avoid Sorbet wrapping this method,
|
||||
# which would interfere with `caller_locations` in methods like `url`.
|
||||
T::Sig::WithoutRuntime.sig {
|
||||
type_parameters(:U).params(
|
||||
stanza: Symbol,
|
||||
should_return: T::Boolean,
|
||||
_block: T.proc.returns(T.all(BasicObject, T.type_parameter(:U))),
|
||||
).returns(T.type_parameter(:U))
|
||||
}
|
||||
def set_unique_stanza(stanza, should_return, &_block)
|
||||
return instance_variable_get(:"@#{stanza}") if should_return
|
||||
|
||||
unless @cask.allow_reassignment
|
||||
@@ -261,10 +316,18 @@ module Cask
|
||||
# ```
|
||||
#
|
||||
# @api public
|
||||
sig { params(homepage: T.nilable(String)).returns(T.nilable(String)) }
|
||||
def homepage(homepage = nil)
|
||||
set_unique_stanza(:homepage, homepage.nil?) { homepage }
|
||||
end
|
||||
|
||||
sig {
|
||||
params(
|
||||
args: String,
|
||||
default: T::Boolean,
|
||||
block: T.nilable(T.proc.returns(String)),
|
||||
).returns(T.nilable(String))
|
||||
}
|
||||
def language(*args, default: false, &block)
|
||||
if args.empty?
|
||||
language_eval
|
||||
@@ -278,11 +341,13 @@ module Cask
|
||||
end
|
||||
|
||||
@language_blocks.default = block
|
||||
nil
|
||||
else
|
||||
raise CaskInvalidError.new(cask, "No block given to language stanza.")
|
||||
end
|
||||
end
|
||||
|
||||
sig { returns(T.nilable(String)) }
|
||||
def language_eval
|
||||
return @language_eval unless @language_eval.nil?
|
||||
|
||||
@@ -300,7 +365,7 @@ module Cask
|
||||
end
|
||||
|
||||
locales.each do |locale|
|
||||
key = locale.detect(@language_blocks.keys)
|
||||
key = T.cast(locale.detect(@language_blocks.keys), T.nilable(T::Array[String]))
|
||||
next if key.nil? || (language_block = @language_blocks[key]).nil?
|
||||
|
||||
return @language_eval = language_block.call
|
||||
@@ -309,6 +374,7 @@ module Cask
|
||||
@language_eval = language_blocks_default.call
|
||||
end
|
||||
|
||||
sig { returns(T::Array[String]) }
|
||||
def languages
|
||||
@language_blocks.keys.flatten
|
||||
end
|
||||
@@ -322,11 +388,13 @@ module Cask
|
||||
# ```
|
||||
#
|
||||
# @api public
|
||||
def url(*args, **options)
|
||||
caller_location = T.must(caller_locations).fetch(0)
|
||||
T::Sig::WithoutRuntime.sig { params(uri: T.nilable(T.any(URI::Generic, String)), options: T.untyped).returns(T.nilable(URL)) }
|
||||
def url(uri = nil, **options)
|
||||
caller_location = caller_locations.fetch(0)
|
||||
return @url unless uri
|
||||
|
||||
set_unique_stanza(:url, args.empty? && options.empty?) do
|
||||
URL.new(*args, **options, caller_location:)
|
||||
set_unique_stanza(:url, false) do
|
||||
URL.new(uri, **options, caller_location:)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -347,9 +415,10 @@ module Cask
|
||||
# ```
|
||||
#
|
||||
# @api public
|
||||
def container(**kwargs)
|
||||
set_unique_stanza(:container, kwargs.empty?) do
|
||||
DSL::Container.new(**kwargs)
|
||||
sig { params(nested: T.nilable(String), type: T.nilable(Symbol)).returns(T.nilable(DSL::Container)) }
|
||||
def container(nested: nil, type: nil)
|
||||
set_unique_stanza(:container, nested.nil? && type.nil?) do
|
||||
DSL::Container.new(nested:, type:)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -372,7 +441,7 @@ module Cask
|
||||
def rename(from = T.unsafe(nil), to = T.unsafe(nil))
|
||||
return @rename if from.nil?
|
||||
|
||||
@rename << DSL::Rename.new(T.must(from), T.must(to))
|
||||
@rename << DSL::Rename.new(from, to)
|
||||
end
|
||||
|
||||
# Sets the cask's version.
|
||||
@@ -443,7 +512,7 @@ module Cask
|
||||
)
|
||||
case val
|
||||
when :no_check
|
||||
val
|
||||
:no_check
|
||||
when String
|
||||
Checksum.new(val)
|
||||
else
|
||||
@@ -461,6 +530,7 @@ module Cask
|
||||
# ```
|
||||
#
|
||||
# @api public
|
||||
sig { params(arm: T.nilable(String), intel: T.nilable(String)).returns(T.nilable(String)) }
|
||||
def arch(arm: nil, intel: nil)
|
||||
should_return = arm.nil? && intel.nil?
|
||||
|
||||
@@ -502,6 +572,7 @@ module Cask
|
||||
# NOTE: Multiple dependencies can be specified.
|
||||
#
|
||||
# @api public
|
||||
sig { params(kwargs: T.untyped).returns(DSL::DependsOn) }
|
||||
def depends_on(**kwargs)
|
||||
@depends_on_set_in_block = true if @called_in_on_system_block
|
||||
return @depends_on if kwargs.empty?
|
||||
@@ -515,6 +586,7 @@ module Cask
|
||||
end
|
||||
|
||||
# @api private
|
||||
sig { void }
|
||||
def add_implicit_macos_dependency
|
||||
return if (cask_depends_on = @depends_on).present? && cask_depends_on.macos.present?
|
||||
|
||||
@@ -524,6 +596,7 @@ module Cask
|
||||
# Declare conflicts that keep a cask from installing or working correctly.
|
||||
#
|
||||
# @api public
|
||||
sig { params(kwargs: T.anything).returns(T.nilable(DSL::ConflictsWith)) }
|
||||
def conflicts_with(**kwargs)
|
||||
# TODO: Remove this constraint and instead merge multiple `conflicts_with` stanzas
|
||||
set_unique_stanza(:conflicts_with, kwargs.empty?) { DSL::ConflictsWith.new(**kwargs) }
|
||||
@@ -548,6 +621,12 @@ module Cask
|
||||
# Provide the user with cask-specific information at install time.
|
||||
#
|
||||
# @api public
|
||||
sig {
|
||||
params(
|
||||
strings: String,
|
||||
block: T.nilable(T.proc.returns(T.nilable(T.any(Symbol, String)))),
|
||||
).returns(T.any(String, DSL::Caveats))
|
||||
}
|
||||
def caveats(*strings, &block)
|
||||
if block
|
||||
@caveats.eval_caveats(&block)
|
||||
@@ -567,6 +646,7 @@ module Cask
|
||||
# Asserts that the cask artifacts auto-update.
|
||||
#
|
||||
# @api public
|
||||
sig { params(auto_updates: T.nilable(T::Boolean)).returns(T.nilable(T::Boolean)) }
|
||||
def auto_updates(auto_updates = nil)
|
||||
set_unique_stanza(:auto_updates, auto_updates.nil?) { auto_updates }
|
||||
end
|
||||
@@ -574,6 +654,7 @@ module Cask
|
||||
# Automatically fetch the latest version of a cask from changelogs.
|
||||
#
|
||||
# @api public
|
||||
sig { params(block: T.nilable(T.proc.void)).returns(Livecheck) }
|
||||
def livecheck(&block)
|
||||
return @livecheck unless block
|
||||
|
||||
@@ -611,6 +692,7 @@ module Cask
|
||||
end
|
||||
|
||||
# Is the cask in autobump list?
|
||||
sig { returns(T::Boolean) }
|
||||
def autobump?
|
||||
@autobump == true
|
||||
end
|
||||
@@ -620,6 +702,15 @@ module Cask
|
||||
# NOTE: A warning will be shown when trying to install this cask.
|
||||
#
|
||||
# @api public
|
||||
sig {
|
||||
params(
|
||||
date: String,
|
||||
because: T.any(String, Symbol),
|
||||
replacement: T.nilable(String),
|
||||
replacement_formula: T.nilable(String),
|
||||
replacement_cask: T.nilable(String),
|
||||
).void
|
||||
}
|
||||
def deprecate!(date:, because:, replacement: nil, replacement_formula: nil, replacement_cask: nil)
|
||||
if [replacement, replacement_formula, replacement_cask].filter_map(&:presence).length > 1
|
||||
raise ArgumentError, "more than one of replacement, replacement_formula and/or replacement_cask specified!"
|
||||
@@ -648,6 +739,15 @@ module Cask
|
||||
# NOTE: An error will be thrown when trying to install this cask.
|
||||
#
|
||||
# @api public
|
||||
sig {
|
||||
params(
|
||||
date: String,
|
||||
because: T.any(String, Symbol),
|
||||
replacement: T.nilable(String),
|
||||
replacement_formula: T.nilable(String),
|
||||
replacement_cask: T.nilable(String),
|
||||
).void
|
||||
}
|
||||
def disable!(date:, because:, replacement: nil, replacement_formula: nil, replacement_cask: nil)
|
||||
if [replacement, replacement_formula, replacement_cask].filter_map(&:presence).length > 1
|
||||
raise ArgumentError, "more than one of replacement, replacement_formula and/or replacement_cask specified!"
|
||||
@@ -706,11 +806,13 @@ module Cask
|
||||
end
|
||||
end
|
||||
|
||||
def method_missing(method, *)
|
||||
sig { override.params(method: Symbol, _args: T.anything).returns(T.noreturn) }
|
||||
def method_missing(method, *_args)
|
||||
raise NoMethodError, "undefined method '#{method}' for Cask '#{token}'"
|
||||
end
|
||||
|
||||
def respond_to_missing?(*)
|
||||
sig { override.params(_method_name: T.any(String, Symbol), _include_private: T::Boolean).returns(T::Boolean) }
|
||||
def respond_to_missing?(_method_name, _include_private = false)
|
||||
false
|
||||
end
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# typed: true # rubocop:todo Sorbet/StrictSigil
|
||||
# typed: strict
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Cask
|
||||
@@ -26,12 +26,16 @@ module Cask
|
||||
@invoked_caveats = T.let(Set.new, T::Set[Symbol])
|
||||
end
|
||||
|
||||
sig {
|
||||
params(name: Symbol, block: T.proc.bind(Caveats).void).void
|
||||
}
|
||||
def self.caveat(name, &block)
|
||||
define_method(name) do |*args|
|
||||
T.bind(self, Caveats)
|
||||
key = [name, *args]
|
||||
@invoked_caveats.add(name)
|
||||
invoked_caveats.add(name)
|
||||
text = instance_exec(*args, &block)
|
||||
@built_in_caveats[key] = text if text
|
||||
built_in_caveats[key] = text if text
|
||||
:built_in_caveat
|
||||
end
|
||||
end
|
||||
@@ -89,7 +93,7 @@ module Cask
|
||||
end
|
||||
|
||||
<<~EOS
|
||||
#{@cask} requires a kernel extension to work.
|
||||
#{cask} requires a kernel extension to work.
|
||||
If the installation fails, retry after you enable it in:
|
||||
#{navigation_path}
|
||||
|
||||
@@ -108,18 +112,18 @@ module Cask
|
||||
end
|
||||
|
||||
<<~EOS
|
||||
#{@cask} is not signed and requires Accessibility access,
|
||||
#{cask} is not signed and requires Accessibility access,
|
||||
so you will need to re-grant Accessibility access every time the app is updated.
|
||||
|
||||
Enable or re-enable it in:
|
||||
#{navigation_path} → #{access}
|
||||
To re-enable, untick and retick #{@cask}.app.
|
||||
To re-enable, untick and retick #{cask}.app.
|
||||
EOS
|
||||
end
|
||||
|
||||
caveat :path_environment_variable do |path|
|
||||
<<~EOS
|
||||
To use #{@cask}, you may need to add the #{path} directory
|
||||
To use #{cask}, you may need to add the #{path} directory
|
||||
to your PATH environment variable, e.g. (for Bash shell):
|
||||
export PATH=#{path}:"$PATH"
|
||||
EOS
|
||||
@@ -127,7 +131,7 @@ module Cask
|
||||
|
||||
caveat :zsh_path_helper do |path|
|
||||
<<~EOS
|
||||
To use #{@cask}, zsh users may need to add the following line to their
|
||||
To use #{cask}, zsh users may need to add the following line to their
|
||||
~/.zprofile. (Among other effects, #{path} will be added to the
|
||||
PATH environment variable):
|
||||
eval `/usr/libexec/path_helper -s`
|
||||
@@ -138,7 +142,7 @@ module Cask
|
||||
next unless HOMEBREW_PREFIX.to_s.downcase.start_with?("/usr/local")
|
||||
|
||||
<<~EOS
|
||||
Cask #{@cask} installs files under /usr/local. The presence of such
|
||||
Cask #{cask} installs files under /usr/local. The presence of such
|
||||
files can cause warnings when running `brew doctor`, which is considered
|
||||
to be a bug in Homebrew Cask.
|
||||
EOS
|
||||
@@ -147,17 +151,17 @@ module Cask
|
||||
caveat :depends_on_java do |java_version = :any|
|
||||
if java_version == :any
|
||||
<<~EOS
|
||||
#{@cask} requires Java. You can install the latest version with:
|
||||
#{cask} requires Java. You can install the latest version with:
|
||||
brew install --cask temurin
|
||||
EOS
|
||||
elsif java_version.include?("+")
|
||||
elsif java_version.to_s.include?("+")
|
||||
<<~EOS
|
||||
#{@cask} requires Java #{java_version}. You can install the latest version with:
|
||||
#{cask} requires Java #{java_version}. You can install the latest version with:
|
||||
brew install --cask temurin
|
||||
EOS
|
||||
else
|
||||
<<~EOS
|
||||
#{@cask} requires Java #{java_version}. You can install it with:
|
||||
#{cask} requires Java #{java_version}. You can install it with:
|
||||
brew install --cask temurin@#{java_version}
|
||||
EOS
|
||||
end
|
||||
@@ -167,7 +171,7 @@ module Cask
|
||||
next if Homebrew::SimulateSystem.current_arch != :arm
|
||||
|
||||
<<~EOS
|
||||
#{@cask} is built for Intel macOS and so requires Rosetta 2 to be installed.
|
||||
#{cask} is built for Intel macOS and so requires Rosetta 2 to be installed.
|
||||
You can install Rosetta 2 with:
|
||||
softwareupdate --install-rosetta --agree-to-license
|
||||
Note that it is very difficult to remove Rosetta 2 once it is installed.
|
||||
@@ -176,29 +180,39 @@ module Cask
|
||||
|
||||
caveat :logout do
|
||||
<<~EOS
|
||||
You must log out and log back in for the installation of #{@cask} to take effect.
|
||||
You must log out and log back in for the installation of #{cask} to take effect.
|
||||
EOS
|
||||
end
|
||||
|
||||
caveat :reboot do
|
||||
<<~EOS
|
||||
You must reboot for the installation of #{@cask} to take effect.
|
||||
You must reboot for the installation of #{cask} to take effect.
|
||||
EOS
|
||||
end
|
||||
|
||||
caveat :license do |web_page|
|
||||
<<~EOS
|
||||
Installing #{@cask} means you have AGREED to the license at:
|
||||
Installing #{cask} means you have AGREED to the license at:
|
||||
#{Formatter.url(web_page.to_s)}
|
||||
EOS
|
||||
end
|
||||
|
||||
caveat :free_license do |web_page|
|
||||
<<~EOS
|
||||
The vendor offers a free license for #{@cask} at:
|
||||
The vendor offers a free license for #{cask} at:
|
||||
#{Formatter.url(web_page.to_s)}
|
||||
EOS
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# These attrs are required as a workaround for https://github.com/sorbet/sorbet/issues/8106
|
||||
|
||||
sig { returns(T::Set[Symbol]) }
|
||||
attr_reader :invoked_caveats
|
||||
|
||||
sig { returns(T::Hash[T::Array[Symbol], String]) }
|
||||
attr_reader :built_in_caveats
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# typed: true # rubocop:todo Sorbet/StrictSigil
|
||||
# typed: strict
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Cask
|
||||
@@ -7,10 +7,11 @@ module Cask
|
||||
|
||||
# Cask error containing multiple other errors.
|
||||
class MultipleCaskErrors < CaskError
|
||||
sig { params(errors: T::Array[StandardError]).void }
|
||||
def initialize(errors)
|
||||
super()
|
||||
|
||||
@errors = errors
|
||||
@errors = T.let(errors, T::Array[StandardError])
|
||||
end
|
||||
|
||||
sig { returns(String) }
|
||||
@@ -30,11 +31,12 @@ module Cask
|
||||
sig { returns(String) }
|
||||
attr_reader :reason
|
||||
|
||||
sig { params(token: T.any(String, Symbol, Cask), reason: T.nilable(Object)).void }
|
||||
def initialize(token, reason = nil)
|
||||
super()
|
||||
|
||||
@token = token.to_s
|
||||
@reason = reason.to_s
|
||||
@token = T.let(token.to_s, String)
|
||||
@reason = T.let(reason.to_s, String)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -48,8 +50,10 @@ module Cask
|
||||
|
||||
# Error when a cask cannot be installed.
|
||||
class CaskCannotBeInstalledError < AbstractCaskErrorWithToken
|
||||
sig { returns(String) }
|
||||
attr_reader :message
|
||||
|
||||
sig { params(token: T.any(String, Symbol, Cask), message: String).void }
|
||||
def initialize(token, message)
|
||||
super(token)
|
||||
@message = message
|
||||
@@ -63,8 +67,10 @@ module Cask
|
||||
|
||||
# Error when a cask conflicts with another cask.
|
||||
class CaskConflictError < AbstractCaskErrorWithToken
|
||||
sig { returns(Cask) }
|
||||
attr_reader :conflicting_cask
|
||||
|
||||
sig { params(token: T.any(String, Symbol, Cask), conflicting_cask: Cask).void }
|
||||
def initialize(token, conflicting_cask)
|
||||
super(token)
|
||||
@conflicting_cask = conflicting_cask
|
||||
@@ -94,8 +100,10 @@ module Cask
|
||||
|
||||
# Error when a cask in a specific tap is not available.
|
||||
class TapCaskUnavailableError < CaskUnavailableError
|
||||
sig { returns(Tap) }
|
||||
attr_reader :tap
|
||||
|
||||
sig { params(tap: Tap, token: String).void }
|
||||
def initialize(tap, token)
|
||||
super("#{tap}/#{token}")
|
||||
@tap = tap
|
||||
@@ -122,6 +130,7 @@ module Cask
|
||||
|
||||
sig { params(token: String, loaders: T::Array[CaskLoader::FromNameLoader]).void }
|
||||
def initialize(token, loaders)
|
||||
@token = token
|
||||
@loaders = loaders
|
||||
|
||||
taps = loaders.map(&:tap)
|
||||
@@ -178,6 +187,7 @@ module Cask
|
||||
|
||||
# Error when a cask token does not match the file name.
|
||||
class CaskTokenMismatchError < CaskInvalidError
|
||||
sig { params(token: T.any(String, Symbol, Cask), header_token: String).void }
|
||||
def initialize(token, header_token)
|
||||
super(token, "Token '#{header_token}' in header line does not match the file name.")
|
||||
end
|
||||
@@ -185,8 +195,13 @@ module Cask
|
||||
|
||||
# Error during quarantining of a file.
|
||||
class CaskQuarantineError < CaskError
|
||||
attr_reader :path, :reason
|
||||
sig { returns(T.any(String, Pathname)) }
|
||||
attr_reader :path
|
||||
|
||||
sig { returns(String) }
|
||||
attr_reader :reason
|
||||
|
||||
sig { params(path: T.any(String, Pathname), reason: String).void }
|
||||
def initialize(path, reason)
|
||||
super()
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ module Cask
|
||||
def self.info(cask, args:)
|
||||
puts get_info(cask)
|
||||
|
||||
return unless cask.tap.core_cask_tap?
|
||||
return unless cask.tap&.core_cask_tap?
|
||||
|
||||
require "utils/analytics"
|
||||
::Utils::Analytics.cask_output(cask, args:)
|
||||
@@ -177,12 +177,12 @@ module Cask
|
||||
|
||||
sig { params(cask: Cask).returns(T.nilable(String)) }
|
||||
def self.repo_info(cask)
|
||||
return if cask.tap.nil?
|
||||
return unless (tap = cask.tap)
|
||||
|
||||
url = if cask.tap.custom_remote? && !cask.tap.remote.nil?
|
||||
cask.tap.remote
|
||||
url = if tap.custom_remote? && !tap.remote.nil?
|
||||
tap.remote
|
||||
else
|
||||
"#{cask.tap.default_remote}/blob/HEAD/#{cask.tap.relative_cask_path(cask.token)}"
|
||||
"#{tap.default_remote}/blob/HEAD/#{tap.relative_cask_path(cask.token)}"
|
||||
end
|
||||
|
||||
"From: #{Formatter.url(url)}"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# typed: true # rubocop:todo Sorbet/StrictSigil
|
||||
# typed: strict
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "formula_installer"
|
||||
@@ -53,6 +53,7 @@ module Cask
|
||||
@download_queue = download_queue
|
||||
@defer_fetch = T.let(defer_fetch, T::Boolean)
|
||||
@ran_prelude = T.let(false, T::Boolean)
|
||||
@cask_and_formula_dependencies = T.let(nil, T.nilable(T::Array[T.any(Formula, ::Cask::Cask)]))
|
||||
end
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
@@ -94,6 +95,7 @@ module Cask
|
||||
sig { returns(T::Boolean) }
|
||||
def zap? = @zap
|
||||
|
||||
sig { params(cask: ::Cask::Cask).returns(T.nilable(String)) }
|
||||
def self.caveats(cask)
|
||||
odebug "Printing caveats"
|
||||
|
||||
@@ -242,7 +244,7 @@ on_request: true)
|
||||
|
||||
sig { returns(Download) }
|
||||
def downloader
|
||||
@downloader ||= Download.new(@cask, quarantine: quarantine?)
|
||||
@downloader ||= T.let(Download.new(@cask, quarantine: quarantine?), T.nilable(Download))
|
||||
end
|
||||
|
||||
sig { params(quiet: T.nilable(T::Boolean), timeout: T.nilable(T.any(Integer, Float))).returns(Pathname) }
|
||||
@@ -263,11 +265,15 @@ on_request: true)
|
||||
EOS
|
||||
end
|
||||
|
||||
sig { returns(UnpackStrategy) }
|
||||
def primary_container
|
||||
@primary_container ||= begin
|
||||
downloaded_path = download(quiet: true)
|
||||
UnpackStrategy.detect(downloaded_path, type: @cask.container&.type, merge_xattrs: true)
|
||||
end
|
||||
@primary_container ||= T.let(
|
||||
begin
|
||||
downloaded_path = download(quiet: true)
|
||||
UnpackStrategy.detect(downloaded_path, type: @cask.container&.type, merge_xattrs: true)
|
||||
end,
|
||||
T.nilable(UnpackStrategy),
|
||||
)
|
||||
end
|
||||
|
||||
sig { returns(ArtifactSet) }
|
||||
@@ -279,14 +285,17 @@ on_request: true)
|
||||
def extract_primary_container(to: @cask.staged_path)
|
||||
odebug "Extracting primary container"
|
||||
|
||||
odebug "Using container class #{primary_container.class} for #{primary_container.path}"
|
||||
container = primary_container
|
||||
raise "unexpected nil primary_container" unless container
|
||||
|
||||
odebug "Using container class #{container.class} for #{container.path}"
|
||||
|
||||
basename = downloader.basename
|
||||
|
||||
if (nested_container = @cask.container&.nested)
|
||||
Dir.mktmpdir("cask-installer", HOMEBREW_TEMP) do |tmpdir|
|
||||
tmpdir = Pathname(tmpdir)
|
||||
primary_container.extract(to: tmpdir, basename:, verbose: verbose?)
|
||||
container.extract(to: tmpdir, basename:, verbose: verbose?)
|
||||
|
||||
FileUtils.chmod_R "+rw", tmpdir/nested_container, force: true, verbose: verbose?
|
||||
|
||||
@@ -294,13 +303,13 @@ on_request: true)
|
||||
.extract_nestedly(to:, verbose: verbose?)
|
||||
end
|
||||
else
|
||||
primary_container.extract_nestedly(to:, basename:, verbose: verbose?)
|
||||
container.extract_nestedly(to:, basename:, verbose: verbose?)
|
||||
end
|
||||
|
||||
return unless quarantine?
|
||||
return unless Quarantine.available?
|
||||
|
||||
Quarantine.propagate(from: primary_container.path, to:)
|
||||
Quarantine.propagate(from: container.path, to:)
|
||||
end
|
||||
|
||||
sig { params(target_dir: T.nilable(Pathname)).void }
|
||||
@@ -384,6 +393,7 @@ on_request: true)
|
||||
nil
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def check_macos_requirements
|
||||
return unless @cask.depends_on.macos
|
||||
return if @cask.depends_on.macos.satisfied?
|
||||
@@ -395,6 +405,7 @@ on_request: true)
|
||||
def check_arch_requirements
|
||||
return if @cask.depends_on.arch.nil?
|
||||
|
||||
@current_arch = T.let(@current_arch, T.nilable(T::Hash[Symbol, T.untyped]))
|
||||
@current_arch ||= { type: Hardware::CPU.type, bits: Hardware::CPU.bits }
|
||||
return if @cask.depends_on.arch.any? do |arch|
|
||||
arch[:type] == @current_arch[:type] &&
|
||||
@@ -407,7 +418,7 @@ on_request: true)
|
||||
"but you are running #{@current_arch}."
|
||||
end
|
||||
|
||||
sig { returns(T::Array[T.untyped]) }
|
||||
sig { returns(T::Array[T.any(Formula, ::Cask::Cask)]) }
|
||||
def cask_and_formula_dependencies
|
||||
return @cask_and_formula_dependencies if @cask_and_formula_dependencies
|
||||
|
||||
@@ -415,7 +426,10 @@ on_request: true)
|
||||
|
||||
raise CaskSelfReferencingDependencyError, @cask.token if graph.fetch(@cask).include?(@cask)
|
||||
|
||||
::Utils::TopologicalHash.graph_package_dependencies(primary_container.dependencies, graph)
|
||||
pc = primary_container
|
||||
raise "unexpected nil primary_container" unless pc
|
||||
|
||||
::Utils::TopologicalHash.graph_package_dependencies(pc.dependencies, graph)
|
||||
|
||||
begin
|
||||
@cask_and_formula_dependencies = graph.tsort - [@cask]
|
||||
@@ -426,6 +440,7 @@ on_request: true)
|
||||
end
|
||||
end
|
||||
|
||||
sig { returns(T::Array[T.any(Formula, ::Cask::Cask)]) }
|
||||
def missing_cask_and_formula_dependencies
|
||||
cask_and_formula_dependencies.reject do |cask_or_formula|
|
||||
case cask_or_formula
|
||||
@@ -437,6 +452,7 @@ on_request: true)
|
||||
end
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def satisfy_cask_and_formula_dependencies
|
||||
return if installed_as_dependency?
|
||||
|
||||
@@ -498,14 +514,25 @@ on_request: true)
|
||||
end
|
||||
end
|
||||
|
||||
sig { returns(T.nilable(String)) }
|
||||
def caveats
|
||||
self.class.caveats(@cask)
|
||||
end
|
||||
|
||||
sig { returns(Pathname) }
|
||||
def metadata_subdir
|
||||
@metadata_subdir ||= @cask.metadata_subdir("Casks", timestamp: :now, create: true)
|
||||
@metadata_subdir ||= T.let(
|
||||
begin
|
||||
msd = @cask.metadata_subdir("Casks", timestamp: :now, create: true)
|
||||
raise "unexpected nil metadata_subdir" unless msd
|
||||
|
||||
msd
|
||||
end,
|
||||
T.nilable(Pathname),
|
||||
)
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def save_caskfile
|
||||
old_savedir = @cask.metadata_timestamped_path
|
||||
|
||||
@@ -530,12 +557,16 @@ on_request: true)
|
||||
FileUtils.rm_r(old_savedir) if old_savedir
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def save_config_file
|
||||
@cask.config_path.atomic_write(@cask.config.to_json)
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def save_download_sha
|
||||
@cask.download_sha_path.atomic_write(@cask.new_download_sha) if @cask.checksumable?
|
||||
return unless @cask.checksumable?
|
||||
|
||||
@cask.download_sha_path.atomic_write(@cask.new_download_sha)
|
||||
end
|
||||
|
||||
sig { params(successor: T.nilable(Cask)).void }
|
||||
@@ -552,17 +583,20 @@ on_request: true)
|
||||
purge_caskroom_path if force?
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def remove_tabfile
|
||||
tabfile = @cask.tab.tabfile
|
||||
FileUtils.rm_f tabfile if tabfile
|
||||
@cask.config_path.parent.rmdir_if_possible
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def remove_config_file
|
||||
FileUtils.rm_f @cask.config_path
|
||||
@cask.config_path.parent.rmdir_if_possible
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def remove_download_sha
|
||||
FileUtils.rm_f @cask.download_sha_path
|
||||
@cask.download_sha_path.parent.rmdir_if_possible
|
||||
@@ -574,19 +608,33 @@ on_request: true)
|
||||
backup
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def backup
|
||||
@cask.staged_path.rename backup_path
|
||||
@cask.metadata_versioned_path.rename backup_metadata_path
|
||||
bp = backup_path
|
||||
raise "unexpected nil backup_path" unless bp
|
||||
|
||||
bmp = backup_metadata_path
|
||||
raise "unexpected nil backup_metadata_path" unless bmp
|
||||
|
||||
@cask.staged_path.rename bp.to_s
|
||||
@cask.metadata_versioned_path.rename bmp.to_s
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def restore_backup
|
||||
return if !backup_path.directory? || !backup_metadata_path.directory?
|
||||
bp = backup_path
|
||||
return unless bp
|
||||
|
||||
bmp = backup_metadata_path
|
||||
return unless bmp
|
||||
|
||||
return if !bp.directory? || !bmp.directory?
|
||||
|
||||
FileUtils.rm_r(@cask.staged_path) if @cask.staged_path.exist?
|
||||
FileUtils.rm_r(@cask.metadata_versioned_path) if @cask.metadata_versioned_path.exist?
|
||||
|
||||
backup_path.rename @cask.staged_path
|
||||
backup_metadata_path.rename @cask.metadata_versioned_path
|
||||
bp.rename @cask.staged_path.to_s
|
||||
bmp.rename @cask.metadata_versioned_path.to_s
|
||||
end
|
||||
|
||||
sig { params(predecessor: Cask).void }
|
||||
@@ -596,6 +644,7 @@ on_request: true)
|
||||
install_artifacts(predecessor:)
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def finalize_upgrade
|
||||
ohai "Purging files for version #{@cask.version} of Cask #{@cask}"
|
||||
|
||||
@@ -651,6 +700,7 @@ on_request: true)
|
||||
end
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def zap
|
||||
load_installed_caskfile!
|
||||
uninstall_artifacts
|
||||
@@ -666,35 +716,41 @@ on_request: true)
|
||||
purge_caskroom_path
|
||||
end
|
||||
|
||||
sig { returns(T.nilable(Pathname)) }
|
||||
def backup_path
|
||||
return if @cask.staged_path.nil?
|
||||
|
||||
Pathname("#{@cask.staged_path}.upgrading")
|
||||
end
|
||||
|
||||
sig { returns(T.nilable(Pathname)) }
|
||||
def backup_metadata_path
|
||||
return if @cask.metadata_versioned_path.nil?
|
||||
|
||||
Pathname("#{@cask.metadata_versioned_path}.upgrading")
|
||||
end
|
||||
|
||||
sig { params(path: Pathname).void }
|
||||
def gain_permissions_remove(path)
|
||||
Utils.gain_permissions_remove(path, command: @command)
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def purge_backed_up_versioned_files
|
||||
# versioned staged distribution
|
||||
gain_permissions_remove(backup_path) if backup_path&.exist?
|
||||
gain_permissions_remove(T.must(backup_path)) if backup_path&.exist?
|
||||
|
||||
# Homebrew Cask metadata
|
||||
return unless backup_metadata_path.directory?
|
||||
bmp = backup_metadata_path
|
||||
return unless bmp&.directory?
|
||||
|
||||
backup_metadata_path.children.each do |subdir|
|
||||
bmp.children.each do |subdir|
|
||||
gain_permissions_remove(subdir)
|
||||
end
|
||||
backup_metadata_path.rmdir_if_possible
|
||||
bmp.rmdir_if_possible
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def purge_versioned_files
|
||||
ohai "Purging files for version #{@cask.version} of Cask #{@cask}"
|
||||
|
||||
@@ -721,6 +777,7 @@ on_request: true)
|
||||
end
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def purge_caskroom_path
|
||||
odebug "Purging all staged versions of Cask #{@cask}"
|
||||
gain_permissions_remove(@cask.caskroom_path)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# typed: true # rubocop:todo Sorbet/StrictSigil
|
||||
# typed: strict
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "development_tools"
|
||||
@@ -14,29 +14,35 @@ module Cask
|
||||
|
||||
QUARANTINE_ATTRIBUTE = "com.apple.quarantine"
|
||||
|
||||
QUARANTINE_SCRIPT = (HOMEBREW_LIBRARY_PATH/"cask/utils/quarantine.swift").freeze
|
||||
COPY_XATTRS_SCRIPT = (HOMEBREW_LIBRARY_PATH/"cask/utils/copy-xattrs.swift").freeze
|
||||
QUARANTINE_SCRIPT = T.let((HOMEBREW_LIBRARY_PATH/"cask/utils/quarantine.swift").freeze, Pathname)
|
||||
COPY_XATTRS_SCRIPT = T.let((HOMEBREW_LIBRARY_PATH/"cask/utils/copy-xattrs.swift").freeze, Pathname)
|
||||
|
||||
sig { returns(T.nilable(T.any(String, Pathname))) }
|
||||
def self.swift
|
||||
@swift ||= begin
|
||||
# /usr/bin/swift (which runs via xcrun) adds `/usr/local/include` to the top of the include path,
|
||||
# which allows really broken local setups to break our Swift usage here. Using the underlying
|
||||
# Swift executable directly however (returned by `xcrun -find`) avoids this CPATH mess.
|
||||
xcrun_swift = ::Utils.popen_read("/usr/bin/xcrun", "-find", "swift", err: :close).chomp
|
||||
if $CHILD_STATUS.success? && File.executable?(xcrun_swift)
|
||||
xcrun_swift
|
||||
else
|
||||
DevelopmentTools.locate("swift")
|
||||
end
|
||||
end
|
||||
@swift ||= T.let(
|
||||
begin
|
||||
# /usr/bin/swift (which runs via xcrun) adds `/usr/local/include` to the top of the include path,
|
||||
# which allows really broken local setups to break our Swift usage here. Using the underlying
|
||||
# Swift executable directly however (returned by `xcrun -find`) avoids this CPATH mess.
|
||||
xcrun_swift = ::Utils.popen_read("/usr/bin/xcrun", "-find", "swift", err: :close).chomp
|
||||
if $CHILD_STATUS.success? && File.executable?(xcrun_swift)
|
||||
xcrun_swift
|
||||
else
|
||||
DevelopmentTools.locate("swift")
|
||||
end
|
||||
end,
|
||||
T.nilable(T.any(String, Pathname)),
|
||||
)
|
||||
end
|
||||
private_class_method :swift
|
||||
|
||||
sig { returns(T.nilable(Pathname)) }
|
||||
def self.xattr
|
||||
@xattr ||= DevelopmentTools.locate("xattr")
|
||||
@xattr ||= T.let(DevelopmentTools.locate("xattr"), T.nilable(Pathname))
|
||||
end
|
||||
private_class_method :xattr
|
||||
|
||||
sig { returns(T::Array[String]) }
|
||||
def self.swift_target_args
|
||||
["-target", "#{Hardware::CPU.arch}-apple-macosx#{MacOS.version}"]
|
||||
end
|
||||
@@ -47,14 +53,17 @@ module Cask
|
||||
odebug "Checking quarantine support"
|
||||
|
||||
check_output = nil
|
||||
status = if xattr.nil? || !system_command(xattr, args: ["-h"], print_stderr: false).success?
|
||||
status = if xattr.nil? || !system_command(T.must(xattr), args: ["-h"], print_stderr: false).success?
|
||||
odebug "There's no working version of `xattr` on this system."
|
||||
:xattr_broken
|
||||
elsif swift.nil?
|
||||
odebug "Swift is not available on this system."
|
||||
:no_swift
|
||||
else
|
||||
api_check = system_command(swift,
|
||||
s = swift
|
||||
raise "unexpected nil swift" unless s
|
||||
|
||||
api_check = system_command(s,
|
||||
args: [*swift_target_args, QUARANTINE_SCRIPT],
|
||||
print_stderr: false)
|
||||
|
||||
@@ -90,11 +99,12 @@ module Cask
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
def self.available?
|
||||
@quarantine_support ||= check_quarantine_support
|
||||
@quarantine_support ||= T.let(check_quarantine_support, T.nilable([Symbol, T.nilable(String)]))
|
||||
|
||||
@quarantine_support[0] == :quarantine_available
|
||||
end
|
||||
|
||||
sig { params(file: T.nilable(T.any(String, Pathname))).returns(T.nilable(T::Boolean)) }
|
||||
def self.detect(file)
|
||||
return if file.nil?
|
||||
|
||||
@@ -107,12 +117,16 @@ module Cask
|
||||
quarantine_status
|
||||
end
|
||||
|
||||
sig { params(file: T.any(String, Pathname)).returns(String) }
|
||||
def self.status(file)
|
||||
system_command(xattr,
|
||||
raise "unexpected nil xattr" unless xattr
|
||||
|
||||
system_command(T.must(xattr),
|
||||
args: ["-p", QUARANTINE_ATTRIBUTE, file],
|
||||
print_stderr: false).stdout.rstrip
|
||||
end
|
||||
|
||||
sig { params(attribute: String).returns(String) }
|
||||
def self.toggle_no_translocation_bit(attribute)
|
||||
fields = attribute.split(";")
|
||||
|
||||
@@ -120,17 +134,20 @@ module Cask
|
||||
# Let's toggle the app translocation bit, bit 8
|
||||
# http://www.openradar.me/radar?id=5022734169931776
|
||||
|
||||
fields[0] = (fields[0].to_i(16) | 0x0100).to_s(16).rjust(4, "0")
|
||||
fields[0] = (fields.fetch(0).to_i(16) | 0x0100).to_s(16).rjust(4, "0")
|
||||
|
||||
fields.join(";")
|
||||
end
|
||||
|
||||
sig { params(download_path: T.nilable(Pathname)).void }
|
||||
def self.release!(download_path: nil)
|
||||
return unless detect(download_path)
|
||||
return if !download_path || !detect(download_path)
|
||||
|
||||
odebug "Releasing #{download_path} from quarantine"
|
||||
|
||||
quarantiner = system_command(xattr,
|
||||
raise "unexpected nil xattr" unless xattr
|
||||
|
||||
quarantiner = system_command(T.must(xattr),
|
||||
args: [
|
||||
"-d",
|
||||
QUARANTINE_ATTRIBUTE,
|
||||
@@ -143,6 +160,7 @@ module Cask
|
||||
raise CaskQuarantineReleaseError.new(download_path, quarantiner.stderr)
|
||||
end
|
||||
|
||||
sig { params(cask: T.nilable(Cask), download_path: T.nilable(Pathname), action: T::Boolean).void }
|
||||
def self.cask!(cask: nil, download_path: nil, action: true)
|
||||
return if cask.nil? || download_path.nil?
|
||||
|
||||
@@ -150,7 +168,9 @@ module Cask
|
||||
|
||||
odebug "Quarantining #{download_path}"
|
||||
|
||||
quarantiner = system_command(swift,
|
||||
raise "unexpected nil swift" unless swift
|
||||
|
||||
quarantiner = system_command(T.must(swift),
|
||||
args: [
|
||||
*swift_target_args,
|
||||
QUARANTINE_SCRIPT,
|
||||
@@ -170,6 +190,7 @@ module Cask
|
||||
end
|
||||
end
|
||||
|
||||
sig { params(from: T.nilable(Pathname), to: T.nilable(Pathname)).void }
|
||||
def self.propagate(from: nil, to: nil)
|
||||
return if from.nil? || to.nil?
|
||||
|
||||
@@ -191,11 +212,13 @@ module Cask
|
||||
],
|
||||
input: resolved_paths.join("\0"))
|
||||
|
||||
raise "unexpected nil xattr" unless xattr
|
||||
|
||||
quarantiner = system_command("/usr/bin/xargs",
|
||||
args: [
|
||||
"-0",
|
||||
"--",
|
||||
xattr,
|
||||
T.must(xattr),
|
||||
"-w",
|
||||
QUARANTINE_ATTRIBUTE,
|
||||
quarantine_status,
|
||||
@@ -212,8 +235,10 @@ module Cask
|
||||
def self.copy_xattrs(from, to, command:)
|
||||
odebug "Copying xattrs from #{from} to #{to}"
|
||||
|
||||
raise "unexpected nil swift" unless swift
|
||||
|
||||
command.run!(
|
||||
swift,
|
||||
T.must(swift),
|
||||
args: [
|
||||
*swift_target_args,
|
||||
COPY_XATTRS_SCRIPT,
|
||||
|
||||
@@ -161,7 +161,7 @@ module Cask
|
||||
invalid_cask = !c.installed?
|
||||
|
||||
invalid_cask ||= begin
|
||||
loaded_cask = CaskLoader.load(c.installed_caskfile)
|
||||
loaded_cask = CaskLoader.load(T.must(c.installed_caskfile))
|
||||
false
|
||||
rescue CaskInvalidError, CaskUnavailableError
|
||||
true
|
||||
|
||||
@@ -8,7 +8,10 @@ class CaskDependent
|
||||
# Defines a dependency on another cask
|
||||
class Requirement < ::Requirement
|
||||
satisfy(build_env: false) do
|
||||
Cask::CaskLoader.load(cask).installed?
|
||||
cask_token = cask
|
||||
raise "unexpected nil cask" unless cask_token
|
||||
|
||||
Cask::CaskLoader.load(cask_token).installed?
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -70,7 +70,12 @@ module Homebrew
|
||||
end
|
||||
when Cask::Cask
|
||||
cask = formula_or_cask
|
||||
ref = cask.loaded_from_api? ? cask.full_name : cask.sourcefile_path
|
||||
if cask.loaded_from_api?
|
||||
ref = cask.full_name
|
||||
else
|
||||
ref = cask.sourcefile_path
|
||||
raise "unexpected nil cask sourcefile_path" unless ref
|
||||
end
|
||||
|
||||
os_arch_combinations.each do |os, arch|
|
||||
next if os == :linux
|
||||
|
||||
@@ -331,7 +331,7 @@ module Homebrew
|
||||
when Formula
|
||||
Utils::Analytics.formula_output(obj, args:) if obj.core_formula?
|
||||
when Cask::Cask
|
||||
Utils::Analytics.cask_output(obj, args:) if obj.tap.core_cask_tap?
|
||||
Utils::Analytics.cask_output(obj, args:) if obj.tap&.core_cask_tap?
|
||||
when FormulaOrCaskUnavailableError
|
||||
Utils::Analytics.output(filter: obj.name, args:)
|
||||
else
|
||||
@@ -431,6 +431,7 @@ module Homebrew
|
||||
|
||||
sig { params(formula_or_cask: T.any(Formula, Cask::Cask)).returns(String) }
|
||||
def github_info(formula_or_cask)
|
||||
tap = T.let(nil, T.nilable(Tap))
|
||||
path = case formula_or_cask
|
||||
when Formula
|
||||
formula = formula_or_cask
|
||||
@@ -443,14 +444,18 @@ module Homebrew
|
||||
tap = cask.tap
|
||||
return cask.sourcefile_path.to_s if tap.blank? || tap.remote.blank?
|
||||
|
||||
if cask.sourcefile_path.blank? || cask.sourcefile_path.extname != ".rb"
|
||||
sourcefile_path = cask.sourcefile_path
|
||||
if sourcefile_path.blank? || sourcefile_path.extname != ".rb"
|
||||
return "#{tap.default_remote}/blob/HEAD/#{tap.relative_cask_path(cask.token)}"
|
||||
end
|
||||
|
||||
cask.sourcefile_path.relative_path_from(tap.path)
|
||||
sourcefile_path.relative_path_from(tap.path)
|
||||
end
|
||||
|
||||
github_remote_path(tap.remote, path.to_s)
|
||||
remote = tap.remote
|
||||
raise "unexpected nil tap.remote" unless remote
|
||||
|
||||
github_remote_path(remote, path.to_s)
|
||||
end
|
||||
|
||||
sig { params(formula: Formula).void }
|
||||
|
||||
@@ -155,8 +155,11 @@ module Homebrew
|
||||
else
|
||||
c = formula_or_cask
|
||||
|
||||
c.outdated_info(upgrade_greedy_cask?(args.greedy?, formula_or_cask),
|
||||
verbose?, true, args.greedy_latest?, args.greedy_auto_updates?)
|
||||
T.cast(
|
||||
c.outdated_info(upgrade_greedy_cask?(args.greedy?, formula_or_cask),
|
||||
verbose?, true, args.greedy_latest?, args.greedy_auto_updates?),
|
||||
T::Hash[Symbol, T.untyped],
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -83,10 +83,12 @@ module Homebrew
|
||||
retry
|
||||
end
|
||||
|
||||
odie "This cask is not in a tap!" if cask.tap.blank?
|
||||
odie "This cask's tap is not a Git repository!" unless cask.tap.git?
|
||||
tap = cask.tap
|
||||
odie "This cask is not in a tap!" if tap.nil?
|
||||
|
||||
odie <<~EOS unless cask.tap.allow_bump?(cask.token)
|
||||
odie "This cask's tap is not a Git repository!" unless tap.git?
|
||||
|
||||
odie <<~EOS unless tap.allow_bump?(cask.token)
|
||||
Whoops, the #{cask.token} cask has its version update
|
||||
pull requests automatically opened by BrewTestBot every ~3 hours!
|
||||
We'd still love your contributions, though, so try another one
|
||||
@@ -131,7 +133,10 @@ module Homebrew
|
||||
branch_name = "bump-#{cask.token}"
|
||||
commit_message = nil
|
||||
|
||||
old_contents = File.read(cask.sourcefile_path)
|
||||
sourcefile_path = cask.sourcefile_path
|
||||
raise "unexpected nil cask.sourcefile_path" unless sourcefile_path
|
||||
|
||||
old_contents = File.read(sourcefile_path)
|
||||
|
||||
if new_base_url
|
||||
commit_message ||= "#{cask.token}: update URL"
|
||||
@@ -174,7 +179,7 @@ module Homebrew
|
||||
|
||||
# Remove nested arrays where elements are identical
|
||||
replacement_pairs = replacement_pairs.reject { |pair| pair[0] == pair[1] }.uniq.compact
|
||||
Utils::Inreplace.inreplace_pairs(cask.sourcefile_path,
|
||||
Utils::Inreplace.inreplace_pairs(sourcefile_path,
|
||||
replacement_pairs,
|
||||
read_only_run: args.dry_run?,
|
||||
silent: args.quiet?)
|
||||
@@ -275,11 +280,14 @@ module Homebrew
|
||||
).returns(T::Array[[T.any(Regexp, String), T.any(Pathname, String)]])
|
||||
}
|
||||
def replace_version_and_checksum(cask, new_hash, new_version, replacement_pairs)
|
||||
cask_sourcefile_path = cask.sourcefile_path
|
||||
raise "unexpected nil cask.sourcefile_path" unless cask_sourcefile_path
|
||||
|
||||
generate_system_options(cask, new_version).each do |os, arch|
|
||||
SimulateSystem.with(os:, arch:) do
|
||||
# Handle the cask being invalid for specific os/arch combinations
|
||||
old_cask = begin
|
||||
Cask::CaskLoader.load(cask.sourcefile_path)
|
||||
Cask::CaskLoader.load(cask_sourcefile_path)
|
||||
rescue Cask::CaskInvalidError, Cask::CaskUnreadableError
|
||||
raise unless cask.on_system_blocks_exist?
|
||||
end
|
||||
@@ -296,7 +304,7 @@ module Homebrew
|
||||
"version #{bump_version.latest? ? ":latest" : %Q("#{bump_version}")}"]
|
||||
|
||||
# We are replacing our version here so we can get the new hash
|
||||
tmp_contents = Utils::Inreplace.inreplace_pairs(cask.sourcefile_path,
|
||||
tmp_contents = Utils::Inreplace.inreplace_pairs(cask_sourcefile_path,
|
||||
replacement_pairs.uniq.compact,
|
||||
read_only_run: true,
|
||||
silent: true)
|
||||
@@ -359,11 +367,17 @@ module Homebrew
|
||||
|
||||
sig { params(cask: Cask::Cask, new_version: BumpVersionParser).void }
|
||||
def check_pull_requests(cask, new_version:)
|
||||
tap_remote_repo = cask.tap.full_name || cask.tap.remote_repository
|
||||
tap = cask.tap
|
||||
raise "unexpected nil cask.tap" unless tap
|
||||
|
||||
file = cask.sourcefile_path.relative_path_from(cask.tap.path).to_s
|
||||
tap_remote_repo = tap.full_name || tap.remote_repository
|
||||
|
||||
sourcefile_path = cask.sourcefile_path
|
||||
raise "unexpected nil cask.sourcefile_path" unless sourcefile_path
|
||||
|
||||
file = sourcefile_path.relative_path_from(tap.path).to_s
|
||||
quiet = args.quiet?
|
||||
official_tap = cask.tap.official?
|
||||
official_tap = tap.official?
|
||||
GitHub.check_for_duplicate_pull_requests(cask.token, tap_remote_repo,
|
||||
state: "open", file:, quiet:, official_tap:)
|
||||
|
||||
@@ -398,17 +412,23 @@ module Homebrew
|
||||
end
|
||||
return unless failed_audit
|
||||
|
||||
cask.sourcefile_path.atomic_write(old_contents)
|
||||
sourcefile_path = cask.sourcefile_path
|
||||
raise "unexpected nil cask.sourcefile_path" unless sourcefile_path
|
||||
|
||||
sourcefile_path.atomic_write(old_contents)
|
||||
odie "`brew audit` failed!"
|
||||
end
|
||||
|
||||
sig { params(cask: Cask::Cask, old_contents: String).void }
|
||||
def run_cask_style(cask, old_contents)
|
||||
sourcefile_path = cask.sourcefile_path
|
||||
raise "unexpected nil cask.sourcefile_path" unless sourcefile_path
|
||||
|
||||
if args.dry_run?
|
||||
if args.no_style?
|
||||
ohai "Skipping `brew style --fix`"
|
||||
else
|
||||
ohai "brew style --fix #{cask.sourcefile_path.basename}"
|
||||
ohai "brew style --fix #{sourcefile_path.basename}"
|
||||
end
|
||||
return
|
||||
end
|
||||
@@ -416,12 +436,12 @@ module Homebrew
|
||||
if args.no_style?
|
||||
ohai "Skipping `brew style --fix`"
|
||||
else
|
||||
system HOMEBREW_BREW_FILE, "style", "--fix", cask.sourcefile_path
|
||||
system HOMEBREW_BREW_FILE, "style", "--fix", sourcefile_path.to_s
|
||||
failed_style = !$CHILD_STATUS.success?
|
||||
end
|
||||
return unless failed_style
|
||||
|
||||
cask.sourcefile_path.atomic_write(old_contents)
|
||||
sourcefile_path.atomic_write(old_contents)
|
||||
odie "`brew style --fix` failed!"
|
||||
end
|
||||
end
|
||||
|
||||
@@ -144,12 +144,15 @@ module Homebrew
|
||||
if cask.version == version
|
||||
oh1 "Cask #{cask} is up-to-date at #{version}"
|
||||
else
|
||||
sourcefile_path = cask.sourcefile_path
|
||||
raise "unexpected nil cask.sourcefile_path" unless sourcefile_path
|
||||
|
||||
bump_cask_pr_args = [
|
||||
"bump-cask-pr",
|
||||
"--version", version.to_s,
|
||||
"--sha256", ":no_check",
|
||||
"--message", "Automatic update via `brew bump-unversioned-casks`.",
|
||||
cask.sourcefile_path
|
||||
sourcefile_path
|
||||
]
|
||||
|
||||
if args.dry_run?
|
||||
|
||||
@@ -147,7 +147,7 @@ module Homebrew
|
||||
casks = args.formula? ? [] : Cask::Caskroom.casks
|
||||
formulae + casks
|
||||
elsif args.named.present?
|
||||
args.named.to_formulae_and_casks_with_taps
|
||||
T.cast(args.named.to_formulae_and_casks_with_taps, T::Array[T.any(Formula, Cask::Cask)])
|
||||
elsif eval_all
|
||||
formulae = args.cask? ? [] : Formula.all(eval_all:)
|
||||
casks = args.formula? ? [] : Cask::Cask.all(eval_all:)
|
||||
@@ -158,15 +158,15 @@ module Homebrew
|
||||
"`HOMEBREW_EVAL_ALL=1` set!"
|
||||
end
|
||||
|
||||
if args.start_with
|
||||
if (start_with = args.start_with)
|
||||
formulae_and_casks.select! do |formula_or_cask|
|
||||
name = formula_or_cask.respond_to?(:token) ? formula_or_cask.token : formula_or_cask.name
|
||||
name.start_with?(args.start_with)
|
||||
name = formula_or_cask.is_a?(Cask::Cask) ? formula_or_cask.token : formula_or_cask.name
|
||||
name.start_with?(start_with)
|
||||
end
|
||||
end
|
||||
|
||||
formulae_and_casks = formulae_and_casks.sort_by do |formula_or_cask|
|
||||
formula_or_cask.respond_to?(:token) ? formula_or_cask.token : formula_or_cask.name
|
||||
formula_or_cask.is_a?(Cask::Cask) ? formula_or_cask.token : formula_or_cask.name
|
||||
end
|
||||
|
||||
formulae_and_casks -= excluded_autobump
|
||||
@@ -334,6 +334,8 @@ module Homebrew
|
||||
}
|
||||
def retrieve_pull_requests(formula_or_cask, name, version: nil)
|
||||
tap_remote_repo = formula_or_cask.tap&.remote_repository || formula_or_cask.tap&.full_name
|
||||
odie "unexpected nil tap remote repository" if tap_remote_repo.nil?
|
||||
|
||||
pull_requests = begin
|
||||
GitHub.fetch_pull_requests(name, tap_remote_repo, version:)
|
||||
rescue GitHub::API::ValidationFailedError => e
|
||||
@@ -379,9 +381,15 @@ module Homebrew
|
||||
# correct version for the current arch
|
||||
if formula_or_cask.is_a?(Formula)
|
||||
loaded_formula_or_cask = formula_or_cask
|
||||
current_version_value = T.must(loaded_formula_or_cask.stable).version
|
||||
stable = loaded_formula_or_cask.stable
|
||||
raise "unexpected nil stable" unless stable
|
||||
|
||||
current_version_value = stable.version
|
||||
else
|
||||
loaded_formula_or_cask = Cask::CaskLoader.load(formula_or_cask.sourcefile_path)
|
||||
sourcefile_path = formula_or_cask.sourcefile_path
|
||||
raise "unexpected nil sourcefile_path" unless sourcefile_path
|
||||
|
||||
loaded_formula_or_cask = Cask::CaskLoader.load(sourcefile_path)
|
||||
current_version_value = Version.new(loaded_formula_or_cask.version)
|
||||
end
|
||||
|
||||
|
||||
@@ -130,7 +130,7 @@ module Homebrew
|
||||
filtered_macos_runners = RUNNERS.select do |runner, _|
|
||||
runner[:symbol] != :linux &&
|
||||
cask.depends_on.macos.present? &&
|
||||
cask.depends_on.macos.allows?(MacOSVersion.from_symbol(T.must(runner[:symbol]).to_sym))
|
||||
cask.depends_on.macos.allows?(MacOSVersion.from_symbol(runner.fetch(:symbol).to_sym))
|
||||
end
|
||||
|
||||
filtered_runners = if filtered_macos_runners.any?
|
||||
@@ -208,8 +208,10 @@ module Homebrew
|
||||
Float]).returns(T::Hash[Symbol, T.any(Symbol, String)])
|
||||
}
|
||||
def random_runner(available_runners = ARM_MACOS_RUNNERS)
|
||||
T.must(available_runners.max_by { |(_, weight)| rand ** (1.0 / weight) })
|
||||
.first
|
||||
max_runner = available_runners.max_by { |(_, weight)| rand ** (1.0 / weight) }
|
||||
raise "unexpected nil max_runner" unless max_runner
|
||||
|
||||
max_runner.first
|
||||
end
|
||||
|
||||
sig { params(cask: Cask::Cask).returns([T::Array[T::Hash[Symbol, T.any(Symbol, String)]], T::Boolean]) }
|
||||
@@ -217,7 +219,7 @@ module Homebrew
|
||||
filtered_runners = filter_runners(cask)
|
||||
|
||||
filtered_macos_found = filtered_runners.keys.any? do |runner|
|
||||
cask.to_hash_with_variations["variations"].key?(T.must(runner[:symbol]).to_sym)
|
||||
cask.to_hash_with_variations["variations"].key?(runner.fetch(:symbol).to_sym)
|
||||
end
|
||||
|
||||
if filtered_macos_found
|
||||
@@ -244,10 +246,10 @@ module Homebrew
|
||||
changed_files = find_changed_files(tap)
|
||||
|
||||
ruby_files_in_wrong_directory =
|
||||
T.must(changed_files[:modified_ruby_files]) - (
|
||||
T.must(changed_files[:modified_cask_files]) +
|
||||
T.must(changed_files[:modified_command_files]) +
|
||||
T.must(changed_files[:modified_github_actions_files])
|
||||
changed_files[:modified_ruby_files] - (
|
||||
changed_files[:modified_cask_files] +
|
||||
changed_files[:modified_command_files] +
|
||||
changed_files[:modified_github_actions_files]
|
||||
)
|
||||
|
||||
if ruby_files_in_wrong_directory.any?
|
||||
@@ -263,7 +265,7 @@ module Homebrew
|
||||
Cask::CaskLoader.find_cask_in_tap(cask_name, tap).relative_path_from(tap.path)
|
||||
end
|
||||
else
|
||||
T.must(changed_files[:modified_cask_files])
|
||||
changed_files[:modified_cask_files]
|
||||
end
|
||||
|
||||
jobs = cask_files_to_check.count
|
||||
@@ -273,7 +275,7 @@ module Homebrew
|
||||
cask_token = path.basename(".rb")
|
||||
|
||||
audit_args = ["--online", "--signing"]
|
||||
audit_args << "--new" if T.must(changed_files[:added_files]).include?(path) || new_cask
|
||||
audit_args << "--new" if changed_files.fetch(:added_files).include?(path) || new_cask
|
||||
|
||||
audit_exceptions = []
|
||||
|
||||
@@ -326,7 +328,16 @@ module Homebrew
|
||||
end
|
||||
end
|
||||
|
||||
sig { params(tap: Tap).returns(T::Hash[Symbol, T::Array[String]]) }
|
||||
sig {
|
||||
params(tap: Tap).returns({
|
||||
modified_files: T::Array[Pathname],
|
||||
added_files: T::Array[Pathname],
|
||||
modified_ruby_files: T::Array[Pathname],
|
||||
modified_command_files: T::Array[Pathname],
|
||||
modified_github_actions_files: T::Array[Pathname],
|
||||
modified_cask_files: T::Array[Pathname],
|
||||
})
|
||||
}
|
||||
def find_changed_files(tap)
|
||||
commit_range_start = Utils.safe_popen_read("git", "rev-parse", "origin").chomp
|
||||
commit_range_end = Utils.safe_popen_read("git", "rev-parse", "HEAD").chomp
|
||||
|
||||
@@ -60,44 +60,47 @@ module Homebrew
|
||||
puts Homebrew::EnvConfig.livecheck_watchlist if Homebrew::EnvConfig.livecheck_watchlist.present?
|
||||
end
|
||||
|
||||
formulae_and_casks_to_check = Homebrew.with_no_api_env do
|
||||
if args.tap
|
||||
tap = Tap.fetch(args.tap)
|
||||
formulae = args.cask? ? [] : tap.formula_files.map { |path| Formulary.factory(path) }
|
||||
casks = args.formula? ? [] : tap.cask_files.map { |path| Cask::CaskLoader.load(path) }
|
||||
formulae + casks
|
||||
elsif args.installed?
|
||||
formulae = args.cask? ? [] : Formula.installed
|
||||
casks = args.formula? ? [] : Cask::Caskroom.casks
|
||||
formulae + casks
|
||||
elsif args.named.present?
|
||||
args.named.to_formulae_and_casks_with_taps
|
||||
elsif eval_all
|
||||
formulae = args.cask? ? [] : Formula.all(eval_all:)
|
||||
casks = args.formula? ? [] : Cask::Cask.all(eval_all:)
|
||||
formulae + casks
|
||||
elsif File.exist?(watchlist_path)
|
||||
begin
|
||||
# This removes blank lines, comment lines, and trailing comments
|
||||
names = Pathname.new(watchlist_path).read.lines
|
||||
.filter_map do |line|
|
||||
comment_index = line.index("#")
|
||||
next if comment_index&.zero?
|
||||
formulae_and_casks_to_check = T.let(
|
||||
Homebrew.with_no_api_env do
|
||||
if args.tap
|
||||
tap = Tap.fetch(args.tap)
|
||||
formulae = args.cask? ? [] : tap.formula_files.map { |path| Formulary.factory(path) }
|
||||
casks = args.formula? ? [] : tap.cask_files.map { |path| Cask::CaskLoader.load(path) }
|
||||
formulae + casks
|
||||
elsif args.installed?
|
||||
formulae = args.cask? ? [] : Formula.installed
|
||||
casks = args.formula? ? [] : Cask::Caskroom.casks
|
||||
formulae + casks
|
||||
elsif args.named.present?
|
||||
args.named.to_formulae_and_casks_with_taps
|
||||
elsif eval_all
|
||||
formulae = args.cask? ? [] : Formula.all(eval_all:)
|
||||
casks = args.formula? ? [] : Cask::Cask.all(eval_all:)
|
||||
formulae + casks
|
||||
elsif File.exist?(watchlist_path)
|
||||
begin
|
||||
# This removes blank lines, comment lines, and trailing comments
|
||||
names = Pathname.new(watchlist_path).read.lines
|
||||
.filter_map do |line|
|
||||
comment_index = line.index("#")
|
||||
next if comment_index&.zero?
|
||||
|
||||
line = line[0...comment_index] if comment_index
|
||||
line&.strip.presence
|
||||
end
|
||||
line = line[0...comment_index] if comment_index
|
||||
line&.strip.presence
|
||||
end
|
||||
|
||||
named_args = CLI::NamedArgs.new(*names, parent: args)
|
||||
named_args.to_formulae_and_casks(ignore_unavailable: true)
|
||||
rescue Errno::ENOENT => e
|
||||
onoe e
|
||||
named_args = CLI::NamedArgs.new(*names, parent: args)
|
||||
named_args.to_formulae_and_casks(ignore_unavailable: true)
|
||||
rescue Errno::ENOENT => e
|
||||
onoe e
|
||||
end
|
||||
else
|
||||
raise UsageError,
|
||||
"`brew livecheck` with no arguments needs a watchlist file to be present or `--eval-all` passed!"
|
||||
end
|
||||
else
|
||||
raise UsageError,
|
||||
"`brew livecheck` with no arguments needs a watchlist file to be present or `--eval-all` passed!"
|
||||
end
|
||||
end
|
||||
end,
|
||||
T::Array[T.any(Formula, Cask::Cask)],
|
||||
)
|
||||
|
||||
skipped_autobump = T.let(false, T::Boolean)
|
||||
if skip_autobump?
|
||||
@@ -109,7 +112,7 @@ module Homebrew
|
||||
|
||||
autobump_lists[tap] ||= tap.autobump
|
||||
|
||||
name = formula_or_cask.respond_to?(:token) ? formula_or_cask.token : formula_or_cask.name
|
||||
name = formula_or_cask.is_a?(Cask::Cask) ? formula_or_cask.token : formula_or_cask.name
|
||||
next unless autobump_lists[tap].include?(name)
|
||||
|
||||
odebug "Skipping #{name} as it is autobumped in #{tap}."
|
||||
@@ -119,7 +122,7 @@ module Homebrew
|
||||
end
|
||||
|
||||
formulae_and_casks_to_check = formulae_and_casks_to_check.sort_by do |formula_or_cask|
|
||||
formula_or_cask.respond_to?(:token) ? formula_or_cask.token : formula_or_cask.name
|
||||
formula_or_cask.is_a?(Cask::Cask) ? formula_or_cask.token : formula_or_cask.name
|
||||
end
|
||||
|
||||
raise UsageError, "No formulae or casks to check." if formulae_and_casks_to_check.blank? && !skipped_autobump
|
||||
|
||||
@@ -469,7 +469,7 @@ module Homebrew
|
||||
end
|
||||
end
|
||||
|
||||
sig { params(tap: Tap, original_commit: String).returns(T::Array[String]) }
|
||||
sig { params(tap: Tap, original_commit: String).returns(T::Array[T.any(Formula, Cask::Cask)]) }
|
||||
def changed_packages(tap, original_commit)
|
||||
formulae = Utils.popen_read("git", "-C", tap.path, "diff-tree",
|
||||
"-r", "--name-only", "--diff-filter=AM",
|
||||
|
||||
@@ -52,12 +52,13 @@ module Homebrew
|
||||
def self.load_other_tap_strategies(formulae_and_casks_to_check)
|
||||
other_taps = {}
|
||||
formulae_and_casks_to_check.each do |formula_or_cask|
|
||||
next if formula_or_cask.tap.blank?
|
||||
next if formula_or_cask.tap.core_tap?
|
||||
next if formula_or_cask.tap.core_cask_tap?
|
||||
next if other_taps[formula_or_cask.tap.name]
|
||||
tap = formula_or_cask.tap
|
||||
next unless tap
|
||||
next if tap.core_tap?
|
||||
next if tap.core_cask_tap?
|
||||
next if other_taps[tap.name]
|
||||
|
||||
other_taps[formula_or_cask.tap.name] = formula_or_cask.tap
|
||||
other_taps[tap.name] = tap
|
||||
end
|
||||
other_taps = other_taps.sort.to_h
|
||||
|
||||
@@ -97,6 +98,8 @@ module Homebrew
|
||||
Formulary.factory(livecheck_formula)
|
||||
elsif livecheck_cask
|
||||
Cask::CaskLoader.load(livecheck_cask)
|
||||
else
|
||||
raise "livecheck formula or cask not found"
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -283,7 +283,7 @@ module Homebrew
|
||||
verbose:,
|
||||
extract_plist:,
|
||||
)
|
||||
return if skip_info.blank?
|
||||
return if skip_info.empty?
|
||||
|
||||
referenced_name = Livecheck.package_or_resource_name(livecheck_package_or_resource, full_name:)
|
||||
referenced_type = case livecheck_package_or_resource
|
||||
|
||||
@@ -128,7 +128,10 @@ module Homebrew
|
||||
|
||||
# Create a copy of the cask that overrides the artifact URL with the
|
||||
# provided URL and supported `livecheck` block URL options
|
||||
cask_copy = Cask::CaskLoader.load(cask.sourcefile_path)
|
||||
sourcefile_path = cask.sourcefile_path
|
||||
raise "unexpected nil cask.sourcefile_path" unless sourcefile_path
|
||||
|
||||
cask_copy = Cask::CaskLoader.load(sourcefile_path)
|
||||
cask_copy.allow_reassignment = true
|
||||
cask_copy.url(url, **url_kwargs)
|
||||
cask_copy
|
||||
|
||||
@@ -7,10 +7,10 @@ end
|
||||
|
||||
module OnSystem::MacOSAndLinux
|
||||
sig {
|
||||
params(
|
||||
macos: T.nilable(T.any(T::Array[T.any(String, Pathname)], String, Pathname)),
|
||||
linux: T.nilable(T.any(T::Array[T.any(String, Pathname)], String, Pathname)),
|
||||
).returns(T.nilable(T.any(T::Array[T.any(String, Pathname)], String, Pathname)))
|
||||
type_parameters(:U).params(
|
||||
macos: T.all(T.type_parameter(:U), T.nilable(T.any(T::Array[T.any(String, Pathname)], String, Pathname))),
|
||||
linux: T.all(T.type_parameter(:U), T.nilable(T.any(T::Array[T.any(String, Pathname)], String, Pathname))),
|
||||
).returns(T.type_parameter(:U))
|
||||
}
|
||||
def on_system_conditional(macos: nil, linux: nil); end
|
||||
|
||||
|
||||
@@ -185,7 +185,7 @@ module Homebrew
|
||||
end
|
||||
|
||||
results.sort.filter_map do |name|
|
||||
cask = Cask::CaskLoader.load(name)
|
||||
cask = Cask::CaskLoader.load(name.to_s)
|
||||
next if ignore_cask?(cask)
|
||||
|
||||
display_name = if cask.installed?
|
||||
|
||||
Reference in New Issue
Block a user