Stricter handling of CLI args

This commit is contained in:
Bo Anderson 2021-03-18 14:46:48 +00:00
parent 3c3bf1c74d
commit 8e98ce69f3
No known key found for this signature in database
GPG Key ID: 3DB94E204E137D65
19 changed files with 349 additions and 167 deletions

View File

@ -27,7 +27,7 @@ class Build
@formula.build = BuildOptions.new(options, formula.options) @formula.build = BuildOptions.new(options, formula.options)
@args = args @args = args
if args.ignore_deps? if args.ignore_dependencies?
@deps = [] @deps = []
@reqs = [] @reqs = []
else else

View File

@ -13,15 +13,15 @@ module Cask
def self.parser def self.parser
super do super do
switch "--download", switch "--[no-]download",
description: "Audit the downloaded file" description: "Audit the downloaded file"
switch "--[no-]appcast", switch "--[no-]appcast",
description: "Audit the appcast" description: "Audit the appcast"
switch "--token-conflicts", switch "--[no-]token-conflicts",
description: "Audit for token conflicts" description: "Audit for token conflicts"
switch "--strict", switch "--[no-]strict",
description: "Run additional, stricter style checks" description: "Run additional, stricter style checks"
switch "--online", switch "--[no-]online",
description: "Run additional, slower style checks that require a network connection" description: "Run additional, slower style checks that require a network connection"
switch "--new-cask", switch "--new-cask",
description: "Run various additional style checks to determine if a new cask is eligible " \ description: "Run various additional style checks to determine if a new cask is eligible " \

View File

@ -22,6 +22,7 @@ module Homebrew
@processed_options = [] @processed_options = []
@options_only = [] @options_only = []
@flags_only = [] @flags_only = []
@cask_options = false
# Can set these because they will be overwritten by freeze_named_args! # Can set these because they will be overwritten by freeze_named_args!
# (whereas other values below will only be overwritten if passed). # (whereas other values below will only be overwritten if passed).
@ -33,12 +34,13 @@ module Homebrew
self[:remaining] = remaining_args.freeze self[:remaining] = remaining_args.freeze
end end
def freeze_named_args!(named_args) def freeze_named_args!(named_args, cask_options:)
self[:named] = NamedArgs.new( self[:named] = NamedArgs.new(
*named_args.freeze, *named_args.freeze,
override_spec: spec(nil), override_spec: spec(nil),
force_bottle: force_bottle?, force_bottle: self[:force_bottle?],
flags: flags_only, flags: flags_only,
cask_options: cask_options,
parent: self, parent: self,
) )
end end
@ -64,12 +66,8 @@ module Homebrew
named.blank? named.blank?
end end
def build_stable?
!HEAD?
end
def build_from_source_formulae def build_from_source_formulae
if build_from_source? || HEAD? || build_bottle? if build_from_source? || self[:HEAD?] || self[:build_bottle?]
named.to_formulae_and_casks.select { |f| f.is_a?(Formula) }.map(&:full_name) named.to_formulae_and_casks.select { |f| f.is_a?(Formula) }.map(&:full_name)
else else
[] []
@ -129,12 +127,28 @@ module Homebrew
end end
def spec(default = :stable) def spec(default = :stable)
if HEAD? if self[:HEAD?]
:head :head
else else
default default
end end
end end
def respond_to_missing?(*)
!frozen?
end
def method_missing(method_name, *args)
return_value = super
# Once we are frozen, verify any arg method calls are already defined in the table.
# The default OpenStruct behaviour is to return nil for anything unknown.
if frozen? && args.empty? && !@table.key?(method_name)
raise NoMethodError, "CLI arg for `#{method_name}` is not declared for this command"
end
return_value
end
end end
end end
end end

View File

