Add CLI::Options DSL.

This commit is contained in:
Markus Reiter 2017-05-21 00:15:56 +02:00
parent debe4540e4
commit df1864ee43
35 changed files with 377 additions and 308 deletions

View File

@ -34,6 +34,9 @@ Metrics/ModuleLength:
- 'cask/lib/hbc/macos.rb' - 'cask/lib/hbc/macos.rb'
- 'cask/lib/hbc/utils.rb' - 'cask/lib/hbc/utils.rb'
Metrics/ParameterLists:
CountKeywordArgs: false
# so many of these in formulae but none in here # so many of these in formulae but none in here
Style/GuardClause: Style/GuardClause:
Enabled: true Enabled: true

View File

@ -54,12 +54,12 @@ module Hbc
Zap, Zap,
].freeze ].freeze
def self.for_cask(cask, command: SystemCommand, force: false) def self.for_cask(cask, options = {})
odebug "Determining which artifacts are present in Cask #{cask}" odebug "Determining which artifacts are present in Cask #{cask}"
TYPES TYPES
.select { |klass| klass.me?(cask) } .select { |klass| klass.me?(cask) }
.map { |klass| klass.new(cask, command: command, force: force) } .map { |klass| klass.new(cask, options) }
end end
end end
end end

View File

@ -65,10 +65,19 @@ module Hbc
{} {}
end end
def initialize(cask, command: SystemCommand, force: false) def verbose?
@verbose
end
def force?
@force
end
def initialize(cask, command: SystemCommand, force: false, verbose: false)
@cask = cask @cask = cask
@command = command @command = command
@force = force @force = force
@verbose = verbose
end end
end end
end end

View File

@ -20,7 +20,7 @@ module Hbc
def move def move
if Utils.path_occupied?(target) if Utils.path_occupied?(target)
message = "It seems there is already #{self.class.artifact_english_article} #{self.class.artifact_english_name} at '#{target}'" message = "It seems there is already #{self.class.artifact_english_article} #{self.class.artifact_english_name} at '#{target}'"
raise CaskError, "#{message}." unless force raise CaskError, "#{message}." unless force?
opoo "#{message}; overwriting." opoo "#{message}; overwriting."
delete delete
end end

View File

@ -48,7 +48,7 @@ module Hbc
"-pkg", source, "-pkg", source,
"-target", "/" "-target", "/"
] ]
args << "-verboseR" if CLI.verbose? args << "-verboseR" if verbose?
args << "-allowUntrusted" if pkg_install_opts :allow_untrusted args << "-allowUntrusted" if pkg_install_opts :allow_untrusted
with_choices_file do |choices_path| with_choices_file do |choices_path|
args << "-applyChoiceChangesXML" << choices_path if choices_path args << "-applyChoiceChangesXML" << choices_path if choices_path

View File

@ -173,7 +173,7 @@ module Hbc
unless executable_path.exist? unless executable_path.exist?
message = "uninstall script #{executable} does not exist" message = "uninstall script #{executable} does not exist"
raise CaskError, "#{message}." unless force raise CaskError, "#{message}." unless force?
opoo "#{message}, skipping." opoo "#{message}, skipping."
return return
end end

View File

