Mike McQuaid 311c10636b
Deprecation fixes
- `brew uninstall --zap` replaces `brew cask zap`
- clarify how `--json` should behave

Fixes https://github.com/Homebrew/brew/issues/9296
2020-11-25 16:33:16 +00:00

263 lines
6.5 KiB
Ruby

# typed: false
# frozen_string_literal: true
require "optparse"
require "shellwords"
require "cli/parser"
require "extend/optparse"
require "cask/config"
require "cask/cmd/abstract_command"
require "cask/cmd/--cache"
require "cask/cmd/audit"
require "cask/cmd/cat"
require "cask/cmd/create"
require "cask/cmd/doctor"
require "cask/cmd/edit"
require "cask/cmd/fetch"
require "cask/cmd/help"
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"
module Cask
# Implementation of the `brew cask` command-line interface.
#
# @api private
class Cmd
extend T::Sig
include Context
ALIASES = {
"ls" => "list",
"homepage" => "home",
"instal" => "install", # gem does the same
"uninstal" => "uninstall",
"rm" => "uninstall",
"remove" => "uninstall",
"abv" => "info",
"dr" => "doctor",
}.freeze
DEPRECATED_COMMANDS = {
Cmd::Cache => "brew --cache [--cask]",
Cmd::Audit => "brew audit [--cask]",
Cmd::Cat => "brew cat [--cask]",
Cmd::Create => "brew create --cask --set-name <name> <url>",
Cmd::Doctor => "brew doctor --verbose",
Cmd::Edit => "brew edit [--cask]",
Cmd::Fetch => "brew fetch [--cask]",
Cmd::Help => "brew help",
Cmd::Home => "brew home",
Cmd::Info => "brew info [--cask]",
Cmd::Install => "brew install [--cask]",
Cmd::List => "brew list [--cask]",
Cmd::Outdated => "brew outdated [--cask]",
Cmd::Reinstall => "brew reinstall [--cask]",
Cmd::Style => "brew style",
Cmd::Uninstall => "brew uninstall [--cask]",
Cmd::Upgrade => "brew upgrade [--cask]",
Cmd::Zap => "brew uninstall --zap [--cask]",
}.freeze
def self.parser(&block)
Homebrew::CLI::Parser.new do
if block
instance_eval(&block)
else
usage_banner <<~EOS
`cask` <command> [<options>] [<cask>]
Homebrew Cask provides a friendly CLI workflow for the administration of macOS applications distributed as binaries.
See also: `man brew`
EOS
end
cask_options
end
end
def self.command_classes
@command_classes ||= constants.map(&method(:const_get))
.select { |klass| klass.is_a?(Class) && klass < AbstractCommand }
.reject(&:abstract?)
.sort_by(&:command_name)
end
def self.commands
@commands ||= command_classes.map(&:command_name)
end
def self.lookup_command(command_name)
@lookup ||= Hash[commands.zip(command_classes)]
command_name = ALIASES.fetch(command_name, command_name)
@lookup.fetch(command_name, nil)
end
def self.aliases
ALIASES
end
def self.run(*args)
new(*args).run
end
def initialize(*args)
@argv = args
end
def find_external_command(command)
@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" }
.find(&:file?)
external_ruby_cmd ||= which("brewcask-#{command}.rb", @path)
if external_ruby_cmd
ExternalRubyCommand.new(command, external_ruby_cmd)
elsif external_command = which("brewcask-#{command}", @path)
ExternalCommand.new(external_command)
end
end
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
nil
end
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
nil
end
def run
argv = @argv
args = self.class.parser.parse(argv, ignore_invalid_options: true)
Tap.default_cask_tap.install unless Tap.default_cask_tap.installed?
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
if args.help?
puts command.help
else
command.run(*argv)
end
rescue CaskError, MethodDeprecatedError, ArgumentError => e
onoe e.message
$stderr.puts e.backtrace if args.debug?
exit 1
end
# Wrapper class for running an external Ruby command.
class ExternalRubyCommand
def initialize(command, path)
@command_name = command.to_s.capitalize.to_sym
@path = path
end
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)
require @path
@command_class = begin
Cmd.const_get(@command_name)
rescue NameError
nil
end
end
end
# Wrapper class for running an external command.
class ExternalCommand
def initialize(path)
@path = path
end
def run(*argv)
exec @path, *argv
end
def help
exec @path, "--help"
end
end
# Helper class for showing help for unknown subcommands.
class UnknownSubcommand
def initialize(command_name)
@command_name = command_name
end
def run(*)
raise UsageError, "Subcommand `#{@command_name}` does not exist."
end
def help
run
end
end
# Helper class for showing help when no subcommand is given.
class NullCommand
def self.run(*)
raise UsageError, "No subcommand given."
end
def self.help
Cmd.parser.generate_help_text
end
end
end
end