@ -3,145 +3,145 @@
module Homebrew module Homebrew
module CLI module CLI
class Args < OpenStruct class Args < OpenStruct
sig { returns(T.nilable(T::Boolean)) } sig { returns(T::Boolean) }
def HEAD?; end def HEAD?; end
sig { returns(T.nilable(T::Boolean)) } sig { returns(T::Boolean) }
def include_test?; end def include_test?; end
sig { returns(T.nilable(T::Boolean)) } sig { returns(T::Boolean) }
def build_bottle?; end def build_bottle?; end
sig { returns(T.nilable(T::Boolean)) } sig { returns(T::Boolean) }
def build_universal?; end def build_universal?; end
sig { returns(T.nilable(T::Boolean)) } sig { returns(T::Boolean) }
def build_from_source?; end def build_from_source?; end
sig { returns(T.nilable(T::Boolean)) } sig { returns(T::Boolean) }
def force_bottle?; end def force_bottle?; end
sig { returns(T.nilable(T::Boolean)) } sig { returns(T::Boolean) }
def newer_only?; end def newer_only?; end
sig { returns(T.nilable(T::Boolean)) } sig { returns(T::Boolean) }
def full_name?; end def full_name?; end
sig { returns(T.nilable(T::Boolean)) } sig { returns(T::Boolean) }
def json?; end def json?; end
sig { returns(T.nilable(T::Boolean)) } sig { returns(T::Boolean) }
def debug?; end def debug?; end
sig { returns(T.nilable(T::Boolean)) } sig { returns(T::Boolean) }
def quiet?; end def quiet?; end
sig { returns(T.nilable(T::Boolean)) } sig { returns(T::Boolean) }
def verbose?; end def verbose?; end
sig { returns(T.nilable(T::Boolean)) } sig { returns(T::Boolean) }
def fetch_HEAD?; end def fetch_HEAD?; end
sig { returns(T.nilable(T::Boolean)) } sig { returns(T::Boolean) }
def cask?; end def cask?; end
sig { returns(T.nilable(T::Boolean)) } sig { returns(T::Boolean) }
def dry_run?; end def dry_run?; end
sig { returns(T.nilable(T::Boolean)) } sig { returns(T::Boolean) }
def skip_cask_deps?; end def skip_cask_deps?; end
sig { returns(T.nilable(T::Boolean)) } sig { returns(T::Boolean) }
def greedy?; end def greedy?; end
sig { returns(T.nilable(T::Boolean)) } sig { returns(T::Boolean) }
def force?; end def force?; end
sig { returns(T.nilable(T::Boolean)) } sig { returns(T::Boolean) }
def ignore_pinned?; end def ignore_pinned?; end
sig { returns(T.nilable(T::Boolean)) } sig { returns(T::Boolean) }
def display_times?; end def display_times?; end
sig { returns(T.nilable(T::Boolean)) } sig { returns(T::Boolean) }
def formula?; end def formula?; end
sig { returns(T.nilable(T::Boolean)) } sig { returns(T::Boolean) }
def zap?; end def zap?; end
sig { returns(T.nilable(T::Boolean)) } sig { returns(T::Boolean) }
def ignore_dependencies?; end def ignore_dependencies?; end
sig { returns(T.nilable(T::Boolean)) } sig { returns(T::Boolean) }
def aliases?; end def aliases?; end
sig { returns(T.nilable(T::Boolean)) } sig { returns(T::Boolean) }
def fix?; end def fix?; end
sig { returns(T.nilable(T::Boolean)) } sig { returns(T::Boolean) }
def keep_tmp?; end def keep_tmp?; end
sig { returns(T.nilable(T::Boolean)) } sig { returns(T::Boolean) }
def overwrite?; end def overwrite?; end
sig { returns(T.nilable(T::Boolean)) } sig { returns(T::Boolean) }
def silent?; end def silent?; end
sig { returns(T.nilable(T::Boolean)) } sig { returns(T::Boolean) }
def repair?; end def repair?; end
sig { returns(T.nilable(T::Boolean)) } sig { returns(T::Boolean) }
def prune_prefix?; end def prune_prefix?; end
sig { returns(T.nilable(T::Boolean)) } sig { returns(T::Boolean) }
def upload?; end def upload?; end
sig { returns(T.nilable(T::Boolean)) } sig { returns(T::Boolean) }
def total?; end def total?; end
sig { returns(T.nilable(T::Boolean)) } sig { returns(T::Boolean) }
def dependents?; end def dependents?; end
sig { returns(T.nilable(T::Boolean)) } sig { returns(T::Boolean) }
def installed?; end def installed?; end
sig { returns(T.nilable(T::Boolean)) } sig { returns(T::Boolean) }
def all?; end def all?; end
sig { returns(T.nilable(T::Boolean)) } sig { returns(T::Boolean) }
def full?; end def full?; end
sig { returns(T.nilable(T::Boolean)) } sig { returns(T::Boolean) }
def list_pinned?; end def list_pinned?; end
sig { returns(T.nilable(T::Boolean)) } sig { returns(T::Boolean) }
def display_cop_names?; end def display_cop_names?; end
sig { returns(T.nilable(T::Boolean)) } sig { returns(T::Boolean) }
def syntax?; end def syntax?; end
sig { returns(T.nilable(T::Boolean)) } sig { returns(T::Boolean) }
def ignore_non_pypi_packages?; end def ignore_non_pypi_packages?; end
sig { returns(T.nilable(T::Boolean)) } sig { returns(T::Boolean) }
def test?; end def test?; end
sig { returns(T.nilable(T::Boolean)) } sig { returns(T::Boolean) }
def reverse?; end def reverse?; end
sig { returns(T.nilable(T::Boolean)) } sig { returns(T::Boolean) }
def print_only?; end def print_only?; end
sig { returns(T.nilable(T::Boolean)) } sig { returns(T::Boolean) }
def markdown?; end def markdown?; end
sig { returns(T.nilable(T::Boolean)) } sig { returns(T::Boolean) }
def reset_cache?; end def reset_cache?; end
sig { returns(T.nilable(T::Boolean)) } sig { returns(T::Boolean) }
def major?; end def major?; end
sig { returns(T.nilable(T::Boolean)) } sig { returns(T::Boolean) }
def minor?; end def minor?; end
sig { returns(T.nilable(String)) } sig { returns(T.nilable(String)) }
@ -162,13 +162,13 @@ module Homebrew
sig { returns(T.nilable(String)) } sig { returns(T.nilable(String)) }
def name; end def name; end
sig { returns(T.nilable(T::Boolean)) } sig { returns(T::Boolean) }
def no_publish?; end def no_publish?; end
sig { returns(T.nilable(T::Boolean)) } sig { returns(T::Boolean) }
def shallow?; end def shallow?; end
sig { returns(T.nilable(T::Boolean)) } sig { returns(T::Boolean) }
def fail_if_not_changed?; end def fail_if_not_changed?; end
sig { returns(T.nilable(String)) } sig { returns(T.nilable(String)) }
@ -230,7 +230,7 @@ module Homebrew
sig { returns(T.nilable(T::Array[String])) } sig { returns(T.nilable(T::Array[String])) }
def update; end def update; end
sig { returns(T.nilable(T::Boolean)) } sig { returns(T::Boolean) }
def s?; end def s?; end
sig { returns(T.nilable(String)) } sig { returns(T.nilable(String)) }

View File

