Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Begin porting non-dev commands to use AbstractCommand #16975

Merged
merged 23 commits into from
Mar 31, 2024
Merged
Show file tree
Hide file tree
Changes from 22 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 3 additions & 0 deletions Library/Homebrew/abstract_command.rb
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
# typed: strong
# frozen_string_literal: true

require "cli/parser"

module Homebrew
# Subclass this to implement a `brew` command. This is preferred to declaring a named function in the `Homebrew`
# module, because:
# - Each Command lives in an isolated namespace.
# - Each Command implements a defined interface.
# - `args` is available as an ivar, and thus does not need to be passed as an argument to helper methods.
# - Subclasses no longer need to reference `CLI::Parser` or parse args explicitly.
#
# To subclass, implement a `run` method and provide a `cmd_args` block to document the command and its allowed args.
# To generate method signatures for command args, run `brew typecheck --update`.
Expand Down
2 changes: 1 addition & 1 deletion Library/Homebrew/build.rb
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@ def fixopt(formula)
end

begin
args = Homebrew.install_args.parse
args = Homebrew::Cmd::InstallCmd.new.args
Context.current = args.context

error_pipe = UNIXSocket.open(ENV.fetch("HOMEBREW_ERROR_PIPE"), &:recv_io)
Expand Down
73 changes: 35 additions & 38 deletions Library/Homebrew/cmd/analytics.rb
Original file line number Diff line number Diff line change
@@ -1,51 +1,48 @@
# typed: strict
# frozen_string_literal: true

require "cli/parser"
require "abstract_command"

module Homebrew
module_function
module Cmd
class Analytics < AbstractCommand
cmd_args do
description <<~EOS
Control Homebrew's anonymous aggregate user behaviour analytics.
Read more at <https://docs.brew.sh/Analytics>.

sig { returns(CLI::Parser) }
def analytics_args
Homebrew::CLI::Parser.new do
description <<~EOS
Control Homebrew's anonymous aggregate user behaviour analytics.
Read more at <https://docs.brew.sh/Analytics>.
`brew analytics` [`state`]:
Display the current state of Homebrew's analytics.

`brew analytics` [`state`]:
Display the current state of Homebrew's analytics.
`brew analytics` (`on`|`off`):
Turn Homebrew's analytics on or off respectively.
EOS

`brew analytics` (`on`|`off`):
Turn Homebrew's analytics on or off respectively.
EOS

named_args %w[state on off regenerate-uuid], max: 1
end
end

sig { void }
def analytics
args = analytics_args.parse
named_args %w[state on off regenerate-uuid], max: 1
end

case args.named.first
when nil, "state"
if Utils::Analytics.disabled?
puts "InfluxDB analytics are disabled."
else
puts "InfluxDB analytics are enabled."
sig { override.void }
def run
case args.named.first
when nil, "state"
if Utils::Analytics.disabled?
puts "InfluxDB analytics are disabled."
else
puts "InfluxDB analytics are enabled."
end
puts "Google Analytics were destroyed."
when "on"
Utils::Analytics.enable!
when "off"
Utils::Analytics.disable!
when "regenerate-uuid"
Utils::Analytics.delete_uuid!
opoo "Homebrew no longer uses an analytics UUID so this has been deleted!"
puts "brew analytics regenerate-uuid is no longer necessary."
else
raise UsageError, "unknown subcommand: #{args.named.first}"
end
end
puts "Google Analytics were destroyed."
when "on"
Utils::Analytics.enable!
when "off"
Utils::Analytics.disable!
when "regenerate-uuid"
Utils::Analytics.delete_uuid!
opoo "Homebrew no longer uses an analytics UUID so this has been deleted!"
puts "brew analytics regenerate-uuid is no longer necessary."
else
raise UsageError, "unknown subcommand: #{args.named.first}"
end
end
end
33 changes: 16 additions & 17 deletions Library/Homebrew/cmd/autoremove.rb
Original file line number Diff line number Diff line change
@@ -1,27 +1,26 @@
# typed: strict
# frozen_string_literal: true