@ -2,6 +2,7 @@ require "optparse"
require "shellwords" require "shellwords"
require "extend/optparse" require "extend/optparse"
require "hbc/cli/options"
require "hbc/cli/abstract_command" require "hbc/cli/abstract_command"
require "hbc/cli/audit" require "hbc/cli/audit"
@ -44,49 +45,36 @@ module Hbc
"remove" => "uninstall", "remove" => "uninstall",
"abv" => "info", "abv" => "info",
"dr" => "doctor", "dr" => "doctor",
# aliases from Homebrew that we don't (yet) support
# 'ln' => 'link',
# 'configure' => 'diy',
# '--repo' => '--repository',
# 'environment' => '--env',
# '-c1' => '--config',
}.freeze }.freeze
OPTIONS = { include Options
"--caskroom=" => :caskroom=,
"--appdir=" => :appdir=,
"--colorpickerdir=" => :colorpickerdir=,
"--prefpanedir=" => :prefpanedir=,
"--qlplugindir=" => :qlplugindir=,
"--dictionarydir=" => :dictionarydir=,
"--fontdir=" => :fontdir=,
"--servicedir=" => :servicedir=,
"--input_methoddir=" => :input_methoddir=,
"--internet_plugindir=" => :internet_plugindir=,
"--audio_unit_plugindir=" => :audio_unit_plugindir=,
"--vst_plugindir=" => :vst_plugindir=,
"--vst3_plugindir=" => :vst3_plugindir=,
"--screen_saverdir=" => :screen_saverdir=,
}.freeze
FLAGS = { option "--caskroom=PATH", ->(value) { Hbc.caskroom = value }
["--[no-]binaries", :binaries] => true, option "--appdir=PATH", ->(value) { Hbc.appdir = value }
["--verbose", :verbose] => false, option "--colorpickerdir=PATH", ->(value) { Hbc.colorpickerdir = value }
["--outdated", :outdated] => false, option "--prefpanedir=PATH", ->(value) { Hbc.prefpanedir = value }
["--help", :help] => false, option "--qlplugindir=PATH", ->(value) { Hbc.qlplugindir = value }
}.freeze option "--dictionarydir=PATH", ->(value) { Hbc.dictionarydir = value }
option "--fontdir=PATH", ->(value) { Hbc.fontdir = value }
option "--servicedir=PATH", ->(value) { Hbc.servicedir = value }
option "--input_methoddir=PATH", ->(value) { Hbc.input_methoddir = value }
option "--internet_plugindir=PATH", ->(value) { Hbc.internet_plugindir = value }
option "--audio_unit_plugindir=PATH", ->(value) { Hbc.audio_unit_plugindir = value }
option "--vst_plugindir=PATH", ->(value) { Hbc.vst_plugindir = value }
option "--vst3_plugindir=PATH", ->(value) { Hbc.vst3_plugindir = value }
option "--screen_saverdir=PATH", ->(value) { Hbc.screen_saverdir = value }
option "--binarydir=PATH", ->(*) { opoo(<<-EOS.undent) }
Option --binarydir is obsolete!
Homebrew-Cask now uses the same location as your Homebrew installation for executable links.
EOS
FLAGS.each do |(_, method), default_value| option "--help", :help, false
instance_variable_set(:"@#{method}", default_value)
define_singleton_method(:"#{method}=") do |arg| # handled in OS::Mac
instance_variable_set(:"@#{method}", arg) option "--language a,b,c", ->(*) { raise OptionParser::InvalidOption }
end
define_singleton_method(:"#{method}?") do # override default handling of --version
instance_variable_get(:"@#{method}") option "--version", ->(*) { raise OptionParser::InvalidOption }
end
end
def self.command_classes def self.command_classes
@command_classes ||= constants.map(&method(:const_get)) @command_classes ||= constants.map(&method(:const_get))
@ -99,10 +87,10 @@ module Hbc
@commands ||= command_classes.map(&:command_name) @commands ||= command_classes.map(&:command_name)
end end
def self.lookup_command(command_string) def self.lookup_command(command_name)
@lookup ||= Hash[commands.zip(command_classes)] @lookup ||= Hash[commands.zip(command_classes)]
command_string = ALIASES.fetch(command_string, command_string) command_name = ALIASES.fetch(command_name, command_name)
@lookup.fetch(command_string, command_string) @lookup.fetch(command_name, command_name)
end end
def self.should_init?(command) def self.should_init?(command)
@ -145,18 +133,26 @@ module Hbc
end end
end end
def self.process(arguments) def self.run(*args)
new(*args).run
end
def initialize(*args)
@args = process_options(*args)
end
def run
command_name, *args = *@args
command = help? ? "help" : self.class.lookup_command(command_name)
unless ENV["MACOS_VERSION"].nil? unless ENV["MACOS_VERSION"].nil?
MacOS.full_version = ENV["MACOS_VERSION"] MacOS.full_version = ENV["MACOS_VERSION"]
end end
command_string, *rest = *arguments
rest = process_options(rest)
command = help? ? "help" : lookup_command(command_string)
Hbc.default_tap.install unless Hbc.default_tap.installed? Hbc.default_tap.install unless Hbc.default_tap.installed?
Hbc.init if should_init?(command) Hbc.init if self.class.should_init?(command)
run_command(command, *rest) self.class.run_command(command, *args)
rescue CaskError, CaskSha256MismatchError, ArgumentError => e rescue CaskError, CaskSha256MismatchError, ArgumentError, OptionParser::InvalidOption => e
msg = e.message msg = e.message
msg << e.backtrace.join("\n") if ARGV.debug? msg << e.backtrace.join("\n") if ARGV.debug?
onoe msg onoe msg
@ -188,59 +184,25 @@ module Hbc
list.sort list.sort
end end
def self.parser def process_options(*args)
# If you modify these arguments, please update USAGE.md
@parser ||= OptionParser.new do |opts|
opts.on("--language STRING") do
# handled in OS::Mac
end
OPTIONS.each do |option, method|
opts.on("#{option}" "PATH", Pathname) do |path|
Hbc.public_send(method, path)
end
end
opts.on("--binarydir=PATH") do
opoo <<-EOS.undent
Option --binarydir is obsolete!
Homebrew-Cask now uses the same location as your Homebrew installation for executable links.
EOS
end
FLAGS.keys.each do |flag, method|
opts.on(flag) do |bool|
send(:"#{method}=", bool)
end
end
opts.on("--version") do
raise OptionParser::InvalidOption # override default handling of --version
end
end
end
def self.process_options(args)
all_args = Shellwords.shellsplit(ENV["HOMEBREW_CASK_OPTS"] || "") + args all_args = Shellwords.shellsplit(ENV["HOMEBREW_CASK_OPTS"] || "") + args
remaining = []
until all_args.empty? non_options = []
if idx = all_args.index("--")
non_options += all_args.drop(idx)
all_args = all_args.first(idx)
end
remaining = all_args.select do |arg|
begin begin
head = all_args.shift !process_arguments([arg]).empty?
remaining.concat(parser.parse([head])) rescue OptionParser::InvalidOption, OptionParser::MissingArgument, OptionParser::AmbiguousOption
rescue OptionParser::InvalidOption true
remaining << head
retry
rescue OptionParser::MissingArgument
raise ArgumentError, "The option '#{head}' requires an argument."
rescue OptionParser::AmbiguousOption
raise ArgumentError, "There is more than one possible option that starts with '#{head}'."
end end
end end
# for compat with Homebrew, not certain if this is desirable remaining + non_options
self.verbose = true if ARGV.verbose?
remaining
end end
class NullCommand class NullCommand

View File

@ -5,8 +5,13 @@ module Hbc
"--#{super}" "--#{super}"
end end
def initialize(*)
super
return if args.empty?
raise ArgumentError, "#{self.class.command_name} does not take arguments."
end
def run def run
raise ArgumentError, "#{self.class.command_name} does not take arguments." unless @args.empty?
puts Hbc.full_version puts Hbc.full_version
end end

View File