@ -13,7 +13,7 @@ module Homebrew
class NamedArgs < Array class NamedArgs < Array
extend T::Sig extend T::Sig
def initialize(*args, parent: Args.new, override_spec: nil, force_bottle: false, flags: []) def initialize(*args, parent: Args.new, override_spec: nil, force_bottle: false, flags: [], cask_options: false)
require "cask/cask" require "cask/cask"
require "cask/cask_loader" require "cask/cask_loader"
require "formulary" require "formulary"
@ -24,6 +24,7 @@ module Homebrew
@override_spec = override_spec @override_spec = override_spec
@force_bottle = force_bottle @force_bottle = force_bottle
@flags = flags @flags = flags
@cask_options = cask_options
@parent = parent @parent = parent
super(@args) super(@args)
@ -110,7 +111,8 @@ module Homebrew
if only != :formula if only != :formula
begin begin
cask = Cask::CaskLoader.load(name, config: Cask::Config.from_args(@parent)) config = Cask::Config.from_args(@parent) if @cask_options
cask = Cask::CaskLoader.load(name, config: config)
if unreadable_error.present? if unreadable_error.present?
onoe <<~EOS onoe <<~EOS

View File

@ -136,6 +136,7 @@ module Homebrew
@usage_banner = nil @usage_banner = nil
@hide_from_man_page = false @hide_from_man_page = false
@formula_options = false @formula_options = false
@cask_options = false
self.class.global_options.each do |short, long, desc| self.class.global_options.each do |short, long, desc|
switch short, long, description: desc, env: option_to_name(long), method: :on_tail switch short, long, description: desc, env: option_to_name(long), method: :on_tail
@ -328,9 +329,10 @@ module Homebrew
check_named_args(named_args) check_named_args(named_args)
end end
@args.freeze_named_args!(named_args) @args.freeze_named_args!(named_args, cask_options: @cask_options)
@args.freeze_remaining_args!(non_options.empty? ? remaining : [*remaining, "--", non_options]) @args.freeze_remaining_args!(non_options.empty? ? remaining : [*remaining, "--", non_options])
@args.freeze_processed_options!(@processed_options) @args.freeze_processed_options!(@processed_options)
@args.freeze
@args_parsed = true @args_parsed = true
@ -357,6 +359,7 @@ module Homebrew
send(method, *args, **options) send(method, *args, **options)
conflicts "--formula", args.last conflicts "--formula", args.last
end end
@cask_options = true
end end
sig { void } sig { void }
@ -518,7 +521,11 @@ module Homebrew
def disable_switch(*names) def disable_switch(*names)
names.each do |name| names.each do |name|
@args.delete_field("#{option_to_name(name)}?") @args["#{option_to_name(name)}?"] = if name.start_with?("--[no-]")
nil
else
false
end
end end
end end
@ -617,6 +624,14 @@ module Homebrew
@processed_options.reject! { |existing| existing.second == option.long.first } if option.long.first.present? @processed_options.reject! { |existing| existing.second == option.long.first } if option.long.first.present?
@processed_options << [option.short.first, option.long.first, option.arg, option.desc.first] @processed_options << [option.short.first, option.long.first, option.arg, option.desc.first]
if type == :switch
disable_switch(*args)
else
args.each do |name|
@args[option_to_name(name)] = nil
end
end
return if self.class.global_options.include? [option.short.first, option.long.first, option.desc.first] return if self.class.global_options.include? [option.short.first, option.long.first, option.desc.first]
@non_global_processed_options << [option.long.first || option.short.first, type] @non_global_processed_options << [option.long.first || option.short.first, type]

View File

@ -34,8 +34,8 @@ module Homebrew
def __env def __env
args = __env_args.parse args = __env_args.parse
ENV.activate_extensions!(env: args.env) ENV.activate_extensions!
ENV.deps = args.named.to_formulae if superenv?(args.env) ENV.deps = args.named.to_formulae if superenv?(nil)
ENV.setup_build_environment ENV.setup_build_environment
shell = if args.plain? shell = if args.plain?

View File

@ -316,7 +316,19 @@ module Homebrew
Cleanup.install_formula_clean!(f) Cleanup.install_formula_clean!(f)
end end
Upgrade.check_installed_dependents(installed_formulae, args: args) Upgrade.check_installed_dependents(
installed_formulae,
flags: args.flags_only,
installed_on_request: args.named.present?,
force_bottle: args.force_bottle?,
build_from_source_formulae: args.build_from_source_formulae,
interactive: args.interactive?,
keep_tmp: args.keep_tmp?,
force: args.force?,
debug: args.debug?,
quiet: args.quiet?,
verbose: args.verbose?,
)
Homebrew.messages.display_messages(display_times: args.display_times?) Homebrew.messages.display_messages(display_times: args.display_times?)
rescue FormulaUnreadableError, FormulaClassUnavailableError, rescue FormulaUnreadableError, FormulaClassUnavailableError,

View File

@ -171,7 +171,7 @@ module Homebrew
if args.named.present? if args.named.present?
select_outdated(args.named.to_casks, args: args) select_outdated(args.named.to_casks, args: args)
else else
select_outdated(Cask::Caskroom.casks(config: Cask::Config.from_args(args)), args: args) select_outdated(Cask::Caskroom.casks, args: args)
end end
end end
@ -180,7 +180,7 @@ module Homebrew
if formulae.blank? && casks.blank? if formulae.blank? && casks.blank?
formulae = Formula.installed formulae = Formula.installed
casks = Cask::Caskroom.casks(config: Cask::Config.from_args(args)) casks = Cask::Caskroom.casks
end end
[select_outdated(formulae, args: args).sort, select_outdated(casks, args: args)] [select_outdated(formulae, args: args).sort, select_outdated(casks, args: args)]

View File