require "abstract_command"
require "cleanup"
require "cli/parser"

module Homebrew
sig { returns(CLI::Parser) }
def self.autoremove_args
Homebrew::CLI::Parser.new do
description <<~EOS
Uninstall formulae that were only installed as a dependency of another formula and are now no longer needed.
EOS
switch "-n", "--dry-run",
description: "List what would be uninstalled, but do not actually uninstall anything."
module Cmd
class Autoremove < AbstractCommand
cmd_args do
description <<~EOS
Uninstall formulae that were only installed as a dependency of another formula and are now no longer needed.
EOS
switch "-n", "--dry-run",
description: "List what would be uninstalled, but do not actually uninstall anything."

named_args :none
end
end
named_args :none
end

sig { void }
def self.autoremove
args = autoremove_args.parse

Cleanup.autoremove(dry_run: args.dry_run?)
sig { override.void }
def run
Cleanup.autoremove(dry_run: args.dry_run?)
end
end
end
end
113 changes: 55 additions & 58 deletions Library/Homebrew/cmd/cleanup.rb
Original file line number Diff line number Diff line change
@@ -1,75 +1,72 @@
# typed: strict
# frozen_string_literal: true

require "abstract_command"
require "cleanup"
require "cli/parser"

module Homebrew
module_function
module Cmd
class CleanupCmd < AbstractCommand
cmd_args do
days = Homebrew::EnvConfig::ENVS[:HOMEBREW_CLEANUP_MAX_AGE_DAYS][:default]
description <<~EOS
Remove stale lock files and outdated downloads for all formulae and casks,
and remove old versions of installed formulae. If arguments are specified,
only do this for the given formulae and casks. Removes all downloads more than
#{days} days old. This can be adjusted with `HOMEBREW_CLEANUP_MAX_AGE_DAYS`.
EOS
flag "--prune=",
description: "Remove all cache files older than specified <days>. " \
"If you want to remove everything, use `--prune=all`."
switch "-n", "--dry-run",
description: "Show what would be removed, but do not actually remove anything."
switch "-s",
description: "Scrub the cache, including downloads for even the latest versions. " \
"Note that downloads for any installed formulae or casks will still not be deleted. " \
"If you want to delete those too: `rm -rf \"$(brew --cache)\"`"
switch "--prune-prefix",
description: "Only prune the symlinks and directories from the prefix and remove no other files."

sig { returns(CLI::Parser) }
def cleanup_args
Homebrew::CLI::Parser.new do
days = Homebrew::EnvConfig::ENVS[:HOMEBREW_CLEANUP_MAX_AGE_DAYS][:default]
description <<~EOS
Remove stale lock files and outdated downloads for all formulae and casks,
and remove old versions of installed formulae. If arguments are specified,
only do this for the given formulae and casks. Removes all downloads more than
#{days} days old. This can be adjusted with `HOMEBREW_CLEANUP_MAX_AGE_DAYS`.
EOS
flag "--prune=",
description: "Remove all cache files older than specified <days>. " \
"If you want to remove everything, use `--prune=all`."
switch "-n", "--dry-run",
description: "Show what would be removed, but do not actually remove anything."
switch "-s",
description: "Scrub the cache, including downloads for even the latest versions. " \
"Note that downloads for any installed formulae or casks will still not be deleted. " \
"If you want to delete those too: `rm -rf \"$(brew --cache)\"`"
switch "--prune-prefix",
description: "Only prune the symlinks and directories from the prefix and remove no other files."
named_args [:formula, :cask]
end

named_args [:formula, :cask]
end
end
sig { override.void }
def run
days = args.prune.presence&.then do |prune|
case prune
when /\A\d+\Z/
prune.to_i
when "all"
0
else
raise UsageError, "`--prune` expects an integer or `all`."
end
end

