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
|
# typed: strong
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require "cli/parser"
|
||||||
|
|
||||||
module Homebrew
|
module Homebrew
|
||||||
# Subclass this to implement a `brew` command. This is preferred to declaring a named function in the `Homebrew`
|
# Subclass this to implement a `brew` command. This is preferred to declaring a named function in the `Homebrew`
|
||||||
# module, because:
|
# module, because:
|
||||||
# - Each Command lives in an isolated namespace.
|
# - Each Command lives in an isolated namespace.
|
||||||
# - Each Command implements a defined interface.
|
# - 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.
|
# - `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 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`.
|
# To generate method signatures for command args, run `brew typecheck --update`.
|
||||||
|
|||||||
@ -217,7 +217,7 @@ class Build
|
|||||||
end
|
end
|
||||||
|
|
||||||
begin
|
begin
|
||||||
args = Homebrew.install_args.parse
|
args = Homebrew::Cmd::InstallCmd.new.args
|
||||||
Context.current = args.context
|
Context.current = args.context
|
||||||
|
|
||||||
error_pipe = UNIXSocket.open(ENV.fetch("HOMEBREW_ERROR_PIPE"), &:recv_io)
|
error_pipe = UNIXSocket.open(ENV.fetch("HOMEBREW_ERROR_PIPE"), &:recv_io)
|
||||||
|
|||||||
@ -1,51 +1,48 @@
|
|||||||
# typed: strict
|
# typed: strict
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require "cli/parser"
|
require "abstract_command"
|
||||||
|
|
||||||
module Homebrew
|
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) }
|
`brew analytics` [`state`]:
|
||||||
def analytics_args
|
Display the current state of Homebrew's analytics.
|
||||||
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`]:
|
`brew analytics` (`on`|`off`):
|
||||||
Display the current state of Homebrew's analytics.
|
Turn Homebrew's analytics on or off respectively.
|
||||||
|
EOS
|
||||||
|
|
||||||
`brew analytics` (`on`|`off`):
|
named_args %w[state on off regenerate-uuid], max: 1
|
||||||
Turn Homebrew's analytics on or off respectively.
|
end
|
||||||
EOS
|
|
||||||
|
sig { override.void }
|
||||||
named_args %w[state on off regenerate-uuid], max: 1
|
def run
|
||||||
end
|
case args.named.first
|
||||||
end
|
when nil, "state"
|
||||||
|
if Utils::Analytics.disabled?
|
||||||
sig { void }
|
puts "InfluxDB analytics are disabled."
|
||||||
def analytics
|
else
|
||||||
args = analytics_args.parse
|
puts "InfluxDB analytics are enabled."
|
||||||
|
end
|
||||||
case args.named.first
|
puts "Google Analytics were destroyed."
|
||||||
when nil, "state"
|
when "on"
|
||||||
if Utils::Analytics.disabled?
|
Utils::Analytics.enable!
|
||||||
puts "InfluxDB analytics are disabled."
|
when "off"
|
||||||
else
|
Utils::Analytics.disable!
|
||||||
puts "InfluxDB analytics are enabled."
|
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
|
||||||
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
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@ -1,27 +1,26 @@
|
|||||||
# typed: strict
|
# typed: strict
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require "abstract_command"
|
||||||
require "cleanup"
|
require "cleanup"
|
||||||
require "cli/parser"
|
|
||||||
|
|
||||||
module Homebrew
|
module Homebrew
|
||||||
sig { returns(CLI::Parser) }
|
module Cmd
|
||||||
def self.autoremove_args
|
class Autoremove < AbstractCommand
|
||||||
Homebrew::CLI::Parser.new do
|
cmd_args do
|
||||||
description <<~EOS
|
description <<~EOS
|
||||||
Uninstall formulae that were only installed as a dependency of another formula and are now no longer needed.
|
Uninstall formulae that were only installed as a dependency of another formula and are now no longer needed.
|
||||||
EOS
|
EOS
|
||||||
switch "-n", "--dry-run",
|
switch "-n", "--dry-run",
|
||||||
description: "List what would be uninstalled, but do not actually uninstall anything."
|
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
|
||||||
end
|
end
|
||||||
|
|
||||||
sig { void }
|
|
||||||
def self.autoremove
|
|
||||||
args = autoremove_args.parse
|
|
||||||
|
|
||||||
Cleanup.autoremove(dry_run: args.dry_run?)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|||||||
@ -1,75 +1,72 @@
|
|||||||
# typed: strict
|
# typed: strict
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require "abstract_command"
|
||||||
require "cleanup"
|
require "cleanup"
|
||||||
require "cli/parser"
|
|
||||||
|
|
||||||
module Homebrew
|
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) }
|
named_args [:formula, :cask]
|
||||||
def cleanup_args
|
end
|
||||||
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]
|
sig { override.void }
|
||||||
end
|
def run
|
||||||
end
|
days = args.prune.presence&.then do |prune|
|
||||||
|
case prune
|
||||||
|
when /\A\d+\Z/
|
||||||
|
prune.to_i
|
||||||
|
when "all"
|
||||||
|
0
|
||||||
|
else
|
||||||
|
raise UsageError, "`--prune` expects an integer or `all`."
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
sig { void }
|
cleanup = Cleanup.new(*args.named, dry_run: args.dry_run?, scrub: args.s?, days:)
|
||||||
def cleanup
|
if args.prune_prefix?
|
||||||
args = cleanup_args.parse
|
cleanup.prune_prefix_symlinks_and_directories
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
days = args.prune.presence&.then do |prune|
|
cleanup.clean!(quiet: args.quiet?, periodic: false)
|
||||||
case prune
|
|
||||||
when /\A\d+\Z/
|
unless cleanup.disk_cleanup_size.zero?
|
||||||
prune.to_i
|
disk_space = disk_usage_readable(cleanup.disk_cleanup_size)
|
||||||
when "all"
|
if args.dry_run?
|
||||||
0
|
ohai "This operation would free approximately #{disk_space} of disk space."
|
||||||
else
|
else
|
||||||
raise UsageError, "`--prune` expects an integer or `all`."
|
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
|
||||||
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
|
||||||
end
|
end
|
||||||
|
|||||||
@ -1,49 +1,46 @@
|
|||||||
# typed: strict
|
# typed: strict
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require "cli/parser"
|
require "abstract_command"
|
||||||
|
|
||||||
module Homebrew
|
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) }
|
named_args :none
|
||||||
def commands_args
|
end
|
||||||
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
|
sig { override.void }
|
||||||
end
|
def run
|
||||||
end
|
if args.quiet?
|
||||||
|
puts Formatter.columns(Commands.commands(aliases: args.include_aliases?))
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
sig { void }
|
prepend_separator = T.let(false, T::Boolean)
|
||||||
def commands
|
|
||||||
args = commands_args.parse
|
|
||||||
|
|
||||||
if args.quiet?
|
{
|
||||||
puts Formatter.columns(Commands.commands(aliases: args.include_aliases?))
|
"Built-in commands" => Commands.internal_commands,
|
||||||
return
|
"Built-in developer commands" => Commands.internal_developer_commands,
|
||||||
end
|
"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)
|
||||||
|
|
||||||
{
|
prepend_separator ||= true
|
||||||
"Built-in commands" => Commands.internal_commands,
|
end
|
||||||
"Built-in developer commands" => Commands.internal_developer_commands,
|
end
|
||||||
"External commands" => Commands.external_commands,
|
|
||||||
}.each do |title, commands|
|
|
||||||
next if commands.blank?
|
|
||||||
|
|
||||||
puts if prepend_separator
|
|
||||||
ohai title, Formatter.columns(commands)
|
|
||||||
|
|
||||||
prepend_separator ||= true
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@ -1,49 +1,46 @@
|
|||||||
# typed: strict
|
# typed: strict
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require "cli/parser"
|
require "abstract_command"
|
||||||
require "completions"
|
require "completions"
|
||||||
|
|
||||||
module Homebrew
|
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) }
|
`brew completions` [`state`]:
|
||||||
def completions_args
|
Display the current state of Homebrew's completions.
|
||||||
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`]:
|
`brew completions` (`link`|`unlink`):
|
||||||
Display the current state of Homebrew's completions.
|
Link or unlink Homebrew's completions.
|
||||||
|
EOS
|
||||||
|
|
||||||
`brew completions` (`link`|`unlink`):
|
named_args %w[state link unlink], max: 1
|
||||||
Link or unlink Homebrew's completions.
|
end
|
||||||
EOS
|
|
||||||
|
sig { override.void }
|
||||||
named_args %w[state link unlink], max: 1
|
def run
|
||||||
end
|
case args.named.first
|
||||||
end
|
when nil, "state"
|
||||||
|
if Completions.link_completions?
|
||||||
sig { void }
|
puts "Completions are linked."
|
||||||
def completions
|
else
|
||||||
args = completions_args.parse
|
puts "Completions are not linked."
|
||||||
|
end
|
||||||
case args.named.first
|
when "link"
|
||||||
when nil, "state"
|
Completions.link!
|
||||||
if Completions.link_completions?
|
puts "Completions are now linked."
|
||||||
puts "Completions are linked."
|
when "unlink"
|
||||||
else
|
Completions.unlink!
|
||||||
puts "Completions are not linked."
|
puts "Completions are no longer linked."
|
||||||
|
else
|
||||||
|
raise UsageError, "unknown subcommand: #{args.named.first}"
|
||||||
|
end
|
||||||
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
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@ -1,28 +1,25 @@
|
|||||||
# typed: strict
|
# typed: strict
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require "abstract_command"
|
||||||
require "system_config"
|
require "system_config"
|
||||||
require "cli/parser"
|
|
||||||
|
|
||||||
module Homebrew
|
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) }
|
named_args :none
|
||||||
def config_args
|
end
|
||||||
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
|
sig { override.void }
|
||||||
|
def run
|
||||||
|
SystemConfig.dump_verbose_config
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
sig { void }
|
|
||||||
def config
|
|
||||||
config_args.parse
|
|
||||||
|
|
||||||
SystemConfig.dump_verbose_config
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|||||||
@ -1,340 +1,339 @@
|
|||||||
# typed: true
|
# typed: true
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require "abstract_command"
|
||||||
require "formula"
|
require "formula"
|
||||||
require "cli/parser"
|
|
||||||
require "cask/caskroom"
|
require "cask/caskroom"
|
||||||
require "dependencies_helpers"
|
require "dependencies_helpers"
|
||||||
|
|
||||||
module Homebrew
|
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) }
|
If any version of each formula argument is installed and no other options
|
||||||
def self.deps_args
|
are passed, this command displays their actual runtime dependencies (similar
|
||||||
Homebrew::CLI::Parser.new do
|
to `brew linkage`), which may differ from the current versions' stated
|
||||||
description <<~EOS
|
dependencies if the installed versions are outdated.
|
||||||
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
|
*Note:* `--missing` and `--skip-recommended` have precedence over `--include-*`.
|
||||||
are passed, this command displays their actual runtime dependencies (similar
|
EOS
|
||||||
to `brew linkage`), which may differ from the current versions' stated
|
switch "-n", "--topological",
|
||||||
dependencies if the installed versions are outdated.
|
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-*`.
|
conflicts "--tree", "--graph"
|
||||||
EOS
|
conflicts "--installed", "--missing"
|
||||||
switch "-n", "--topological",
|
conflicts "--installed", "--eval-all"
|
||||||
description: "Sort dependencies in topological order."
|
conflicts "--formula", "--cask"
|
||||||
switch "-1", "--direct", "--declared", "--1",
|
formula_options
|
||||||
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"
|
named_args [:formula, :cask]
|
||||||
conflicts "--installed", "--missing"
|
end
|
||||||
conflicts "--installed", "--eval-all"
|
|
||||||
conflicts "--formula", "--cask"
|
|
||||||
formula_options
|
|
||||||
|
|
||||||
named_args [:formula, :cask]
|
sig { override.void }
|
||||||
end
|
def run
|
||||||
end
|
all = args.eval_all?
|
||||||
|
|
||||||
def self.deps
|
Formulary.enable_factory_cache!
|
||||||
args = deps_args.parse
|
|
||||||
|
|
||||||
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?
|
if args.tree? || args.graph?
|
||||||
installed = args.installed? || dependents(args.named.to_formulae_and_casks).all?(&:any_version_installed?)
|
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 &&
|
if args.graph?
|
||||||
!args.tree? &&
|
dot_code = dot_code(dependents, recursive:)
|
||||||
!args.graph? &&
|
if args.dot?
|
||||||
!args.HEAD? &&
|
puts dot_code
|
||||||
!args.include_build? &&
|
else
|
||||||
!args.include_test? &&
|
exec_browser "https://dreampuf.github.io/GraphvizOnline/##{ERB::Util.url_encode(dot_code)}"
|
||||||
!args.include_optional? &&
|
end
|
||||||
!args.skip_recommended? &&
|
return
|
||||||
!args.missing?
|
end
|
||||||
|
|
||||||
if args.tree? || args.graph?
|
puts_deps_tree(dependents, recursive:)
|
||||||
dependents = if args.named.present?
|
return
|
||||||
sorted_dependents(args.named.to_formulae_and_casks)
|
elsif all
|
||||||
elsif args.installed?
|
puts_deps(sorted_dependents(
|
||||||
case args.only_formula_or_cask
|
Formula.all(eval_all: args.eval_all?) + Cask::Cask.all(eval_all: args.eval_all?),
|
||||||
when :formula
|
), recursive:)
|
||||||
sorted_dependents(Formula.installed)
|
return
|
||||||
when :cask
|
elsif !args.no_named? && args.for_each?
|
||||||
sorted_dependents(Cask::Caskroom.casks)
|
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
|
else
|
||||||
sorted_dependents(Formula.installed + Cask::Caskroom.casks)
|
dep.name
|
||||||
end
|
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
|
end
|
||||||
|
|
||||||
if args.graph?
|
def deps_for_dependent(dependency, recursive: false)
|
||||||
dot_code = dot_code(dependents, recursive:, args:)
|
includes, ignores = args_includes_ignores(args)
|
||||||
if args.dot?
|
|
||||||
puts dot_code
|
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
|
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
|
end
|
||||||
return
|
|
||||||
|
deps + reqs.to_a
|
||||||
end
|
end
|
||||||
|
|
||||||
puts_deps_tree(dependents, recursive:, args:)
|
def deps_for_dependents(dependents, recursive: false, &block)
|
||||||
return
|
dependents.map { |d| deps_for_dependent(d, recursive:) }.reduce(&block)
|
||||||
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)
|
|
||||||
end
|
end
|
||||||
puts_deps(sorted_dependents_formulae_and_casks, recursive:, args:)
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
dependents = dependents(args.named.to_formulae_and_casks)
|
def check_head_spec(dependents)
|
||||||
check_head_spec(dependents) if args.HEAD?
|
headless = dependents.select { |d| d.is_a?(Formula) && d.active_spec_sym != :head }
|
||||||
|
.to_sentence two_words_connector: " or ", last_word_connector: " or "
|
||||||
all_deps = deps_for_dependents(dependents, recursive:, args:, &(args.union? ? :| : :&))
|
opoo "No head spec for #{headless}, using stable spec instead" unless headless.empty?
|
||||||
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}"
|
|
||||||
end
|
end
|
||||||
elsif args.full_name?
|
|
||||||
dep.to_formula.full_name
|
|
||||||
else
|
|
||||||
dep.name
|
|
||||||
end
|
|
||||||
|
|
||||||
if args.annotate?
|
def puts_deps(dependents, recursive: false)
|
||||||
str = "#{str} " if args.tree?
|
check_head_spec(dependents) if args.HEAD?
|
||||||
str = "#{str} [build]" if dep.build?
|
dependents.each do |dependent|
|
||||||
str = "#{str} [test]" if dep.test?
|
deps = deps_for_dependent(dependent, recursive:)
|
||||||
str = "#{str} [optional]" if dep.optional?
|
condense_requirements(deps)
|
||||||
str = "#{str} [recommended]" if dep.recommended?
|
deps.sort_by!(&:name)
|
||||||
str = "#{str} [implicit]" if dep.implicit?
|
deps.map! { |d| dep_display_name(d) }
|
||||||
end
|
puts "#{dependent.full_name}: #{deps.join(" ")}"
|
||||||
|
|
||||||
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"
|
|
||||||
end
|
end
|
||||||
comment = " # #{dep.tags.map(&:inspect).join(", ")}" if dep.tags.any?
|
|
||||||
" \"#{d.name}\" -> \"#{dep}\"#{" [#{attributes.join(", ")}]" if attributes.any?}#{comment}"
|
|
||||||
end
|
end
|
||||||
end.flatten.join("\n")
|
|
||||||
"digraph {\n#{dot_code}\n}"
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.graph_deps(formula, dep_graph:, recursive:, args:)
|
def dot_code(dependents, recursive:)
|
||||||
return if dep_graph.key?(formula)
|
dep_graph = {}
|
||||||
|
dependents.each do |d|
|
||||||
|
graph_deps(d, dep_graph:, recursive:)
|
||||||
|
end
|
||||||
|
|
||||||
dependables = dependables(formula, args:)
|
dot_code = dep_graph.map do |d, deps|
|
||||||
dep_graph[formula] = dependables
|
deps.map do |dep|
|
||||||
return unless recursive
|
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|
|
def graph_deps(formula, dep_graph:, recursive:)
|
||||||
next unless dep.is_a? Dependency
|
return if dep_graph.key?(formula)
|
||||||
|
|
||||||
graph_deps(Formulary.factory(dep.name),
|
dependables = dependables(formula)
|
||||||
dep_graph:,
|
dep_graph[formula] = dependables
|
||||||
recursive: true,
|
return unless recursive
|
||||||
args:)
|
|
||||||
|
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
|
||||||
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
|
end
|
||||||
|
|||||||
@ -1,74 +1,75 @@
|
|||||||
# typed: true
|
# typed: strict
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require "abstract_command"
|
||||||
require "descriptions"
|
require "descriptions"
|
||||||
require "search"
|
require "search"
|
||||||
require "description_cache_store"
|
require "description_cache_store"
|
||||||
require "cli/parser"
|
|
||||||
|
|
||||||
module Homebrew
|
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) }
|
conflicts "--search", "--name", "--description"
|
||||||
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"
|
named_args [:formula, :cask, :text_or_regex], min: 1
|
||||||
|
end
|
||||||
|
|
||||||
named_args [:formula, :cask, :text_or_regex], min: 1
|
sig { override.void }
|
||||||
end
|
def run
|
||||||
end
|
if !args.eval_all? && !Homebrew::EnvConfig.eval_all?
|
||||||
|
raise UsageError, "`brew desc` needs `--eval-all` passed or `HOMEBREW_EVAL_ALL` set!"
|
||||||
|
end
|
||||||
|
|
||||||
def desc
|
search_type = if args.search?
|
||||||
args = desc_args.parse
|
:either
|
||||||
|
elsif args.name?
|
||||||
|
:name
|
||||||
|
elsif args.description?
|
||||||
|
:desc
|
||||||
|
end
|
||||||
|
|
||||||
if !args.eval_all? && !Homebrew::EnvConfig.eval_all?
|
if search_type.blank?
|
||||||
raise UsageError, "`brew desc` needs `--eval-all` passed or `HOMEBREW_EVAL_ALL` set!"
|
desc = {}
|
||||||
end
|
args.named.to_formulae_and_casks.each do |formula_or_cask|
|
||||||
|
case formula_or_cask
|
||||||
search_type = if args.search?
|
when Formula
|
||||||
:either
|
desc[formula_or_cask.full_name] = formula_or_cask.desc
|
||||||
elsif args.name?
|
when Cask::Cask
|
||||||
:name
|
description = formula_or_cask.desc.presence || Formatter.warning("[no description]")
|
||||||
elsif args.description?
|
desc[formula_or_cask.full_name] = "(#{formula_or_cask.name.join(", ")}) #{description}"
|
||||||
:desc
|
else
|
||||||
end
|
raise TypeError, "Unsupported formula_or_cask type: #{formula_or_cask.class}"
|
||||||
|
end
|
||||||
if search_type.blank?
|
end
|
||||||
desc = {}
|
Descriptions.new(desc).print
|
||||||
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
|
|
||||||
else
|
else
|
||||||
description = formula_or_cask.desc.presence || Formatter.warning("[no description]")
|
query = args.named.join(" ")
|
||||||
desc[formula_or_cask.full_name] = "(#{formula_or_cask.name.join(", ")}) #{description}"
|
string_or_regex = Search.query_regexp(query)
|
||||||
|
Search.search_descriptions(string_or_regex, args, search_type:)
|
||||||
end
|
end
|
||||||
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
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@ -1,56 +1,55 @@
|
|||||||
# typed: true
|
# typed: strict
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require "cli/parser"
|
require "abstract_command"
|
||||||
|
|
||||||
module Homebrew
|
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) }
|
`brew developer` [`state`]:
|
||||||
def developer_args
|
Display the current state of Homebrew's developer mode.
|
||||||
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`]:
|
`brew developer` (`on`|`off`):
|
||||||
Display the current state of Homebrew's developer mode.
|
Turn Homebrew's developer mode on or off respectively.
|
||||||
|
EOS
|
||||||
|
|
||||||
`brew developer` (`on`|`off`):
|
named_args %w[state on off], max: 1
|
||||||
Turn Homebrew's developer mode on or off respectively.
|
end
|
||||||
EOS
|
|
||||||
|
sig { override.void }
|
||||||
named_args %w[state on off], max: 1
|
def run
|
||||||
end
|
env_vars = []
|
||||||
end
|
env_vars << "HOMEBREW_DEVELOPER" if Homebrew::EnvConfig.developer?
|
||||||
|
env_vars << "HOMEBREW_UPDATE_TO_TAG" if Homebrew::EnvConfig.update_to_tag?
|
||||||
def developer
|
env_vars.map! do |var|
|
||||||
args = developer_args.parse
|
"#{Tty.bold}#{var}#{Tty.reset}"
|
||||||
|
end
|
||||||
env_vars = []
|
|
||||||
env_vars << "HOMEBREW_DEVELOPER" if Homebrew::EnvConfig.developer?
|
case args.named.first
|
||||||
env_vars << "HOMEBREW_UPDATE_TO_TAG" if Homebrew::EnvConfig.update_to_tag?
|
when nil, "state"
|
||||||
env_vars.map! do |var|
|
if env_vars.any?
|
||||||
"#{Tty.bold}#{var}#{Tty.reset}"
|
verb = (env_vars.count == 1) ? "is" : "are"
|
||||||
end
|
puts "Developer mode is enabled because #{env_vars.to_sentence} #{verb} set."
|
||||||
|
elsif Homebrew::Settings.read("devcmdrun") == "true"
|
||||||
case args.named.first
|
puts "Developer mode is enabled."
|
||||||
when nil, "state"
|
else
|
||||||
if env_vars.any?
|
puts "Developer mode is disabled."
|
||||||
puts "Developer mode is enabled because #{env_vars.to_sentence} #{(env_vars.count == 1) ? "is" : "are"} set."
|
end
|
||||||
elsif Homebrew::Settings.read("devcmdrun") == "true"
|
when "on"
|
||||||
puts "Developer mode is enabled."
|
Homebrew::Settings.write "devcmdrun", true
|
||||||
else
|
when "off"
|
||||||
puts "Developer mode is disabled."
|
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
|
||||||
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
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@ -1,22 +1,21 @@
|
|||||||
# typed: strict
|
# typed: strict
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require "cli/parser"
|
require "abstract_command"
|
||||||
|
|
||||||
module Homebrew
|
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) }
|
sig { override.void }
|
||||||
def docs_args
|
def run
|
||||||
Homebrew::CLI::Parser.new do
|
exec_browser HOMEBREW_DOCS_WWW
|
||||||
description <<~EOS
|
end
|
||||||
Open Homebrew's online documentation at <#{HOMEBREW_DOCS_WWW}> in a browser.
|
|
||||||
EOS
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
sig { void }
|
|
||||||
def docs
|
|
||||||
exec_browser HOMEBREW_DOCS_WWW
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|||||||
@ -1,80 +1,80 @@
|
|||||||
# typed: true
|
# typed: strict
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require "abstract_command"
|
||||||
require "diagnostic"
|
require "diagnostic"
|
||||||
require "cli/parser"
|
|
||||||
require "cask/caskroom"
|
require "cask/caskroom"
|
||||||
|
|
||||||
module Homebrew
|
module Homebrew
|
||||||
sig { returns(CLI::Parser) }
|
module Cmd
|
||||||
def self.doctor_args
|
class Doctor < AbstractCommand
|
||||||
Homebrew::CLI::Parser.new do
|
cmd_args do
|
||||||
description <<~EOS
|
description <<~EOS
|
||||||
Check your system for potential problems. Will exit with a non-zero status
|
Check your system for potential problems. Will exit with a non-zero status
|
||||||
if any potential problems are found.
|
if any potential problems are found.
|
||||||
|
|
||||||
Please note that these warnings are just used to help the Homebrew maintainers
|
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
|
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.
|
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}
|
|
||||||
EOS
|
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
|
||||||
|
|
||||||
$stderr.puts
|
sig { override.void }
|
||||||
opoo out
|
def run
|
||||||
Homebrew.failed = true
|
Homebrew.inject_dump_stats!(Diagnostic::Checks, /^check_*/) if args.audit_debug?
|
||||||
first_warning = false
|
|
||||||
end
|
|
||||||
|
|
||||||
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
|
||||||
end
|
end
|
||||||
|
|||||||
@ -1,256 +1,257 @@
|
|||||||
# typed: true
|
# typed: true
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require "abstract_command"
|
||||||
require "formula"
|
require "formula"
|
||||||
require "fetch"
|
require "fetch"
|
||||||
require "cli/parser"
|
|
||||||
require "cask/download"
|
require "cask/download"
|
||||||
|
|
||||||
module Homebrew
|
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) }
|
conflicts "--build-from-source", "--build-bottle", "--force-bottle", "--bottle-tag"
|
||||||
def self.fetch_args
|
conflicts "--cask", "--HEAD"
|
||||||
Homebrew::CLI::Parser.new do
|
conflicts "--cask", "--deps"
|
||||||
description <<~EOS
|
conflicts "--cask", "-s"
|
||||||
Download a bottle (if available) or source packages for <formula>e
|
conflicts "--cask", "--build-bottle"
|
||||||
and binaries for <cask>s. For files, also print SHA-256 checksums.
|
conflicts "--cask", "--force-bottle"
|
||||||
EOS
|
conflicts "--cask", "--bottle-tag"
|
||||||
flag "--os=",
|
conflicts "--formula", "--cask"
|
||||||
description: "Download for the given operating system. " \
|
conflicts "--os", "--bottle-tag"
|
||||||
"(Pass `all` to download for all operating systems.)"
|
conflicts "--arch", "--bottle-tag"
|
||||||
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"
|
named_args [:formula, :cask], min: 1
|
||||||
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
|
|
||||||
end
|
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 = if args.deps?
|
||||||
bucket.each do |formula_or_cask|
|
args.named.to_formulae_and_casks.flat_map do |formula_or_cask|
|
||||||
case formula_or_cask
|
case formula_or_cask
|
||||||
when Formula
|
when Formula
|
||||||
formula = T.cast(formula_or_cask, Formula)
|
formula = formula_or_cask
|
||||||
ref = formula.loaded_from_api? ? formula.full_name : formula.path
|
[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|
|
os_arch_combinations = args.os_arch_combinations
|
||||||
SimulateSystem.with(os:, arch:) do
|
|
||||||
formula = Formulary.factory(ref, args.HEAD? ? :head : :stable)
|
|
||||||
|
|
||||||
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
|
os_arch_combinations.each do |os, arch|
|
||||||
if fetch_bottle?(
|
SimulateSystem.with(os:, arch:) do
|
||||||
formula,
|
formula = Formulary.factory(ref, args.HEAD? ? :head : :stable)
|
||||||
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)
|
formula.print_tap_action verb: "Fetching"
|
||||||
Utils::Bottles::Tag.from_symbol(bottle_tag)
|
|
||||||
else
|
fetched_bottle = false
|
||||||
Utils::Bottles::Tag.new(system: os, arch:)
|
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
|
end
|
||||||
|
|
||||||
bottle = formula.bottle_for_tag(bottle_tag)
|
next if fetched_bottle
|
||||||
|
|
||||||
if bottle.nil?
|
fetch_formula(formula)
|
||||||
opoo "Bottle for tag #{bottle_tag.to_sym.inspect} is unavailable."
|
|
||||||
|
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
|
next
|
||||||
end
|
end
|
||||||
|
|
||||||
begin
|
quarantine = args.quarantine?
|
||||||
bottle.fetch_tab
|
quarantine = true if quarantine.nil?
|
||||||
rescue DownloadError
|
|
||||||
retry if retry_fetch?(bottle, args:)
|
|
||||||
raise
|
|
||||||
end
|
|
||||||
fetch_formula(bottle, args:)
|
|
||||||
rescue Interrupt
|
|
||||||
raise
|
|
||||||
rescue => e
|
|
||||||
raise if Homebrew::EnvConfig.developer?
|
|
||||||
|
|
||||||
fetched_bottle = false
|
download = Cask::Download.new(cask, quarantine:)
|
||||||
onoe e.message
|
fetch_cask(download)
|
||||||
opoo "Bottle fetch failed, fetching the source instead."
|
|
||||||
else
|
|
||||||
fetched_bottle = true
|
|
||||||
end
|
end
|
||||||
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
|
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
|
||||||
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
|
end
|
||||||
|
|||||||
@ -1,130 +1,132 @@
|
|||||||
# typed: true
|
# typed: true
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require "abstract_command"
|
||||||
require "formula"
|
require "formula"
|
||||||
require "install"
|
require "install"
|
||||||
require "system_config"
|
require "system_config"
|
||||||
require "stringio"
|
require "stringio"
|
||||||
require "socket"
|
require "socket"
|
||||||
require "cli/parser"
|
|
||||||
|
|
||||||
module Homebrew
|
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) }
|
sig { override.void }
|
||||||
def gist_logs_args
|
def run
|
||||||
Homebrew::CLI::Parser.new do
|
Install.perform_preinstall_checks(all_fatal: true)
|
||||||
description <<~EOS
|
Install.perform_build_from_source_checks(all_fatal: true)
|
||||||
Upload logs for a failed build of <formula> to a new Gist. Presents an
|
gistify_logs(args.named.to_resolved_formulae.first)
|
||||||
error message if no logs are found.
|
end
|
||||||
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."
|
|
||||||
|
|
||||||
named_args :formula, number: 1
|
private
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def gistify_logs(formula, args:)
|
def gistify_logs(formula)
|
||||||
files = load_logs(formula.logs)
|
files = load_logs(formula.logs)
|
||||||
build_time = formula.logs.ctime
|
build_time = formula.logs.ctime
|
||||||
timestamp = build_time.strftime("%Y-%m-%d_%H-%M-%S")
|
timestamp = build_time.strftime("%Y-%m-%d_%H-%M-%S")
|
||||||
|
|
||||||
s = StringIO.new
|
s = StringIO.new
|
||||||
SystemConfig.dump_verbose_config s
|
SystemConfig.dump_verbose_config s
|
||||||
# Dummy summary file, asciibetically first, to control display title of gist
|
# Dummy summary file, asciibetically first, to control display title of gist
|
||||||
files["# #{formula.name} - #{timestamp}.txt"] = {
|
files["# #{formula.name} - #{timestamp}.txt"] = {
|
||||||
content: brief_build_info(formula, with_hostname: args.with_hostname?),
|
content: brief_build_info(formula, with_hostname: args.with_hostname?),
|
||||||
}
|
}
|
||||||
files["00.config.out"] = { content: s.string }
|
files["00.config.out"] = { content: s.string }
|
||||||
files["00.doctor.out"] = { content: Utils.popen_read("#{HOMEBREW_PREFIX}/bin/brew", "doctor", err: :out) }
|
files["00.doctor.out"] = { content: Utils.popen_read("#{HOMEBREW_PREFIX}/bin/brew", "doctor", err: :out) }
|
||||||
unless formula.core_formula?
|
unless formula.core_formula?
|
||||||
tap = <<~EOS
|
tap = <<~EOS
|
||||||
Formula: #{formula.name}
|
Formula: #{formula.name}
|
||||||
Tap: #{formula.tap}
|
Tap: #{formula.tap}
|
||||||
Path: #{formula.path}
|
Path: #{formula.path}
|
||||||
EOS
|
EOS
|
||||||
files["00.tap.out"] = { content: tap }
|
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 }
|
|
||||||
end
|
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
|
||||||
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
|
||||||
end
|
end
|
||||||
|
|||||||
@ -1,11 +1,16 @@
|
|||||||
# typed: strict
|
# typed: strong
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require "abstract_command"
|
||||||
require "help"
|
require "help"
|
||||||
|
|
||||||
module Homebrew
|
module Homebrew
|
||||||
sig { returns(T.noreturn) }
|
module Cmd
|
||||||
def help
|
class HelpCmd < AbstractCommand
|
||||||
Help.help
|
sig { override.void }
|
||||||
|
def run
|
||||||
|
Help.help
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@ -1,54 +1,53 @@
|
|||||||
# typed: true
|
# typed: true
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require "cli/parser"
|
require "abstract_command"
|
||||||
require "formula"
|
require "formula"
|
||||||
|
|
||||||
module Homebrew
|
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) }
|
conflicts "--formula", "--cask"
|
||||||
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"
|
named_args [:formula, :cask]
|
||||||
|
end
|
||||||
|
|
||||||
named_args [:formula, :cask]
|
sig { override.void }
|
||||||
end
|
def run
|
||||||
end
|
if args.no_named?
|
||||||
|
exec_browser HOMEBREW_WWW
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
sig { void }
|
# to_formulae_and_casks is typed to possibly return Kegs (but won't without explicitly asking)
|
||||||
def home
|
formulae_or_casks = T.cast(args.named.to_formulae_and_casks, T::Array[T.any(Formula, Cask::Cask)])
|
||||||
args = home_args.parse
|
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(*T.unsafe(homepages))
|
||||||
exec_browser HOMEBREW_WWW
|
end
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
# to_formulae_and_casks is typed to possibly return Kegs (but won't without explicitly asking)
|
private
|
||||||
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
|
|
||||||
|
|
||||||
exec_browser(*T.unsafe(homepages))
|
def name_of(formula_or_cask)
|
||||||
end
|
if formula_or_cask.is_a? Formula
|
||||||
|
"Formula #{formula_or_cask.name}"
|
||||||
def name_of(formula_or_cask)
|
else
|
||||||
if formula_or_cask.is_a? Formula
|
"Cask #{formula_or_cask.token}"
|
||||||
"Formula #{formula_or_cask.name}"
|
end
|
||||||
else
|
end
|
||||||
"Cask #{formula_or_cask.token}"
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
# typed: true
|
# typed: true
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require "abstract_command"
|
||||||
require "missing_formula"
|
require "missing_formula"
|
||||||
require "caveats"
|
require "caveats"
|
||||||
require "cli/parser"
|
|
||||||
require "options"
|
require "options"
|
||||||
require "formula"
|
require "formula"
|
||||||
require "keg"
|
require "keg"
|
||||||
@ -14,364 +14,363 @@ require "deprecate_disable"
|
|||||||
require "api"
|
require "api"
|
||||||
|
|
||||||
module Homebrew
|
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
|
cmd_args do
|
||||||
VALID_FORMULA_CATEGORIES = %w[install install-on-request build-error].freeze
|
description <<~EOS
|
||||||
VALID_CATEGORIES = (VALID_FORMULA_CATEGORIES + %w[cask-install os-version]).freeze
|
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) }
|
conflicts "--installed", "--eval-all"
|
||||||
def info_args
|
conflicts "--installed", "--all"
|
||||||
Homebrew::CLI::Parser.new do
|
conflicts "--formula", "--cask"
|
||||||
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"
|
named_args [:formula, :cask]
|
||||||
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(", ")}."
|
|
||||||
end
|
end
|
||||||
|
|
||||||
if args.category.present?
|
sig { override.void }
|
||||||
if args.named.present? && VALID_FORMULA_CATEGORIES.exclude?(args.category)
|
def run
|
||||||
raise UsageError,
|
if args.analytics?
|
||||||
"`--category` must be one of #{VALID_FORMULA_CATEGORIES.join(", ")} when querying formulae."
|
if args.days.present? && VALID_DAYS.exclude?(args.days)
|
||||||
end
|
raise UsageError, "`--days` must be one of #{VALID_DAYS.join(", ")}."
|
||||||
|
end
|
||||||
|
|
||||||
unless VALID_CATEGORIES.include?(args.category)
|
if args.category.present?
|
||||||
raise UsageError, "`--category` must be one of #{VALID_CATEGORIES.join(", ")}."
|
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
|
||||||
end
|
end
|
||||||
|
|
||||||
print_analytics(args:)
|
def github_remote_path(remote, path)
|
||||||
elsif args.json
|
if remote =~ %r{^(?:https?://|git(?:@|://))github\.com[:/](.+)/(.+?)(?:\.git)?$}
|
||||||
all = args.eval_all?
|
"https://github.com/#{Regexp.last_match(1)}/#{Regexp.last_match(2)}/blob/HEAD/#{path}"
|
||||||
|
else
|
||||||
print_json(all, args:)
|
"#{remote}/#{path}"
|
||||||
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
|
|
||||||
end
|
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
|
end
|
||||||
|
|
||||||
if args.variations?
|
private
|
||||||
formulae.map(&:to_hash_with_variations)
|
|
||||||
else
|
sig { void }
|
||||||
formulae.map(&:to_hash)
|
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
|
end
|
||||||
when :v2
|
|
||||||
formulae, casks = if all
|
sig { void }
|
||||||
[
|
def print_analytics
|
||||||
Formula.all(eval_all: args.eval_all?).sort,
|
if args.no_named?
|
||||||
Cask::Cask.all(eval_all: args.eval_all?).sort_by(&:full_name),
|
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?
|
if kegs.empty?
|
||||||
[Formula.installed.sort, Cask::Caskroom.casks.sort_by(&:full_name)]
|
puts "Not installed"
|
||||||
else
|
else
|
||||||
args.named.to_formulae_to_casks
|
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
|
end
|
||||||
|
|
||||||
if args.variations?
|
def decorate_dependencies(dependencies)
|
||||||
{
|
deps_status = dependencies.map do |dep|
|
||||||
"formulae" => formulae.map(&:to_hash_with_variations),
|
if dep.satisfied?([])
|
||||||
"casks" => casks.map(&:to_hash_with_variations),
|
pretty_installed(dep_display_s(dep))
|
||||||
}
|
else
|
||||||
else
|
pretty_uninstalled(dep_display_s(dep))
|
||||||
{
|
end
|
||||||
"formulae" => formulae.map(&:to_hash),
|
end
|
||||||
"casks" => casks.map(&:to_h),
|
deps_status.join(", ")
|
||||||
}
|
|
||||||
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)}"
|
|
||||||
end
|
end
|
||||||
|
|
||||||
cask.sourcefile_path.relative_path_from(cask.tap.path)
|
def decorate_requirements(requirements)
|
||||||
end
|
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)
|
def dep_display_s(dep)
|
||||||
end
|
return dep.name if dep.option_tags.empty?
|
||||||
|
|
||||||
def info_formula(formula, args:)
|
"#{dep.name} #{dep.option_tags.map { |o| "--#{o}" }.join(" ")}"
|
||||||
specs = []
|
end
|
||||||
|
|
||||||
if (stable = formula.stable)
|
def info_cask(cask)
|
||||||
string = "stable #{stable.version}"
|
require "cask/info"
|
||||||
string += " (bottled)" if stable.bottled? && formula.pour_bottle?
|
|
||||||
specs << string
|
|
||||||
end
|
|
||||||
|
|
||||||
specs << "HEAD" if formula.head
|
Cask::Info.info(cask)
|
||||||
|
|
||||||
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?
|
|
||||||
end
|
end
|
||||||
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
|
||||||
end
|
end
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
# typed: true
|
# typed: strict
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require "abstract_command"
|
||||||
require "cask/config"
|
require "cask/config"
|
||||||
require "cask/installer"
|
require "cask/installer"
|
||||||
require "cask_dependent"
|
require "cask_dependent"
|
||||||
@ -9,400 +10,402 @@ require "formula_installer"
|
|||||||
require "development_tools"
|
require "development_tools"
|
||||||
require "install"
|
require "install"
|
||||||
require "cleanup"
|
require "cleanup"
|
||||||
require "cli/parser"
|
|
||||||
require "upgrade"
|
require "upgrade"
|
||||||
|
|
||||||
module Homebrew
|
module Homebrew
|
||||||
sig { returns(CLI::Parser) }
|
module Cmd
|
||||||
def self.install_args
|
class InstallCmd < AbstractCommand
|
||||||
Homebrew::CLI::Parser.new do
|
cmd_args do
|
||||||
description <<~EOS
|
description <<~EOS
|
||||||
Install a <formula> or <cask>. Additional options specific to a <formula> may be
|
Install a <formula> or <cask>. Additional options specific to a <formula> may be
|
||||||
appended to the command.
|
appended to the command.
|
||||||
|
|
||||||
Unless `HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK` is set, `brew upgrade` or `brew reinstall` will be run for
|
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.
|
outdated dependents and dependents with broken linkage, respectively.
|
||||||
|
|
||||||
Unless `HOMEBREW_NO_INSTALL_CLEANUP` is set, `brew cleanup` will then be run for
|
Unless `HOMEBREW_NO_INSTALL_CLEANUP` is set, `brew cleanup` will then be run for
|
||||||
the installed formulae or, every 30 days, for all formulae.
|
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
|
Unless `HOMEBREW_NO_INSTALL_UPGRADE` is set, `brew install` <formula> will upgrade <formula> if it
|
||||||
is already installed but outdated.
|
is already installed but outdated.
|
||||||
EOS
|
EOS
|
||||||
switch "-d", "--debug",
|
switch "-d", "--debug",
|
||||||
description: "If brewing fails, open an interactive debugging session with access to IRB " \
|
description: "If brewing fails, open an interactive debugging session with access to IRB " \
|
||||||
"or a shell inside the temporary build directory."
|
"or a shell inside the temporary build directory."
|
||||||
switch "-f", "--force",
|
switch "-f", "--force",
|
||||||
description: "Install formulae without checking for previously installed keg-only or " \
|
description: "Install formulae without checking for previously installed keg-only or " \
|
||||||
"non-migrated versions. When installing casks, overwrite existing files " \
|
"non-migrated versions. When installing casks, overwrite existing files " \
|
||||||
"(binaries and symlinks are excluded, unless originally from the same cask)."
|
"(binaries and symlinks are excluded, unless originally from the same cask)."
|
||||||
switch "-v", "--verbose",
|
switch "-v", "--verbose",
|
||||||
description: "Print the verification and post-install steps."
|
description: "Print the verification and post-install steps."
|
||||||
switch "-n", "--dry-run",
|
switch "-n", "--dry-run",
|
||||||
description: "Show what would be installed, but do not actually install anything."
|
description: "Show what would be installed, but do not actually install anything."
|
||||||
[
|
[
|
||||||
[:switch, "--formula", "--formulae", {
|
[:switch, "--formula", "--formulae", {
|
||||||
description: "Treat all named arguments as formulae.",
|
description: "Treat all named arguments as formulae.",
|
||||||
}],
|
}],
|
||||||
[:flag, "--env=", {
|
[:flag, "--env=", {
|
||||||
description: "Disabled other than for internal Homebrew use.",
|
description: "Disabled other than for internal Homebrew use.",
|
||||||
hidden: true,
|
hidden: true,
|
||||||
}],
|
}],
|
||||||
[:switch, "--ignore-dependencies", {
|
[:switch, "--ignore-dependencies", {
|
||||||
description: "An unsupported Homebrew development option to skip installing any dependencies of any " \
|
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 " \
|
"kind. If the dependencies are not already present, the formula will have issues. If " \
|
||||||
"not developing Homebrew, consider adjusting your PATH rather than using this option.",
|
"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 " \
|
[:switch, "--only-dependencies", {
|
||||||
"formula itself.",
|
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 " \
|
[:flag, "--cc=", {
|
||||||
"compiler's executable, e.g. `gcc-7` for GCC 7. In order to use LLVM's clang, specify " \
|
description: "Attempt to compile using the specified <compiler>, which should be the name of the " \
|
||||||
"`llvm_clang`. To use the Apple-provided clang, specify `clang`. This option will only " \
|
"compiler's executable, e.g. `gcc-7` for GCC 7. In order to use LLVM's clang, specify " \
|
||||||
"accept compilers that are provided by Homebrew or bundled with macOS. Please do not " \
|
"`llvm_clang`. To use the Apple-provided clang, specify `clang`. This option will only " \
|
||||||
"file issues if you encounter errors while using this option.",
|
"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. " \
|
[:switch, "-s", "--build-from-source", {
|
||||||
"Dependencies will still be installed from bottles if they are available.",
|
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 " \
|
[:switch, "--force-bottle", {
|
||||||
"macOS, even if it would not normally be used for installation.",
|
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, "--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, "--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 " \
|
[:switch, "--fetch-HEAD", {
|
||||||
"formula is outdated. Otherwise, the repository's HEAD will only be checked for " \
|
description: "Fetch the upstream repository to detect if the HEAD installation of the " \
|
||||||
"updates when a new stable or development version has been released.",
|
"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, "--keep-tmp", {
|
||||||
}],
|
description: "Retain the temporary files created during installation.",
|
||||||
[:switch, "--debug-symbols", {
|
}],
|
||||||
depends_on: "--build-from-source",
|
[:switch, "--debug-symbols", {
|
||||||
description: "Generate debug symbols on build. Source will be retained in a cache directory.",
|
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 " \
|
[:switch, "--build-bottle", {
|
||||||
"post-install steps.",
|
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.",
|
[:switch, "--skip-post-install", {
|
||||||
}],
|
description: "Install but skip any post-install steps.",
|
||||||
[:flag, "--bottle-arch=", {
|
}],
|
||||||
depends_on: "--build-bottle",
|
[:flag, "--bottle-arch=", {
|
||||||
description: "Optimise bottles for the specified architecture rather than the oldest " \
|
depends_on: "--build-bottle",
|
||||||
"architecture supported by the version of macOS the bottles are built on.",
|
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,
|
[:switch, "--display-times", {
|
||||||
description: "Print install times for each package at the end of the run.",
|
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 " \
|
[:switch, "-i", "--interactive", {
|
||||||
"run `./configure --help` and otherwise determine how to turn the software " \
|
description: "Download and patch <formula>, then open a shell. This allows the user to " \
|
||||||
"package into a Homebrew package.",
|
"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, "-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.",
|
[:switch, "--overwrite", {
|
||||||
}],
|
description: "Delete files that already exist in the prefix while linking.",
|
||||||
].each do |args|
|
}],
|
||||||
options = args.pop
|
].each do |args|
|
||||||
send(*args, **options)
|
options = args.pop
|
||||||
conflicts "--cask", args.last
|
send(*args, **options)
|
||||||
end
|
conflicts "--cask", args.last
|
||||||
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(" ")
|
|
||||||
end
|
end
|
||||||
casks.each do |cask|
|
formula_options
|
||||||
dep_names = CaskDependent.new(cask)
|
[
|
||||||
.runtime_dependencies
|
[:switch, "--cask", "--casks", { description: "Treat all named arguments as casks." }],
|
||||||
.reject(&:installed?)
|
[:switch, "--[no-]binaries", {
|
||||||
.map(&:to_formula)
|
description: "Disable/enable linking of helper executables (default: enabled).",
|
||||||
.map(&:name)
|
env: :cask_opts_binaries,
|
||||||
next if dep_names.blank?
|
}],
|
||||||
|
[: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}:"
|
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
|
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|
|
if build_flags.present? && !Homebrew::EnvConfig.developer?
|
||||||
Cask::Installer.new(
|
opoo "building from source is not supported!"
|
||||||
cask,
|
puts "You're on your own. Failures are expected so don't create any issues, please!"
|
||||||
binaries: args.binaries?,
|
end
|
||||||
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?
|
installed_formulae = formulae.select do |f|
|
||||||
require "cask/upgrade"
|
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(
|
return if installed_formulae.empty?
|
||||||
*installed_casks,
|
|
||||||
force: args.force?,
|
Install.perform_preinstall_checks(cc: args.cc)
|
||||||
dry_run: args.dry_run?,
|
|
||||||
binaries: args.binaries?,
|
Install.install_formulae(
|
||||||
quarantine: args.quarantine?,
|
installed_formulae,
|
||||||
require_sha: args.require_sha?,
|
build_bottle: args.build_bottle?,
|
||||||
skip_cask_deps: args.skip_cask_deps?,
|
force_bottle: args.force_bottle?,
|
||||||
verbose: args.verbose?,
|
bottle_arch: args.bottle_arch,
|
||||||
args:,
|
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
|
||||||
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
|
||||||
end
|
end
|
||||||
|
|||||||
@ -1,51 +1,51 @@
|
|||||||
# typed: true
|
# typed: true
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require "abstract_command"
|
||||||
require "formula"
|
require "formula"
|
||||||
require "cask_dependent"
|
require "cask_dependent"
|
||||||
require "cli/parser"
|
|
||||||
|
|
||||||
module Homebrew
|
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) }
|
conflicts "--installed-on-request", "--installed-as-dependency"
|
||||||
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"
|
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
|
||||||
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
|
end
|
||||||
|
|||||||
@ -1,135 +1,136 @@
|
|||||||
# typed: true
|
# typed: true
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require "abstract_command"
|
||||||
require "caveats"
|
require "caveats"
|
||||||
require "cli/parser"
|
|
||||||
require "unlink"
|
require "unlink"
|
||||||
|
|
||||||
module Homebrew
|
module Homebrew
|
||||||
module_function
|
module Cmd
|
||||||
|
class Link < AbstractCommand
|
||||||
sig { returns(CLI::Parser) }
|
cmd_args do
|
||||||
def link_args
|
description <<~EOS
|
||||||
Homebrew::CLI::Parser.new do
|
Symlink all of <formula>'s installed files into Homebrew's prefix.
|
||||||
description <<~EOS
|
This is done automatically when you install formulae but can be useful
|
||||||
Symlink all of <formula>'s installed files into Homebrew's prefix.
|
for manual installations.
|
||||||
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}
|
|
||||||
EOS
|
EOS
|
||||||
end
|
switch "--overwrite",
|
||||||
else
|
description: "Delete files that already exist in the prefix while linking."
|
||||||
args.named.to_latest_kegs
|
switch "-n", "--dry-run",
|
||||||
end
|
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|
|
named_args :installed_formula, min: 1
|
||||||
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
|
end
|
||||||
|
|
||||||
if args.dry_run?
|
sig { override.void }
|
||||||
if args.overwrite?
|
def run
|
||||||
puts "Would remove:"
|
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
|
else
|
||||||
puts "Would link:"
|
args.named.to_latest_kegs
|
||||||
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
|
end
|
||||||
|
|
||||||
if !args.force? && (formula.blank? || !formula.keg_only_reason.versioned_formula?)
|
kegs.freeze.each do |keg|
|
||||||
opoo "#{keg.name} is keg-only and must be linked with `--force`."
|
keg_only = Formulary.keg_only?(keg.rack)
|
||||||
puts_keg_only_path_message(keg)
|
|
||||||
next
|
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
|
||||||
end
|
end
|
||||||
|
|
||||||
Unlink.unlink_versioned_formulae(formula, verbose: args.verbose?) if formula
|
private
|
||||||
|
|
||||||
keg.lock do
|
def puts_keg_only_path_message(keg)
|
||||||
print "Linking #{keg}... "
|
bin = keg/"bin"
|
||||||
puts if args.verbose?
|
sbin = keg/"sbin"
|
||||||
|
return if !bin.directory? && !sbin.directory?
|
||||||
|
|
||||||
begin
|
opt = HOMEBREW_PREFIX/"opt/#{keg.name}"
|
||||||
n = keg.link(**options)
|
puts "\nIf you need to have this software first in your PATH instead consider running:"
|
||||||
rescue Keg::LinkError
|
puts " #{Utils::Shell.prepend_path_in_profile(opt/"bin")}" if bin.directory?
|
||||||
puts
|
puts " #{Utils::Shell.prepend_path_in_profile(opt/"sbin")}" if sbin.directory?
|
||||||
raise
|
|
||||||
else
|
|
||||||
puts "#{n} symlinks created."
|
|
||||||
end
|
|
||||||
|
|
||||||
puts_keg_only_path_message(keg) if keg_only && !Homebrew::EnvConfig.developer?
|
|
||||||
end
|
end
|
||||||
end
|
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
|
end
|
||||||
|
|||||||
@ -177,7 +177,10 @@ module Commands
|
|||||||
external_commands_file.atomic_write("#{external_commands.sort.join("\n")}\n")
|
external_commands_file.atomic_write("#{external_commands.sort.join("\n")}\n")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
sig { params(command: String).returns(T.nilable(T::Array[[String, String]])) }
|
||||||
def self.command_options(command)
|
def self.command_options(command)
|
||||||
|
return if command == "help"
|
||||||
|
|
||||||
path = self.path(command)
|
path = self.path(command)
|
||||||
return if path.blank?
|
return if path.blank?
|
||||||
|
|
||||||
@ -207,7 +210,7 @@ module Commands
|
|||||||
|
|
||||||
if (cmd_parser = Homebrew::CLI::Parser.from_cmd_path(path))
|
if (cmd_parser = Homebrew::CLI::Parser.from_cmd_path(path))
|
||||||
if short
|
if short
|
||||||
cmd_parser.description.split(DESCRIPTION_SPLITTING_PATTERN).first
|
cmd_parser.description&.split(DESCRIPTION_SPLITTING_PATTERN)&.first
|
||||||
else
|
else
|
||||||
cmd_parser.description
|
cmd_parser.description
|
||||||
end
|
end
|
||||||
|
|||||||
@ -15,7 +15,6 @@ require "style"
|
|||||||
require "date"
|
require "date"
|
||||||
require "missing_formula"
|
require "missing_formula"
|
||||||
require "digest"
|
require "digest"
|
||||||
require "cli/parser"
|
|
||||||
require "json"
|
require "json"
|
||||||
require "formula_auditor"
|
require "formula_auditor"
|
||||||
require "tap_auditor"
|
require "tap_auditor"
|
||||||
|
|||||||
@ -8,7 +8,6 @@ require "utils/bottles"
|
|||||||
require "tab"
|
require "tab"
|
||||||
require "keg"
|
require "keg"
|
||||||
require "formula_versions"
|
require "formula_versions"
|
||||||
require "cli/parser"
|
|
||||||
require "utils/inreplace"
|
require "utils/inreplace"
|
||||||
require "erb"
|
require "erb"
|
||||||
require "utils/gzip"
|
require "utils/gzip"
|
||||||
@ -108,6 +107,110 @@ module Homebrew
|
|||||||
end
|
end
|
||||||
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)
|
def keg_contain?(string, keg, ignores, formula_and_runtime_deps_names = nil)
|
||||||
@put_string_exists_header, @put_filenames = nil
|
@put_string_exists_header, @put_filenames = nil
|
||||||
|
|
||||||
@ -186,49 +289,6 @@ module Homebrew
|
|||||||
cellar.present? && default_cellars.exclude?(cellar)
|
cellar.present? && default_cellars.exclude?(cellar)
|
||||||
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 sudo_purge
|
def sudo_purge
|
||||||
return unless ENV["HOMEBREW_BOTTLE_SUDO_PURGE"]
|
return unless ENV["HOMEBREW_BOTTLE_SUDO_PURGE"]
|
||||||
|
|
||||||
@ -601,25 +661,6 @@ module Homebrew
|
|||||||
json_path.write(JSON.pretty_generate(json))
|
json_path.write(JSON.pretty_generate(json))
|
||||||
end
|
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
|
def merge
|
||||||
bottles_hash = merge_json_files(parse_json_files(args.named))
|
bottles_hash = merge_json_files(parse_json_files(args.named))
|
||||||
|
|
||||||
@ -774,46 +815,6 @@ module Homebrew
|
|||||||
end
|
end
|
||||||
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)
|
def old_checksums(formula, formula_ast, bottle_hash)
|
||||||
bottle_node = formula_ast.bottle_block
|
bottle_node = formula_ast.bottle_block
|
||||||
return if bottle_node.nil?
|
return if bottle_node.nil?
|
||||||
|
|||||||
@ -5,7 +5,6 @@ require "abstract_command"
|
|||||||
require "bump_version_parser"
|
require "bump_version_parser"
|
||||||
require "cask"
|
require "cask"
|
||||||
require "cask/download"
|
require "cask/download"
|
||||||
require "cli/parser"
|
|
||||||
require "utils/tar"
|
require "utils/tar"
|
||||||
|
|
||||||
module Homebrew
|
module Homebrew
|
||||||
|
|||||||
@ -4,7 +4,6 @@
|
|||||||
require "abstract_command"
|
require "abstract_command"
|
||||||
require "fileutils"
|
require "fileutils"
|
||||||
require "formula"
|
require "formula"
|
||||||
require "cli/parser"
|
|
||||||
require "utils/pypi"
|
require "utils/pypi"
|
||||||
require "utils/tar"
|
require "utils/tar"
|
||||||
|
|
||||||
|
|||||||
@ -3,7 +3,6 @@
|
|||||||
|
|
||||||
require "abstract_command"
|
require "abstract_command"
|
||||||
require "formula"
|
require "formula"
|
||||||
require "cli/parser"
|
|
||||||
|
|
||||||
module Homebrew
|
module Homebrew
|
||||||
module DevCmd
|
module DevCmd
|
||||||
|
|||||||
@ -5,7 +5,6 @@ require "timeout"
|
|||||||
require "cask/download"
|
require "cask/download"
|
||||||
require "cask/installer"
|
require "cask/installer"
|
||||||
require "cask/cask_loader"
|
require "cask/cask_loader"
|
||||||
require "cli/parser"
|
|
||||||
require "system_command"
|
require "system_command"
|
||||||
require "tap"
|
require "tap"
|
||||||
require "unversioned_cask_checker"
|
require "unversioned_cask_checker"
|
||||||
|
|||||||
@ -3,7 +3,6 @@
|
|||||||
|
|
||||||
require "abstract_command"
|
require "abstract_command"
|
||||||
require "bump_version_parser"
|
require "bump_version_parser"
|
||||||
require "cli/parser"
|
|
||||||
require "livecheck/livecheck"
|
require "livecheck/livecheck"
|
||||||
|
|
||||||
module Homebrew
|
module Homebrew
|
||||||
|
|||||||
@ -2,7 +2,6 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require "abstract_command"
|
require "abstract_command"
|
||||||
require "cli/parser"
|
|
||||||
require "fileutils"
|
require "fileutils"
|
||||||
|
|
||||||
module Homebrew
|
module Homebrew
|
||||||
|
|||||||
@ -3,7 +3,6 @@
|
|||||||
|
|
||||||
require "abstract_command"
|
require "abstract_command"
|
||||||
require "commands"
|
require "commands"
|
||||||
require "cli/parser"
|
|
||||||
|
|
||||||
module Homebrew
|
module Homebrew
|
||||||
module DevCmd
|
module DevCmd
|
||||||
|
|||||||
@ -2,7 +2,6 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require "abstract_command"
|
require "abstract_command"
|
||||||
require "cli/parser"
|
|
||||||
require "csv"
|
require "csv"
|
||||||
|
|
||||||
module Homebrew
|
module Homebrew
|
||||||
|
|||||||
@ -4,7 +4,6 @@
|
|||||||
require "formula"
|
require "formula"
|
||||||
require "formula_creator"
|
require "formula_creator"
|
||||||
require "missing_formula"
|
require "missing_formula"
|
||||||
require "cli/parser"
|
|
||||||
require "utils/pypi"
|
require "utils/pypi"
|
||||||
require "cask/cask_loader"
|
require "cask/cask_loader"
|
||||||
|
|
||||||
|
|||||||
@ -2,7 +2,6 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require "abstract_command"
|
require "abstract_command"
|
||||||
require "cli/parser"
|
|
||||||
require "test_runner_formula"
|
require "test_runner_formula"
|
||||||
require "github_runner_matrix"
|
require "github_runner_matrix"
|
||||||
|
|
||||||
|
|||||||
@ -2,7 +2,6 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require "abstract_command"
|
require "abstract_command"
|
||||||
require "cli/parser"
|
|
||||||
require "utils/github"
|
require "utils/github"
|
||||||
|
|
||||||
module Homebrew
|
module Homebrew
|
||||||
|
|||||||
@ -3,7 +3,6 @@
|
|||||||
|
|
||||||
require "abstract_command"
|
require "abstract_command"
|
||||||
require "formula"
|
require "formula"
|
||||||
require "cli/parser"
|
|
||||||
|
|
||||||
module Homebrew
|
module Homebrew
|
||||||
module DevCmd
|
module DevCmd
|
||||||
|
|||||||
@ -2,7 +2,6 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require "abstract_command"
|
require "abstract_command"
|
||||||
require "cli/parser"
|
|
||||||
require "utils/git"
|
require "utils/git"
|
||||||
require "formulary"
|
require "formulary"
|
||||||
require "software_spec"
|
require "software_spec"
|
||||||
|
|||||||
@ -3,7 +3,6 @@
|
|||||||
|
|
||||||
require "abstract_command"
|
require "abstract_command"
|
||||||
require "formula"
|
require "formula"
|
||||||
require "cli/parser"
|
|
||||||
|
|
||||||
module Homebrew
|
module Homebrew
|
||||||
module DevCmd
|
module DevCmd
|
||||||
|
|||||||
@ -2,7 +2,6 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require "abstract_command"
|
require "abstract_command"
|
||||||
require "cli/parser"
|
|
||||||
require "cask/cask"
|
require "cask/cask"
|
||||||
require "fileutils"
|
require "fileutils"
|
||||||
require "formula"
|
require "formula"
|
||||||
|
|||||||
@ -2,7 +2,6 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require "abstract_command"
|
require "abstract_command"
|
||||||
require "cli/parser"
|
|
||||||
require "fileutils"
|
require "fileutils"
|
||||||
require "formula"
|
require "formula"
|
||||||
|
|
||||||
|
|||||||
@ -2,7 +2,6 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require "abstract_command"
|
require "abstract_command"
|
||||||
require "cli/parser"
|
|
||||||
|
|
||||||
module Homebrew
|
module Homebrew
|
||||||
module DevCmd
|
module DevCmd
|
||||||
|
|||||||
@ -4,7 +4,6 @@
|
|||||||
require "abstract_command"
|
require "abstract_command"
|
||||||
require "formulary"
|
require "formulary"
|
||||||
require "cask/cask_loader"
|
require "cask/cask_loader"
|
||||||
require "cli/parser"
|
|
||||||
|
|
||||||
class String
|
class String
|
||||||
def f(*args)
|
def f(*args)
|
||||||
|
|||||||
@ -4,7 +4,6 @@
|
|||||||
require "abstract_command"
|
require "abstract_command"
|
||||||
require "cache_store"
|
require "cache_store"
|
||||||
require "linkage_checker"
|
require "linkage_checker"
|
||||||
require "cli/parser"
|
|
||||||
|
|
||||||
module Homebrew
|
module Homebrew
|
||||||
module DevCmd
|
module DevCmd
|
||||||
|
|||||||
@ -2,7 +2,6 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require "abstract_command"
|
require "abstract_command"
|
||||||
require "cli/parser"
|
|
||||||
require "formula"
|
require "formula"
|
||||||
require "livecheck/livecheck"
|
require "livecheck/livecheck"
|
||||||
require "livecheck/strategy"
|
require "livecheck/strategy"
|
||||||
|
|||||||
@ -2,7 +2,6 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require "abstract_command"
|
require "abstract_command"
|
||||||
require "cli/parser"
|
|
||||||
require "utils/github"
|
require "utils/github"
|
||||||
|
|
||||||
module Homebrew
|
module Homebrew
|
||||||
|
|||||||
@ -2,7 +2,6 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require "abstract_command"
|
require "abstract_command"
|
||||||
require "cli/parser"
|
|
||||||
require "utils/github"
|
require "utils/github"
|
||||||
|
|
||||||
module Homebrew
|
module Homebrew
|
||||||
|
|||||||
@ -2,7 +2,6 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require "abstract_command"
|
require "abstract_command"
|
||||||
require "cli/parser"
|
|
||||||
require "fileutils"
|
require "fileutils"
|
||||||
require "utils/github"
|
require "utils/github"
|
||||||
require "utils/github/artifacts"
|
require "utils/github/artifacts"
|
||||||
|
|||||||
@ -2,7 +2,6 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require "abstract_command"
|
require "abstract_command"
|
||||||
require "cli/parser"
|
|
||||||
require "formula"
|
require "formula"
|
||||||
require "github_packages"
|
require "github_packages"
|
||||||
require "github_releases"
|
require "github_releases"
|
||||||
|
|||||||
@ -2,7 +2,6 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require "abstract_command"
|
require "abstract_command"
|
||||||
require "cli/parser"
|
|
||||||
|
|
||||||
module Homebrew
|
module Homebrew
|
||||||
module DevCmd
|
module DevCmd
|
||||||
|
|||||||
@ -2,7 +2,6 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require "abstract_command"
|
require "abstract_command"
|
||||||
require "cli/parser"
|
|
||||||
|
|
||||||
module Homebrew
|
module Homebrew
|
||||||
module DevCmd
|
module DevCmd
|
||||||
|
|||||||
@ -2,7 +2,6 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require "abstract_command"
|
require "abstract_command"
|
||||||
require "cli/parser"
|
|
||||||
|
|
||||||
module Homebrew
|
module Homebrew
|
||||||
module DevCmd
|
module DevCmd
|
||||||
|
|||||||
@ -4,7 +4,6 @@
|
|||||||
require "abstract_command"
|
require "abstract_command"
|
||||||
require "extend/ENV"
|
require "extend/ENV"
|
||||||
require "formula"
|
require "formula"
|
||||||
require "cli/parser"
|
|
||||||
|
|
||||||
module Homebrew
|
module Homebrew
|
||||||
module DevCmd
|
module DevCmd
|
||||||
|
|||||||
@ -5,7 +5,6 @@ require "abstract_command"
|
|||||||
require "json"
|
require "json"
|
||||||
require "open3"
|
require "open3"
|
||||||
require "style"
|
require "style"
|
||||||
require "cli/parser"
|
|
||||||
|
|
||||||
module Homebrew
|
module Homebrew
|
||||||
module DevCmd
|
module DevCmd
|
||||||
|
|||||||
@ -4,7 +4,6 @@
|
|||||||
require "abstract_command"
|
require "abstract_command"
|
||||||
require "fileutils"
|
require "fileutils"
|
||||||
require "tap"
|
require "tap"
|
||||||
require "cli/parser"
|
|
||||||
|
|
||||||
module Homebrew
|
module Homebrew
|
||||||
module DevCmd
|
module DevCmd
|
||||||
|
|||||||
@ -5,7 +5,6 @@ require "abstract_command"
|
|||||||
require "extend/ENV"
|
require "extend/ENV"
|
||||||
require "sandbox"
|
require "sandbox"
|
||||||
require "timeout"
|
require "timeout"
|
||||||
require "cli/parser"
|
|
||||||
|
|
||||||
module Homebrew
|
module Homebrew
|
||||||
module DevCmd
|
module DevCmd
|
||||||
|
|||||||
@ -2,7 +2,6 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require "abstract_command"
|
require "abstract_command"
|
||||||
require "cli/parser"
|
|
||||||
require "fileutils"
|
require "fileutils"
|
||||||
require "system_command"
|
require "system_command"
|
||||||
|
|
||||||
|
|||||||
@ -2,7 +2,6 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require "abstract_command"
|
require "abstract_command"
|
||||||
require "cli/parser"
|
|
||||||
require "fileutils"
|
require "fileutils"
|
||||||
|
|
||||||
module Homebrew
|
module Homebrew
|
||||||
|
|||||||
@ -2,7 +2,6 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require "abstract_command"
|
require "abstract_command"
|
||||||
require "cli/parser"
|
|
||||||
require "formula"
|
require "formula"
|
||||||
require "api"
|
require "api"
|
||||||
require "os/mac/xcode"
|
require "os/mac/xcode"
|
||||||
|
|||||||
@ -5,7 +5,6 @@ require "abstract_command"
|
|||||||
require "fileutils"
|
require "fileutils"
|
||||||
require "stringio"
|
require "stringio"
|
||||||
require "formula"
|
require "formula"
|
||||||
require "cli/parser"
|
|
||||||
|
|
||||||
module Homebrew
|
module Homebrew
|
||||||
module DevCmd
|
module DevCmd
|
||||||
|
|||||||
@ -2,7 +2,6 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require "abstract_command"
|
require "abstract_command"
|
||||||
require "cli/parser"
|
|
||||||
require "utils/spdx"
|
require "utils/spdx"
|
||||||
require "system_command"
|
require "system_command"
|
||||||
|
|
||||||
|
|||||||
@ -2,7 +2,6 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require "abstract_command"
|
require "abstract_command"
|
||||||
require "cli/parser"
|
|
||||||
require "utils/github"
|
require "utils/github"
|
||||||
require "manpages"
|
require "manpages"
|
||||||
require "system_command"
|
require "system_command"
|
||||||
|
|||||||
@ -2,7 +2,6 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require "abstract_command"
|
require "abstract_command"
|
||||||
require "cli/parser"
|
|
||||||
require "utils/pypi"
|
require "utils/pypi"
|
||||||
|
|
||||||
module Homebrew
|
module Homebrew
|
||||||
|
|||||||
@ -2,7 +2,6 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require "abstract_command"
|
require "abstract_command"
|
||||||
require "cli/parser"
|
|
||||||
require "utils/github"
|
require "utils/github"
|
||||||
require "system_command"
|
require "system_command"
|
||||||
|
|
||||||
|
|||||||
@ -2,7 +2,6 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require "abstract_command"
|
require "abstract_command"
|
||||||
require "cli/parser"
|
|
||||||
require "fileutils"
|
require "fileutils"
|
||||||
|
|
||||||
module Homebrew
|
module Homebrew
|
||||||
|
|||||||
@ -2,7 +2,6 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require "abstract_command"
|
require "abstract_command"
|
||||||
require "cli/parser"
|
|
||||||
|
|
||||||
module Homebrew
|
module Homebrew
|
||||||
module DevCmd
|
module DevCmd
|
||||||
|
|||||||
@ -1,8 +1,9 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require "cmd/analytics"
|
||||||
require "cmd/shared_examples/args_parse"
|
require "cmd/shared_examples/args_parse"
|
||||||
|
|
||||||
RSpec.describe "brew analytics" do
|
RSpec.describe Homebrew::Cmd::Analytics do
|
||||||
it_behaves_like "parseable arguments"
|
it_behaves_like "parseable arguments"
|
||||||
|
|
||||||
it "when HOMEBREW_NO_ANALYTICS is unset is disabled after running `brew analytics off`", :integration_test do
|
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
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require "cmd/autoremove"
|
||||||
require "cmd/shared_examples/args_parse"
|
require "cmd/shared_examples/args_parse"
|
||||||
|
|
||||||
RSpec.describe "brew autoremove" do
|
RSpec.describe Homebrew::Cmd::Autoremove do
|
||||||
it_behaves_like "parseable arguments"
|
it_behaves_like "parseable arguments"
|
||||||
|
|
||||||
describe "integration test" do
|
describe "integration test" do
|
||||||
|
|||||||
@ -1,8 +1,9 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require "cmd/cleanup"
|
||||||
require "cmd/shared_examples/args_parse"
|
require "cmd/shared_examples/args_parse"
|
||||||
|
|
||||||
RSpec.describe "brew cleanup" do
|
RSpec.describe Homebrew::Cmd::CleanupCmd do
|
||||||
before do
|
before do
|
||||||
FileUtils.mkdir_p HOMEBREW_LIBRARY/"Homebrew/vendor/"
|
FileUtils.mkdir_p HOMEBREW_LIBRARY/"Homebrew/vendor/"
|
||||||
FileUtils.touch HOMEBREW_LIBRARY/"Homebrew/vendor/portable-ruby-version"
|
FileUtils.touch HOMEBREW_LIBRARY/"Homebrew/vendor/portable-ruby-version"
|
||||||
|
|||||||
@ -1,11 +1,9 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require "cmd/commands"
|
require "cmd/commands"
|
||||||
require "fileutils"
|
|
||||||
|
|
||||||
require "cmd/shared_examples/args_parse"
|
require "cmd/shared_examples/args_parse"
|
||||||
|
|
||||||
RSpec.describe "brew commands" do
|
RSpec.describe Homebrew::Cmd::CommandsCmd do
|
||||||
it_behaves_like "parseable arguments"
|
it_behaves_like "parseable arguments"
|
||||||
|
|
||||||
it "prints a list of all available commands", :integration_test do
|
it "prints a list of all available commands", :integration_test do
|
||||||
|
|||||||
@ -1,8 +1,9 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require "cmd/completions"
|
||||||
require "cmd/shared_examples/args_parse"
|
require "cmd/shared_examples/args_parse"
|
||||||
|
|
||||||
RSpec.describe "brew completions" do
|
RSpec.describe Homebrew::Cmd::CompletionsCmd do
|
||||||
it_behaves_like "parseable arguments"
|
it_behaves_like "parseable arguments"
|
||||||
|
|
||||||
it "runs the status subcommand correctly", :integration_test do
|
it "runs the status subcommand correctly", :integration_test do
|
||||||
|
|||||||
@ -1,8 +1,9 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require "cmd/config"
|
||||||
require "cmd/shared_examples/args_parse"
|
require "cmd/shared_examples/args_parse"
|
||||||
|
|
||||||
RSpec.describe "brew config" do
|
RSpec.describe Homebrew::Cmd::Config do
|
||||||
it_behaves_like "parseable arguments"
|
it_behaves_like "parseable arguments"
|
||||||
|
|
||||||
it "prints information about the current Homebrew configuration", :integration_test do
|
it "prints information about the current Homebrew configuration", :integration_test do
|
||||||
|
|||||||
@ -1,8 +1,9 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require "cmd/deps"
|
||||||
require "cmd/shared_examples/args_parse"
|
require "cmd/shared_examples/args_parse"
|
||||||
|
|
||||||
RSpec.describe "brew deps" do
|
RSpec.describe Homebrew::Cmd::Deps do
|
||||||
it_behaves_like "parseable arguments"
|
it_behaves_like "parseable arguments"
|
||||||
|
|
||||||
it "outputs all of a Formula's dependencies and their dependencies on separate lines", :integration_test do
|
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
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require "cmd/desc"
|
||||||
require "cmd/shared_examples/args_parse"
|
require "cmd/shared_examples/args_parse"
|
||||||
|
|
||||||
RSpec.describe "brew desc" do
|
RSpec.describe Homebrew::Cmd::Desc do
|
||||||
it_behaves_like "parseable arguments"
|
it_behaves_like "parseable arguments"
|
||||||
|
|
||||||
it "shows a given Formula's description", :integration_test do
|
it "shows a given Formula's description", :integration_test do
|
||||||
|
|||||||
@ -1,7 +1,8 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require "cmd/developer"
|
||||||
require "cmd/shared_examples/args_parse"
|
require "cmd/shared_examples/args_parse"
|
||||||
|
|
||||||
RSpec.describe "brew developer" do
|
RSpec.describe Homebrew::Cmd::Developer do
|
||||||
it_behaves_like "parseable arguments"
|
it_behaves_like "parseable arguments"
|
||||||
end
|
end
|
||||||
|
|||||||
@ -1,6 +1,11 @@
|
|||||||
# frozen_string_literal: true
|
# 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
|
it "opens the docs page", :integration_test do
|
||||||
expect { brew "docs", "HOMEBREW_BROWSER" => "echo" }
|
expect { brew "docs", "HOMEBREW_BROWSER" => "echo" }
|
||||||
.to output("https://docs.brew.sh\n").to_stdout
|
.to output("https://docs.brew.sh\n").to_stdout
|
||||||
|
|||||||
@ -1,8 +1,9 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require "cmd/doctor"
|
||||||
require "cmd/shared_examples/args_parse"
|
require "cmd/shared_examples/args_parse"
|
||||||
|
|
||||||
RSpec.describe "brew doctor" do
|
RSpec.describe Homebrew::Cmd::Doctor do
|
||||||
it_behaves_like "parseable arguments"
|
it_behaves_like "parseable arguments"
|
||||||
|
|
||||||
specify "check_integration_test", :integration_test do
|
specify "check_integration_test", :integration_test do
|
||||||
|
|||||||
@ -1,8 +1,9 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require "cmd/fetch"
|
||||||
require "cmd/shared_examples/args_parse"
|
require "cmd/shared_examples/args_parse"
|
||||||
|
|
||||||
RSpec.describe "brew fetch" do
|
RSpec.describe Homebrew::Cmd::FetchCmd do
|
||||||
it_behaves_like "parseable arguments"
|
it_behaves_like "parseable arguments"
|
||||||
|
|
||||||
it "downloads the Formula's URL", :integration_test do
|
it "downloads the Formula's URL", :integration_test do
|
||||||
|
|||||||
@ -1,7 +1,8 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require "cmd/gist-logs"
|
||||||
require "cmd/shared_examples/args_parse"
|
require "cmd/shared_examples/args_parse"
|
||||||
|
|
||||||
RSpec.describe "brew gist-logs" do
|
RSpec.describe Homebrew::Cmd::GistLogs do
|
||||||
it_behaves_like "parseable arguments"
|
it_behaves_like "parseable arguments"
|
||||||
end
|
end
|
||||||
|
|||||||
@ -1,6 +1,11 @@
|
|||||||
# frozen_string_literal: true
|
# 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
|
describe "help" do
|
||||||
it "prints help for a documented Ruby command" do
|
it "prints help for a documented Ruby command" do
|
||||||
expect { brew "help", "cat" }
|
expect { brew "help", "cat" }
|
||||||
|
|||||||
@ -1,8 +1,9 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require "cmd/home"
|
||||||
require "cmd/shared_examples/args_parse"
|
require "cmd/shared_examples/args_parse"
|
||||||
|
|
||||||
RSpec.describe "brew home" do
|
RSpec.describe Homebrew::Cmd::Home do
|
||||||
let(:testballhome_homepage) do
|
let(:testballhome_homepage) do
|
||||||
Formula["testballhome"].homepage
|
Formula["testballhome"].homepage
|
||||||
end
|
end
|
||||||
|
|||||||
@ -1,10 +1,9 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require "cmd/info"
|
require "cmd/info"
|
||||||
|
|
||||||
require "cmd/shared_examples/args_parse"
|
require "cmd/shared_examples/args_parse"
|
||||||
|
|
||||||
RSpec.describe "brew info" do
|
RSpec.describe Homebrew::Cmd::Info do
|
||||||
it_behaves_like "parseable arguments"
|
it_behaves_like "parseable arguments"
|
||||||
|
|
||||||
it "prints as json with the --json=v1 flag", :integration_test do
|
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
|
.and be_a_success
|
||||||
end
|
end
|
||||||
|
|
||||||
describe Homebrew do
|
describe "::github_remote_path" do
|
||||||
describe "::github_remote_path" do
|
let(:remote) { "https://github.com/Homebrew/homebrew-core" }
|
||||||
let(:remote) { "https://github.com/Homebrew/homebrew-core" }
|
|
||||||
|
|
||||||
specify "returns correct URLs" do
|
specify "returns correct URLs" do
|
||||||
expect(described_class.github_remote_path(remote, "Formula/git.rb"))
|
expect(described_class.new([]).github_remote_path(remote, "Formula/git.rb"))
|
||||||
.to eq("https://github.com/Homebrew/homebrew-core/blob/HEAD/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"))
|
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")
|
.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"))
|
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")
|
.to eq("https://github.com/user/repo/blob/HEAD/foo.rb")
|
||||||
|
|
||||||
expect(described_class.github_remote_path("https://mywebsite.com", "foo/bar.rb"))
|
expect(described_class.new([]).github_remote_path("https://mywebsite.com", "foo/bar.rb"))
|
||||||
.to eq("https://mywebsite.com/foo/bar.rb")
|
.to eq("https://mywebsite.com/foo/bar.rb")
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@ -1,8 +1,9 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require "cmd/install"
|
||||||
require "cmd/shared_examples/args_parse"
|
require "cmd/shared_examples/args_parse"
|
||||||
|
|
||||||
RSpec.describe "brew install" do
|
RSpec.describe Homebrew::Cmd::InstallCmd do
|
||||||
it_behaves_like "parseable arguments"
|
it_behaves_like "parseable arguments"
|
||||||
|
|
||||||
it "installs formulae", :integration_test do
|
it "installs formulae", :integration_test do
|
||||||
|
|||||||
@ -1,8 +1,9 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require "cmd/leaves"
|
||||||
require "cmd/shared_examples/args_parse"
|
require "cmd/shared_examples/args_parse"
|
||||||
|
|
||||||
RSpec.describe "brew leaves" do
|
RSpec.describe Homebrew::Cmd::Leaves do
|
||||||
it_behaves_like "parseable arguments"
|
it_behaves_like "parseable arguments"
|
||||||
|
|
||||||
context "when there are no installed Formulae", :integration_test do
|
context "when there are no installed Formulae", :integration_test do
|
||||||
|
|||||||
@ -1,8 +1,9 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require "cmd/link"
|
||||||
require "cmd/shared_examples/args_parse"
|
require "cmd/shared_examples/args_parse"
|
||||||
|
|
||||||
RSpec.describe "brew link" do
|
RSpec.describe Homebrew::Cmd::Link do
|
||||||
it_behaves_like "parseable arguments"
|
it_behaves_like "parseable arguments"
|
||||||
|
|
||||||
it "links a given Formula", :integration_test do
|
it "links a given Formula", :integration_test do
|
||||||
|
|||||||
@ -70,7 +70,7 @@ RSpec.describe FormulaInstaller do
|
|||||||
# rubocop:disable RSpec/NoExpectationExample
|
# rubocop:disable RSpec/NoExpectationExample
|
||||||
specify "basic bottle install" do
|
specify "basic bottle install" do
|
||||||
allow(DevelopmentTools).to receive(:installed?).and_return(false)
|
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|
|
temporarily_install_bottle(TestballBottle.new) do |f|
|
||||||
test_basic_formula_setup(f)
|
test_basic_formula_setup(f)
|
||||||
end
|
end
|
||||||
@ -79,7 +79,7 @@ RSpec.describe FormulaInstaller do
|
|||||||
|
|
||||||
specify "basic bottle install with cellar information on sha256 line" do
|
specify "basic bottle install with cellar information on sha256 line" do
|
||||||
allow(DevelopmentTools).to receive(:installed?).and_return(false)
|
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|
|
temporarily_install_bottle(TestballBottleCellar.new) do |f|
|
||||||
test_basic_formula_setup(f)
|
test_basic_formula_setup(f)
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user