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,15 +1,18 @@
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?
# 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)
raise CaskUnavailableError, cask_token.to_s unless cask_path.exist? raise CaskUnavailableError, cask_token.to_s unless cask_path.exist?
puts File.open(cask_path, &:read) puts File.open(cask_path, &:read)
end end
end
def self.help def self.help
"dump raw source of the given Cask to the standard output" "dump raw source of the given Cask to the standard output"

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 }
def initialize(*)
super
if args.count != 1
raise ArgumentError, <<-EOS.undent
This command requires exactly one argument.
#{self.class.usage}
EOS
end end
def self.commit_range(args) @commit_range = args.first
posargs = args.reject { |a| a.empty? || a.chars.first == "-" }
odie usage unless posargs.size == 1
posargs.first
end
def self.posargs(args)
args.reject { |a| a.empty? || a.chars.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