This commit is contained in:
metacollin 2020-11-19 20:18:12 -07:00
commit 87ff28431f
432 changed files with 7375 additions and 3507 deletions

View File

@ -6,7 +6,6 @@ on:
env:
HOMEBREW_DEVELOPER: 1
HOMEBREW_NO_AUTO_UPDATE: 1
HOMEBREW_GITHUB_ACTIONS_BIG_SUR_TESTING: 1 # TODO: remove when Big Sur is released.
jobs:
tests:
if: github.repository == 'Homebrew/brew'

2
.gitignore vendored
View File

@ -24,6 +24,7 @@
# Ignore Bundler files
**/.bundle/bin
**/.bundle/cache
**/vendor/bundle/ruby/*/bundler.lock
**/vendor/bundle/ruby/*/bin
**/vendor/bundle/ruby/*/build_info/
**/vendor/bundle/ruby/*/cache
@ -127,6 +128,7 @@
**/vendor/bundle/ruby/*/gems/rspec-mocks-*/
**/vendor/bundle/ruby/*/gems/rspec-retry-*/
**/vendor/bundle/ruby/*/gems/rspec-support-*/
**/vendor/bundle/ruby/*/gems/rspec-sorbet-*/
**/vendor/bundle/ruby/*/gems/rspec-wait-*/
**/vendor/bundle/ruby/*/gems/rubocop-1*/
**/vendor/bundle/ruby/*/gems/rubocop-ast-*/

View File

@ -18,6 +18,7 @@ AllCops:
- 'Homebrew/sorbet/rbi/gems/**/*.rbi'
- 'Homebrew/sorbet/rbi/hidden-definitions/**/*.rbi'
- 'Homebrew/sorbet/rbi/todo.rbi'
- 'Homebrew/sorbet/rbi/upstream.rbi'
- 'Homebrew/bin/*'
- 'Homebrew/vendor/**/*'
@ -69,7 +70,7 @@ Naming/HeredocDelimiterNaming:
Naming/MethodName:
IgnoredPatterns:
- '\AHEAD\?\Z'
- '\A(fetch_)?HEAD\?\Z'
# Allow dashes in filenames.
Naming/FileName:
@ -108,7 +109,9 @@ Style/HashTransformValues:
# Allow for license expressions
Style/HashAsLastArrayItem:
Exclude:
- 'Taps/*/*/{Formula/,}*.rb'
- 'Taps/*/*/*.rb'
- '/**/Formula/*.rb'
- '**/Formula/*.rb'
# Enabled now LineLength is lowish.
Style/IfUnlessModifier:
@ -118,6 +121,8 @@ Style/IfUnlessModifier:
Style/NumericLiterals:
MinDigits: 7
Strict: true
Exclude:
- '**/Brewfile'
# Zero-prefixed octal literals are widely used and understood.
Style/NumericLiteralPrefix:
@ -161,11 +166,17 @@ Performance/CaseWhenSplat:
Performance/Caller:
Enabled: false
# Makes code less readable for minor performance increases.
Performance/MethodObjectAsBlock:
Enabled: false
# Don't allow cops to be disabled in casks and formulae.
Style/DisableCopsWithinSourceCodeDirective:
Enabled: true
Include:
- 'Taps/*/*/{Formula/,Casks/,}*.rb'
- 'Taps/*/*/*.rb'
- '/**/{Formula,Casks}/*.rb'
- '**/{Formula,Casks}/*.rb'
# make our hashes consistent
Layout/HashAlignment:
@ -175,7 +186,9 @@ Layout/HashAlignment:
# `system` is a special case and aligns on second argument, so allow this for formulae.
Layout/ArgumentAlignment:
Exclude:
- 'Taps/*/*/{Formula/,}*.rb'
- 'Taps/*/*/*.rb'
- '/**/Formula/*.rb'
- '**/Formula/*.rb'
# this is a bit less "floaty"
Layout/CaseIndentation:
@ -197,6 +210,12 @@ Layout/RescueEnsureAlignment:
Lint/AmbiguousBlockAssociation:
Enabled: false
Lint/DuplicateBranch:
Exclude:
- 'Taps/*/*/*.rb'
- '/**/{Formula,Casks}/*.rb'
- '**/{Formula,Casks}/*.rb'
# needed for lazy_object magic
Naming/MemoizedInstanceVariableName:
Exclude:
@ -206,7 +225,9 @@ Naming/MemoizedInstanceVariableName:
# TODO: fix these as `ruby -w` complains about them.
Lint/AmbiguousRegexpLiteral:
Exclude:
- 'Taps/*/*/{Formula/,}*.rb'
- 'Taps/*/*/*.rb'
- '/**/Formula/*.rb'
- '**/Formula/*.rb'
# useful for metaprogramming in RSpec
Lint/ConstantDefinitionInBlock:
@ -216,30 +237,46 @@ Lint/ConstantDefinitionInBlock:
# so many of these in formulae and can't be autocorrected
Lint/ParenthesesAsGroupedExpression:
Exclude:
- 'Taps/*/*/{Formula/,}*.rb'
- 'Taps/*/*/*.rb'
- '/**/Formula/*.rb'
- '**/Formula/*.rb'
# Most metrics don't make sense to apply for casks/formulae/taps.
Metrics/AbcSize:
Exclude:
- 'Taps/**/*'
- '/**/{Formula,Casks}/*.rb'
- '**/{Formula,Casks}/*.rb'
Metrics/BlockLength:
Exclude:
- 'Taps/**/*'
- '/**/{Formula,Casks}/*.rb'
- '**/{Formula,Casks}/*.rb'
Metrics/ClassLength:
Exclude:
- 'Taps/**/*'
- '/**/{Formula,Casks}/*.rb'
- '**/{Formula,Casks}/*.rb'
Metrics/CyclomaticComplexity:
Exclude:
- 'Taps/**/*'
- '/**/{Formula,Casks}/*.rb'
- '**/{Formula,Casks}/*.rb'
Metrics/MethodLength:
Exclude:
- 'Taps/**/*'
- '/**/{Formula,Casks}/*.rb'
- '**/{Formula,Casks}/*.rb'
Metrics/ModuleLength:
Exclude:
- 'Taps/**/*'
- '/**/{Formula,Casks}/*.rb'
- '**/{Formula,Casks}/*.rb'
Metrics/PerceivedComplexity:
Exclude:
- 'Taps/**/*'
- '/**/{Formula,Casks}/*.rb'
- '**/{Formula,Casks}/*.rb'
# allow those that are standard
# TODO: try to remove some of these
@ -280,7 +317,9 @@ Layout/LineLength:
Sorbet/FalseSigil:
Exclude:
- 'Taps/**/*.rb'
- 'Taps/**/*'
- '/**/{Formula,Casks}/*.rb'
- '**/{Formula,Casks}/*.rb'
- 'Homebrew/test/**/Casks/**/*.rb'
Sorbet/StrictSigil:
@ -297,15 +336,9 @@ Sorbet/ConstantsFromStrings:
Style/AccessModifierDeclarations:
Enabled: false
# don't group nicely documented or private attr_readers
# Conflicts with type signatures on `attr_*`s.
Style/AccessorGrouping:
Exclude:
- 'Homebrew/formula.rb'
- 'Homebrew/formulary.rb'
- 'Homebrew/migrator.rb'
- 'Homebrew/resource.rb'
- 'Homebrew/system_command.rb'
- 'Homebrew/tap.rb'
Enabled: false
# make rspec formatting more flexible
Style/BlockDelimiters:
@ -322,6 +355,8 @@ Style/ClassVars:
Style/Documentation:
Exclude:
- 'Taps/**/*'
- '/**/{Formula,Casks}/*.rb'
- '**/{Formula,Casks}/*.rb'
- '**/*.rbi'
Style/DocumentationMethod:
@ -332,9 +367,12 @@ Style/DocumentationMethod:
Style/FrozenStringLiteralComment:
EnforcedStyle: always
Exclude:
- 'Taps/*/*/{Formula,Casks,}/*.rb'
- 'Taps/*/*/*.rb'
- '/**/{Formula,Casks}/*.rb'
- '**/{Formula,Casks}/*.rb'
- 'Homebrew/test/**/Casks/**/*.rb'
- '**/*.rbi'
- '**/Brewfile'
# TODO: remove this when possible.
Style/GlobalVars:
@ -344,7 +382,9 @@ Style/GlobalVars:
# potential for errors in formulae too high with this
Style/GuardClause:
Exclude:
- 'Taps/*/*/{Formula/,Casks/,}*.rb'
- 'Taps/*/*/*.rb'
- '/**/{Formula,Casks}/*.rb'
- '**/{Formula,Casks}/*.rb'
# avoid hash rockets where possible
Style/HashSyntax:
@ -353,7 +393,9 @@ Style/HashSyntax:
# so many of these in formulae and can't be autocorrected
Style/StringConcatenation:
Exclude:
- 'Taps/*/*/{Formula/,Casks/,}*.rb'
- 'Taps/*/*/*.rb'
- '/**/{Formula,Casks}/*.rb'
- '**/{Formula,Casks}/*.rb'
# ruby style guide favorite
Style/StringLiterals:

View File

@ -10,6 +10,7 @@ gem "ronn", require: false
gem "rspec", require: false
gem "rspec-its", require: false
gem "rspec-retry", require: false
gem "rspec-sorbet", require: false
gem "rspec-wait", require: false
gem "rubocop", require: false
gem "simplecov", require: false

View File

@ -44,7 +44,7 @@ GEM
method_source (1.0.0)
mime-types (3.3.1)
mime-types-data (~> 3.2015)
mime-types-data (3.2020.0512)
mime-types-data (3.2020.1104)
mini_portile2 (2.4.0)
minitest (5.14.2)
mustache (1.1.1)
@ -95,39 +95,42 @@ GEM
rspec-support (~> 3.10.0)
rspec-retry (0.6.2)
rspec-core (> 3.3)
rspec-sorbet (1.7.0)
sorbet
sorbet-runtime
rspec-support (3.10.0)
rspec-wait (0.0.9)
rspec (>= 3, < 4)
rubocop (1.2.0)
rubocop (1.3.1)
parallel (~> 1.10)
parser (>= 2.7.1.5)
rainbow (>= 2.2.2, < 4.0)
regexp_parser (>= 1.8)
rexml
rubocop-ast (>= 1.0.1)
rubocop-ast (>= 1.1.1)
ruby-progressbar (~> 1.7)
unicode-display_width (>= 1.4.0, < 2.0)
rubocop-ast (1.1.1)
parser (>= 2.7.1.5)
rubocop-performance (1.8.1)
rubocop (>= 0.87.0)
rubocop-performance (1.9.0)
rubocop (>= 0.90.0, < 2.0)
rubocop-ast (>= 0.4.0)
rubocop-rspec (2.0.0)
rubocop (~> 1.0)
rubocop-ast (>= 1.1.0)
rubocop-sorbet (0.5.1)
rubocop
ruby-macho (2.2.0)
ruby-macho (2.5.0)
ruby-progressbar (1.10.1)
simplecov (0.19.1)
docile (~> 1.1)
simplecov-html (~> 0.11)
simplecov-html (0.12.3)
sorbet (0.5.6042)
sorbet-static (= 0.5.6042)
sorbet-runtime (0.5.6040)
sorbet (0.5.6076)
sorbet-static (= 0.5.6076)
sorbet-runtime (0.5.6076)
sorbet-runtime-stub (0.2.0)
sorbet-static (0.5.6042-universal-darwin-14)
sorbet-static (0.5.6076-universal-darwin-14)
spoom (1.0.4)
colorize
sorbet (~> 0.5.5)
@ -142,14 +145,14 @@ GEM
thor (>= 0.19.2)
thor (1.0.1)
thread_safe (0.3.6)
tzinfo (1.2.7)
tzinfo (1.2.8)
thread_safe (~> 0.1)
unf (0.1.4)
unf_ext
unf_ext (0.0.7.7)
unicode-display_width (1.7.0)
webrobots (0.1.2)
zeitwerk (2.4.0)
zeitwerk (2.4.1)
PLATFORMS
ruby
@ -167,6 +170,7 @@ DEPENDENCIES
rspec
rspec-its
rspec-retry
rspec-sorbet
rspec-wait
rubocop
rubocop-performance