@ -88,17 +88,41 @@ module Homebrew
formulae, casks = args.named.to_formulae_and_casks(method: :resolve) formulae, casks = args.named.to_formulae_and_casks(method: :resolve)
.partition { |o| o.is_a?(Formula) } .partition { |o| o.is_a?(Formula) }
formulae.each do |f| formulae.each do |formula|
if f.pinned? if formula.pinned?
onoe "#{f.full_name} is pinned. You must unpin it to reinstall." onoe "#{formula.full_name} is pinned. You must unpin it to reinstall."
next next
end end
Migrator.migrate_if_needed(f, force: args.force?) Migrator.migrate_if_needed(formula, force: args.force?)
reinstall_formula(f, args: args) reinstall_formula(
Cleanup.install_formula_clean!(f) formula,
flags: args.flags_only,
installed_on_request: args.named.present?,
force_bottle: args.force_bottle?,
build_from_source_formulae: args.build_from_source_formulae,
interactive: args.interactive?,
keep_tmp: args.keep_tmp?,
force: args.force?,
debug: args.debug?,
quiet: args.quiet?,
verbose: args.verbose?,
)
Cleanup.install_formula_clean!(formula)
end end
Upgrade.check_installed_dependents(formulae, args: args) Upgrade.check_installed_dependents(
formulae,
flags: args.flags_only,
installed_on_request: args.named.present?,
force_bottle: args.force_bottle?,
build_from_source_formulae: args.build_from_source_formulae,
interactive: args.interactive?,
keep_tmp: args.keep_tmp?,
force: args.force?,
debug: args.debug?,
quiet: args.quiet?,
verbose: args.verbose?,
)
if casks.any? if casks.any?
Cask::Cmd::Reinstall.reinstall_casks( Cask::Cmd::Reinstall.reinstall_casks(

View File

@ -172,9 +172,34 @@ module Homebrew
puts formulae_upgrades.join("\n") puts formulae_upgrades.join("\n")
end end
Upgrade.upgrade_formulae(formulae_to_install, args: args) Upgrade.upgrade_formulae(
formulae_to_install,
flags: args.flags_only,
installed_on_request: args.named.present?,
force_bottle: args.force_bottle?,
build_from_source_formulae: args.build_from_source_formulae,
interactive: args.interactive?,
keep_tmp: args.keep_tmp?,
force: args.force?,
debug: args.debug?,
quiet: args.quiet?,
verbose: args.verbose?,
)
Upgrade.check_installed_dependents(formulae_to_install, args: args) Upgrade.check_installed_dependents(
formulae_to_install,
flags: args.flags_only,
dry_run: args.dry_run?,
installed_on_request: args.named.present?,
force_bottle: args.force_bottle?,
build_from_source_formulae: args.build_from_source_formulae,
interactive: args.interactive?,
keep_tmp: args.keep_tmp?,
force: args.force?,
debug: args.debug?,
quiet: args.quiet?,
verbose: args.verbose?,
)
true true
end end

View File

@ -179,7 +179,6 @@ module Homebrew
tap_audit_exceptions: f.tap&.audit_exceptions, tap_audit_exceptions: f.tap&.audit_exceptions,
style_offenses: style_offenses ? style_offenses.for_path(f.path) : nil, style_offenses: style_offenses ? style_offenses.for_path(f.path) : nil,
display_cop_names: args.display_cop_names?, display_cop_names: args.display_cop_names?,
build_stable: args.build_stable?,
}.compact }.compact
fa = FormulaAuditor.new(f, **options) fa = FormulaAuditor.new(f, **options)

View File

@ -21,6 +21,8 @@ module Homebrew
*Example:* `brew install jruby && brew test jruby` *Example:* `brew install jruby && brew test jruby`
EOS EOS
switch "-f", "--force",
description: "Test formulae even if they are unlinked."
switch "--HEAD", switch "--HEAD",
description: "Test the head version of a formula." description: "Test the head version of a formula."
switch "--keep-tmp", switch "--keep-tmp",

View File

@ -21,7 +21,6 @@ module Homebrew
@new_formula = options[:new_formula] && !@versioned_formula @new_formula = options[:new_formula] && !@versioned_formula
@strict = options[:strict] @strict = options[:strict]
@online = options[:online] @online = options[:online]
@build_stable = options[:build_stable]
@git = options[:git] @git = options[:git]
@display_cop_names = options[:display_cop_names] @display_cop_names = options[:display_cop_names]
@only = options[:only] @only = options[:only]

View File

