2016-08-18 22:11:42 +03:00
|
|
|
require "optparse"
|
|
|
|
require "shellwords"
|
|
|
|
|
2016-10-04 15:24:58 +02:00
|
|
|
require "extend/optparse"
|
2018-06-09 12:20:58 +02:00
|
|
|
|
2018-09-03 19:39:07 +01:00
|
|
|
require "cask/config"
|
|
|
|
|
2018-09-03 20:17:29 +01:00
|
|
|
require "cask/cmd/options"
|
|
|
|
|
|
|
|
require "cask/cmd/abstract_command"
|
|
|
|
require "cask/cmd/audit"
|
2019-02-15 12:33:43 +01:00
|
|
|
require "cask/cmd/automerge"
|
2018-09-03 20:17:29 +01:00
|
|
|
require "cask/cmd/cat"
|
|
|
|
require "cask/cmd/create"
|
|
|
|
require "cask/cmd/doctor"
|
|
|
|
require "cask/cmd/edit"
|
|
|
|
require "cask/cmd/fetch"
|
|
|
|
require "cask/cmd/home"
|
|
|
|
require "cask/cmd/info"
|
|
|
|
require "cask/cmd/install"
|
|
|
|
require "cask/cmd/list"
|
|
|
|
require "cask/cmd/outdated"
|
|
|
|
require "cask/cmd/reinstall"
|
|
|
|
require "cask/cmd/style"
|
|
|
|
require "cask/cmd/uninstall"
|
|
|
|
require "cask/cmd/upgrade"
|
|
|
|
require "cask/cmd/zap"
|
|
|
|
|
|
|
|
require "cask/cmd/abstract_internal_command"
|
|
|
|
require "cask/cmd/internal_help"
|
|
|
|
require "cask/cmd/internal_stanza"
|
2016-08-18 22:11:42 +03:00
|
|
|
|
2018-09-06 08:29:14 +02:00
|
|
|
module Cask
|
2018-09-04 08:45:48 +01:00
|
|
|
class Cmd
|
2016-09-24 13:52:43 +02:00
|
|
|
ALIASES = {
|
2016-10-14 20:33:16 +02:00
|
|
|
"ls" => "list",
|
|
|
|
"homepage" => "home",
|
|
|
|
"-S" => "search", # verb starting with "-" is questionable
|
|
|
|
"up" => "update",
|
|
|
|
"instal" => "install", # gem does the same
|
2017-02-21 04:35:52 -08:00
|
|
|
"uninstal" => "uninstall",
|
2016-10-14 20:33:16 +02:00
|
|
|
"rm" => "uninstall",
|
|
|
|
"remove" => "uninstall",
|
|
|
|
"abv" => "info",
|
|
|
|
"dr" => "doctor",
|
|
|
|
}.freeze
|
2016-08-18 22:11:42 +03:00
|
|
|
|
2017-05-21 00:15:56 +02:00
|
|
|
include Options
|
|
|
|
|
2017-12-03 09:06:23 +01:00
|
|
|
option "--appdir=PATH", ->(value) { Config.global.appdir = value }
|
|
|
|
option "--colorpickerdir=PATH", ->(value) { Config.global.colorpickerdir = value }
|
|
|
|
option "--prefpanedir=PATH", ->(value) { Config.global.prefpanedir = value }
|
|
|
|
option "--qlplugindir=PATH", ->(value) { Config.global.qlplugindir = value }
|
|
|
|
option "--dictionarydir=PATH", ->(value) { Config.global.dictionarydir = value }
|
|
|
|
option "--fontdir=PATH", ->(value) { Config.global.fontdir = value }
|
|
|
|
option "--servicedir=PATH", ->(value) { Config.global.servicedir = value }
|
|
|
|
option "--input_methoddir=PATH", ->(value) { Config.global.input_methoddir = value }
|
|
|
|
option "--internet_plugindir=PATH", ->(value) { Config.global.internet_plugindir = value }
|
|
|
|
option "--audio_unit_plugindir=PATH", ->(value) { Config.global.audio_unit_plugindir = value }
|
|
|
|
option "--vst_plugindir=PATH", ->(value) { Config.global.vst_plugindir = value }
|
|
|
|
option "--vst3_plugindir=PATH", ->(value) { Config.global.vst3_plugindir = value }
|
|
|
|
option "--screen_saverdir=PATH", ->(value) { Config.global.screen_saverdir = value }
|
2017-05-21 00:15:56 +02:00
|
|
|
|
|
|
|
option "--help", :help, false
|
|
|
|
|
|
|
|
# handled in OS::Mac
|
2017-06-12 19:07:52 +02:00
|
|
|
option "--language a,b,c", ->(*) {}
|
2017-05-21 00:15:56 +02:00
|
|
|
|
|
|
|
# override default handling of --version
|
|
|
|
option "--version", ->(*) { raise OptionParser::InvalidOption }
|
2017-03-06 20:37:13 +01:00
|
|
|
|
2016-09-24 13:52:43 +02:00
|
|
|
def self.command_classes
|
2016-10-14 20:55:09 +02:00
|
|
|
@command_classes ||= constants.map(&method(:const_get))
|
2017-05-20 19:08:03 +02:00
|
|
|
.select { |klass| klass.respond_to?(:run) }
|
|
|
|
.reject(&:abstract?)
|
2016-12-30 16:13:09 +01:00
|
|
|
.sort_by(&:command_name)
|
2016-09-24 13:52:43 +02:00
|
|
|
end
|
2016-08-18 22:11:42 +03:00
|
|
|
|
2016-09-24 13:52:43 +02:00
|
|
|
def self.commands
|
|
|
|
@commands ||= command_classes.map(&:command_name)
|
|
|
|
end
|
2016-08-18 22:11:42 +03:00
|
|
|
|
2017-05-21 00:15:56 +02:00
|
|
|
def self.lookup_command(command_name)
|
2016-09-24 13:52:43 +02:00
|
|
|
@lookup ||= Hash[commands.zip(command_classes)]
|
2017-05-21 00:15:56 +02:00
|
|
|
command_name = ALIASES.fetch(command_name, command_name)
|
|
|
|
@lookup.fetch(command_name, command_name)
|
2016-09-24 13:52:43 +02:00
|
|
|
end
|
2016-08-18 22:11:42 +03:00
|
|
|
|
2017-06-03 01:29:29 +02:00
|
|
|
def self.run_command(command, *args)
|
2018-06-20 22:16:08 +02:00
|
|
|
return command.run(*args) if command.respond_to?(:run)
|
|
|
|
|
|
|
|
tap_cmd_directories = Tap.cmd_directories
|
|
|
|
|
|
|
|
path = PATH.new(tap_cmd_directories, ENV["HOMEBREW_PATH"])
|
|
|
|
|
|
|
|
external_ruby_cmd = tap_cmd_directories.map { |d| d/"brewcask-#{command}.rb" }
|
2018-09-02 20:14:54 +01:00
|
|
|
.find(&:file?)
|
2018-06-20 22:16:08 +02:00
|
|
|
external_ruby_cmd ||= which("brewcask-#{command}.rb", path)
|
|
|
|
|
|
|
|
if external_ruby_cmd
|
|
|
|
require external_ruby_cmd
|
|
|
|
|
2018-07-25 09:38:14 +02:00
|
|
|
klass = begin
|
|
|
|
const_get(command.to_s.capitalize.to_sym)
|
2018-06-20 22:16:08 +02:00
|
|
|
rescue NameError
|
|
|
|
# External command is a stand-alone Ruby script.
|
|
|
|
return
|
2016-09-24 13:52:43 +02:00
|
|
|
end
|
2018-07-25 09:38:14 +02:00
|
|
|
|
|
|
|
return klass.run(*args)
|
2018-06-20 22:16:08 +02:00
|
|
|
end
|
|
|
|
|
|
|
|
if external_command = which("brewcask-#{command}", path)
|
2017-11-28 20:22:46 +01:00
|
|
|
exec external_command, *ARGV[1..-1]
|
2016-08-18 22:11:42 +03:00
|
|
|
end
|
2018-06-20 22:16:08 +02:00
|
|
|
|
|
|
|
NullCommand.new(command, *args).run
|
2016-08-18 22:11:42 +03:00
|
|
|
end
|
|
|
|
|
2017-05-21 00:15:56 +02:00
|
|
|
def self.run(*args)
|
|
|
|
new(*args).run
|
|
|
|
end
|
|
|
|
|
|
|
|
def initialize(*args)
|
|
|
|
@args = process_options(*args)
|
|
|
|
end
|
|
|
|
|
2017-06-03 01:29:29 +02:00
|
|
|
def detect_command_and_arguments(*args)
|
2018-09-02 20:14:54 +01:00
|
|
|
command = args.find do |arg|
|
2017-06-03 01:29:29 +02:00
|
|
|
if self.class.commands.include?(arg)
|
|
|
|
true
|
|
|
|
else
|
|
|
|
break unless arg.start_with?("-")
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
if index = args.index(command)
|
|
|
|
args.delete_at(index)
|
|
|
|
end
|
|
|
|
|
|
|
|
[*command, *args]
|
|
|
|
end
|
|
|
|
|
2017-05-21 00:15:56 +02:00
|
|
|
def run
|
2017-06-03 01:29:29 +02:00
|
|
|
command_name, *args = detect_command_and_arguments(*@args)
|
|
|
|
command = if help?
|
2017-10-21 15:43:20 -03:00
|
|
|
args.unshift(command_name) unless command_name.nil?
|
2017-06-03 01:29:29 +02:00
|
|
|
"help"
|
|
|
|
else
|
|
|
|
self.class.lookup_command(command_name)
|
|
|
|
end
|
2017-05-21 00:15:56 +02:00
|
|
|
|
2017-05-29 18:24:52 +01:00
|
|
|
MacOS.full_version = ENV["MACOS_VERSION"] unless ENV["MACOS_VERSION"].nil?
|
2017-01-20 09:00:53 +01:00
|
|
|
|
2018-06-09 10:13:28 +02:00
|
|
|
Tap.default_cask_tap.install unless Tap.default_cask_tap.installed?
|
2017-05-21 00:15:56 +02:00
|
|
|
self.class.run_command(command, *args)
|
2018-08-15 12:13:21 +02:00
|
|
|
rescue CaskError, MethodDeprecatedError, ArgumentError, OptionParser::InvalidOption => e
|
2016-09-24 13:52:43 +02:00
|
|
|
msg = e.message
|
2017-06-03 01:29:29 +02:00
|
|
|
msg << e.backtrace.join("\n").prepend("\n") if ARGV.debug?
|
2016-09-24 13:52:43 +02:00
|
|
|
onoe msg
|
|
|
|
exit 1
|
|
|
|
rescue StandardError, ScriptError, NoMemoryError => e
|
2016-10-17 15:28:40 -05:00
|
|
|
msg = "#{e.message}\n"
|
2016-09-24 13:52:43 +02:00
|
|
|
msg << Utils.error_message_with_suggestions
|
|
|
|
msg << e.backtrace.join("\n")
|
|
|
|
onoe msg
|
|
|
|
exit 1
|
2016-08-18 22:11:42 +03:00
|
|
|
end
|
2016-09-24 13:52:43 +02:00
|
|
|
|
|
|
|
def self.nice_listing(cask_list)
|
|
|
|
cask_taps = {}
|
|
|
|
cask_list.each do |c|
|
|
|
|
user, repo, token = c.split "/"
|
2016-10-14 20:03:34 +02:00
|
|
|
repo.sub!(/^homebrew-/i, "")
|
2016-09-24 13:52:43 +02:00
|
|
|
cask_taps[token] ||= []
|
|
|
|
cask_taps[token].push "#{user}/#{repo}"
|
2016-08-18 22:11:42 +03:00
|
|
|
end
|
2016-09-24 13:52:43 +02:00
|
|
|
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
|
2016-08-18 22:11:42 +03:00
|
|
|
end
|
|
|
|
|
2017-05-21 00:15:56 +02:00
|
|
|
def process_options(*args)
|
2019-02-03 17:41:51 +01:00
|
|
|
exclude_regex = /^\-\-#{Regexp.union(*Config::DEFAULT_DIRS.keys.map(&Regexp.public_method(:escape)))}=/
|
|
|
|
|
|
|
|
all_args = Shellwords.shellsplit(ENV.fetch("HOMEBREW_CASK_OPTS", ""))
|
|
|
|
.reject { |arg| arg.match?(exclude_regex) } + args
|
|
|
|
|
2017-05-21 00:15:56 +02:00
|
|
|
non_options = []
|
2016-08-18 22:11:42 +03:00
|
|
|
|
2019-02-03 17:41:51 +01:00
|
|
|
if idx = all_args.index("--")
|
|
|
|
non_options += all_args.drop(idx)
|
|
|
|
all_args = all_args.first(idx)
|
2016-08-18 22:11:42 +03:00
|
|
|
end
|
|
|
|
|
2019-02-03 17:41:51 +01:00
|
|
|
remaining = all_args.select do |arg|
|
2016-09-24 13:52:43 +02:00
|
|
|
begin
|
2017-05-21 00:15:56 +02:00
|
|
|
!process_arguments([arg]).empty?
|
|
|
|
rescue OptionParser::InvalidOption, OptionParser::MissingArgument, OptionParser::AmbiguousOption
|
|
|
|
true
|
2016-09-24 13:52:43 +02:00
|
|
|
end
|
2016-08-18 22:11:42 +03:00
|
|
|
end
|
|
|
|
|
2017-05-21 00:15:56 +02:00
|
|
|
remaining + non_options
|
2016-08-18 22:11:42 +03:00
|
|
|
end
|
|
|
|
|
2016-09-24 13:52:43 +02:00
|
|
|
class NullCommand
|
2017-06-03 01:29:29 +02:00
|
|
|
def initialize(command, *args)
|
|
|
|
@command = command
|
|
|
|
@args = args
|
2016-09-24 13:52:43 +02:00
|
|
|
end
|
|
|
|
|
2017-03-08 15:49:37 +01:00
|
|
|
def run(*_args)
|
|
|
|
purpose
|
|
|
|
usage
|
|
|
|
|
2017-12-24 14:56:07 +00:00
|
|
|
return if @command.nil?
|
2017-06-03 01:29:29 +02:00
|
|
|
return if @command == "help" && @args.empty?
|
2017-03-08 15:49:37 +01:00
|
|
|
|
2017-10-20 20:21:38 -03:00
|
|
|
raise ArgumentError, "help does not take arguments."
|
2016-08-18 22:11:42 +03:00
|
|
|
end
|
|
|
|
|
2016-09-24 13:52:43 +02:00
|
|
|
def purpose
|
2017-10-15 02:28:32 +02:00
|
|
|
puts <<~EOS
|
2018-09-03 20:12:29 +01:00
|
|
|
Homebrew Cask provides a friendly CLI workflow for the administration
|
|
|
|
of macOS applications distributed as binaries.
|
2016-08-18 22:11:42 +03:00
|
|
|
|
2016-09-24 13:52:43 +02:00
|
|
|
EOS
|
|
|
|
end
|
2016-08-18 22:11:42 +03:00
|
|
|
|
2016-09-24 13:52:43 +02:00
|
|
|
def usage
|
2018-09-04 08:45:48 +01:00
|
|
|
max_command_len = Cmd.commands.map(&:length).max
|
2016-08-18 22:11:42 +03:00
|
|
|
|
2016-09-24 13:52:43 +02:00
|
|
|
puts "Commands:\n\n"
|
2018-09-04 08:45:48 +01:00
|
|
|
Cmd.command_classes.each do |klass|
|
2016-09-24 13:52:43 +02:00
|
|
|
next unless klass.visible
|
2018-09-17 02:45:00 +02:00
|
|
|
|
2016-09-24 13:52:43 +02:00
|
|
|
puts " #{klass.command_name.ljust(max_command_len)} #{_help_for(klass)}"
|
|
|
|
end
|
2016-10-14 20:08:05 +02:00
|
|
|
puts %Q(\nSee also "man brew-cask")
|
2016-08-18 22:11:42 +03:00
|
|
|
end
|
|
|
|
|
2016-09-24 13:52:43 +02:00
|
|
|
def help
|
|
|
|
""
|
|
|
|
end
|
2016-08-18 22:11:42 +03:00
|
|
|
|
2016-09-24 13:52:43 +02:00
|
|
|
def _help_for(klass)
|
|
|
|
klass.respond_to?(:help) ? klass.help : nil
|
|
|
|
end
|
2016-08-18 22:11:42 +03:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|