332 lines
9.2 KiB
Ruby
Raw Normal View History

# frozen_string_literal: true
2016-08-18 22:11:42 +03:00
require "optparse"
require "shellwords"
2020-08-01 02:30:46 +02:00
require "cli/parser"
2016-10-04 15:24:58 +02:00
require "extend/optparse"
require "cask/config"
2018-09-03 20:17:29 +01:00
require "cask/cmd/abstract_command"
require "cask/cmd/--cache"
2018-09-03 20:17:29 +01:00
require "cask/cmd/audit"
require "cask/cmd/cat"
require "cask/cmd/create"
require "cask/cmd/doctor"
require "cask/cmd/edit"
require "cask/cmd/fetch"
2020-04-07 08:32:30 +02:00
require "cask/cmd/help"
2018-09-03 20:17:29 +01:00
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
2020-08-19 10:34:07 +02:00
# Implementation of the `brew cask` command-line interface.
#
# @api private
2018-09-04 08:45:48 +01:00
class Cmd
include Context
2016-09-24 13:52:43 +02:00
ALIASES = {
2016-10-14 20:33:16 +02:00
"ls" => "list",
"homepage" => "home",
2020-04-07 08:32:30 +02:00
"instal" => "install", # gem does the same
"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
DEPRECATED_COMMANDS = {
Cmd::Cache => "brew --cache --cask",
Cmd::Doctor => "brew doctor --verbose",
Cmd::Home => "brew home",
Cmd::List => "brew list --cask",
Cmd::Outdated => "brew outdated --cask",
Cmd::Reinstall => "brew reinstall",
Cmd::Upgrade => "brew upgrade --cask",
}.freeze
2020-08-01 02:30:46 +02:00
def self.description
2020-08-19 17:12:32 +01:00
max_command_length = Cmd.commands.map(&:length).max
2020-08-01 02:30:46 +02:00
2020-08-19 17:12:32 +01:00
command_lines = Cmd.command_classes
.select(&:visible?)
.map do |klass|
" - #{"`#{klass.command_name}`".ljust(max_command_length + 2)} #{klass.short_description}\n"
end
<<~EOS
2020-08-01 02:30:46 +02:00
Homebrew Cask provides a friendly CLI workflow for the administration of macOS applications distributed as binaries.
Commands:
2020-08-19 17:12:32 +01:00
#{command_lines.join}
See also: `man brew`
2020-08-01 02:30:46 +02:00
EOS
end
2017-05-21 00:15:56 +02:00
OPTIONS = [
[:flag, "--appdir=", {
description: "Target location for Applications. " \
"Default: `#{Config::DEFAULT_DIRS[:appdir]}`",
}],
[:flag, "--colorpickerdir=", {
description: "Target location for Color Pickers. " \
"Default: `#{Config::DEFAULT_DIRS[:colorpickerdir]}`",
}],
[:flag, "--prefpanedir=", {
description: "Target location for Preference Panes. " \
"Default: `#{Config::DEFAULT_DIRS[:prefpanedir]}`",
}],
[:flag, "--qlplugindir=", {
description: "Target location for QuickLook Plugins. " \
"Default: `#{Config::DEFAULT_DIRS[:qlplugindir]}`",
}],
[:flag, "--mdimporterdir=", {
description: "Target location for Spotlight Plugins. " \
"Default: `#{Config::DEFAULT_DIRS[:mdimporterdir]}`",
}],
[:flag, "--dictionarydir=", {
description: "Target location for Dictionaries. " \
"Default: `#{Config::DEFAULT_DIRS[:dictionarydir]}`",
}],
[:flag, "--fontdir=", {
description: "Target location for Fonts. " \
"Default: `#{Config::DEFAULT_DIRS[:fontdir]}`",
}],
[:flag, "--servicedir=", {
description: "Target location for Services. " \
"Default: `#{Config::DEFAULT_DIRS[:servicedir]}`",
}],
[:flag, "--input_methoddir=", {
description: "Target location for Input Methods. " \
"Default: `#{Config::DEFAULT_DIRS[:input_methoddir]}`",
}],
[:flag, "--internet_plugindir=", {
description: "Target location for Internet Plugins. " \
"Default: `#{Config::DEFAULT_DIRS[:internet_plugindir]}`",
}],
[:flag, "--audio_unit_plugindir=", {
description: "Target location for Audio Unit Plugins. " \
"Default: `#{Config::DEFAULT_DIRS[:audio_unit_plugindir]}`",
}],
[:flag, "--vst_plugindir=", {
description: "Target location for VST Plugins. " \
"Default: `#{Config::DEFAULT_DIRS[:vst_plugindir]}`",
}],
[:flag, "--vst3_plugindir=", {
description: "Target location for VST3 Plugins. " \
"Default: `#{Config::DEFAULT_DIRS[:vst3_plugindir]}`",
}],
[:flag, "--screen_saverdir=", {
description: "Target location for Screen Savers. " \
"Default: `#{Config::DEFAULT_DIRS[:screen_saverdir]}`",
}],
[:comma_array, "--language", {
description: "Set language of the Cask to install. The first matching " \
"language is used, otherwise the default language on the Cask. " \
"The default value is the `language of your system`",
}],
].freeze
2020-08-01 02:30:46 +02:00
def self.parser(&block)
Homebrew::CLI::Parser.new do
if block_given?
instance_eval(&block)
else
usage_banner <<~EOS
`cask` <command> [<options>] [<cask>]
2017-05-21 00:15:56 +02:00
2020-08-01 02:30:46 +02:00
#{Cmd.description}
EOS
end
2017-05-21 00:15:56 +02:00
OPTIONS.each do |option|
send(*option)
end
2020-08-01 02:30:46 +02:00
end
end
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))
2020-08-01 02:30:46 +02:00
.select { |klass| klass.is_a?(Class) && klass < AbstractCommand }
2017-05-20 19:08:03 +02:00
.reject(&:abstract?)
.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)
2020-04-07 08:32:30 +02:00
@lookup.fetch(command_name, nil)
2016-09-24 13:52:43 +02:00
end
2016-08-18 22:11:42 +03:00
2020-07-20 13:18:09 -04:00
def self.aliases
ALIASES
end
2020-04-07 08:32:30 +02:00
def self.run(*args)
new(*args).run
end
2020-04-07 08:32:30 +02:00
def initialize(*args)
2020-08-01 02:30:46 +02:00
@argv = args
2020-04-07 08:32:30 +02:00
end
2020-04-07 08:32:30 +02:00
def find_external_command(command)
@tap_cmd_directories ||= Tap.cmd_directories
@path ||= PATH.new(@tap_cmd_directories, ENV["HOMEBREW_PATH"])
2020-04-07 08:32:30 +02:00
external_ruby_cmd = @tap_cmd_directories.map { |d| d/"brewcask-#{command}.rb" }
.find(&:file?)
external_ruby_cmd ||= which("brewcask-#{command}.rb", @path)
if external_ruby_cmd
2020-04-07 08:32:30 +02:00
ExternalRubyCommand.new(command, external_ruby_cmd)
elsif external_command = which("brewcask-#{command}", @path)
ExternalCommand.new(external_command)
2016-08-18 22:11:42 +03:00
end
end
2020-04-07 08:32:30 +02:00
def detect_internal_command(*args)
args.each_with_index do |arg, i|
if command = self.class.lookup_command(arg)
args.delete_at(i)
return [command, args]
elsif !arg.start_with?("-")
break
end
end
2017-05-21 00:15:56 +02:00
2020-04-07 08:32:30 +02:00
nil
2017-05-21 00:15:56 +02:00
end
2020-04-07 08:32:30 +02:00
def detect_external_command(*args)
args.each_with_index do |arg, i|
if command = find_external_command(arg)
args.delete_at(i)
return [command, args]
elsif !arg.start_with?("-")
break
end
end
2020-04-07 08:32:30 +02:00
nil
end
2017-05-21 00:15:56 +02:00
def run
2020-08-01 02:30:46 +02:00
argv = @argv
2016-08-18 22:11:42 +03:00
2020-08-01 02:30:46 +02:00
args = self.class.parser.parse(argv, ignore_invalid_options: true)
2016-08-18 22:11:42 +03:00
2020-08-01 02:30:46 +02:00
Tap.default_cask_tap.install unless Tap.default_cask_tap.installed?
2020-08-01 02:30:46 +02:00
command, argv = detect_internal_command(*argv) ||
detect_external_command(*argv) ||
[args.remaining.empty? ? NullCommand : UnknownSubcommand.new(args.remaining.first), argv]
if (replacement = DEPRECATED_COMMANDS[command])
odeprecated "brew cask #{command.command_name}", replacement
end
2020-08-01 02:30:46 +02:00
if args.help?
puts command.help
else
command.run(*argv)
2016-08-18 22:11:42 +03:00
end
2020-08-01 02:30:46 +02:00
rescue CaskError, MethodDeprecatedError, ArgumentError => e
onoe e.message
$stderr.puts e.backtrace if args.debug?
exit 1
2016-08-18 22:11:42 +03:00
end
2020-08-19 10:34:07 +02:00
# Wrapper class for running an external Ruby command.
2020-04-07 08:32:30 +02:00
class ExternalRubyCommand
def initialize(command, path)
@command_name = command.to_s.capitalize.to_sym
@path = path
2016-09-24 13:52:43 +02:00
end
2020-04-07 08:32:30 +02:00
def run(*args)
command_class&.run(*args)
end
def help
command_class&.help
end
private
def command_class
return @command_class if defined?(@command_class)
2020-04-07 08:32:30 +02:00
require @path
@command_class = begin
2020-04-07 08:32:30 +02:00
Cmd.const_get(@command_name)
rescue NameError
nil
end
2016-08-18 22:11:42 +03:00
end
2020-04-07 08:32:30 +02:00
end
2016-08-18 22:11:42 +03:00
2020-08-19 10:34:07 +02:00
# Wrapper class for running an external command.
2020-04-07 08:32:30 +02:00
class ExternalCommand
def initialize(path)
@path = path
2016-09-24 13:52:43 +02:00
end
2016-08-18 22:11:42 +03:00
2020-08-01 02:30:46 +02:00
def run(*argv)
exec @path, *argv
2016-08-18 22:11:42 +03:00
end
def help
exec @path, "--help"
end
2020-04-07 08:32:30 +02:00
end
2016-08-18 22:11:42 +03:00
2020-08-19 10:34:07 +02:00
# Helper class for showing help for unknown subcommands.
2020-08-01 02:30:46 +02:00
class UnknownSubcommand
def initialize(command_name)
@command_name = command_name
end
2016-08-18 22:11:42 +03:00
2020-08-01 02:30:46 +02:00
def run(*)
raise UsageError, "Subcommand `#{@command_name}` does not exist."
2016-09-24 13:52:43 +02:00
end
def help
run
end
2016-08-18 22:11:42 +03:00
end
2020-08-01 02:30:46 +02:00
2020-08-19 10:34:07 +02:00
# Helper class for showing help when no subcommand is given.
2020-08-01 02:30:46 +02:00
class NullCommand
def self.run(*)
raise UsageError, "No subcommand given."
end
def self.help
Cmd.parser.generate_help_text
end
end
2016-08-18 22:11:42 +03:00
end
end