2015-01-01 22:08:06 +08:00
|
|
|
class Hbc::CLI; end
|
2013-09-22 10:44:49 +08:00
|
|
|
|
2012-12-18 18:50:44 +08:00
|
|
|
require 'optparse'
|
|
|
|
require 'shellwords'
|
|
|
|
|
2015-01-01 22:08:06 +08:00
|
|
|
require 'hbc/cli/base'
|
2015-02-06 23:52:06 +08:00
|
|
|
require 'hbc/cli/alfred'
|
2015-01-01 22:08:06 +08:00
|
|
|
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'
|
2013-09-22 10:44:49 +08:00
|
|
|
|
2015-01-01 22:08:06 +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'
|
2014-06-21 02:16:28 +08:00
|
|
|
|
2015-01-01 22:08:06 +08:00
|
|
|
class Hbc::CLI
|
2014-06-21 02:40:29 +08:00
|
|
|
|
2014-06-21 02:00:43 +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
|
2015-01-01 22:08:06 +08:00
|
|
|
@@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
|
|
|
|
|
2012-03-05 10:46:30 +08:00
|
|
|
def self.commands
|
2014-06-21 02:10:46 +08:00
|
|
|
@@commands ||= command_classes.map { |sym| sym.command_name }
|
2012-03-05 10:46:30 +08:00
|
|
|
end
|
|
|
|
|
2014-01-25 01:07:42 +08:00
|
|
|
def self.lookup_command(command_string)
|
2014-06-21 02:12:09 +08:00
|
|
|
@@lookup ||= Hash[commands.zip(command_classes)]
|
2014-06-21 02:02:13 +08:00
|
|
|
command_string = ALIASES.fetch(command_string, command_string)
|
2014-06-21 02:12:09 +08:00
|
|
|
@@lookup.fetch(command_string, command_string)
|
2014-01-25 01:07:42 +08:00
|
|
|
end
|
|
|
|
|
2014-06-22 02:51:57 +08:00
|
|
|
# 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
|
|
|
|
|
2014-01-25 01:07:42 +08:00
|
|
|
def self.run_command(command, *rest)
|
|
|
|
if command.respond_to?(:run)
|
2014-06-22 02:51:57 +08:00
|
|
|
# usual case: built-in command verb
|
2014-01-25 01:07:42 +08:00
|
|
|
command.run(*rest)
|
2015-01-01 22:08:06 +08:00
|
|
|
elsif require? Hbc::Utils.which("brewcask-#{command}.rb").to_s
|
2014-06-22 02:51:57 +08:00
|
|
|
# external command as Ruby library on PATH, Homebrew-style
|
2014-01-25 01:07:42 +08:00
|
|
|
exit 0
|
2014-06-22 02:51:57 +08:00
|
|
|
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
|
2015-01-01 22:08:06 +08:00
|
|
|
Hbc::CLI.const_get(sym)
|
2014-10-23 23:57:47 +08:00
|
|
|
rescue NameError => e
|
2014-06-22 02:51:57 +08:00
|
|
|
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
|
2015-01-01 22:08:06 +08:00
|
|
|
elsif Hbc::Utils.which "brewcask-#{command}"
|
2014-06-22 02:51:57 +08:00
|
|
|
# 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]
|
2014-01-25 01:07:42 +08:00
|
|
|
else
|
2014-06-22 02:51:57 +08:00
|
|
|
# failure
|
2015-01-01 22:08:06 +08:00
|
|
|
Hbc::CLI::NullCommand.new(command).run
|
2012-03-05 10:46:30 +08:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def self.process(arguments)
|
2014-01-25 01:07:42 +08:00
|
|
|
command_string, *rest = *arguments
|
2012-12-18 18:50:44 +08:00
|
|
|
rest = process_options(rest)
|
2015-01-01 22:08:06 +08:00
|
|
|
Hbc.init
|
2014-01-25 01:07:42 +08:00
|
|
|
command = lookup_command(command_string)
|
|
|
|
run_command(command, *rest)
|
2015-01-01 22:08:06 +08:00
|
|
|
rescue Hbc::CaskError, Hbc::CaskSha256MismatchError => e
|
2013-11-07 08:47:00 +08:00
|
|
|
onoe e
|
2015-01-01 22:08:06 +08:00
|
|
|
$stderr.puts e.backtrace if Hbc.debug
|
2013-11-07 08:47:00 +08:00
|
|
|
exit 1
|
2014-04-19 21:21:48 +08:00
|
|
|
rescue StandardError, ScriptError, NoMemoryError => e
|
|
|
|
onoe e
|
2015-01-01 22:08:06 +08:00
|
|
|
puts Hbc::Utils.error_message_with_suggestions
|
2014-04-19 21:21:48 +08:00
|
|
|
puts e.backtrace
|
|
|
|
exit 1
|
2012-03-05 10:46:30 +08:00
|
|
|
end
|
|
|
|
|
2012-10-14 05:17:52 +08:00
|
|
|
def self.nice_listing(cask_list)
|
2014-11-29 03:36:16 +08:00
|
|
|
cask_taps = {}
|
|
|
|
cask_list.each do |c|
|
|
|
|
user, repo, token = c.split '/'
|
2014-04-26 01:01:32 +08:00
|
|
|
repo.sub!(/^homebrew-/i, '')
|
2014-11-29 03:36:16 +08:00
|
|
|
cask_taps[token] ||= []
|
|
|
|
cask_taps[token].push "#{user}/#{repo}"
|
|
|
|
end
|
2012-10-14 05:17:52 +08:00
|
|
|
list = []
|
2014-11-29 03:36:16 +08:00
|
|
|
cask_taps.each do |token,taps|
|
2014-04-26 01:01:32 +08:00
|
|
|
if taps.length == 1
|
2014-11-29 03:36:16 +08:00
|
|
|
list.push token
|
2012-10-14 05:17:52 +08:00
|
|
|
else
|
2014-11-29 03:36:16 +08:00
|
|
|
taps.each { |r| list.push [r,token].join '/' }
|
2012-10-14 05:17:52 +08:00
|
|
|
end
|
2014-11-29 03:36:16 +08:00
|
|
|
end
|
2012-10-14 05:17:52 +08:00
|
|
|
list.sort
|
|
|
|
end
|
2013-04-05 05:56:43 +08:00
|
|
|
|
|
|
|
def self.parser
|
2013-12-14 17:13:06 +08:00
|
|
|
# If you modify these arguments, please update USAGE.md
|
2013-04-05 05:56:43 +08:00
|
|
|
@parser ||= OptionParser.new do |opts|
|
2013-12-12 19:31:57 +08:00
|
|
|
opts.on("--caskroom=MANDATORY") do |v|
|
2015-01-01 22:08:06 +08:00
|
|
|
Hbc.caskroom = Pathname(v).expand_path
|
2013-12-12 19:31:57 +08:00
|
|
|
end
|
2012-12-18 18:50:44 +08:00
|
|
|
opts.on("--appdir=MANDATORY") do |v|
|
2015-01-01 22:08:06 +08:00
|
|
|
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|
|
2015-01-01 22:08:06 +08:00
|
|
|
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|
|
2015-01-01 22:08:06 +08:00
|
|
|
Hbc.prefpanedir = Pathname(v).expand_path
|
2012-12-18 18:50:44 +08:00
|
|
|
end
|
2013-11-15 02:09:29 +08:00
|
|
|
opts.on("--qlplugindir=MANDATORY") do |v|
|
2015-01-01 22:08:06 +08:00
|
|
|
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|
|
2015-01-01 22:08:06 +08:00
|
|
|
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|
|
2015-01-01 22:08:06 +08:00
|
|
|
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|
|
2015-01-01 22:08:06 +08:00
|
|
|
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|
|
2015-01-01 22:08:06 +08:00
|
|
|
Hbc.input_methoddir = Pathname(v).expand_path
|
2014-01-14 01:21:44 +08:00
|
|
|
end
|
2014-08-26 23:22:25 +08:00
|
|
|
opts.on("--internet_plugindir=MANDATORY") do |v|
|
2015-01-01 22:08:06 +08:00
|
|
|
Hbc.internet_plugindir = Pathname(v).expand_path
|
2014-08-26 23:22:25 +08:00
|
|
|
end
|
2014-01-28 23:17:50 +08:00
|
|
|
opts.on("--screen_saverdir=MANDATORY") do |v|
|
2015-01-01 22:08:06 +08:00
|
|
|
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|
|
2015-01-01 22:08:06 +08:00
|
|
|
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|
|
2015-01-01 22:08:06 +08:00
|
|
|
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|
|
2015-01-01 22:08:06 +08:00
|
|
|
Hbc.verbose = true
|
2014-12-27 01:33:09 +08:00
|
|
|
end
|
2014-02-05 22:42:36 +08:00
|
|
|
opts.on("--outdated") do |v|
|
2015-02-08 03:59:14 +08:00
|
|
|
Hbc.cleanup_outdated = true
|
2014-02-05 22:42:36 +08:00
|
|
|
end
|
2013-04-05 05:56:43 +08:00
|
|
|
end
|
2012-12-18 18:50:44 +08:00
|
|
|
end
|
2012-10-14 05:17:52 +08:00
|
|
|
|
2013-04-05 05:56:43 +08:00
|
|
|
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?
|
2013-04-05 05:56:43 +08:00
|
|
|
begin
|
|
|
|
head = all_args.shift
|
|
|
|
remaining.concat(parser.parse([head]))
|
|
|
|
rescue OptionParser::InvalidOption
|
|
|
|
remaining << head
|
|
|
|
retry
|
User-friendly error messages for invalid options/arguments; fixes #5997
- Show a more user-friendly error message when dealing with an option whose (mandatory) argument is missing.
- Likewise, we show a more user-friendly error message when a given option is ambiguous. Examples:
- Given `-f`, the parser successfully expands to `--fontdir`.
- Given `-c`, we fail to expand because the parser can’t tell if the user means `--caskroom` or rather `--colorpickerdir`. This exception now results in a nicer error message.
- Some commands now result in a consistent error message when a required cask name is missing.
This affects all stable commands which require one or more cask names as arguments, i. e. `cat`, `create`, `edit`, `fetch`, `info`, `install`, `uninstall`, and `zap`.
- Up to now, the commands `cat`, `create`, `edit`, and `info` used to treat any unknown option as a cask name. This commit changes that behaviour to make it consistent to the rest of the commands (like `fetch`, `install`, `uninstall`, and `zap`), who have silently discarded any unknown option in the past, and continue to do so.
2014-10-06 00:32:33 +08:00
|
|
|
rescue OptionParser::MissingArgument
|
2015-01-01 22:08:06 +08:00
|
|
|
raise Hbc::CaskError.new("The option '#{head}' requires an argument")
|
User-friendly error messages for invalid options/arguments; fixes #5997
- Show a more user-friendly error message when dealing with an option whose (mandatory) argument is missing.
- Likewise, we show a more user-friendly error message when a given option is ambiguous. Examples:
- Given `-f`, the parser successfully expands to `--fontdir`.
- Given `-c`, we fail to expand because the parser can’t tell if the user means `--caskroom` or rather `--colorpickerdir`. This exception now results in a nicer error message.
- Some commands now result in a consistent error message when a required cask name is missing.
This affects all stable commands which require one or more cask names as arguments, i. e. `cat`, `create`, `edit`, `fetch`, `info`, `install`, `uninstall`, and `zap`.
- Up to now, the commands `cat`, `create`, `edit`, and `info` used to treat any unknown option as a cask name. This commit changes that behaviour to make it consistent to the rest of the commands (like `fetch`, `install`, `uninstall`, and `zap`), who have silently discarded any unknown option in the past, and continue to do so.
2014-10-06 00:32:33 +08:00
|
|
|
rescue OptionParser::AmbiguousOption
|
2015-01-01 22:08:06 +08:00
|
|
|
raise Hbc::CaskError.new(
|
User-friendly error messages for invalid options/arguments; fixes #5997
- Show a more user-friendly error message when dealing with an option whose (mandatory) argument is missing.
- Likewise, we show a more user-friendly error message when a given option is ambiguous. Examples:
- Given `-f`, the parser successfully expands to `--fontdir`.
- Given `-c`, we fail to expand because the parser can’t tell if the user means `--caskroom` or rather `--colorpickerdir`. This exception now results in a nicer error message.
- Some commands now result in a consistent error message when a required cask name is missing.
This affects all stable commands which require one or more cask names as arguments, i. e. `cat`, `create`, `edit`, `fetch`, `info`, `install`, `uninstall`, and `zap`.
- Up to now, the commands `cat`, `create`, `edit`, and `info` used to treat any unknown option as a cask name. This commit changes that behaviour to make it consistent to the rest of the commands (like `fetch`, `install`, `uninstall`, and `zap`), who have silently discarded any unknown option in the past, and continue to do so.
2014-10-06 00:32:33 +08:00
|
|
|
"There is more than one possible option that starts with '#{head}'")
|
2013-04-05 05:56:43 +08:00
|
|
|
end
|
|
|
|
end
|
2014-12-27 01:33:09 +08:00
|
|
|
|
|
|
|
# for compat with Homebrew, not certain if this is desirable
|
2015-01-01 22:08:06 +08:00
|
|
|
Hbc.verbose = true if !ENV['VERBOSE'].nil? or !ENV['HOMEBREW_VERBOSE'].nil?
|
2014-12-27 01:33:09 +08:00
|
|
|
|
2013-04-05 05:56:43 +08:00
|
|
|
remaining
|
|
|
|
end
|
2012-10-14 05:17:52 +08:00
|
|
|
|
2012-03-05 10:46:30 +08:00
|
|
|
class NullCommand
|
2014-11-29 03:36:16 +08:00
|
|
|
def initialize(attempted_verb)
|
|
|
|
@attempted_verb = attempted_verb
|
2012-03-05 10:46:30 +08:00
|
|
|
end
|
|
|
|
|
|
|
|
def run(*args)
|
2014-11-29 03:36:16 +08:00
|
|
|
if args.include?('--version') or @attempted_verb == '--version'
|
2015-01-01 22:08:06 +08:00
|
|
|
puts HBC_VERSION
|
2014-09-16 22:04:47 +08:00
|
|
|
else
|
|
|
|
purpose
|
2014-11-29 03:36:16 +08:00
|
|
|
if @attempted_verb and @attempted_verb != "help"
|
2014-09-16 22:04:47 +08:00
|
|
|
puts "!! "
|
2014-11-29 03:36:16 +08:00
|
|
|
puts "!! no command verb: #{@attempted_verb}"
|
2014-09-16 22:04:47 +08:00
|
|
|
puts "!! \n\n"
|
|
|
|
end
|
|
|
|
usage
|
2012-03-05 10:46:30 +08:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def purpose
|
2012-12-18 18:50:44 +08:00
|
|
|
puts <<-PURPOSE.undent
|
2012-03-05 10:46:30 +08:00
|
|
|
brew-cask provides a friendly homebrew-style CLI workflow for the
|
2014-06-09 23:44:24 +08:00
|
|
|
administration of Mac applications distributed as binaries.
|
|
|
|
|
2012-03-05 10:46:30 +08:00
|
|
|
PURPOSE
|
|
|
|
end
|
|
|
|
|
|
|
|
def usage
|
2015-01-01 22:08:06 +08:00
|
|
|
max_command_len = Hbc::CLI.commands.map(&:length).max
|
2014-06-09 23:44:24 +08:00
|
|
|
|
|
|
|
puts "Commands:\n\n"
|
2015-01-01 22:08:06 +08:00
|
|
|
Hbc::CLI.command_classes.each do |klass|
|
2014-06-21 02:16:28 +08:00
|
|
|
next unless klass.visible
|
2014-06-21 02:39:45 +08:00
|
|
|
puts " #{klass.command_name.ljust(max_command_len)} #{_help_for(klass)}"
|
2014-06-09 23:44:24 +08:00
|
|
|
end
|
|
|
|
puts %Q{\nSee also "man brew-cask"}
|
2012-03-05 10:46:30 +08:00
|
|
|
end
|
|
|
|
|
2013-02-18 02:51:20 +08:00
|
|
|
def help
|
|
|
|
''
|
|
|
|
end
|
|
|
|
|
2014-06-21 02:06:14 +08:00
|
|
|
def _help_for(klass)
|
|
|
|
klass.respond_to?(:help) ? klass.help : nil
|
2012-03-05 10:46:30 +08:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|