aboutsummaryrefslogtreecommitdiffstats
path: root/Library/Homebrew/cask/lib/hbc/cli
diff options
context:
space:
mode:
Diffstat (limited to 'Library/Homebrew/cask/lib/hbc/cli')
-rw-r--r--Library/Homebrew/cask/lib/hbc/cli/audit.rb52
-rw-r--r--Library/Homebrew/cask/lib/hbc/cli/base.rb21
-rw-r--r--Library/Homebrew/cask/lib/hbc/cli/cat.rb15
-rw-r--r--Library/Homebrew/cask/lib/hbc/cli/cleanup.rb88
-rw-r--r--Library/Homebrew/cask/lib/hbc/cli/create.rb37
-rw-r--r--Library/Homebrew/cask/lib/hbc/cli/doctor.rb213
-rw-r--r--Library/Homebrew/cask/lib/hbc/cli/edit.rb18
-rw-r--r--Library/Homebrew/cask/lib/hbc/cli/fetch.rb19
-rw-r--r--Library/Homebrew/cask/lib/hbc/cli/home.rb18
-rw-r--r--Library/Homebrew/cask/lib/hbc/cli/info.rb66
-rw-r--r--Library/Homebrew/cask/lib/hbc/cli/install.rb60
-rw-r--r--Library/Homebrew/cask/lib/hbc/cli/internal_audit_modified_casks.rb135
-rw-r--r--Library/Homebrew/cask/lib/hbc/cli/internal_checkurl.rb15
-rw-r--r--Library/Homebrew/cask/lib/hbc/cli/internal_dump.rb30
-rw-r--r--Library/Homebrew/cask/lib/hbc/cli/internal_help.rb19
-rw-r--r--Library/Homebrew/cask/lib/hbc/cli/internal_stanza.rb127
-rw-r--r--Library/Homebrew/cask/lib/hbc/cli/internal_use_base.rb9
-rw-r--r--Library/Homebrew/cask/lib/hbc/cli/list.rb82
-rw-r--r--Library/Homebrew/cask/lib/hbc/cli/search.rb56
-rw-r--r--Library/Homebrew/cask/lib/hbc/cli/style.rb69
-rw-r--r--Library/Homebrew/cask/lib/hbc/cli/uninstall.rb40
-rw-r--r--Library/Homebrew/cask/lib/hbc/cli/update.rb16
-rw-r--r--Library/Homebrew/cask/lib/hbc/cli/zap.rb15
23 files changed, 1220 insertions, 0 deletions
diff --git a/Library/Homebrew/cask/lib/hbc/cli/audit.rb b/Library/Homebrew/cask/lib/hbc/cli/audit.rb
new file mode 100644
index 000000000..289547b44
--- /dev/null
+++ b/Library/Homebrew/cask/lib/hbc/cli/audit.rb
@@ -0,0 +1,52 @@
+class Hbc::CLI::Audit < Hbc::CLI::Base
+ def self.help
+ "verifies installability of Casks"
+ end
+
+ def self.run(*args)
+ failed_casks = new(args, Hbc::Auditor).run
+ return if failed_casks.empty?
+ raise Hbc::CaskError, "audit failed for casks: #{failed_casks.join(' ')}"
+ end
+
+ def initialize(args, auditor)
+ @args = args
+ @auditor = auditor
+ end
+
+ def run
+ casks_to_audit.each_with_object([]) do |cask, failed|
+ failed << cask unless audit(cask)
+ end
+ end
+
+ def audit(cask)
+ odebug "Auditing Cask #{cask}"
+ @auditor.audit(cask, audit_download: audit_download?,
+ check_token_conflicts: check_token_conflicts?)
+ end
+
+ def audit_download?
+ @args.include?("--download")
+ end
+
+ def check_token_conflicts?
+ @args.include?("--token-conflicts")
+ end
+
+ def casks_to_audit
+ if cask_tokens.empty?
+ Hbc.all
+ else
+ cask_tokens.map { |token| Hbc.load(token) }
+ end
+ end
+
+ def cask_tokens
+ @cask_tokens ||= self.class.cask_tokens_from(@args)
+ end
+
+ def self.needs_init?
+ true
+ end
+end
diff --git a/Library/Homebrew/cask/lib/hbc/cli/base.rb b/Library/Homebrew/cask/lib/hbc/cli/base.rb
new file mode 100644
index 000000000..af03969af
--- /dev/null
+++ b/Library/Homebrew/cask/lib/hbc/cli/base.rb
@@ -0,0 +1,21 @@
+class Hbc::CLI::Base
+ def self.command_name
+ @command_name ||= name.sub(%r{^.*:}, "").gsub(%r{(.)([A-Z])}, '\1_\2').downcase
+ end
+
+ def self.visible
+ true
+ end
+
+ def self.cask_tokens_from(args)
+ args.reject { |a| a.empty? || a.chars.first == "-" }
+ end
+
+ def self.help
+ "No help available for the #{command_name} command"
+ end
+
+ def self.needs_init?
+ false
+ end
+end
diff --git a/Library/Homebrew/cask/lib/hbc/cli/cat.rb b/Library/Homebrew/cask/lib/hbc/cli/cat.rb
new file mode 100644
index 000000000..d6d545c3b
--- /dev/null
+++ b/Library/Homebrew/cask/lib/hbc/cli/cat.rb
@@ -0,0 +1,15 @@
+class Hbc::CLI::Cat < Hbc::CLI::Base
+ def self.run(*args)
+ cask_tokens = cask_tokens_from(args)
+ raise Hbc::CaskUnspecifiedError if cask_tokens.empty?
+ # only respects the first argument
+ cask_token = cask_tokens.first.sub(%r{\.rb$}i, "")
+ cask_path = Hbc.path(cask_token)
+ raise Hbc::CaskUnavailableError, cask_token.to_s unless cask_path.exist?
+ puts File.open(cask_path, &:read)
+ end
+
+ def self.help
+ "dump raw source of the given Cask to the standard output"
+ end
+end
diff --git a/Library/Homebrew/cask/lib/hbc/cli/cleanup.rb b/Library/Homebrew/cask/lib/hbc/cli/cleanup.rb
new file mode 100644
index 000000000..b098a243d
--- /dev/null
+++ b/Library/Homebrew/cask/lib/hbc/cli/cleanup.rb
@@ -0,0 +1,88 @@
+class Hbc::CLI::Cleanup < Hbc::CLI::Base
+ OUTDATED_DAYS = 10
+ OUTDATED_TIMESTAMP = Time.now - (60 * 60 * 24 * OUTDATED_DAYS)
+
+ def self.help
+ "cleans up cached downloads and tracker symlinks"
+ end
+
+ def self.needs_init?
+ true
+ end
+
+ def self.run(*_ignored)
+ default.cleanup!
+ end
+
+ def self.default
+ @default ||= new(Hbc.cache, Hbc.cleanup_outdated)
+ end
+
+ attr_reader :cache_location, :outdated_only
+ def initialize(cache_location, outdated_only)
+ @cache_location = Pathname.new(cache_location)
+ @outdated_only = outdated_only
+ end
+
+ def cleanup!
+ remove_all_cache_files
+ end
+
+ def cache_files
+ return [] unless cache_location.exist?
+ cache_location.children
+ .map(&method(:Pathname))
+ .reject(&method(:outdated?))
+ end
+
+ def outdated?(file)
+ outdated_only && file && file.stat.mtime > OUTDATED_TIMESTAMP
+ end
+
+ def incomplete?(file)
+ file.extname == ".incomplete"
+ end
+
+ def cache_incompletes
+ cache_files.select(&method(:incomplete?))
+ end
+
+ def cache_completes
+ cache_files.reject(&method(:incomplete?))
+ end
+
+ def disk_cleanup_size
+ Hbc::Utils.size_in_bytes(cache_files)
+ end
+
+ def remove_all_cache_files
+ message = "Removing cached downloads"
+ message.concat " older than #{OUTDATED_DAYS} days old" if outdated_only
+ ohai message
+ delete_paths(cache_files)
+ end
+
+ def delete_paths(paths)
+ cleanup_size = 0
+ processed_files = 0
+ paths.each do |item|
+ next unless item.exist?
+ processed_files += 1
+ if Hbc::Utils.file_locked?(item)
+ puts "skipping: #{item} is locked"
+ next
+ end
+ puts item
+ item_size = File.size?(item)
+ cleanup_size += item_size unless item_size.nil?
+ item.unlink
+ end
+
+ if processed_files.zero?
+ puts "Nothing to do"
+ else
+ disk_space = disk_usage_readable(cleanup_size)
+ ohai "This operation has freed approximately #{disk_space} of disk space."
+ end
+ end
+end
diff --git a/Library/Homebrew/cask/lib/hbc/cli/create.rb b/Library/Homebrew/cask/lib/hbc/cli/create.rb
new file mode 100644
index 000000000..3c1ac76ed
--- /dev/null
+++ b/Library/Homebrew/cask/lib/hbc/cli/create.rb
@@ -0,0 +1,37 @@
+class Hbc::CLI::Create < Hbc::CLI::Base
+ def self.run(*args)
+ cask_tokens = cask_tokens_from(args)
+ raise Hbc::CaskUnspecifiedError if cask_tokens.empty?
+ cask_token = cask_tokens.first.sub(%r{\.rb$}i, "")
+ cask_path = Hbc.path(cask_token)
+ odebug "Creating Cask #{cask_token}"
+
+ raise Hbc::CaskAlreadyCreatedError, cask_token if cask_path.exist?
+
+ File.open(cask_path, "w") do |f|
+ f.write template(cask_token)
+ end
+
+ exec_editor cask_path
+ end
+
+ def self.template(cask_token)
+ <<-EOS.undent
+ cask '#{cask_token}' do
+ version ''
+ sha256 ''
+
+ url 'https://'
+ name ''
+ homepage ''
+ license :unknown # TODO: change license and remove this comment; ':unknown' is a machine-generated placeholder
+
+ app ''
+ end
+ EOS
+ end
+
+ def self.help
+ "creates the given Cask and opens it in an editor"
+ end
+end
diff --git a/Library/Homebrew/cask/lib/hbc/cli/doctor.rb b/Library/Homebrew/cask/lib/hbc/cli/doctor.rb
new file mode 100644
index 000000000..d2feb1e06
--- /dev/null
+++ b/Library/Homebrew/cask/lib/hbc/cli/doctor.rb
@@ -0,0 +1,213 @@
+class Hbc::CLI::Doctor < Hbc::CLI::Base
+ def self.run
+ ohai "macOS Release:", render_with_none_as_error(MacOS.full_version)
+ ohai "Hardware Architecture:", render_with_none_as_error("#{Hardware::CPU.type}-#{Hardware::CPU.bits}")
+ ohai "Ruby Version:", render_with_none_as_error("#{RUBY_VERSION}-p#{RUBY_PATCHLEVEL}")
+ ohai "Ruby Path:", render_with_none_as_error(RbConfig.ruby)
+ # TODO: consider removing most Homebrew constants from doctor output
+ ohai "Homebrew Version:", render_with_none_as_error(homebrew_version)
+ ohai "Homebrew Executable Path:", render_with_none_as_error(Hbc.homebrew_executable)
+ ohai "Homebrew Cellar Path:", render_with_none_as_error(homebrew_cellar)
+ ohai "Homebrew Repository Path:", render_with_none_as_error(homebrew_repository)
+ ohai "Homebrew Origin:", render_with_none_as_error(homebrew_origin)
+ ohai "Homebrew-Cask Version:", render_with_none_as_error(Hbc.full_version)
+ ohai "Homebrew-Cask Install Location:", render_install_location
+ ohai "Homebrew-Cask Staging Location:", render_staging_location(Hbc.caskroom)
+ ohai "Homebrew-Cask Cached Downloads:", render_cached_downloads
+ ohai "Homebrew-Cask Default Tap Path:", render_tap_paths(Hbc.default_tap.path)
+ ohai "Homebrew-Cask Alternate Cask Taps:", render_tap_paths(alt_taps)
+ ohai "Homebrew-Cask Default Tap Cask Count:", render_with_none_as_error(default_cask_count)
+ ohai "Contents of $LOAD_PATH:", render_load_path($LOAD_PATH)
+ ohai "Contents of $RUBYLIB Environment Variable:", render_env_var("RUBYLIB")
+ ohai "Contents of $RUBYOPT Environment Variable:", render_env_var("RUBYOPT")
+ ohai "Contents of $RUBYPATH Environment Variable:", render_env_var("RUBYPATH")
+ ohai "Contents of $RBENV_VERSION Environment Variable:", render_env_var("RBENV_VERSION")
+ ohai "Contents of $CHRUBY_VERSION Environment Variable:", render_env_var("CHRUBY_VERSION")
+ ohai "Contents of $GEM_HOME Environment Variable:", render_env_var("GEM_HOME")
+ ohai "Contents of $GEM_PATH Environment Variable:", render_env_var("GEM_PATH")
+ ohai "Contents of $BUNDLE_PATH Environment Variable:", render_env_var("BUNDLE_PATH")
+ ohai "Contents of $PATH Environment Variable:", render_env_var("PATH")
+ ohai "Contents of $SHELL Environment Variable:", render_env_var("SHELL")
+ ohai "Contents of Locale Environment Variables:", render_with_none(locale_variables)
+ ohai "Running As Privileged User:", render_with_none_as_error(privileged_uid)
+ end
+
+ def self.alt_taps
+ Tap.select { |t| t.cask_dir.directory? && t != Hbc.default_tap }
+ .map(&:path)
+ end
+
+ def self.default_cask_count
+ default_cask_count = notfound_string
+ begin
+ default_cask_count = Hbc.default_tap.cask_dir.children.count(&:file?)
+ rescue StandardError
+ default_cask_count = "0 #{error_string "Error reading #{Hbc.default_tap.path}"}"
+ end
+ default_cask_count
+ end
+
+ def self.homebrew_origin
+ homebrew_origin = notfound_string
+ begin
+ Dir.chdir(homebrew_repository) do
+ homebrew_origin = Hbc::SystemCommand.run("/usr/bin/git",
+ args: %w[config --get remote.origin.url],
+ print_stderr: false).stdout.strip
+ end
+ if homebrew_origin !~ %r{\S}
+ homebrew_origin = "#{none_string} #{error_string}"
+ elsif homebrew_origin !~ %r{(mxcl|Homebrew)/(home)?brew(\.git)?\Z}
+ homebrew_origin.concat " #{error_string 'warning: nonstandard origin'}"
+ end
+ rescue StandardError
+ homebrew_origin = error_string "Not Found - Error running git"
+ end
+ homebrew_origin
+ end
+
+ def self.homebrew_repository
+ homebrew_constants("repository")
+ end
+
+ def self.homebrew_cellar
+ homebrew_constants("cellar")
+ end
+
+ def self.homebrew_version
+ homebrew_constants("version")
+ end
+
+ def self.homebrew_taps
+ @homebrew_taps ||= if homebrew_repository.respond_to?(:join)
+ homebrew_repository.join("Library", "Taps")
+ end
+ end
+
+ def self.homebrew_constants(name)
+ @homebrew_constants ||= {}
+ return @homebrew_constants[name] if @homebrew_constants.key?(name)
+ @homebrew_constants[name] = notfound_string
+ begin
+ @homebrew_constants[name] = Hbc::SystemCommand.run!(Hbc.homebrew_executable,
+ args: ["--#{name}"],
+ print_stderr: false)
+ .stdout
+ .strip
+ if @homebrew_constants[name] !~ %r{\S}
+ @homebrew_constants[name] = "#{none_string} #{error_string}"
+ end
+ path = Pathname.new(@homebrew_constants[name])
+ @homebrew_constants[name] = path if path.exist?
+ rescue StandardError
+ @homebrew_constants[name] = error_string "Not Found - Error running brew"
+ end
+ @homebrew_constants[name]
+ end
+
+ def self.locale_variables
+ ENV.keys.grep(%r{^(?:LC_\S+|LANG|LANGUAGE)\Z}).collect { |v| %Q{#{v}="#{ENV[v]}"} }.sort.join("\n")
+ end
+
+ def self.privileged_uid
+ Process.euid == 0 ? "Yes #{error_string 'warning: not recommended'}" : "No"
+ rescue StandardError
+ notfound_string
+ end
+
+ def self.none_string
+ "<NONE>"
+ end
+
+ def self.legacy_tap_pattern
+ %r{phinze}
+ end
+
+ def self.notfound_string
+ "#{Hbc::Utils::Tty.red.underline}Not Found - Unknown Error#{Hbc::Utils::Tty.reset}"
+ end
+
+ def self.error_string(string = "Error")
+ "#{Hbc::Utils::Tty.red.underline}(#{string})#{Hbc::Utils::Tty.reset}"
+ end
+
+ def self.render_with_none(string)
+ return string if !string.nil? && string.respond_to?(:to_s) && !string.to_s.empty?
+ none_string
+ end
+
+ def self.render_with_none_as_error(string)
+ return string if !string.nil? && string.respond_to?(:to_s) && !string.to_s.empty?
+ "#{none_string} #{error_string}"
+ end
+
+ def self.render_tap_paths(paths)
+ paths = [paths] unless paths.respond_to?(:each)
+ paths.collect do |dir|
+ if dir.nil? || dir.to_s.empty?
+ none_string
+ elsif dir.to_s.match(legacy_tap_pattern)
+ dir.to_s.concat(" #{error_string 'Warning: legacy tap path'}")
+ else
+ dir.to_s
+ end
+ end
+ end
+
+ def self.render_env_var(var)
+ if ENV.key?(var)
+ %Q{#{var}="#{ENV[var]}"}
+ else
+ none_string
+ end
+ end
+
+ # This could be done by calling into Homebrew, but the situation
+ # where "doctor" is needed is precisely the situation where such
+ # things are less dependable.
+ def self.render_install_location
+ locations = Dir.glob(homebrew_cellar.join("brew-cask", "*")).reverse
+ if locations.empty?
+ none_string
+ else
+ locations.collect do |l|
+ "#{l} #{error_string 'error: legacy install. Run "brew uninstall --force brew-cask".'}"
+ end
+ end
+ end
+
+ def self.render_staging_location(path)
+ path = Pathname.new(path)
+ if !path.exist?
+ "#{path} #{error_string 'error: path does not exist'}}"
+ elsif !path.writable?
+ "#{path} #{error_string 'error: not writable by current user'}"
+ else
+ path
+ end
+ end
+
+ def self.render_load_path(paths)
+ return "#{none_string} #{error_string}" if paths.nil? || paths.empty?
+ copy = Array.new(paths)
+ unless Hbc::Utils.file_is_descendant(copy[0], homebrew_taps)
+ copy[0] = "#{copy[0]} #{error_string 'error: should be descendant of Homebrew taps directory'}"
+ end
+ copy
+ end
+
+ def self.render_cached_downloads
+ cleanup = Hbc::CLI::Cleanup.default
+ files = cleanup.cache_files
+ count = files.count
+ size = cleanup.disk_cleanup_size
+ size_msg = "#{number_readable(count)} files, #{disk_usage_readable(size)}"
+ warn_msg = error_string('warning: run "brew cask cleanup"')
+ size_msg << " #{warn_msg}" if count > 0
+ [Hbc.cache, size_msg]
+ end
+
+ def self.help
+ "checks for configuration issues"
+ end
+end
diff --git a/Library/Homebrew/cask/lib/hbc/cli/edit.rb b/Library/Homebrew/cask/lib/hbc/cli/edit.rb
new file mode 100644
index 000000000..b2d4a9156
--- /dev/null
+++ b/Library/Homebrew/cask/lib/hbc/cli/edit.rb
@@ -0,0 +1,18 @@
+class Hbc::CLI::Edit < Hbc::CLI::Base
+ def self.run(*args)
+ cask_tokens = cask_tokens_from(args)
+ raise Hbc::CaskUnspecifiedError if cask_tokens.empty?
+ # only respects the first argument
+ cask_token = cask_tokens.first.sub(%r{\.rb$}i, "")
+ cask_path = Hbc.path(cask_token)
+ odebug "Opening editor for Cask #{cask_token}"
+ unless cask_path.exist?
+ raise Hbc::CaskUnavailableError, %Q{#{cask_token}, run "brew cask create #{cask_token}" to create a new Cask}
+ end
+ exec_editor cask_path
+ end
+
+ def self.help
+ "edits the given Cask"
+ end
+end
diff --git a/Library/Homebrew/cask/lib/hbc/cli/fetch.rb b/Library/Homebrew/cask/lib/hbc/cli/fetch.rb
new file mode 100644
index 000000000..647f2af2c
--- /dev/null
+++ b/Library/Homebrew/cask/lib/hbc/cli/fetch.rb
@@ -0,0 +1,19 @@
+class Hbc::CLI::Fetch < Hbc::CLI::Base
+ def self.run(*args)
+ cask_tokens = cask_tokens_from(args)
+ raise Hbc::CaskUnspecifiedError if cask_tokens.empty?
+ force = args.include? "--force"
+
+ cask_tokens.each do |cask_token|
+ ohai "Downloading external files for Cask #{cask_token}"
+ cask = Hbc.load(cask_token)
+ downloaded_path = Hbc::Download.new(cask, force: force).perform
+ Hbc::Verify.all(cask, downloaded_path)
+ ohai "Success! Downloaded to -> #{downloaded_path}"
+ end
+ end
+
+ def self.help
+ "downloads remote application files to local cache"
+ end
+end
diff --git a/Library/Homebrew/cask/lib/hbc/cli/home.rb b/Library/Homebrew/cask/lib/hbc/cli/home.rb
new file mode 100644
index 000000000..9c8c0a0e4
--- /dev/null
+++ b/Library/Homebrew/cask/lib/hbc/cli/home.rb
@@ -0,0 +1,18 @@
+class Hbc::CLI::Home < Hbc::CLI::Base
+ def self.run(*cask_tokens)
+ if cask_tokens.empty?
+ odebug "Opening project homepage"
+ system "/usr/bin/open", "--", "http://caskroom.io/"
+ else
+ cask_tokens.each do |cask_token|
+ odebug "Opening homepage for Cask #{cask_token}"
+ cask = Hbc.load(cask_token)
+ system "/usr/bin/open", "--", cask.homepage
+ end
+ end
+ end
+
+ def self.help
+ "opens the homepage of the given Cask"
+ end
+end
diff --git a/Library/Homebrew/cask/lib/hbc/cli/info.rb b/Library/Homebrew/cask/lib/hbc/cli/info.rb
new file mode 100644
index 000000000..dda405705
--- /dev/null
+++ b/Library/Homebrew/cask/lib/hbc/cli/info.rb
@@ -0,0 +1,66 @@
+class Hbc::CLI::Info < Hbc::CLI::Base
+ def self.run(*args)
+ cask_tokens = cask_tokens_from(args)
+ raise Hbc::CaskUnspecifiedError if cask_tokens.empty?
+ cask_tokens.each do |cask_token|
+ odebug "Getting info for Cask #{cask_token}"
+ cask = Hbc.load(cask_token)
+
+ info(cask)
+ end
+ end
+
+ def self.help
+ "displays information about the given Cask"
+ end
+
+ def self.info(cask)
+ puts "#{cask.token}: #{cask.version}"
+ puts formatted_url(cask.homepage) if cask.homepage
+ installation_info(cask)
+ puts "From: #{formatted_url(github_info(cask))}" if github_info(cask)
+ name_info(cask)
+ artifact_info(cask)
+ Hbc::Installer.print_caveats(cask)
+ end
+
+ def self.formatted_url(url)
+ "#{Hbc::Utils::Tty.underline}#{url}#{Hbc::Utils::Tty.reset}"
+ end
+
+ def self.installation_info(cask)
+ if cask.installed?
+ cask.versions.each do |version|
+ versioned_staged_path = cask.caskroom_path.join(version)
+
+ puts versioned_staged_path.to_s
+ .concat(" (")
+ .concat(versioned_staged_path.exist? ? versioned_staged_path.abv : "#{Hbc::Utils::Tty.red}does not exist#{Hbc::Utils::Tty.reset}")
+ .concat(")")
+ end
+ else
+ puts "Not installed"
+ end
+ end
+
+ def self.name_info(cask)
+ ohai cask.name.size > 1 ? "Names" : "Name"
+ puts cask.name.empty? ? "#{Hbc::Utils::Tty.red}None#{Hbc::Utils::Tty.reset}" : cask.name
+ end
+
+ def self.github_info(cask)
+ user, repo, token = Hbc::QualifiedToken.parse(Hbc.all_tokens.detect { |t| t.split("/").last == cask.token })
+ "#{Tap.fetch(user, repo).default_remote}/blob/master/Casks/#{token}.rb"
+ end
+
+ def self.artifact_info(cask)
+ ohai "Artifacts"
+ Hbc::DSL::ORDINARY_ARTIFACT_TYPES.each do |type|
+ next if cask.artifacts[type].empty?
+ cask.artifacts[type].each do |artifact|
+ activatable_item = type == :stage_only ? "<none>" : artifact.first
+ puts "#{activatable_item} (#{type})"
+ end
+ end
+ end
+end
diff --git a/Library/Homebrew/cask/lib/hbc/cli/install.rb b/Library/Homebrew/cask/lib/hbc/cli/install.rb
new file mode 100644
index 000000000..43eab9f3d
--- /dev/null
+++ b/Library/Homebrew/cask/lib/hbc/cli/install.rb
@@ -0,0 +1,60 @@
+
+class Hbc::CLI::Install < Hbc::CLI::Base
+ def self.run(*args)
+ cask_tokens = cask_tokens_from(args)
+ raise Hbc::CaskUnspecifiedError if cask_tokens.empty?
+ force = args.include? "--force"
+ skip_cask_deps = args.include? "--skip-cask-deps"
+ require_sha = args.include? "--require-sha"
+ retval = install_casks cask_tokens, force, skip_cask_deps, require_sha
+ # retval is ternary: true/false/nil
+
+ raise Hbc::CaskError, "nothing to install" if retval.nil?
+ raise Hbc::CaskError, "install incomplete" unless retval
+ end
+
+ def self.install_casks(cask_tokens, force, skip_cask_deps, require_sha)
+ count = 0
+ cask_tokens.each do |cask_token|
+ begin
+ cask = Hbc.load(cask_token)
+ Hbc::Installer.new(cask,
+ force: force,
+ skip_cask_deps: skip_cask_deps,
+ require_sha: require_sha).install
+ count += 1
+ rescue Hbc::CaskAlreadyInstalledError => e
+ opoo e.message
+ count += 1
+ rescue Hbc::CaskAutoUpdatesError => e
+ opoo e.message
+ count += 1
+ rescue Hbc::CaskUnavailableError => e
+ warn_unavailable_with_suggestion cask_token, e
+ rescue Hbc::CaskNoShasumError => e
+ opoo e.message
+ count += 1
+ end
+ end
+ count == 0 ? nil : count == cask_tokens.length
+ end
+
+ def self.warn_unavailable_with_suggestion(cask_token, e)
+ exact_match, partial_matches = Hbc::CLI::Search.search(cask_token)
+ errmsg = e.message
+ if exact_match
+ errmsg.concat(". Did you mean:\n#{exact_match}")
+ elsif !partial_matches.empty?
+ errmsg.concat(". Did you mean one of:\n#{puts_columns(partial_matches.take(20))}\n")
+ end
+ onoe errmsg
+ end
+
+ def self.help
+ "installs the given Cask"
+ end
+
+ def self.needs_init?
+ true
+ end
+end
diff --git a/Library/Homebrew/cask/lib/hbc/cli/internal_audit_modified_casks.rb b/Library/Homebrew/cask/lib/hbc/cli/internal_audit_modified_casks.rb
new file mode 100644
index 000000000..f05dbe803
--- /dev/null
+++ b/Library/Homebrew/cask/lib/hbc/cli/internal_audit_modified_casks.rb
@@ -0,0 +1,135 @@
+class Hbc::CLI::InternalAuditModifiedCasks < Hbc::CLI::InternalUseBase
+ RELEVANT_STANZAS = %i{version sha256 url appcast}.freeze
+
+ class << self
+ def needs_init?
+ true
+ end
+
+ def run(*args)
+ commit_range = commit_range(args)
+ cleanup = args.any? { |a| a =~ %r{^-+c(leanup)?$}i }
+ new(commit_range, cleanup: cleanup).run
+ end
+
+ def commit_range(args)
+ posargs = args.reject { |a| a.empty? || a.chars.first == "-" }
+ odie usage unless posargs.size == 1
+ posargs.first
+ end
+
+ def posargs(args)
+ args.reject { |a| a.empty? || a.chars.first == "-" }
+ end
+
+ def usage
+ <<-EOS.undent
+ Usage: brew cask _audit_modified_casks [options...] <commit range>
+
+ Given a range of Git commits, find any Casks that were modified and run `brew
+ cask audit' on them. If the `url', `version', or `sha256' stanzas were modified,
+ run with the `--download' flag to verify the hash.
+
+ Options:
+ -c, --cleanup
+ Remove all cached downloads. Use with care.
+ EOS
+ end
+ end
+
+ def initialize(commit_range, cleanup: false)
+ @commit_range = commit_range
+ @cleanup = cleanup
+ end
+
+ attr_reader :commit_range
+
+ def cleanup?
+ @cleanup
+ end
+
+ def run
+ at_exit do
+ cleanup
+ end
+
+ Dir.chdir git_root do
+ modified_cask_files.zip(modified_casks).each do |cask_file, cask|
+ audit(cask, cask_file)
+ end
+ end
+ report_failures
+ end
+
+ def git_root
+ @git_root ||= git(*%w[rev-parse --show-toplevel])
+ end
+
+ def modified_cask_files
+ @modified_cask_files ||= git_filter_cask_files("AM")
+ end
+
+ def added_cask_files
+ @added_cask_files ||= git_filter_cask_files("A")
+ end
+
+ def git_filter_cask_files(filter)
+ git("diff", "--name-only", "--diff-filter=#{filter}", commit_range,
+ "--", Pathname.new(git_root).join("Casks", "*.rb").to_s).split("\n")
+ end
+
+ def modified_casks
+ return @modified_casks if defined? @modified_casks
+ @modified_casks = modified_cask_files.map { |f| Hbc.load(f) }
+ if @modified_casks.any?
+ num_modified = @modified_casks.size
+ ohai "#{num_modified} modified #{pluralize('cask', num_modified)}: " \
+ "#{@modified_casks.join(' ')}"
+ end
+ @modified_casks
+ end
+
+ def audit(cask, cask_file)
+ audit_download = audit_download?(cask, cask_file)
+ check_token_conflicts = added_cask_files.include?(cask_file)
+ success = Hbc::Auditor.audit(cask, audit_download: audit_download,
+ check_token_conflicts: check_token_conflicts)
+ failed_casks << cask unless success
+ end
+
+ def failed_casks
+ @failed_casks ||= []
+ end
+
+ def audit_download?(cask, cask_file)
+ cask.sha256 != :no_check && relevant_stanza_modified?(cask_file)
+ end
+
+ def relevant_stanza_modified?(cask_file)
+ out = git("diff", commit_range, "--", cask_file)
+ out =~ %r{^\+\s*(#{RELEVANT_STANZAS.join('|')})}
+ end
+
+ def git(*args)
+ odebug ["git", *args].join(" ")
+ out, err, status = Open3.capture3("git", *args)
+ return out.chomp if status.success?
+ odie err.chomp
+ end
+
+ def report_failures
+ return if failed_casks.empty?
+ num_failed = failed_casks.size
+ cask_pluralized = pluralize("cask", num_failed)
+ odie "audit failed for #{num_failed} #{cask_pluralized}: " \
+ "#{failed_casks.join(' ')}"
+ end
+
+ def pluralize(str, num)
+ num == 1 ? str : "#{str}s"
+ end
+
+ def cleanup
+ Hbc::CLI::Cleanup.run if cleanup?
+ end
+end
diff --git a/Library/Homebrew/cask/lib/hbc/cli/internal_checkurl.rb b/Library/Homebrew/cask/lib/hbc/cli/internal_checkurl.rb
new file mode 100644
index 000000000..d53f420e2
--- /dev/null
+++ b/Library/Homebrew/cask/lib/hbc/cli/internal_checkurl.rb
@@ -0,0 +1,15 @@
+class Hbc::CLI::InternalCheckurl < Hbc::CLI::InternalUseBase
+ def self.run(*args)
+ casks_to_check = args.empty? ? Hbc.all : args.map { |arg| Hbc.load(arg) }
+ casks_to_check.each do |cask|
+ odebug "Checking URL for Cask #{cask}"
+ checker = Hbc::UrlChecker.new(cask)
+ checker.run
+ puts checker.summary
+ end
+ end
+
+ def self.help
+ "checks for bad Cask URLs"
+ end
+end
diff --git a/Library/Homebrew/cask/lib/hbc/cli/internal_dump.rb b/Library/Homebrew/cask/lib/hbc/cli/internal_dump.rb
new file mode 100644
index 000000000..d1cfe8d63
--- /dev/null
+++ b/Library/Homebrew/cask/lib/hbc/cli/internal_dump.rb
@@ -0,0 +1,30 @@
+class Hbc::CLI::InternalDump < Hbc::CLI::InternalUseBase
+ def self.run(*arguments)
+ cask_tokens = cask_tokens_from(arguments)
+ raise Hbc::CaskUnspecifiedError if cask_tokens.empty?
+ retval = dump_casks(*cask_tokens)
+ # retval is ternary: true/false/nil
+
+ raise Hbc::CaskError, "nothing to dump" if retval.nil?
+ raise Hbc::CaskError, "dump incomplete" unless retval
+ end
+
+ def self.dump_casks(*cask_tokens)
+ Hbc.debug = true # Yuck. At the moment this is the only way to make dumps visible
+ count = 0
+ cask_tokens.each do |cask_token|
+ begin
+ cask = Hbc.load(cask_token)
+ count += 1
+ cask.dumpcask
+ rescue StandardError => e
+ opoo "#{cask_token} was not found or would not load: #{e}"
+ end
+ end
+ count == 0 ? nil : count == cask_tokens.length
+ end
+
+ def self.help
+ "Dump the given Cask in YAML format"
+ end
+end
diff --git a/Library/Homebrew/cask/lib/hbc/cli/internal_help.rb b/Library/Homebrew/cask/lib/hbc/cli/internal_help.rb
new file mode 100644
index 000000000..81d7ee673
--- /dev/null
+++ b/Library/Homebrew/cask/lib/hbc/cli/internal_help.rb
@@ -0,0 +1,19 @@
+class Hbc::CLI::InternalHelp < Hbc::CLI::InternalUseBase
+ def self.run(*_ignored)
+ max_command_len = Hbc::CLI.commands.map(&:length).max
+ puts "Unstable Internal-use Commands:\n\n"
+ Hbc::CLI.command_classes.each do |klass|
+ next if klass.visible
+ puts " #{klass.command_name.ljust(max_command_len)} #{help_for(klass)}"
+ end
+ puts "\n"
+ end
+
+ def self.help_for(klass)
+ klass.respond_to?(:help) ? klass.help : nil
+ end
+
+ def self.help
+ "Print help strings for unstable internal-use commands"
+ end
+end
diff --git a/Library/Homebrew/cask/lib/hbc/cli/internal_stanza.rb b/Library/Homebrew/cask/lib/hbc/cli/internal_stanza.rb
new file mode 100644
index 000000000..651a9ae37
--- /dev/null
+++ b/Library/Homebrew/cask/lib/hbc/cli/internal_stanza.rb
@@ -0,0 +1,127 @@
+class Hbc::CLI::InternalStanza < Hbc::CLI::InternalUseBase
+ # Syntax
+ #
+ # brew cask _stanza <stanza_name> [ --table | --yaml | --inspect | --quiet ] [ <cask_token> ... ]
+ #
+ # If no tokens are given, then data for all Casks is returned.
+ #
+ # The pseudo-stanza "artifacts" is available.
+ #
+ # On failure, a blank line is returned on the standard output.
+ #
+ # Examples
+ #
+ # brew cask _stanza appcast --table
+ # brew cask _stanza app --table alfred google-chrome adium voicemac logisim vagrant
+ # brew cask _stanza url --table alfred google-chrome adium voicemac logisim vagrant
+ # brew cask _stanza version --table alfred google-chrome adium voicemac logisim vagrant
+ # brew cask _stanza artifacts --table --inspect alfred google-chrome adium voicemac logisim vagrant
+ # brew cask _stanza artifacts --table --yaml alfred google-chrome adium voicemac logisim vagrant
+ #
+
+ # TODO: this should be retrievable from Hbc::DSL
+ ARTIFACTS = Set.new [
+ :app,
+ :suite,
+ :artifact,
+ :prefpane,
+ :qlplugin,
+ :font,
+ :service,
+ :colorpicker,
+ :binary,
+ :input_method,
+ :internet_plugin,
+ :audio_unit_plugin,
+ :vst_plugin,
+ :vst3_plugin,
+ :screen_saver,
+ :pkg,
+ :installer,
+ :stage_only,
+ :nested_container,
+ :uninstall,
+ :postflight,
+ :uninstall_postflight,
+ :preflight,
+ :uninstall_postflight,
+ ]
+
+ def self.run(*arguments)
+ table = arguments.include? "--table"
+ quiet = arguments.include? "--quiet"
+ format = :to_yaml if arguments.include? "--yaml"
+ format = :inspect if arguments.include? "--inspect"
+ cask_tokens = arguments.reject { |arg| arg.chars.first == "-" }
+ stanza = cask_tokens.shift.to_sym
+ cask_tokens = Hbc.all_tokens if cask_tokens.empty?
+
+ retval = print_stanzas(stanza, format, table, quiet, *cask_tokens)
+
+ # retval is ternary: true/false/nil
+ if retval.nil?
+ exit 1 if quiet
+ raise Hbc::CaskError, "nothing to print"
+ elsif !retval
+ exit 1 if quiet
+ raise Hbc::CaskError, "print incomplete"
+ end
+ end
+
+ def self.print_stanzas(stanza, format = nil, table = nil, quiet = nil, *cask_tokens)
+ count = 0
+ if ARTIFACTS.include?(stanza)
+ artifact_name = stanza
+ stanza = :artifacts
+ end
+
+ cask_tokens.each do |cask_token|
+ print "#{cask_token}\t" if table
+
+ begin
+ cask = Hbc.load(cask_token)
+ rescue StandardError
+ opoo "Cask '#{cask_token}' was not found" unless quiet
+ puts ""
+ next
+ end
+
+ unless cask.respond_to?(stanza)
+ opoo "no such stanza '#{stanza}' on Cask '#{cask_token}'" unless quiet
+ puts ""
+ next
+ end
+
+ begin
+ value = cask.send(stanza)
+ rescue StandardError
+ opoo "failure calling '#{stanza}' on Cask '#{cask_token}'" unless quiet
+ puts ""
+ next
+ end
+
+ if artifact_name && !value.key?(artifact_name)
+ opoo "no such stanza '#{artifact_name}' on Cask '#{cask_token}'" unless quiet
+ puts ""
+ next
+ end
+
+ value = value.fetch(artifact_name).to_a.flatten if artifact_name
+
+ if format
+ puts value.send(format)
+ elsif artifact_name || value.is_a?(Symbol)
+ puts value.inspect
+ else
+ puts value.to_s
+ end
+
+ count += 1
+ end
+ count == 0 ? nil : count == cask_tokens.length
+ end
+
+ def self.help
+ "Extract and render a specific stanza for the given Casks"
+ end
+end
diff --git a/Library/Homebrew/cask/lib/hbc/cli/internal_use_base.rb b/Library/Homebrew/cask/lib/hbc/cli/internal_use_base.rb
new file mode 100644
index 000000000..6a4359ea1
--- /dev/null
+++ b/Library/Homebrew/cask/lib/hbc/cli/internal_use_base.rb
@@ -0,0 +1,9 @@
+class Hbc::CLI::InternalUseBase < Hbc::CLI::Base
+ def self.command_name
+ super.sub(%r{^internal_}i, "_")
+ end
+
+ def self.visible
+ false
+ end
+end
diff --git a/Library/Homebrew/cask/lib/hbc/cli/list.rb b/Library/Homebrew/cask/lib/hbc/cli/list.rb
new file mode 100644
index 000000000..ce507a827
--- /dev/null
+++ b/Library/Homebrew/cask/lib/hbc/cli/list.rb
@@ -0,0 +1,82 @@
+class Hbc::CLI::List < Hbc::CLI::Base
+ def self.run(*arguments)
+ @options = {}
+ @options[:one] = true if arguments.delete("-1")
+ @options[:versions] = true if arguments.delete("--versions")
+
+ if arguments.delete("-l")
+ @options[:one] = true
+ opoo "Option -l is obsolete! Implying option -1."
+ end
+
+ retval = arguments.any? ? list(*arguments) : list_installed
+ # retval is ternary: true/false/nil
+ if retval.nil? && !arguments.any?
+ opoo "nothing to list" # special case: avoid exit code
+ elsif retval.nil?
+ raise Hbc::CaskError, "nothing to list"
+ elsif !retval
+ raise Hbc::CaskError, "listing incomplete"
+ end
+ end
+
+ def self.list(*cask_tokens)
+ count = 0
+
+ cask_tokens.each do |cask_token|
+ odebug "Listing files for Cask #{cask_token}"
+ begin
+ cask = Hbc.load(cask_token)
+
+ if cask.installed?
+ if @options[:one]
+ puts cask.token
+ elsif @options[:versions]
+ puts format_versioned(cask)
+ else
+ installed_caskfile = cask.metadata_master_container_path.join(*cask.timestamped_versions.last, "Casks", "#{cask_token}.rb")
+ cask = Hbc.load(installed_caskfile)
+ list_artifacts(cask)
+ end
+
+ count += 1
+ else
+ opoo "#{cask} is not installed"
+ end
+ rescue Hbc::CaskUnavailableError => e
+ onoe e
+ end
+ end
+
+ count == 0 ? nil : count == cask_tokens.length
+ end
+
+ def self.list_artifacts(cask)
+ Hbc::Artifact.for_cask(cask).each do |artifact|
+ summary = artifact.new(cask).summary
+ ohai summary[:english_description], summary[:contents] unless summary.empty?
+ end
+ end
+
+ def self.list_installed
+ installed_casks = Hbc.installed
+
+ if @options[:one]
+ puts installed_casks.map(&:to_s)
+ elsif @options[:versions]
+ puts installed_casks.map(&method(:format_versioned))
+ else
+ puts_columns installed_casks.map(&:to_s)
+ end
+
+ installed_casks.empty? ? nil : true
+ end
+
+ def self.format_versioned(cask)
+ cask.to_s.concat(cask.versions.map(&:to_s).join(" ").prepend(" "))
+ end
+
+ def self.help
+ "with no args, lists installed Casks; given installed Casks, lists staged files"
+ end
+end
diff --git a/Library/Homebrew/cask/lib/hbc/cli/search.rb b/Library/Homebrew/cask/lib/hbc/cli/search.rb
new file mode 100644
index 000000000..c356128a6
--- /dev/null
+++ b/Library/Homebrew/cask/lib/hbc/cli/search.rb
@@ -0,0 +1,56 @@
+class Hbc::CLI::Search < Hbc::CLI::Base
+ def self.run(*arguments)
+ render_results(*search(*arguments))
+ end
+
+ def self.extract_regexp(string)
+ if string =~ %r{^/(.*)/$}
+ Regexp.last_match[1]
+ else
+ false
+ end
+ end
+
+ def self.search(*arguments)
+ exact_match = nil
+ partial_matches = []
+ search_term = arguments.join(" ")
+ search_regexp = extract_regexp arguments.first
+ if search_regexp
+ search_term = arguments.first
+ partial_matches = Hbc::CLI.nice_listing(Hbc.all_tokens).grep(%r{#{search_regexp}}i)
+ else
+ # suppressing search of the font Tap is a quick hack until behavior can be made configurable
+ all_tokens = Hbc::CLI.nice_listing Hbc.all_tokens.reject { |t| %r{^caskroom/homebrew-fonts/}.match(t) }
+ simplified_tokens = all_tokens.map { |t| t.sub(%r{^.*\/}, "").gsub(%r{[^a-z0-9]+}i, "") }
+ simplified_search_term = search_term.sub(%r{\.rb$}i, "").gsub(%r{[^a-z0-9]+}i, "")
+ exact_match = simplified_tokens.grep(%r{^#{simplified_search_term}$}i) { |t| all_tokens[simplified_tokens.index(t)] }.first
+ partial_matches = simplified_tokens.grep(%r{#{simplified_search_term}}i) { |t| all_tokens[simplified_tokens.index(t)] }
+ partial_matches.delete(exact_match)
+ end
+ [exact_match, partial_matches, search_term]
+ end
+
+ def self.render_results(exact_match, partial_matches, search_term)
+ if !exact_match && partial_matches.empty?
+ puts "No Cask found for \"#{search_term}\"."
+ return
+ end
+ if exact_match
+ ohai "Exact match"
+ puts exact_match
+ end
+ unless partial_matches.empty?
+ if extract_regexp search_term
+ ohai "Regexp matches"
+ else
+ ohai "Partial matches"
+ end
+ puts_columns partial_matches
+ end
+ end
+
+ def self.help
+ "searches all known Casks"
+ end
+end
diff --git a/Library/Homebrew/cask/lib/hbc/cli/style.rb b/Library/Homebrew/cask/lib/hbc/cli/style.rb
new file mode 100644
index 000000000..ac7cbfb44
--- /dev/null
+++ b/Library/Homebrew/cask/lib/hbc/cli/style.rb
@@ -0,0 +1,69 @@
+require "English"
+
+class Hbc::CLI::Style < Hbc::CLI::Base
+ def self.help
+ "checks Cask style using RuboCop"
+ end
+
+ def self.run(*args)
+ retval = new(args).run
+ raise Hbc::CaskError, "style check failed" unless retval
+ end
+
+ attr_reader :args
+ def initialize(args)
+ @args = args
+ end
+
+ def run
+ install_rubocop
+ system "rubocop", *rubocop_args, "--", *cask_paths
+ $CHILD_STATUS.success?
+ end
+
+ RUBOCOP_CASK_VERSION = "~> 0.8.3".freeze
+
+ def install_rubocop
+ Hbc::Utils.capture_stderr do
+ begin
+ Homebrew.install_gem_setup_path! "rubocop-cask", RUBOCOP_CASK_VERSION, "rubocop"
+ rescue SystemExit
+ raise Hbc::CaskError, $stderr.string.chomp.sub("#{::Tty.red}Error#{::Tty.reset}: ", "")
+ end
+ end
+ end
+
+ def cask_paths
+ @cask_paths ||= if cask_tokens.empty?
+ Hbc.all_tapped_cask_dirs
+ elsif cask_tokens.any? { |file| File.exist?(file) }
+ cask_tokens
+ else
+ cask_tokens.map { |token| Hbc.path(token) }
+ end
+ end
+
+ def cask_tokens
+ @cask_tokens ||= self.class.cask_tokens_from(args)
+ end
+
+ def rubocop_args
+ fix? ? autocorrect_args : default_args
+ end
+
+ def default_args
+ ["--format", "simple", "--force-exclusion", "--config", rubocop_config]
+ end
+
+ def autocorrect_args
+ default_args + ["--auto-correct"]
+ end
+
+ def rubocop_config
+ Hbc.default_tap.cask_dir.join(".rubocop.yml")
+ end
+
+ def fix?
+ args.any? { |arg| arg =~ %r{--(fix|(auto-?)?correct)} }
+ end
+end
diff --git a/Library/Homebrew/cask/lib/hbc/cli/uninstall.rb b/Library/Homebrew/cask/lib/hbc/cli/uninstall.rb
new file mode 100644
index 000000000..cd98b6e61
--- /dev/null
+++ b/Library/Homebrew/cask/lib/hbc/cli/uninstall.rb
@@ -0,0 +1,40 @@
+class Hbc::CLI::Uninstall < Hbc::CLI::Base
+ def self.run(*args)
+ cask_tokens = cask_tokens_from(args)
+ raise Hbc::CaskUnspecifiedError if cask_tokens.empty?
+ force = args.include? "--force"
+
+ cask_tokens.each do |cask_token|
+ odebug "Uninstalling Cask #{cask_token}"
+ cask = Hbc.load(cask_token)
+
+ raise Hbc::CaskNotInstalledError, cask unless cask.installed? || force
+
+ latest_installed_version = cask.timestamped_versions.last
+
+ unless latest_installed_version.nil?
+ latest_installed_cask_file = cask.metadata_master_container_path
+ .join(latest_installed_version.join(File::Separator),
+ "Casks", "#{cask_token}.rb")
+
+ # use the same cask file that was used for installation, if possible
+ cask = Hbc.load(latest_installed_cask_file) if latest_installed_cask_file.exist?
+ end
+
+ Hbc::Installer.new(cask, force: force).uninstall
+
+ next if (versions = cask.versions).empty?
+
+ single = versions.count == 1
+
+ puts <<-EOF.undent
+ #{cask_token} #{versions.join(', ')} #{single ? 'is' : 'are'} still installed.
+ Remove #{single ? 'it' : 'them all'} with `brew cask uninstall --force #{cask_token}`.
+ EOF
+ end
+ end
+
+ def self.help
+ "uninstalls the given Cask"
+ end
+end
diff --git a/Library/Homebrew/cask/lib/hbc/cli/update.rb b/Library/Homebrew/cask/lib/hbc/cli/update.rb
new file mode 100644
index 000000000..ceb947544
--- /dev/null
+++ b/Library/Homebrew/cask/lib/hbc/cli/update.rb
@@ -0,0 +1,16 @@
+class Hbc::CLI::Update < Hbc::CLI::Base
+ def self.run(*_ignored)
+ result = Hbc::SystemCommand.run(Hbc.homebrew_executable,
+ args: %w[update])
+ # TODO: separating stderr/stdout is undesirable here.
+ # Hbc::SystemCommand should have an option for plain
+ # unbuffered output.
+ print result.stdout
+ $stderr.print result.stderr
+ exit result.exit_status
+ end
+
+ def self.help
+ "a synonym for 'brew update'"
+ end
+end
diff --git a/Library/Homebrew/cask/lib/hbc/cli/zap.rb b/Library/Homebrew/cask/lib/hbc/cli/zap.rb
new file mode 100644
index 000000000..081378330
--- /dev/null
+++ b/Library/Homebrew/cask/lib/hbc/cli/zap.rb
@@ -0,0 +1,15 @@
+class Hbc::CLI::Zap < Hbc::CLI::Base
+ def self.run(*args)
+ cask_tokens = cask_tokens_from(args)
+ raise Hbc::CaskUnspecifiedError if cask_tokens.empty?
+ cask_tokens.each do |cask_token|
+ odebug "Zapping Cask #{cask_token}"
+ cask = Hbc.load(cask_token)
+ Hbc::Installer.new(cask).zap
+ end
+ end
+
+ def self.help
+ "zaps all files associated with the given Cask"
+ end
+end