@ -1,6 +1,15 @@
require_relative "options"
module Hbc module Hbc
class CLI class CLI
class AbstractCommand class AbstractCommand
include Options
option "--[no-]binaries", :binaries, true
option "--debug", :debug, false
option "--verbose", :verbose, false
option "--outdated", :outdated_only, false
def self.command_name def self.command_name
@command_name ||= name.sub(/^.*:/, "").gsub(/(.)([A-Z])/, '\1_\2').downcase @command_name ||= name.sub(/^.*:/, "").gsub(/(.)([A-Z])/, '\1_\2').downcase
end end
@ -29,8 +38,11 @@ module Hbc
new(*args).run new(*args).run
end end
attr_accessor :args
private :args=
def initialize(*args) def initialize(*args)
@args = args @args = process_arguments(*args)
end end
end end
end end

View File

@ -1,21 +1,18 @@
module Hbc module Hbc
class CLI class CLI
class Audit < AbstractCommand class Audit < AbstractCommand
option "--download", :download, false
option "--token-conflicts", :token_conflicts, false
def self.help def self.help
"verifies installability of Casks" "verifies installability of Casks"
end end
def initialize(*args, auditor: Auditor)
@args = args
@auditor = auditor
end
def run def run
failed_casks = [] casks_to_audit = args.empty? ? Hbc.all : args.map(&CaskLoader.public_method(:load))
casks_to_audit.each do |cask| failed_casks = casks_to_audit.reject do |cask|
next if audit(cask) audit(cask)
failed_casks << cask
end end
return if failed_casks.empty? return if failed_casks.empty?
@ -24,28 +21,7 @@ module Hbc
def audit(cask) def audit(cask)
odebug "Auditing Cask #{cask}" odebug "Auditing Cask #{cask}"
@auditor.audit(cask, audit_download: audit_download?, Auditor.audit(cask, audit_download: download?, check_token_conflicts: token_conflicts?)
check_token_conflicts: check_token_conflicts?)
end
def audit_download?
@args.include?("--download")
end
def check_token_conflicts?
@args.include?("--token-conflicts")
end
def casks_to_audit
if cask_tokens.empty?
Hbc.all
else
cask_tokens.map { |token| CaskLoader.load(token) }
end
end
def cask_tokens
@cask_tokens ||= self.class.cask_tokens_from(@args)
end end
def self.needs_init? def self.needs_init?

View File

@ -1,14 +1,17 @@
module Hbc module Hbc
class CLI class CLI
class Cat < AbstractCommand class Cat < AbstractCommand
def initialize(*)
super
raise CaskUnspecifiedError if args.empty?
end
def run def run
cask_tokens = self.class.cask_tokens_from(@args) args.each do |cask_token|
raise CaskUnspecifiedError if cask_tokens.empty? cask_path = CaskLoader.path(cask_token)
# only respects the first argument raise CaskUnavailableError, cask_token.to_s unless cask_path.exist?
cask_token = cask_tokens.first.sub(/\.rb$/i, "") puts File.open(cask_path, &:read)
cask_path = CaskLoader.path(cask_token) end
raise CaskUnavailableError, cask_token.to_s unless cask_path.exist?
puts File.open(cask_path, &:read)
end end
def self.help def self.help

View File

@ -12,12 +12,11 @@ module Hbc
true true
end end
attr_reader :cache_location, :outdated_only attr_reader :cache_location
def initialize(*args, cache_location: Hbc.cache, outdated_only: CLI.outdated?) def initialize(*args, cache_location: Hbc.cache)
@args = args super(*args)
@cache_location = Pathname.new(cache_location) @cache_location = Pathname.new(cache_location)
@outdated_only = outdated_only
end end
def run def run
@ -32,7 +31,7 @@ module Hbc
end end
def outdated?(file) def outdated?(file)
outdated_only && file && file.stat.mtime > OUTDATED_TIMESTAMP outdated_only? && file && file.stat.mtime > OUTDATED_TIMESTAMP
end end
def incomplete?(file) def incomplete?(file)
@ -54,7 +53,7 @@ module Hbc
def remove_cache_files(*tokens) def remove_cache_files(*tokens)
message = "Removing cached downloads" message = "Removing cached downloads"
message.concat " for #{tokens.join(", ")}" unless tokens.empty? message.concat " for #{tokens.join(", ")}" unless tokens.empty?
message.concat " older than #{OUTDATED_DAYS} days old" if outdated_only message.concat " older than #{OUTDATED_DAYS} days old" if outdated_only?
ohai message ohai message
deletable_cache_files = if tokens.empty? deletable_cache_files = if tokens.empty?

View File

@ -1,15 +1,18 @@
module Hbc module Hbc
class CLI class CLI
class Create < AbstractCommand class Create < AbstractCommand
def run def initialize(*)
cask_tokens = self.class.cask_tokens_from(@args) super
raise CaskUnspecifiedError if cask_tokens.empty? raise CaskUnspecifiedError if args.empty?
cask_token = cask_tokens.first.sub(/\.rb$/i, "") raise ArgumentError, "Only one Cask can be created at a time." if args.count > 1
cask_path = CaskLoader.path(cask_token) end
odebug "Creating Cask #{cask_token}"
def run
cask_token = args.first
cask_path = CaskLoader.path(cask_token)
raise CaskAlreadyCreatedError, cask_token if cask_path.exist? raise CaskAlreadyCreatedError, cask_token if cask_path.exist?
odebug "Creating Cask #{cask_token}"
File.open(cask_path, "w") do |f| File.open(cask_path, "w") do |f|
f.write self.class.template(cask_token) f.write self.class.template(cask_token)
end end

View File