@ -8,55 +8,61 @@ require "messages"
module Homebrew module Homebrew
module_function module_function
def reinstall_formula(f, args:, build_from_source: false) def reinstall_formula(
return if args.dry_run? formula,
flags:,
if f.opt_prefix.directory? installed_on_request: false,
keg = Keg.new(f.opt_prefix.resolved_path) force_bottle: false,
build_from_source_formulae: [],
interactive: false,
keep_tmp: false,
force: false,
debug: false,
quiet: false,
verbose: false
)
if formula.opt_prefix.directory?
keg = Keg.new(formula.opt_prefix.resolved_path)
tab = Tab.for_keg(keg) tab = Tab.for_keg(keg)
keg_had_linked_opt = true keg_had_linked_opt = true
keg_was_linked = keg.linked? keg_was_linked = keg.linked?
backup keg backup keg
end end
build_options = BuildOptions.new(Options.create(args.flags_only), f.options) build_options = BuildOptions.new(Options.create(flags), formula.options)
options = build_options.used_options options = build_options.used_options
options |= f.build.used_options options |= formula.build.used_options
options &= f.options options &= formula.options
build_from_source_formulae = args.build_from_source_formulae
build_from_source_formulae << f.full_name if build_from_source
fi = FormulaInstaller.new( fi = FormulaInstaller.new(
f, formula,
**{ **{
options: options, options: options,
link_keg: keg_had_linked_opt ? keg_was_linked : nil, link_keg: keg_had_linked_opt ? keg_was_linked : nil,
installed_as_dependency: tab&.installed_as_dependency, installed_as_dependency: tab&.installed_as_dependency,
installed_on_request: tab&.installed_on_request, installed_on_request: installed_on_request || tab&.installed_on_request,
build_bottle: args.build_bottle? || tab&.built_bottle?, build_bottle: tab&.built_bottle?,
force_bottle: args.force_bottle?, force_bottle: force_bottle,
build_from_source_formulae: build_from_source_formulae, build_from_source_formulae: build_from_source_formulae,
git: args.git?, interactive: interactive,
interactive: args.interactive?, keep_tmp: keep_tmp,
keep_tmp: args.keep_tmp?, force: force,
force: args.force?, debug: debug,
debug: args.debug?, quiet: quiet,
quiet: args.quiet?, verbose: verbose,
verbose: args.verbose?,
}.compact, }.compact,
) )
fi.prelude fi.prelude
fi.fetch fi.fetch
oh1 "Reinstalling #{Formatter.identifier(f.full_name)} #{options.to_a.join " "}" oh1 "Reinstalling #{Formatter.identifier(formula.full_name)} #{options.to_a.join " "}"
fi.install fi.install
fi.finish fi.finish
rescue FormulaInstallationAlreadyAttemptedError rescue FormulaInstallationAlreadyAttemptedError
nil nil
rescue Exception # rubocop:disable Lint/RescueException rescue Exception # rubocop:disable Lint/RescueException
ignore_interrupts { restore_backup(keg, keg_was_linked, verbose: args.verbose?) } ignore_interrupts { restore_backup(keg, keg_was_linked, verbose: verbose) }
raise raise
else else
begin begin

View File

@ -21,7 +21,7 @@ describe Cask::Cmd::Audit, :cask do
expect(Cask::CaskLoader).to receive(:load).with(cask_token, any_args).and_return(cask) expect(Cask::CaskLoader).to receive(:load).with(cask_token, any_args).and_return(cask)
expect(Cask::Auditor).to receive(:audit) expect(Cask::Auditor).to receive(:audit)
.with(cask, quarantine: true, any_named_args: true) .with(cask, audit_new_cask: false, quarantine: true, any_named_args: true)
.and_return(result) .and_return(result)
described_class.run(cask_token) described_class.run(cask_token)
@ -31,7 +31,7 @@ describe Cask::Cmd::Audit, :cask do
it "does not pass anything if no flags are specified" do it "does not pass anything if no flags are specified" do
allow(Cask::CaskLoader).to receive(:load).and_return(cask) allow(Cask::CaskLoader).to receive(:load).and_return(cask)
expect(Cask::Auditor).to receive(:audit) expect(Cask::Auditor).to receive(:audit)
.with(cask, quarantine: true, any_named_args: true) .with(cask, audit_new_cask: false, quarantine: true, any_named_args: true)
.and_return(result) .and_return(result)
described_class.run("casktoken") described_class.run("casktoken")
@ -40,7 +40,7 @@ describe Cask::Cmd::Audit, :cask do
it "passes `audit_download` if the `--download` flag is specified" do it "passes `audit_download` if the `--download` flag is specified" do
allow(Cask::CaskLoader).to receive(:load).and_return(cask) allow(Cask::CaskLoader).to receive(:load).and_return(cask)
expect(Cask::Auditor).to receive(:audit) expect(Cask::Auditor).to receive(:audit)
.with(cask, audit_download: true, quarantine: true, any_named_args: true) .with(cask, audit_download: true, audit_new_cask: false, quarantine: true, any_named_args: true)
.and_return(result) .and_return(result)
described_class.run("casktoken", "--download") described_class.run("casktoken", "--download")
@ -49,7 +49,7 @@ describe Cask::Cmd::Audit, :cask do
it "passes `audit_token_conflicts` if the `--token-conflicts` flag is specified" do it "passes `audit_token_conflicts` if the `--token-conflicts` flag is specified" do
allow(Cask::CaskLoader).to receive(:load).and_return(cask) allow(Cask::CaskLoader).to receive(:load).and_return(cask)
expect(Cask::Auditor).to receive(:audit) expect(Cask::Auditor).to receive(:audit)
.with(cask, audit_token_conflicts: true, quarantine: true, any_named_args: true) .with(cask, audit_token_conflicts: true, audit_new_cask: false, quarantine: true, any_named_args: true)
.and_return(result) .and_return(result)
described_class.run("casktoken", "--token-conflicts") described_class.run("casktoken", "--token-conflicts")
@ -58,7 +58,7 @@ describe Cask::Cmd::Audit, :cask do
it "passes `audit_strict` if the `--strict` flag is specified" do it "passes `audit_strict` if the `--strict` flag is specified" do
allow(Cask::CaskLoader).to receive(:load).and_return(cask) allow(Cask::CaskLoader).to receive(:load).and_return(cask)
expect(Cask::Auditor).to receive(:audit) expect(Cask::Auditor).to receive(:audit)
.with(cask, audit_strict: true, quarantine: true, any_named_args: true) .with(cask, audit_strict: true, audit_new_cask: false, quarantine: true, any_named_args: true)
.and_return(result) .and_return(result)
described_class.run("casktoken", "--strict") described_class.run("casktoken", "--strict")
@ -67,7 +67,7 @@ describe Cask::Cmd::Audit, :cask do
it "passes `audit_online` if the `--online` flag is specified" do it "passes `audit_online` if the `--online` flag is specified" do
allow(Cask::CaskLoader).to receive(:load).and_return(cask) allow(Cask::CaskLoader).to receive(:load).and_return(cask)
expect(Cask::Auditor).to receive(:audit) expect(Cask::Auditor).to receive(:audit)
.with(cask, audit_online: true, quarantine: true, any_named_args: true) .with(cask, audit_online: true, audit_new_cask: false, quarantine: true, any_named_args: true)
.and_return(result) .and_return(result)
described_class.run("casktoken", "--online") described_class.run("casktoken", "--online")
@ -85,7 +85,7 @@ describe Cask::Cmd::Audit, :cask do
it "passes `language` if the `--language` flag is specified" do it "passes `language` if the `--language` flag is specified" do
allow(Cask::CaskLoader).to receive(:load).and_return(cask) allow(Cask::CaskLoader).to receive(:load).and_return(cask)
expect(Cask::Auditor).to receive(:audit) expect(Cask::Auditor).to receive(:audit)
.with(cask, quarantine: true, language: ["de-AT"], any_named_args: true) .with(cask, audit_new_cask: false, quarantine: true, language: ["de-AT"], any_named_args: true)
.and_return(result) .and_return(result)
described_class.run("casktoken", "--language=de-AT") described_class.run("casktoken", "--language=de-AT")
@ -94,7 +94,7 @@ describe Cask::Cmd::Audit, :cask do
it "passes `quarantine` if the `--no-quarantine` flag is specified" do it "passes `quarantine` if the `--no-quarantine` flag is specified" do
allow(Cask::CaskLoader).to receive(:load).and_return(cask) allow(Cask::CaskLoader).to receive(:load).and_return(cask)
expect(Cask::Auditor).to receive(:audit) expect(Cask::Auditor).to receive(:audit)
.with(cask, quarantine: false, any_named_args: true) .with(cask, audit_new_cask: false, quarantine: false, any_named_args: true)
.and_return(result) .and_return(result)
described_class.run("casktoken", "--no-quarantine") described_class.run("casktoken", "--no-quarantine")
@ -105,7 +105,7 @@ describe Cask::Cmd::Audit, :cask do
allow(Cask::CaskLoader).to receive(:load).and_return(cask) allow(Cask::CaskLoader).to receive(:load).and_return(cask)
expect(Cask::Auditor).to receive(:audit) expect(Cask::Auditor).to receive(:audit)
.with(cask, quarantine: false, any_named_args: true) .with(cask, audit_new_cask: false, quarantine: false, any_named_args: true)
.and_return(result) .and_return(result)
described_class.run("casktoken") described_class.run("casktoken")

