Merge pull request #16975 from Homebrew/ported-cmds
Begin porting non-dev commands to use AbstractCommand
This commit is contained in:
commit
21dd3c263f
@ -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`.
|
||||
|
||||
@ -217,7 +217,7 @@ class Build
|
||||
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)
|
||||
|
||||
@ -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
|
||||
|
||||
case args.named.first
|
||||
when nil, "state"
|
||||
if Utils::Analytics.disabled?
|
||||
puts "InfluxDB analytics are disabled."
|
||||
else
|
||||
puts "InfluxDB analytics are enabled."
|
||||
named_args %w[state on off regenerate-uuid], max: 1
|
||||
end
|
||||
|
||||
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
|
||||
|
||||
@ -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
|
||||
named_args :none
|
||||
end
|
||||
|
||||
sig { override.void }
|
||||
def run
|
||||
Cleanup.autoremove(dry_run: args.dry_run?)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def self.autoremove
|
||||
args = autoremove_args.parse
|
||||
|
||||
Cleanup.autoremove(dry_run: args.dry_run?)
|
||||
end
|
||||
end
|
||||
|
||||
@ -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`."
|
||||
cleanup.clean!(quiet: args.quiet?, periodic: false)
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
cleanup.clean!(quiet: args.quiet?, periodic: false)
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
@ -1,49 +1,46 @@
|
||||
# typed: strict
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "cli/parser"
|
||||
require "abstract_command"
|
||||
|
||||
module Homebrew
|
||||
module_function
|
||||
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."
|
||||
|
||||
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
|
||||
|
||||
named_args :none
|
||||
end
|
||||
end
|
||||
sig { override.void }
|
||||
def run
|
||||
if args.quiet?
|
||||
puts Formatter.columns(Commands.commands(aliases: args.include_aliases?))
|
||||
return
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def commands
|
||||
args = commands_args.parse
|
||||
prepend_separator = T.let(false, T::Boolean)
|
||||
|
||||
if args.quiet?
|
||||
puts Formatter.columns(Commands.commands(aliases: args.include_aliases?))
|
||||
return
|
||||
end
|
||||
{
|
||||
"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?
|
||||
|
||||
prepend_separator = T.let(false, T::Boolean)
|
||||
puts if prepend_separator
|
||||
ohai title, Formatter.columns(commands)
|
||||
|
||||
{
|
||||
"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
|
||||
prepend_separator ||= true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -1,49 +1,46 @@
|
||||
# typed: strict
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "cli/parser"
|
||||
require "abstract_command"
|
||||
require "completions"
|
||||
|
||||
module Homebrew
|
||||
module_function
|
||||
module Cmd
|
||||
class CompletionsCmd < AbstractCommand
|
||||
cmd_args do
|
||||
description <<~EOS
|
||||
Control whether Homebrew automatically links external tap shell completion files.
|
||||
Read more at <https://docs.brew.sh/Shell-Completion>.
|
||||
|
||||
sig { returns(CLI::Parser) }
|
||||
def completions_args
|
||||
Homebrew::CLI::Parser.new do
|
||||
description <<~EOS
|
||||
Control whether Homebrew automatically links external tap shell completion files.
|
||||
Read more at <https://docs.brew.sh/Shell-Completion>.
|
||||
`brew completions` [`state`]:
|
||||
Display the current state of Homebrew's completions.
|
||||
|
||||
`brew completions` [`state`]:
|
||||
Display the current state of Homebrew's completions.
|
||||
`brew completions` (`link`|`unlink`):
|
||||
Link or unlink Homebrew's completions.
|
||||
EOS
|
||||
|
||||
`brew completions` (`link`|`unlink`):
|
||||
Link or unlink Homebrew's completions.
|
||||
EOS
|
||||
|
||||
named_args %w[state link unlink], max: 1
|
||||
end
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def completions
|
||||
args = completions_args.parse
|
||||
|
||||
case args.named.first
|
||||
when nil, "state"
|
||||
if Completions.link_completions?
|
||||
puts "Completions are linked."
|
||||
else
|
||||
puts "Completions are not linked."
|
||||
named_args %w[state link unlink], max: 1
|
||||
end
|
||||
|
||||
sig { override.void }
|
||||
def run
|
||||
case args.named.first
|
||||
when nil, "state"
|
||||
if Completions.link_completions?
|
||||
puts "Completions are linked."
|
||||
else
|
||||
puts "Completions are not linked."
|
||||
end
|
||||
when "link"
|
||||
Completions.link!
|
||||
puts "Completions are now linked."
|
||||
when "unlink"
|
||||
Completions.unlink!
|
||||
puts "Completions are no longer linked."
|
||||
else
|
||||
raise UsageError, "unknown subcommand: #{args.named.first}"
|
||||
end
|
||||
end
|
||||
when "link"
|
||||
Completions.link!
|
||||
puts "Completions are now linked."
|
||||
when "unlink"
|
||||
Completions.unlink!
|
||||
puts "Completions are no longer linked."
|
||||
else
|
||||
raise UsageError, "unknown subcommand: #{args.named.first}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -1,28 +1,25 @@
|
||||
# typed: strict
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "abstract_command"
|
||||
require "system_config"
|
||||
require "cli/parser"
|
||||
|
||||
module Homebrew
|
||||
module_function
|
||||
module Cmd
|
||||
class Config < AbstractCommand
|
||||
cmd_args do
|
||||
description <<~EOS
|
||||
Show Homebrew and system configuration info useful for debugging. If you file
|
||||
a bug report, you will be required to provide this information.
|
||||
EOS
|
||||
|
||||
sig { returns(CLI::Parser) }
|
||||
def config_args
|
||||
Homebrew::CLI::Parser.new do
|
||||
description <<~EOS
|
||||
Show Homebrew and system configuration info useful for debugging. If you file
|
||||
a bug report, you will be required to provide this information.
|
||||
EOS
|
||||
named_args :none
|
||||
end
|
||||
|
||||
named_args :none
|
||||
sig { override.void }
|
||||
def run
|
||||
SystemConfig.dump_verbose_config
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def config
|
||||
config_args.parse
|
||||
|
||||
SystemConfig.dump_verbose_config
|
||||
end
|
||||
end
|
||||
|
||||
@ -1,340 +1,339 @@
|
||||
# typed: true
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "abstract_command"
|
||||
require "formula"
|
||||
require "cli/parser"
|
||||
require "cask/caskroom"
|
||||
require "dependencies_helpers"
|
||||
|
||||
module Homebrew
|
||||
extend DependenciesHelpers
|
||||
module Cmd
|
||||
class Deps < AbstractCommand
|
||||
include DependenciesHelpers
|
||||
cmd_args do
|
||||
description <<~EOS
|
||||
Show dependencies for <formula>. When given multiple formula arguments,
|
||||
show the intersection of dependencies for each formula. By default, `deps`
|
||||
shows all required and recommended dependencies.
|
||||
|
||||
sig { returns(CLI::Parser) }
|
||||
def self.deps_args
|
||||
Homebrew::CLI::Parser.new do
|
||||
description <<~EOS
|
||||
Show dependencies for <formula>. When given multiple formula arguments,
|
||||
show the intersection of dependencies for each formula. By default, `deps`
|
||||
shows all required and recommended dependencies.
|
||||
If any version of each formula argument is installed and no other options
|
||||
are passed, this command displays their actual runtime dependencies (similar
|
||||
to `brew linkage`), which may differ from the current versions' stated
|
||||
dependencies if the installed versions are outdated.
|
||||
|
||||
If any version of each formula argument is installed and no other options
|
||||
are passed, this command displays their actual runtime dependencies (similar
|
||||
to `brew linkage`), which may differ from the current versions' stated
|
||||
dependencies if the installed versions are outdated.
|
||||
*Note:* `--missing` and `--skip-recommended` have precedence over `--include-*`.
|
||||
EOS
|
||||
switch "-n", "--topological",
|
||||
description: "Sort dependencies in topological order."
|
||||
switch "-1", "--direct", "--declared", "--1",
|
||||
description: "Show only the direct dependencies declared in the formula."
|
||||
switch "--union",
|
||||
description: "Show the union of dependencies for multiple <formula>, instead of the intersection."
|
||||
switch "--full-name",
|
||||
description: "List dependencies by their full name."
|
||||
switch "--include-build",
|
||||
description: "Include `:build` dependencies for <formula>."
|
||||
switch "--include-optional",
|
||||
description: "Include `:optional` dependencies for <formula>."
|
||||
switch "--include-test",
|
||||
description: "Include `:test` dependencies for <formula> (non-recursive)."
|
||||
switch "--skip-recommended",
|
||||
description: "Skip `:recommended` dependencies for <formula>."
|
||||
switch "--include-requirements",
|
||||
description: "Include requirements in addition to dependencies for <formula>."
|
||||
switch "--tree",
|
||||
description: "Show dependencies as a tree. When given multiple formula arguments, " \
|
||||
"show individual trees for each formula."
|
||||
switch "--graph",
|
||||
description: "Show dependencies as a directed graph."
|
||||
switch "--dot",
|
||||
depends_on: "--graph",
|
||||
description: "Show text-based graph description in DOT format."
|
||||
switch "--annotate",
|
||||
description: "Mark any build, test, implicit, optional, or recommended dependencies as " \
|
||||
"such in the output."
|
||||
switch "--installed",
|
||||
description: "List dependencies for formulae that are currently installed. If <formula> is " \
|
||||
"specified, list only its dependencies that are currently installed."
|
||||
switch "--missing",
|
||||
description: "Show only missing dependencies."
|
||||
switch "--eval-all",
|
||||
description: "Evaluate all available formulae and casks, whether installed or not, to list " \
|
||||
"their dependencies."
|
||||
switch "--for-each",
|
||||
description: "Switch into the mode used by the `--eval-all` option, but only list dependencies " \
|
||||
"for each provided <formula>, one formula per line. This is used for " \
|
||||
"debugging the `--installed`/`--eval-all` display mode."
|
||||
switch "--HEAD",
|
||||
description: "Show dependencies for HEAD version instead of stable version."
|
||||
switch "--formula", "--formulae",
|
||||
description: "Treat all named arguments as formulae."
|
||||
switch "--cask", "--casks",
|
||||
description: "Treat all named arguments as casks."
|
||||
|
||||
*Note:* `--missing` and `--skip-recommended` have precedence over `--include-*`.
|
||||
EOS
|
||||
switch "-n", "--topological",
|
||||
description: "Sort dependencies in topological order."
|
||||
switch "-1", "--direct", "--declared", "--1",
|
||||
description: "Show only the direct dependencies declared in the formula."
|
||||
switch "--union",
|
||||
description: "Show the union of dependencies for multiple <formula>, instead of the intersection."
|
||||
switch "--full-name",
|
||||
description: "List dependencies by their full name."
|
||||
switch "--include-build",
|
||||
description: "Include `:build` dependencies for <formula>."
|
||||
switch "--include-optional",
|
||||
description: "Include `:optional` dependencies for <formula>."
|
||||
switch "--include-test",
|
||||
description: "Include `:test` dependencies for <formula> (non-recursive)."
|
||||
switch "--skip-recommended",
|
||||
description: "Skip `:recommended` dependencies for <formula>."
|
||||
switch "--include-requirements",
|
||||
description: "Include requirements in addition to dependencies for <formula>."
|
||||
switch "--tree",
|
||||
description: "Show dependencies as a tree. When given multiple formula arguments, " \
|
||||
"show individual trees for each formula."
|
||||
switch "--graph",
|
||||
description: "Show dependencies as a directed graph."
|
||||
switch "--dot",
|
||||
depends_on: "--graph",
|
||||
description: "Show text-based graph description in DOT format."
|
||||
switch "--annotate",
|
||||
description: "Mark any build, test, implicit, optional, or recommended dependencies as " \
|
||||
"such in the output."
|
||||
switch "--installed",
|
||||
description: "List dependencies for formulae that are currently installed. If <formula> is " \
|
||||
"specified, list only its dependencies that are currently installed."
|
||||
switch "--missing",
|
||||
description: "Show only missing dependencies."
|
||||
switch "--eval-all",
|
||||
description: "Evaluate all available formulae and casks, whether installed or not, to list " \
|
||||
"their dependencies."
|
||||
switch "--for-each",
|
||||
description: "Switch into the mode used by the `--eval-all` option, but only list dependencies " \
|
||||
"for each provided <formula>, one formula per line. This is used for " \
|
||||
"debugging the `--installed`/`--eval-all` display mode."
|
||||
switch "--HEAD",
|
||||
description: "Show dependencies for HEAD version instead of stable version."
|
||||
switch "--formula", "--formulae",
|
||||
description: "Treat all named arguments as formulae."
|
||||
switch "--cask", "--casks",
|
||||
description: "Treat all named arguments as casks."
|
||||
conflicts "--tree", "--graph"
|
||||
conflicts "--installed", "--missing"
|
||||
conflicts "--installed", "--eval-all"
|
||||
conflicts "--formula", "--cask"
|
||||
formula_options
|
||||
|
||||
conflicts "--tree", "--graph"
|
||||
conflicts "--installed", "--missing"
|
||||
conflicts "--installed", "--eval-all"
|
||||
conflicts "--formula", "--cask"
|
||||
formula_options
|
||||
named_args [:formula, :cask]
|
||||
end
|
||||
|
||||
named_args [:formula, :cask]
|
||||
end
|
||||
end
|
||||
sig { override.void }
|
||||
def run
|
||||
all = args.eval_all?
|
||||
|
||||
def self.deps
|
||||
args = deps_args.parse
|
||||
Formulary.enable_factory_cache!
|
||||
|
||||
all = args.eval_all?
|
||||
recursive = !args.direct?
|
||||
installed = args.installed? || dependents(args.named.to_formulae_and_casks).all?(&:any_version_installed?)
|
||||
|
||||
Formulary.enable_factory_cache!
|
||||
@use_runtime_dependencies = installed && recursive &&
|
||||
!args.tree? &&
|
||||
!args.graph? &&
|
||||
!args.HEAD? &&
|
||||
!args.include_build? &&
|
||||
!args.include_test? &&
|
||||
!args.include_optional? &&
|
||||
!args.skip_recommended? &&
|
||||
!args.missing?
|
||||
|
||||
recursive = !args.direct?
|
||||
installed = args.installed? || dependents(args.named.to_formulae_and_casks).all?(&:any_version_installed?)
|
||||
if args.tree? || args.graph?
|
||||
dependents = if args.named.present?
|
||||
sorted_dependents(args.named.to_formulae_and_casks)
|
||||
elsif args.installed?
|
||||
case args.only_formula_or_cask
|
||||
when :formula
|
||||
sorted_dependents(Formula.installed)
|
||||
when :cask
|
||||
sorted_dependents(Cask::Caskroom.casks)
|
||||
else
|
||||
sorted_dependents(Formula.installed + Cask::Caskroom.casks)
|
||||
end
|
||||
else
|
||||
raise FormulaUnspecifiedError
|
||||
end
|
||||
|
||||
@use_runtime_dependencies = installed && recursive &&
|
||||
!args.tree? &&
|
||||
!args.graph? &&
|
||||
!args.HEAD? &&
|
||||
!args.include_build? &&
|
||||
!args.include_test? &&
|
||||
!args.include_optional? &&
|
||||
!args.skip_recommended? &&
|
||||
!args.missing?
|
||||
if args.graph?
|
||||
dot_code = dot_code(dependents, recursive:)
|
||||
if args.dot?
|
||||
puts dot_code
|
||||
else
|
||||
exec_browser "https://dreampuf.github.io/GraphvizOnline/##{ERB::Util.url_encode(dot_code)}"
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
if args.tree? || args.graph?
|
||||
dependents = if args.named.present?
|
||||
sorted_dependents(args.named.to_formulae_and_casks)
|
||||
elsif args.installed?
|
||||
case args.only_formula_or_cask
|
||||
when :formula
|
||||
sorted_dependents(Formula.installed)
|
||||
when :cask
|
||||
sorted_dependents(Cask::Caskroom.casks)
|
||||
puts_deps_tree(dependents, recursive:)
|
||||
return
|
||||
elsif all
|
||||
puts_deps(sorted_dependents(
|
||||
Formula.all(eval_all: args.eval_all?) + Cask::Cask.all(eval_all: args.eval_all?),
|
||||
), recursive:)
|
||||
return
|
||||
elsif !args.no_named? && args.for_each?
|
||||
puts_deps(sorted_dependents(args.named.to_formulae_and_casks), recursive:)
|
||||
return
|
||||
end
|
||||
|
||||
if args.no_named?
|
||||
raise FormulaUnspecifiedError unless args.installed?
|
||||
|
||||
sorted_dependents_formulae_and_casks = case args.only_formula_or_cask
|
||||
when :formula
|
||||
sorted_dependents(Formula.installed)
|
||||
when :cask
|
||||
sorted_dependents(Cask::Caskroom.casks)
|
||||
else
|
||||
sorted_dependents(Formula.installed + Cask::Caskroom.casks)
|
||||
end
|
||||
puts_deps(sorted_dependents_formulae_and_casks, recursive:)
|
||||
return
|
||||
end
|
||||
|
||||
dependents = dependents(args.named.to_formulae_and_casks)
|
||||
check_head_spec(dependents) if args.HEAD?
|
||||
|
||||
all_deps = deps_for_dependents(dependents, recursive:, &(args.union? ? :| : :&))
|
||||
condense_requirements(all_deps)
|
||||
all_deps.map! { |d| dep_display_name(d) }
|
||||
all_deps.uniq!
|
||||
all_deps.sort! unless args.topological?
|
||||
puts all_deps
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def sorted_dependents(formulae_or_casks)
|
||||
dependents(formulae_or_casks).sort_by(&:name)
|
||||
end
|
||||
|
||||
def condense_requirements(deps)
|
||||
deps.select! { |dep| dep.is_a?(Dependency) } unless args.include_requirements?
|
||||
deps.select! { |dep| dep.is_a?(Requirement) || dep.installed? } if args.installed?
|
||||
end
|
||||
|
||||
def dep_display_name(dep)
|
||||
str = if dep.is_a? Requirement
|
||||
if args.include_requirements?
|
||||
":#{dep.display_s}"
|
||||
else
|
||||
# This shouldn't happen, but we'll put something here to help debugging
|
||||
"::#{dep.name}"
|
||||
end
|
||||
elsif args.full_name?
|
||||
dep.to_formula.full_name
|
||||
else
|
||||
sorted_dependents(Formula.installed + Cask::Caskroom.casks)
|
||||
dep.name
|
||||
end
|
||||
else
|
||||
raise FormulaUnspecifiedError
|
||||
|
||||
if args.annotate?
|
||||
str = "#{str} " if args.tree?
|
||||
str = "#{str} [build]" if dep.build?
|
||||
str = "#{str} [test]" if dep.test?
|
||||
str = "#{str} [optional]" if dep.optional?
|
||||
str = "#{str} [recommended]" if dep.recommended?
|
||||
str = "#{str} [implicit]" if dep.implicit?
|
||||
end
|
||||
|
||||
str
|
||||
end
|
||||
|
||||
if args.graph?
|
||||
dot_code = dot_code(dependents, recursive:, args:)
|
||||
if args.dot?
|
||||
puts dot_code
|
||||
def deps_for_dependent(dependency, recursive: false)
|
||||
includes, ignores = args_includes_ignores(args)
|
||||
|
||||
deps = dependency.runtime_dependencies if @use_runtime_dependencies
|
||||
|
||||
if recursive
|
||||
deps ||= recursive_includes(Dependency, dependency, includes, ignores)
|
||||
reqs = recursive_includes(Requirement, dependency, includes, ignores)
|
||||
else
|
||||
exec_browser "https://dreampuf.github.io/GraphvizOnline/##{ERB::Util.url_encode(dot_code)}"
|
||||
deps ||= select_includes(dependency.deps, ignores, includes)
|
||||
reqs = select_includes(dependency.requirements, ignores, includes)
|
||||
end
|
||||
return
|
||||
|
||||
deps + reqs.to_a
|
||||
end
|
||||
|
||||
puts_deps_tree(dependents, recursive:, args:)
|
||||
return
|
||||
elsif all
|
||||
puts_deps(sorted_dependents(
|
||||
Formula.all(eval_all: args.eval_all?) + Cask::Cask.all(eval_all: args.eval_all?),
|
||||
), recursive:, args:)
|
||||
return
|
||||
elsif !args.no_named? && args.for_each?
|
||||
puts_deps(sorted_dependents(args.named.to_formulae_and_casks), recursive:, args:)
|
||||
return
|
||||
end
|
||||
|
||||
if args.no_named?
|
||||
raise FormulaUnspecifiedError unless args.installed?
|
||||
|
||||
sorted_dependents_formulae_and_casks = case args.only_formula_or_cask
|
||||
when :formula
|
||||
sorted_dependents(Formula.installed)
|
||||
when :cask
|
||||
sorted_dependents(Cask::Caskroom.casks)
|
||||
else
|
||||
sorted_dependents(Formula.installed + Cask::Caskroom.casks)
|
||||
def deps_for_dependents(dependents, recursive: false, &block)
|
||||
dependents.map { |d| deps_for_dependent(d, recursive:) }.reduce(&block)
|
||||
end
|
||||
puts_deps(sorted_dependents_formulae_and_casks, recursive:, args:)
|
||||
return
|
||||
end
|
||||
|
||||
dependents = dependents(args.named.to_formulae_and_casks)
|
||||
check_head_spec(dependents) if args.HEAD?
|
||||
|
||||
all_deps = deps_for_dependents(dependents, recursive:, args:, &(args.union? ? :| : :&))
|
||||
condense_requirements(all_deps, args:)
|
||||
all_deps.map! { |d| dep_display_name(d, args:) }
|
||||
all_deps.uniq!
|
||||
all_deps.sort! unless args.topological?
|
||||
puts all_deps
|
||||
end
|
||||
|
||||
def self.sorted_dependents(formulae_or_casks)
|
||||
dependents(formulae_or_casks).sort_by(&:name)
|
||||
end
|
||||
|
||||
def self.condense_requirements(deps, args:)
|
||||
deps.select! { |dep| dep.is_a?(Dependency) } unless args.include_requirements?
|
||||
deps.select! { |dep| dep.is_a?(Requirement) || dep.installed? } if args.installed?
|
||||
end
|
||||
|
||||
def self.dep_display_name(dep, args:)
|
||||
str = if dep.is_a? Requirement
|
||||
if args.include_requirements?
|
||||
":#{dep.display_s}"
|
||||
else
|
||||
# This shouldn't happen, but we'll put something here to help debugging
|
||||
"::#{dep.name}"
|
||||
def check_head_spec(dependents)
|
||||
headless = dependents.select { |d| d.is_a?(Formula) && d.active_spec_sym != :head }
|
||||
.to_sentence two_words_connector: " or ", last_word_connector: " or "
|
||||
opoo "No head spec for #{headless}, using stable spec instead" unless headless.empty?
|
||||
end
|
||||
elsif args.full_name?
|
||||
dep.to_formula.full_name
|
||||
else
|
||||
dep.name
|
||||
end
|
||||
|
||||
if args.annotate?
|
||||
str = "#{str} " if args.tree?
|
||||
str = "#{str} [build]" if dep.build?
|
||||
str = "#{str} [test]" if dep.test?
|
||||
str = "#{str} [optional]" if dep.optional?
|
||||
str = "#{str} [recommended]" if dep.recommended?
|
||||
str = "#{str} [implicit]" if dep.implicit?
|
||||
end
|
||||
|
||||
str
|
||||
end
|
||||
|
||||
def self.deps_for_dependent(dependency, args:, recursive: false)
|
||||
includes, ignores = args_includes_ignores(args)
|
||||
|
||||
deps = dependency.runtime_dependencies if @use_runtime_dependencies
|
||||
|
||||
if recursive
|
||||
deps ||= recursive_includes(Dependency, dependency, includes, ignores)
|
||||
reqs = recursive_includes(Requirement, dependency, includes, ignores)
|
||||
else
|
||||
deps ||= select_includes(dependency.deps, ignores, includes)
|
||||
reqs = select_includes(dependency.requirements, ignores, includes)
|
||||
end
|
||||
|
||||
deps + reqs.to_a
|
||||
end
|
||||
|
||||
def self.deps_for_dependents(dependents, args:, recursive: false, &block)
|
||||
dependents.map { |d| deps_for_dependent(d, recursive:, args:) }.reduce(&block)
|
||||
end
|
||||
|
||||
def self.check_head_spec(dependents)
|
||||
headless = dependents.select { |d| d.is_a?(Formula) && d.active_spec_sym != :head }
|
||||
.to_sentence two_words_connector: " or ", last_word_connector: " or "
|
||||
opoo "No head spec for #{headless}, using stable spec instead" unless headless.empty?
|
||||
end
|
||||
|
||||
def self.puts_deps(dependents, args:, recursive: false)
|
||||
check_head_spec(dependents) if args.HEAD?
|
||||
dependents.each do |dependent|
|
||||
deps = deps_for_dependent(dependent, recursive:, args:)
|
||||
condense_requirements(deps, args:)
|
||||
deps.sort_by!(&:name)
|
||||
deps.map! { |d| dep_display_name(d, args:) }
|
||||
puts "#{dependent.full_name}: #{deps.join(" ")}"
|
||||
end
|
||||
end
|
||||
|
||||
def self.dot_code(dependents, recursive:, args:)
|
||||
dep_graph = {}
|
||||
dependents.each do |d|
|
||||
graph_deps(d, dep_graph:, recursive:, args:)
|
||||
end
|
||||
|
||||
dot_code = dep_graph.map do |d, deps|
|
||||
deps.map do |dep|
|
||||
attributes = []
|
||||
attributes << "style = dotted" if dep.build?
|
||||
attributes << "arrowhead = empty" if dep.test?
|
||||
if dep.optional?
|
||||
attributes << "color = red"
|
||||
elsif dep.recommended?
|
||||
attributes << "color = green"
|
||||
def puts_deps(dependents, recursive: false)
|
||||
check_head_spec(dependents) if args.HEAD?
|
||||
dependents.each do |dependent|
|
||||
deps = deps_for_dependent(dependent, recursive:)
|
||||
condense_requirements(deps)
|
||||
deps.sort_by!(&:name)
|
||||
deps.map! { |d| dep_display_name(d) }
|
||||
puts "#{dependent.full_name}: #{deps.join(" ")}"
|
||||
end
|
||||
comment = " # #{dep.tags.map(&:inspect).join(", ")}" if dep.tags.any?
|
||||
" \"#{d.name}\" -> \"#{dep}\"#{" [#{attributes.join(", ")}]" if attributes.any?}#{comment}"
|
||||
end
|
||||
end.flatten.join("\n")
|
||||
"digraph {\n#{dot_code}\n}"
|
||||
end
|
||||
|
||||
def self.graph_deps(formula, dep_graph:, recursive:, args:)
|
||||
return if dep_graph.key?(formula)
|
||||
def dot_code(dependents, recursive:)
|
||||
dep_graph = {}
|
||||
dependents.each do |d|
|
||||
graph_deps(d, dep_graph:, recursive:)
|
||||
end
|
||||
|
||||
dependables = dependables(formula, args:)
|
||||
dep_graph[formula] = dependables
|
||||
return unless recursive
|
||||
dot_code = dep_graph.map do |d, deps|
|
||||
deps.map do |dep|
|
||||
attributes = []
|
||||
attributes << "style = dotted" if dep.build?
|
||||
attributes << "arrowhead = empty" if dep.test?
|
||||
if dep.optional?
|
||||
attributes << "color = red"
|
||||
elsif dep.recommended?
|
||||
attributes << "color = green"
|
||||
end
|
||||
comment = " # #{dep.tags.map(&:inspect).join(", ")}" if dep.tags.any?
|
||||
" \"#{d.name}\" -> \"#{dep}\"#{" [#{attributes.join(", ")}]" if attributes.any?}#{comment}"
|
||||
end
|
||||
end.flatten.join("\n")
|
||||
"digraph {\n#{dot_code}\n}"
|
||||
end
|
||||
|
||||
dependables.each do |dep|
|
||||
next unless dep.is_a? Dependency
|
||||
def graph_deps(formula, dep_graph:, recursive:)
|
||||
return if dep_graph.key?(formula)
|
||||
|
||||
graph_deps(Formulary.factory(dep.name),
|
||||
dep_graph:,
|
||||
recursive: true,
|
||||
args:)
|
||||
dependables = dependables(formula)
|
||||
dep_graph[formula] = dependables
|
||||
return unless recursive
|
||||
|
||||
dependables.each do |dep|
|
||||
next unless dep.is_a? Dependency
|
||||
|
||||
graph_deps(Formulary.factory(dep.name),
|
||||
dep_graph:,
|
||||
recursive: true)
|
||||
end
|
||||
end
|
||||
|
||||
def puts_deps_tree(dependents, recursive: false)
|
||||
check_head_spec(dependents) if args.HEAD?
|
||||
dependents.each do |d|
|
||||
puts d.full_name
|
||||
recursive_deps_tree(d, dep_stack: [], prefix: "", recursive:)
|
||||
puts
|
||||
end
|
||||
end
|
||||
|
||||
def dependables(formula)
|
||||
includes, ignores = args_includes_ignores(args)
|
||||
deps = @use_runtime_dependencies ? formula.runtime_dependencies : formula.deps
|
||||
deps = select_includes(deps, ignores, includes)
|
||||
reqs = select_includes(formula.requirements, ignores, includes) if args.include_requirements?
|
||||
reqs ||= []
|
||||
reqs + deps
|
||||
end
|
||||
|
||||
def recursive_deps_tree(formula, dep_stack:, prefix:, recursive:)
|
||||
dependables = dependables(formula)
|
||||
max = dependables.length - 1
|
||||
dep_stack.push formula.name
|
||||
dependables.each_with_index do |dep, i|
|
||||
tree_lines = if i == max
|
||||
"└──"
|
||||
else
|
||||
"├──"
|
||||
end
|
||||
|
||||
display_s = "#{tree_lines} #{dep_display_name(dep)}"
|
||||
|
||||
# Detect circular dependencies and consider them a failure if present.
|
||||
is_circular = dep_stack.include?(dep.name)
|
||||
if is_circular
|
||||
display_s = "#{display_s} (CIRCULAR DEPENDENCY)"
|
||||
Homebrew.failed = true
|
||||
end
|
||||
|
||||
puts "#{prefix}#{display_s}"
|
||||
|
||||
next if !recursive || is_circular
|
||||
|
||||
prefix_addition = if i == max
|
||||
" "
|
||||
else
|
||||
"│ "
|
||||
end
|
||||
|
||||
next unless dep.is_a? Dependency
|
||||
|
||||
recursive_deps_tree(Formulary.factory(dep.name),
|
||||
dep_stack:,
|
||||
prefix: prefix + prefix_addition,
|
||||
recursive: true)
|
||||
end
|
||||
|
||||
dep_stack.pop
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def self.puts_deps_tree(dependents, args:, recursive: false)
|
||||
check_head_spec(dependents) if args.HEAD?
|
||||
dependents.each do |d|
|
||||
puts d.full_name
|
||||
recursive_deps_tree(d, dep_stack: [], prefix: "", recursive:, args:)
|
||||
puts
|
||||
end
|
||||
end
|
||||
|
||||
def self.dependables(formula, args:)
|
||||
includes, ignores = args_includes_ignores(args)
|
||||
deps = @use_runtime_dependencies ? formula.runtime_dependencies : formula.deps
|
||||
deps = select_includes(deps, ignores, includes)
|
||||
reqs = select_includes(formula.requirements, ignores, includes) if args.include_requirements?
|
||||
reqs ||= []
|
||||
reqs + deps
|
||||
end
|
||||
|
||||
def self.recursive_deps_tree(formula, dep_stack:, prefix:, recursive:, args:)
|
||||
dependables = dependables(formula, args:)
|
||||
max = dependables.length - 1
|
||||
dep_stack.push formula.name
|
||||
dependables.each_with_index do |dep, i|
|
||||
tree_lines = if i == max
|
||||
"└──"
|
||||
else
|
||||
"├──"
|
||||
end
|
||||
|
||||
display_s = "#{tree_lines} #{dep_display_name(dep, args:)}"
|
||||
|
||||
# Detect circular dependencies and consider them a failure if present.
|
||||
is_circular = dep_stack.include?(dep.name)
|
||||
if is_circular
|
||||
display_s = "#{display_s} (CIRCULAR DEPENDENCY)"
|
||||
Homebrew.failed = true
|
||||
end
|
||||
|
||||
puts "#{prefix}#{display_s}"
|
||||
|
||||
next if !recursive || is_circular
|
||||
|
||||
prefix_addition = if i == max
|
||||
" "
|
||||
else
|
||||
"│ "
|
||||
end
|
||||
|
||||
next unless dep.is_a? Dependency
|
||||
|
||||
recursive_deps_tree(Formulary.factory(dep.name),
|
||||
dep_stack:,
|
||||
prefix: prefix + prefix_addition,
|
||||
recursive: true,
|
||||
args:)
|
||||
end
|
||||
|
||||
dep_stack.pop
|
||||
end
|
||||
end
|
||||
|
||||
@ -1,74 +1,75 @@
|
||||
# typed: true
|
||||
# typed: strict
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "abstract_command"
|
||||
require "descriptions"
|
||||
require "search"
|
||||
require "description_cache_store"
|
||||
require "cli/parser"
|
||||
|
||||
module Homebrew
|
||||
module_function
|
||||
module Cmd
|
||||
class Desc < AbstractCommand
|
||||
cmd_args do
|
||||
description <<~EOS
|
||||
Display <formula>'s name and one-line description.
|
||||
The cache is created on the first search, making that search slower than subsequent ones.
|
||||
EOS
|
||||
switch "-s", "--search",
|
||||
description: "Search both names and descriptions for <text>. If <text> is flanked by " \
|
||||
"slashes, it is interpreted as a regular expression."
|
||||
switch "-n", "--name",
|
||||
description: "Search just names for <text>. If <text> is flanked by slashes, it is " \
|
||||
"interpreted as a regular expression."
|
||||
switch "-d", "--description",
|
||||
description: "Search just descriptions for <text>. If <text> is flanked by slashes, " \
|
||||
"it is interpreted as a regular expression."
|
||||
switch "--eval-all",
|
||||
description: "Evaluate all available formulae and casks, whether installed or not, to search their " \
|
||||
"descriptions. Implied if `HOMEBREW_EVAL_ALL` is set."
|
||||
switch "--formula", "--formulae",
|
||||
description: "Treat all named arguments as formulae."
|
||||
switch "--cask", "--casks",
|
||||
description: "Treat all named arguments as casks."
|
||||
|
||||
sig { returns(CLI::Parser) }
|
||||
def desc_args
|
||||
Homebrew::CLI::Parser.new do
|
||||
description <<~EOS
|
||||
Display <formula>'s name and one-line description.
|
||||
The cache is created on the first search, making that search slower than subsequent ones.
|
||||
EOS
|
||||
switch "-s", "--search",
|
||||
description: "Search both names and descriptions for <text>. If <text> is flanked by " \
|
||||
"slashes, it is interpreted as a regular expression."
|
||||
switch "-n", "--name",
|
||||
description: "Search just names for <text>. If <text> is flanked by slashes, it is " \
|
||||
"interpreted as a regular expression."
|
||||
switch "-d", "--description",
|
||||
description: "Search just descriptions for <text>. If <text> is flanked by slashes, " \
|
||||
"it is interpreted as a regular expression."
|
||||
switch "--eval-all",
|
||||
description: "Evaluate all available formulae and casks, whether installed or not, to search their " \
|
||||
"descriptions. Implied if `HOMEBREW_EVAL_ALL` is set."
|
||||
switch "--formula", "--formulae",
|
||||
description: "Treat all named arguments as formulae."
|
||||
switch "--cask", "--casks",
|
||||
description: "Treat all named arguments as casks."
|
||||
conflicts "--search", "--name", "--description"
|
||||
|
||||
conflicts "--search", "--name", "--description"
|
||||
named_args [:formula, :cask, :text_or_regex], min: 1
|
||||
end
|
||||
|
||||
named_args [:formula, :cask, :text_or_regex], min: 1
|
||||
end
|
||||
end
|
||||
sig { override.void }
|
||||
def run
|
||||
if !args.eval_all? && !Homebrew::EnvConfig.eval_all?
|
||||
raise UsageError, "`brew desc` needs `--eval-all` passed or `HOMEBREW_EVAL_ALL` set!"
|
||||
end
|
||||
|
||||
def desc
|
||||
args = desc_args.parse
|
||||
search_type = if args.search?
|
||||
:either
|
||||
elsif args.name?
|
||||
:name
|
||||
elsif args.description?
|
||||
:desc
|
||||
end
|
||||
|
||||
if !args.eval_all? && !Homebrew::EnvConfig.eval_all?
|
||||
raise UsageError, "`brew desc` needs `--eval-all` passed or `HOMEBREW_EVAL_ALL` set!"
|
||||
end
|
||||
|
||||
search_type = if args.search?
|
||||
:either
|
||||
elsif args.name?
|
||||
:name
|
||||
elsif args.description?
|
||||
:desc
|
||||
end
|
||||
|
||||
if search_type.blank?
|
||||
desc = {}
|
||||
args.named.to_formulae_and_casks.each do |formula_or_cask|
|
||||
if formula_or_cask.is_a? Formula
|
||||
desc[formula_or_cask.full_name] = formula_or_cask.desc
|
||||
if search_type.blank?
|
||||
desc = {}
|
||||
args.named.to_formulae_and_casks.each do |formula_or_cask|
|
||||
case formula_or_cask
|
||||
when Formula
|
||||
desc[formula_or_cask.full_name] = formula_or_cask.desc
|
||||
when Cask::Cask
|
||||
description = formula_or_cask.desc.presence || Formatter.warning("[no description]")
|
||||
desc[formula_or_cask.full_name] = "(#{formula_or_cask.name.join(", ")}) #{description}"
|
||||
else
|
||||
raise TypeError, "Unsupported formula_or_cask type: #{formula_or_cask.class}"
|
||||
end
|
||||
end
|
||||
Descriptions.new(desc).print
|
||||
else
|
||||
description = formula_or_cask.desc.presence || Formatter.warning("[no description]")
|
||||
desc[formula_or_cask.full_name] = "(#{formula_or_cask.name.join(", ")}) #{description}"
|
||||
query = args.named.join(" ")
|
||||
string_or_regex = Search.query_regexp(query)
|
||||
Search.search_descriptions(string_or_regex, args, search_type:)
|
||||
end
|
||||
end
|
||||
Descriptions.new(desc).print
|
||||
else
|
||||
query = args.named.join(" ")
|
||||
string_or_regex = Search.query_regexp(query)
|
||||
Search.search_descriptions(string_or_regex, args, search_type:)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -1,56 +1,55 @@
|
||||
# typed: true
|
||||
# typed: strict
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "cli/parser"
|
||||
require "abstract_command"
|
||||
|
||||
module Homebrew
|
||||
module_function
|
||||
module Cmd
|
||||
class Developer < AbstractCommand
|
||||
cmd_args do
|
||||
description <<~EOS
|
||||
Control Homebrew's developer mode. When developer mode is enabled,
|
||||
`brew update` will update Homebrew to the latest commit on the `master`
|
||||
branch instead of the latest stable version along with some other behaviour changes.
|
||||
|
||||
sig { returns(CLI::Parser) }
|
||||
def developer_args
|
||||
Homebrew::CLI::Parser.new do
|
||||
description <<~EOS
|
||||
Control Homebrew's developer mode. When developer mode is enabled,
|
||||
`brew update` will update Homebrew to the latest commit on the `master`
|
||||
branch instead of the latest stable version along with some other behaviour changes.
|
||||
`brew developer` [`state`]:
|
||||
Display the current state of Homebrew's developer mode.
|
||||
|
||||
`brew developer` [`state`]:
|
||||
Display the current state of Homebrew's developer mode.
|
||||
`brew developer` (`on`|`off`):
|
||||
Turn Homebrew's developer mode on or off respectively.
|
||||
EOS
|
||||
|
||||
`brew developer` (`on`|`off`):
|
||||
Turn Homebrew's developer mode on or off respectively.
|
||||
EOS
|
||||
|
||||
named_args %w[state on off], max: 1
|
||||
end
|
||||
end
|
||||
|
||||
def developer
|
||||
args = developer_args.parse
|
||||
|
||||
env_vars = []
|
||||
env_vars << "HOMEBREW_DEVELOPER" if Homebrew::EnvConfig.developer?
|
||||
env_vars << "HOMEBREW_UPDATE_TO_TAG" if Homebrew::EnvConfig.update_to_tag?
|
||||
env_vars.map! do |var|
|
||||
"#{Tty.bold}#{var}#{Tty.reset}"
|
||||
end
|
||||
|
||||
case args.named.first
|
||||
when nil, "state"
|
||||
if env_vars.any?
|
||||
puts "Developer mode is enabled because #{env_vars.to_sentence} #{(env_vars.count == 1) ? "is" : "are"} set."
|
||||
elsif Homebrew::Settings.read("devcmdrun") == "true"
|
||||
puts "Developer mode is enabled."
|
||||
else
|
||||
puts "Developer mode is disabled."
|
||||
named_args %w[state on off], max: 1
|
||||
end
|
||||
|
||||
sig { override.void }
|
||||
def run
|
||||
env_vars = []
|
||||
env_vars << "HOMEBREW_DEVELOPER" if Homebrew::EnvConfig.developer?
|
||||
env_vars << "HOMEBREW_UPDATE_TO_TAG" if Homebrew::EnvConfig.update_to_tag?
|
||||
env_vars.map! do |var|
|
||||
"#{Tty.bold}#{var}#{Tty.reset}"
|
||||
end
|
||||
|
||||
case args.named.first
|
||||
when nil, "state"
|
||||
if env_vars.any?
|
||||
verb = (env_vars.count == 1) ? "is" : "are"
|
||||
puts "Developer mode is enabled because #{env_vars.to_sentence} #{verb} set."
|
||||
elsif Homebrew::Settings.read("devcmdrun") == "true"
|
||||
puts "Developer mode is enabled."
|
||||
else
|
||||
puts "Developer mode is disabled."
|
||||
end
|
||||
when "on"
|
||||
Homebrew::Settings.write "devcmdrun", true
|
||||
when "off"
|
||||
Homebrew::Settings.delete "devcmdrun"
|
||||
puts "To fully disable developer mode, you must unset #{env_vars.to_sentence}." if env_vars.any?
|
||||
else
|
||||
raise UsageError, "unknown subcommand: #{args.named.first}"
|
||||
end
|
||||
end
|
||||
when "on"
|
||||
Homebrew::Settings.write "devcmdrun", true
|
||||
when "off"
|
||||
Homebrew::Settings.delete "devcmdrun"
|
||||
puts "To fully disable developer mode, you must unset #{env_vars.to_sentence}." if env_vars.any?
|
||||
else
|
||||
raise UsageError, "unknown subcommand: #{args.named.first}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -1,22 +1,21 @@
|
||||
# typed: strict
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "cli/parser"
|
||||
require "abstract_command"
|
||||
|
||||
module Homebrew
|
||||
module_function
|
||||
module Cmd
|
||||
class Docs < AbstractCommand
|
||||
cmd_args do
|
||||
description <<~EOS
|
||||
Open Homebrew's online documentation at <#{HOMEBREW_DOCS_WWW}> in a browser.
|
||||
EOS
|
||||
end
|
||||
|
||||
sig { returns(CLI::Parser) }
|
||||
def docs_args
|
||||
Homebrew::CLI::Parser.new do
|
||||
description <<~EOS
|
||||
Open Homebrew's online documentation at <#{HOMEBREW_DOCS_WWW}> in a browser.
|
||||
EOS
|
||||
sig { override.void }
|
||||
def run
|
||||
exec_browser HOMEBREW_DOCS_WWW
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def docs
|
||||
exec_browser HOMEBREW_DOCS_WWW
|
||||
end
|
||||
end
|
||||
|
||||
@ -1,80 +1,80 @@
|
||||
# typed: true
|
||||
# typed: strict
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "abstract_command"
|
||||
require "diagnostic"
|
||||
require "cli/parser"
|
||||
require "cask/caskroom"
|
||||
|
||||
module Homebrew
|
||||
sig { returns(CLI::Parser) }
|
||||
def self.doctor_args
|
||||
Homebrew::CLI::Parser.new do
|
||||
description <<~EOS
|
||||
Check your system for potential problems. Will exit with a non-zero status
|
||||
if any potential problems are found.
|
||||
module Cmd
|
||||
class Doctor < AbstractCommand
|
||||
cmd_args do
|
||||
description <<~EOS
|
||||
Check your system for potential problems. Will exit with a non-zero status
|
||||
if any potential problems are found.
|
||||
|
||||
Please note that these warnings are just used to help the Homebrew maintainers
|
||||
with debugging if you file an issue. If everything you use Homebrew for
|
||||
is working fine: please don't worry or file an issue; just ignore this.
|
||||
EOS
|
||||
switch "--list-checks",
|
||||
description: "List all audit methods, which can be run individually " \
|
||||
"if provided as arguments."
|
||||
switch "-D", "--audit-debug",
|
||||
description: "Enable debugging and profiling of audit methods."
|
||||
|
||||
named_args :diagnostic_check
|
||||
end
|
||||
end
|
||||
|
||||
def self.doctor
|
||||
args = doctor_args.parse
|
||||
|
||||
inject_dump_stats!(Diagnostic::Checks, /^check_*/) if args.audit_debug?
|
||||
|
||||
checks = Diagnostic::Checks.new(verbose: args.verbose?)
|
||||
|
||||
if args.list_checks?
|
||||
puts checks.all
|
||||
return
|
||||
end
|
||||
|
||||
if args.no_named?
|
||||
slow_checks = %w[
|
||||
check_for_broken_symlinks
|
||||
check_missing_deps
|
||||
]
|
||||
methods = (checks.all - slow_checks) + slow_checks
|
||||
methods -= checks.cask_checks unless Cask::Caskroom.any_casks_installed?
|
||||
else
|
||||
methods = args.named
|
||||
end
|
||||
|
||||
first_warning = T.let(true, T::Boolean)
|
||||
methods.each do |method|
|
||||
$stderr.puts Formatter.headline("Checking #{method}", color: :magenta) if args.debug?
|
||||
unless checks.respond_to?(method)
|
||||
ofail "No check available by the name: #{method}"
|
||||
next
|
||||
end
|
||||
|
||||
out = checks.send(method)
|
||||
next if out.blank?
|
||||
|
||||
if first_warning
|
||||
$stderr.puts <<~EOS
|
||||
#{Tty.bold}Please note that these warnings are just used to help the Homebrew maintainers
|
||||
with debugging if you file an issue. If everything you use Homebrew for is
|
||||
working fine: please don't worry or file an issue; just ignore this. Thanks!#{Tty.reset}
|
||||
Please note that these warnings are just used to help the Homebrew maintainers
|
||||
with debugging if you file an issue. If everything you use Homebrew for
|
||||
is working fine: please don't worry or file an issue; just ignore this.
|
||||
EOS
|
||||
switch "--list-checks",
|
||||
description: "List all audit methods, which can be run individually " \
|
||||
"if provided as arguments."
|
||||
switch "-D", "--audit-debug",
|
||||
description: "Enable debugging and profiling of audit methods."
|
||||
|
||||
named_args :diagnostic_check
|
||||
end
|
||||
|
||||
$stderr.puts
|
||||
opoo out
|
||||
Homebrew.failed = true
|
||||
first_warning = false
|
||||
end
|
||||
sig { override.void }
|
||||
def run
|
||||
Homebrew.inject_dump_stats!(Diagnostic::Checks, /^check_*/) if args.audit_debug?
|
||||
|
||||
puts "Your system is ready to brew." if !Homebrew.failed? && !args.quiet?
|
||||
checks = Diagnostic::Checks.new(verbose: args.verbose?)
|
||||
|
||||
if args.list_checks?
|
||||
puts checks.all
|
||||
return
|
||||
end
|
||||
|
||||
if args.no_named?
|
||||
slow_checks = %w[
|
||||
check_for_broken_symlinks
|
||||
check_missing_deps
|
||||
]
|
||||
methods = (checks.all - slow_checks) + slow_checks
|
||||
methods -= checks.cask_checks unless Cask::Caskroom.any_casks_installed?
|
||||
else
|
||||
methods = args.named
|
||||
end
|
||||
|
||||
first_warning = T.let(true, T::Boolean)
|
||||
methods.each do |method|
|
||||
$stderr.puts Formatter.headline("Checking #{method}", color: :magenta) if args.debug?
|
||||
unless checks.respond_to?(method)
|
||||
ofail "No check available by the name: #{method}"
|
||||
next
|
||||
end
|
||||
|
||||
out = checks.send(method)
|
||||
next if out.blank?
|
||||
|
||||
if first_warning
|
||||
$stderr.puts <<~EOS
|
||||
#{Tty.bold}Please note that these warnings are just used to help the Homebrew maintainers
|
||||
with debugging if you file an issue. If everything you use Homebrew for is
|
||||
working fine: please don't worry or file an issue; just ignore this. Thanks!#{Tty.reset}
|
||||
EOS
|
||||
end
|
||||
|
||||
$stderr.puts
|
||||
opoo out
|
||||
Homebrew.failed = true
|
||||
first_warning = false
|
||||
end
|
||||
|
||||
puts "Your system is ready to brew." if !Homebrew.failed? && !args.quiet?
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -1,256 +1,257 @@
|
||||
# typed: true
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "abstract_command"
|
||||
require "formula"
|
||||
require "fetch"
|
||||
require "cli/parser"
|
||||
require "cask/download"
|
||||
|
||||
module Homebrew
|
||||
extend Fetch
|
||||
module Cmd
|
||||
class FetchCmd < AbstractCommand
|
||||
include Fetch
|
||||
FETCH_MAX_TRIES = 5
|
||||
|
||||
FETCH_MAX_TRIES = 5
|
||||
cmd_args do
|
||||
description <<~EOS
|
||||
Download a bottle (if available) or source packages for <formula>e
|
||||
and binaries for <cask>s. For files, also print SHA-256 checksums.
|
||||
EOS
|
||||
flag "--os=",
|
||||
description: "Download for the given operating system. " \
|
||||
"(Pass `all` to download for all operating systems.)"
|
||||
flag "--arch=",
|
||||
description: "Download for the given CPU architecture. " \
|
||||
"(Pass `all` to download for all architectures.)"
|
||||
flag "--bottle-tag=",
|
||||
description: "Download a bottle for given tag."
|
||||
switch "--HEAD",
|
||||
description: "Fetch HEAD version instead of stable version."
|
||||
switch "-f", "--force",
|
||||
description: "Remove a previously cached version and re-fetch."
|
||||
switch "-v", "--verbose",
|
||||
description: "Do a verbose VCS checkout, if the URL represents a VCS. This is useful for " \
|
||||
"seeing if an existing VCS cache has been updated."
|
||||
switch "--retry",
|
||||
description: "Retry if downloading fails or re-download if the checksum of a previously cached " \
|
||||
"version no longer matches. Tries at most #{FETCH_MAX_TRIES} times with " \
|
||||
"exponential backoff."
|
||||
switch "--deps",
|
||||
description: "Also download dependencies for any listed <formula>."
|
||||
switch "-s", "--build-from-source",
|
||||
description: "Download source packages rather than a bottle."
|
||||
switch "--build-bottle",
|
||||
description: "Download source packages (for eventual bottling) rather than a bottle."
|
||||
switch "--force-bottle",
|
||||
description: "Download a bottle if it exists for the current or newest version of macOS, " \
|
||||
"even if it would not be used during installation."
|
||||
switch "--[no-]quarantine",
|
||||
description: "Disable/enable quarantining of downloads (default: enabled).",
|
||||
env: :cask_opts_quarantine
|
||||
switch "--formula", "--formulae",
|
||||
description: "Treat all named arguments as formulae."
|
||||
switch "--cask", "--casks",
|
||||
description: "Treat all named arguments as casks."
|
||||
|
||||
sig { returns(CLI::Parser) }
|
||||
def self.fetch_args
|
||||
Homebrew::CLI::Parser.new do
|
||||
description <<~EOS
|
||||
Download a bottle (if available) or source packages for <formula>e
|
||||
and binaries for <cask>s. For files, also print SHA-256 checksums.
|
||||
EOS
|
||||
flag "--os=",
|
||||
description: "Download for the given operating system. " \
|
||||
"(Pass `all` to download for all operating systems.)"
|
||||
flag "--arch=",
|
||||
description: "Download for the given CPU architecture. " \
|
||||
"(Pass `all` to download for all architectures.)"
|
||||
flag "--bottle-tag=",
|
||||
description: "Download a bottle for given tag."
|
||||
switch "--HEAD",
|
||||
description: "Fetch HEAD version instead of stable version."
|
||||
switch "-f", "--force",
|
||||
description: "Remove a previously cached version and re-fetch."
|
||||
switch "-v", "--verbose",
|
||||
description: "Do a verbose VCS checkout, if the URL represents a VCS. This is useful for " \
|
||||
"seeing if an existing VCS cache has been updated."
|
||||
switch "--retry",
|
||||
description: "Retry if downloading fails or re-download if the checksum of a previously cached " \
|
||||
"version no longer matches. Tries at most #{FETCH_MAX_TRIES} times with " \
|
||||
"exponential backoff."
|
||||
switch "--deps",
|
||||
description: "Also download dependencies for any listed <formula>."
|
||||
switch "-s", "--build-from-source",
|
||||
description: "Download source packages rather than a bottle."
|
||||
switch "--build-bottle",
|
||||
description: "Download source packages (for eventual bottling) rather than a bottle."
|
||||
switch "--force-bottle",
|
||||
description: "Download a bottle if it exists for the current or newest version of macOS, " \
|
||||
"even if it would not be used during installation."
|
||||
switch "--[no-]quarantine",
|
||||
description: "Disable/enable quarantining of downloads (default: enabled).",
|
||||
env: :cask_opts_quarantine
|
||||
switch "--formula", "--formulae",
|
||||
description: "Treat all named arguments as formulae."
|
||||
switch "--cask", "--casks",
|
||||
description: "Treat all named arguments as casks."
|
||||
conflicts "--build-from-source", "--build-bottle", "--force-bottle", "--bottle-tag"
|
||||
conflicts "--cask", "--HEAD"
|
||||
conflicts "--cask", "--deps"
|
||||
conflicts "--cask", "-s"
|
||||
conflicts "--cask", "--build-bottle"
|
||||
conflicts "--cask", "--force-bottle"
|
||||
conflicts "--cask", "--bottle-tag"
|
||||
conflicts "--formula", "--cask"
|
||||
conflicts "--os", "--bottle-tag"
|
||||
conflicts "--arch", "--bottle-tag"
|
||||
|
||||
conflicts "--build-from-source", "--build-bottle", "--force-bottle", "--bottle-tag"
|
||||
conflicts "--cask", "--HEAD"
|
||||
conflicts "--cask", "--deps"
|
||||
conflicts "--cask", "-s"
|
||||
conflicts "--cask", "--build-bottle"
|
||||
conflicts "--cask", "--force-bottle"
|
||||
conflicts "--cask", "--bottle-tag"
|
||||
conflicts "--formula", "--cask"
|
||||
conflicts "--os", "--bottle-tag"
|
||||
conflicts "--arch", "--bottle-tag"
|
||||
|
||||
named_args [:formula, :cask], min: 1
|
||||
end
|
||||
end
|
||||
|
||||
def self.fetch
|
||||
args = fetch_args.parse
|
||||
|
||||
Formulary.enable_factory_cache!
|
||||
|
||||
bucket = if args.deps?
|
||||
args.named.to_formulae_and_casks.flat_map do |formula_or_cask|
|
||||
case formula_or_cask
|
||||
when Formula
|
||||
formula = formula_or_cask
|
||||
[formula, *formula.recursive_dependencies.map(&:to_formula)]
|
||||
else
|
||||
formula_or_cask
|
||||
end
|
||||
named_args [:formula, :cask], min: 1
|
||||
end
|
||||
else
|
||||
args.named.to_formulae_and_casks
|
||||
end.uniq
|
||||
|
||||
os_arch_combinations = args.os_arch_combinations
|
||||
sig { override.void }
|
||||
def run
|
||||
Formulary.enable_factory_cache!
|
||||
|
||||
puts "Fetching: #{bucket * ", "}" if bucket.size > 1
|
||||
bucket.each do |formula_or_cask|
|
||||
case formula_or_cask
|
||||
when Formula
|
||||
formula = T.cast(formula_or_cask, Formula)
|
||||
ref = formula.loaded_from_api? ? formula.full_name : formula.path
|
||||
bucket = if args.deps?
|
||||
args.named.to_formulae_and_casks.flat_map do |formula_or_cask|
|
||||
case formula_or_cask
|
||||
when Formula
|
||||
formula = formula_or_cask
|
||||
[formula, *formula.recursive_dependencies.map(&:to_formula)]
|
||||
else
|
||||
formula_or_cask
|
||||
end
|
||||
end
|
||||
else
|
||||
args.named.to_formulae_and_casks
|
||||
end.uniq
|
||||
|
||||
os_arch_combinations.each do |os, arch|
|
||||
SimulateSystem.with(os:, arch:) do
|
||||
formula = Formulary.factory(ref, args.HEAD? ? :head : :stable)
|
||||
os_arch_combinations = args.os_arch_combinations
|
||||
|
||||
formula.print_tap_action verb: "Fetching"
|
||||
puts "Fetching: #{bucket * ", "}" if bucket.size > 1
|
||||
bucket.each do |formula_or_cask|
|
||||
case formula_or_cask
|
||||
when Formula
|
||||
formula = T.cast(formula_or_cask, Formula)
|
||||
ref = formula.loaded_from_api? ? formula.full_name : formula.path
|
||||
|
||||
fetched_bottle = false
|
||||
if fetch_bottle?(
|
||||
formula,
|
||||
force_bottle: args.force_bottle?,
|
||||
bottle_tag: args.bottle_tag&.to_sym,
|
||||
build_from_source_formulae: args.build_from_source_formulae,
|
||||
os: args.os&.to_sym,
|
||||
arch: args.arch&.to_sym,
|
||||
)
|
||||
begin
|
||||
formula.clear_cache if args.force?
|
||||
os_arch_combinations.each do |os, arch|
|
||||
SimulateSystem.with(os:, arch:) do
|
||||
formula = Formulary.factory(ref, args.HEAD? ? :head : :stable)
|
||||
|
||||
bottle_tag = if (bottle_tag = args.bottle_tag&.to_sym)
|
||||
Utils::Bottles::Tag.from_symbol(bottle_tag)
|
||||
else
|
||||
Utils::Bottles::Tag.new(system: os, arch:)
|
||||
formula.print_tap_action verb: "Fetching"
|
||||
|
||||
fetched_bottle = false
|
||||
if fetch_bottle?(
|
||||
formula,
|
||||
force_bottle: args.force_bottle?,
|
||||
bottle_tag: args.bottle_tag&.to_sym,
|
||||
build_from_source_formulae: args.build_from_source_formulae,
|
||||
os: args.os&.to_sym,
|
||||
arch: args.arch&.to_sym,
|
||||
)
|
||||
begin
|
||||
formula.clear_cache if args.force?
|
||||
|
||||
bottle_tag = if (bottle_tag = args.bottle_tag&.to_sym)
|
||||
Utils::Bottles::Tag.from_symbol(bottle_tag)
|
||||
else
|
||||
Utils::Bottles::Tag.new(system: os, arch:)
|
||||
end
|
||||
|
||||
bottle = formula.bottle_for_tag(bottle_tag)
|
||||
|
||||
if bottle.nil?
|
||||
opoo "Bottle for tag #{bottle_tag.to_sym.inspect} is unavailable."
|
||||
next
|
||||
end
|
||||
|
||||
begin
|
||||
bottle.fetch_tab
|
||||
rescue DownloadError
|
||||
retry if retry_fetch?(bottle)
|
||||
raise
|
||||
end
|
||||
fetch_formula(bottle)
|
||||
rescue Interrupt
|
||||
raise
|
||||
rescue => e
|
||||
raise if Homebrew::EnvConfig.developer?
|
||||
|
||||
fetched_bottle = false
|
||||
onoe e.message
|
||||
opoo "Bottle fetch failed, fetching the source instead."
|
||||
else
|
||||
fetched_bottle = true
|
||||
end
|
||||
end
|
||||
|
||||
bottle = formula.bottle_for_tag(bottle_tag)
|
||||
next if fetched_bottle
|
||||
|
||||
if bottle.nil?
|
||||
opoo "Bottle for tag #{bottle_tag.to_sym.inspect} is unavailable."
|
||||
fetch_formula(formula)
|
||||
|
||||
formula.resources.each do |r|
|
||||
fetch_resource(r)
|
||||
r.patches.each { |p| fetch_patch(p) if p.external? }
|
||||
end
|
||||
|
||||
formula.patchlist.each { |p| fetch_patch(p) if p.external? }
|
||||
end
|
||||
end
|
||||
else
|
||||
cask = formula_or_cask
|
||||
ref = cask.loaded_from_api? ? cask.full_name : cask.sourcefile_path
|
||||
|
||||
os_arch_combinations.each do |os, arch|
|
||||
next if os == :linux
|
||||
|
||||
SimulateSystem.with(os:, arch:) do
|
||||
cask = Cask::CaskLoader.load(ref)
|
||||
|
||||
if cask.url.nil? || cask.sha256.nil?
|
||||
opoo "Cask #{cask} is not supported on os #{os} and arch #{arch}"
|
||||
next
|
||||
end
|
||||
|
||||
begin
|
||||
bottle.fetch_tab
|
||||
rescue DownloadError
|
||||
retry if retry_fetch?(bottle, args:)
|
||||
raise
|
||||
end
|
||||
fetch_formula(bottle, args:)
|
||||
rescue Interrupt
|
||||
raise
|
||||
rescue => e
|
||||
raise if Homebrew::EnvConfig.developer?
|
||||
quarantine = args.quarantine?
|
||||
quarantine = true if quarantine.nil?
|
||||
|
||||
fetched_bottle = false
|
||||
onoe e.message
|
||||
opoo "Bottle fetch failed, fetching the source instead."
|
||||
else
|
||||
fetched_bottle = true
|
||||
download = Cask::Download.new(cask, quarantine:)
|
||||
fetch_cask(download)
|
||||
end
|
||||
end
|
||||
|
||||
next if fetched_bottle
|
||||
|
||||
fetch_formula(formula, args:)
|
||||
|
||||
formula.resources.each do |r|
|
||||
fetch_resource(r, args:)
|
||||
r.patches.each { |p| fetch_patch(p, args:) if p.external? }
|
||||
end
|
||||
|
||||
formula.patchlist.each { |p| fetch_patch(p, args:) if p.external? }
|
||||
end
|
||||
end
|
||||
else
|
||||
cask = formula_or_cask
|
||||
ref = cask.loaded_from_api? ? cask.full_name : cask.sourcefile_path
|
||||
|
||||
os_arch_combinations.each do |os, arch|
|
||||
next if os == :linux
|
||||
|
||||
SimulateSystem.with(os:, arch:) do
|
||||
cask = Cask::CaskLoader.load(ref)
|
||||
|
||||
if cask.url.nil? || cask.sha256.nil?
|
||||
opoo "Cask #{cask} is not supported on os #{os} and arch #{arch}"
|
||||
next
|
||||
end
|
||||
|
||||
quarantine = args.quarantine?
|
||||
quarantine = true if quarantine.nil?
|
||||
|
||||
download = Cask::Download.new(cask, quarantine:)
|
||||
fetch_cask(download, args:)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def fetch_resource(resource)
|
||||
puts "Resource: #{resource.name}"
|
||||
fetch_fetchable resource
|
||||
rescue ChecksumMismatchError => e
|
||||
retry if retry_fetch?(resource)
|
||||
opoo "Resource #{resource.name} reports different sha256: #{e.expected}"
|
||||
end
|
||||
|
||||
def fetch_formula(formula)
|
||||
fetch_fetchable(formula)
|
||||
rescue ChecksumMismatchError => e
|
||||
retry if retry_fetch?(formula)
|
||||
opoo "Formula reports different sha256: #{e.expected}"
|
||||
end
|
||||
|
||||
def fetch_cask(cask_download)
|
||||
fetch_fetchable(cask_download)
|
||||
rescue ChecksumMismatchError => e
|
||||
retry if retry_fetch?(cask_download)
|
||||
opoo "Cask reports different sha256: #{e.expected}"
|
||||
end
|
||||
|
||||
def fetch_patch(patch)
|
||||
fetch_fetchable(patch)
|
||||
rescue ChecksumMismatchError => e
|
||||
opoo "Patch reports different sha256: #{e.expected}"
|
||||
Homebrew.failed = true
|
||||
end
|
||||
|
||||
def retry_fetch?(formula)
|
||||
@fetch_tries ||= Hash.new { |h, k| h[k] = 1 }
|
||||
if args.retry? && (@fetch_tries[formula] < FETCH_MAX_TRIES)
|
||||
wait = 2 ** @fetch_tries[formula]
|
||||
remaining = FETCH_MAX_TRIES - @fetch_tries[formula]
|
||||
what = Utils.pluralize("tr", remaining, plural: "ies", singular: "y")
|
||||
|
||||
ohai "Retrying download in #{wait}s... (#{remaining} #{what} left)"
|
||||
sleep wait
|
||||
|
||||
formula.clear_cache
|
||||
@fetch_tries[formula] += 1
|
||||
true
|
||||
else
|
||||
Homebrew.failed = true
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
def fetch_fetchable(formula)
|
||||
formula.clear_cache if args.force?
|
||||
|
||||
already_fetched = formula.cached_download.exist?
|
||||
|
||||
begin
|
||||
download = formula.fetch(verify_download_integrity: false)
|
||||
rescue DownloadError
|
||||
retry if retry_fetch?(formula)
|
||||
raise
|
||||
end
|
||||
|
||||
return unless download.file?
|
||||
|
||||
puts "Downloaded to: #{download}" unless already_fetched
|
||||
puts "SHA256: #{download.sha256}"
|
||||
|
||||
formula.verify_download_integrity(download)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def self.fetch_resource(resource, args:)
|
||||
puts "Resource: #{resource.name}"
|
||||
fetch_fetchable resource, args:
|
||||
rescue ChecksumMismatchError => e
|
||||
retry if retry_fetch?(resource, args:)
|
||||
opoo "Resource #{resource.name} reports different sha256: #{e.expected}"
|
||||
end
|
||||
|
||||
def self.fetch_formula(formula, args:)
|
||||
fetch_fetchable(formula, args:)
|
||||
rescue ChecksumMismatchError => e
|
||||
retry if retry_fetch?(formula, args:)
|
||||
opoo "Formula reports different sha256: #{e.expected}"
|
||||
end
|
||||
|
||||
def self.fetch_cask(cask_download, args:)
|
||||
fetch_fetchable(cask_download, args:)
|
||||
rescue ChecksumMismatchError => e
|
||||
retry if retry_fetch?(cask_download, args:)
|
||||
opoo "Cask reports different sha256: #{e.expected}"
|
||||
end
|
||||
|
||||
def self.fetch_patch(patch, args:)
|
||||
fetch_fetchable(patch, args:)
|
||||
rescue ChecksumMismatchError => e
|
||||
opoo "Patch reports different sha256: #{e.expected}"
|
||||
Homebrew.failed = true
|
||||
end
|
||||
|
||||
def self.retry_fetch?(formula, args:)
|
||||
@fetch_tries ||= Hash.new { |h, k| h[k] = 1 }
|
||||
if args.retry? && (@fetch_tries[formula] < FETCH_MAX_TRIES)
|
||||
wait = 2 ** @fetch_tries[formula]
|
||||
remaining = FETCH_MAX_TRIES - @fetch_tries[formula]
|
||||
what = Utils.pluralize("tr", remaining, plural: "ies", singular: "y")
|
||||
|
||||
ohai "Retrying download in #{wait}s... (#{remaining} #{what} left)"
|
||||
sleep wait
|
||||
|
||||
formula.clear_cache
|
||||
@fetch_tries[formula] += 1
|
||||
true
|
||||
else
|
||||
Homebrew.failed = true
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
def self.fetch_fetchable(formula, args:)
|
||||
formula.clear_cache if args.force?
|
||||
|
||||
already_fetched = formula.cached_download.exist?
|
||||
|
||||
begin
|
||||
download = formula.fetch(verify_download_integrity: false)
|
||||
rescue DownloadError
|
||||
retry if retry_fetch?(formula, args:)
|
||||
raise
|
||||
end
|
||||
|
||||
return unless download.file?
|
||||
|
||||
puts "Downloaded to: #{download}" unless already_fetched
|
||||
puts "SHA256: #{download.sha256}"
|
||||
|
||||
formula.verify_download_integrity(download)
|
||||
end
|
||||
end
|
||||
|
||||
@ -1,130 +1,132 @@
|
||||
# typed: true
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "abstract_command"
|
||||
require "formula"
|
||||
require "install"
|
||||
require "system_config"
|
||||
require "stringio"
|
||||
require "socket"
|
||||
require "cli/parser"
|
||||
|
||||
module Homebrew
|
||||
extend Install
|
||||
module Cmd
|
||||
class GistLogs < AbstractCommand
|
||||
include Install
|
||||
cmd_args do
|
||||
description <<~EOS
|
||||
Upload logs for a failed build of <formula> to a new Gist. Presents an
|
||||
error message if no logs are found.
|
||||
EOS
|
||||
switch "--with-hostname",
|
||||
description: "Include the hostname in the Gist."
|
||||
switch "-n", "--new-issue",
|
||||
description: "Automatically create a new issue in the appropriate GitHub repository " \
|
||||
"after creating the Gist."
|
||||
switch "-p", "--private",
|
||||
description: "The Gist will be marked private and will not appear in listings but will " \
|
||||
"be accessible with its link."
|
||||
|
||||
module_function
|
||||
named_args :formula, number: 1
|
||||
end
|
||||
|
||||
sig { returns(CLI::Parser) }
|
||||
def gist_logs_args
|
||||
Homebrew::CLI::Parser.new do
|
||||
description <<~EOS
|
||||
Upload logs for a failed build of <formula> to a new Gist. Presents an
|
||||
error message if no logs are found.
|
||||
EOS
|
||||
switch "--with-hostname",
|
||||
description: "Include the hostname in the Gist."
|
||||
switch "-n", "--new-issue",
|
||||
description: "Automatically create a new issue in the appropriate GitHub repository " \
|
||||
"after creating the Gist."
|
||||
switch "-p", "--private",
|
||||
description: "The Gist will be marked private and will not appear in listings but will " \
|
||||
"be accessible with its link."
|
||||
sig { override.void }
|
||||
def run
|
||||
Install.perform_preinstall_checks(all_fatal: true)
|
||||
Install.perform_build_from_source_checks(all_fatal: true)
|
||||
gistify_logs(args.named.to_resolved_formulae.first)
|
||||
end
|
||||
|
||||
named_args :formula, number: 1
|
||||
end
|
||||
end
|
||||
private
|
||||
|
||||
def gistify_logs(formula, args:)
|
||||
files = load_logs(formula.logs)
|
||||
build_time = formula.logs.ctime
|
||||
timestamp = build_time.strftime("%Y-%m-%d_%H-%M-%S")
|
||||
def gistify_logs(formula)
|
||||
files = load_logs(formula.logs)
|
||||
build_time = formula.logs.ctime
|
||||
timestamp = build_time.strftime("%Y-%m-%d_%H-%M-%S")
|
||||
|
||||
s = StringIO.new
|
||||
SystemConfig.dump_verbose_config s
|
||||
# Dummy summary file, asciibetically first, to control display title of gist
|
||||
files["# #{formula.name} - #{timestamp}.txt"] = {
|
||||
content: brief_build_info(formula, with_hostname: args.with_hostname?),
|
||||
}
|
||||
files["00.config.out"] = { content: s.string }
|
||||
files["00.doctor.out"] = { content: Utils.popen_read("#{HOMEBREW_PREFIX}/bin/brew", "doctor", err: :out) }
|
||||
unless formula.core_formula?
|
||||
tap = <<~EOS
|
||||
Formula: #{formula.name}
|
||||
Tap: #{formula.tap}
|
||||
Path: #{formula.path}
|
||||
EOS
|
||||
files["00.tap.out"] = { content: tap }
|
||||
end
|
||||
|
||||
odie "`brew gist-logs` requires HOMEBREW_GITHUB_API_TOKEN to be set!" if GitHub::API.credentials_type == :none
|
||||
|
||||
# Description formatted to work well as page title when viewing gist
|
||||
descr = if formula.core_formula?
|
||||
"#{formula.name} on #{OS_VERSION} - Homebrew build logs"
|
||||
else
|
||||
"#{formula.name} (#{formula.full_name}) on #{OS_VERSION} - Homebrew build logs"
|
||||
end
|
||||
|
||||
begin
|
||||
url = GitHub.create_gist(files, descr, private: args.private?)
|
||||
rescue GitHub::API::HTTPNotFoundError
|
||||
odie <<~EOS
|
||||
Your GitHub API token likely doesn't have the `gist` scope.
|
||||
#{GitHub.pat_blurb(GitHub::CREATE_GIST_SCOPES)}
|
||||
EOS
|
||||
end
|
||||
|
||||
url = GitHub.create_issue(formula.tap, "#{formula.name} failed to build on #{OS_VERSION}", url) if args.new_issue?
|
||||
|
||||
puts url if url
|
||||
end
|
||||
|
||||
def brief_build_info(formula, with_hostname:)
|
||||
build_time_string = formula.logs.ctime.strftime("%Y-%m-%d %H:%M:%S")
|
||||
string = +<<~EOS
|
||||
Homebrew build logs for #{formula.full_name} on #{OS_VERSION}
|
||||
EOS
|
||||
if with_hostname
|
||||
hostname = Socket.gethostname
|
||||
string << "Host: #{hostname}\n"
|
||||
end
|
||||
string << "Build date: #{build_time_string}\n"
|
||||
string.freeze
|
||||
end
|
||||
|
||||
# Causes some terminals to display secure password entry indicators.
|
||||
def noecho_gets
|
||||
system "stty", "-echo"
|
||||
result = $stdin.gets
|
||||
system "stty", "echo"
|
||||
puts
|
||||
result
|
||||
end
|
||||
|
||||
def load_logs(dir, basedir = dir)
|
||||
logs = {}
|
||||
if dir.exist?
|
||||
dir.children.sort.each do |file|
|
||||
if file.directory?
|
||||
logs.merge! load_logs(file, basedir)
|
||||
else
|
||||
contents = file.size? ? file.read : "empty log"
|
||||
# small enough to avoid GitHub "unicorn" page-load-timeout errors
|
||||
max_file_size = 1_000_000
|
||||
contents = truncate_text_to_approximate_size(contents, max_file_size, front_weight: 0.2)
|
||||
logs[file.relative_path_from(basedir).to_s.tr("/", ":")] = { content: contents }
|
||||
s = StringIO.new
|
||||
SystemConfig.dump_verbose_config s
|
||||
# Dummy summary file, asciibetically first, to control display title of gist
|
||||
files["# #{formula.name} - #{timestamp}.txt"] = {
|
||||
content: brief_build_info(formula, with_hostname: args.with_hostname?),
|
||||
}
|
||||
files["00.config.out"] = { content: s.string }
|
||||
files["00.doctor.out"] = { content: Utils.popen_read("#{HOMEBREW_PREFIX}/bin/brew", "doctor", err: :out) }
|
||||
unless formula.core_formula?
|
||||
tap = <<~EOS
|
||||
Formula: #{formula.name}
|
||||
Tap: #{formula.tap}
|
||||
Path: #{formula.path}
|
||||
EOS
|
||||
files["00.tap.out"] = { content: tap }
|
||||
end
|
||||
|
||||
odie "`brew gist-logs` requires HOMEBREW_GITHUB_API_TOKEN to be set!" if GitHub::API.credentials_type == :none
|
||||
|
||||
# Description formatted to work well as page title when viewing gist
|
||||
descr = if formula.core_formula?
|
||||
"#{formula.name} on #{OS_VERSION} - Homebrew build logs"
|
||||
else
|
||||
"#{formula.name} (#{formula.full_name}) on #{OS_VERSION} - Homebrew build logs"
|
||||
end
|
||||
|
||||
begin
|
||||
url = GitHub.create_gist(files, descr, private: args.private?)
|
||||
rescue GitHub::API::HTTPNotFoundError
|
||||
odie <<~EOS
|
||||
Your GitHub API token likely doesn't have the `gist` scope.
|
||||
#{GitHub.pat_blurb(GitHub::CREATE_GIST_SCOPES)}
|
||||
EOS
|
||||
end
|
||||
|
||||
if args.new_issue?
|
||||
url = GitHub.create_issue(formula.tap, "#{formula.name} failed to build on #{OS_VERSION}",
|
||||
url)
|
||||
end
|
||||
|
||||
puts url if url
|
||||
end
|
||||
|
||||
def brief_build_info(formula, with_hostname:)
|
||||
build_time_string = formula.logs.ctime.strftime("%Y-%m-%d %H:%M:%S")
|
||||
string = +<<~EOS
|
||||
Homebrew build logs for #{formula.full_name} on #{OS_VERSION}
|
||||
EOS
|
||||
if with_hostname
|
||||
hostname = Socket.gethostname
|
||||
string << "Host: #{hostname}\n"
|
||||
end
|
||||
string << "Build date: #{build_time_string}\n"
|
||||
string.freeze
|
||||
end
|
||||
|
||||
# Causes some terminals to display secure password entry indicators.
|
||||
def noecho_gets
|
||||
system "stty", "-echo"
|
||||
result = $stdin.gets
|
||||
system "stty", "echo"
|
||||
puts
|
||||
result
|
||||
end
|
||||
|
||||
def load_logs(dir, basedir = dir)
|
||||
logs = {}
|
||||
if dir.exist?
|
||||
dir.children.sort.each do |file|
|
||||
if file.directory?
|
||||
logs.merge! load_logs(file, basedir)
|
||||
else
|
||||
contents = file.size? ? file.read : "empty log"
|
||||
# small enough to avoid GitHub "unicorn" page-load-timeout errors
|
||||
max_file_size = 1_000_000
|
||||
contents = truncate_text_to_approximate_size(contents, max_file_size, front_weight: 0.2)
|
||||
logs[file.relative_path_from(basedir).to_s.tr("/", ":")] = { content: contents }
|
||||
end
|
||||
end
|
||||
end
|
||||
odie "No logs." if logs.empty?
|
||||
|
||||
logs
|
||||
end
|
||||
end
|
||||
odie "No logs." if logs.empty?
|
||||
|
||||
logs
|
||||
end
|
||||
|
||||
def gist_logs
|
||||
args = gist_logs_args.parse
|
||||
|
||||
Install.perform_preinstall_checks(all_fatal: true)
|
||||
Install.perform_build_from_source_checks(all_fatal: true)
|
||||
gistify_logs(args.named.to_resolved_formulae.first, args:)
|
||||
end
|
||||
end
|
||||
|
||||
@ -1,11 +1,16 @@
|
||||
# typed: strict
|
||||
# typed: strong
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "abstract_command"
|
||||
require "help"
|
||||
|
||||
module Homebrew
|
||||
sig { returns(T.noreturn) }
|
||||
def help
|
||||
Help.help
|
||||
module Cmd
|
||||
class HelpCmd < AbstractCommand
|
||||
sig { override.void }
|
||||
def run
|
||||
Help.help
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -1,54 +1,53 @@
|
||||
# typed: true
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "cli/parser"
|
||||
require "abstract_command"
|
||||
require "formula"
|
||||
|
||||
module Homebrew
|
||||
module_function
|
||||
module Cmd
|
||||
class Home < AbstractCommand
|
||||
cmd_args do
|
||||
description <<~EOS
|
||||
Open a <formula> or <cask>'s homepage in a browser, or open
|
||||
Homebrew's own homepage if no argument is provided.
|
||||
EOS
|
||||
switch "--formula", "--formulae",
|
||||
description: "Treat all named arguments as formulae."
|
||||
switch "--cask", "--casks",
|
||||
description: "Treat all named arguments as casks."
|
||||
|
||||
sig { returns(CLI::Parser) }
|
||||
def home_args
|
||||
Homebrew::CLI::Parser.new do
|
||||
description <<~EOS
|
||||
Open a <formula> or <cask>'s homepage in a browser, or open
|
||||
Homebrew's own homepage if no argument is provided.
|
||||
EOS
|
||||
switch "--formula", "--formulae",
|
||||
description: "Treat all named arguments as formulae."
|
||||
switch "--cask", "--casks",
|
||||
description: "Treat all named arguments as casks."
|
||||
conflicts "--formula", "--cask"
|
||||
|
||||
conflicts "--formula", "--cask"
|
||||
named_args [:formula, :cask]
|
||||
end
|
||||
|
||||
named_args [:formula, :cask]
|
||||
end
|
||||
end
|
||||
sig { override.void }
|
||||
def run
|
||||
if args.no_named?
|
||||
exec_browser HOMEBREW_WWW
|
||||
return
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def home
|
||||
args = home_args.parse
|
||||
# to_formulae_and_casks is typed to possibly return Kegs (but won't without explicitly asking)
|
||||
formulae_or_casks = T.cast(args.named.to_formulae_and_casks, T::Array[T.any(Formula, Cask::Cask)])
|
||||
homepages = formulae_or_casks.map do |formula_or_cask|
|
||||
puts "Opening homepage for #{name_of(formula_or_cask)}"
|
||||
formula_or_cask.homepage
|
||||
end
|
||||
|
||||
if args.no_named?
|
||||
exec_browser HOMEBREW_WWW
|
||||
return
|
||||
end
|
||||
exec_browser(*T.unsafe(homepages))
|
||||
end
|
||||
|
||||
# to_formulae_and_casks is typed to possibly return Kegs (but won't without explicitly asking)
|
||||
formulae_or_casks = T.cast(args.named.to_formulae_and_casks, T::Array[T.any(Formula, Cask::Cask)])
|
||||
homepages = formulae_or_casks.map do |formula_or_cask|
|
||||
puts "Opening homepage for #{name_of(formula_or_cask)}"
|
||||
formula_or_cask.homepage
|
||||
end
|
||||
private
|
||||
|
||||
exec_browser(*T.unsafe(homepages))
|
||||
end
|
||||
|
||||
def name_of(formula_or_cask)
|
||||
if formula_or_cask.is_a? Formula
|
||||
"Formula #{formula_or_cask.name}"
|
||||
else
|
||||
"Cask #{formula_or_cask.token}"
|
||||
def name_of(formula_or_cask)
|
||||
if formula_or_cask.is_a? Formula
|
||||
"Formula #{formula_or_cask.name}"
|
||||
else
|
||||
"Cask #{formula_or_cask.token}"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
# typed: true
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "abstract_command"
|
||||
require "missing_formula"
|
||||
require "caveats"
|
||||
require "cli/parser"
|
||||
require "options"
|
||||
require "formula"
|
||||
require "keg"
|
||||
@ -14,364 +14,363 @@ require "deprecate_disable"
|
||||
require "api"
|
||||
|
||||
module Homebrew
|
||||
module_function
|
||||
module Cmd
|
||||
class Info < AbstractCommand
|
||||
VALID_DAYS = %w[30 90 365].freeze
|
||||
VALID_FORMULA_CATEGORIES = %w[install install-on-request build-error].freeze
|
||||
VALID_CATEGORIES = (VALID_FORMULA_CATEGORIES + %w[cask-install os-version]).freeze
|
||||
|
||||
VALID_DAYS = %w[30 90 365].freeze
|
||||
VALID_FORMULA_CATEGORIES = %w[install install-on-request build-error].freeze
|
||||
VALID_CATEGORIES = (VALID_FORMULA_CATEGORIES + %w[cask-install os-version]).freeze
|
||||
cmd_args do
|
||||
description <<~EOS
|
||||
Display brief statistics for your Homebrew installation.
|
||||
If a <formula> or <cask> is provided, show summary of information about it.
|
||||
EOS
|
||||
switch "--analytics",
|
||||
description: "List global Homebrew analytics data or, if specified, installation and " \
|
||||
"build error data for <formula> (provided neither `HOMEBREW_NO_ANALYTICS` " \
|
||||
"nor `HOMEBREW_NO_GITHUB_API` are set)."
|
||||
flag "--days=",
|
||||
depends_on: "--analytics",
|
||||
description: "How many days of analytics data to retrieve. " \
|
||||
"The value for <days> must be `30`, `90` or `365`. The default is `30`."
|
||||
flag "--category=",
|
||||
depends_on: "--analytics",
|
||||
description: "Which type of analytics data to retrieve. " \
|
||||
"The value for <category> must be `install`, `install-on-request` or `build-error`; " \
|
||||
"`cask-install` or `os-version` may be specified if <formula> is not. " \
|
||||
"The default is `install`."
|
||||
switch "--github-packages-downloads",
|
||||
description: "Scrape GitHub Packages download counts from HTML for a core formula.",
|
||||
hidden: true
|
||||
switch "--github",
|
||||
description: "Open the GitHub source page for <formula> and <cask> in a browser. " \
|
||||
"To view the history locally: `brew log -p` <formula> or <cask>"
|
||||
flag "--json",
|
||||
description: "Print a JSON representation. Currently the default value for <version> is `v1` for " \
|
||||
"<formula>. For <formula> and <cask> use `v2`. See the docs for examples of using the " \
|
||||
"JSON output: <https://docs.brew.sh/Querying-Brew>"
|
||||
switch "--installed",
|
||||
depends_on: "--json",
|
||||
description: "Print JSON of formulae that are currently installed."
|
||||
switch "--eval-all",
|
||||
depends_on: "--json",
|
||||
description: "Evaluate all available formulae and casks, whether installed or not, to print their " \
|
||||
"JSON. Implied if `HOMEBREW_EVAL_ALL` is set."
|
||||
switch "--variations",
|
||||
depends_on: "--json",
|
||||
description: "Include the variations hash in each formula's JSON output."
|
||||
switch "-v", "--verbose",
|
||||
description: "Show more verbose analytics data for <formula>."
|
||||
switch "--formula", "--formulae",
|
||||
description: "Treat all named arguments as formulae."
|
||||
switch "--cask", "--casks",
|
||||
description: "Treat all named arguments as casks."
|
||||
|
||||
sig { returns(CLI::Parser) }
|
||||
def info_args
|
||||
Homebrew::CLI::Parser.new do
|
||||
description <<~EOS
|
||||
Display brief statistics for your Homebrew installation.
|
||||
If a <formula> or <cask> is provided, show summary of information about it.
|
||||
EOS
|
||||
switch "--analytics",
|
||||
description: "List global Homebrew analytics data or, if specified, installation and " \
|
||||
"build error data for <formula> (provided neither `HOMEBREW_NO_ANALYTICS` " \
|
||||
"nor `HOMEBREW_NO_GITHUB_API` are set)."
|
||||
flag "--days=",
|
||||
depends_on: "--analytics",
|
||||
description: "How many days of analytics data to retrieve. " \
|
||||
"The value for <days> must be `30`, `90` or `365`. The default is `30`."
|
||||
flag "--category=",
|
||||
depends_on: "--analytics",
|
||||
description: "Which type of analytics data to retrieve. " \
|
||||
"The value for <category> must be `install`, `install-on-request` or `build-error`; " \
|
||||
"`cask-install` or `os-version` may be specified if <formula> is not. " \
|
||||
"The default is `install`."
|
||||
switch "--github-packages-downloads",
|
||||
description: "Scrape GitHub Packages download counts from HTML for a core formula.",
|
||||
hidden: true
|
||||
switch "--github",
|
||||
description: "Open the GitHub source page for <formula> and <cask> in a browser. " \
|
||||
"To view the history locally: `brew log -p` <formula> or <cask>"
|
||||
flag "--json",
|
||||
description: "Print a JSON representation. Currently the default value for <version> is `v1` for " \
|
||||
"<formula>. For <formula> and <cask> use `v2`. See the docs for examples of using the " \
|
||||
"JSON output: <https://docs.brew.sh/Querying-Brew>"
|
||||
switch "--installed",
|
||||
depends_on: "--json",
|
||||
description: "Print JSON of formulae that are currently installed."
|
||||
switch "--eval-all",
|
||||
depends_on: "--json",
|
||||
description: "Evaluate all available formulae and casks, whether installed or not, to print their " \
|
||||
"JSON. Implied if `HOMEBREW_EVAL_ALL` is set."
|
||||
switch "--variations",
|
||||
depends_on: "--json",
|
||||
description: "Include the variations hash in each formula's JSON output."
|
||||
switch "-v", "--verbose",
|
||||
description: "Show more verbose analytics data for <formula>."
|
||||
switch "--formula", "--formulae",
|
||||
description: "Treat all named arguments as formulae."
|
||||
switch "--cask", "--casks",
|
||||
description: "Treat all named arguments as casks."
|
||||
conflicts "--installed", "--eval-all"
|
||||
conflicts "--installed", "--all"
|
||||
conflicts "--formula", "--cask"
|
||||
|
||||
conflicts "--installed", "--eval-all"
|
||||
conflicts "--installed", "--all"
|
||||
conflicts "--formula", "--cask"
|
||||
|
||||
named_args [:formula, :cask]
|
||||
end
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def info
|
||||
args = info_args.parse
|
||||
|
||||
if args.analytics?
|
||||
if args.days.present? && VALID_DAYS.exclude?(args.days)
|
||||
raise UsageError, "`--days` must be one of #{VALID_DAYS.join(", ")}."
|
||||
named_args [:formula, :cask]
|
||||
end
|
||||
|
||||
if args.category.present?
|
||||
if args.named.present? && VALID_FORMULA_CATEGORIES.exclude?(args.category)
|
||||
raise UsageError,
|
||||
"`--category` must be one of #{VALID_FORMULA_CATEGORIES.join(", ")} when querying formulae."
|
||||
end
|
||||
sig { override.void }
|
||||
def run
|
||||
if args.analytics?
|
||||
if args.days.present? && VALID_DAYS.exclude?(args.days)
|
||||
raise UsageError, "`--days` must be one of #{VALID_DAYS.join(", ")}."
|
||||
end
|
||||
|
||||
unless VALID_CATEGORIES.include?(args.category)
|
||||
raise UsageError, "`--category` must be one of #{VALID_CATEGORIES.join(", ")}."
|
||||
if args.category.present?
|
||||
if args.named.present? && VALID_FORMULA_CATEGORIES.exclude?(args.category)
|
||||
raise UsageError,
|
||||
"`--category` must be one of #{VALID_FORMULA_CATEGORIES.join(", ")} when querying formulae."
|
||||
end
|
||||
|
||||
unless VALID_CATEGORIES.include?(args.category)
|
||||
raise UsageError, "`--category` must be one of #{VALID_CATEGORIES.join(", ")}."
|
||||
end
|
||||
end
|
||||
|
||||
print_analytics
|
||||
elsif args.json
|
||||
all = args.eval_all?
|
||||
|
||||
print_json(all)
|
||||
elsif args.github?
|
||||
raise FormulaOrCaskUnspecifiedError if args.no_named?
|
||||
|
||||
exec_browser(*args.named.to_formulae_and_casks.map { |f| github_info(f) })
|
||||
elsif args.no_named?
|
||||
print_statistics
|
||||
else
|
||||
print_info
|
||||
end
|
||||
end
|
||||
|
||||
print_analytics(args:)
|
||||
elsif args.json
|
||||
all = args.eval_all?
|
||||
|
||||
print_json(all, args:)
|
||||
elsif args.github?
|
||||
raise FormulaOrCaskUnspecifiedError if args.no_named?
|
||||
|
||||
exec_browser(*args.named.to_formulae_and_casks.map { |f| github_info(f) })
|
||||
elsif args.no_named?
|
||||
print_statistics
|
||||
else
|
||||
print_info(args:)
|
||||
end
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def print_statistics
|
||||
return unless HOMEBREW_CELLAR.exist?
|
||||
|
||||
count = Formula.racks.length
|
||||
puts "#{Utils.pluralize("keg", count, include_count: true)}, #{HOMEBREW_CELLAR.dup.abv}"
|
||||
end
|
||||
|
||||
sig { params(args: CLI::Args).void }
|
||||
def print_analytics(args:)
|
||||
if args.no_named?
|
||||
Utils::Analytics.output(args:)
|
||||
return
|
||||
end
|
||||
|
||||
args.named.to_formulae_and_casks_and_unavailable.each_with_index do |obj, i|
|
||||
puts unless i.zero?
|
||||
|
||||
case obj
|
||||
when Formula
|
||||
Utils::Analytics.formula_output(obj, args:)
|
||||
when Cask::Cask
|
||||
Utils::Analytics.cask_output(obj, args:)
|
||||
when FormulaOrCaskUnavailableError
|
||||
Utils::Analytics.output(filter: obj.name, args:)
|
||||
else
|
||||
raise
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
sig { params(args: CLI::Args).void }
|
||||
def print_info(args:)
|
||||
args.named.to_formulae_and_casks_and_unavailable.each_with_index do |obj, i|
|
||||
puts unless i.zero?
|
||||
|
||||
case obj
|
||||
when Formula
|
||||
info_formula(obj, args:)
|
||||
when Cask::Cask
|
||||
info_cask(obj, args:)
|
||||
when FormulaUnreadableError, FormulaClassUnavailableError,
|
||||
TapFormulaUnreadableError, TapFormulaClassUnavailableError,
|
||||
Cask::CaskUnreadableError
|
||||
# We found the formula/cask, but failed to read it
|
||||
$stderr.puts obj.backtrace if Homebrew::EnvConfig.developer?
|
||||
ofail obj.message
|
||||
when FormulaOrCaskUnavailableError
|
||||
# The formula/cask could not be found
|
||||
ofail obj.message
|
||||
# No formula with this name, try a missing formula lookup
|
||||
if (reason = MissingFormula.reason(obj.name, show_info: true))
|
||||
$stderr.puts reason
|
||||
def github_remote_path(remote, path)
|
||||
if remote =~ %r{^(?:https?://|git(?:@|://))github\.com[:/](.+)/(.+?)(?:\.git)?$}
|
||||
"https://github.com/#{Regexp.last_match(1)}/#{Regexp.last_match(2)}/blob/HEAD/#{path}"
|
||||
else
|
||||
"#{remote}/#{path}"
|
||||
end
|
||||
else
|
||||
raise
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def json_version(version)
|
||||
version_hash = {
|
||||
true => :default,
|
||||
"v1" => :v1,
|
||||
"v2" => :v2,
|
||||
}
|
||||
|
||||
raise UsageError, "invalid JSON version: #{version}" unless version_hash.include?(version)
|
||||
|
||||
version_hash[version]
|
||||
end
|
||||
|
||||
sig { params(all: T::Boolean, args: T.untyped).void }
|
||||
def print_json(all, args:)
|
||||
raise FormulaOrCaskUnspecifiedError if !(all || args.installed?) && args.no_named?
|
||||
|
||||
json = case json_version(args.json)
|
||||
when :v1, :default
|
||||
raise UsageError, "Cannot specify `--cask` when using `--json=v1`!" if args.cask?
|
||||
|
||||
formulae = if all
|
||||
Formula.all(eval_all: args.eval_all?).sort
|
||||
elsif args.installed?
|
||||
Formula.installed.sort
|
||||
else
|
||||
args.named.to_formulae
|
||||
end
|
||||
|
||||
if args.variations?
|
||||
formulae.map(&:to_hash_with_variations)
|
||||
else
|
||||
formulae.map(&:to_hash)
|
||||
private
|
||||
|
||||
sig { void }
|
||||
def print_statistics
|
||||
return unless HOMEBREW_CELLAR.exist?
|
||||
|
||||
count = Formula.racks.length
|
||||
puts "#{Utils.pluralize("keg", count, include_count: true)}, #{HOMEBREW_CELLAR.dup.abv}"
|
||||
end
|
||||
when :v2
|
||||
formulae, casks = if all
|
||||
[
|
||||
Formula.all(eval_all: args.eval_all?).sort,
|
||||
Cask::Cask.all(eval_all: args.eval_all?).sort_by(&:full_name),
|
||||
|
||||
sig { void }
|
||||
def print_analytics
|
||||
if args.no_named?
|
||||
Utils::Analytics.output(args:)
|
||||
return
|
||||
end
|
||||
|
||||
args.named.to_formulae_and_casks_and_unavailable.each_with_index do |obj, i|
|
||||
puts unless i.zero?
|
||||
|
||||
case obj
|
||||
when Formula
|
||||
Utils::Analytics.formula_output(obj, args:)
|
||||
when Cask::Cask
|
||||
Utils::Analytics.cask_output(obj, args:)
|
||||
when FormulaOrCaskUnavailableError
|
||||
Utils::Analytics.output(filter: obj.name, args:)
|
||||
else
|
||||
raise
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def print_info
|
||||
args.named.to_formulae_and_casks_and_unavailable.each_with_index do |obj, i|
|
||||
puts unless i.zero?
|
||||
|
||||
case obj
|
||||
when Formula
|
||||
info_formula(obj)
|
||||
when Cask::Cask
|
||||
info_cask(obj)
|
||||
when FormulaUnreadableError, FormulaClassUnavailableError,
|
||||
TapFormulaUnreadableError, TapFormulaClassUnavailableError,
|
||||
Cask::CaskUnreadableError
|
||||
# We found the formula/cask, but failed to read it
|
||||
$stderr.puts obj.backtrace if Homebrew::EnvConfig.developer?
|
||||
ofail obj.message
|
||||
when FormulaOrCaskUnavailableError
|
||||
# The formula/cask could not be found
|
||||
ofail obj.message
|
||||
# No formula with this name, try a missing formula lookup
|
||||
if (reason = MissingFormula.reason(obj.name, show_info: true))
|
||||
$stderr.puts reason
|
||||
end
|
||||
else
|
||||
raise
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def json_version(version)
|
||||
version_hash = {
|
||||
true => :default,
|
||||
"v1" => :v1,
|
||||
"v2" => :v2,
|
||||
}
|
||||
|
||||
raise UsageError, "invalid JSON version: #{version}" unless version_hash.include?(version)
|
||||
|
||||
version_hash[version]
|
||||
end
|
||||
|
||||
sig { params(all: T::Boolean).void }
|
||||
def print_json(all)
|
||||
raise FormulaOrCaskUnspecifiedError if !(all || args.installed?) && args.no_named?
|
||||
|
||||
json = case json_version(args.json)
|
||||
when :v1, :default
|
||||
raise UsageError, "Cannot specify `--cask` when using `--json=v1`!" if args.cask?
|
||||
|
||||
formulae = if all
|
||||
Formula.all(eval_all: args.eval_all?).sort
|
||||
elsif args.installed?
|
||||
Formula.installed.sort
|
||||
else
|
||||
args.named.to_formulae
|
||||
end
|
||||
|
||||
if args.variations?
|
||||
formulae.map(&:to_hash_with_variations)
|
||||
else
|
||||
formulae.map(&:to_hash)
|
||||
end
|
||||
when :v2
|
||||
formulae, casks = if all
|
||||
[
|
||||
Formula.all(eval_all: args.eval_all?).sort,
|
||||
Cask::Cask.all(eval_all: args.eval_all?).sort_by(&:full_name),
|
||||
]
|
||||
elsif args.installed?
|
||||
[Formula.installed.sort, Cask::Caskroom.casks.sort_by(&:full_name)]
|
||||
else
|
||||
args.named.to_formulae_to_casks
|
||||
end
|
||||
|
||||
if args.variations?
|
||||
{
|
||||
"formulae" => formulae.map(&:to_hash_with_variations),
|
||||
"casks" => casks.map(&:to_hash_with_variations),
|
||||
}
|
||||
else
|
||||
{
|
||||
"formulae" => formulae.map(&:to_hash),
|
||||
"casks" => casks.map(&:to_h),
|
||||
}
|
||||
end
|
||||
else
|
||||
raise
|
||||
end
|
||||
|
||||
puts JSON.pretty_generate(json)
|
||||
end
|
||||
|
||||
def github_info(formula_or_cask)
|
||||
return formula_or_cask.path if formula_or_cask.tap.blank? || formula_or_cask.tap.remote.blank?
|
||||
|
||||
path = case formula_or_cask
|
||||
when Formula
|
||||
formula = formula_or_cask
|
||||
formula.path.relative_path_from(T.must(formula.tap).path)
|
||||
when Cask::Cask
|
||||
cask = formula_or_cask
|
||||
if cask.sourcefile_path.blank?
|
||||
return "#{cask.tap.default_remote}/blob/HEAD/#{cask.tap.relative_cask_path(cask.token)}"
|
||||
end
|
||||
|
||||
cask.sourcefile_path.relative_path_from(cask.tap.path)
|
||||
end
|
||||
|
||||
github_remote_path(formula_or_cask.tap.remote, path)
|
||||
end
|
||||
|
||||
def info_formula(formula)
|
||||
specs = []
|
||||
|
||||
if (stable = formula.stable)
|
||||
string = "stable #{stable.version}"
|
||||
string += " (bottled)" if stable.bottled? && formula.pour_bottle?
|
||||
specs << string
|
||||
end
|
||||
|
||||
specs << "HEAD" if formula.head
|
||||
|
||||
attrs = []
|
||||
attrs << "pinned at #{formula.pinned_version}" if formula.pinned?
|
||||
attrs << "keg-only" if formula.keg_only?
|
||||
|
||||
puts "#{oh1_title(formula.full_name)}: #{specs * ", "}#{" [#{attrs * ", "}]" unless attrs.empty?}"
|
||||
puts formula.desc if formula.desc
|
||||
puts Formatter.url(formula.homepage) if formula.homepage
|
||||
|
||||
deprecate_disable_info_string = DeprecateDisable.message(formula)
|
||||
puts deprecate_disable_info_string.capitalize if deprecate_disable_info_string.present?
|
||||
|
||||
conflicts = formula.conflicts.map do |conflict|
|
||||
reason = " (because #{conflict.reason})" if conflict.reason
|
||||
"#{conflict.name}#{reason}"
|
||||
end.sort!
|
||||
unless conflicts.empty?
|
||||
puts <<~EOS
|
||||
Conflicts with:
|
||||
#{conflicts.join("\n ")}
|
||||
EOS
|
||||
end
|
||||
|
||||
kegs = formula.installed_kegs
|
||||
heads, versioned = kegs.partition { |k| k.version.head? }
|
||||
kegs = [
|
||||
*heads.sort_by { |k| -Tab.for_keg(k).time.to_i },
|
||||
*Keg.sort(versioned),
|
||||
]
|
||||
elsif args.installed?
|
||||
[Formula.installed.sort, Cask::Caskroom.casks.sort_by(&:full_name)]
|
||||
else
|
||||
args.named.to_formulae_to_casks
|
||||
if kegs.empty?
|
||||
puts "Not installed"
|
||||
else
|
||||
kegs.each do |keg|
|
||||
puts "#{keg} (#{keg.abv})#{" *" if keg.linked?}"
|
||||
tab = Tab.for_keg(keg).to_s
|
||||
puts " #{tab}" unless tab.empty?
|
||||
end
|
||||
end
|
||||
|
||||
puts "From: #{Formatter.url(github_info(formula))}"
|
||||
|
||||
puts "License: #{SPDX.license_expression_to_string formula.license}" if formula.license.present?
|
||||
|
||||
unless formula.deps.empty?
|
||||
ohai "Dependencies"
|
||||
%w[build required recommended optional].map do |type|
|
||||
deps = formula.deps.send(type).uniq
|
||||
puts "#{type.capitalize}: #{decorate_dependencies deps}" unless deps.empty?
|
||||
end
|
||||
end
|
||||
|
||||
unless formula.requirements.to_a.empty?
|
||||
ohai "Requirements"
|
||||
%w[build required recommended optional].map do |type|
|
||||
reqs = formula.requirements.select(&:"#{type}?")
|
||||
next if reqs.to_a.empty?
|
||||
|
||||
puts "#{type.capitalize}: #{decorate_requirements(reqs)}"
|
||||
end
|
||||
end
|
||||
|
||||
if !formula.options.empty? || formula.head
|
||||
ohai "Options"
|
||||
Options.dump_for_formula formula
|
||||
end
|
||||
|
||||
caveats = Caveats.new(formula)
|
||||
ohai "Caveats", caveats.to_s unless caveats.empty?
|
||||
|
||||
Utils::Analytics.formula_output(formula, args:)
|
||||
end
|
||||
|
||||
if args.variations?
|
||||
{
|
||||
"formulae" => formulae.map(&:to_hash_with_variations),
|
||||
"casks" => casks.map(&:to_hash_with_variations),
|
||||
}
|
||||
else
|
||||
{
|
||||
"formulae" => formulae.map(&:to_hash),
|
||||
"casks" => casks.map(&:to_h),
|
||||
}
|
||||
end
|
||||
else
|
||||
raise
|
||||
end
|
||||
|
||||
puts JSON.pretty_generate(json)
|
||||
end
|
||||
|
||||
def github_remote_path(remote, path)
|
||||
if remote =~ %r{^(?:https?://|git(?:@|://))github\.com[:/](.+)/(.+?)(?:\.git)?$}
|
||||
"https://github.com/#{Regexp.last_match(1)}/#{Regexp.last_match(2)}/blob/HEAD/#{path}"
|
||||
else
|
||||
"#{remote}/#{path}"
|
||||
end
|
||||
end
|
||||
|
||||
def github_info(formula_or_cask)
|
||||
return formula_or_cask.path if formula_or_cask.tap.blank? || formula_or_cask.tap.remote.blank?
|
||||
|
||||
path = case formula_or_cask
|
||||
when Formula
|
||||
formula = formula_or_cask
|
||||
formula.path.relative_path_from(T.must(formula.tap).path)
|
||||
when Cask::Cask
|
||||
cask = formula_or_cask
|
||||
if cask.sourcefile_path.blank?
|
||||
return "#{cask.tap.default_remote}/blob/HEAD/#{cask.tap.relative_cask_path(cask.token)}"
|
||||
def decorate_dependencies(dependencies)
|
||||
deps_status = dependencies.map do |dep|
|
||||
if dep.satisfied?([])
|
||||
pretty_installed(dep_display_s(dep))
|
||||
else
|
||||
pretty_uninstalled(dep_display_s(dep))
|
||||
end
|
||||
end
|
||||
deps_status.join(", ")
|
||||
end
|
||||
|
||||
cask.sourcefile_path.relative_path_from(cask.tap.path)
|
||||
end
|
||||
def decorate_requirements(requirements)
|
||||
req_status = requirements.map do |req|
|
||||
req_s = req.display_s
|
||||
req.satisfied? ? pretty_installed(req_s) : pretty_uninstalled(req_s)
|
||||
end
|
||||
req_status.join(", ")
|
||||
end
|
||||
|
||||
github_remote_path(formula_or_cask.tap.remote, path)
|
||||
end
|
||||
def dep_display_s(dep)
|
||||
return dep.name if dep.option_tags.empty?
|
||||
|
||||
def info_formula(formula, args:)
|
||||
specs = []
|
||||
"#{dep.name} #{dep.option_tags.map { |o| "--#{o}" }.join(" ")}"
|
||||
end
|
||||
|
||||
if (stable = formula.stable)
|
||||
string = "stable #{stable.version}"
|
||||
string += " (bottled)" if stable.bottled? && formula.pour_bottle?
|
||||
specs << string
|
||||
end
|
||||
def info_cask(cask)
|
||||
require "cask/info"
|
||||
|
||||
specs << "HEAD" if formula.head
|
||||
|
||||
attrs = []
|
||||
attrs << "pinned at #{formula.pinned_version}" if formula.pinned?
|
||||
attrs << "keg-only" if formula.keg_only?
|
||||
|
||||
puts "#{oh1_title(formula.full_name)}: #{specs * ", "}#{" [#{attrs * ", "}]" unless attrs.empty?}"
|
||||
puts formula.desc if formula.desc
|
||||
puts Formatter.url(formula.homepage) if formula.homepage
|
||||
|
||||
deprecate_disable_info_string = DeprecateDisable.message(formula)
|
||||
puts deprecate_disable_info_string.capitalize if deprecate_disable_info_string.present?
|
||||
|
||||
conflicts = formula.conflicts.map do |conflict|
|
||||
reason = " (because #{conflict.reason})" if conflict.reason
|
||||
"#{conflict.name}#{reason}"
|
||||
end.sort!
|
||||
unless conflicts.empty?
|
||||
puts <<~EOS
|
||||
Conflicts with:
|
||||
#{conflicts.join("\n ")}
|
||||
EOS
|
||||
end
|
||||
|
||||
kegs = formula.installed_kegs
|
||||
heads, versioned = kegs.partition { |k| k.version.head? }
|
||||
kegs = [
|
||||
*heads.sort_by { |k| -Tab.for_keg(k).time.to_i },
|
||||
*Keg.sort(versioned),
|
||||
]
|
||||
if kegs.empty?
|
||||
puts "Not installed"
|
||||
else
|
||||
kegs.each do |keg|
|
||||
puts "#{keg} (#{keg.abv})#{" *" if keg.linked?}"
|
||||
tab = Tab.for_keg(keg).to_s
|
||||
puts " #{tab}" unless tab.empty?
|
||||
Cask::Info.info(cask)
|
||||
end
|
||||
end
|
||||
|
||||
puts "From: #{Formatter.url(github_info(formula))}"
|
||||
|
||||
puts "License: #{SPDX.license_expression_to_string formula.license}" if formula.license.present?
|
||||
|
||||
unless formula.deps.empty?
|
||||
ohai "Dependencies"
|
||||
%w[build required recommended optional].map do |type|
|
||||
deps = formula.deps.send(type).uniq
|
||||
puts "#{type.capitalize}: #{decorate_dependencies deps}" unless deps.empty?
|
||||
end
|
||||
end
|
||||
|
||||
unless formula.requirements.to_a.empty?
|
||||
ohai "Requirements"
|
||||
%w[build required recommended optional].map do |type|
|
||||
reqs = formula.requirements.select(&:"#{type}?")
|
||||
next if reqs.to_a.empty?
|
||||
|
||||
puts "#{type.capitalize}: #{decorate_requirements(reqs)}"
|
||||
end
|
||||
end
|
||||
|
||||
if !formula.options.empty? || formula.head
|
||||
ohai "Options"
|
||||
Options.dump_for_formula formula
|
||||
end
|
||||
|
||||
caveats = Caveats.new(formula)
|
||||
ohai "Caveats", caveats.to_s unless caveats.empty?
|
||||
|
||||
Utils::Analytics.formula_output(formula, args:)
|
||||
end
|
||||
|
||||
def decorate_dependencies(dependencies)
|
||||
deps_status = dependencies.map do |dep|
|
||||
if dep.satisfied?([])
|
||||
pretty_installed(dep_display_s(dep))
|
||||
else
|
||||
pretty_uninstalled(dep_display_s(dep))
|
||||
end
|
||||
end
|
||||
deps_status.join(", ")
|
||||
end
|
||||
|
||||
def decorate_requirements(requirements)
|
||||
req_status = requirements.map do |req|
|
||||
req_s = req.display_s
|
||||
req.satisfied? ? pretty_installed(req_s) : pretty_uninstalled(req_s)
|
||||
end
|
||||
req_status.join(", ")
|
||||
end
|
||||
|
||||
def dep_display_s(dep)
|
||||
return dep.name if dep.option_tags.empty?
|
||||
|
||||
"#{dep.name} #{dep.option_tags.map { |o| "--#{o}" }.join(" ")}"
|
||||
end
|
||||
|
||||
def info_cask(cask, args:)
|
||||
require "cask/info"
|
||||
|
||||
Cask::Info.info(cask)
|
||||
end
|
||||
end
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
# typed: true
|
||||
# typed: strict
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "abstract_command"
|
||||
require "cask/config"
|
||||
require "cask/installer"
|
||||
require "cask_dependent"
|
||||
@ -9,400 +10,402 @@ require "formula_installer"
|
||||
require "development_tools"
|
||||
require "install"
|
||||
require "cleanup"
|
||||
require "cli/parser"
|
||||
require "upgrade"
|
||||
|
||||
module Homebrew
|
||||
sig { returns(CLI::Parser) }
|
||||
def self.install_args
|
||||
Homebrew::CLI::Parser.new do
|
||||
description <<~EOS
|
||||
Install a <formula> or <cask>. Additional options specific to a <formula> may be
|
||||
appended to the command.
|
||||
module Cmd
|
||||
class InstallCmd < AbstractCommand
|
||||
cmd_args do
|
||||
description <<~EOS
|
||||
Install a <formula> or <cask>. Additional options specific to a <formula> may be
|
||||
appended to the command.
|
||||
|
||||
Unless `HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK` is set, `brew upgrade` or `brew reinstall` will be run for
|
||||
outdated dependents and dependents with broken linkage, respectively.
|
||||
Unless `HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK` is set, `brew upgrade` or `brew reinstall` will be run for
|
||||
outdated dependents and dependents with broken linkage, respectively.
|
||||
|
||||
Unless `HOMEBREW_NO_INSTALL_CLEANUP` is set, `brew cleanup` will then be run for
|
||||
the installed formulae or, every 30 days, for all formulae.
|
||||
Unless `HOMEBREW_NO_INSTALL_CLEANUP` is set, `brew cleanup` will then be run for
|
||||
the installed formulae or, every 30 days, for all formulae.
|
||||
|
||||
Unless `HOMEBREW_NO_INSTALL_UPGRADE` is set, `brew install` <formula> will upgrade <formula> if it
|
||||
is already installed but outdated.
|
||||
EOS
|
||||
switch "-d", "--debug",
|
||||
description: "If brewing fails, open an interactive debugging session with access to IRB " \
|
||||
"or a shell inside the temporary build directory."
|
||||
switch "-f", "--force",
|
||||
description: "Install formulae without checking for previously installed keg-only or " \
|
||||
"non-migrated versions. When installing casks, overwrite existing files " \
|
||||
"(binaries and symlinks are excluded, unless originally from the same cask)."
|
||||
switch "-v", "--verbose",
|
||||
description: "Print the verification and post-install steps."
|
||||
switch "-n", "--dry-run",
|
||||
description: "Show what would be installed, but do not actually install anything."
|
||||
[
|
||||
[:switch, "--formula", "--formulae", {
|
||||
description: "Treat all named arguments as formulae.",
|
||||
}],
|
||||
[:flag, "--env=", {
|
||||
description: "Disabled other than for internal Homebrew use.",
|
||||
hidden: true,
|
||||
}],
|
||||
[:switch, "--ignore-dependencies", {
|
||||
description: "An unsupported Homebrew development option to skip installing any dependencies of any " \
|
||||
"kind. If the dependencies are not already present, the formula will have issues. If you're " \
|
||||
"not developing Homebrew, consider adjusting your PATH rather than using this option.",
|
||||
}],
|
||||
[:switch, "--only-dependencies", {
|
||||
description: "Install the dependencies with specified options but do not install the " \
|
||||
"formula itself.",
|
||||
}],
|
||||
[:flag, "--cc=", {
|
||||
description: "Attempt to compile using the specified <compiler>, which should be the name of the " \
|
||||
"compiler's executable, e.g. `gcc-7` for GCC 7. In order to use LLVM's clang, specify " \
|
||||
"`llvm_clang`. To use the Apple-provided clang, specify `clang`. This option will only " \
|
||||
"accept compilers that are provided by Homebrew or bundled with macOS. Please do not " \
|
||||
"file issues if you encounter errors while using this option.",
|
||||
}],
|
||||
[:switch, "-s", "--build-from-source", {
|
||||
description: "Compile <formula> from source even if a bottle is provided. " \
|
||||
"Dependencies will still be installed from bottles if they are available.",
|
||||
}],
|
||||
[:switch, "--force-bottle", {
|
||||
description: "Install from a bottle if it exists for the current or newest version of " \
|
||||
"macOS, even if it would not normally be used for installation.",
|
||||
}],
|
||||
[:switch, "--include-test", {
|
||||
description: "Install testing dependencies required to run `brew test` <formula>.",
|
||||
}],
|
||||
[:switch, "--HEAD", {
|
||||
description: "If <formula> defines it, install the HEAD version, aka. main, trunk, unstable, master.",
|
||||
}],
|
||||
[:switch, "--fetch-HEAD", {
|
||||
description: "Fetch the upstream repository to detect if the HEAD installation of the " \
|
||||
"formula is outdated. Otherwise, the repository's HEAD will only be checked for " \
|
||||
"updates when a new stable or development version has been released.",
|
||||
}],
|
||||
[:switch, "--keep-tmp", {
|
||||
description: "Retain the temporary files created during installation.",
|
||||
}],
|
||||
[:switch, "--debug-symbols", {
|
||||
depends_on: "--build-from-source",
|
||||
description: "Generate debug symbols on build. Source will be retained in a cache directory.",
|
||||
}],
|
||||
[:switch, "--build-bottle", {
|
||||
description: "Prepare the formula for eventual bottling during installation, skipping any " \
|
||||
"post-install steps.",
|
||||
}],
|
||||
[:switch, "--skip-post-install", {
|
||||
description: "Install but skip any post-install steps.",
|
||||
}],
|
||||
[:flag, "--bottle-arch=", {
|
||||
depends_on: "--build-bottle",
|
||||
description: "Optimise bottles for the specified architecture rather than the oldest " \
|
||||
"architecture supported by the version of macOS the bottles are built on.",
|
||||
}],
|
||||
[:switch, "--display-times", {
|
||||
env: :display_install_times,
|
||||
description: "Print install times for each package at the end of the run.",
|
||||
}],
|
||||
[:switch, "-i", "--interactive", {
|
||||
description: "Download and patch <formula>, then open a shell. This allows the user to " \
|
||||
"run `./configure --help` and otherwise determine how to turn the software " \
|
||||
"package into a Homebrew package.",
|
||||
}],
|
||||
[:switch, "-g", "--git", {
|
||||
description: "Create a Git repository, useful for creating patches to the software.",
|
||||
}],
|
||||
[:switch, "--overwrite", {
|
||||
description: "Delete files that already exist in the prefix while linking.",
|
||||
}],
|
||||
].each do |args|
|
||||
options = args.pop
|
||||
send(*args, **options)
|
||||
conflicts "--cask", args.last
|
||||
end
|
||||
formula_options
|
||||
[
|
||||
[:switch, "--cask", "--casks", { description: "Treat all named arguments as casks." }],
|
||||
[:switch, "--[no-]binaries", {
|
||||
description: "Disable/enable linking of helper executables (default: enabled).",
|
||||
env: :cask_opts_binaries,
|
||||
}],
|
||||
[:switch, "--require-sha", {
|
||||
description: "Require all casks to have a checksum.",
|
||||
env: :cask_opts_require_sha,
|
||||
}],
|
||||
[:switch, "--[no-]quarantine", {
|
||||
description: "Disable/enable quarantining of downloads (default: enabled).",
|
||||
env: :cask_opts_quarantine,
|
||||
}],
|
||||
[:switch, "--adopt", {
|
||||
description: "Adopt existing artifacts in the destination that are identical to those being installed. " \
|
||||
"Cannot be combined with `--force`.",
|
||||
}],
|
||||
[:switch, "--skip-cask-deps", {
|
||||
description: "Skip installing cask dependencies.",
|
||||
}],
|
||||
[:switch, "--zap", {
|
||||
description: "For use with `brew reinstall --cask`. Remove all files associated with a cask. " \
|
||||
"*May remove files which are shared between applications.*",
|
||||
}],
|
||||
].each do |args|
|
||||
options = args.pop
|
||||
send(*args, **options)
|
||||
conflicts "--formula", args.last
|
||||
end
|
||||
cask_options
|
||||
|
||||
conflicts "--ignore-dependencies", "--only-dependencies"
|
||||
conflicts "--build-from-source", "--build-bottle", "--force-bottle"
|
||||
conflicts "--adopt", "--force"
|
||||
|
||||
named_args [:formula, :cask], min: 1
|
||||
end
|
||||
end
|
||||
|
||||
def self.install
|
||||
args = install_args.parse
|
||||
|
||||
if args.env.present?
|
||||
# Can't use `replacement: false` because `install_args` are used by
|
||||
# `build.rb`. Instead, `hide_from_man_page` and don't do anything with
|
||||
# this argument here.
|
||||
# This odisabled should stick around indefinitely.
|
||||
odisabled "brew install --env", "`env :std` in specific formula files"
|
||||
end
|
||||
|
||||
args.named.each do |name|
|
||||
if (tap_with_name = Tap.with_formula_name(name))
|
||||
tap, = tap_with_name
|
||||
elsif (tap_with_token = Tap.with_cask_token(name))
|
||||
tap, = tap_with_token
|
||||
end
|
||||
|
||||
tap&.ensure_installed!
|
||||
end
|
||||
|
||||
if args.ignore_dependencies?
|
||||
opoo <<~EOS
|
||||
#{Tty.bold}`--ignore-dependencies` is an unsupported Homebrew developer option!#{Tty.reset}
|
||||
Adjust your PATH to put any preferred versions of applications earlier in the
|
||||
PATH rather than using this unsupported option!
|
||||
|
||||
EOS
|
||||
end
|
||||
|
||||
begin
|
||||
formulae, casks = args.named.to_formulae_and_casks(warn: false)
|
||||
.partition { |formula_or_cask| formula_or_cask.is_a?(Formula) }
|
||||
rescue FormulaOrCaskUnavailableError, Cask::CaskUnavailableError
|
||||
cask_tap = CoreCaskTap.instance
|
||||
if !cask_tap.installed? && (args.cask? || Tap.untapped_official_taps.exclude?(cask_tap.name))
|
||||
cask_tap.ensure_installed!
|
||||
retry if cask_tap.installed?
|
||||
end
|
||||
|
||||
raise
|
||||
end
|
||||
|
||||
if casks.any?
|
||||
|
||||
if args.dry_run?
|
||||
if (casks_to_install = casks.reject(&:installed?).presence)
|
||||
ohai "Would install #{::Utils.pluralize("cask", casks_to_install.count, include_count: true)}:"
|
||||
puts casks_to_install.map(&:full_name).join(" ")
|
||||
Unless `HOMEBREW_NO_INSTALL_UPGRADE` is set, `brew install` <formula> will upgrade <formula> if it
|
||||
is already installed but outdated.
|
||||
EOS
|
||||
switch "-d", "--debug",
|
||||
description: "If brewing fails, open an interactive debugging session with access to IRB " \
|
||||
"or a shell inside the temporary build directory."
|
||||
switch "-f", "--force",
|
||||
description: "Install formulae without checking for previously installed keg-only or " \
|
||||
"non-migrated versions. When installing casks, overwrite existing files " \
|
||||
"(binaries and symlinks are excluded, unless originally from the same cask)."
|
||||
switch "-v", "--verbose",
|
||||
description: "Print the verification and post-install steps."
|
||||
switch "-n", "--dry-run",
|
||||
description: "Show what would be installed, but do not actually install anything."
|
||||
[
|
||||
[:switch, "--formula", "--formulae", {
|
||||
description: "Treat all named arguments as formulae.",
|
||||
}],
|
||||
[:flag, "--env=", {
|
||||
description: "Disabled other than for internal Homebrew use.",
|
||||
hidden: true,
|
||||
}],
|
||||
[:switch, "--ignore-dependencies", {
|
||||
description: "An unsupported Homebrew development option to skip installing any dependencies of any " \
|
||||
"kind. If the dependencies are not already present, the formula will have issues. If " \
|
||||
"you're not developing Homebrew, consider adjusting your PATH rather than using this " \
|
||||
"option.",
|
||||
}],
|
||||
[:switch, "--only-dependencies", {
|
||||
description: "Install the dependencies with specified options but do not install the " \
|
||||
"formula itself.",
|
||||
}],
|
||||
[:flag, "--cc=", {
|
||||
description: "Attempt to compile using the specified <compiler>, which should be the name of the " \
|
||||
"compiler's executable, e.g. `gcc-7` for GCC 7. In order to use LLVM's clang, specify " \
|
||||
"`llvm_clang`. To use the Apple-provided clang, specify `clang`. This option will only " \
|
||||
"accept compilers that are provided by Homebrew or bundled with macOS. Please do not " \
|
||||
"file issues if you encounter errors while using this option.",
|
||||
}],
|
||||
[:switch, "-s", "--build-from-source", {
|
||||
description: "Compile <formula> from source even if a bottle is provided. " \
|
||||
"Dependencies will still be installed from bottles if they are available.",
|
||||
}],
|
||||
[:switch, "--force-bottle", {
|
||||
description: "Install from a bottle if it exists for the current or newest version of " \
|
||||
"macOS, even if it would not normally be used for installation.",
|
||||
}],
|
||||
[:switch, "--include-test", {
|
||||
description: "Install testing dependencies required to run `brew test` <formula>.",
|
||||
}],
|
||||
[:switch, "--HEAD", {
|
||||
description: "If <formula> defines it, install the HEAD version, aka. main, trunk, unstable, master.",
|
||||
}],
|
||||
[:switch, "--fetch-HEAD", {
|
||||
description: "Fetch the upstream repository to detect if the HEAD installation of the " \
|
||||
"formula is outdated. Otherwise, the repository's HEAD will only be checked for " \
|
||||
"updates when a new stable or development version has been released.",
|
||||
}],
|
||||
[:switch, "--keep-tmp", {
|
||||
description: "Retain the temporary files created during installation.",
|
||||
}],
|
||||
[:switch, "--debug-symbols", {
|
||||
depends_on: "--build-from-source",
|
||||
description: "Generate debug symbols on build. Source will be retained in a cache directory.",
|
||||
}],
|
||||
[:switch, "--build-bottle", {
|
||||
description: "Prepare the formula for eventual bottling during installation, skipping any " \
|
||||
"post-install steps.",
|
||||
}],
|
||||
[:switch, "--skip-post-install", {
|
||||
description: "Install but skip any post-install steps.",
|
||||
}],
|
||||
[:flag, "--bottle-arch=", {
|
||||
depends_on: "--build-bottle",
|
||||
description: "Optimise bottles for the specified architecture rather than the oldest " \
|
||||
"architecture supported by the version of macOS the bottles are built on.",
|
||||
}],
|
||||
[:switch, "--display-times", {
|
||||
env: :display_install_times,
|
||||
description: "Print install times for each package at the end of the run.",
|
||||
}],
|
||||
[:switch, "-i", "--interactive", {
|
||||
description: "Download and patch <formula>, then open a shell. This allows the user to " \
|
||||
"run `./configure --help` and otherwise determine how to turn the software " \
|
||||
"package into a Homebrew package.",
|
||||
}],
|
||||
[:switch, "-g", "--git", {
|
||||
description: "Create a Git repository, useful for creating patches to the software.",
|
||||
}],
|
||||
[:switch, "--overwrite", {
|
||||
description: "Delete files that already exist in the prefix while linking.",
|
||||
}],
|
||||
].each do |args|
|
||||
options = args.pop
|
||||
send(*args, **options)
|
||||
conflicts "--cask", args.last
|
||||
end
|
||||
casks.each do |cask|
|
||||
dep_names = CaskDependent.new(cask)
|
||||
.runtime_dependencies
|
||||
.reject(&:installed?)
|
||||
.map(&:to_formula)
|
||||
.map(&:name)
|
||||
next if dep_names.blank?
|
||||
formula_options
|
||||
[
|
||||
[:switch, "--cask", "--casks", { description: "Treat all named arguments as casks." }],
|
||||
[:switch, "--[no-]binaries", {
|
||||
description: "Disable/enable linking of helper executables (default: enabled).",
|
||||
env: :cask_opts_binaries,
|
||||
}],
|
||||
[:switch, "--require-sha", {
|
||||
description: "Require all casks to have a checksum.",
|
||||
env: :cask_opts_require_sha,
|
||||
}],
|
||||
[:switch, "--[no-]quarantine", {
|
||||
description: "Disable/enable quarantining of downloads (default: enabled).",
|
||||
env: :cask_opts_quarantine,
|
||||
}],
|
||||
[:switch, "--adopt", {
|
||||
description: "Adopt existing artifacts in the destination that are identical to those being installed. " \
|
||||
"Cannot be combined with `--force`.",
|
||||
}],
|
||||
[:switch, "--skip-cask-deps", {
|
||||
description: "Skip installing cask dependencies.",
|
||||
}],
|
||||
[:switch, "--zap", {
|
||||
description: "For use with `brew reinstall --cask`. Remove all files associated with a cask. " \
|
||||
"*May remove files which are shared between applications.*",
|
||||
}],
|
||||
].each do |args|
|
||||
options = args.pop
|
||||
send(*args, **options)
|
||||
conflicts "--formula", args.last
|
||||
end
|
||||
cask_options
|
||||
|
||||
ohai "Would install #{::Utils.pluralize("dependenc", dep_names.count, plural: "ies", singular: "y",
|
||||
conflicts "--ignore-dependencies", "--only-dependencies"
|
||||
conflicts "--build-from-source", "--build-bottle", "--force-bottle"
|
||||
conflicts "--adopt", "--force"
|
||||
|
||||
named_args [:formula, :cask], min: 1
|
||||
end
|
||||
|
||||
sig { override.void }
|
||||
def run
|
||||
if args.env.present?
|
||||
# Can't use `replacement: false` because `install_args` are used by
|
||||
# `build.rb`. Instead, `hide_from_man_page` and don't do anything with
|
||||
# this argument here.
|
||||
# This odisabled should stick around indefinitely.
|
||||
odisabled "brew install --env", "`env :std` in specific formula files"
|
||||
end
|
||||
|
||||
args.named.each do |name|
|
||||
if (tap_with_name = Tap.with_formula_name(name))
|
||||
tap, = tap_with_name
|
||||
elsif (tap_with_token = Tap.with_cask_token(name))
|
||||
tap, = tap_with_token
|
||||
end
|
||||
|
||||
tap&.ensure_installed!
|
||||
end
|
||||
|
||||
if args.ignore_dependencies?
|
||||
opoo <<~EOS
|
||||
#{Tty.bold}`--ignore-dependencies` is an unsupported Homebrew developer option!#{Tty.reset}
|
||||
Adjust your PATH to put any preferred versions of applications earlier in the
|
||||
PATH rather than using this unsupported option!
|
||||
|
||||
EOS
|
||||
end
|
||||
|
||||
begin
|
||||
formulae, casks = T.cast(
|
||||
args.named.to_formulae_and_casks(warn: false).partition { _1.is_a?(Formula) },
|
||||
[T::Array[Formula], T::Array[Cask::Cask]],
|
||||
)
|
||||
rescue FormulaOrCaskUnavailableError, Cask::CaskUnavailableError
|
||||
cask_tap = CoreCaskTap.instance
|
||||
if !cask_tap.installed? && (args.cask? || Tap.untapped_official_taps.exclude?(cask_tap.name))
|
||||
cask_tap.ensure_installed!
|
||||
retry if cask_tap.installed?
|
||||
end
|
||||
|
||||
raise
|
||||
end
|
||||
|
||||
if casks.any?
|
||||
|
||||
if args.dry_run?
|
||||
if (casks_to_install = casks.reject(&:installed?).presence)
|
||||
ohai "Would install #{::Utils.pluralize("cask", casks_to_install.count, include_count: true)}:"
|
||||
puts casks_to_install.map(&:full_name).join(" ")
|
||||
end
|
||||
casks.each do |cask|
|
||||
dep_names = CaskDependent.new(cask)
|
||||
.runtime_dependencies
|
||||
.reject(&:installed?)
|
||||
.map(&:to_formula)
|
||||
.map(&:name)
|
||||
next if dep_names.blank?
|
||||
|
||||
ohai "Would install #{::Utils.pluralize("dependenc", dep_names.count, plural: "ies", singular: "y",
|
||||
include_count: true)} for #{cask.full_name}:"
|
||||
puts dep_names.join(" ")
|
||||
puts dep_names.join(" ")
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
require "cask/installer"
|
||||
|
||||
installed_casks, new_casks = casks.partition(&:installed?)
|
||||
|
||||
new_casks.each do |cask|
|
||||
Cask::Installer.new(
|
||||
cask,
|
||||
binaries: args.binaries?,
|
||||
verbose: args.verbose?,
|
||||
force: args.force?,
|
||||
adopt: args.adopt?,
|
||||
require_sha: args.require_sha?,
|
||||
skip_cask_deps: args.skip_cask_deps?,
|
||||
quarantine: args.quarantine?,
|
||||
quiet: args.quiet?,
|
||||
).install
|
||||
end
|
||||
|
||||
if !Homebrew::EnvConfig.no_install_upgrade? && installed_casks.any?
|
||||
require "cask/upgrade"
|
||||
|
||||
Cask::Upgrade.upgrade_casks(
|
||||
*installed_casks,
|
||||
force: args.force?,
|
||||
dry_run: args.dry_run?,
|
||||
binaries: args.binaries?,
|
||||
quarantine: args.quarantine?,
|
||||
require_sha: args.require_sha?,
|
||||
skip_cask_deps: args.skip_cask_deps?,
|
||||
verbose: args.verbose?,
|
||||
args:,
|
||||
)
|
||||
end
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
require "cask/installer"
|
||||
# if the user's flags will prevent bottle only-installations when no
|
||||
# developer tools are available, we need to stop them early on
|
||||
build_flags = []
|
||||
unless DevelopmentTools.installed?
|
||||
build_flags << "--HEAD" if args.HEAD?
|
||||
build_flags << "--build-bottle" if args.build_bottle?
|
||||
build_flags << "--build-from-source" if args.build_from_source?
|
||||
|
||||
installed_casks, new_casks = casks.partition(&:installed?)
|
||||
raise BuildFlagsError.new(build_flags, bottled: formulae.all?(&:bottled?)) if build_flags.present?
|
||||
end
|
||||
|
||||
new_casks.each do |cask|
|
||||
Cask::Installer.new(
|
||||
cask,
|
||||
binaries: args.binaries?,
|
||||
verbose: args.verbose?,
|
||||
force: args.force?,
|
||||
adopt: args.adopt?,
|
||||
require_sha: args.require_sha?,
|
||||
skip_cask_deps: args.skip_cask_deps?,
|
||||
quarantine: args.quarantine?,
|
||||
quiet: args.quiet?,
|
||||
).install
|
||||
end
|
||||
if build_flags.present? && !Homebrew::EnvConfig.developer?
|
||||
opoo "building from source is not supported!"
|
||||
puts "You're on your own. Failures are expected so don't create any issues, please!"
|
||||
end
|
||||
|
||||
if !Homebrew::EnvConfig.no_install_upgrade? && installed_casks.any?
|
||||
require "cask/upgrade"
|
||||
installed_formulae = formulae.select do |f|
|
||||
Install.install_formula?(
|
||||
f,
|
||||
head: args.HEAD?,
|
||||
fetch_head: args.fetch_HEAD?,
|
||||
only_dependencies: args.only_dependencies?,
|
||||
force: args.force?,
|
||||
quiet: args.quiet?,
|
||||
)
|
||||
end
|
||||
|
||||
Cask::Upgrade.upgrade_casks(
|
||||
*installed_casks,
|
||||
force: args.force?,
|
||||
dry_run: args.dry_run?,
|
||||
binaries: args.binaries?,
|
||||
quarantine: args.quarantine?,
|
||||
require_sha: args.require_sha?,
|
||||
skip_cask_deps: args.skip_cask_deps?,
|
||||
verbose: args.verbose?,
|
||||
args:,
|
||||
return if installed_formulae.empty?
|
||||
|
||||
Install.perform_preinstall_checks(cc: args.cc)
|
||||
|
||||
Install.install_formulae(
|
||||
installed_formulae,
|
||||
build_bottle: args.build_bottle?,
|
||||
force_bottle: args.force_bottle?,
|
||||
bottle_arch: args.bottle_arch,
|
||||
ignore_deps: args.ignore_dependencies?,
|
||||
only_deps: args.only_dependencies?,
|
||||
include_test_formulae: args.include_test_formulae,
|
||||
build_from_source_formulae: args.build_from_source_formulae,
|
||||
cc: args.cc,
|
||||
git: args.git?,
|
||||
interactive: args.interactive?,
|
||||
keep_tmp: args.keep_tmp?,
|
||||
debug_symbols: args.debug_symbols?,
|
||||
force: args.force?,
|
||||
overwrite: args.overwrite?,
|
||||
debug: args.debug?,
|
||||
quiet: args.quiet?,
|
||||
verbose: args.verbose?,
|
||||
dry_run: args.dry_run?,
|
||||
skip_post_install: args.skip_post_install?,
|
||||
)
|
||||
|
||||
Upgrade.check_installed_dependents(
|
||||
installed_formulae,
|
||||
flags: args.flags_only,
|
||||
installed_on_request: args.named.present?,
|
||||
force_bottle: args.force_bottle?,
|
||||
build_from_source_formulae: args.build_from_source_formulae,
|
||||
interactive: args.interactive?,
|
||||
keep_tmp: args.keep_tmp?,
|
||||
debug_symbols: args.debug_symbols?,
|
||||
force: args.force?,
|
||||
debug: args.debug?,
|
||||
quiet: args.quiet?,
|
||||
verbose: args.verbose?,
|
||||
dry_run: args.dry_run?,
|
||||
)
|
||||
|
||||
Cleanup.periodic_clean!(dry_run: args.dry_run?)
|
||||
|
||||
Homebrew.messages.display_messages(display_times: args.display_times?)
|
||||
rescue FormulaUnreadableError, FormulaClassUnavailableError,
|
||||
TapFormulaUnreadableError, TapFormulaClassUnavailableError => e
|
||||
# Need to rescue before `FormulaUnavailableError` (superclass of this)
|
||||
# is handled, as searching for a formula doesn't make sense here (the
|
||||
# formula was found, but there's a problem with its implementation).
|
||||
$stderr.puts Utils::Backtrace.clean(e) if Homebrew::EnvConfig.developer?
|
||||
ofail e.message
|
||||
rescue FormulaOrCaskUnavailableError, Cask::CaskUnavailableError => e
|
||||
Homebrew.failed = true
|
||||
|
||||
# formula name or cask token
|
||||
name = case e
|
||||
when FormulaOrCaskUnavailableError then e.name
|
||||
when Cask::CaskUnavailableError then e.token
|
||||
else T.absurd(e)
|
||||
end
|
||||
|
||||
if name == "updog"
|
||||
ofail "What's updog?"
|
||||
return
|
||||
end
|
||||
|
||||
opoo e
|
||||
|
||||
reason = MissingFormula.reason(name, silent: true)
|
||||
if !args.cask? && reason
|
||||
$stderr.puts reason
|
||||
return
|
||||
end
|
||||
|
||||
# We don't seem to get good search results when the tap is specified
|
||||
# so we might as well return early.
|
||||
return if name.include?("/")
|
||||
|
||||
require "search"
|
||||
|
||||
package_types = []
|
||||
package_types << "formulae" unless args.cask?
|
||||
package_types << "casks" unless args.formula?
|
||||
|
||||
ohai "Searching for similarly named #{package_types.join(" and ")}..."
|
||||
|
||||
# Don't treat formula/cask name as a regex
|
||||
string_or_regex = name
|
||||
all_formulae, all_casks = Search.search_names(string_or_regex, args)
|
||||
|
||||
if all_formulae.any?
|
||||
ohai "Formulae", Formatter.columns(all_formulae)
|
||||
first_formula = all_formulae.first.to_s
|
||||
puts <<~EOS
|
||||
|
||||
To install #{first_formula}, run:
|
||||
brew install #{first_formula}
|
||||
EOS
|
||||
end
|
||||
puts if all_formulae.any? && all_casks.any?
|
||||
if all_casks.any?
|
||||
ohai "Casks", Formatter.columns(all_casks)
|
||||
first_cask = all_casks.first.to_s
|
||||
puts <<~EOS
|
||||
|
||||
To install #{first_cask}, run:
|
||||
brew install --cask #{first_cask}
|
||||
EOS
|
||||
end
|
||||
return if all_formulae.any? || all_casks.any?
|
||||
|
||||
odie "No #{package_types.join(" or ")} found for #{name}."
|
||||
end
|
||||
end
|
||||
|
||||
# if the user's flags will prevent bottle only-installations when no
|
||||
# developer tools are available, we need to stop them early on
|
||||
build_flags = []
|
||||
unless DevelopmentTools.installed?
|
||||
build_flags << "--HEAD" if args.HEAD?
|
||||
build_flags << "--build-bottle" if args.build_bottle?
|
||||
build_flags << "--build-from-source" if args.build_from_source?
|
||||
|
||||
raise BuildFlagsError.new(build_flags, bottled: formulae.all?(&:bottled?)) if build_flags.present?
|
||||
end
|
||||
|
||||
if build_flags.present? && !Homebrew::EnvConfig.developer?
|
||||
opoo "building from source is not supported!"
|
||||
puts "You're on your own. Failures are expected so don't create any issues, please!"
|
||||
end
|
||||
|
||||
installed_formulae = formulae.select do |f|
|
||||
Install.install_formula?(
|
||||
f,
|
||||
head: args.HEAD?,
|
||||
fetch_head: args.fetch_HEAD?,
|
||||
only_dependencies: args.only_dependencies?,
|
||||
force: args.force?,
|
||||
quiet: args.quiet?,
|
||||
)
|
||||
end
|
||||
|
||||
return if installed_formulae.empty?
|
||||
|
||||
Install.perform_preinstall_checks(cc: args.cc)
|
||||
|
||||
Install.install_formulae(
|
||||
installed_formulae,
|
||||
build_bottle: args.build_bottle?,
|
||||
force_bottle: args.force_bottle?,
|
||||
bottle_arch: args.bottle_arch,
|
||||
ignore_deps: args.ignore_dependencies?,
|
||||
only_deps: args.only_dependencies?,
|
||||
include_test_formulae: args.include_test_formulae,
|
||||
build_from_source_formulae: args.build_from_source_formulae,
|
||||
cc: args.cc,
|
||||
git: args.git?,
|
||||
interactive: args.interactive?,
|
||||
keep_tmp: args.keep_tmp?,
|
||||
debug_symbols: args.debug_symbols?,
|
||||
force: args.force?,
|
||||
overwrite: args.overwrite?,
|
||||
debug: args.debug?,
|
||||
quiet: args.quiet?,
|
||||
verbose: args.verbose?,
|
||||
dry_run: args.dry_run?,
|
||||
skip_post_install: args.skip_post_install?,
|
||||
)
|
||||
|
||||
Upgrade.check_installed_dependents(
|
||||
installed_formulae,
|
||||
flags: args.flags_only,
|
||||
installed_on_request: args.named.present?,
|
||||
force_bottle: args.force_bottle?,
|
||||
build_from_source_formulae: args.build_from_source_formulae,
|
||||
interactive: args.interactive?,
|
||||
keep_tmp: args.keep_tmp?,
|
||||
debug_symbols: args.debug_symbols?,
|
||||
force: args.force?,
|
||||
debug: args.debug?,
|
||||
quiet: args.quiet?,
|
||||
verbose: args.verbose?,
|
||||
dry_run: args.dry_run?,
|
||||
)
|
||||
|
||||
Cleanup.periodic_clean!(dry_run: args.dry_run?)
|
||||
|
||||
Homebrew.messages.display_messages(display_times: args.display_times?)
|
||||
rescue FormulaUnreadableError, FormulaClassUnavailableError,
|
||||
TapFormulaUnreadableError, TapFormulaClassUnavailableError => e
|
||||
# Need to rescue before `FormulaUnavailableError` (superclass of this)
|
||||
# is handled, as searching for a formula doesn't make sense here (the
|
||||
# formula was found, but there's a problem with its implementation).
|
||||
$stderr.puts Utils::Backtrace.clean(e) if Homebrew::EnvConfig.developer?
|
||||
ofail e.message
|
||||
rescue FormulaOrCaskUnavailableError, Cask::CaskUnavailableError => e
|
||||
Homebrew.failed = true
|
||||
|
||||
# formula name or cask token
|
||||
name = case e
|
||||
when FormulaOrCaskUnavailableError then e.name
|
||||
when Cask::CaskUnavailableError then e.token
|
||||
else T.absurd(e)
|
||||
end
|
||||
|
||||
if name == "updog"
|
||||
ofail "What's updog?"
|
||||
return
|
||||
end
|
||||
|
||||
opoo e
|
||||
|
||||
reason = MissingFormula.reason(name, silent: true)
|
||||
if !args.cask? && reason
|
||||
$stderr.puts reason
|
||||
return
|
||||
end
|
||||
|
||||
# We don't seem to get good search results when the tap is specified
|
||||
# so we might as well return early.
|
||||
return if name.include?("/")
|
||||
|
||||
require "search"
|
||||
|
||||
package_types = []
|
||||
package_types << "formulae" unless args.cask?
|
||||
package_types << "casks" unless args.formula?
|
||||
|
||||
ohai "Searching for similarly named #{package_types.join(" and ")}..."
|
||||
|
||||
# Don't treat formula/cask name as a regex
|
||||
string_or_regex = name
|
||||
all_formulae, all_casks = Search.search_names(string_or_regex, args)
|
||||
|
||||
if all_formulae.any?
|
||||
ohai "Formulae", Formatter.columns(all_formulae)
|
||||
first_formula = all_formulae.first.to_s
|
||||
puts <<~EOS
|
||||
|
||||
To install #{first_formula}, run:
|
||||
brew install #{first_formula}
|
||||
EOS
|
||||
end
|
||||
puts if all_formulae.any? && all_casks.any?
|
||||
if all_casks.any?
|
||||
ohai "Casks", Formatter.columns(all_casks)
|
||||
first_cask = all_casks.first.to_s
|
||||
puts <<~EOS
|
||||
|
||||
To install #{first_cask}, run:
|
||||
brew install --cask #{first_cask}
|
||||
EOS
|
||||
end
|
||||
return if all_formulae.any? || all_casks.any?
|
||||
|
||||
odie "No #{package_types.join(" or ")} found for #{name}."
|
||||
end
|
||||
end
|
||||
|
||||
@ -1,51 +1,51 @@
|
||||
# typed: true
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "abstract_command"
|
||||
require "formula"
|
||||
require "cask_dependent"
|
||||
require "cli/parser"
|
||||
|
||||
module Homebrew
|
||||
module_function
|
||||
module Cmd
|
||||
class Leaves < AbstractCommand
|
||||
cmd_args do
|
||||
description <<~EOS
|
||||
List installed formulae that are not dependencies of another installed formula or cask.
|
||||
EOS
|
||||
switch "-r", "--installed-on-request",
|
||||
description: "Only list leaves that were manually installed."
|
||||
switch "-p", "--installed-as-dependency",
|
||||
description: "Only list leaves that were installed as dependencies."
|
||||
|
||||
sig { returns(CLI::Parser) }
|
||||
def leaves_args
|
||||
Homebrew::CLI::Parser.new do
|
||||
description <<~EOS
|
||||
List installed formulae that are not dependencies of another installed formula or cask.
|
||||
EOS
|
||||
switch "-r", "--installed-on-request",
|
||||
description: "Only list leaves that were manually installed."
|
||||
switch "-p", "--installed-as-dependency",
|
||||
description: "Only list leaves that were installed as dependencies."
|
||||
conflicts "--installed-on-request", "--installed-as-dependency"
|
||||
|
||||
conflicts "--installed-on-request", "--installed-as-dependency"
|
||||
named_args :none
|
||||
end
|
||||
|
||||
named_args :none
|
||||
sig { override.void }
|
||||
def run
|
||||
leaves_list = Formula.installed - Formula.installed.flat_map(&:runtime_formula_dependencies)
|
||||
casks_runtime_dependencies = Cask::Caskroom.casks.flat_map do |cask|
|
||||
CaskDependent.new(cask).runtime_dependencies.map(&:to_formula)
|
||||
end
|
||||
leaves_list -= casks_runtime_dependencies
|
||||
leaves_list.select!(&method(:installed_on_request?)) if args.installed_on_request?
|
||||
leaves_list.select!(&method(:installed_as_dependency?)) if args.installed_as_dependency?
|
||||
|
||||
leaves_list.map(&:full_name)
|
||||
.sort
|
||||
.each(&method(:puts))
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def installed_on_request?(formula)
|
||||
Tab.for_keg(formula.any_installed_keg).installed_on_request
|
||||
end
|
||||
|
||||
def installed_as_dependency?(formula)
|
||||
Tab.for_keg(formula.any_installed_keg).installed_as_dependency
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def installed_on_request?(formula)
|
||||
Tab.for_keg(formula.any_installed_keg).installed_on_request
|
||||
end
|
||||
|
||||
def installed_as_dependency?(formula)
|
||||
Tab.for_keg(formula.any_installed_keg).installed_as_dependency
|
||||
end
|
||||
|
||||
def leaves
|
||||
args = leaves_args.parse
|
||||
|
||||
leaves_list = Formula.installed - Formula.installed.flat_map(&:runtime_formula_dependencies)
|
||||
casks_runtime_dependencies = Cask::Caskroom.casks.flat_map do |cask|
|
||||
CaskDependent.new(cask).runtime_dependencies.map(&:to_formula)
|
||||
end
|
||||
leaves_list -= casks_runtime_dependencies
|
||||
leaves_list.select!(&method(:installed_on_request?)) if args.installed_on_request?
|
||||
leaves_list.select!(&method(:installed_as_dependency?)) if args.installed_as_dependency?
|
||||
|
||||
leaves_list.map(&:full_name)
|
||||
.sort
|
||||
.each(&method(:puts))
|
||||
end
|
||||
end
|
||||
|
||||
@ -1,135 +1,136 @@
|
||||
# typed: true
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "abstract_command"
|
||||
require "caveats"
|
||||
require "cli/parser"
|
||||
require "unlink"
|
||||
|
||||
module Homebrew
|
||||
module_function
|
||||
|
||||
sig { returns(CLI::Parser) }
|
||||
def link_args
|
||||
Homebrew::CLI::Parser.new do
|
||||
description <<~EOS
|
||||
Symlink all of <formula>'s installed files into Homebrew's prefix.
|
||||
This is done automatically when you install formulae but can be useful
|
||||
for manual installations.
|
||||
EOS
|
||||
switch "--overwrite",
|
||||
description: "Delete files that already exist in the prefix while linking."
|
||||
switch "-n", "--dry-run",
|
||||
description: "List files which would be linked or deleted by " \
|
||||
"`brew link --overwrite` without actually linking or deleting any files."
|
||||
switch "-f", "--force",
|
||||
description: "Allow keg-only formulae to be linked."
|
||||
switch "--HEAD",
|
||||
description: "Link the HEAD version of the formula if it is installed."
|
||||
|
||||
named_args :installed_formula, min: 1
|
||||
end
|
||||
end
|
||||
|
||||
def link
|
||||
args = link_args.parse
|
||||
|
||||
options = {
|
||||
overwrite: args.overwrite?,
|
||||
dry_run: args.dry_run?,
|
||||
verbose: args.verbose?,
|
||||
}
|
||||
|
||||
kegs = if args.HEAD?
|
||||
args.named.to_kegs.group_by(&:name).filter_map do |name, resolved_kegs|
|
||||
head_keg = resolved_kegs.find { |keg| keg.version.head? }
|
||||
next head_keg if head_keg.present?
|
||||
|
||||
opoo <<~EOS
|
||||
No HEAD keg installed for #{name}
|
||||
To install, run:
|
||||
brew install --HEAD #{name}
|
||||
module Cmd
|
||||
class Link < AbstractCommand
|
||||
cmd_args do
|
||||
description <<~EOS
|
||||
Symlink all of <formula>'s installed files into Homebrew's prefix.
|
||||
This is done automatically when you install formulae but can be useful
|
||||
for manual installations.
|
||||
EOS
|
||||
end
|
||||
else
|
||||
args.named.to_latest_kegs
|
||||
end
|
||||
switch "--overwrite",
|
||||
description: "Delete files that already exist in the prefix while linking."
|
||||
switch "-n", "--dry-run",
|
||||
description: "List files which would be linked or deleted by " \
|
||||
"`brew link --overwrite` without actually linking or deleting any files."
|
||||
switch "-f", "--force",
|
||||
description: "Allow keg-only formulae to be linked."
|
||||
switch "--HEAD",
|
||||
description: "Link the HEAD version of the formula if it is installed."
|
||||
|
||||
kegs.freeze.each do |keg|
|
||||
keg_only = Formulary.keg_only?(keg.rack)
|
||||
|
||||
if keg.linked?
|
||||
opoo "Already linked: #{keg}"
|
||||
name_and_flag = "#{"--HEAD " if args.HEAD?}#{"--force " if keg_only}#{keg.name}"
|
||||
puts <<~EOS
|
||||
To relink, run:
|
||||
brew unlink #{keg.name} && brew link #{name_and_flag}
|
||||
EOS
|
||||
next
|
||||
named_args :installed_formula, min: 1
|
||||
end
|
||||
|
||||
if args.dry_run?
|
||||
if args.overwrite?
|
||||
puts "Would remove:"
|
||||
sig { override.void }
|
||||
def run
|
||||
options = {
|
||||
overwrite: args.overwrite?,
|
||||
dry_run: args.dry_run?,
|
||||
verbose: args.verbose?,
|
||||
}
|
||||
|
||||
kegs = if args.HEAD?
|
||||
args.named.to_kegs.group_by(&:name).filter_map do |name, resolved_kegs|
|
||||
head_keg = resolved_kegs.find { |keg| keg.version.head? }
|
||||
next head_keg if head_keg.present?
|
||||
|
||||
opoo <<~EOS
|
||||
No HEAD keg installed for #{name}
|
||||
To install, run:
|
||||
brew install --HEAD #{name}
|
||||
EOS
|
||||
end
|
||||
else
|
||||
puts "Would link:"
|
||||
end
|
||||
keg.link(**options)
|
||||
puts_keg_only_path_message(keg) if keg_only
|
||||
next
|
||||
end
|
||||
|
||||
formula = begin
|
||||
keg.to_formula
|
||||
rescue FormulaUnavailableError
|
||||
# Not all kegs may belong to formulae
|
||||
nil
|
||||
end
|
||||
|
||||
if keg_only
|
||||
if HOMEBREW_PREFIX.to_s == HOMEBREW_DEFAULT_PREFIX && formula.present? && formula.keg_only_reason.by_macos?
|
||||
caveats = Caveats.new(formula)
|
||||
opoo <<~EOS
|
||||
Refusing to link macOS provided/shadowed software: #{keg.name}
|
||||
#{caveats.keg_only_text(skip_reason: true).strip}
|
||||
EOS
|
||||
next
|
||||
args.named.to_latest_kegs
|
||||
end
|
||||
|
||||
if !args.force? && (formula.blank? || !formula.keg_only_reason.versioned_formula?)
|
||||
opoo "#{keg.name} is keg-only and must be linked with `--force`."
|
||||
puts_keg_only_path_message(keg)
|
||||
next
|
||||
kegs.freeze.each do |keg|
|
||||
keg_only = Formulary.keg_only?(keg.rack)
|
||||
|
||||
if keg.linked?
|
||||
opoo "Already linked: #{keg}"
|
||||
name_and_flag = "#{"--HEAD " if args.HEAD?}#{"--force " if keg_only}#{keg.name}"
|
||||
puts <<~EOS
|
||||
To relink, run:
|
||||
brew unlink #{keg.name} && brew link #{name_and_flag}
|
||||
EOS
|
||||
next
|
||||
end
|
||||
|
||||
if args.dry_run?
|
||||
if args.overwrite?
|
||||
puts "Would remove:"
|
||||
else
|
||||
puts "Would link:"
|
||||
end
|
||||
keg.link(**options)
|
||||
puts_keg_only_path_message(keg) if keg_only
|
||||
next
|
||||
end
|
||||
|
||||
formula = begin
|
||||
keg.to_formula
|
||||
rescue FormulaUnavailableError
|
||||
# Not all kegs may belong to formulae
|
||||
nil
|
||||
end
|
||||
|
||||
if keg_only
|
||||
if HOMEBREW_PREFIX.to_s == HOMEBREW_DEFAULT_PREFIX && formula.present? &&
|
||||
formula.keg_only_reason.by_macos?
|
||||
caveats = Caveats.new(formula)
|
||||
opoo <<~EOS
|
||||
Refusing to link macOS provided/shadowed software: #{keg.name}
|
||||
#{caveats.keg_only_text(skip_reason: true).strip}
|
||||
EOS
|
||||
next
|
||||
end
|
||||
|
||||
if !args.force? && (formula.blank? || !formula.keg_only_reason.versioned_formula?)
|
||||
opoo "#{keg.name} is keg-only and must be linked with `--force`."
|
||||
puts_keg_only_path_message(keg)
|
||||
next
|
||||
end
|
||||
end
|
||||
|
||||
Unlink.unlink_versioned_formulae(formula, verbose: args.verbose?) if formula
|
||||
|
||||
keg.lock do
|
||||
print "Linking #{keg}... "
|
||||
puts if args.verbose?
|
||||
|
||||
begin
|
||||
n = keg.link(**options)
|
||||
rescue Keg::LinkError
|
||||
puts
|
||||
raise
|
||||
else
|
||||
puts "#{n} symlinks created."
|
||||
end
|
||||
|
||||
puts_keg_only_path_message(keg) if keg_only && !Homebrew::EnvConfig.developer?
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Unlink.unlink_versioned_formulae(formula, verbose: args.verbose?) if formula
|
||||
private
|
||||
|
||||
keg.lock do
|
||||
print "Linking #{keg}... "
|
||||
puts if args.verbose?
|
||||
def puts_keg_only_path_message(keg)
|
||||
bin = keg/"bin"
|
||||
sbin = keg/"sbin"
|
||||
return if !bin.directory? && !sbin.directory?
|
||||
|
||||
begin
|
||||
n = keg.link(**options)
|
||||
rescue Keg::LinkError
|
||||
puts
|
||||
raise
|
||||
else
|
||||
puts "#{n} symlinks created."
|
||||
end
|
||||
|
||||
puts_keg_only_path_message(keg) if keg_only && !Homebrew::EnvConfig.developer?
|
||||
opt = HOMEBREW_PREFIX/"opt/#{keg.name}"
|
||||
puts "\nIf you need to have this software first in your PATH instead consider running:"
|
||||
puts " #{Utils::Shell.prepend_path_in_profile(opt/"bin")}" if bin.directory?
|
||||
puts " #{Utils::Shell.prepend_path_in_profile(opt/"sbin")}" if sbin.directory?
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def puts_keg_only_path_message(keg)
|
||||
bin = keg/"bin"
|
||||
sbin = keg/"sbin"
|
||||
return if !bin.directory? && !sbin.directory?
|
||||
|
||||
opt = HOMEBREW_PREFIX/"opt/#{keg.name}"
|
||||
puts "\nIf you need to have this software first in your PATH instead consider running:"
|
||||
puts " #{Utils::Shell.prepend_path_in_profile(opt/"bin")}" if bin.directory?
|
||||
puts " #{Utils::Shell.prepend_path_in_profile(opt/"sbin")}" if sbin.directory?
|
||||
end
|
||||
end
|
||||
|
||||
@ -177,7 +177,10 @@ module Commands
|
||||
external_commands_file.atomic_write("#{external_commands.sort.join("\n")}\n")
|
||||
end
|
||||
|
||||
sig { params(command: String).returns(T.nilable(T::Array[[String, String]])) }
|
||||
def self.command_options(command)
|
||||
return if command == "help"
|
||||
|
||||
path = self.path(command)
|
||||
return if path.blank?
|
||||
|
||||
@ -207,7 +210,7 @@ module Commands
|
||||
|
||||
if (cmd_parser = Homebrew::CLI::Parser.from_cmd_path(path))
|
||||
if short
|
||||
cmd_parser.description.split(DESCRIPTION_SPLITTING_PATTERN).first
|
||||
cmd_parser.description&.split(DESCRIPTION_SPLITTING_PATTERN)&.first
|
||||
else
|
||||
cmd_parser.description
|
||||
end
|
||||
|
||||
@ -15,7 +15,6 @@ require "style"
|
||||
require "date"
|
||||
require "missing_formula"
|
||||
require "digest"
|
||||
require "cli/parser"
|
||||
require "json"
|
||||
require "formula_auditor"
|
||||
require "tap_auditor"
|
||||
|
||||
@ -8,7 +8,6 @@ require "utils/bottles"
|
||||
require "tab"
|
||||
require "keg"
|
||||
require "formula_versions"
|
||||
require "cli/parser"
|
||||
require "utils/inreplace"
|
||||
require "erb"
|
||||
require "utils/gzip"
|
||||
@ -108,6 +107,110 @@ module Homebrew
|
||||
end
|
||||
end
|
||||
|
||||
def generate_sha256_line(tag, digest, cellar, tag_column, digest_column)
|
||||
line = "sha256 "
|
||||
tag_column += line.length
|
||||
digest_column += line.length
|
||||
if cellar.is_a?(Symbol)
|
||||
line += "cellar: :#{cellar},"
|
||||
elsif cellar_parameter_needed?(cellar)
|
||||
line += %Q(cellar: "#{cellar}",)
|
||||
end
|
||||
line += " " * (tag_column - line.length)
|
||||
line += "#{tag}:"
|
||||
line += " " * (digest_column - line.length)
|
||||
%Q(#{line}"#{digest}")
|
||||
end
|
||||
|
||||
def bottle_output(bottle, root_url_using)
|
||||
cellars = bottle.checksums.filter_map do |checksum|
|
||||
cellar = checksum["cellar"]
|
||||
next unless cellar_parameter_needed? cellar
|
||||
|
||||
case cellar
|
||||
when String
|
||||
%Q("#{cellar}")
|
||||
when Symbol
|
||||
":#{cellar}"
|
||||
end
|
||||
end
|
||||
tag_column = cellars.empty? ? 0 : "cellar: #{cellars.max_by(&:length)}, ".length
|
||||
|
||||
tags = bottle.checksums.map { |checksum| checksum["tag"] }
|
||||
# Start where the tag ends, add the max length of the tag, add two for the `: `
|
||||
digest_column = tag_column + tags.max_by(&:length).length + 2
|
||||
|
||||
sha256_lines = bottle.checksums.map do |checksum|
|
||||
generate_sha256_line(checksum["tag"], checksum["digest"], checksum["cellar"], tag_column, digest_column)
|
||||
end
|
||||
erb_binding = bottle.instance_eval { binding }
|
||||
erb_binding.local_variable_set(:sha256_lines, sha256_lines)
|
||||
erb_binding.local_variable_set(:root_url_using, root_url_using)
|
||||
erb = ERB.new BOTTLE_ERB
|
||||
erb.result(erb_binding).gsub(/^\s*$\n/, "")
|
||||
end
|
||||
|
||||
def parse_json_files(filenames)
|
||||
filenames.map do |filename|
|
||||
JSON.parse(File.read(filename))
|
||||
end
|
||||
end
|
||||
|
||||
def merge_json_files(json_files)
|
||||
json_files.reduce({}) do |hash, json_file|
|
||||
json_file.each_value do |json_hash|
|
||||
json_bottle = json_hash["bottle"]
|
||||
cellar = json_bottle.delete("cellar")
|
||||
json_bottle["tags"].each_value do |json_platform|
|
||||
json_platform["cellar"] ||= cellar
|
||||
end
|
||||
end
|
||||
hash.deep_merge(json_file)
|
||||
end
|
||||
end
|
||||
|
||||
def merge_bottle_spec(old_keys, old_bottle_spec, new_bottle_hash)
|
||||
mismatches = []
|
||||
checksums = []
|
||||
|
||||
new_values = {
|
||||
root_url: new_bottle_hash["root_url"],
|
||||
rebuild: new_bottle_hash["rebuild"],
|
||||
}
|
||||
|
||||
skip_keys = [:sha256, :cellar]
|
||||
old_keys.each do |key|
|
||||
next if skip_keys.include?(key)
|
||||
|
||||
old_value = old_bottle_spec.send(key).to_s
|
||||
new_value = new_values[key].to_s
|
||||
|
||||
next if old_value.present? && new_value == old_value
|
||||
|
||||
mismatches << "#{key}: old: #{old_value.inspect}, new: #{new_value.inspect}"
|
||||
end
|
||||
|
||||
return [mismatches, checksums] if old_keys.exclude? :sha256
|
||||
|
||||
old_bottle_spec.collector.each_tag do |tag|
|
||||
old_tag_spec = old_bottle_spec.collector.specification_for(tag)
|
||||
old_hexdigest = old_tag_spec.checksum.hexdigest
|
||||
old_cellar = old_tag_spec.cellar
|
||||
new_value = new_bottle_hash.dig("tags", tag.to_s)
|
||||
if new_value.present? && new_value["sha256"] != old_hexdigest
|
||||
mismatches << "sha256 #{tag}: old: #{old_hexdigest.inspect}, new: #{new_value["sha256"].inspect}"
|
||||
elsif new_value.present? && new_value["cellar"] != old_cellar.to_s
|
||||
mismatches << "cellar #{tag}: old: #{old_cellar.to_s.inspect}, new: #{new_value["cellar"].inspect}"
|
||||
else
|
||||
checksums << { cellar: old_cellar, tag.to_sym => old_hexdigest }
|
||||
end
|
||||
end
|
||||
|
||||
[mismatches, checksums]
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def keg_contain?(string, keg, ignores, formula_and_runtime_deps_names = nil)
|
||||
@put_string_exists_header, @put_filenames = nil
|
||||
|
||||
@ -186,49 +289,6 @@ module Homebrew
|
||||
cellar.present? && default_cellars.exclude?(cellar)
|
||||
end
|
||||
|
||||
def generate_sha256_line(tag, digest, cellar, tag_column, digest_column)
|
||||
line = "sha256 "
|
||||
tag_column += line.length
|
||||
digest_column += line.length
|
||||
if cellar.is_a?(Symbol)
|
||||
line += "cellar: :#{cellar},"
|
||||
elsif cellar_parameter_needed?(cellar)
|
||||
line += %Q(cellar: "#{cellar}",)
|
||||
end
|
||||
line += " " * (tag_column - line.length)
|
||||
line += "#{tag}:"
|
||||
line += " " * (digest_column - line.length)
|
||||
%Q(#{line}"#{digest}")
|
||||
end
|
||||
|
||||
def bottle_output(bottle, root_url_using)
|
||||
cellars = bottle.checksums.filter_map do |checksum|
|
||||
cellar = checksum["cellar"]
|
||||
next unless cellar_parameter_needed? cellar
|
||||
|
||||
case cellar
|
||||
when String
|
||||
%Q("#{cellar}")
|
||||
when Symbol
|
||||
":#{cellar}"
|
||||
end
|
||||
end
|
||||
tag_column = cellars.empty? ? 0 : "cellar: #{cellars.max_by(&:length)}, ".length
|
||||
|
||||
tags = bottle.checksums.map { |checksum| checksum["tag"] }
|
||||
# Start where the tag ends, add the max length of the tag, add two for the `: `
|
||||
digest_column = tag_column + tags.max_by(&:length).length + 2
|
||||
|
||||
sha256_lines = bottle.checksums.map do |checksum|
|
||||
generate_sha256_line(checksum["tag"], checksum["digest"], checksum["cellar"], tag_column, digest_column)
|
||||
end
|
||||
erb_binding = bottle.instance_eval { binding }
|
||||
erb_binding.local_variable_set(:sha256_lines, sha256_lines)
|
||||
erb_binding.local_variable_set(:root_url_using, root_url_using)
|
||||
erb = ERB.new BOTTLE_ERB
|
||||
erb.result(erb_binding).gsub(/^\s*$\n/, "")
|
||||
end
|
||||
|
||||
def sudo_purge
|
||||
return unless ENV["HOMEBREW_BOTTLE_SUDO_PURGE"]
|
||||
|
||||
@ -601,25 +661,6 @@ module Homebrew
|
||||
json_path.write(JSON.pretty_generate(json))
|
||||
end
|
||||
|
||||
def parse_json_files(filenames)
|
||||
filenames.map do |filename|
|
||||
JSON.parse(File.read(filename))
|
||||
end
|
||||
end
|
||||
|
||||
def merge_json_files(json_files)
|
||||
json_files.reduce({}) do |hash, json_file|
|
||||
json_file.each_value do |json_hash|
|
||||
json_bottle = json_hash["bottle"]
|
||||
cellar = json_bottle.delete("cellar")
|
||||
json_bottle["tags"].each_value do |json_platform|
|
||||
json_platform["cellar"] ||= cellar
|
||||
end
|
||||
end
|
||||
hash.deep_merge(json_file)
|
||||
end
|
||||
end
|
||||
|
||||
def merge
|
||||
bottles_hash = merge_json_files(parse_json_files(args.named))
|
||||
|
||||
@ -774,46 +815,6 @@ module Homebrew
|
||||
end
|
||||
end
|
||||
|
||||
def merge_bottle_spec(old_keys, old_bottle_spec, new_bottle_hash)
|
||||
mismatches = []
|
||||
checksums = []
|
||||
|
||||
new_values = {
|
||||
root_url: new_bottle_hash["root_url"],
|
||||
rebuild: new_bottle_hash["rebuild"],
|
||||
}
|
||||
|
||||
skip_keys = [:sha256, :cellar]
|
||||
old_keys.each do |key|
|
||||
next if skip_keys.include?(key)
|
||||
|
||||
old_value = old_bottle_spec.send(key).to_s
|
||||
new_value = new_values[key].to_s
|
||||
|
||||
next if old_value.present? && new_value == old_value
|
||||
|
||||
mismatches << "#{key}: old: #{old_value.inspect}, new: #{new_value.inspect}"
|
||||
end
|
||||
|
||||
return [mismatches, checksums] if old_keys.exclude? :sha256
|
||||
|
||||
old_bottle_spec.collector.each_tag do |tag|
|
||||
old_tag_spec = old_bottle_spec.collector.specification_for(tag)
|
||||
old_hexdigest = old_tag_spec.checksum.hexdigest
|
||||
old_cellar = old_tag_spec.cellar
|
||||
new_value = new_bottle_hash.dig("tags", tag.to_s)
|
||||
if new_value.present? && new_value["sha256"] != old_hexdigest
|
||||
mismatches << "sha256 #{tag}: old: #{old_hexdigest.inspect}, new: #{new_value["sha256"].inspect}"
|
||||
elsif new_value.present? && new_value["cellar"] != old_cellar.to_s
|
||||
mismatches << "cellar #{tag}: old: #{old_cellar.to_s.inspect}, new: #{new_value["cellar"].inspect}"
|
||||
else
|
||||
checksums << { cellar: old_cellar, tag.to_sym => old_hexdigest }
|
||||
end
|
||||
end
|
||||
|
||||
[mismatches, checksums]
|
||||
end
|
||||
|
||||
def old_checksums(formula, formula_ast, bottle_hash)
|
||||
bottle_node = formula_ast.bottle_block
|
||||
return if bottle_node.nil?
|
||||
|
||||
@ -5,7 +5,6 @@ require "abstract_command"
|
||||
require "bump_version_parser"
|
||||
require "cask"
|
||||
require "cask/download"
|
||||
require "cli/parser"
|
||||
require "utils/tar"
|
||||
|
||||
module Homebrew
|
||||
|
||||
@ -4,7 +4,6 @@
|
||||
require "abstract_command"
|
||||
require "fileutils"
|
||||
require "formula"
|
||||
require "cli/parser"
|
||||
require "utils/pypi"
|
||||
require "utils/tar"
|
||||
|
||||
|
||||
@ -3,7 +3,6 @@
|
||||
|
||||
require "abstract_command"
|
||||
require "formula"
|
||||
require "cli/parser"
|
||||
|
||||
module Homebrew
|
||||
module DevCmd
|
||||
|
||||
@ -5,7 +5,6 @@ require "timeout"
|
||||
require "cask/download"
|
||||
require "cask/installer"
|
||||
require "cask/cask_loader"
|
||||
require "cli/parser"
|
||||
require "system_command"
|
||||
require "tap"
|
||||
require "unversioned_cask_checker"
|
||||
|
||||
@ -3,7 +3,6 @@
|
||||
|
||||
require "abstract_command"
|
||||
require "bump_version_parser"
|
||||
require "cli/parser"
|
||||
require "livecheck/livecheck"
|
||||
|
||||
module Homebrew
|
||||
|
||||
@ -2,7 +2,6 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "abstract_command"
|
||||
require "cli/parser"
|
||||
require "fileutils"
|
||||
|
||||
module Homebrew
|
||||
|
||||
@ -3,7 +3,6 @@
|
||||
|
||||
require "abstract_command"
|
||||
require "commands"
|
||||
require "cli/parser"
|
||||
|
||||
module Homebrew
|
||||
module DevCmd
|
||||
|
||||
@ -2,7 +2,6 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "abstract_command"
|
||||
require "cli/parser"
|
||||
require "csv"
|
||||
|
||||
module Homebrew
|
||||
|
||||
@ -4,7 +4,6 @@
|
||||
require "formula"
|
||||
require "formula_creator"
|
||||
require "missing_formula"
|
||||
require "cli/parser"
|
||||
require "utils/pypi"
|
||||
require "cask/cask_loader"
|
||||
|
||||
|
||||
@ -2,7 +2,6 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "abstract_command"
|
||||
require "cli/parser"
|
||||
require "test_runner_formula"
|
||||
require "github_runner_matrix"
|
||||
|
||||
|
||||
@ -2,7 +2,6 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "abstract_command"
|
||||
require "cli/parser"
|
||||
require "utils/github"
|
||||
|
||||
module Homebrew
|
||||
|
||||
@ -3,7 +3,6 @@
|
||||
|
||||
require "abstract_command"
|
||||
require "formula"
|
||||
require "cli/parser"
|
||||
|
||||
module Homebrew
|
||||
module DevCmd
|
||||
|
||||
@ -2,7 +2,6 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "abstract_command"
|
||||
require "cli/parser"
|
||||
require "utils/git"
|
||||
require "formulary"
|
||||
require "software_spec"
|
||||
|
||||
@ -3,7 +3,6 @@
|
||||
|
||||
require "abstract_command"
|
||||
require "formula"
|
||||
require "cli/parser"
|
||||
|
||||
module Homebrew
|
||||
module DevCmd
|
||||
|
||||
@ -2,7 +2,6 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "abstract_command"
|
||||
require "cli/parser"
|
||||
require "cask/cask"
|
||||
require "fileutils"
|
||||
require "formula"
|
||||
|
||||
@ -2,7 +2,6 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "abstract_command"
|
||||
require "cli/parser"
|
||||
require "fileutils"
|
||||
require "formula"
|
||||
|
||||
|
||||
@ -2,7 +2,6 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "abstract_command"
|
||||
require "cli/parser"
|
||||
|
||||
module Homebrew
|
||||
module DevCmd
|
||||
|
||||
@ -4,7 +4,6 @@
|
||||
require "abstract_command"
|
||||
require "formulary"
|
||||
require "cask/cask_loader"
|
||||
require "cli/parser"
|
||||
|
||||
class String
|
||||
def f(*args)
|
||||
|
||||
@ -4,7 +4,6 @@
|
||||
require "abstract_command"
|
||||
require "cache_store"
|
||||
require "linkage_checker"
|
||||
require "cli/parser"
|
||||
|
||||
module Homebrew
|
||||
module DevCmd
|
||||
|
||||
@ -2,7 +2,6 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "abstract_command"
|
||||
require "cli/parser"
|
||||
require "formula"
|
||||
require "livecheck/livecheck"
|
||||
require "livecheck/strategy"
|
||||
|
||||
@ -2,7 +2,6 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "abstract_command"
|
||||
require "cli/parser"
|
||||
require "utils/github"
|
||||
|
||||
module Homebrew
|
||||
|
||||
@ -2,7 +2,6 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "abstract_command"
|
||||
require "cli/parser"
|
||||
require "utils/github"
|
||||
|
||||
module Homebrew
|
||||
|
||||
@ -2,7 +2,6 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "abstract_command"
|
||||
require "cli/parser"
|
||||
require "fileutils"
|
||||
require "utils/github"
|
||||
require "utils/github/artifacts"
|
||||
|
||||
@ -2,7 +2,6 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "abstract_command"
|
||||
require "cli/parser"
|
||||
require "formula"
|
||||
require "github_packages"
|
||||
require "github_releases"
|
||||
|
||||
@ -2,7 +2,6 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "abstract_command"
|
||||
require "cli/parser"
|
||||
|
||||
module Homebrew
|
||||
module DevCmd
|
||||
|
||||
@ -2,7 +2,6 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "abstract_command"
|
||||
require "cli/parser"
|
||||
|
||||
module Homebrew
|
||||
module DevCmd
|
||||
|
||||
@ -2,7 +2,6 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "abstract_command"
|
||||
require "cli/parser"
|
||||
|
||||
module Homebrew
|
||||
module DevCmd
|
||||
|
||||
@ -4,7 +4,6 @@
|
||||
require "abstract_command"
|
||||
require "extend/ENV"
|
||||
require "formula"
|
||||
require "cli/parser"
|
||||
|
||||
module Homebrew
|
||||
module DevCmd
|
||||
|
||||
@ -5,7 +5,6 @@ require "abstract_command"
|
||||
require "json"
|
||||
require "open3"
|
||||
require "style"
|
||||
require "cli/parser"
|
||||
|
||||
module Homebrew
|
||||
module DevCmd
|
||||
|
||||
@ -4,7 +4,6 @@
|
||||
require "abstract_command"
|
||||
require "fileutils"
|
||||
require "tap"
|
||||
require "cli/parser"
|
||||
|
||||
module Homebrew
|
||||
module DevCmd
|
||||
|
||||
@ -5,7 +5,6 @@ require "abstract_command"
|
||||
require "extend/ENV"
|
||||
require "sandbox"
|
||||
require "timeout"
|
||||
require "cli/parser"
|
||||
|
||||
module Homebrew
|
||||
module DevCmd
|
||||
|
||||
@ -2,7 +2,6 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "abstract_command"
|
||||
require "cli/parser"
|
||||
require "fileutils"
|
||||
require "system_command"
|
||||
|
||||
|
||||
@ -2,7 +2,6 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "abstract_command"
|
||||
require "cli/parser"
|
||||
require "fileutils"
|
||||
|
||||
module Homebrew
|
||||
|
||||
@ -2,7 +2,6 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "abstract_command"
|
||||
require "cli/parser"
|
||||
require "formula"
|
||||
require "api"
|
||||
require "os/mac/xcode"
|
||||
|
||||
@ -5,7 +5,6 @@ require "abstract_command"
|
||||
require "fileutils"
|
||||
require "stringio"
|
||||
require "formula"
|
||||
require "cli/parser"
|
||||
|
||||
module Homebrew
|
||||
module DevCmd
|
||||
|
||||
@ -2,7 +2,6 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "abstract_command"
|
||||
require "cli/parser"
|
||||
require "utils/spdx"
|
||||
require "system_command"
|
||||
|
||||
|
||||
@ -2,7 +2,6 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "abstract_command"
|
||||
require "cli/parser"
|
||||
require "utils/github"
|
||||
require "manpages"
|
||||
require "system_command"
|
||||
|
||||
@ -2,7 +2,6 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "abstract_command"
|
||||
require "cli/parser"
|
||||
require "utils/pypi"
|
||||
|
||||
module Homebrew
|
||||
|
||||
@ -2,7 +2,6 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "abstract_command"
|
||||
require "cli/parser"
|
||||
require "utils/github"
|
||||
require "system_command"
|
||||
|
||||
|
||||
@ -2,7 +2,6 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "abstract_command"
|
||||
require "cli/parser"
|
||||
require "fileutils"
|
||||
|
||||
module Homebrew
|
||||
|
||||
@ -2,7 +2,6 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "abstract_command"
|
||||
require "cli/parser"
|
||||
|
||||
module Homebrew
|
||||
module DevCmd
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "cmd/analytics"
|
||||
require "cmd/shared_examples/args_parse"
|
||||
|
||||
RSpec.describe "brew analytics" do
|
||||
RSpec.describe Homebrew::Cmd::Analytics do
|
||||
it_behaves_like "parseable arguments"
|
||||
|
||||
it "when HOMEBREW_NO_ANALYTICS is unset is disabled after running `brew analytics off`", :integration_test do
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "cmd/autoremove"
|
||||
require "cmd/shared_examples/args_parse"
|
||||
|
||||
RSpec.describe "brew autoremove" do
|
||||
RSpec.describe Homebrew::Cmd::Autoremove do
|
||||
it_behaves_like "parseable arguments"
|
||||
|
||||
describe "integration test" do
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "cmd/cleanup"
|
||||
require "cmd/shared_examples/args_parse"
|
||||
|
||||
RSpec.describe "brew cleanup" do
|
||||
RSpec.describe Homebrew::Cmd::CleanupCmd do
|
||||
before do
|
||||
FileUtils.mkdir_p HOMEBREW_LIBRARY/"Homebrew/vendor/"
|
||||
FileUtils.touch HOMEBREW_LIBRARY/"Homebrew/vendor/portable-ruby-version"
|
||||
|
||||
@ -1,11 +1,9 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "cmd/commands"
|
||||
require "fileutils"
|
||||
|
||||
require "cmd/shared_examples/args_parse"
|
||||
|
||||
RSpec.describe "brew commands" do
|
||||
RSpec.describe Homebrew::Cmd::CommandsCmd do
|
||||
it_behaves_like "parseable arguments"
|
||||
|
||||
it "prints a list of all available commands", :integration_test do
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "cmd/completions"
|
||||
require "cmd/shared_examples/args_parse"
|
||||
|
||||
RSpec.describe "brew completions" do
|
||||
RSpec.describe Homebrew::Cmd::CompletionsCmd do
|
||||
it_behaves_like "parseable arguments"
|
||||
|
||||
it "runs the status subcommand correctly", :integration_test do
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "cmd/config"
|
||||
require "cmd/shared_examples/args_parse"
|
||||
|
||||
RSpec.describe "brew config" do
|
||||
RSpec.describe Homebrew::Cmd::Config do
|
||||
it_behaves_like "parseable arguments"
|
||||
|
||||
it "prints information about the current Homebrew configuration", :integration_test do
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "cmd/deps"
|
||||
require "cmd/shared_examples/args_parse"
|
||||
|
||||
RSpec.describe "brew deps" do
|
||||
RSpec.describe Homebrew::Cmd::Deps do
|
||||
it_behaves_like "parseable arguments"
|
||||
|
||||
it "outputs all of a Formula's dependencies and their dependencies on separate lines", :integration_test do
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "cmd/desc"
|
||||
require "cmd/shared_examples/args_parse"
|
||||
|
||||
RSpec.describe "brew desc" do
|
||||
RSpec.describe Homebrew::Cmd::Desc do
|
||||
it_behaves_like "parseable arguments"
|
||||
|
||||
it "shows a given Formula's description", :integration_test do
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "cmd/developer"
|
||||
require "cmd/shared_examples/args_parse"
|
||||
|
||||
RSpec.describe "brew developer" do
|
||||
RSpec.describe Homebrew::Cmd::Developer do
|
||||
it_behaves_like "parseable arguments"
|
||||
end
|
||||
|
||||
@ -1,6 +1,11 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe "brew docs" do
|
||||
require "cmd/docs"
|
||||
require "cmd/shared_examples/args_parse"
|
||||
|
||||
RSpec.describe Homebrew::Cmd::Docs do
|
||||
it_behaves_like "parseable arguments"
|
||||
|
||||
it "opens the docs page", :integration_test do
|
||||
expect { brew "docs", "HOMEBREW_BROWSER" => "echo" }
|
||||
.to output("https://docs.brew.sh\n").to_stdout
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "cmd/doctor"
|
||||
require "cmd/shared_examples/args_parse"
|
||||
|
||||
RSpec.describe "brew doctor" do
|
||||
RSpec.describe Homebrew::Cmd::Doctor do
|
||||
it_behaves_like "parseable arguments"
|
||||
|
||||
specify "check_integration_test", :integration_test do
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "cmd/fetch"
|
||||
require "cmd/shared_examples/args_parse"
|
||||
|
||||
RSpec.describe "brew fetch" do
|
||||
RSpec.describe Homebrew::Cmd::FetchCmd do
|
||||
it_behaves_like "parseable arguments"
|
||||
|
||||
it "downloads the Formula's URL", :integration_test do
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "cmd/gist-logs"
|
||||
require "cmd/shared_examples/args_parse"
|
||||
|
||||
RSpec.describe "brew gist-logs" do
|
||||
RSpec.describe Homebrew::Cmd::GistLogs do
|
||||
it_behaves_like "parseable arguments"
|
||||
end
|
||||
|
||||
@ -1,6 +1,11 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe "brew", :integration_test do
|
||||
require "cmd/help"
|
||||
require "cmd/shared_examples/args_parse"
|
||||
|
||||
RSpec.describe Homebrew::Cmd::HelpCmd, :integration_test do
|
||||
it_behaves_like "parseable arguments"
|
||||
|
||||
describe "help" do
|
||||
it "prints help for a documented Ruby command" do
|
||||
expect { brew "help", "cat" }
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "cmd/home"
|
||||
require "cmd/shared_examples/args_parse"
|
||||
|
||||
RSpec.describe "brew home" do
|
||||
RSpec.describe Homebrew::Cmd::Home do
|
||||
let(:testballhome_homepage) do
|
||||
Formula["testballhome"].homepage
|
||||
end
|
||||
|
||||
@ -1,10 +1,9 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "cmd/info"
|
||||
|
||||
require "cmd/shared_examples/args_parse"
|
||||
|
||||
RSpec.describe "brew info" do
|
||||
RSpec.describe Homebrew::Cmd::Info do
|
||||
it_behaves_like "parseable arguments"
|
||||
|
||||
it "prints as json with the --json=v1 flag", :integration_test do
|
||||
@ -25,23 +24,21 @@ RSpec.describe "brew info" do
|
||||
.and be_a_success
|
||||
end
|
||||
|
||||
describe Homebrew do
|
||||
describe "::github_remote_path" do
|
||||
let(:remote) { "https://github.com/Homebrew/homebrew-core" }
|
||||
describe "::github_remote_path" do
|
||||
let(:remote) { "https://github.com/Homebrew/homebrew-core" }
|
||||
|
||||
specify "returns correct URLs" do
|
||||
expect(described_class.github_remote_path(remote, "Formula/git.rb"))
|
||||
.to eq("https://github.com/Homebrew/homebrew-core/blob/HEAD/Formula/git.rb")
|
||||
specify "returns correct URLs" do
|
||||
expect(described_class.new([]).github_remote_path(remote, "Formula/git.rb"))
|
||||
.to eq("https://github.com/Homebrew/homebrew-core/blob/HEAD/Formula/git.rb")
|
||||
|
||||
expect(described_class.github_remote_path("#{remote}.git", "Formula/git.rb"))
|
||||
.to eq("https://github.com/Homebrew/homebrew-core/blob/HEAD/Formula/git.rb")
|
||||
expect(described_class.new([]).github_remote_path("#{remote}.git", "Formula/git.rb"))
|
||||
.to eq("https://github.com/Homebrew/homebrew-core/blob/HEAD/Formula/git.rb")
|
||||
|
||||
expect(described_class.github_remote_path("git@github.com:user/repo", "foo.rb"))
|
||||
.to eq("https://github.com/user/repo/blob/HEAD/foo.rb")
|
||||
expect(described_class.new([]).github_remote_path("git@github.com:user/repo", "foo.rb"))
|
||||
.to eq("https://github.com/user/repo/blob/HEAD/foo.rb")
|
||||
|
||||
expect(described_class.github_remote_path("https://mywebsite.com", "foo/bar.rb"))
|
||||
.to eq("https://mywebsite.com/foo/bar.rb")
|
||||
end
|
||||
expect(described_class.new([]).github_remote_path("https://mywebsite.com", "foo/bar.rb"))
|
||||
.to eq("https://mywebsite.com/foo/bar.rb")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "cmd/install"
|
||||
require "cmd/shared_examples/args_parse"
|
||||
|
||||
RSpec.describe "brew install" do
|
||||
RSpec.describe Homebrew::Cmd::InstallCmd do
|
||||
it_behaves_like "parseable arguments"
|
||||
|
||||
it "installs formulae", :integration_test do
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "cmd/leaves"
|
||||
require "cmd/shared_examples/args_parse"
|
||||
|
||||
RSpec.describe "brew leaves" do
|
||||
RSpec.describe Homebrew::Cmd::Leaves do
|
||||
it_behaves_like "parseable arguments"
|
||||
|
||||
context "when there are no installed Formulae", :integration_test do
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "cmd/link"
|
||||
require "cmd/shared_examples/args_parse"
|
||||
|
||||
RSpec.describe "brew link" do
|
||||
RSpec.describe Homebrew::Cmd::Link do
|
||||
it_behaves_like "parseable arguments"
|
||||
|
||||
it "links a given Formula", :integration_test do
|
||||
|
||||
@ -70,7 +70,7 @@ RSpec.describe FormulaInstaller do
|
||||
# rubocop:disable RSpec/NoExpectationExample
|
||||
specify "basic bottle install" do
|
||||
allow(DevelopmentTools).to receive(:installed?).and_return(false)
|
||||
Homebrew.install_args.parse(["testball_bottle"])
|
||||
Homebrew::Cmd::InstallCmd.new(["testball_bottle"])
|
||||
temporarily_install_bottle(TestballBottle.new) do |f|
|
||||
test_basic_formula_setup(f)
|
||||
end
|
||||
@ -79,7 +79,7 @@ RSpec.describe FormulaInstaller do
|
||||
|
||||
specify "basic bottle install with cellar information on sha256 line" do
|
||||
allow(DevelopmentTools).to receive(:installed?).and_return(false)
|
||||
Homebrew.install_args.parse(["testball_bottle_cellar"])
|
||||
Homebrew::Cmd::InstallCmd.new(["testball_bottle_cellar"])
|
||||
temporarily_install_bottle(TestballBottleCellar.new) do |f|
|
||||
test_basic_formula_setup(f)
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user