@ -1,14 +1,20 @@
module Hbc module Hbc
class CLI class CLI
class Doctor < AbstractCommand class Doctor < AbstractCommand
def self.run def initialize(*)
super
return if args.empty?
raise ArgumentError, "#{self.class.command_name} does not take arguments."
end
def run
ohai "Homebrew-Cask Version", Hbc.full_version ohai "Homebrew-Cask Version", Hbc.full_version
ohai "Homebrew-Cask Install Location", render_install_location ohai "Homebrew-Cask Install Location", self.class.render_install_location
ohai "Homebrew-Cask Staging Location", render_staging_location(Hbc.caskroom) ohai "Homebrew-Cask Staging Location", self.class.render_staging_location(Hbc.caskroom)
ohai "Homebrew-Cask Cached Downloads", render_cached_downloads ohai "Homebrew-Cask Cached Downloads", self.class.render_cached_downloads
ohai "Homebrew-Cask Taps:" ohai "Homebrew-Cask Taps:"
puts render_taps(Hbc.default_tap, *alt_taps) puts self.class.render_taps(Hbc.default_tap, *self.class.alt_taps)
ohai "Contents of $LOAD_PATH", render_load_path($LOAD_PATH) ohai "Contents of $LOAD_PATH", self.class.render_load_path($LOAD_PATH)
ohai "Environment Variables" ohai "Environment Variables"
environment_variables = [ environment_variables = [
@ -24,7 +30,7 @@ module Hbc
"SHELL", "SHELL",
] ]
(locale_variables + environment_variables).sort.each(&method(:render_env_var)) (self.class.locale_variables + environment_variables).sort.each(&self.class.method(:render_env_var))
end end
def self.locale_variables def self.locale_variables
@ -45,7 +51,7 @@ module Hbc
end end
def self.alt_taps def self.alt_taps
Tap.select { |t| t.cask_dir && t != Hbc.default_tap } Tap.select { |t| t.cask_dir.exist? && t != Hbc.default_tap }
end end
def self.cask_count_for_tap(tap) def self.cask_count_for_tap(tap)

View File