View File

@ -11,6 +11,7 @@ end
def setup_unredable_cask(name) def setup_unredable_cask(name)
error = Cask::CaskUnreadableError.new(name, "testing") error = Cask::CaskUnreadableError.new(name, "testing")
allow(Cask::CaskLoader).to receive(:load).with(name).and_raise(error) allow(Cask::CaskLoader).to receive(:load).with(name).and_raise(error)
allow(Cask::CaskLoader).to receive(:load).with(name, config: nil).and_raise(error)
config = instance_double(Cask::Config) config = instance_double(Cask::Config)
allow(Cask::Config).to receive(:from_args).and_return(config) allow(Cask::Config).to receive(:from_args).and_return(config)

View File

@ -23,6 +23,11 @@ describe Homebrew::CLI::Parser do
end end
} }
it "does not create no_positive?" do
args = parser.parse(["--no-positive"])
expect { args.no_positive? }.to raise_error(NoMethodError)
end
it "sets the positive name to false if the negative flag is passed" do it "sets the positive name to false if the negative flag is passed" do
args = parser.parse(["--no-positive"]) args = parser.parse(["--no-positive"])
expect(args).not_to be_positive expect(args).not_to be_positive
@ -32,6 +37,11 @@ describe Homebrew::CLI::Parser do
args = parser.parse(["--positive"]) args = parser.parse(["--positive"])
expect(args).to be_positive expect(args).to be_positive
end end
it "does not set the positive name if the positive flag is not passed" do
args = parser.parse([])
expect(args.positive?).to be nil
end
end end
context "when using negative options" do context "when using negative options" do
@ -43,7 +53,7 @@ describe Homebrew::CLI::Parser do
it "does not set the positive name" do it "does not set the positive name" do
args = parser.parse(["--no-positive"]) args = parser.parse(["--no-positive"])
expect(args.positive?).to be nil expect { args.positive? }.to raise_error(NoMethodError)
end end
it "fails when using the positive name" do it "fails when using the positive name" do
@ -82,10 +92,10 @@ describe Homebrew::CLI::Parser do
expect(args.named).to eq %w[unnamed args] expect(args.named).to eq %w[unnamed args]
end end
it "parses a single option and checks other options to be nil" do it "parses a single option and checks other options to be false" do
args = parser.parse(["--verbose"]) args = parser.parse(["--verbose"])
expect(args).to be_verbose expect(args).to be_verbose
expect(args.more_verbose?).to be nil expect(args.more_verbose?).to be false
end end
it "raises an exception and outputs help text when an invalid option is passed" do it "raises an exception and outputs help text when an invalid option is passed" do
@ -227,7 +237,7 @@ describe Homebrew::CLI::Parser do
allow(Homebrew::EnvConfig).to receive(:switch_a?).and_return(true) allow(Homebrew::EnvConfig).to receive(:switch_a?).and_return(true)
allow(Homebrew::EnvConfig).to receive(:switch_b?).and_return(false) allow(Homebrew::EnvConfig).to receive(:switch_b?).and_return(false)
args = parser.parse(["--switch-b"]) args = parser.parse(["--switch-b"])
expect(args.switch_a).to be_falsy expect(args.switch_a?).to be false
expect(args).to be_switch_b expect(args).to be_switch_b
end end

