diff --git a/Library/Homebrew/bundle.rb b/Library/Homebrew/bundle.rb index 8c13ae9e52287..3052bf4dadda7 100644 --- a/Library/Homebrew/bundle.rb +++ b/Library/Homebrew/bundle.rb @@ -101,7 +101,7 @@ def which_formula?(name) which(name).present? end - sig { params(block: T.proc.returns(T.anything)).returns(T.untyped) } + sig { type_parameters(:U).params(block: T.proc.returns(T.type_parameter(:U))).returns(T.type_parameter(:U)) } def exchange_uid_if_needed!(&block) euid = Process.euid uid = Process.uid diff --git a/Library/Homebrew/cache_store.rb b/Library/Homebrew/cache_store.rb index d9c2440f7043b..d71766b984281 100644 --- a/Library/Homebrew/cache_store.rb +++ b/Library/Homebrew/cache_store.rb @@ -22,9 +22,9 @@ class CacheStoreDatabase .returns(T.type_parameter(:U)) } def self.use(type, &_blk) - @db_type_reference_hash ||= T.let({}, T.nilable(T::Hash[T.untyped, T.untyped])) - @db_type_reference_hash[type] ||= {} - type_ref = @db_type_reference_hash[type] + @db_type_reference_hash ||= T.let({}, T.nilable(T::Hash[Symbol, { count: Integer, db: CacheStoreDatabase }])) + @db_type_reference_hash[type] ||= { count: 0, db: CacheStoreDatabase.new(type) } + type_ref = @db_type_reference_hash.fetch(type) type_ref[:count] ||= 0 type_ref[:count] += 1 diff --git a/Library/Homebrew/cask/artifact/app.rb b/Library/Homebrew/cask/artifact/app.rb index d1a86d59d0c00..dbae46760ccfb 100644 --- a/Library/Homebrew/cask/artifact/app.rb +++ b/Library/Homebrew/cask/artifact/app.rb @@ -8,7 +8,7 @@ module Artifact # Artifact corresponding to the `app` stanza. class App < Moved sig { - params( + override.params( adopt: T::Boolean, auto_updates: T.nilable(T::Boolean), force: T::Boolean, diff --git a/Library/Homebrew/cask/artifact/artifact.rb b/Library/Homebrew/cask/artifact/artifact.rb index 7d39512db8c2c..9cd30a479b3a9 100644 --- a/Library/Homebrew/cask/artifact/artifact.rb +++ b/Library/Homebrew/cask/artifact/artifact.rb @@ -12,20 +12,21 @@ def self.english_name "Generic Artifact" end - sig { params(cask: Cask, args: T.untyped).returns(T.attached_class) } - def self.from_args(cask, *args) - source, options = args - + sig { params(cask: Cask, source: T.nilable(String), target_hash: T.anything).returns(Relocated) } + def self.from_args(cask, source = nil, **target_hash) raise CaskInvalidError.new(cask.token, "No source provided for #{english_name}.") if source.blank? - unless options&.key?(:target) + unless target_hash.key?(:target) raise CaskInvalidError.new(cask.token, "#{english_name} '#{source}' requires a target.") end - new(cask, source, **options) + new(cask, source, **target_hash) end - sig { params(target: T.any(String, Pathname)).returns(Pathname) } + # FIXME: This is a pre-existing violation + # rubocop:disable Sorbet/AllowIncompatibleOverride + sig { override(allow_incompatible: true).params(target: T.any(String, Pathname)).returns(Pathname) } + # rubocop:enable Sorbet/AllowIncompatibleOverride def resolve_target(target) super(target, base_dir: nil) end diff --git a/Library/Homebrew/cask/artifact/bashcompletion.rb b/Library/Homebrew/cask/artifact/bashcompletion.rb index 3ffe7c6833f9d..5b5d34825ffc1 100644 --- a/Library/Homebrew/cask/artifact/bashcompletion.rb +++ b/Library/Homebrew/cask/artifact/bashcompletion.rb @@ -7,7 +7,10 @@ module Cask module Artifact # Artifact corresponding to the `bash_completion` stanza. class BashCompletion < ShellCompletion - sig { params(target: T.any(String, Pathname)).returns(Pathname) } + # FIXME: This is a pre-existing violation + # rubocop:disable Sorbet/AllowIncompatibleOverride + sig { override(allow_incompatible: true).params(target: T.any(String, Pathname)).returns(Pathname) } + # rubocop:enable Sorbet/AllowIncompatibleOverride def resolve_target(target) name = if File.extname(target).nil? target diff --git a/Library/Homebrew/cask/artifact/binary.rb b/Library/Homebrew/cask/artifact/binary.rb index 56b47a6826cfd..fbe5a994c4626 100644 --- a/Library/Homebrew/cask/artifact/binary.rb +++ b/Library/Homebrew/cask/artifact/binary.rb @@ -1,4 +1,4 @@ -# typed: true # rubocop:todo Sorbet/StrictSigil +# typed: strict # frozen_string_literal: true require "cask/artifact/symlinked" @@ -7,7 +7,8 @@ module Cask module Artifact # Artifact corresponding to the `binary` stanza. class Binary < Symlinked - def link(command: nil, **options) + sig { override.params(command: T.class_of(SystemCommand), force: T::Boolean, adopt: T::Boolean, _options: T.anything).void } + def link(command:, force: false, adopt: false, **_options) super return if source.executable? diff --git a/Library/Homebrew/cask/artifact/fishcompletion.rb b/Library/Homebrew/cask/artifact/fishcompletion.rb index ef4e13c8cdbbf..8ac2834cf82da 100644 --- a/Library/Homebrew/cask/artifact/fishcompletion.rb +++ b/Library/Homebrew/cask/artifact/fishcompletion.rb @@ -7,7 +7,10 @@ module Cask module Artifact # Artifact corresponding to the `fish_completion` stanza. class FishCompletion < ShellCompletion - sig { params(target: T.any(String, Pathname)).returns(Pathname) } + # FIXME: This is a pre-existing violation + # rubocop:disable Sorbet/AllowIncompatibleOverride + sig { override(allow_incompatible: true).params(target: T.any(String, Pathname)).returns(Pathname) } + # rubocop:enable Sorbet/AllowIncompatibleOverride def resolve_target(target) name = if target.to_s.end_with? ".fish" target diff --git a/Library/Homebrew/cask/artifact/installer.rb b/Library/Homebrew/cask/artifact/installer.rb index 43e042e5d1889..844fac2387e1b 100644 --- a/Library/Homebrew/cask/artifact/installer.rb +++ b/Library/Homebrew/cask/artifact/installer.rb @@ -1,4 +1,4 @@ -# typed: true # rubocop:todo Sorbet/StrictSigil +# typed: strict # frozen_string_literal: true require "cask/artifact/abstract_artifact" @@ -8,12 +8,13 @@ module Cask module Artifact # Artifact corresponding to the `installer` stanza. class Installer < AbstractArtifact - VALID_KEYS = Set.new([ - :manual, - :script, - ]).freeze + VALID_KEYS = T.let( + Set.new([:manual, :script]).freeze, + T::Set[Symbol], + ) - def install_phase(command: nil, **_) + sig { params(command: T.class_of(SystemCommand), _options: T.anything).void } + def install_phase(command:, **_options) if manual_install puts <<~EOS Cask #{cask} only provides a manual installer. To run it and complete the installation: @@ -35,6 +36,7 @@ def install_phase(command: nil, **_) end end + sig { params(cask: Cask, args: DirectivesType).returns(Installer) } def self.from_args(cask, **args) raise CaskInvalidError.new(cask, "'installer' stanza requires an argument.") if args.empty? @@ -59,18 +61,23 @@ def self.from_args(cask, **args) new(cask, **args) end - attr_reader :path, :args + sig { returns(Pathname) } + attr_reader :path + + sig { returns(T::Hash[Symbol, DirectivesType]) } + attr_reader :args sig { returns(T::Boolean) } attr_reader :manual_install + sig { params(cask: Cask, args: DirectivesType).void } def initialize(cask, **args) super - if args.key?(:manual) - @path = Pathname(args[:manual]) - @args = [] - @manual_install = true + if (manual = T.cast(args[:manual], T.nilable(String))) + @path = T.let(Pathname(manual), Pathname) + @args = T.let({}, T::Hash[Symbol, DirectivesType]) + @manual_install = T.let(true, T::Boolean) else path, @args = self.class.read_script_arguments( args[:script], self.class.dsl_key.to_s, { must_succeed: true, sudo: false }, print_stdout: true @@ -85,10 +92,9 @@ def initialize(cask, **args) sig { override.returns(String) } def summarize = path.to_s + sig { returns(T::Hash[Symbol, T.anything]) } def to_h - { path: }.tap do |h| - h[:args] = args unless manual_install - end + manual_install ? { path: } : { path:, args: } end end end diff --git a/Library/Homebrew/cask/artifact/keyboard_layout.rb b/Library/Homebrew/cask/artifact/keyboard_layout.rb index b1e72ccaaff5c..f2a3f6b7bea9a 100644 --- a/Library/Homebrew/cask/artifact/keyboard_layout.rb +++ b/Library/Homebrew/cask/artifact/keyboard_layout.rb @@ -1,4 +1,4 @@ -# typed: true # rubocop:todo Sorbet/StrictSigil +# typed: strict # frozen_string_literal: true require "cask/artifact/moved" @@ -7,19 +7,22 @@ module Cask module Artifact # Artifact corresponding to the `keyboard_layout` stanza. class KeyboardLayout < Moved - def install_phase(**options) + sig { override.params(command: T.class_of(SystemCommand), options: T.anything).void } + def install_phase(command:, **options) super delete_keyboard_layout_cache(**options) end - def uninstall_phase(**options) + sig { override.params(command: T.class_of(SystemCommand), options: T.anything).void } + def uninstall_phase(command:, **options) super delete_keyboard_layout_cache(**options) end private - def delete_keyboard_layout_cache(command: nil, **_) + sig { params(command: T.class_of(SystemCommand), _options: T.anything).void } + def delete_keyboard_layout_cache(command:, **_options) command.run!( "/bin/rm", args: ["-f", "--", "/System/Library/Caches/com.apple.IntlDataCache.le*"], diff --git a/Library/Homebrew/cask/artifact/manpage.rb b/Library/Homebrew/cask/artifact/manpage.rb index 170a0904d3b5b..514fe326531f7 100644 --- a/Library/Homebrew/cask/artifact/manpage.rb +++ b/Library/Homebrew/cask/artifact/manpage.rb @@ -1,4 +1,4 @@ -# typed: true # rubocop:todo Sorbet/StrictSigil +# typed: strict # frozen_string_literal: true require "cask/artifact/symlinked" @@ -7,9 +7,11 @@ module Cask module Artifact # Artifact corresponding to the `manpage` stanza. class Manpage < Symlinked + sig { returns(String) } attr_reader :section - def self.from_args(cask, source) + sig { params(cask: Cask, source: String, _target_hash: T.anything).returns(Manpage) } + def self.from_args(cask, source, **_target_hash) section = source.to_s[/\.([1-8]|n|l)(?:\.gz)?$/, 1] raise CaskInvalidError, "'#{source}' is not a valid man page name" unless section @@ -17,12 +19,17 @@ def self.from_args(cask, source) new(cask, source, section) end + sig { params(cask: Cask, source: String, section: String).void } def initialize(cask, source, section) @section = section super(cask, source) end + # FIXME: This is a pre-existing violation + # rubocop:disable Sorbet/AllowIncompatibleOverride + sig { override(allow_incompatible: true).params(target: T.any(String, Pathname)).returns(Pathname) } + # rubocop:enable Sorbet/AllowIncompatibleOverride def resolve_target(target) config.manpagedir.join("man#{section}", target) end diff --git a/Library/Homebrew/cask/artifact/mdimporter.rb b/Library/Homebrew/cask/artifact/mdimporter.rb index da83a909e4779..42747daa80641 100644 --- a/Library/Homebrew/cask/artifact/mdimporter.rb +++ b/Library/Homebrew/cask/artifact/mdimporter.rb @@ -1,4 +1,4 @@ -# typed: true # rubocop:todo Sorbet/StrictSigil +# typed: strict # frozen_string_literal: true require "cask/artifact/moved" @@ -12,14 +12,36 @@ def self.english_name "Spotlight metadata importer" end - def install_phase(**options) + sig { + override.params( + adopt: T::Boolean, + auto_updates: T.nilable(T::Boolean), + force: T::Boolean, + verbose: T::Boolean, + predecessor: T.nilable(Cask), + successor: T.nilable(Cask), + reinstall: T::Boolean, + command: T.class_of(SystemCommand), + ).void + } + def install_phase( + adopt: false, + auto_updates: false, + force: false, + verbose: false, + predecessor: nil, + successor: nil, + reinstall: false, + command: SystemCommand + ) super - reload_spotlight(**options) + reload_spotlight(command:) end private - def reload_spotlight(command: nil, **_) + sig { params(command: T.class_of(SystemCommand)).void } + def reload_spotlight(command:) command.run!("/usr/bin/mdimport", args: ["-r", target]) end end diff --git a/Library/Homebrew/cask/artifact/moved.rb b/Library/Homebrew/cask/artifact/moved.rb index ba0c2a8ee3a1d..1d24ec8f239be 100644 --- a/Library/Homebrew/cask/artifact/moved.rb +++ b/Library/Homebrew/cask/artifact/moved.rb @@ -1,4 +1,4 @@ -# typed: true # rubocop:todo Sorbet/StrictSigil +# typed: strict # frozen_string_literal: true require "cask/artifact/relocated" @@ -13,14 +13,37 @@ def self.english_description "#{english_name}s" end - def install_phase(**options) - move(**options) + sig { + overridable.params( + adopt: T::Boolean, + auto_updates: T.nilable(T::Boolean), + force: T::Boolean, + verbose: T::Boolean, + predecessor: T.nilable(Cask), + successor: T.nilable(Cask), + reinstall: T::Boolean, + command: T.class_of(SystemCommand), + ).void + } + def install_phase( + adopt: false, + auto_updates: false, + force: false, + verbose: false, + predecessor: nil, + successor: nil, + reinstall: false, + command: SystemCommand + ) + move(adopt:, auto_updates:, force:, verbose:, predecessor:, successor:, reinstall:, command:) end - def uninstall_phase(**options) - move_back(**options) + sig { overridable.params(command: T.class_of(SystemCommand), options: T.anything).void } + def uninstall_phase(command:, **options) + move_back(command:, **options) end + sig { overridable.returns(String) } def summarize_installed if target.exist? "#{printable_target} (#{target.abv})" @@ -31,8 +54,13 @@ def summarize_installed private - def move(adopt: false, auto_updates: false, force: false, verbose: false, predecessor: nil, reinstall: false, - command: nil, **options) + sig { + params(command: T.class_of(SystemCommand), adopt: T::Boolean, auto_updates: T.nilable(T::Boolean), + force: T::Boolean, verbose: T::Boolean, predecessor: T.nilable(Cask), reinstall: T::Boolean, + options: T.anything).void + } + def move(command:, adopt: false, auto_updates: false, force: false, verbose: false, predecessor: nil, + reinstall: false, **options) unless source.exist? raise CaskError, "It seems the #{self.class.english_name} source '#{source}' is not there." end @@ -128,12 +156,14 @@ def move(adopt: false, auto_updates: false, force: false, verbose: false, predec end # Performs any actions necessary after the source has been moved to the target location. + sig { params(command: T.class_of(SystemCommand)).void } def post_move(command) FileUtils.ln_sf target, source add_altname_metadata(target, source.basename, command:) end + sig { params(cask: T.nilable(Cask)).returns(T::Boolean) } def matching_artifact?(cask) return false unless cask @@ -142,7 +172,8 @@ def matching_artifact?(cask) end end - def move_back(skip: false, force: false, adopt: false, command: nil, **options) + sig { params(command: T.class_of(SystemCommand), skip: T::Boolean, force: T::Boolean, adopt: T::Boolean, options: T.anything).void } + def move_back(command:, skip: false, force: false, adopt: false, **options) FileUtils.rm source if source.symlink? && source.dirname.join(source.readlink) == target if Utils.path_occupied?(source) @@ -178,7 +209,8 @@ def move_back(skip: false, force: false, adopt: false, command: nil, **options) delete(target, force:, command:, **options) end - def delete(target, force: false, successor: nil, command: nil, **_) + sig { params(target: Pathname, command: T.class_of(SystemCommand), force: T::Boolean, successor: T.nilable(Cask), _options: T.anything).void } + def delete(target, command:, force: false, successor: nil, **_options) ohai "Removing #{self.class.english_name} '#{target}'" raise CaskError, "Cannot remove undeletable #{self.class.english_name}." if undeletable?(target) diff --git a/Library/Homebrew/cask/artifact/pkg.rb b/Library/Homebrew/cask/artifact/pkg.rb index d3bef370e81ae..7edbf6fe9fee7 100644 --- a/Library/Homebrew/cask/artifact/pkg.rb +++ b/Library/Homebrew/cask/artifact/pkg.rb @@ -1,4 +1,4 @@ -# typed: true # rubocop:todo Sorbet/StrictSigil +# typed: strict # frozen_string_literal: true require "plist" @@ -11,30 +11,48 @@ module Cask module Artifact # Artifact corresponding to the `pkg` stanza. class Pkg < AbstractArtifact - attr_reader :path, :stanza_options + Choices = T.type_alias { T.nilable(T.any(T::Array[T::Hash[T.anything, T.anything]], T::Hash[Symbol, T.anything])) } + private_constant :Choices + StanzaOptions = T.type_alias { { allow_untrusted: T.nilable(T::Boolean), choices: Choices } } + private_constant :StanzaOptions + sig { returns(Pathname) } + attr_reader :path + + sig { returns(T::Hash[Symbol, T.anything]) } + attr_reader :stanza_options + + # sig { params(cask: Cask, path: String, allow_untrusted: T.nilable(T::Boolean), choices: Choices).returns(Pkg) } + # def self.from_args(cask, path, allow_untrusted: nil, choices: nil) + sig { params(cask: Cask, path: String, stanza_options: T.anything).returns(Pkg) } def self.from_args(cask, path, **stanza_options) stanza_options.assert_valid_keys(:allow_untrusted, :choices) new(cask, path, **stanza_options) end + # sig { params(cask: Cask, path: String, allow_untrusted: T.nilable(T::Boolean), choices: Choices).void } + # def initialize(cask, path, allow_untrusted: nil, choices: nil) + sig { params(cask: Cask, path: String, stanza_options: T.anything).void } def initialize(cask, path, **stanza_options) super - @path = cask.staged_path.join(path) - @stanza_options = stanza_options + @path = T.let(cask.staged_path.join(path), Pathname) + @stanza_options = T.let(stanza_options, T::Hash[Symbol, T.anything]) end + sig { override.returns(String) } def summarize path.relative_path_from(cask.staged_path).to_s end - def install_phase(**options) - run_installer(**options) + sig { params(command: T.class_of(SystemCommand), options: T.anything).void } + def install_phase(command:, **options) + run_installer(command:, **options) end private - def run_installer(command: nil, verbose: false, **_options) + sig { params(command: T.class_of(SystemCommand), verbose: T::Boolean, _options: T.anything).void } + def run_installer(command:, verbose: false, **_options) ohai "Running installer for #{cask} with `sudo` (which may request your password)..." unless path.exist? pkg = path.relative_path_from(cask.staged_path) @@ -71,9 +89,10 @@ def run_installer(command: nil, verbose: false, **_options) end end - def with_choices_file + sig { params(_block: T.proc.params(file_path: T.nilable(String)).void).void } + def with_choices_file(&_block) choices = stanza_options.fetch(:choices, {}) - return yield nil if choices.empty? + return yield nil if T.unsafe(choices).empty? Tempfile.open(["choices", ".xml"]) do |file| file.write Plist::Emit.dump(choices) diff --git a/Library/Homebrew/cask/artifact/qlplugin.rb b/Library/Homebrew/cask/artifact/qlplugin.rb index 4e0c2430f8079..86bf097c7cdad 100644 --- a/Library/Homebrew/cask/artifact/qlplugin.rb +++ b/Library/Homebrew/cask/artifact/qlplugin.rb @@ -1,4 +1,4 @@ -# typed: true # rubocop:todo Sorbet/StrictSigil +# typed: strict # frozen_string_literal: true require "cask/artifact/moved" @@ -12,19 +12,22 @@ def self.english_name "Quick Look Plugin" end - def install_phase(**options) + sig { override.params(command: T.class_of(SystemCommand), options: T.anything).void } + def install_phase(command:, **options) super - reload_quicklook(**options) + reload_quicklook(command:, **options) end - def uninstall_phase(**options) + sig { override.params(command: T.class_of(SystemCommand), options: T.anything).void } + def uninstall_phase(command:, **options) super - reload_quicklook(**options) + reload_quicklook(command:, **options) end private - def reload_quicklook(command: nil, **_) + sig { params(command: T.class_of(SystemCommand), _options: T.anything).void } + def reload_quicklook(command:, **_options) command.run!("/usr/bin/qlmanage", args: ["-r"]) end end diff --git a/Library/Homebrew/cask/artifact/relocated.rb b/Library/Homebrew/cask/artifact/relocated.rb index 24373ccf749eb..cbee4a9ee076b 100644 --- a/Library/Homebrew/cask/artifact/relocated.rb +++ b/Library/Homebrew/cask/artifact/relocated.rb @@ -1,4 +1,4 @@ -# typed: true # rubocop:todo Sorbet/StrictSigil +# typed: strict # frozen_string_literal: true require "cask/artifact/abstract_artifact" @@ -8,20 +8,16 @@ module Cask module Artifact # Superclass for all artifacts which have a source and a target location. class Relocated < AbstractArtifact - def self.from_args(cask, *args) - source_string, target_hash = args - - if target_hash - raise CaskInvalidError, cask unless target_hash.respond_to?(:keys) - - target_hash.assert_valid_keys(:target) - end - - target_hash ||= {} + sig { + params(cask: Cask, source_string: T.nilable(String), target_hash: DirectivesType).returns(Relocated) + } + def self.from_args(cask, source_string = nil, **target_hash) + target_hash.assert_valid_keys(:target) new(cask, source_string, **target_hash) end + sig { overridable.params(target: T.any(String, Pathname), base_dir: T.nilable(Pathname)).returns(Pathname) } def resolve_target(target, base_dir: config.public_send(self.class.dirmethod)) target = Pathname(target) @@ -33,20 +29,18 @@ def resolve_target(target, base_dir: config.public_send(self.class.dirmethod)) target end - sig { - params(cask: Cask, source: T.nilable(T.any(String, Pathname)), target_hash: T.any(String, Pathname)) - .void - } + sig { params(cask: Cask, source: T.nilable(T.any(String, Pathname)), target_hash: DirectivesType).void } def initialize(cask, source, **target_hash) super target = target_hash[:target] - @source = nil - @source_string = source.to_s - @target = nil - @target_string = target.to_s + @source = T.let(nil, T.nilable(Pathname)) + @source_string = T.let(source.to_s, String) + @target = T.let(nil, T.nilable(Pathname)) + @target_string = T.let(target.to_s, String) end + sig { returns(Pathname) } def source @source ||= begin base_path = cask.staged_path @@ -55,10 +49,12 @@ def source end end + sig { returns(Pathname) } def target @target ||= resolve_target(@target_string.presence || source.basename) end + sig { returns(T::Array[T.anything]) } def to_a [@source_string].tap do |ary| ary << { target: @target_string } unless @target_string.empty? @@ -103,6 +99,7 @@ def add_altname_metadata(file, altname, command:) sudo: !file.writable?) end + sig { returns(String) } def printable_target target.to_s.sub(/^#{Dir.home}(#{File::SEPARATOR}|$)/, "~/") end diff --git a/Library/Homebrew/cask/artifact/shellcompletion.rb b/Library/Homebrew/cask/artifact/shellcompletion.rb index 54bac0ec7ddcc..f908f82e97925 100644 --- a/Library/Homebrew/cask/artifact/shellcompletion.rb +++ b/Library/Homebrew/cask/artifact/shellcompletion.rb @@ -6,8 +6,13 @@ module Cask module Artifact class ShellCompletion < Symlinked - sig { params(_: T.any(String, Pathname)).returns(Pathname) } - def resolve_target(_) + sig { + override + .overridable + .params(_target: T.any(String, Pathname), base_dir: T.nilable(Pathname)) + .returns(T.noreturn) + } + def resolve_target(_target, base_dir: nil) raise CaskInvalidError, "Shell completion without shell info" end end diff --git a/Library/Homebrew/cask/artifact/stage_only.rb b/Library/Homebrew/cask/artifact/stage_only.rb index 5a1cb6f9d0e70..9362abc6f67a0 100644 --- a/Library/Homebrew/cask/artifact/stage_only.rb +++ b/Library/Homebrew/cask/artifact/stage_only.rb @@ -1,4 +1,4 @@ -# typed: true # rubocop:todo Sorbet/StrictSigil +# typed: strict # frozen_string_literal: true require "cask/artifact/abstract_artifact" @@ -7,8 +7,9 @@ module Cask module Artifact # Artifact corresponding to the `stage_only` stanza. class StageOnly < AbstractArtifact - def self.from_args(cask, *args, **kwargs) - if (args != [true] && args != ["true"]) || kwargs.present? + sig { params(cask: Cask, arg: T.any(String, TrueClass)).returns(StageOnly) } + def self.from_args(cask, arg) + unless [true, "true"].include?(arg) raise CaskInvalidError.new(cask.token, "'stage_only' takes only a single argument: true") end diff --git a/Library/Homebrew/cask/artifact/symlinked.rb b/Library/Homebrew/cask/artifact/symlinked.rb index ba03e8d99d1d2..01f370a3321c7 100644 --- a/Library/Homebrew/cask/artifact/symlinked.rb +++ b/Library/Homebrew/cask/artifact/symlinked.rb @@ -1,4 +1,4 @@ -# typed: true # rubocop:todo Sorbet/StrictSigil +# typed: strict # frozen_string_literal: true require "cask/artifact/relocated" @@ -17,14 +17,17 @@ def self.english_description "#{english_name} #{link_type_english_name}s" end + sig { params(options: T.anything).void } def install_phase(**options) link(**options) end + sig { params(options: T.anything).void } def uninstall_phase(**options) unlink(**options) end + sig { returns(String) } def summarize_installed if target.symlink? && target.exist? && target.readlink.exist? "#{printable_target} -> #{target.readlink} (#{target.readlink.abv})" @@ -41,7 +44,8 @@ def summarize_installed private - def link(force: false, adopt: false, command: nil, **_options) + sig { overridable.params(command: T.class_of(SystemCommand), force: T::Boolean, adopt: T::Boolean, _options: T.anything).void } + def link(command:, force: false, adopt: false, **_options) unless source.exist? raise CaskError, "It seems the #{self.class.link_type_english_name.downcase} " \ @@ -68,7 +72,8 @@ def link(force: false, adopt: false, command: nil, **_options) create_filesystem_link(command) end - def unlink(command: nil, **) + sig { params(command: T.class_of(SystemCommand), _options: T.anything).void } + def unlink(command:, **_options) return unless target.symlink? ohai "Unlinking #{self.class.english_name} '#{target}'" diff --git a/Library/Homebrew/cask/artifact/uninstall.rb b/Library/Homebrew/cask/artifact/uninstall.rb index 8dbcf1afc8e18..dbd2fd6d77e66 100644 --- a/Library/Homebrew/cask/artifact/uninstall.rb +++ b/Library/Homebrew/cask/artifact/uninstall.rb @@ -1,4 +1,4 @@ -# typed: true # rubocop:todo Sorbet/StrictSigil +# typed: strict # frozen_string_literal: true require "cask/artifact/abstract_uninstall" @@ -9,10 +9,8 @@ module Artifact class Uninstall < AbstractUninstall UPGRADE_REINSTALL_SKIP_DIRECTIVES = [:quit, :signal].freeze - def uninstall_phase(**options) - upgrade = options.fetch(:upgrade, false) - reinstall = options.fetch(:reinstall, false) - + sig { params(upgrade: T::Boolean, reinstall: T::Boolean, options: T.anything).void } + def uninstall_phase(upgrade: false, reinstall: false, **options) raw_on_upgrade = directives[:on_upgrade] on_upgrade_syms = case raw_on_upgrade @@ -42,6 +40,7 @@ def uninstall_phase(**options) end end + sig { params(options: T.anything).void } def post_uninstall_phase(**options) dispatch_uninstall_directive(:rmdir, **options) end diff --git a/Library/Homebrew/cask/artifact/zshcompletion.rb b/Library/Homebrew/cask/artifact/zshcompletion.rb index 971de918570c9..233883f605bcf 100644 --- a/Library/Homebrew/cask/artifact/zshcompletion.rb +++ b/Library/Homebrew/cask/artifact/zshcompletion.rb @@ -7,7 +7,10 @@ module Cask module Artifact # Artifact corresponding to the `zsh_completion` stanza. class ZshCompletion < ShellCompletion - sig { params(target: T.any(String, Pathname)).returns(Pathname) } + # FIXME: This is a pre-existing violation + # rubocop:disable Sorbet/AllowIncompatibleOverride + sig { override(allow_incompatible: true).params(target: T.any(String, Pathname)).returns(Pathname) } + # rubocop:enable Sorbet/AllowIncompatibleOverride def resolve_target(target) name = if target.to_s.start_with? "_" target diff --git a/Library/Homebrew/cask/artifact_set.rb b/Library/Homebrew/cask/artifact_set.rb index 8f1da460b4594..7c68538275b28 100644 --- a/Library/Homebrew/cask/artifact_set.rb +++ b/Library/Homebrew/cask/artifact_set.rb @@ -8,7 +8,7 @@ class ArtifactSet < ::Set Elem = type_member(:out) { { fixed: Artifact::AbstractArtifact } } - sig { params(block: T.nilable(T.proc.params(arg0: Elem).returns(T.untyped))).void } + sig { override.params(block: T.nilable(T.proc.params(arg0: Elem).returns(BasicObject))).returns(T.untyped) } def each(&block) return enum_for(T.must(__method__)) { size } unless block diff --git a/Library/Homebrew/cask/pkg.rb b/Library/Homebrew/cask/pkg.rb index 2f1241fda90a0..fb74e4a70dd63 100644 --- a/Library/Homebrew/cask/pkg.rb +++ b/Library/Homebrew/cask/pkg.rb @@ -104,9 +104,12 @@ def root @root ||= T.let(Pathname.new(info.fetch("volume")).join(info.fetch("install-location")), T.nilable(Pathname)) end - sig { returns(T.untyped) } + sig { returns(T::Hash[String, T.untyped]) } def info - @info ||= T.let(@command.run!("/usr/sbin/pkgutil", args: ["--pkg-info-plist", package_id]).plist, T.untyped) + @info ||= T.let( + @command.run!("/usr/sbin/pkgutil", args: ["--pkg-info-plist", package_id]).plist, + T.nilable(T::Hash[String, T.untyped]), + ) end private diff --git a/Library/Homebrew/dev-cmd/extract.rb b/Library/Homebrew/dev-cmd/extract.rb index f9f1d8b79adeb..73bd81b9b2ace 100644 --- a/Library/Homebrew/dev-cmd/extract.rb +++ b/Library/Homebrew/dev-cmd/extract.rb @@ -172,7 +172,7 @@ def formula_at_revision(repo, name, file, rev) with_monkey_patch { Formulary.from_contents(name, file, contents, ignore_errors: true) } end - sig { params(_block: T.proc.void).returns(T.untyped) } + sig { type_parameters(:U).params(_block: T.proc.returns(T.type_parameter(:U))).returns(T.type_parameter(:U)) } def with_monkey_patch(&_block) # Since `method_defined?` is not a supported type guard, the use of `alias_method` below is not typesafe: BottleSpecification.class_eval do