@ -1,16 +1,21 @@
module Hbc module Hbc
class CLI class CLI
class Edit < AbstractCommand class Edit < AbstractCommand
def initialize(*)
super
raise CaskUnspecifiedError if args.empty?
raise ArgumentError, "Only one Cask can be created at a time." if args.count > 1
end
def run def run
cask_tokens = self.class.cask_tokens_from(@args) cask_token = args.first
raise CaskUnspecifiedError if cask_tokens.empty?
# only respects the first argument
cask_token = cask_tokens.first.sub(/\.rb$/i, "")
cask_path = CaskLoader.path(cask_token) cask_path = CaskLoader.path(cask_token)
odebug "Opening editor for Cask #{cask_token}"
unless cask_path.exist? unless cask_path.exist?
raise CaskUnavailableError, %Q(#{cask_token}, run "brew cask create #{cask_token}" to create a new Cask) raise CaskUnavailableError, %Q(#{cask_token}, run "brew cask create #{cask_token}" to create a new Cask)
end end
odebug "Opening editor for Cask #{cask_token}"
exec_editor cask_path exec_editor cask_path
end end

View File

@ -1,15 +1,18 @@
module Hbc module Hbc
class CLI class CLI
class Fetch < AbstractCommand class Fetch < AbstractCommand
def run option "--force", :force, false
cask_tokens = self.class.cask_tokens_from(@args)
raise CaskUnspecifiedError if cask_tokens.empty?
force = @args.include? "--force"
cask_tokens.each do |cask_token| def initialize(*)
super
raise CaskUnspecifiedError if args.empty?
end
def run
args.each do |cask_token|
ohai "Downloading external files for Cask #{cask_token}" ohai "Downloading external files for Cask #{cask_token}"
cask = CaskLoader.load(cask_token) cask = CaskLoader.load(cask_token)
downloaded_path = Download.new(cask, force: force).perform downloaded_path = Download.new(cask, force: force?).perform
Verify.all(cask, downloaded_path) Verify.all(cask, downloaded_path)
ohai "Success! Downloaded to -> #{downloaded_path}" ohai "Success! Downloaded to -> #{downloaded_path}"
end end

View File

@ -2,7 +2,7 @@ module Hbc
class CLI class CLI
class Home < AbstractCommand class Home < AbstractCommand
def run def run
casks = @args.map(&CaskLoader.public_method(:load)) casks = args.map(&CaskLoader.public_method(:load))
if casks.empty? if casks.empty?
odebug "Opening project homepage" odebug "Opening project homepage"

View File

@ -1,13 +1,13 @@
module Hbc module Hbc
class CLI class CLI
class Info < AbstractCommand class Info < AbstractCommand
def initialize(*args) def initialize(*)
@cask_tokens = self.class.cask_tokens_from(args) super
raise CaskUnspecifiedError if @cask_tokens.empty? raise CaskUnspecifiedError if args.empty?
end end
def run def run
@cask_tokens.each do |cask_token| args.each do |cask_token|
odebug "Getting info for Cask #{cask_token}" odebug "Getting info for Cask #{cask_token}"
cask = CaskLoader.load(cask_token) cask = CaskLoader.load(cask_token)

View File

@ -1,12 +1,13 @@
module Hbc module Hbc
class CLI class CLI
class Install < AbstractCommand class Install < AbstractCommand
def initialize(*args) option "--force", :force, false
@cask_tokens = self.class.cask_tokens_from(args) option "--skip-cask-deps", :skip_cask_deps, false
raise CaskUnspecifiedError if @cask_tokens.empty? option "--require-sha", :require_sha, false
@force = args.include? "--force"
@skip_cask_deps = args.include? "--skip-cask-deps" def initialize(*)
@require_sha = args.include? "--require-sha" super
raise CaskUnspecifiedError if args.empty?
end end
def run def run
@ -19,13 +20,14 @@ module Hbc
def install_casks def install_casks
count = 0 count = 0
@cask_tokens.each do |cask_token| args.each do |cask_token|
begin begin
cask = CaskLoader.load(cask_token) cask = CaskLoader.load(cask_token)
Installer.new(cask, binaries: CLI.binaries?, Installer.new(cask, binaries: binaries?,
force: @force, verbose: verbose?,
skip_cask_deps: @skip_cask_deps, force: force?,
require_sha: @require_sha).install skip_cask_deps: skip_cask_deps?,
require_sha: require_sha?).install
count += 1 count += 1
rescue CaskAlreadyInstalledError => e rescue CaskAlreadyInstalledError => e
opoo e.message opoo e.message
@ -43,7 +45,7 @@ module Hbc
end end
end end
count.zero? ? nil : count == @cask_tokens.length count.zero? ? nil : count == args.length
end end
def self.warn_unavailable_with_suggestion(cask_token, e) def self.warn_unavailable_with_suggestion(cask_token, e)

View File

@ -1,17 +1,18 @@
module Hbc module Hbc
class CLI class CLI
class InternalAppcastCheckpoint < AbstractInternalCommand class InternalAppcastCheckpoint < AbstractInternalCommand
def initialize(*args) option "--calculate", :calculate, false
@cask_tokens = cask_tokens_from(args)
raise CaskUnspecifiedError if cask_tokens.empty? def initialize(*)
@calculate = args.include? "--calculate" super
raise CaskUnspecifiedError if args.empty?
end end
def run def run
if @cask_tokens.all? { |t| t =~ %r{^https?://} && t !~ /\.rb$/ } if cask_tokens.all? { |t| t =~ %r{^https?://} && t !~ /\.rb$/ }
self.class.appcask_checkpoint_for_url(cask_tokens) self.class.appcask_checkpoint_for_url(cask_tokens)
else else
self.class.appcask_checkpoint(@cask_tokens, @calculate) self.class.appcask_checkpoint(cask_tokens, calculate?)
end end
end end

View File

@ -3,23 +3,27 @@ module Hbc
class InternalAuditModifiedCasks < AbstractInternalCommand class InternalAuditModifiedCasks < AbstractInternalCommand
RELEVANT_STANZAS = [:version, :sha256, :url, :appcast].freeze RELEVANT_STANZAS = [:version, :sha256, :url, :appcast].freeze
option "--cleanup", :cleanup, false
def self.needs_init? def self.needs_init?
true true
end end
def initialize(*args) attr_accessor :commit_range
@commit_range = self.class.commit_range(args) private :commit_range=
@cleanup = args.any? { |a| a =~ /^-+c(leanup)?$/i }
end
def self.commit_range(args) def initialize(*)
posargs = args.reject { |a| a.empty? || a.chars.first == "-" } super
odie usage unless posargs.size == 1
posargs.first
end
def self.posargs(args) if args.count != 1
args.reject { |a| a.empty? || a.chars.first == "-" } raise ArgumentError, <<-EOS.undent
This command requires exactly one argument.
#{self.class.usage}
EOS
end
@commit_range = args.first
end end
def self.help def self.help
@ -40,12 +44,6 @@ module Hbc
EOS EOS
end end
attr_reader :commit_range
def cleanup?
@cleanup
end
def run def run
at_exit do at_exit do
cleanup cleanup

View File

@ -2,7 +2,7 @@ module Hbc
class CLI class CLI
class InternalCheckurl < AbstractInternalCommand class InternalCheckurl < AbstractInternalCommand
def run def run
casks_to_check = @args.empty? ? Hbc.all : @args.map { |arg| CaskLoader.load(arg) } casks_to_check = args.empty? ? Hbc.all : args.map(&CaskLoader.public_method(:load))
casks_to_check.each do |cask| casks_to_check.each do |cask|
odebug "Checking URL for Cask #{cask}" odebug "Checking URL for Cask #{cask}"
checker = UrlChecker.new(cask) checker = UrlChecker.new(cask)

View File

@ -1,9 +1,9 @@
module Hbc module Hbc
class CLI class CLI
class InternalDump < AbstractInternalCommand class InternalDump < AbstractInternalCommand
def initialize(*args) def initialize(*)
@cask_tokens = self.class.cask_tokens_from(args) super
raise CaskUnspecifiedError if @cask_tokens.empty? raise CaskUnspecifiedError if args.empty?
end end
def run def run
@ -16,7 +16,7 @@ module Hbc
def dump_casks def dump_casks
count = 0 count = 0
@cask_tokens.each do |cask_token| args.each do |cask_token|
begin begin
cask = CaskLoader.load(cask_token) cask = CaskLoader.load(cask_token)
count += 1 count += 1
@ -25,7 +25,7 @@ module Hbc
opoo "#{cask_token} was not found or would not load: #{e}" opoo "#{cask_token} was not found or would not load: #{e}"
end end
end end
count.zero? ? nil : count == @cask_tokens.length count.zero? ? nil : count == args.length
end end
def self.help def self.help

View File

@ -1,6 +1,12 @@
module Hbc module Hbc
class CLI class CLI
class InternalHelp < AbstractInternalCommand class InternalHelp < AbstractInternalCommand
def initialize(*)
super
return if args.empty?
raise ArgumentError, "#{self.class.command_name} does not take arguments."
end
def run def run
max_command_len = CLI.commands.map(&:length).max max_command_len = CLI.commands.map(&:length).max
puts "Unstable Internal-use Commands:\n\n" puts "Unstable Internal-use Commands:\n\n"

View File

@ -50,51 +50,60 @@ module Hbc
:uninstall_postflight, :uninstall_postflight,
] ]
def initialize(*args) option "--table", :table, false
option "--quiet", :quiet, false
option "--yaml", :yaml, false
option "--inspect", :inspect, false
attr_accessor :format
private :format, :format=
attr_accessor :stanza
private :stanza, :stanza=
def initialize(*)
super
raise ArgumentError, "No stanza given." if args.empty? raise ArgumentError, "No stanza given." if args.empty?
@table = args.include? "--table" @stanza = args.shift.to_sym
@quiet = args.include? "--quiet"
@format = :to_yaml if args.include? "--yaml" @format = :to_yaml if yaml?
@format = :inspect if args.include? "--inspect" @format = :inspect if inspect?
@cask_tokens = self.class.cask_tokens_from(args)
@stanza = @cask_tokens.shift.to_sym
@cask_tokens = Hbc.all_tokens if @cask_tokens.empty?
end end
def run def run
retval = print_stanzas retval = print_stanzas
# retval is ternary: true/false/nil # retval is ternary: true/false/nil
if retval.nil? if retval.nil?
exit 1 if @quiet exit 1 if quiet?
raise CaskError, "nothing to print" raise CaskError, "nothing to print"
elsif !retval elsif !retval
exit 1 if @quiet exit 1 if quiet?
raise CaskError, "print incomplete" raise CaskError, "print incomplete"
end end
end end
def print_stanzas def print_stanzas
count = 0 count = 0
if ARTIFACTS.include?(@stanza) if ARTIFACTS.include?(stanza)
artifact_name = @stanza artifact_name = stanza
@stanza = :artifacts @stanza = :artifacts
end end
@cask_tokens.each do |cask_token| cask_tokens = args.empty? ? Hbc.all_tokens : args
print "#{cask_token}\t" if @table cask_tokens.each do |cask_token|
print "#{cask_token}\t" if table?
begin begin
cask = CaskLoader.load(cask_token) cask = CaskLoader.load(cask_token)
rescue StandardError rescue StandardError
opoo "Cask '#{cask_token}' was not found" unless @quiet opoo "Cask '#{cask_token}' was not found" unless quiet?
puts "" puts ""
next next
end end
unless cask.respond_to?(@stanza) unless cask.respond_to?(stanza)
opoo "no such stanza '#{@stanza}' on Cask '#{cask_token}'" unless @quiet opoo "no such stanza '#{stanza}' on Cask '#{cask_token}'" unless quiet?
puts "" puts ""
next next
end end
@ -102,13 +111,13 @@ module Hbc
begin begin
value = cask.send(@stanza) value = cask.send(@stanza)
rescue StandardError rescue StandardError
opoo "failure calling '#{@stanza}' on Cask '#{cask_token}'" unless @quiet opoo "failure calling '#{stanza}' on Cask '#{cask_token}'" unless quiet?
puts "" puts ""
next next
end end
if artifact_name && !value.key?(artifact_name) if artifact_name && !value.key?(artifact_name)
opoo "no such stanza '#{artifact_name}' on Cask '#{cask_token}'" unless @quiet opoo "no such stanza '#{artifact_name}' on Cask '#{cask_token}'" unless quiet?
puts "" puts ""
next next
end end
@ -125,7 +134,7 @@ module Hbc
count += 1 count += 1
end end
count.zero? ? nil : count == @cask_tokens.length count.zero? ? nil : count == cask_tokens.length
end end
def self.help def self.help

View File

@ -1,20 +1,18 @@
module Hbc module Hbc
class CLI class CLI
class List < AbstractCommand class List < AbstractCommand
def initialize(*args) option "-1", :one, false
@cask_tokens = self.class.cask_tokens_from(args) option "--versions", :versions, false
@one = true if args.delete("-1")
@versions = true if args.delete("--versions")
return unless args.delete("-l") option "-l", (lambda do |*|
@one = true one = true # rubocop:disable Lint/UselessAssignment
opoo "Option -l is obsolete! Implying option -1." opoo "Option -l is obsolete! Implying option -1."
end end)
def run def run
retval = @cask_tokens.any? ? list : list_installed retval = args.any? ? list : list_installed
# retval is ternary: true/false/nil # retval is ternary: true/false/nil
if retval.nil? && !@cask_tokens.any? if retval.nil? && !args.any?
opoo "nothing to list" # special case: avoid exit code opoo "nothing to list" # special case: avoid exit code
elsif retval.nil? elsif retval.nil?
raise CaskError, "nothing to list" raise CaskError, "nothing to list"
@ -26,15 +24,15 @@ module Hbc
def list def list
count = 0 count = 0
@cask_tokens.each do |cask_token| args.each do |cask_token|
odebug "Listing files for Cask #{cask_token}" odebug "Listing files for Cask #{cask_token}"
begin begin
cask = CaskLoader.load(cask_token) cask = CaskLoader.load(cask_token)
if cask.installed? if cask.installed?
if @one if one?
puts cask.token puts cask.token
elsif @versions elsif versions?
puts self.class.format_versioned(cask) puts self.class.format_versioned(cask)
else else
cask = CaskLoader.load_from_file(cask.installed_caskfile) cask = CaskLoader.load_from_file(cask.installed_caskfile)
@ -50,7 +48,7 @@ module Hbc
end end
end end
count.zero? ? nil : count == @cask_tokens.length count.zero? ? nil : count == args.length
end end
def self.list_artifacts(cask) def self.list_artifacts(cask)
@ -63,9 +61,9 @@ module Hbc
def list_installed def list_installed
installed_casks = Hbc.installed installed_casks = Hbc.installed
if @one if one?
puts installed_casks.map(&:to_s) puts installed_casks.map(&:to_s)
elsif @versions elsif versions?
puts installed_casks.map(&self.class.method(:format_versioned)) puts installed_casks.map(&self.class.method(:format_versioned))
elsif !installed_casks.empty? elsif !installed_casks.empty?
puts Formatter.columns(installed_casks.map(&:to_s)) puts Formatter.columns(installed_casks.map(&:to_s))

View File

@ -0,0 +1,62 @@
module Hbc
class CLI
module Options
def self.included(klass)
klass.extend(ClassMethods)
end
module ClassMethods
def options
@options ||= {}
return @options unless superclass.respond_to?(:options)
superclass.options.merge(@options)
end
def option(name, method, default_value = nil)
@options ||= {}
@options[name] = method
return if method.respond_to?(:call)
define_method(:"#{method}=") do |value|
instance_variable_set(:"@#{method}", value)
end
if [true, false].include?(default_value)
define_method(:"#{method}?") do
instance_variable_get(:"@#{method}") == true
end
else
define_method(:"#{method}") do
instance_variable_get(:"@#{method}")
end
end
end
end
def process_arguments(*arguments)
parser = OptionParser.new do |opts|
next if self.class.options.nil?
self.class.options.each do |option_name, option_method|
option_type = case option_name.split(/(\ |\=)/).last
when "PATH"
Pathname
when /\w+(,\w+)+/
Array
end
opts.on(option_name, *option_type) do |value|
if option_method.respond_to?(:call)
option_method.call(value)
else
send(:"#{option_method}=", value)
end
end
end
end
parser.parse(*arguments)
end
end
end
end

View File

@ -1,23 +1,20 @@
module Hbc module Hbc
class CLI class CLI
class Outdated < AbstractCommand class Outdated < AbstractCommand
def initialize(*args) option "--greedy", :greedy, false
@cask_tokens = self.class.cask_tokens_from(args) option "--quiet", :quiet, false
@greedy = args.include?("--greedy") def initialize(*)
@verbose = ($stdout.tty? || CLI.verbose?) && !args.include?("--quiet") super
self.verbose = ($stdout.tty? || verbose?) && !quiet?
end end
def run def run
casks_to_check = if @cask_tokens.empty? casks_to_check = args.empty? ? Hbc.installed : args.map(&CaskLoader.public_method(:load))
Hbc.installed
else
@cask_tokens.map(&CaskLoader.public_method(:load))
end
casks_to_check.each do |cask| casks_to_check.each do |cask|
odebug "Checking update info of Cask #{cask}" odebug "Checking update info of Cask #{cask}"
self.class.list_if_outdated(cask, @greedy, @verbose) self.class.list_if_outdated(cask, greedy?, verbose?)
end end
end end

View File

@ -3,15 +3,16 @@ module Hbc
class Reinstall < Install class Reinstall < Install
def install_casks def install_casks
count = 0 count = 0
@cask_tokens.each do |cask_token| args.each do |cask_token|
begin begin
cask = CaskLoader.load(cask_token) cask = CaskLoader.load(cask_token)
Installer.new(cask, Installer.new(cask,
binaries: CLI.binaries?, binaries: binaries?,
force: @force, verbose: verbose?,
skip_cask_deps: @skip_cask_deps, force: force?,
require_sha: @require_sha).reinstall skip_cask_deps: skip_cask_deps?,
require_sha: require_sha?).reinstall
count += 1 count += 1
rescue CaskUnavailableError => e rescue CaskUnavailableError => e
@ -22,7 +23,7 @@ module Hbc
end end
end end
count.zero? ? nil : count == @cask_tokens.length count.zero? ? nil : count == args.length
end end
def self.help def self.help

View File

@ -1,8 +1,13 @@
module Hbc module Hbc
class CLI class CLI
class Search < AbstractCommand class Search < AbstractCommand
def initialize(*args)
@args = args
end
def run def run
self.class.render_results(*self.class.search(*@args)) results = self.class.search(*args)
self.class.render_results(*results)
end end
def self.extract_regexp(string) def self.extract_regexp(string)

View File

@ -7,15 +7,7 @@ module Hbc
"checks Cask style using RuboCop" "checks Cask style using RuboCop"
end end
attr_reader :args option "--fix", :fix, false
def initialize(*args)
@cask_tokens = self.class.cask_tokens_from(args)
@fix = args.any? { |arg| arg =~ /^--(fix|(auto-?)?correct)$/ }
end
def fix?
@fix
end
def run def run
install_rubocop install_rubocop
@ -35,12 +27,12 @@ module Hbc
end end
def cask_paths def cask_paths
@cask_paths ||= if @cask_tokens.empty? @cask_paths ||= if args.empty?
Hbc.all_tapped_cask_dirs Hbc.all_tapped_cask_dirs
elsif @cask_tokens.any? { |file| File.exist?(file) } elsif args.any? { |file| File.exist?(file) }
@cask_tokens args
else else
@cask_tokens.map { |token| CaskLoader.path(token) } args.map { |token| CaskLoader.path(token) }
end end
end end

View File

@ -1,25 +1,26 @@
module Hbc module Hbc
class CLI class CLI
class Uninstall < AbstractCommand class Uninstall < AbstractCommand
def initialize(*args) option "--force", :force, false
@cask_tokens = self.class.cask_tokens_from(args)
raise CaskUnspecifiedError if @cask_tokens.empty? def initialize(*)
@force = args.include? "--force" super
raise CaskUnspecifiedError if args.empty?
end end
def run def run
@cask_tokens.each do |cask_token| args.each do |cask_token|
odebug "Uninstalling Cask #{cask_token}" odebug "Uninstalling Cask #{cask_token}"
cask = CaskLoader.load(cask_token) cask = CaskLoader.load(cask_token)
raise CaskNotInstalledError, cask unless cask.installed? || @force raise CaskNotInstalledError, cask unless cask.installed? || force?
if cask.installed? && !cask.installed_caskfile.nil? if cask.installed? && !cask.installed_caskfile.nil?
# use the same cask file that was used for installation, if possible # use the same cask file that was used for installation, if possible
cask = CaskLoader.load_from_file(cask.installed_caskfile) if cask.installed_caskfile.exist? cask = CaskLoader.load_from_file(cask.installed_caskfile) if cask.installed_caskfile.exist?
end end
Installer.new(cask, binaries: CLI.binaries?, force: @force).uninstall Installer.new(cask, binaries: binaries?, verbose: verbose?, force: force?).uninstall
next if (versions = cask.versions).empty? next if (versions = cask.versions).empty?

View File

@ -1,16 +1,16 @@
module Hbc module Hbc
class CLI class CLI
class Zap < AbstractCommand class Zap < AbstractCommand
def initialize(*args) def initialize(*)
@cask_tokens = self.class.cask_tokens_from(args) super
raise CaskUnspecifiedError if @cask_tokens.empty? raise CaskUnspecifiedError if args.empty?
end end
def run def run
@cask_tokens.each do |cask_token| args.each do |cask_token|
odebug "Zapping Cask #{cask_token}" odebug "Zapping Cask #{cask_token}"
cask = CaskLoader.load(cask_token) cask = CaskLoader.load(cask_token)
Installer.new(cask).zap Installer.new(cask, verbose: verbose?).zap
end end
end end

View File

@ -14,24 +14,35 @@ module Hbc
include Staged include Staged
include Verify include Verify
attr_reader :force, :skip_cask_deps
PERSISTENT_METADATA_SUBDIRS = ["gpg"].freeze PERSISTENT_METADATA_SUBDIRS = ["gpg"].freeze
def initialize(cask, command: SystemCommand, force: false, skip_cask_deps: false, binaries: true, require_sha: false) def initialize(cask, command: SystemCommand, force: false, skip_cask_deps: false, binaries: true, verbose: false, require_sha: false)
@cask = cask @cask = cask
@command = command @command = command
@force = force @force = force
@skip_cask_deps = skip_cask_deps @skip_cask_deps = skip_cask_deps
@binaries = binaries @binaries = binaries
@verbose = verbose
@require_sha = require_sha @require_sha = require_sha
@reinstall = false @reinstall = false
end end
def skip_cask_deps?
@skip_cask_deps
end
def force?
@force
end
def binaries? def binaries?
@binaries @binaries
end end
def verbose?
@verbose
end
def self.print_caveats(cask) def self.print_caveats(cask)
odebug "Printing caveats" odebug "Printing caveats"
return if cask.caveats.empty? return if cask.caveats.empty?
@ -64,7 +75,7 @@ module Hbc
odebug "Hbc::Installer#fetch" odebug "Hbc::Installer#fetch"
satisfy_dependencies satisfy_dependencies
verify_has_sha if @require_sha && !@force verify_has_sha if @require_sha && !force?
download download
verify verify
end end
@ -82,7 +93,7 @@ module Hbc
def install def install
odebug "Hbc::Installer#install" odebug "Hbc::Installer#install"
if @cask.installed? && !force && !@reinstall if @cask.installed? && !force? && !@reinstall
raise CaskAlreadyInstalledAutoUpdatesError, @cask if @cask.auto_updates raise CaskAlreadyInstalledAutoUpdatesError, @cask if @cask.auto_updates
raise CaskAlreadyInstalledError, @cask raise CaskAlreadyInstalledError, @cask
end end
@ -113,7 +124,7 @@ module Hbc
installed_cask = installed_caskfile.exist? ? CaskLoader.load_from_file(installed_caskfile) : @cask installed_cask = installed_caskfile.exist? ? CaskLoader.load_from_file(installed_caskfile) : @cask
# Always force uninstallation, ignore method parameter # Always force uninstallation, ignore method parameter
Installer.new(installed_cask, binaries: binaries?, force: true).uninstall Installer.new(installed_cask, binaries: binaries?, verbose: verbose?, force: true).uninstall
end end
def summary def summary
@ -161,7 +172,7 @@ module Hbc
already_installed_artifacts = [] already_installed_artifacts = []
odebug "Installing artifacts" odebug "Installing artifacts"
artifacts = Artifact.for_cask(@cask, command: @command, force: force) artifacts = Artifact.for_cask(@cask, command: @command, verbose: verbose?, force: force?)
odebug "#{artifacts.length} artifact/s defined", artifacts odebug "#{artifacts.length} artifact/s defined", artifacts
artifacts.each do |artifact| artifacts.each do |artifact|
@ -199,7 +210,7 @@ module Hbc
arch_dependencies arch_dependencies
x11_dependencies x11_dependencies
formula_dependencies formula_dependencies
cask_dependencies unless skip_cask_deps cask_dependencies unless skip_cask_deps?
puts "complete" puts "complete"
end end
@ -264,7 +275,7 @@ module Hbc
if dep.installed? if dep.installed?
puts "already installed" puts "already installed"
else else
Installer.new(dep, force: false, binaries: binaries?, skip_cask_deps: true).install Installer.new(dep, binaries: binaries?, verbose: verbose?, skip_cask_deps: true, force: false).install
puts "done" puts "done"
end end
end end
@ -340,12 +351,12 @@ module Hbc
disable_accessibility_access disable_accessibility_access
uninstall_artifacts uninstall_artifacts
purge_versioned_files purge_versioned_files
purge_caskroom_path if force purge_caskroom_path if force?
end end
def uninstall_artifacts def uninstall_artifacts
odebug "Un-installing artifacts" odebug "Un-installing artifacts"
artifacts = Artifact.for_cask(@cask, command: @command, force: force) artifacts = Artifact.for_cask(@cask, command: @command, verbose: verbose?, force: force?)
# Make sure the `uninstall` stanza is run first, as it # Make sure the `uninstall` stanza is run first, as it
# may depend on other artifacts still being installed. # may depend on other artifacts still being installed.

View File

@ -5,6 +5,6 @@ module Homebrew
module_function module_function
def cask def cask
Hbc::CLI.process(ARGV) Hbc::CLI.run(*ARGV)
end end
end end