View File

@ -14,9 +14,20 @@ module Homebrew
module Upgrade module Upgrade
module_function module_function
def upgrade_formulae(formulae_to_install, args:) def upgrade_formulae(
formulae_to_install,
flags:,
installed_on_request: false,
force_bottle: false,
build_from_source_formulae: [],
interactive: false,
keep_tmp: false,
force: false,
debug: false,
quiet: false,
verbose: false
)
return if formulae_to_install.empty? return if formulae_to_install.empty?
return if args.dry_run?
# Sort keg-only before non-keg-only formulae to avoid any needless conflicts # Sort keg-only before non-keg-only formulae to avoid any needless conflicts
# with outdated, non-keg-only versions of formulae being upgraded. # with outdated, non-keg-only versions of formulae being upgraded.
@ -30,66 +41,90 @@ module Homebrew
end end
end end
formulae_to_install.each do |f| formulae_to_install.each do |formula|
Migrator.migrate_if_needed(f, force: args.force?) Migrator.migrate_if_needed(formula, force: force)
begin begin
upgrade_formula(f, args: args) upgrade_formula(
Cleanup.install_formula_clean!(f) formula,
flags: flags,
installed_on_request: installed_on_request,
force_bottle: force_bottle,
build_from_source_formulae: build_from_source_formulae,
interactive: interactive,
keep_tmp: keep_tmp,
force: force,
debug: debug,
quiet: quiet,
verbose: verbose,
)
Cleanup.install_formula_clean!(formula)
rescue UnsatisfiedRequirements => e rescue UnsatisfiedRequirements => e
ofail "#{f}: #{e}" ofail "#{formula}: #{e}"
end end
end end
end end
def upgrade_formula(f, args:) def upgrade_formula(
return if args.dry_run? formula,
flags:,
if f.opt_prefix.directory? installed_on_request: false,
keg = Keg.new(f.opt_prefix.resolved_path) force_bottle: false,
build_from_source_formulae: [],
interactive: false,
keep_tmp: false,
force: false,
debug: false,
quiet: false,
verbose: false
)
if formula.opt_prefix.directory?
keg = Keg.new(formula.opt_prefix.resolved_path)
keg_had_linked_opt = true keg_had_linked_opt = true
keg_was_linked = keg.linked? keg_was_linked = keg.linked?
end end
formulae_maybe_with_kegs = [f] + f.old_installed_formulae formulae_maybe_with_kegs = [formula] + formula.old_installed_formulae
outdated_kegs = formulae_maybe_with_kegs.map(&:linked_keg) outdated_kegs = formulae_maybe_with_kegs.map(&:linked_keg)
.select(&:directory?) .select(&:directory?)
.map { |k| Keg.new(k.resolved_path) } .map { |k| Keg.new(k.resolved_path) }
linked_kegs = outdated_kegs.select(&:linked?) linked_kegs = outdated_kegs.select(&:linked?)
if f.opt_prefix.directory? if formula.opt_prefix.directory?
keg = Keg.new(f.opt_prefix.resolved_path) keg = Keg.new(formula.opt_prefix.resolved_path)
tab = Tab.for_keg(keg) tab = Tab.for_keg(keg)
end end
build_options = BuildOptions.new(Options.create(args.flags_only), f.options) build_options = BuildOptions.new(Options.create(flags), formula.options)
options = build_options.used_options options = build_options.used_options
options |= f.build.used_options options |= formula.build.used_options
options &= f.options options &= formula.options
fi = FormulaInstaller.new( fi = FormulaInstaller.new(
f, formula,
**{ **{
options: options, options: options,
link_keg: keg_had_linked_opt ? keg_was_linked : nil, link_keg: keg_had_linked_opt ? keg_was_linked : nil,
installed_as_dependency: tab&.installed_as_dependency, installed_as_dependency: tab&.installed_as_dependency,
installed_on_request: args.named.present? || tab&.installed_on_request, installed_on_request: installed_on_request || tab&.installed_on_request,
build_bottle: args.build_bottle? || tab&.built_bottle?, build_bottle: tab&.built_bottle?,
force_bottle: args.force_bottle?, force_bottle: force_bottle,
build_from_source_formulae: args.build_from_source_formulae, build_from_source_formulae: build_from_source_formulae,
keep_tmp: args.keep_tmp?, interactive: interactive,
force: args.force?, keep_tmp: keep_tmp,
debug: args.debug?, force: force,
quiet: args.quiet?, debug: debug,
verbose: args.verbose?, quiet: quiet,
verbose: verbose,
}.compact, }.compact,
) )
upgrade_version = if f.optlinked? upgrade_version = if formula.optlinked?
"#{Keg.new(f.opt_prefix).version} -> #{f.pkg_version}" "#{Keg.new(formula.opt_prefix).version} -> #{formula.pkg_version}"
else else
"-> #{f.pkg_version}" "-> #{formula.pkg_version}"
end end
oh1 "Upgrading #{Formatter.identifier(f.full_specified_name)} #{upgrade_version} #{fi.options.to_a.join(" ")}" oh1 "Upgrading #{Formatter.identifier(formula.full_specified_name)} " \
"#{upgrade_version} #{fi.options.to_a.join(" ")}"
fi.prelude fi.prelude
fi.fetch fi.fetch
@ -108,13 +143,13 @@ module Homebrew
rescue CannotInstallFormulaError, DownloadError => e rescue CannotInstallFormulaError, DownloadError => e
ofail e ofail e
rescue BuildError => e rescue BuildError => e
e.dump(verbose: args.verbose?) e.dump(verbose: verbose)
puts puts
Homebrew.failed = true Homebrew.failed = true
ensure ensure
# restore previous installation state if build failed # restore previous installation state if build failed
begin begin
linked_kegs.each(&:link) unless f.latest_version_installed? linked_kegs.each(&:link) unless formula.latest_version_installed?
rescue rescue
nil nil
end end
@ -136,10 +171,23 @@ module Homebrew
end end
end end
def check_installed_dependents(formulae, args:) def check_installed_dependents(
formulae,
flags:,
dry_run: false,
installed_on_request: false,
force_bottle: false,
build_from_source_formulae: [],
interactive: false,
keep_tmp: false,
force: false,
debug: false,
quiet: false,
verbose: false
)
return if Homebrew::EnvConfig.no_installed_dependents_check? return if Homebrew::EnvConfig.no_installed_dependents_check?
installed_formulae = args.dry_run? ? formulae : FormulaInstaller.installed.to_a installed_formulae = dry_run ? formulae : FormulaInstaller.installed.to_a
return if installed_formulae.empty? return if installed_formulae.empty?
already_broken_dependents = check_broken_dependents(installed_formulae) already_broken_dependents = check_broken_dependents(installed_formulae)
@ -150,7 +198,7 @@ module Homebrew
.select(&:outdated?) .select(&:outdated?)
return if outdated_dependents.blank? && already_broken_dependents.blank? return if outdated_dependents.blank? && already_broken_dependents.blank?
outdated_dependents -= installed_formulae if args.dry_run? outdated_dependents -= installed_formulae if dry_run
upgradeable_dependents = upgradeable_dependents =
outdated_dependents.reject(&:pinned?) outdated_dependents.reject(&:pinned?)
@ -169,10 +217,10 @@ module Homebrew
# Print the upgradable dependents. # Print the upgradable dependents.
if upgradeable_dependents.blank? if upgradeable_dependents.blank?
ohai "No outdated dependents to upgrade!" unless args.dry_run? ohai "No outdated dependents to upgrade!" unless dry_run
else else
plural = "dependent".pluralize(upgradeable_dependents.count) plural = "dependent".pluralize(upgradeable_dependents.count)
verb = args.dry_run? ? "Would upgrade" : "Upgrading" verb = dry_run ? "Would upgrade" : "Upgrading"
ohai "#{verb} #{upgradeable_dependents.count} #{plural}:" ohai "#{verb} #{upgradeable_dependents.count} #{plural}:"
formulae_upgrades = upgradeable_dependents.map do |f| formulae_upgrades = upgradeable_dependents.map do |f|
name = f.full_specified_name name = f.full_specified_name
@ -185,16 +233,30 @@ module Homebrew
puts formulae_upgrades.join(", ") puts formulae_upgrades.join(", ")
end end
upgrade_formulae(upgradeable_dependents, args: args) unless dry_run
upgrade_formulae(
upgradeable_dependents,
flags: flags,
installed_on_request: installed_on_request,
force_bottle: force_bottle,
build_from_source_formulae: build_from_source_formulae,
interactive: interactive,
keep_tmp: keep_tmp,
force: force,
debug: debug,
quiet: quiet,
verbose: verbose,
)
end
# Update installed formulae after upgrading # Update installed formulae after upgrading
installed_formulae = FormulaInstaller.installed.to_a installed_formulae = FormulaInstaller.installed.to_a
# Assess the dependents tree again now we've upgraded. # Assess the dependents tree again now we've upgraded.
oh1 "Checking for dependents of upgraded formulae..." unless args.dry_run? oh1 "Checking for dependents of upgraded formulae..." unless dry_run
broken_dependents = check_broken_dependents(installed_formulae) broken_dependents = check_broken_dependents(installed_formulae)
if broken_dependents.blank? if broken_dependents.blank?
if args.dry_run? if dry_run
ohai "No currently broken dependents found!" ohai "No currently broken dependents found!"
opoo "If they are broken by the upgrade they will also be upgraded or reinstalled." opoo "If they are broken by the upgrade they will also be upgraded or reinstalled."
else else
@ -233,10 +295,21 @@ module Homebrew
.join(", ") .join(", ")
end end
return if args.dry_run? return if dry_run
reinstallable_broken_dependents.each do |f| reinstallable_broken_dependents.each do |formula|
Homebrew.reinstall_formula(f, build_from_source: true, args: args) Homebrew.reinstall_formula(
formula,
flags: flags,
force_bottle: force_bottle,
build_from_source_formulae: build_from_source_formulae + [formula],
interactive: interactive,
keep_tmp: keep_tmp,
force: force,
debug: debug,
quiet: quiet,
verbose: verbose,
)
rescue FormulaInstallationAlreadyAttemptedError rescue FormulaInstallationAlreadyAttemptedError
# We already attempted to reinstall f as part of the dependency tree of # We already attempted to reinstall f as part of the dependency tree of
# another formula. In that case, don't generate an error, just move on. # another formula. In that case, don't generate an error, just move on.
@ -244,7 +317,7 @@ module Homebrew
rescue CannotInstallFormulaError, DownloadError => e rescue CannotInstallFormulaError, DownloadError => e
ofail e ofail e
rescue BuildError => e rescue BuildError => e
e.dump(verbose: args.verbose?) e.dump(verbose: verbose)
puts puts
Homebrew.failed = true Homebrew.failed = true
end end