View File

@ -67,13 +67,9 @@ class PATH
sig { params(other: T.untyped).returns(T::Boolean) }
def ==(other)
if other.respond_to?(:to_ary) && to_ary == other.to_ary
true
elsif other.respond_to?(:to_str) && to_str == other.to_str
true
else
(other.respond_to?(:to_ary) && to_ary == other.to_ary) ||
(other.respond_to?(:to_str) && to_str == other.to_str) ||
false
end
end
sig { returns(T::Boolean) }

View File

@ -8,6 +8,8 @@ require "json"
#
# @api private
class Bintray
extend T::Sig
include Context
API_URL = "https://api.bintray.com"
@ -15,6 +17,7 @@ class Bintray
class Error < RuntimeError
end
sig { returns(String) }
def inspect
"#<Bintray: org=#{@bintray_org}>"
end

View File

@ -164,7 +164,7 @@ rescue BuildError => e
if e.formula.head? || e.formula.deprecated? || e.formula.disabled?
$stderr.puts <<~EOS
Please create pull requests instead of asking for help on Homebrew's GitHub,
Discourse, Twitter or any other official channels.
Twitter or any other official channels.
EOS
end

View File

@ -53,11 +53,7 @@ class Build
def expand_reqs
formula.recursive_requirements do |dependent, req|
build = effective_build_options_for(dependent)
if req.prune_from_option?(build)
Requirement.prune
elsif req.prune_if_build_and_not_dependent?(dependent, formula)
Requirement.prune
elsif req.test?
if req.prune_from_option?(build) || req.prune_if_build_and_not_dependent?(dependent, formula) || req.test?
Requirement.prune
end
end
@ -66,14 +62,12 @@ class Build
def expand_deps
formula.recursive_dependencies do |dependent, dep|
build = effective_build_options_for(dependent)
if dep.prune_from_option?(build)
Dependency.prune
elsif dep.prune_if_build_and_not_dependent?(dependent, formula)
if dep.prune_from_option?(build) ||
dep.prune_if_build_and_not_dependent?(dependent, formula) ||
(dep.test? && !dep.build?)
Dependency.prune
elsif dep.build?
Dependency.keep_but_prune_recursive_deps
elsif dep.test?
Dependency.prune
end
end
end

View File

@ -1,34 +1,44 @@
# typed: false
# typed: true
# frozen_string_literal: true
# Settings for the build environment.
#
# @api private
class BuildEnvironment
extend T::Sig
sig { params(settings: Symbol).void }
def initialize(*settings)
@settings = Set.new(*settings)
@settings = Set.new(settings)
end
sig { params(args: T::Enumerable[Symbol]).returns(T.self_type) }
def merge(*args)
@settings.merge(*args)
self
end
sig { params(o: Symbol).returns(T.self_type) }
def <<(o)
@settings << o
self
end
sig { returns(T::Boolean) }
def std?
@settings.include? :std
end
sig { returns(T::Boolean) }
def userpaths?
@settings.include? :userpaths
end
# DSL for specifying build environment settings.
module DSL
extend T::Sig
sig { params(settings: Symbol).returns(BuildEnvironment) }
def env(*settings)
@env ||= BuildEnvironment.new
@env.merge(settings)
@ -50,16 +60,18 @@ class BuildEnvironment
].freeze
private_constant :KEYS
sig { params(env: T.untyped).returns(T::Array[String]) }
def self.keys(env)
KEYS & env.keys
end
sig { params(env: T.untyped, f: IO).void }
def self.dump(env, f = $stdout)
keys = self.keys(env)
keys -= %w[CC CXX OBJC OBJCXX] if env["CC"] == env["HOMEBREW_CC"]
keys.each do |key|
value = env[key]
value = env.fetch(key)
s = +"#{key}: #{value}"
case key
when "CC", "CXX", "LD"

View File

@ -7,6 +7,8 @@ module Cask
#
# @api private
class AbstractArtifact
extend T::Sig
include Comparable
extend Predicable
@ -132,6 +134,7 @@ module Cask
cask.config
end
sig { returns(String) }
def to_s
"#{summarize} (#{self.class.english_name})"
end

View File

