homebrew-cask/lib/hbc/cli.rb

273 lines
7.9 KiB
Ruby
Raw Normal View History

class Hbc::CLI; end
naked pkg support + major container refactor `Cask::Installer` was already much too complex, so I took this opportunity to throw a `Cask::Container` abstraction around the extraction part of the package install step. It goes like this: a Cask's URL points to a Container of some sort. The containers we currently support are: dmg, zip, tar, and (new) naked. Naked refers to a raw file that just needs to be copied in place. This currently just means a pkg file, but in the future it may expand. A Container knows how to do two things: identify a path as being its type (`Container.me?`) and extracting the contents of its container to the proper destination for a Cask (`Container#extract`). The first Cask we have that supports the naked pkg type is `heroku-toolbelt`. (Thanks to @sheerun for the Cask definition.) Other miscellania batched in with this refactor: - switched to an explicit require strategy rather than globbing - `Cask::Installer` is instantiated now to match its interface with other similar collaorators - simplified zip and tar identification to shorter strings rather than exact matches of full `file -Izb` output - `Cask::SystemCommand` gets explicit output redirection options - many rogue backticks replaced to properly use `SystemCommand` - fixed misnamed test file `link_checker_spec.rb` - remove some extraneous `after` clauses in tests; leaning more on `test/support/cleanup.rb` to uninstall for us - pkg uninstall `:files` gets a `-rf` to support removing dirs refs #839 and #1043
2013-09-22 10:44:49 +08:00
require 'optparse'
require 'shellwords'
require 'hbc/cli/base'
require 'hbc/cli/alfred'
require 'hbc/cli/audit'
require 'hbc/cli/cat'
require 'hbc/cli/cleanup'
require 'hbc/cli/create'
require 'hbc/cli/doctor'
require 'hbc/cli/edit'
require 'hbc/cli/fetch'
require 'hbc/cli/home'
require 'hbc/cli/info'
require 'hbc/cli/install'
require 'hbc/cli/list'
require 'hbc/cli/search'
require 'hbc/cli/uninstall'
require 'hbc/cli/update'
require 'hbc/cli/zap'
naked pkg support + major container refactor `Cask::Installer` was already much too complex, so I took this opportunity to throw a `Cask::Container` abstraction around the extraction part of the package install step. It goes like this: a Cask's URL points to a Container of some sort. The containers we currently support are: dmg, zip, tar, and (new) naked. Naked refers to a raw file that just needs to be copied in place. This currently just means a pkg file, but in the future it may expand. A Container knows how to do two things: identify a path as being its type (`Container.me?`) and extracting the contents of its container to the proper destination for a Cask (`Container#extract`). The first Cask we have that supports the naked pkg type is `heroku-toolbelt`. (Thanks to @sheerun for the Cask definition.) Other miscellania batched in with this refactor: - switched to an explicit require strategy rather than globbing - `Cask::Installer` is instantiated now to match its interface with other similar collaorators - simplified zip and tar identification to shorter strings rather than exact matches of full `file -Izb` output - `Cask::SystemCommand` gets explicit output redirection options - many rogue backticks replaced to properly use `SystemCommand` - fixed misnamed test file `link_checker_spec.rb` - remove some extraneous `after` clauses in tests; leaning more on `test/support/cleanup.rb` to uninstall for us - pkg uninstall `:files` gets a `-rf` to support removing dirs refs #839 and #1043
2013-09-22 10:44:49 +08:00
require 'hbc/cli/internal_use_base'
require 'hbc/cli/internal_checkurl'
require 'hbc/cli/internal_dump'
require 'hbc/cli/internal_help'
require 'hbc/cli/internal_stanza'
class Hbc::CLI
2014-06-21 02:40:29 +08:00
ALIASES = {
'ls' => 'list',
'homepage' => 'home',
'-S' => 'search', # verb starting with "-" is questionable
'up' => 'update',
'instal' => 'install', # gem does the same
'rm' => 'uninstall',
'remove' => 'uninstall',
'abv' => 'info',
'dr' => 'doctor',
# aliases from Homebrew that we don't (yet) support
# 'ln' => 'link',
# 'configure' => 'diy',
# '--repo' => '--repository',
# 'environment' => '--env',
# '-c1' => '--config',
}
2014-06-21 02:03:15 +08:00
def self.command_classes
@@command_classes ||= Hbc::CLI.constants.
map { |sym| Hbc::CLI.const_get sym }.
2014-06-21 02:03:15 +08:00
select { |sym| sym.respond_to?(:run) }
end
def self.commands
@@commands ||= command_classes.map { |sym| sym.command_name }
end
def self.lookup_command(command_string)
@@lookup ||= Hash[commands.zip(command_classes)]
2014-06-21 02:02:13 +08:00
command_string = ALIASES.fetch(command_string, command_string)
@@lookup.fetch(command_string, command_string)
end
# modified from Homebrew
def self.require? path
require path
true # OK if already loaded
rescue LoadError => e
# HACK :( because we should raise on syntax errors but
# not if the file doesn't exist. TODO make robust!
raise unless e.to_s.include? path
end
def self.run_command(command, *rest)
if command.respond_to?(:run)
# usual case: built-in command verb
command.run(*rest)
elsif require? Hbc::Utils.which("brewcask-#{command}.rb").to_s
# external command as Ruby library on PATH, Homebrew-style
exit 0
elsif command.to_s.include?('/') and require? command.to_s
# external command as Ruby library with literal path, useful
# for development and troubleshooting
sym = Pathname.new(command.to_s).basename('.rb').to_s.capitalize
klass = begin
Hbc::CLI.const_get(sym)
rescue NameError => e
nil
end
if klass.respond_to?(:run)
# invoke "run" on a Ruby library which follows our coding conventions
klass.run(*rest)
else
# other Ruby libraries must do everything via "require"
exit 0
end
elsif Hbc::Utils.which "brewcask-#{command}"
# arbitrary external executable on PATH, Homebrew-style
exec "brewcask-#{command}", *ARGV[1..-1]
elsif Pathname.new(command.to_s).executable? and
command.to_s.include?('/') and
not command.to_s.match(%{\.rb$})
# arbitrary external executable with literal path, useful
# for development and troubleshooting
exec command, *ARGV[1..-1]
else
# failure
Hbc::CLI::NullCommand.new(command).run
end
end
def self.process(arguments)
command_string, *rest = *arguments
rest = process_options(rest)
Hbc.init
command = lookup_command(command_string)
run_command(command, *rest)
rescue Hbc::CaskError, Hbc::CaskSha256MismatchError => e
2013-11-07 08:47:00 +08:00
onoe e
$stderr.puts e.backtrace if Hbc.debug
2013-11-07 08:47:00 +08:00
exit 1
rescue StandardError, ScriptError, NoMemoryError => e
onoe e
puts Hbc::Utils.error_message_with_suggestions
puts e.backtrace
exit 1
end
def self.nice_listing(cask_list)
cask_taps = {}
cask_list.each do |c|
user, repo, token = c.split '/'
repo.sub!(/^homebrew-/i, '')
cask_taps[token] ||= []
cask_taps[token].push "#{user}/#{repo}"
end
list = []
cask_taps.each do |token,taps|
if taps.length == 1
list.push token
else
taps.each { |r| list.push [r,token].join '/' }
end
end
list.sort
end
def self.parser
# If you modify these arguments, please update USAGE.md
@parser ||= OptionParser.new do |opts|
opts.on("--caskroom=MANDATORY") do |v|
Hbc.caskroom = Pathname(v).expand_path
end
opts.on("--appdir=MANDATORY") do |v|
Hbc.appdir = Pathname(v).expand_path
2013-10-08 06:45:58 +08:00
end
2013-12-08 13:32:41 +08:00
opts.on("--colorpickerdir=MANDATORY") do |v|
Hbc.colorpickerdir = Pathname(v).expand_path
2013-12-08 13:32:41 +08:00
end
2013-10-08 06:45:58 +08:00
opts.on("--prefpanedir=MANDATORY") do |v|
Hbc.prefpanedir = Pathname(v).expand_path
end
2013-11-15 02:09:29 +08:00
opts.on("--qlplugindir=MANDATORY") do |v|
Hbc.qlplugindir = Pathname(v).expand_path
2013-11-15 02:09:29 +08:00
end
2013-11-26 21:38:43 +08:00
opts.on("--fontdir=MANDATORY") do |v|
Hbc.fontdir = Pathname(v).expand_path
2013-11-26 21:38:43 +08:00
end
2013-12-14 08:13:19 +08:00
opts.on("--servicedir=MANDATORY") do |v|
Hbc.servicedir = Pathname(v).expand_path
2013-12-14 08:13:19 +08:00
end
2014-01-06 04:30:53 +08:00
opts.on("--binarydir=MANDATORY") do |v|
Hbc.binarydir = Pathname(v).expand_path
2014-01-06 04:30:53 +08:00
end
2014-01-14 01:21:44 +08:00
opts.on("--input_methoddir=MANDATORY") do |v|
Hbc.input_methoddir = Pathname(v).expand_path
2014-01-14 01:21:44 +08:00
end
opts.on("--internet_plugindir=MANDATORY") do |v|
Hbc.internet_plugindir = Pathname(v).expand_path
end
2014-01-28 23:17:50 +08:00
opts.on("--screen_saverdir=MANDATORY") do |v|
Hbc.screen_saverdir = Pathname(v).expand_path
2014-01-28 23:17:50 +08:00
end
2014-01-06 04:30:53 +08:00
opts.on("--no-binaries") do |v|
Hbc.no_binaries = true
2014-01-06 04:30:53 +08:00
end
2013-11-07 08:47:00 +08:00
opts.on("--debug") do |v|
Hbc.debug = true
2013-11-07 08:47:00 +08:00
end
2014-12-27 01:33:09 +08:00
opts.on("--verbose") do |v|
Hbc.verbose = true
2014-12-27 01:33:09 +08:00
end
opts.on("--outdated") do |v|
Hbc.cleanup_outdated = true
end
end
end
def self.process_options(args)
all_args = Shellwords.shellsplit(ENV['HOMEBREW_CASK_OPTS'] || "") + args
remaining = []
2013-11-10 01:17:20 +08:00
until all_args.empty?
begin
head = all_args.shift
remaining.concat(parser.parse([head]))
rescue OptionParser::InvalidOption
remaining << head
retry
rescue OptionParser::MissingArgument
raise Hbc::CaskError.new("The option '#{head}' requires an argument")
rescue OptionParser::AmbiguousOption
raise Hbc::CaskError.new(
"There is more than one possible option that starts with '#{head}'")
end
end
2014-12-27 01:33:09 +08:00
# for compat with Homebrew, not certain if this is desirable
Hbc.verbose = true if !ENV['VERBOSE'].nil? or !ENV['HOMEBREW_VERBOSE'].nil?
2014-12-27 01:33:09 +08:00
remaining
end
class NullCommand
def initialize(attempted_verb)
@attempted_verb = attempted_verb
end
def run(*args)
if args.include?('--version') or @attempted_verb == '--version'
puts HBC_VERSION
else
purpose
if @attempted_verb and @attempted_verb != "help"
puts "!! "
puts "!! no command verb: #{@attempted_verb}"
puts "!! \n\n"
end
usage
end
end
def purpose
puts <<-PURPOSE.undent
brew-cask provides a friendly homebrew-style CLI workflow for the
administration of Mac applications distributed as binaries.
PURPOSE
end
def usage
max_command_len = Hbc::CLI.commands.map(&:length).max
puts "Commands:\n\n"
Hbc::CLI.command_classes.each do |klass|
next unless klass.visible
2014-06-21 02:39:45 +08:00
puts " #{klass.command_name.ljust(max_command_len)} #{_help_for(klass)}"
end
puts %Q{\nSee also "man brew-cask"}
end
def help
''
end
def _help_for(klass)
klass.respond_to?(:help) ? klass.help : nil
end
end
end