sig { void }
def cleanup
args = cleanup_args.parse
cleanup = Cleanup.new(*args.named, dry_run: args.dry_run?, scrub: args.s?, days:)
if args.prune_prefix?
cleanup.prune_prefix_symlinks_and_directories
return
end

days = args.prune.presence&.then do |prune|
case prune
when /\A\d+\Z/
prune.to_i
when "all"
0
else
raise UsageError, "`--prune` expects an integer or `all`."
end
end
cleanup.clean!(quiet: args.quiet?, periodic: false)

cleanup = Cleanup.new(*args.named, dry_run: args.dry_run?, scrub: args.s?, days:)
if args.prune_prefix?
cleanup.prune_prefix_symlinks_and_directories
return
end
unless cleanup.disk_cleanup_size.zero?
disk_space = disk_usage_readable(cleanup.disk_cleanup_size)
if args.dry_run?
ohai "This operation would free approximately #{disk_space} of disk space."
else
ohai "This operation has freed approximately #{disk_space} of disk space."
end
end

cleanup.clean!(quiet: args.quiet?, periodic: false)
return if cleanup.unremovable_kegs.empty?

unless cleanup.disk_cleanup_size.zero?
disk_space = disk_usage_readable(cleanup.disk_cleanup_size)
if args.dry_run?
ohai "This operation would free approximately #{disk_space} of disk space."
else
ohai "This operation has freed approximately #{disk_space} of disk space."
ofail <<~EOS
Could not cleanup old kegs! Fix your permissions on:
#{cleanup.unremovable_kegs.join "\n "}
EOS
end
end

return if cleanup.unremovable_kegs.empty?

ofail <<~EOS
Could not cleanup old kegs! Fix your permissions on:
#{cleanup.unremovable_kegs.join "\n "}
EOS
end
end
79 changes: 38 additions & 41 deletions Library/Homebrew/cmd/commands.rb
Original file line number Diff line number Diff line change
@@ -1,49 +1,46 @@
# typed: strict
# frozen_string_literal: true

require "cli/parser"
require "abstract_command"

module Homebrew
module_function

sig { returns(CLI::Parser) }
def commands_args
Homebrew::CLI::Parser.new do
description <<~EOS
Show lists of built-in and external commands.
EOS
switch "-q", "--quiet",
description: "List only the names of commands without category headers."
switch "--include-aliases",
depends_on: "--quiet",
description: "Include aliases of internal commands."

named_args :none
end
end

sig { void }
def commands
args = commands_args.parse

if args.quiet?
puts Formatter.columns(Commands.commands(aliases: args.include_aliases?))
return
end

prepend_separator = T.let(false, T::Boolean)

{
"Built-in commands" => Commands.internal_commands,
"Built-in developer commands" => Commands.internal_developer_commands,
"External commands" => Commands.external_commands,
}.each do |title, commands|
next if commands.blank?

puts if prepend_separator
ohai title, Formatter.columns(commands)

prepend_separator ||= true
module Cmd
class CommandsCmd < AbstractCommand
cmd_args do
description <<~EOS
Show lists of built-in and external commands.
EOS
switch "-q", "--quiet",
description: "List only the names of commands without category headers."
switch "--include-aliases",
depends_on: "--quiet",
description: "Include aliases of internal commands."

named_args :none
end

sig { override.void }
def run
if args.quiet?
puts Formatter.columns(Commands.commands(aliases: args.include_aliases?))
return
end

prepend_separator = T.let(false, T::Boolean)

{
"Built-in commands" => Commands.internal_commands,
"Built-in developer commands" => Commands.internal_developer_commands,
"External commands" => Commands.external_commands,
}.each do |title, commands|
next if commands.blank?

puts if prepend_separator
ohai title, Formatter.columns(commands)

prepend_separator ||= true
end
end
end
end
end