@ -15,6 +15,8 @@ module Cask
#
# @api private
class AbstractUninstall < AbstractArtifact
extend T::Sig
ORDERED_DIRECTIVES = [
:early_script,
:launchctl,
@ -53,6 +55,7 @@ module Cask
directives.to_h
end
sig { returns(String) }
def summarize
to_h.flat_map { |key, val| Array(val).map { |v| "#{key.inspect} => #{v.inspect}" } }.join(", ")
end
@ -128,6 +131,7 @@ module Cask
end
end
sig { returns(String) }
def automation_access_instructions
"Enable Automation Access for “Terminal > System Events” in " \
"“System Preferences > Security > Privacy > Automation” if you haven't already."

View File

@ -12,6 +12,9 @@ module Cask
#
# @api private
class Artifact < Moved
extend T::Sig
sig { returns(String) }
def self.english_name
"Generic Artifact"
end

View File

@ -9,6 +9,9 @@ module Cask
#
# @api private
class Mdimporter < Moved
extend T::Sig
sig { returns(String) }
def self.english_name
"Spotlight metadata importer"
end

View File

@ -9,6 +9,9 @@ module Cask
#
# @api private
class Moved < Relocated
extend T::Sig
sig { returns(String) }
def self.english_description
"#{english_name}s"
end

View File

@ -9,6 +9,9 @@ module Cask
#
# @api private
class Prefpane < Moved
extend T::Sig
sig { returns(String) }
def self.english_name
"Preference Pane"
end

View File

@ -9,6 +9,9 @@ module Cask
#
# @api private
class Qlplugin < Moved
extend T::Sig
sig { returns(String) }
def self.english_name
"QuickLook Plugin"
end

View File

@ -12,6 +12,8 @@ module Cask
#
# @api private
class Relocated < AbstractArtifact
extend T::Sig
def self.from_args(cask, *args)
source_string, target_hash = args
@ -49,6 +51,7 @@ module Cask
end
end
sig { returns(String) }
def summarize
target_string = @target_string.empty? ? "" : " -> #{@target_string}"
"#{@source_string}#{target_string}"

View File

@ -9,16 +9,20 @@ module Cask
#
# @api private
class StageOnly < AbstractArtifact
extend T::Sig
def self.from_args(cask, *args)
raise CaskInvalidError.new(cask.token, "'stage_only' takes only a single argument: true") if args != [true]
new(cask)
end
sig { returns(T::Array[T::Boolean]) }
def to_a
[true]
end
sig { returns(String) }
def summarize
"true"
end

View File

@ -9,10 +9,14 @@ module Cask
#
# @api private
class Suite < Moved
extend T::Sig
sig { returns(String) }
def self.english_name
"App Suite"
end
sig { returns(Symbol) }
def self.dirmethod
:appdir
end

View File

@ -9,10 +9,14 @@ module Cask
#
# @api private
class Symlinked < Relocated
extend T::Sig
sig { returns(String) }
def self.link_type_english_name
"Symlink"
end
sig { returns(String) }
def self.english_description
"#{english_name} #{link_type_english_name}s"
end

View File

@ -13,6 +13,8 @@ module Cask
#
# @api private
class Audit
extend T::Sig
extend Predicable
attr_reader :cask, :download
@ -117,6 +119,7 @@ module Cask
end
end
sig { returns(String) }
def summary
summary = ["audit for #{cask}: #{result}"]
@ -416,6 +419,7 @@ module Cask
core_tap.formula_names
end
sig { returns(String) }
def core_formula_url
"#{core_tap.default_remote}/blob/HEAD/Formula/#{cask.token}.rb"
end

View File

@ -6,9 +6,10 @@ module Cask
#
# @api private
module Cache
module_function
extend T::Sig
def path
sig { returns(Pathname) }
def self.path
@path ||= HOMEBREW_CACHE/"Cask"
end
end

View File

@ -12,6 +12,8 @@ module Cask
#
# @api private
class Cask
extend T::Sig
extend Enumerable
extend Forwardable
extend Searchable
@ -20,7 +22,7 @@ module Cask
attr_reader :token, :sourcefile_path, :config, :default_config
def self.each(&block)
return to_enum unless block_given?
return to_enum unless block
Tap.flat_map(&:cask_files).each do |f|
block.call CaskLoader::FromTapPathLoader.new(f).load(config: nil)
@ -64,6 +66,7 @@ module Cask
define_method(method_name) { |&block| @dsl.send(method_name, &block) }
end
sig { returns(T::Array[[String, String]]) }
def timestamped_versions
Pathname.glob(metadata_timestamped_path(version: "*", timestamp: "*"))
.map { |p| p.relative_path_from(p.parent.parent) }
@ -89,6 +92,7 @@ module Cask
!versions.empty?
end
sig { returns(T.nilable(Time)) }
def install_time
_, time = timestamped_versions.last
return unless time

View File

@ -90,6 +90,8 @@ module Cask
# Loads a cask from a URI.
class FromURILoader < FromPathLoader
extend T::Sig
def self.can_load?(ref)
uri_regex = ::URI::DEFAULT_PARSER.make_regexp
return false unless ref.to_s.match?(Regexp.new("\\A#{uri_regex.source}\\Z", uri_regex.options))
@ -103,6 +105,7 @@ module Cask
attr_reader :url
sig { params(url: T.any(URI::Generic, String)).void }
def initialize(url)
@url = URI(url)
super Cache.path/File.basename(@url.path)
@ -177,10 +180,13 @@ module Cask
# Pseudo-loader which raises an error when trying to load the corresponding cask.
class NullLoader < FromPathLoader
extend T::Sig
def self.can_load?(*)
true
end
sig { params(ref: T.any(String, Pathname)).void }
def initialize(ref)
token = File.basename(ref, ".rb")
super CaskLoader.default_path(token)

View File

@ -1,4 +1,4 @@
# typed: false
# typed: true
# frozen_string_literal: true
require "utils/user"
@ -8,13 +8,15 @@ module Cask
#
# @api private
module Caskroom
module_function
extend T::Sig
def path
sig { returns(Pathname) }
def self.path
@path ||= HOMEBREW_PREFIX.join("Caskroom")
end
def ensure_caskroom_exists
sig { void }
def self.ensure_caskroom_exists
return if path.exist?
sudo = !path.parent.writable?
@ -30,7 +32,8 @@ module Cask
SystemCommand.run("/usr/bin/chgrp", args: ["admin", path], sudo: sudo)
end
def casks(config: nil)
sig { params(config: T.nilable(Config)).returns(T::Array[Cask]) }
def self.casks(config: nil)
return [] unless path.exist?
Pathname.glob(path.join("*")).sort.select(&:directory?).map do |path|

View File

@ -38,6 +38,8 @@ module Cask
#
# @api private
class Cmd
extend T::Sig
include Context
ALIASES = {
@ -61,11 +63,13 @@ module Cask
Cmd::Upgrade => "brew upgrade --cask",
}.freeze
sig { returns(String) }
def self.description
max_command_length = Cmd.commands.map(&:length).max
command_lines = Cmd.command_classes
.select(&:visible?)
.reject { |command| DEPRECATED_COMMANDS.key?(command) }
.map do |klass|
" - #{"`#{klass.command_name}`".ljust(max_command_length + 2)} #{klass.short_description}\n"
end
@ -75,14 +79,13 @@ module Cask
Commands:
#{command_lines.join}
See also: `man brew`
EOS
end
def self.parser(&block)
Homebrew::CLI::Parser.new do
if block_given?
if block
instance_eval(&block)
else
usage_banner <<~EOS

View File

@ -7,18 +7,24 @@ module Cask
#
# @api private
class Cache < AbstractCommand
extend T::Sig
sig { override.returns(T.nilable(T.any(Integer, Symbol))) }
def self.min_named
:cask
end
sig { returns(String) }
def self.description
"Display the file used to cache a <cask>."
end
sig { returns(String) }
def self.command_name
"--cache"
end
sig { void }
def run
casks.each do |cask|
puts self.class.cached_location(cask)

View File

@ -9,16 +9,22 @@ module Cask
#
# @api private
class AbstractCommand
extend T::Sig
extend T::Helpers
include Homebrew::Search
sig { returns(T.nilable(T.any(Integer, Symbol))) }
def self.min_named
nil
end
sig { returns(T.nilable(Integer)) }
def self.max_named
nil
end
sig { returns(String) }
def self.banner_args
if min_named == :cask && max_named != 1
" <cask>"
@ -29,13 +35,14 @@ module Cask
end
end
sig { returns(String) }
def self.banner_headline
"`#{command_name}` [<options>]#{banner_args}"
end
OPTIONS = [
[:switch, "--[no-]binaries", {
description: "Disable/enable linking of helper executables. Default: enabled",
description: "Disable/enable linking of helper executables (default: enabled).",
env: :cask_opts_binaries,
}],
[:switch, "--require-sha", {
@ -43,7 +50,7 @@ module Cask
env: :cask_opts_require_sha,
}],
[:switch, "--[no-]quarantine", {
description: "Disable/enable quarantining of downloads. Default: enabled",
description: "Disable/enable quarantining of downloads (default: enabled).",
env: :cask_opts_quarantine,
}],
].freeze
@ -61,7 +68,7 @@ module Cask
Cmd.parser do
usage_banner banner
instance_eval(&block) if block_given?
instance_eval(&block) if block
OPTIONS.each do |option|
send(*option)
@ -72,24 +79,29 @@ module Cask
end
end
sig { returns(String) }
def self.command_name
@command_name ||= name.sub(/^.*:/, "").gsub(/(.)([A-Z])/, '\1_\2').downcase
end
sig { returns(T::Boolean) }
def self.abstract?
name.split("::").last.match?(/^Abstract[^a-z]/)
end
sig { returns(T::Boolean) }
def self.visible?
true
end
sig { returns(String) }
def self.help
parser.generate_help_text
end
sig { returns(String) }
def self.short_description
description.split(".").first
description[/\A[^.]*\./]
end
def self.run(*args)

View File

@ -7,10 +7,14 @@ module Cask
#
# @api private
class AbstractInternalCommand < AbstractCommand
extend T::Sig
sig { returns(String) }
def self.command_name
super.sub(/^internal_/i, "_")
end
sig { returns(T::Boolean) }
def self.visible?
false
end

View File

@ -9,6 +9,9 @@ module Cask
#
# @api private
class Audit < AbstractCommand
extend T::Sig
sig { returns(String) }
def self.description
<<~EOS
Check <cask> for Homebrew coding style violations. This should be run before
@ -37,57 +40,84 @@ module Cask
end
end
sig { void }
def run
require "cask/auditor"
Homebrew.auditing = true
options = {
audit_download: args.download?,
audit_appcast: args.appcast?,
audit_online: args.online?,
audit_strict: args.strict?,
audit_new_cask: args.new_cask?,
audit_token_conflicts: args.token_conflicts?,
quarantine: args.quarantine?,
language: args.language,
}.compact
options[:quarantine] = true if options[:quarantine].nil?
casks = args.named.flat_map do |name|
if File.exist?(name)
name
elsif name.count("/") == 1
Tap.fetch(name).cask_files
else
name
end
next name if File.exist?(name)
next Tap.fetch(name).cask_files if name.count("/") == 1
name
end
casks = casks.map { |c| CaskLoader.load(c, config: Config.from_args(args)) }
casks = Cask.to_a if casks.empty?
failed_casks = casks.reject do |cask|
odebug "Auditing Cask #{cask}"
result = Auditor.audit(cask, **options)
results = self.class.audit_casks(
*casks,
download: args.download?,
appcast: args.appcast?,
online: args.online?,
strict: args.strict?,
new_cask: args.new_cask?,
token_conflicts: args.token_conflicts?,
quarantine: args.quarantine?,
language: args.language,
)
if ENV["GITHUB_ACTIONS"]
cask_path = cask.sourcefile_path
annotations = (result[:warnings].map { |w| [:warning, w] } + result[:errors].map { |e| [:error, e] })
.map { |type, message| GitHub::Actions::Annotation.new(type, message, file: cask_path) }
annotations.each do |annotation|
puts annotation if annotation.relevant?
end
end
result[:errors].empty?
end
self.class.print_annotations(results)
failed_casks = results.reject { |_, result| result[:errors].empty? }.map(&:first)
return if failed_casks.empty?
raise CaskError, "audit failed for casks: #{failed_casks.join(" ")}"
end
def self.audit_casks(
*casks,
download: nil,
appcast: nil,
online: nil,
strict: nil,
new_cask: nil,
token_conflicts: nil,
quarantine: nil,
language: nil
)
options = {
audit_download: download,
audit_appcast: appcast,
audit_online: online,
audit_strict: strict,
audit_new_cask: new_cask,
audit_token_conflicts: token_conflicts,
quarantine: quarantine,
language: language,
}.compact
options[:quarantine] = true if options[:quarantine].nil?
Homebrew.auditing = true
require "cask/auditor"
casks.map do |cask|
odebug "Auditing Cask #{cask}"
[cask, Auditor.audit(cask, **options)]
end.to_h
end
def self.print_annotations(results)
return unless ENV["GITHUB_ACTIONS"]
results.each do |cask, result|
cask_path = cask.sourcefile_path
annotations = (result[:warnings].map { |w| [:warning, w] } + result[:errors].map { |e| [:error, e] })
.map { |type, message| GitHub::Actions::Annotation.new(type, message, file: cask_path) }
annotations.each do |annotation|
puts annotation if annotation.relevant?
end
end
end
end
end
end

View File

@ -7,14 +7,19 @@ module Cask
#
# @api private
class Cat < AbstractCommand
extend T::Sig
sig { override.returns(T.nilable(T.any(Integer, Symbol))) }
def self.min_named
:cask
end
sig { returns(String) }
def self.description
"Dump raw source of a <cask> to the standard output."
end
sig { void }
def run
casks.each do |cask|
if Homebrew::EnvConfig.bat?

View File

@ -7,14 +7,19 @@ module Cask
#
# @api private
class Create < AbstractCommand
extend T::Sig
sig { override.returns(T.nilable(T.any(Integer, Symbol))) }
def self.min_named
:cask
end
sig { override.returns(T.nilable(Integer)) }
def self.max_named
1
end
sig { returns(String) }
def self.description
"Creates the given <cask> and opens it in an editor."
end
@ -25,6 +30,7 @@ module Cask
raise UsageError, "Only one cask can be created at a time."
end
sig { void }
def run
cask_token = args.named.first
cask_path = CaskLoader.path(cask_token)

View File

@ -7,14 +7,19 @@ module Cask
#
# @api private
class Doctor < AbstractCommand
extend T::Sig
sig { override.returns(T.nilable(Integer)) }
def self.max_named
0
end
sig { returns(String) }
def self.description
"Checks for configuration issues."
end
sig { void }
def run
require "diagnostic"

View File

@ -7,14 +7,19 @@ module Cask
#
# @api private
class Edit < AbstractCommand
extend T::Sig
sig { override.returns(T.nilable(T.any(Integer, Symbol))) }
def self.min_named
:cask
end
sig { override.returns(T.nilable(Integer)) }
def self.max_named
1
end
sig { returns(String) }
def self.description
"Open the given <cask> for editing."
end
@ -25,6 +30,7 @@ module Cask
raise UsageError, "Only one cask can be edited at a time."
end
sig { void }
def run
exec_editor cask_path
rescue CaskUnavailableError => e

View File

@ -7,6 +7,9 @@ module Cask
#
# @api private
class Fetch < AbstractCommand
extend T::Sig
sig { override.returns(T.nilable(T.any(Integer, Symbol))) }
def self.min_named
:cask
end
@ -18,10 +21,12 @@ module Cask
end
end
sig { returns(String) }
def self.description
"Downloads remote application files to local cache."
end
sig { void }
def run
require "cask/download"
require "cask/installer"

View File

@ -7,14 +7,19 @@ module Cask
#
# @api private
class Help < AbstractCommand
extend T::Sig
sig { override.returns(T.nilable(Integer)) }
def self.max_named
1
end
sig { returns(String) }
def self.description
"Print help for `cask` commands."
end
sig { void }
def run
if args.named.empty?
puts Cmd.parser.generate_help_text

View File

@ -7,10 +7,14 @@ module Cask
#
# @api private
class Home < AbstractCommand
extend T::Sig
sig { returns(String) }
def self.description
"Opens the homepage of the given <cask>. If no cask is given, opens the Homebrew homepage."
end
sig { void }
def run
if casks.none?
odebug "Opening project homepage"

View File

@ -9,10 +9,14 @@ module Cask
#
# @api private
class Info < AbstractCommand
extend T::Sig
sig { override.returns(T.nilable(T.any(Integer, Symbol))) }
def self.min_named
:cask
end
sig { returns(String) }
def self.description
"Displays information about the given <cask>."
end
@ -42,6 +46,7 @@ module Cask
end
end
sig { void }
def run
if args.json == "v1"
puts JSON.generate(args.named.to_casks.map(&:to_h))

View File

@ -7,10 +7,14 @@ module Cask
#
# @api private
class Install < AbstractCommand
extend T::Sig
sig { override.returns(T.nilable(T.any(Integer, Symbol))) }
def self.min_named
:cask
end
sig { returns(String) }
def self.description
"Installs the given <cask>."
end
@ -30,10 +34,11 @@ module Cask
send(*option)
end
instance_eval(&block) if block_given?
instance_eval(&block) if block
end
end
sig { void }
def run
self.class.install_casks(
*casks,

View File

@ -7,14 +7,19 @@ module Cask
#
# @api private
class InternalHelp < AbstractInternalCommand
extend T::Sig
sig { override.returns(T.nilable(Integer)) }
def self.max_named
0
end
sig { returns(String) }
def self.description
"Print help for unstable internal-use commands."
end
sig { void }
def run
max_command_len = Cmd.commands.map(&:length).max
puts "Unstable Internal-use Commands:\n\n"

View File

@ -9,6 +9,8 @@ module Cask
#
# @api private
class InternalStanza < AbstractInternalCommand
extend T::Sig
# Syntax
#
# brew cask _stanza <stanza_name> [ --quiet ] [ --table | --yaml ] [ <cask_token> ... ]
@ -24,14 +26,17 @@ module Cask
(DSL::ORDINARY_ARTIFACT_CLASSES.map(&:dsl_key) +
DSL::ARTIFACT_BLOCK_CLASSES.map(&:dsl_key)).freeze
sig { override.returns(T.nilable(T.any(Integer, Symbol))) }
def self.min_named
1
end
sig { returns(String) }
def self.banner_args
" <stanza_name> [<cask>]"
end
sig { returns(String) }
def self.description
<<~EOS
Extract and render a specific stanza for the given <cask>.
@ -80,6 +85,7 @@ module Cask
EOS
end
sig { void }
def run
if ARTIFACTS.include?(stanza)
artifact_name = stanza

View File

@ -9,6 +9,9 @@ module Cask
#
# @api private
class List < AbstractCommand
extend T::Sig
sig { returns(String) }
def self.description
"Lists installed casks or the casks provided in the arguments."
end
@ -26,6 +29,7 @@ module Cask
end
end
sig { void }
def run
self.class.list_casks(
*casks,

View File

@ -7,6 +7,9 @@ module Cask
#
# @api private
class Outdated < AbstractCommand
extend T::Sig
sig { returns(String) }
def self.description
"List the outdated installed casks."
end
@ -20,6 +23,7 @@ module Cask
end
end
sig { void }
def run
outdated_casks = casks(alternative: -> { Caskroom.casks(config: Config.from_args(args)) }).select do |cask|
odebug "Checking update info of Cask #{cask}"

View File

@ -7,10 +7,14 @@ module Cask
#
# @api private
class Reinstall < Install
extend T::Sig
sig { returns(String) }
def self.description
"Reinstalls the given <cask>."
end
sig { void }
def run
self.class.reinstall_casks(
*casks,

View File

@ -10,6 +10,9 @@ module Cask
#
# @api private
class Style < AbstractCommand
extend T::Sig
sig { returns(String) }
def self.description
"Checks style of the given <cask> using RuboCop."
end
@ -21,6 +24,7 @@ module Cask
end
end
sig { void }
def run
success = Homebrew::Style.check_style_and_print(
cask_paths,

View File

@ -7,10 +7,14 @@ module Cask
#
# @api private
class Uninstall < AbstractCommand
extend T::Sig
sig { override.returns(T.nilable(T.any(Integer, Symbol))) }
def self.min_named
:cask
end
sig { returns(String) }
def self.description
"Uninstalls the given <cask>."
end
@ -23,6 +27,7 @@ module Cask
end
end
sig { void }
def run
self.class.uninstall_casks(
*casks,

View File

@ -10,6 +10,9 @@ module Cask
#
# @api private
class Upgrade < AbstractCommand
extend T::Sig
sig { returns(String) }
def self.description
"Upgrades all outdated casks or the specified casks."
end
@ -23,6 +26,7 @@ module Cask
}],
].freeze
sig { returns(Homebrew::CLI::Parser) }
def self.parser
super do
switch "--force",
@ -36,6 +40,7 @@ module Cask
end
end
sig { void }
def run
verbose = ($stdout.tty? || args.verbose?) && !args.quiet?
self.class.upgrade_casks(
@ -52,6 +57,20 @@ module Cask
)
end
sig do
params(
casks: Cask,
args: Homebrew::CLI::Args,
force: T.nilable(T::Boolean),
greedy: T.nilable(T::Boolean),
dry_run: T.nilable(T::Boolean),
skip_cask_deps: T.nilable(T::Boolean),
verbose: T.nilable(T::Boolean),
binaries: T.nilable(T::Boolean),
quarantine: T.nilable(T::Boolean),
require_sha: T.nilable(T::Boolean),
).void
end
def self.upgrade_casks(
*casks,
args:,
@ -81,7 +100,9 @@ module Cask
return if outdated_casks.empty?
ohai "Casks with `auto_updates` or `version :latest` will not be upgraded" if casks.empty? && !greedy
if casks.empty? && !greedy
ohai "Casks with `auto_updates` or `version :latest` will not be upgraded; pass `--greedy` to upgrade them."
end
verb = dry_run ? "Would upgrade" : "Upgrading"
oh1 "#{verb} #{outdated_casks.count} #{"outdated package".pluralize(outdated_casks.count)}:"

View File

@ -7,10 +7,14 @@ module Cask
#
# @api private
class Zap < AbstractCommand
extend T::Sig
sig { override.returns(T.nilable(T.any(Integer, Symbol))) }
def self.min_named
:cask
end
sig { returns(String) }
def self.description
<<~EOS
Zaps all files associated with the given <cask>. Implicitly also performs all actions associated with `uninstall`.
@ -26,6 +30,7 @@ module Cask
end
end
sig { void }
def run
require "cask/installer"

View File

@ -1,4 +1,4 @@
# typed: true
# typed: strict
# frozen_string_literal: true
module Cask
@ -6,6 +6,9 @@ module Cask
#
# @api private
module Denylist
extend T::Sig
sig { params(name: String).returns(T.nilable(String)) }
def self.reason(name)
case name
when /^adobe-(after|illustrator|indesign|photoshop|premiere)/

View File

@ -125,7 +125,7 @@ module Cask
def language(*args, default: false, &block)
if args.empty?
language_eval
elsif block_given?
elsif block
@language_blocks ||= {}
@language_blocks[args] = block
@ -248,7 +248,7 @@ module Cask
def caveats(*strings, &block)
@caveats ||= DSL::Caveats.new(cask)
if block_given?
if block
@caveats.eval_caveats(&block)
elsif strings.any?
strings.each do |string|

View File

@ -64,7 +64,7 @@ module Cask
MacOSRequirement.new([version.to_sym], comparator: comparator)
elsif /^\s*(?<comparator><|>|[=<>]=)\s*(?<version>\S+)\s*$/ =~ args.first
MacOSRequirement.new([version], comparator: comparator)
else
else # rubocop:disable Lint/DuplicateBranch
MacOSRequirement.new([args.first], comparator: "==")
end
rescue MacOSVersionError => e

View File

@ -7,6 +7,8 @@ module Cask
#
# @api private
class Version < ::String
extend T::Sig
DIVIDERS = {
"." => :dots,
"-" => :hyphens,
@ -62,6 +64,7 @@ module Cask
attr_reader :raw_version
sig { params(raw_version: T.nilable(T.any(String, Symbol))).void }
def initialize(raw_version)
@raw_version = raw_version
super(raw_version.to_s)
@ -73,6 +76,7 @@ module Cask
raw_version.scan(INVALID_CHARACTERS)
end
sig { returns(T::Boolean) }
def unstable?
return false if latest?
@ -84,6 +88,7 @@ module Cask
false
end
sig { returns(T::Boolean) }
def latest?
to_s == "latest"
end
@ -134,6 +139,7 @@ module Cask
private
sig { returns(T.self_type) }
def version
return self if empty? || latest?

View File

@ -11,12 +11,15 @@ module Cask
#
# @api private
class MultipleCaskErrors < CaskError
extend T::Sig
def initialize(errors)
super()
@errors = errors
end
sig { returns(String) }
def to_s
<<~EOS
Problems with multiple casks:
@ -29,12 +32,18 @@ module Cask
#
# @api private
class AbstractCaskErrorWithToken < CaskError
attr_reader :token, :reason
extend T::Sig
sig { returns(String) }
attr_reader :token
sig { returns(String) }
attr_reader :reason
def initialize(token, reason = nil)
super()
@token = token
@token = token.to_s
@reason = reason.to_s
end
end
@ -43,6 +52,9 @@ module Cask
#
# @api private
class CaskNotInstalledError < AbstractCaskErrorWithToken
extend T::Sig
sig { returns(String) }
def to_s
"Cask '#{token}' is not installed."
end
@ -52,6 +64,8 @@ module Cask
#
# @api private
class CaskConflictError < AbstractCaskErrorWithToken
extend T::Sig
attr_reader :conflicting_cask
def initialize(token, conflicting_cask)
@ -59,6 +73,7 @@ module Cask
@conflicting_cask = conflicting_cask
end
sig { returns(String) }
def to_s
"Cask '#{token}' conflicts with '#{conflicting_cask}'."
end
@ -68,6 +83,9 @@ module Cask
#
# @api private
class CaskUnavailableError < AbstractCaskErrorWithToken
extend T::Sig
sig { returns(String) }
def to_s
"Cask '#{token}' is unavailable#{reason.empty? ? "." : ": #{reason}"}"
end
@ -77,6 +95,9 @@ module Cask
#
# @api private
class CaskUnreadableError < CaskUnavailableError
extend T::Sig
sig { returns(String) }
def to_s
"Cask '#{token}' is unreadable#{reason.empty? ? "." : ": #{reason}"}"
end
@ -86,6 +107,9 @@ module Cask
#
# @api private
class CaskAlreadyCreatedError < AbstractCaskErrorWithToken
extend T::Sig
sig { returns(String) }
def to_s
%Q(Cask '#{token}' already exists. Run #{Formatter.identifier("brew cask edit #{token}")} to edit it.)
end
@ -95,6 +119,9 @@ module Cask
#
# @api private
class CaskAlreadyInstalledError < AbstractCaskErrorWithToken
extend T::Sig
sig { returns(String) }
def to_s
<<~EOS
Cask '#{token}' is already installed.
@ -109,6 +136,9 @@ module Cask
#
# @api private
class CaskX11DependencyError < AbstractCaskErrorWithToken
extend T::Sig
sig { returns(String) }
def to_s
<<~EOS
Cask '#{token}' requires XQuartz/X11, which can be installed using Homebrew Cask by running:
@ -124,6 +154,9 @@ module Cask
#
# @api private
class CaskCyclicDependencyError < AbstractCaskErrorWithToken
extend T::Sig
sig { returns(String) }
def to_s
"Cask '#{token}' includes cyclic dependencies on other Casks#{reason.empty? ? "." : ": #{reason}"}"
end
@ -133,6 +166,9 @@ module Cask
#
# @api private
class CaskSelfReferencingDependencyError < CaskCyclicDependencyError
extend T::Sig
sig { returns(String) }
def to_s
"Cask '#{token}' depends on itself."
end
@ -142,6 +178,9 @@ module Cask
#
# @api private
class CaskUnspecifiedError < CaskError
extend T::Sig
sig { returns(String) }
def to_s
"This command requires a Cask token."
end
@ -151,6 +190,9 @@ module Cask
#
# @api private
class CaskInvalidError < AbstractCaskErrorWithToken
extend T::Sig
sig { returns(String) }
def to_s
"Cask '#{token}' definition is invalid#{reason.empty? ? "." : ": #{reason}"}"
end
@ -182,6 +224,9 @@ module Cask
#
# @api private
class CaskSha256MissingError < CaskSha256Error
extend T::Sig
sig { returns(String) }
def to_s
<<~EOS
Cask '#{token}' requires a checksum:
@ -194,6 +239,8 @@ module Cask
#
# @api private
class CaskSha256MismatchError < CaskSha256Error
extend T::Sig
attr_reader :path
def initialize(token, expected, actual, path)
@ -201,6 +248,7 @@ module Cask
@path = path
end
sig { returns(String) }
def to_s
<<~EOS
Checksum for Cask '#{token}' does not match.
@ -218,6 +266,9 @@ module Cask
#
# @api private
class CaskNoShasumError < CaskSha256Error
extend T::Sig
sig { returns(String) }
def to_s
<<~EOS
Cask '#{token}' does not have a sha256 checksum defined and was not installed.
@ -230,6 +281,8 @@ module Cask
#
# @api private
class CaskQuarantineError < CaskError
extend T::Sig
attr_reader :path, :reason
def initialize(path, reason)
@ -239,6 +292,7 @@ module Cask
@reason = reason
end
sig { returns(String) }
def to_s
s = +"Failed to quarantine #{path}."
@ -256,6 +310,9 @@ module Cask
#
# @api private
class CaskQuarantinePropagationError < CaskQuarantineError
extend T::Sig
sig { returns(String) }
def to_s
s = +"Failed to quarantine one or more files within #{path}."
@ -273,6 +330,9 @@ module Cask
#
# @api private
class CaskQuarantineReleaseError < CaskQuarantineError
extend T::Sig
sig { returns(String) }
def to_s
s = +"Failed to release #{path} from quarantine."

View File

@ -18,6 +18,8 @@ module Cask
#
# @api private
class Installer
extend T::Sig
extend Predicable
# TODO: it is unwise for Cask::Staged to be a module, when we are
# dealing with both staged and unstaged casks here. This should
@ -142,6 +144,7 @@ module Cask
Installer.new(installed_cask, binaries: binaries?, verbose: verbose?, force: true, upgrade: upgrade?).uninstall
end
sig { returns(String) }
def summary
s = +""
s << "#{Homebrew::EnvConfig.install_badge} " unless Homebrew::EnvConfig.no_emoji?
@ -368,15 +371,19 @@ module Cask
force: false,
).install
else
FormulaInstaller.new(cask_or_formula, verbose: verbose?).yield_self do |fi|
fi.installed_as_dependency = true
fi.installed_on_request = false
fi.show_header = true
fi.prelude
fi.fetch
fi.install
fi.finish
end
fi = FormulaInstaller.new(
cask_or_formula,
**{
show_header: true,
installed_as_dependency: true,
installed_on_request: false,
verbose: verbose?,
}.compact,
)
fi.prelude
fi.fetch
fi.install
fi.finish
end
end
end

View File

@ -8,19 +8,25 @@ module Cask
#
# @api private
class Pkg
extend T::Sig
sig { params(regexp: String, command: T.class_of(SystemCommand)).returns(T::Array[Pkg]) }
def self.all_matching(regexp, command)
command.run("/usr/sbin/pkgutil", args: ["--pkgs=#{regexp}"]).stdout.split("\n").map do |package_id|
new(package_id.chomp, command)
end
end
sig { returns(String) }
attr_reader :package_id
sig { params(package_id: String, command: T.class_of(SystemCommand)).void }
def initialize(package_id, command = SystemCommand)
@package_id = package_id
@command = command
end
sig { void }
def uninstall
unless pkgutil_bom_files.empty?
odebug "Deleting pkg files"
@ -65,23 +71,28 @@ module Cask
forget
end
sig { void }
def forget
odebug "Unregistering pkg receipt (aka forgetting)"
@command.run!("/usr/sbin/pkgutil", args: ["--forget", package_id], sudo: true)
end
sig { returns(T::Array[Pathname]) }
def pkgutil_bom_files
@pkgutil_bom_files ||= pkgutil_bom_all.select(&:file?) - pkgutil_bom_specials
end
sig { returns(T::Array[Pathname]) }
def pkgutil_bom_specials
@pkgutil_bom_specials ||= pkgutil_bom_all.select(&method(:special?))
end
sig { returns(T::Array[Pathname]) }
def pkgutil_bom_dirs
@pkgutil_bom_dirs ||= pkgutil_bom_all.select(&:directory?) - pkgutil_bom_specials
end
sig { returns(T::Array[Pathname]) }
def pkgutil_bom_all
@pkgutil_bom_all ||= @command.run!("/usr/sbin/pkgutil", args: ["--files", package_id])
.stdout
@ -90,6 +101,7 @@ module Cask
.reject(&MacOS.public_method(:undeletable?))
end
sig { returns(Pathname) }
def root
@root ||= Pathname.new(info.fetch("volume")).join(info.fetch("install-location"))
end
@ -101,10 +113,12 @@ module Cask
private
sig { params(path: Pathname).returns(T::Boolean) }
def special?(path)
path.symlink? || path.chardev? || path.blockdev?
end
sig { params(path: Pathname).void }
def rmdir(path)
return unless path.children.empty?
@ -115,7 +129,8 @@ module Cask
end
end
def with_full_permissions(path)
sig { params(path: Pathname, _block: T.proc.void).void }
def with_full_permissions(path, &_block)
original_mode = (path.stat.mode % 01000).to_s(8)
original_flags = @command.run!("/usr/bin/stat", args: ["-f", "%Of", "--", path]).stdout.chomp
@ -128,10 +143,12 @@ module Cask
end
end
sig { params(paths: T::Array[Pathname]).returns(T::Array[Pathname]) }
def deepest_path_first(paths)
paths.sort_by { |path| -path.to_s.split(File::SEPARATOR).count }
end
sig { params(dir: Pathname).void }
def clean_ds_store(dir)
return unless (ds_store = dir.join(".DS_Store")).exist?
@ -140,12 +157,14 @@ module Cask
# Some packages leave broken symlinks around; we clean them out before
# attempting to `rmdir` to prevent extra cruft from accumulating.
sig { params(dir: Pathname).void }
def clean_broken_symlinks(dir)
dir.children.select(&method(:broken_symlink?)).each do |path|
@command.run!("/bin/rm", args: ["--", path], sudo: true)
end
end
sig { params(path: Pathname).returns(T::Boolean) }
def broken_symlink?(path)
path.symlink? && !path.exist?
end

View File

@ -9,6 +9,8 @@ module Cask
#
# @api private
module Quarantine
extend T::Sig
module_function
QUARANTINE_ATTRIBUTE = "com.apple.quarantine"
@ -25,6 +27,7 @@ module Cask
end
private :xattr
sig { returns(Symbol) }
def check_quarantine_support
odebug "Checking quarantine support"

View File

@ -1,4 +1,4 @@
# typed: false
# typed: true
# frozen_string_literal: true
require "utils/user"
@ -8,25 +8,35 @@ module Cask
#
# @api private
module Staged
extend T::Sig
# FIXME: Enable cop again when https://github.com/sorbet/sorbet/issues/3532 is fixed.
# rubocop:disable Style/MutableConstant
Paths = T.type_alias { T.any(String, Pathname, T::Array[T.any(String, Pathname)]) }
# rubocop:enable Style/MutableConstant
sig { params(paths: Paths, permissions_str: String).void }
def set_permissions(paths, permissions_str)
full_paths = remove_nonexistent(paths)
return if full_paths.empty?
@command.run!("/bin/chmod", args: ["-R", "--", permissions_str] + full_paths,
@command.run!("/bin/chmod", args: ["-R", "--", permissions_str, *full_paths],
sudo: false)
end
def set_ownership(paths, user: User.current, group: "staff")
sig { params(paths: Paths, user: T.any(String, User), group: String).void }
def set_ownership(paths, user: T.must(User.current), group: "staff")
full_paths = remove_nonexistent(paths)
return if full_paths.empty?
ohai "Changing ownership of paths required by #{@cask}; your password may be necessary"
@command.run!("/usr/sbin/chown", args: ["-R", "--", "#{user}:#{group}"] + full_paths,
@command.run!("/usr/sbin/chown", args: ["-R", "--", "#{user}:#{group}", *full_paths],
sudo: true)
end
private
sig { params(paths: Paths).returns(T::Array[Pathname]) }
def remove_nonexistent(paths)
Array(paths).map { |p| Pathname(p).expand_path }.select(&:exist?)
end

View File

@ -0,0 +1,7 @@
# typed: strict
module Cask
module Staged
include Kernel
end
end

View File

@ -1,4 +1,4 @@
# typed: false
# typed: true
# frozen_string_literal: true
require "tsort"
@ -8,7 +8,11 @@ module Cask
class TopologicalHash < Hash
include TSort
alias tsort_each_node each_key
private
def tsort_each_node(&block)
each_key(&block)
end
def tsort_each_child(node, &block)
fetch(node).each(&block)

View File

@ -13,6 +13,8 @@ module Cask
#
# @api private
module Utils
extend T::Sig
def self.gain_permissions_remove(path, command: SystemCommand)
if path.respond_to?(:rmtree) && path.exist?
gain_permissions(path, ["-R"], command) do |p|
@ -72,10 +74,12 @@ module Cask
end
end
sig { params(path: Pathname).returns(T::Boolean) }
def self.path_occupied?(path)
File.exist?(path) || File.symlink?(path)
path.exist? || path.symlink?
end
sig { returns(String) }
def self.error_message_with_suggestions
<<~EOS
Follow the instructions here:

View File

@ -76,6 +76,12 @@ class Cleaner
path.text_executable? || path.executable?
end
# Both these files are completely unnecessary to package and cause
# pointless conflicts with other formulae. They are removed by Debian,
# Arch & MacPorts amongst other packagers as well. The files are
# created as part of installing any Perl module.
PERL_BASENAMES = Set.new(%w[perllocal.pod .packlist]).freeze
# Clean a top-level (bin, sbin, lib) directory, recursively, by fixing file
# permissions and removing .la files, unless the files (or parent
# directories) are protected by skip_clean.
@ -93,18 +99,10 @@ class Cleaner
next if path.directory?
if path.extname == ".la"
if path.extname == ".la" || PERL_BASENAMES.include?(path.basename.to_s)
path.unlink
elsif path.symlink?
# Skip it.
elsif path.basename.to_s == "perllocal.pod"
# Both this file & the .packlist one below are completely unnecessary
# to package & causes pointless conflict with other formulae. They are
# removed by Debian, Arch & MacPorts amongst other packagers as well.
# The files are created as part of installing any Perl module.
path.unlink
elsif path.basename.to_s == ".packlist" # Hidden file, not file extension!
path.unlink
else
# Set permissions for executables and non-executables
perms = if executable_path?(path)

View File

@ -168,6 +168,7 @@ module Homebrew
return false if Homebrew::EnvConfig.no_install_cleanup?
unless PERIODIC_CLEAN_FILE.exist?
HOMEBREW_CACHE.mkpath
FileUtils.touch PERIODIC_CLEAN_FILE
return false
end

View File

@ -7,11 +7,14 @@ require "ostruct"
module Homebrew
module CLI
class Args < OpenStruct
extend T::Sig
attr_reader :options_only, :flags_only
# undefine tap to allow --tap argument
undef tap
sig { void }
def initialize
super()
@ -21,7 +24,7 @@ module Homebrew
# Can set these because they will be overwritten by freeze_named_args!
# (whereas other values below will only be overwritten if passed).
self[:named_args] = NamedArgs.new(parent: self)
self[:named] = NamedArgs.new(parent: self)
self[:remaining] = []
end
@ -30,7 +33,7 @@ module Homebrew
end
def freeze_named_args!(named_args)
self[:named_args] = NamedArgs.new(
self[:named] = NamedArgs.new(
*named_args.freeze,
override_spec: spec(nil),
force_bottle: force_bottle?,
@ -50,8 +53,9 @@ module Homebrew
@flags_only = cli_args.select { |a| a.start_with?("--") }.freeze
end
sig { returns(NamedArgs) }
def named
named_args
self[:named]
end
def no_named?
@ -131,6 +135,7 @@ module Homebrew
flag_with_value.delete_prefix(arg_prefix)
end
sig { returns(Context::ContextStruct) }
def context
Context::ContextStruct.new(debug: debug?, quiet: quiet?, verbose: verbose?)
end

View File

@ -3,27 +3,62 @@
module Homebrew
module CLI
class Args < OpenStruct
sig { returns(T.nilable(T::Boolean)) }
def devel?; end
sig { returns(T.nilable(T::Boolean)) }
def HEAD?; end
sig { returns(T.nilable(T::Boolean)) }
def include_test?; end
sig { returns(T.nilable(T::Boolean)) }
def build_bottle?; end
sig { returns(T.nilable(T::Boolean)) }
def build_universal?; end
sig { returns(T.nilable(T::Boolean)) }
def build_from_source?; end
def named_args; end
sig { returns(T.nilable(T::Boolean)) }
def force_bottle?; end
sig { returns(T.nilable(T::Boolean)) }
def debug?; end
sig { returns(T.nilable(T::Boolean)) }
def quiet?; end
sig { returns(T.nilable(T::Boolean)) }
def verbose?; end
sig { returns(T.nilable(T::Boolean)) }
def fetch_HEAD?; end
sig { returns(T.nilable(T::Boolean)) }
def cask?; end
sig { returns(T.nilable(T::Boolean)) }
def dry_run?; end
sig { returns(T.nilable(T::Boolean)) }
def skip_cask_deps?; end
sig { returns(T.nilable(T::Boolean)) }
def greedy?; end
sig { returns(T.nilable(T::Boolean)) }
def force?; end
sig { returns(T.nilable(T::Boolean)) }
def ignore_pinned?; end
sig { returns(T.nilable(T::Boolean)) }
def display_times?; end
sig { returns(T.nilable(T::Boolean)) }
def formula?; end
end
end
end

View File

@ -6,6 +6,7 @@ require "delegate"
require "cask/cask_loader"
require "cli/args"
require "formulary"
require "keg"
require "missing_formula"
module Homebrew
@ -13,7 +14,9 @@ module Homebrew
# Helper class for loading formulae/casks from named arguments.
#
# @api private
class NamedArgs < SimpleDelegator
class NamedArgs < Array
extend T::Sig
def initialize(*args, parent: Args.new, override_spec: nil, force_bottle: false, flags: [])
@args = args
@override_spec = override_spec
@ -39,9 +42,9 @@ module Homebrew
end
end
def to_formulae_to_casks(method: nil, only: nil)
def to_formulae_to_casks(only: nil, method: nil)
@to_formulae_to_casks ||= {}
@to_formulae_to_casks[[method, only]] = to_formulae_and_casks(method: method, only: only)
@to_formulae_to_casks[[method, only]] = to_formulae_and_casks(only: only, method: method)
.partition { |o| o.is_a?(Formula) }
.map(&:freeze).freeze
end
@ -62,14 +65,16 @@ module Homebrew
when nil, :factory
Formulary.factory(name, *spec, force_bottle: @force_bottle, flags: @flags)
when :resolve
Formulary.resolve(name, spec: spec, force_bottle: @force_bottle, flags: @flags)
resolve_formula(name)
when :keg
resolve_keg(name)
else
raise
end
warn_if_cask_conflicts(name, "formula") unless only == :formula
return formula
rescue FormulaUnavailableError => e
rescue NoSuchKegError, FormulaUnavailableError => e
raise e if only == :formula
end
end
@ -97,24 +102,22 @@ module Homebrew
end
def to_resolved_formulae_to_casks(only: nil)
to_formulae_to_casks(method: :resolve, only: only)
to_formulae_to_casks(only: only, method: :resolve)
end
# Convert named arguments to {Tap}, {Formula} or {Cask} objects.
# Convert named arguments to {Formula} or {Cask} objects.
# If both a formula and cask exist with the same name, returns the
# formula and prints a warning unless `only` is specified.
def to_objects(only: nil, method: nil)
@to_objects ||= {}
@to_objects[only] ||= downcased_unique_named.flat_map do |name|
next Tap.fetch(name) if only == :tap || (only.nil? && name.count("/") == 1 && !name.start_with?("./", "/"))
@to_objects[only] ||= downcased_unique_named.map do |name|
load_formula_or_cask(name, only: only, method: method)
end.uniq.freeze
end
private :to_objects
def to_formulae_paths
to_paths(only: :formulae)
to_paths(only: :formula)
end
# Keep existing paths and try to convert others to tap, formula or cask paths.
@ -125,11 +128,11 @@ module Homebrew
@to_paths[only] ||= downcased_unique_named.flat_map do |name|
if File.exist?(name)
Pathname(name)
elsif name.count("/") == 1
elsif name.count("/") == 1 && !name.start_with?("./", "/")
Tap.fetch(name).path
else
next Formulary.path(name) if only == :formulae
next Cask::CaskLoader.path(name) if only == :casks
next Formulary.path(name) if only == :formula
next Cask::CaskLoader.path(name) if only == :cask
formula_path = Formulary.path(name)
cask_path = Cask::CaskLoader.path(name)
@ -144,44 +147,33 @@ module Homebrew
end.uniq.freeze
end
sig { returns(T::Array[Keg]) }
def to_kegs
@to_kegs ||= downcased_unique_named.map do |name|
resolve_keg name
@to_kegs ||= begin
to_formulae_and_casks(only: :formula, method: :keg).freeze
rescue NoSuchKegError => e
if (reason = Homebrew::MissingFormula.suggest_command(name, "uninstall"))
if (reason = Homebrew::MissingFormula.suggest_command(e.name, "uninstall"))
$stderr.puts reason
end
raise e
end.freeze
end
def to_kegs_to_casks
@to_kegs_to_casks ||= begin
kegs = []
casks = []
downcased_unique_named.each do |name|
kegs << resolve_keg(name)
warn_if_cask_conflicts(name, "keg")
rescue NoSuchKegError, FormulaUnavailableError
begin
casks << Cask::CaskLoader.load(name, config: Cask::Config.from_args(@parent))
rescue Cask::CaskUnavailableError
raise "No installed keg or cask with the name \"#{name}\""
end
end
[kegs.freeze, casks.freeze].freeze
end
end
sig { params(only: T.nilable(Symbol)).returns([T::Array[Keg], T::Array[Cask::Cask]]) }
def to_kegs_to_casks(only: nil)
@to_kegs_to_casks ||= to_formulae_and_casks(only: only, method: :keg)
.partition { |o| o.is_a?(Keg) }
.map(&:freeze).freeze
end
sig { returns(T::Array[String]) }
def homebrew_tap_cask_names
downcased_unique_named.grep(HOMEBREW_CASK_TAP_CASK_REGEX)
end
private
sig { returns(T::Array[String]) }
def downcased_unique_named
# Only lowercase names, not paths, bottle filenames or URLs
map do |arg|

View File

@ -14,6 +14,8 @@ OPTION_DESC_WIDTH = 43
module Homebrew
module CLI
class Parser
extend T::Sig
attr_reader :processed_options, :hide_from_man_page
def self.from_cmd_path(cmd_path)
@ -31,78 +33,82 @@ module Homebrew
def self.global_cask_options
[
[:flag, "--appdir=", {
description: "Target location for Applications. " \
"Default: `#{Cask::Config::DEFAULT_DIRS[:appdir]}`",
description: "Target location for Applications " \
"(default: `#{Cask::Config::DEFAULT_DIRS[:appdir]}`).",
}],
[:flag, "--colorpickerdir=", {
description: "Target location for Color Pickers. " \
"Default: `#{Cask::Config::DEFAULT_DIRS[:colorpickerdir]}`",
description: "Target location for Color Pickers " \
"(default: `#{Cask::Config::DEFAULT_DIRS[:colorpickerdir]}`).",
}],
[:flag, "--prefpanedir=", {
description: "Target location for Preference Panes. " \
"Default: `#{Cask::Config::DEFAULT_DIRS[:prefpanedir]}`",
description: "Target location for Preference Panes " \
"(default: `#{Cask::Config::DEFAULT_DIRS[:prefpanedir]}`).",
}],
[:flag, "--qlplugindir=", {
description: "Target location for QuickLook Plugins. " \
"Default: `#{Cask::Config::DEFAULT_DIRS[:qlplugindir]}`",
description: "Target location for QuickLook Plugins " \
"(default: `#{Cask::Config::DEFAULT_DIRS[:qlplugindir]}`).",
}],
[:flag, "--mdimporterdir=", {
description: "Target location for Spotlight Plugins. " \
"Default: `#{Cask::Config::DEFAULT_DIRS[:mdimporterdir]}`",
description: "Target location for Spotlight Plugins " \
"(default: `#{Cask::Config::DEFAULT_DIRS[:mdimporterdir]}`).",
}],
[:flag, "--dictionarydir=", {
description: "Target location for Dictionaries. " \
"Default: `#{Cask::Config::DEFAULT_DIRS[:dictionarydir]}`",
description: "Target location for Dictionaries " \
"(default: `#{Cask::Config::DEFAULT_DIRS[:dictionarydir]}`).",
}],
[:flag, "--fontdir=", {
description: "Target location for Fonts. " \
"Default: `#{Cask::Config::DEFAULT_DIRS[:fontdir]}`",
description: "Target location for Fonts " \
"(default: `#{Cask::Config::DEFAULT_DIRS[:fontdir]}`).",
}],
[:flag, "--servicedir=", {
description: "Target location for Services. " \
"Default: `#{Cask::Config::DEFAULT_DIRS[:servicedir]}`",
description: "Target location for Services " \
"(default: `#{Cask::Config::DEFAULT_DIRS[:servicedir]}`).",
}],
[:flag, "--input_methoddir=", {
description: "Target location for Input Methods. " \
"Default: `#{Cask::Config::DEFAULT_DIRS[:input_methoddir]}`",
description: "Target location for Input Methods " \
"(default: `#{Cask::Config::DEFAULT_DIRS[:input_methoddir]}`).",
}],
[:flag, "--internet_plugindir=", {
description: "Target location for Internet Plugins. " \
"Default: `#{Cask::Config::DEFAULT_DIRS[:internet_plugindir]}`",
description: "Target location for Internet Plugins " \
"(default: `#{Cask::Config::DEFAULT_DIRS[:internet_plugindir]}`).",
}],
[:flag, "--audio_unit_plugindir=", {
description: "Target location for Audio Unit Plugins. " \
"Default: `#{Cask::Config::DEFAULT_DIRS[:audio_unit_plugindir]}`",
description: "Target location for Audio Unit Plugins " \
"(default: `#{Cask::Config::DEFAULT_DIRS[:audio_unit_plugindir]}`).",
}],
[:flag, "--vst_plugindir=", {
description: "Target location for VST Plugins. " \
"Default: `#{Cask::Config::DEFAULT_DIRS[:vst_plugindir]}`",
description: "Target location for VST Plugins " \
"(default: `#{Cask::Config::DEFAULT_DIRS[:vst_plugindir]}`).",
}],
[:flag, "--vst3_plugindir=", {
description: "Target location for VST3 Plugins. " \
"Default: `#{Cask::Config::DEFAULT_DIRS[:vst3_plugindir]}`",
description: "Target location for VST3 Plugins " \
"(default: `#{Cask::Config::DEFAULT_DIRS[:vst3_plugindir]}`).",
}],
[:flag, "--screen_saverdir=", {
description: "Target location for Screen Savers. " \
"Default: `#{Cask::Config::DEFAULT_DIRS[:screen_saverdir]}`",
description: "Target location for Screen Savers " \
"(default: `#{Cask::Config::DEFAULT_DIRS[:screen_saverdir]}`).",
}],
[:comma_array, "--language", {
description: "Set language of the Cask to install. The first matching " \
"language is used, otherwise the default language on the Cask. " \
"The default value is the `language of your system`",
description: "Comma-separated list of language codes to prefer for cask installation. " \
"The first matching language is used, otherwise it reverts to the cask's " \
"default language. The default value is the language of your system.",
}],
]
end
sig { returns(T::Array[[String, String, String]]) }
def self.global_options
[
["-d", "--debug", "Display any debugging information."],
["-q", "--quiet", "Suppress any warnings."],
["-q", "--quiet", "Make some output more quiet."],
["-v", "--verbose", "Make some output more verbose."],
["-h", "--help", "Show this message."],
]
end
# FIXME: Block should be `T.nilable(T.proc.bind(Parser).void)`.
# See https://github.com/sorbet/sorbet/issues/498.
sig { params(block: T.proc.bind(Parser).void).void.checked(:never) }
def initialize(&block)
@parser = OptionParser.new
@ -130,7 +136,7 @@ module Homebrew
switch short, long, description: desc, env: option_to_name(long), method: :on_tail
end
instance_eval(&block) if block_given?
instance_eval(&block) if block
end
def switch(*names, description: nil, env: nil, required_for: nil, depends_on: nil, method: :on)
@ -170,7 +176,7 @@ module Homebrew
def usage_banner_text
@parser.banner
.gsub(/^ - (`[^`]+`)\s+/, "\n- \\1 \n ") # Format `cask` subcommands as MarkDown list.
.gsub(/^ - (`[^`]+`)\s+/, "\n- \\1:\n <br>") # Format `cask` subcommands as Markdown list.
end
def comma_array(name, description: nil)
@ -331,6 +337,7 @@ module Homebrew
end
end
sig { void }
def formula_options
@formula_options = true
end
@ -367,6 +374,7 @@ module Homebrew
end
end
sig { void }
def hide_from_man_page!
@hide_from_man_page = true
end
@ -454,20 +462,24 @@ module Homebrew
end
def check_named_args(args)
min_exception = case @min_named_type
when :cask
Cask::CaskUnspecifiedError
when :formula
FormulaUnspecifiedError
when :formula_or_cask
FormulaOrCaskUnspecifiedError
when :keg
KegUnspecifiedError
else
MinNamedArgumentsError.new(@min_named_args)
exception = if @min_named_args && args.size < @min_named_args
case @min_named_type
when :cask
Cask::CaskUnspecifiedError
when :formula
FormulaUnspecifiedError
when :formula_or_cask
FormulaOrCaskUnspecifiedError
when :keg
KegUnspecifiedError
else
MinNamedArgumentsError.new(@min_named_args)
end
elsif @max_named_args && args.size > @max_named_args
MaxNamedArgumentsError.new(@max_named_args)
end
raise min_exception if @min_named_args && args.size < @min_named_args
raise MaxNamedArgumentsError, @max_named_args if @max_named_args && args.size > @max_named_args
raise exception if exception
end
def process_option(*args)
@ -535,6 +547,9 @@ module Homebrew
end
class MaxNamedArgumentsError < UsageError
extend T::Sig
sig { params(maximum: Integer).void }
def initialize(maximum)
super case maximum
when 0
@ -546,6 +561,9 @@ module Homebrew
end
class MinNamedArgumentsError < UsageError
extend T::Sig
sig { params(minimum: Integer).void }
def initialize(minimum)
super "This command requires at least #{minimum} named #{"argument".pluralize(minimum)}."
end

View File

@ -6,14 +6,17 @@ require "cli/parser"
require "cask/download"
module Homebrew
extend T::Sig
extend Fetch
module_function
sig { returns(CLI::Parser) }
def __cache_args
Homebrew::CLI::Parser.new do
usage_banner <<~EOS
`--cache` [<options>] [<formula|cask>]
`--cache` [<options>] [<formula>|<cask>]
Display Homebrew's download cache. See also `HOMEBREW_CACHE`.

View File

@ -1,9 +1,12 @@
# typed: false
# typed: strict
# frozen_string_literal: true
module Homebrew
extend T::Sig
module_function
sig { returns(CLI::Parser) }
def __caskroom_args
Homebrew::CLI::Parser.new do
usage_banner <<~EOS
@ -17,6 +20,7 @@ module Homebrew
end
end
sig { void }
def __caskroom
args = __caskroom_args.parse

View File

@ -7,8 +7,11 @@ require "utils/shell"
require "cli/parser"
module Homebrew
extend T::Sig
module_function
sig { returns(CLI::Parser) }
def __env_args
Homebrew::CLI::Parser.new do
usage_banner <<~EOS
@ -44,11 +47,10 @@ module Homebrew
Utils::Shell.from_path(args.shell)
end
env_keys = BuildEnvironment.keys(ENV)
if shell.nil?
BuildEnvironment.dump ENV
else
env_keys.each do |key|
BuildEnvironment.keys(ENV).each do |key|
puts Utils::Shell.export_value(key, ENV[key], shell)
end
end

View File

@ -4,8 +4,11 @@
require "cli/parser"
module Homebrew
extend T::Sig
module_function
sig { returns(CLI::Parser) }
def __prefix_args
Homebrew::CLI::Parser.new do
usage_banner <<~EOS

View File

@ -4,8 +4,11 @@
require "cli/parser"
module Homebrew
extend T::Sig
module_function
sig { returns(CLI::Parser) }
def __repository_args
Homebrew::CLI::Parser.new do
usage_banner <<~EOS

View File

@ -4,8 +4,11 @@
require "cli/parser"
module Homebrew
extend T::Sig
module_function
sig { returns(CLI::Parser) }
def __version_args
Homebrew::CLI::Parser.new do
usage_banner <<~EOS
@ -14,6 +17,7 @@ module Homebrew
Print the version numbers of Homebrew, Homebrew/homebrew-core and Homebrew/homebrew-cask
(if tapped) to standard output.
EOS
max_named 0
end
end

View File

@ -4,8 +4,11 @@
require "cli/parser"
module Homebrew
extend T::Sig
module_function
sig { returns(CLI::Parser) }
def analytics_args
Homebrew::CLI::Parser.new do
usage_banner <<~EOS
@ -17,12 +20,13 @@ module Homebrew
`brew analytics` [`state`]:
Display the current state of Homebrew's analytics.
`brew analytics` [`on`|`off`]:
`brew analytics` (`on`|`off`):
Turn Homebrew's analytics on or off respectively.
`brew analytics regenerate-uuid`:
Regenerate the UUID used for Homebrew's analytics.
EOS
max_named 1
end
end

View File

@ -17,7 +17,8 @@ module Homebrew
EOS
switch "-n", "--dry-run",
description: "List what would be uninstalled, but do not actually uninstall anything."
named 0
max_named 0
end
end

View File

@ -5,8 +5,11 @@ require "cleanup"
require "cli/parser"
module Homebrew
extend T::Sig
module_function
sig { returns(CLI::Parser) }
def cleanup_args
Homebrew::CLI::Parser.new do
days = Homebrew::EnvConfig::ENVS[:HOMEBREW_CLEANUP_MAX_AGE_DAYS][:default]

View File

@ -4,8 +4,11 @@
require "cli/parser"
module Homebrew
extend T::Sig
module_function
sig { returns(CLI::Parser) }
def commands_args
Homebrew::CLI::Parser.new do
usage_banner <<~EOS

View File

@ -5,8 +5,11 @@ require "system_config"
require "cli/parser"
module Homebrew
extend T::Sig
module_function
sig { returns(CLI::Parser) }
def config_args
Homebrew::CLI::Parser.new do
usage_banner <<~EOS

View File

@ -8,10 +8,13 @@ require "cask/caskroom"
require "dependencies_helpers"
module Homebrew
extend T::Sig
extend DependenciesHelpers
module_function
sig { returns(CLI::Parser) }
def deps_args
Homebrew::CLI::Parser.new do
usage_banner <<~EOS

View File

@ -7,10 +7,13 @@ require "description_cache_store"
require "cli/parser"
module Homebrew
extend T::Sig
module_function
extend Search
sig { returns(CLI::Parser) }
def desc_args
Homebrew::CLI::Parser.new do
usage_banner <<~EOS

View File

@ -6,8 +6,11 @@ require "cli/parser"
require "cask/caskroom"
module Homebrew
extend T::Sig
module_function
sig { returns(CLI::Parser) }
def doctor_args
Homebrew::CLI::Parser.new do
usage_banner <<~EOS

View File

@ -6,10 +6,13 @@ require "fetch"
require "cli/parser"
module Homebrew
extend T::Sig
extend Fetch
module_function
sig { returns(CLI::Parser) }
def fetch_args
Homebrew::CLI::Parser.new do
usage_banner <<~EOS

View File

@ -9,10 +9,13 @@ require "socket"
require "cli/parser"
module Homebrew
extend T::Sig
extend Install
module_function
sig { returns(CLI::Parser) }
def gist_logs_args
Homebrew::CLI::Parser.new do
usage_banner <<~EOS

View File

@ -4,8 +4,11 @@
require "cli/parser"
module Homebrew
extend T::Sig
module_function
sig { returns(CLI::Parser) }
def home_args
Homebrew::CLI::Parser.new do
usage_banner <<~EOS

View File

@ -13,12 +13,15 @@ require "utils/spdx"
require "deprecate_disable"
module Homebrew
extend T::Sig
module_function
VALID_DAYS = %w[30 90 365].freeze
VALID_FORMULA_CATEGORIES = %w[install install-on-request build-error].freeze
VALID_CATEGORIES = (VALID_FORMULA_CATEGORIES + %w[cask-install os-version]).freeze
sig { returns(CLI::Parser) }
def info_args
Homebrew::CLI::Parser.new do
usage_banner <<~EOS
@ -87,20 +90,23 @@ module Homebrew
raise FormulaOrCaskUnspecifiedError if args.no_named?
exec_browser(*args.named.to_formulae_and_casks.map { |f| github_info(f) })
elsif args.no_named?
print_statistics
else
print_info(args: args)
end
end
def print_statistics
return unless HOMEBREW_CELLAR.exist?
count = Formula.racks.length
puts "#{count} #{"keg".pluralize(count)}, #{HOMEBREW_CELLAR.dup.abv}"
end
def print_analytics(args:)
if args.no_named?
if args.analytics?
Utils::Analytics.output(args: args)
elsif HOMEBREW_CELLAR.exist?
count = Formula.racks.length
puts "#{count} #{"keg".pluralize(count)}, #{HOMEBREW_CELLAR.dup.abv}"
end
Utils::Analytics.output(args: args)
return
end

View File

@ -14,10 +14,13 @@ require "cli/parser"
require "upgrade"
module Homebrew
extend T::Sig
extend Search
module_function
sig { returns(CLI::Parser) }
def install_args
Homebrew::CLI::Parser.new do
usage_banner <<~EOS
@ -82,7 +85,6 @@ module Homebrew
}],
[:switch, "--keep-tmp", {
description: "Retain the temporary files created during installation.",
}],
[:switch, "--build-bottle", {
description: "Prepare the formula for eventual bottling during installation, skipping any " \
@ -122,7 +124,6 @@ module Homebrew
conflicts "--ignore-dependencies", "--only-dependencies"
conflicts "--build-from-source", "--build-bottle", "--force-bottle"
min_named :formula_or_cask
end
end
@ -203,7 +204,7 @@ module Homebrew
EOS
elsif args.only_dependencies?
installed_formulae << f
else
elsif !args.quiet?
opoo <<~EOS
#{f.full_name} #{f.pkg_version} is already installed and up-to-date
To reinstall #{f.pkg_version}, run `brew reinstall #{f.name}`
@ -223,11 +224,14 @@ module Homebrew
msg = "#{f.full_name} #{installed_version} is already installed"
linked_not_equals_installed = f.linked_version != installed_version
if f.linked? && linked_not_equals_installed
msg = <<~EOS
#{msg}
The currently linked version is #{f.linked_version}
You can use `brew switch #{f} #{installed_version}` to link this version.
EOS
msg = if args.quiet?
nil
else
<<~EOS
#{msg}
The currently linked version is #{f.linked_version}
EOS
end
elsif !f.linked? || f.keg_only?
msg = <<~EOS
#{msg}, it's just not linked
@ -237,10 +241,14 @@ module Homebrew
msg = nil
installed_formulae << f
else
msg = <<~EOS
#{msg} and up-to-date
To reinstall #{f.pkg_version}, run `brew reinstall #{f.name}`
EOS
msg = if args.quiet?
nil
else
<<~EOS
#{msg} and up-to-date
To reinstall #{f.pkg_version}, run `brew reinstall #{f.name}`
EOS
end
end
opoo msg if msg
elsif !f.any_version_installed? && old_formula = f.old_installed_formulae.first
@ -250,8 +258,10 @@ module Homebrew
#{msg}, it's just not linked.
You can use `brew link #{old_formula.full_name}` to link this version.
EOS
elsif args.quiet?
msg = nil
end
opoo msg
opoo msg if msg
elsif f.migration_needed? && !args.force?
# Check if the formula we try to install is the same as installed
# but not migrated one. If --force is passed then install anyway.
@ -288,7 +298,7 @@ module Homebrew
Cleanup.install_formula_clean!(f)
end
Upgrade.check_installed_dependents(args: args)
Upgrade.check_installed_dependents(installed_formulae, args: args)
Homebrew.messages.display_messages(display_times: args.display_times?)
rescue FormulaUnreadableError, FormulaClassUnavailableError,
@ -347,21 +357,28 @@ module Homebrew
f.print_tap_action
build_options = f.build
fi = FormulaInstaller.new(f, force_bottle: args.force_bottle?,
include_test_formulae: args.include_test_formulae,
build_from_source_formulae: args.build_from_source_formulae,
debug: args.debug?, quiet: args.quiet?, verbose: args.verbose?)
fi.options = build_options.used_options
fi.env = args.env
fi.force = args.force?
fi.keep_tmp = args.keep_tmp?
fi.ignore_deps = args.ignore_dependencies?
fi.only_deps = args.only_dependencies?
fi.build_bottle = args.build_bottle?
fi.bottle_arch = args.bottle_arch
fi.interactive = args.interactive?
fi.git = args.git?
fi.cc = args.cc
fi = FormulaInstaller.new(
f,
**{
options: build_options.used_options,
build_bottle: args.build_bottle?,
force_bottle: args.force_bottle?,
bottle_arch: args.bottle_arch,
ignore_deps: args.ignore_dependencies?,
only_deps: args.only_dependencies?,
include_test_formulae: args.include_test_formulae,
build_from_source_formulae: args.build_from_source_formulae,
env: args.env,
cc: args.cc,
git: args.git?,
interactive: args.interactive?,
keep_tmp: args.keep_tmp?,
force: args.force?,
debug: args.debug?,
quiet: args.quiet?,
verbose: args.verbose?,
}.compact,
)
fi.prelude
fi.fetch
fi.install

View File

@ -5,8 +5,11 @@ require "formula"
require "cli/parser"
module Homebrew
extend T::Sig
module_function
sig { returns(CLI::Parser) }
def leaves_args
Homebrew::CLI::Parser.new do
usage_banner <<~EOS

View File

@ -7,8 +7,11 @@ require "cli/parser"
require "unlink"
module Homebrew
extend T::Sig
module_function
sig { returns(CLI::Parser) }
def link_args
Homebrew::CLI::Parser.new do
usage_banner <<~EOS

View File

@ -7,19 +7,22 @@ require "cli/parser"
require "cask/cmd"
module Homebrew
extend T::Sig
module_function
sig { returns(CLI::Parser) }
def list_args
Homebrew::CLI::Parser.new do
usage_banner <<~EOS
`list`, `ls` [<options>] [<formula|cask>]
`list`, `ls` [<options>] [<formula>|<cask>]
List all installed formulae or casks
List all installed formulae and casks.
If <formula> is provided, summarise the paths within its current keg.
EOS
switch "--formula", "--formulae",
description: "List only formulae. `This is the default action on non TTY.`"
description: "List only formulae. This is the default when output is not to a terminal."
switch "--cask", "--casks",
description: "List only casks, or <cask> if provided."
switch "--unbrewed",

View File

@ -5,8 +5,11 @@ require "formula"
require "cli/parser"
module Homebrew
extend T::Sig
module_function
sig { returns(CLI::Parser) }
def log_args
Homebrew::CLI::Parser.new do
usage_banner <<~EOS
@ -25,8 +28,9 @@ module Homebrew
description: "Print only one commit."
flag "-n", "--max-count=",
description: "Print only a specified number of commits."
max_named 1
conflicts "-1", "--max-count"
max_named 1
end
end

View File

@ -5,8 +5,11 @@ require "migrator"
require "cli/parser"
module Homebrew
extend T::Sig
module_function
sig { returns(CLI::Parser) }
def migrate_args
Homebrew::CLI::Parser.new do
usage_banner <<~EOS

View File

@ -7,8 +7,11 @@ require "diagnostic"
require "cli/parser"
module Homebrew
extend T::Sig
module_function
sig { returns(CLI::Parser) }
def missing_args
Homebrew::CLI::Parser.new do
usage_banner <<~EOS

View File

@ -7,8 +7,11 @@ require "cli/parser"
require "commands"
module Homebrew
extend T::Sig
module_function
sig { returns(CLI::Parser) }
def options_args
Homebrew::CLI::Parser.new do
usage_banner <<~EOS

View File

@ -8,8 +8,11 @@ require "cask/cmd"
require "cask/caskroom"
module Homebrew
extend T::Sig
module_function
sig { returns(CLI::Parser) }
def outdated_args
Homebrew::CLI::Parser.new do
usage_banner <<~EOS
@ -23,9 +26,9 @@ module Homebrew
switch "-v", "--verbose",
description: "Include detailed version information."
switch "--formula",
description: "Only output outdated formulae."
description: "List only outdated formulae."
switch "--cask",
description: "Only output outdated casks."
description: "List only outdated casks."
flag "--json",
description: "Print output in JSON format. There are two versions: v1 and v2. " \
"v1 is deprecated and is currently the default if no version is specified. " \

View File

@ -5,8 +5,11 @@ require "formula"
require "cli/parser"
module Homebrew
extend T::Sig
module_function
sig { returns(CLI::Parser) }
def pin_args
Homebrew::CLI::Parser.new do
usage_banner <<~EOS

View File

@ -6,8 +6,11 @@ require "formula_installer"
require "cli/parser"
module Homebrew
extend T::Sig
module_function
sig { returns(CLI::Parser) }
def postinstall_args
Homebrew::CLI::Parser.new do
usage_banner <<~EOS
@ -25,7 +28,7 @@ module Homebrew
args.named.to_resolved_formulae.each do |f|
ohai "Postinstalling #{f}"
fi = FormulaInstaller.new(f, debug: args.debug?, quiet: args.quiet?, verbose: args.verbose?)
fi = FormulaInstaller.new(f, **{ debug: args.debug?, quiet: args.quiet?, verbose: args.verbose? }.compact)
fi.post_install
end
end

View File

@ -5,8 +5,11 @@ require "readall"
require "cli/parser"
module Homebrew
extend T::Sig
module_function
sig { returns(CLI::Parser) }
def readall_args
Homebrew::CLI::Parser.new do
usage_banner <<~EOS

View File

@ -14,8 +14,11 @@ require "cask/macos"
require "upgrade"
module Homebrew
extend T::Sig
module_function
sig { returns(CLI::Parser) }
def reinstall_args
Homebrew::CLI::Parser.new do
usage_banner <<~EOS
@ -42,8 +45,8 @@ module Homebrew
}],
[:switch, "-i", "--interactive", {
description: "Download and patch <formula>, then open a shell. This allows the user to " \
"run `./configure --help` and otherwise determine how to turn the software " \
"package into a Homebrew package.",
"run `./configure --help` and otherwise determine how to turn the software " \
"package into a Homebrew package.",
}],
[:switch, "--force-bottle", {
description: "Install from a bottle if it exists for the current or newest version of " \
@ -72,7 +75,6 @@ module Homebrew
cask_options
conflicts "--build-from-source", "--force-bottle"
min_named :formula_or_cask
end
end
@ -100,7 +102,7 @@ module Homebrew
Cleanup.install_formula_clean!(f)
end
Upgrade.check_installed_dependents(args: args)
Upgrade.check_installed_dependents(formulae, args: args)
if casks.any?
Cask::Cmd::Reinstall.reinstall_casks(

View File

@ -8,6 +8,8 @@ require "cli/parser"
require "search"
module Homebrew
extend T::Sig
module_function
extend Search
@ -25,6 +27,7 @@ module Homebrew
},
}.freeze
sig { returns(CLI::Parser) }
def search_args
Homebrew::CLI::Parser.new do
usage_banner <<~EOS
@ -47,7 +50,7 @@ module Homebrew
description: "Search for formulae with a description matching <text> and casks with "\
"a name matching <text>."
switch "--pull-request",
description: "Search for GitHub pull requests for <text>."
description: "Search for GitHub pull requests containing <text>."
package_manager_switches = PACKAGE_MANAGERS.keys.map { |name| "--#{name}" }
package_manager_switches.each do |s|

View File

@ -6,8 +6,11 @@ require "keg"
require "cli/parser"
module Homebrew
extend T::Sig
module_function
sig { returns(CLI::Parser) }
def switch_args
Homebrew::CLI::Parser.new do
usage_banner <<~EOS

Some files were not shown because too many files have changed in this diff Show More