Skip to content

Commit

Permalink
Merge pull request #1334 from dduugg/abstract-cmd
Browse files Browse the repository at this point in the history
Port bundle command to use AbstractCommand
  • Loading branch information
MikeMcQuaid authored Apr 2, 2024
2 parents e6af487 + 5bdef58 commit 984473e
Showing 1 changed file with 187 additions and 188 deletions.
375 changes: 187 additions & 188 deletions cmd/bundle.rb
Original file line number Diff line number Diff line change
@@ -1,197 +1,196 @@
# frozen_string_literal: true

require "cli/parser"
module Homebrew
module_function

def bundle_args
Homebrew::CLI::Parser.new do
usage_banner <<~EOS
`bundle` [<subcommand>]
Bundler for non-Ruby dependencies from Homebrew, Homebrew Cask, Mac App Store, Whalebrew and Visual Studio Code.
`brew bundle` [`install`]:
Install and upgrade (by default) all dependencies from the `Brewfile`.
You can specify the `Brewfile` location using `--file` or by setting the `HOMEBREW_BUNDLE_FILE` environment variable.
You can skip the installation of dependencies by adding space-separated values to one or more of the following environment variables: `HOMEBREW_BUNDLE_BREW_SKIP`, `HOMEBREW_BUNDLE_CASK_SKIP`, `HOMEBREW_BUNDLE_MAS_SKIP`, `HOMEBREW_BUNDLE_WHALEBREW_SKIP`, `HOMEBREW_BUNDLE_TAP_SKIP`.
`brew bundle` will output a `Brewfile.lock.json` in the same directory as the `Brewfile` if all dependencies are installed successfully. This contains dependency and system status information which can be useful for debugging `brew bundle` failures and replicating a "last known good build" state. You can opt-out of this behaviour by setting the `HOMEBREW_BUNDLE_NO_LOCK` environment variable or passing the `--no-lock` option. You may wish to check this file into the same version control system as your `Brewfile` (or ensure your version control system ignores it if you'd prefer to rely on debugging information from a local machine).
`brew bundle dump`:
Write all installed casks/formulae/images/taps into a `Brewfile` in the current directory.
`brew bundle cleanup`:
Uninstall all dependencies not present in the `Brewfile`.
This workflow is useful for maintainers or testers who regularly install lots of formulae.
`brew bundle check`:
Check if all dependencies present in the `Brewfile` are installed.
This provides a successful exit code if everything is up-to-date, making it useful for scripting.
`brew bundle list`:
List all dependencies present in the `Brewfile`.
By default, only Homebrew formula dependencies are listed.
`brew bundle exec` <command>:
Run an external command in an isolated build environment based on the `Brewfile` dependencies.
This sanitized build environment ignores unrequested dependencies, which makes sure that things you didn't specify in your `Brewfile` won't get picked up by commands like `bundle install`, `npm install`, etc. It will also add compiler flags which will help with finding keg-only dependencies like `openssl`, `icu4c`, etc.
EOS
flag "--file=",
description: "Read the `Brewfile` from this location. Use `--file=-` to pipe to stdin/stdout."
switch "--global",
description: "Read the `Brewfile` from `~/.Brewfile` or " \
"the `HOMEBREW_BUNDLE_FILE_GLOBAL` environment variable, if set."
switch "-v", "--verbose",
description: "`install` prints output from commands as they are run. " \
"`check` lists all missing dependencies."
switch "--no-upgrade",
description: "`install` does not run `brew upgrade` on outdated dependencies. " \
"Note they may still be upgraded by `brew install` if needed."
switch "-f", "--force",
description: "`install` runs with `--force`/`--overwrite`. " \
"`dump` overwrites an existing `Brewfile`. " \
"`cleanup` actually performs its cleanup operations."
switch "--cleanup",
env: :bundle_install_cleanup,
description: "`install` performs cleanup operation, same as running `cleanup --force`. " \
"This is enabled by default if `HOMEBREW_BUNDLE_INSTALL_CLEANUP` is set and " \
"`--global` is passed."
switch "--no-lock",
description: "`install` does not output a `Brewfile.lock.json`."
switch "--all",
description: "`list` all dependencies."
switch "--formula", "--brews",
description: "`list` Homebrew formula dependencies."
switch "--cask", "--casks",
description: "`list` Homebrew cask dependencies."
switch "--tap", "--taps",
description: "`list` Homebrew tap dependencies."
switch "--mas",
description: "`list` Mac App Store dependencies."
switch "--whalebrew",
description: "`list` Whalebrew dependencies."
switch "--vscode",
description: "`list` VSCode extensions."
switch "--describe",
env: :bundle_dump_describe,
description: "`dump` adds a description comment above each line, unless the " \
"dependency does not have a description. " \
"This is enabled by default if `HOMEBREW_BUNDLE_DUMP_DESCRIBE` is set."
switch "--no-restart",
description: "`dump` does not add `restart_service` to formula lines."
switch "--zap",
description: "`cleanup` casks using the `zap` command instead of `uninstall`."
end
end
require "abstract_command"

def bundle
args = bundle_args.parse

# Keep this after the .parse to keep --help fast.
require_relative "../lib/bundle"

begin
case subcommand = args.named.first.presence
when nil, "install"
Bundle::Commands::Install.run(
global: args.global?,
file: args.file,
no_lock: args.no_lock?,
no_upgrade: args.no_upgrade?,
verbose: args.verbose?,
force: args.force?,
)

cleanup = if ENV.fetch("HOMEBREW_BUNDLE_INSTALL_CLEANUP", nil)
args.global?
else
args.cleanup?
end
module Homebrew
module Cmd
class BundleCmd < AbstractCommand
cmd_args do
usage_banner <<~EOS
`bundle` [<subcommand>]
Bundler for non-Ruby dependencies from Homebrew, Homebrew Cask, Mac App Store, Whalebrew and Visual Studio Code.
`brew bundle` [`install`]:
Install and upgrade (by default) all dependencies from the `Brewfile`.
You can specify the `Brewfile` location using `--file` or by setting the `HOMEBREW_BUNDLE_FILE` environment variable.
You can skip the installation of dependencies by adding space-separated values to one or more of the following environment variables: `HOMEBREW_BUNDLE_BREW_SKIP`, `HOMEBREW_BUNDLE_CASK_SKIP`, `HOMEBREW_BUNDLE_MAS_SKIP`, `HOMEBREW_BUNDLE_WHALEBREW_SKIP`, `HOMEBREW_BUNDLE_TAP_SKIP`.
`brew bundle` will output a `Brewfile.lock.json` in the same directory as the `Brewfile` if all dependencies are installed successfully. This contains dependency and system status information which can be useful for debugging `brew bundle` failures and replicating a "last known good build" state. You can opt-out of this behaviour by setting the `HOMEBREW_BUNDLE_NO_LOCK` environment variable or passing the `--no-lock` option. You may wish to check this file into the same version control system as your `Brewfile` (or ensure your version control system ignores it if you'd prefer to rely on debugging information from a local machine).
`brew bundle dump`:
Write all installed casks/formulae/images/taps into a `Brewfile` in the current directory.
`brew bundle cleanup`:
Uninstall all dependencies not present in the `Brewfile`.
This workflow is useful for maintainers or testers who regularly install lots of formulae.
`brew bundle check`:
Check if all dependencies present in the `Brewfile` are installed.
This provides a successful exit code if everything is up-to-date, making it useful for scripting.
`brew bundle list`:
List all dependencies present in the `Brewfile`.
By default, only Homebrew formula dependencies are listed.
`brew bundle exec` <command>:
Run an external command in an isolated build environment based on the `Brewfile` dependencies.
This sanitized build environment ignores unrequested dependencies, which makes sure that things you didn't specify in your `Brewfile` won't get picked up by commands like `bundle install`, `npm install`, etc. It will also add compiler flags which will help with finding keg-only dependencies like `openssl`, `icu4c`, etc.
EOS
flag "--file=",
description: "Read the `Brewfile` from this location. Use `--file=-` to pipe to stdin/stdout."
switch "--global",
description: "Read the `Brewfile` from `~/.Brewfile` or " \
"the `HOMEBREW_BUNDLE_FILE_GLOBAL` environment variable, if set."
switch "-v", "--verbose",
description: "`install` prints output from commands as they are run. " \
"`check` lists all missing dependencies."
switch "--no-upgrade",
description: "`install` does not run `brew upgrade` on outdated dependencies. " \
"Note they may still be upgraded by `brew install` if needed."
switch "-f", "--force",
description: "`install` runs with `--force`/`--overwrite`. " \
"`dump` overwrites an existing `Brewfile`. " \
"`cleanup` actually performs its cleanup operations."
switch "--cleanup",
env: :bundle_install_cleanup,
description: "`install` performs cleanup operation, same as running `cleanup --force`. " \
"This is enabled by default if `HOMEBREW_BUNDLE_INSTALL_CLEANUP` is set and " \
"`--global` is passed."
switch "--no-lock",
description: "`install` does not output a `Brewfile.lock.json`."
switch "--all",
description: "`list` all dependencies."
switch "--formula", "--brews",
description: "`list` Homebrew formula dependencies."
switch "--cask", "--casks",
description: "`list` Homebrew cask dependencies."
switch "--tap", "--taps",
description: "`list` Homebrew tap dependencies."
switch "--mas",
description: "`list` Mac App Store dependencies."
switch "--whalebrew",
description: "`list` Whalebrew dependencies."
switch "--vscode",
description: "`list` VSCode extensions."
switch "--describe",
env: :bundle_dump_describe,
description: "`dump` adds a description comment above each line, unless the " \
"dependency does not have a description. " \
"This is enabled by default if `HOMEBREW_BUNDLE_DUMP_DESCRIBE` is set."
switch "--no-restart",
description: "`dump` does not add `restart_service` to formula lines."
switch "--zap",
description: "`cleanup` casks using the `zap` command instead of `uninstall`."
end

if cleanup
Bundle::Commands::Cleanup.run(
global: args.global?,
file: args.file,
force: true,
zap: args.zap?,
)
def run
# Keep this inside `run` to keep --help fast.
require_relative "../lib/bundle"

begin
case subcommand = args.named.first.presence
when nil, "install"
Bundle::Commands::Install.run(
global: args.global?,
file: args.file,
no_lock: args.no_lock?,
no_upgrade: args.no_upgrade?,
verbose: args.verbose?,
force: args.force?,
)

cleanup = if ENV.fetch("HOMEBREW_BUNDLE_INSTALL_CLEANUP", nil)
args.global?
else
args.cleanup?
end

if cleanup
Bundle::Commands::Cleanup.run(
global: args.global?,
file: args.file,
force: true,
zap: args.zap?,
)
end
when "dump"
Bundle::Commands::Dump.run(
global: args.global?,
file: args.file,
describe: args.describe?,
force: args.force?,
no_restart: args.no_restart?,
all: args.all?,
taps: args.taps?,
brews: args.brews?,
casks: args.casks?,
mas: args.mas?,
whalebrew: args.whalebrew?,
vscode: args.vscode?,
)
when "cleanup"
Bundle::Commands::Cleanup.run(
global: args.global?,
file: args.file,
force: args.force?,
zap: args.zap?,
)
when "check"
Bundle::Commands::Check.run(
global: args.global?,
file: args.file,
no_upgrade: args.no_upgrade?,
verbose: args.verbose?,
)
when "exec"
_subcommand, *named_args = args.named
Bundle::Commands::Exec.run(
*named_args,
global: args.global?,
file: args.file,
)
when "list"
Bundle::Commands::List.run(
global: args.global?,
file: args.file,
all: args.all?,
casks: args.casks?,
taps: args.taps?,
mas: args.mas?,
whalebrew: args.whalebrew?,
vscode: args.vscode?,
brews: args.brews?,
)
else
raise UsageError, "unknown subcommand: #{subcommand}"
end
rescue SystemExit => e
Homebrew.failed = true unless e.success?
puts "Kernel.exit" if args.debug?
rescue Interrupt
puts # seemingly a newline is typical
Homebrew.failed = true
rescue RuntimeError, SystemCallError => e
raise if e.message.empty?

onoe e
puts e.backtrace if args.debug?
Homebrew.failed = true
rescue => e
onoe e
puts "#{Tty.bold}Please report this bug:#{Tty.reset}"
puts " #{Formatter.url("https://github.com/Homebrew/homebrew-bundle/issues")}"
puts e.backtrace
Homebrew.failed = true
end
when "dump"
Bundle::Commands::Dump.run(
global: args.global?,
file: args.file,
describe: args.describe?,
force: args.force?,
no_restart: args.no_restart?,
all: args.all?,
taps: args.taps?,
brews: args.brews?,
casks: args.casks?,
mas: args.mas?,
whalebrew: args.whalebrew?,
vscode: args.vscode?,
)
when "cleanup"
Bundle::Commands::Cleanup.run(
global: args.global?,
file: args.file,
force: args.force?,
zap: args.zap?,
)
when "check"
Bundle::Commands::Check.run(
global: args.global?,
file: args.file,
no_upgrade: args.no_upgrade?,
verbose: args.verbose?,
)
when "exec"
_subcommand, *named_args = args.named
Bundle::Commands::Exec.run(
*named_args,
global: args.global?,
file: args.file,
)
when "list"
Bundle::Commands::List.run(
global: args.global?,
file: args.file,
all: args.all?,
casks: args.casks?,
taps: args.taps?,
mas: args.mas?,
whalebrew: args.whalebrew?,
vscode: args.vscode?,
brews: args.brews?,
)
else
raise UsageError, "unknown subcommand: #{subcommand}"
end
rescue SystemExit => e
Homebrew.failed = true unless e.success?
puts "Kernel.exit" if args.debug?
rescue Interrupt
puts # seemingly a newline is typical
Homebrew.failed = true
rescue RuntimeError, SystemCallError => e
raise if e.message.empty?

onoe e
puts e.backtrace if args.debug?
Homebrew.failed = true
rescue => e
onoe e
puts "#{Tty.bold}Please report this bug:#{Tty.reset}"
puts " #{Formatter.url("https://github.com/Homebrew/homebrew-bundle/issues")}"
puts e.backtrace
Homebrew.failed = true
end
end
end

0 comments on commit 984473e

Please sign in to comment.