Move o* output methods to Utils::Output

This reduces the surface area of our `Kernel` monkeypatch and removes
the need to `include Kernel` in a bunch of modules.

While we're here, also move `Kernel#require?` to `Homebrew` and fully
scope the calls to it.
This commit is contained in:
Mike McQuaid 2025-08-20 19:20:19 +01:00
parent fc85e051aa
commit a1f112f3fe
No known key found for this signature in database
116 changed files with 735 additions and 419 deletions

View File

@ -74,6 +74,7 @@ Style/Documentation:
- resource.rb - resource.rb
- startup/config.rb - startup/config.rb
- utils/inreplace.rb - utils/inreplace.rb
- utils/output.rb
- utils/shebang.rb - utils/shebang.rb
- utils/string_inreplace_extension.rb - utils/string_inreplace_extension.rb
- version.rb - version.rb

View File

@ -3,6 +3,7 @@
require "cli/parser" require "cli/parser"
require "shell_command" require "shell_command"
require "utils/output"
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`
@ -19,6 +20,7 @@ module Homebrew
# @api public # @api public
class AbstractCommand class AbstractCommand
extend T::Helpers extend T::Helpers
include Utils::Output::Mixin
abstract! abstract!

View File

@ -2,10 +2,13 @@
# frozen_string_literal: true # frozen_string_literal: true
require "fileutils" require "fileutils"
require "utils/output"
module Homebrew module Homebrew
module Aliases module Aliases
class Alias class Alias
include ::Utils::Output::Mixin
sig { returns(String) } sig { returns(String) }
attr_accessor :name attr_accessor :name

View File

@ -2,9 +2,12 @@
# frozen_string_literal: true # frozen_string_literal: true
require "aliases/alias" require "aliases/alias"
require "utils/output"
module Homebrew module Homebrew
module Aliases module Aliases
extend Utils::Output::Mixin
RESERVED = T.let(( RESERVED = T.let((
Commands.internal_commands + Commands.internal_commands +
Commands.internal_developer_commands + Commands.internal_developer_commands +

View File

@ -6,10 +6,13 @@ require "api/cask"
require "api/formula" require "api/formula"
require "api/internal" require "api/internal"
require "base64" require "base64"
require "utils/output"
module Homebrew module Homebrew
# Helper functions for using Homebrew's formulae.brew.sh API. # Helper functions for using Homebrew's formulae.brew.sh API.
module API module API
extend Utils::Output::Mixin
extend Cachable extend Cachable
HOMEBREW_CACHE_API = T.let((HOMEBREW_CACHE/"api").freeze, Pathname) HOMEBREW_CACHE_API = T.let((HOMEBREW_CACHE/"api").freeze, Pathname)

View File

@ -7,10 +7,12 @@ require "utils/popen"
require "utils/github/api" require "utils/github/api"
require "exceptions" require "exceptions"
require "system_command" require "system_command"
require "utils/output"
module Homebrew module Homebrew
module Attestation module Attestation
extend SystemCommand::Mixin extend SystemCommand::Mixin
extend Utils::Output::Mixin
# @api private # @api private
HOMEBREW_CORE_REPO = "Homebrew/homebrew-core" HOMEBREW_CORE_REPO = "Homebrew/homebrew-core"

View File

@ -16,6 +16,7 @@ end
std_trap = trap("INT") { exit! 130 } # no backtrace thanks std_trap = trap("INT") { exit! 130 } # no backtrace thanks
require_relative "global" require_relative "global"
require "utils/output"
begin begin
trap("INT", std_trap) # restore default CTRL-C handler trap("INT", std_trap) # restore default CTRL-C handler
@ -115,14 +116,14 @@ begin
converted_cmd = cmd.downcase.tr("-", "_") converted_cmd = cmd.downcase.tr("-", "_")
case_error = "undefined method `#{converted_cmd}' for module Homebrew" case_error = "undefined method `#{converted_cmd}' for module Homebrew"
private_method_error = "private method `#{converted_cmd}' called for module Homebrew" private_method_error = "private method `#{converted_cmd}' called for module Homebrew"
odie "Unknown command: brew #{cmd}" if [case_error, private_method_error].include?(e.message) Utils::Output.odie "Unknown command: brew #{cmd}" if [case_error, private_method_error].include?(e.message)
raise raise
end end
end end
elsif (path = Commands.external_ruby_cmd_path(cmd)) elsif (path = Commands.external_ruby_cmd_path(cmd))
Homebrew.running_command = cmd Homebrew.running_command = cmd
require?(path) Homebrew.require?(path)
exit Homebrew.failed? ? 1 : 0 exit Homebrew.failed? ? 1 : 0
elsif Commands.external_cmd_path(cmd) elsif Commands.external_cmd_path(cmd)
%w[CACHE LIBRARY_PATH].each do |env| %w[CACHE LIBRARY_PATH].each do |env|
@ -139,14 +140,16 @@ begin
possible_tap.installed? || possible_tap.installed? ||
(blocked_tap = Tap.untapped_official_taps.include?(possible_tap.name)) (blocked_tap = Tap.untapped_official_taps.include?(possible_tap.name))
if blocked_tap if blocked_tap
onoe <<~EOS Utils::Output.onoe <<~EOS
`brew #{cmd}` is unavailable because #{possible_tap.name} was manually untapped. `brew #{cmd}` is unavailable because #{possible_tap.name} was manually untapped.
Run `brew tap #{possible_tap.name}` to reenable `brew #{cmd}`. Run `brew tap #{possible_tap.name}` to reenable `brew #{cmd}`.
EOS EOS
end end
# Check for cask explicitly because it's very common in old guides # Check for cask explicitly because it's very common in old guides
odie "`brew cask` is no longer a `brew` command. Use `brew <command> --cask` instead." if cmd == "cask" if cmd == "cask"
odie "Unknown command: brew #{cmd}" Utils::Output.odie "`brew cask` is no longer a `brew` command. Use `brew <command> --cask` instead."
end
Utils::Output.odie "Unknown command: brew #{cmd}"
end end
# Unset HOMEBREW_HELP to avoid confusing the tap # Unset HOMEBREW_HELP to avoid confusing the tap
@ -171,7 +174,7 @@ rescue UsageError => e
require "help" require "help"
Homebrew::Help.help cmd, remaining_args: args&.remaining || [], usage_error: e.message Homebrew::Help.help cmd, remaining_args: args&.remaining || [], usage_error: e.message
rescue SystemExit => e rescue SystemExit => e
onoe "Kernel.exit" if args&.debug? && !e.success? Utils::Output.onoe "Kernel.exit" if args&.debug? && !e.success?
if args&.debug? || ARGV.include?("--debug") if args&.debug? || ARGV.include?("--debug")
require "utils/backtrace" require "utils/backtrace"
$stderr.puts Utils::Backtrace.clean(e) $stderr.puts Utils::Backtrace.clean(e)
@ -209,7 +212,7 @@ rescue BuildError => e
rescue RuntimeError, SystemCallError => e rescue RuntimeError, SystemCallError => e
raise if e.message.empty? raise if e.message.empty?
onoe e Utils::Output.onoe e
if args&.debug? || ARGV.include?("--debug") if args&.debug? || ARGV.include?("--debug")
require "utils/backtrace" require "utils/backtrace"
$stderr.puts Utils::Backtrace.clean(e) $stderr.puts Utils::Backtrace.clean(e)
@ -218,7 +221,7 @@ rescue RuntimeError, SystemCallError => e
exit 1 exit 1
# Catch any other types of exceptions. # Catch any other types of exceptions.
rescue Exception => e # rubocop:disable Lint/RescueException rescue Exception => e # rubocop:disable Lint/RescueException
onoe e Utils::Output.onoe e
method_deprecated_error = e.is_a?(MethodDeprecatedError) method_deprecated_error = e.is_a?(MethodDeprecatedError)
require "utils/backtrace" require "utils/backtrace"

View File

@ -16,9 +16,12 @@ require "fcntl"
require "utils/socket" require "utils/socket"
require "cmd/install" require "cmd/install"
require "json/add/exception" require "json/add/exception"
require "utils/output"
# A formula build. # A formula build.
class Build class Build
include Utils::Output::Mixin
attr_reader :formula, :deps, :reqs, :args attr_reader :formula, :deps, :reqs, :args
def initialize(formula, options, args:) def initialize(formula, options, args:)

View File

@ -6,11 +6,14 @@ require "exceptions"
require "extend/ENV" require "extend/ENV"
require "utils" require "utils"
require "PATH" require "PATH"
require "utils/output"
module Homebrew module Homebrew
module Bundle module Bundle
module Commands module Commands
module Exec module Exec
extend Utils::Output::Mixin
PATH_LIKE_ENV_REGEX = /.+#{File::PATH_SEPARATOR}/ PATH_LIKE_ENV_REGEX = /.+#{File::PATH_SEPARATOR}/
sig { sig {

View File

@ -3,11 +3,14 @@
require "json" require "json"
require "tsort" require "tsort"
require "utils/output"
module Homebrew module Homebrew
module Bundle module Bundle
# TODO: refactor into multiple modules # TODO: refactor into multiple modules
module FormulaDumper module FormulaDumper
extend Utils::Output::Mixin
def self.reset! def self.reset!
require "bundle/brew_services" require "bundle/brew_services"

View File

@ -1,9 +1,13 @@
# typed: true # rubocop:todo Sorbet/StrictSigil # typed: true # rubocop:todo Sorbet/StrictSigil
# frozen_string_literal: true # frozen_string_literal: true
require "utils/output"
module Homebrew module Homebrew
module Bundle module Bundle
module Remover module Remover
extend ::Utils::Output::Mixin
def self.remove(*args, type:, global:, file:) def self.remove(*args, type:, global:, file:)
require "bundle/brewfile" require "bundle/brewfile"
require "bundle/dumper" require "bundle/dumper"

View File

@ -1,9 +1,13 @@
# typed: strict # typed: strict
# frozen_string_literal: true # frozen_string_literal: true
require "utils/output"
module Homebrew module Homebrew
module Bundle module Bundle
module WhalebrewDumper module WhalebrewDumper
extend Utils::Output::Mixin
sig { void } sig { void }
def self.reset! def self.reset!
@images = T.let(nil, T.nilable(T::Array[String])) @images = T.let(nil, T.nilable(T::Array[String]))

View File

@ -1,9 +1,13 @@
# typed: true # rubocop:todo Sorbet/StrictSigil # typed: true # rubocop:todo Sorbet/StrictSigil
# frozen_string_literal: true # frozen_string_literal: true
require "utils/output"
module Homebrew module Homebrew
module Bundle module Bundle
module WhalebrewInstaller module WhalebrewInstaller
extend Utils::Output::Mixin
def self.reset! def self.reset!
@installed_images = nil @installed_images = nil
end end

View File

@ -2,16 +2,19 @@
# frozen_string_literal: true # frozen_string_literal: true
require "extend/object/deep_dup" require "extend/object/deep_dup"
require "utils/output"
module Cask module Cask
module Artifact module Artifact
# Abstract superclass for all artifacts. # Abstract superclass for all artifacts.
class AbstractArtifact class AbstractArtifact
extend T::Helpers extend T::Helpers
extend ::Utils::Output::Mixin
abstract! abstract!
include Comparable include Comparable
include ::Utils::Output::Mixin
def self.english_name def self.english_name
@english_name ||= T.must(name).sub(/^.*:/, "").gsub(/(.)([A-Z])/, '\1 \2') @english_name ||= T.must(name).sub(/^.*:/, "").gsub(/(.)([A-Z])/, '\1 \2')

View File

@ -14,12 +14,14 @@ require "formula_name_cask_token_auditor"
require "utils/curl" require "utils/curl"
require "utils/git" require "utils/git"
require "utils/shared_audits" require "utils/shared_audits"
require "utils/output"
module Cask module Cask
# Audit a cask for various problems. # Audit a cask for various problems.
class Audit class Audit
include SystemCommand::Mixin include SystemCommand::Mixin
include ::Utils::Curl include ::Utils::Curl
include ::Utils::Output::Mixin
Error = T.type_alias do Error = T.type_alias do
{ {

View File

@ -2,10 +2,13 @@
# frozen_string_literal: true # frozen_string_literal: true
require "cask/audit" require "cask/audit"
require "utils/output"
module Cask module Cask
# Helper class for auditing all available languages of a cask. # Helper class for auditing all available languages of a cask.
class Auditor class Auditor
include ::Utils::Output::Mixin
# TODO: use argument forwarding (...) when Sorbet supports it in strict mode # TODO: use argument forwarding (...) when Sorbet supports it in strict mode
sig { sig {
params( params(

View File

@ -8,6 +8,7 @@ require "cask/dsl"
require "cask/metadata" require "cask/metadata"
require "cask/tab" require "cask/tab"
require "utils/bottles" require "utils/bottles"
require "utils/output"
require "api_hashable" require "api_hashable"
module Cask module Cask
@ -15,6 +16,7 @@ module Cask
class Cask class Cask
extend Forwardable extend Forwardable
extend APIHashable extend APIHashable
extend ::Utils::Output::Mixin
include Metadata include Metadata
# The token of this {Cask}. # The token of this {Cask}.

View File

@ -5,6 +5,7 @@ require "cask/cache"
require "cask/cask" require "cask/cask"
require "uri" require "uri"
require "utils/curl" require "utils/curl"
require "utils/output"
require "extend/hash/keys" require "extend/hash/keys"
require "api" require "api"
@ -12,12 +13,14 @@ module Cask
# Loads a cask from various sources. # Loads a cask from various sources.
module CaskLoader module CaskLoader
extend Context extend Context
extend ::Utils::Output::Mixin
ALLOWED_URL_SCHEMES = %w[file].freeze ALLOWED_URL_SCHEMES = %w[file].freeze
private_constant :ALLOWED_URL_SCHEMES private_constant :ALLOWED_URL_SCHEMES
module ILoader module ILoader
extend T::Helpers extend T::Helpers
include ::Utils::Output::Mixin
interface! interface!

View File

@ -2,12 +2,15 @@
# frozen_string_literal: true # frozen_string_literal: true
require "utils/user" require "utils/user"
require "utils/output"
module Cask module Cask
# Helper functions for interacting with the `Caskroom` directory. # Helper functions for interacting with the `Caskroom` directory.
# #
# @api internal # @api internal
module Caskroom module Caskroom
extend ::Utils::Output::Mixin
sig { returns(Pathname) } sig { returns(Pathname) }
def self.path def self.path
@path ||= T.let(HOMEBREW_PREFIX/"Caskroom", T.nilable(Pathname)) @path ||= T.let(HOMEBREW_PREFIX/"Caskroom", T.nilable(Pathname))

View File

@ -5,6 +5,7 @@ require "autobump_constants"
require "locale" require "locale"
require "lazy_object" require "lazy_object"
require "livecheck" require "livecheck"
require "utils/output"
require "cask/artifact" require "cask/artifact"
require "cask/artifact_set" require "cask/artifact_set"
@ -32,6 +33,8 @@ require "on_system"
module Cask module Cask
# Class representing the domain-specific language used for casks. # Class representing the domain-specific language used for casks.
class DSL class DSL
include ::Utils::Output::Mixin
ORDINARY_ARTIFACT_CLASSES = [ ORDINARY_ARTIFACT_CLASSES = [
Artifact::Installer, Artifact::Installer,
Artifact::App, Artifact::App,

View File

@ -3,6 +3,7 @@
require "delegate" require "delegate"
require "extend/hash/keys" require "extend/hash/keys"
require "utils/output"
module Cask module Cask
class DSL class DSL
@ -23,7 +24,7 @@ module Cask
options.assert_valid_keys(*VALID_KEYS, *ODEPRECATED_KEYS) options.assert_valid_keys(*VALID_KEYS, *ODEPRECATED_KEYS)
options.keys.intersection(ODEPRECATED_KEYS).each do |key| options.keys.intersection(ODEPRECATED_KEYS).each do |key|
Kernel.odeprecated "conflicts_with #{key}:" ::Utils::Output.odeprecated "conflicts_with #{key}:"
end end
conflicts = options.transform_values { |v| Set.new(Kernel.Array(v)) } conflicts = options.transform_values { |v| Set.new(Kernel.Array(v)) }

View File

@ -3,9 +3,12 @@
require "json" require "json"
require "cmd/info" require "cmd/info"
require "utils/output"
module Cask module Cask
class Info class Info
extend ::Utils::Output::Mixin
sig { params(cask: Cask).returns(String) } sig { params(cask: Cask).returns(String) }
def self.get_info(cask) def self.get_info(cask)
require "cask/installer" require "cask/installer"

View File

@ -5,6 +5,7 @@ require "formula_installer"
require "unpack_strategy" require "unpack_strategy"
require "utils/topological_hash" require "utils/topological_hash"
require "utils/analytics" require "utils/analytics"
require "utils/output"
require "cask/config" require "cask/config"
require "cask/download" require "cask/download"
@ -15,6 +16,9 @@ require "cask/tab"
module Cask module Cask
# Installer for a {Cask}. # Installer for a {Cask}.
class Installer class Installer
extend ::Utils::Output::Mixin
include ::Utils::Output::Mixin
sig { sig {
params( params(
cask: ::Cask::Cask, command: T::Class[SystemCommand], force: T::Boolean, adopt: T::Boolean, cask: ::Cask::Cask, command: T::Class[SystemCommand], force: T::Boolean, adopt: T::Boolean,

View File

@ -2,9 +2,12 @@
# frozen_string_literal: true # frozen_string_literal: true
require "cask/artifact/relocated" require "cask/artifact/relocated"
require "utils/output"
module Cask module Cask
class List class List
extend ::Utils::Output::Mixin
sig { params(casks: Cask, one: T::Boolean, full_name: T::Boolean, versions: T::Boolean).void } sig { params(casks: Cask, one: T::Boolean, full_name: T::Boolean, versions: T::Boolean).void }
def self.list_casks(*casks, one: false, full_name: false, versions: false) def self.list_casks(*casks, one: false, full_name: false, versions: false)
output = if casks.any? output = if casks.any?

View File

@ -1,10 +1,13 @@
# typed: true # rubocop:todo Sorbet/StrictSigil # typed: true # rubocop:todo Sorbet/StrictSigil
# frozen_string_literal: true # frozen_string_literal: true
require "utils/output"
module Cask module Cask
# Helper module for reading and writing cask metadata. # Helper module for reading and writing cask metadata.
module Metadata module Metadata
extend T::Helpers extend T::Helpers
include ::Utils::Output::Mixin
METADATA_SUBDIR = ".metadata" METADATA_SUBDIR = ".metadata"
TIMESTAMP_FORMAT = "%Y%m%d%H%M%S.%L" TIMESTAMP_FORMAT = "%Y%m%d%H%M%S.%L"

View File

@ -3,9 +3,12 @@
require "cask/cask_loader" require "cask/cask_loader"
require "utils/inreplace" require "utils/inreplace"
require "utils/output"
module Cask module Cask
class Migrator class Migrator
include ::Utils::Output::Mixin
sig { returns(Cask) } sig { returns(Cask) }
attr_reader :old_cask, :new_cask attr_reader :old_cask, :new_cask

View File

@ -2,10 +2,13 @@
# frozen_string_literal: true # frozen_string_literal: true
require "cask/macos" require "cask/macos"
require "utils/output"
module Cask module Cask
# Helper class for uninstalling `.pkg` installers. # Helper class for uninstalling `.pkg` installers.
class Pkg class Pkg
include ::Utils::Output::Mixin
sig { params(regexp: String, command: T.class_of(SystemCommand)).returns(T::Array[Pkg]) } sig { params(regexp: String, command: T.class_of(SystemCommand)).returns(T::Array[Pkg]) }
def self.all_matching(regexp, command) def self.all_matching(regexp, command)
command.run("/usr/sbin/pkgutil", args: ["--pkgs=#{regexp}"]).stdout.split("\n").map do |package_id| command.run("/usr/sbin/pkgutil", args: ["--pkgs=#{regexp}"]).stdout.split("\n").map do |package_id|

View File

@ -4,11 +4,13 @@
require "development_tools" require "development_tools"
require "cask/exceptions" require "cask/exceptions"
require "system_command" require "system_command"
require "utils/output"
module Cask module Cask
# Helper module for quarantining files. # Helper module for quarantining files.
module Quarantine module Quarantine
extend SystemCommand::Mixin extend SystemCommand::Mixin
extend ::Utils::Output::Mixin
QUARANTINE_ATTRIBUTE = "com.apple.quarantine" QUARANTINE_ATTRIBUTE = "com.apple.quarantine"

View File

@ -1,8 +1,12 @@
# typed: strict # typed: strict
# frozen_string_literal: true # frozen_string_literal: true
require "utils/output"
module Cask module Cask
class Reinstall class Reinstall
extend ::Utils::Output::Mixin
sig { sig {
params( params(
casks: ::Cask::Cask, verbose: T::Boolean, force: T::Boolean, skip_cask_deps: T::Boolean, binaries: T::Boolean, casks: ::Cask::Cask, verbose: T::Boolean, force: T::Boolean, skip_cask_deps: T::Boolean, binaries: T::Boolean,

View File

@ -2,10 +2,12 @@
# frozen_string_literal: true # frozen_string_literal: true
require "utils/user" require "utils/user"
require "utils/output"
module Cask module Cask
# Helper functions for staged casks. # Helper functions for staged casks.
module Staged module Staged
include ::Utils::Output::Mixin
extend T::Helpers extend T::Helpers
requires_ancestor { ::Cask::DSL::Base } requires_ancestor { ::Cask::DSL::Base }

View File

@ -1,8 +1,12 @@
# typed: strict # typed: strict
# frozen_string_literal: true # frozen_string_literal: true
require "utils/output"
module Cask module Cask
class Uninstall class Uninstall
extend ::Utils::Output::Mixin
sig { params(casks: ::Cask::Cask, binaries: T::Boolean, force: T::Boolean, verbose: T::Boolean).void } sig { params(casks: ::Cask::Cask, binaries: T::Boolean, force: T::Boolean, verbose: T::Boolean).void }
def self.uninstall_casks(*casks, binaries: false, force: false, verbose: false) def self.uninstall_casks(*casks, binaries: false, force: false, verbose: false)
require "cask/installer" require "cask/installer"

View File

@ -3,9 +3,12 @@
require "env_config" require "env_config"
require "cask/config" require "cask/config"
require "utils/output"
module Cask module Cask
class Upgrade class Upgrade
extend ::Utils::Output::Mixin
sig { sig {
params( params(
casks: Cask, casks: Cask,

View File

@ -3,10 +3,13 @@
require "utils/user" require "utils/user"
require "open3" require "open3"
require "utils/output"
module Cask module Cask
# Helper functions for various cask operations. # Helper functions for various cask operations.
module Utils module Utils
extend ::Utils::Output::Mixin
BUG_REPORTS_URL = "https://github.com/Homebrew/homebrew-cask#reporting-bugs" BUG_REPORTS_URL = "https://github.com/Homebrew/homebrew-cask#reporting-bugs"
def self.gain_permissions_mkpath(path, command: SystemCommand) def self.gain_permissions_mkpath(path, command: SystemCommand)

View File

@ -1,6 +1,8 @@
# typed: strict # typed: strict
# frozen_string_literal: true # frozen_string_literal: true
require "utils/output"
# Cleans a newly installed keg. # Cleans a newly installed keg.
# By default: # By default:
# #
@ -13,6 +15,7 @@
# * removes unresolved symlinks # * removes unresolved symlinks
class Cleaner class Cleaner
include Context include Context
include Utils::Output::Mixin
# Create a cleaner for the given formula. # Create a cleaner for the given formula.
sig { params(formula: Formula).void } sig { params(formula: Formula).void }

View File

@ -2,6 +2,7 @@
# frozen_string_literal: true # frozen_string_literal: true
require "utils/bottles" require "utils/bottles"
require "utils/output"
require "formula" require "formula"
require "cask/cask_loader" require "cask/cask_loader"
@ -9,6 +10,9 @@ require "cask/cask_loader"
module Homebrew module Homebrew
# Helper class for cleaning up the Homebrew cache. # Helper class for cleaning up the Homebrew cache.
class Cleanup class Cleanup
extend Utils::Output::Mixin
include Utils::Output::Mixin
CLEANUP_DEFAULT_DAYS = Homebrew::EnvConfig.cleanup_periodic_full_days.to_i.freeze CLEANUP_DEFAULT_DAYS = Homebrew::EnvConfig.cleanup_periodic_full_days.to_i.freeze
GH_ACTIONS_ARTIFACT_CLEANUP_DAYS = 3 GH_ACTIONS_ARTIFACT_CLEANUP_DAYS = 3
private_constant :CLEANUP_DEFAULT_DAYS, :GH_ACTIONS_ARTIFACT_CLEANUP_DAYS private_constant :CLEANUP_DEFAULT_DAYS, :GH_ACTIONS_ARTIFACT_CLEANUP_DAYS

View File

@ -2,11 +2,13 @@
# frozen_string_literal: true # frozen_string_literal: true
require "cli/args" require "cli/args"
require "utils/output"
module Homebrew module Homebrew
module CLI module CLI
# Helper class for loading formulae/casks from named arguments. # Helper class for loading formulae/casks from named arguments.
class NamedArgs < Array class NamedArgs < Array
include Utils::Output::Mixin
extend T::Generic extend T::Generic
Elem = type_member(:out) { { fixed: String } } Elem = type_member(:out) { { fixed: String } }

View File

@ -10,10 +10,13 @@ require "commands"
require "optparse" require "optparse"
require "utils/tty" require "utils/tty"
require "utils/formatter" require "utils/formatter"
require "utils/output"
module Homebrew module Homebrew
module CLI module CLI
class Parser class Parser
include Utils::Output::Mixin
ArgType = T.type_alias { T.any(NilClass, Symbol, T::Array[String], T::Array[Symbol]) } ArgType = T.type_alias { T.any(NilClass, Symbol, T::Array[String], T::Array[Symbol]) }
HIDDEN_DESC_PLACEHOLDER = "@@HIDDEN@@" HIDDEN_DESC_PLACEHOLDER = "@@HIDDEN@@"
SYMBOL_TO_USAGE_MAPPING = T.let({ SYMBOL_TO_USAGE_MAPPING = T.let({
@ -40,7 +43,7 @@ module Homebrew
cmd_name = cmd_args_method_name.to_s.delete_suffix("_args").tr("_", "-") cmd_name = cmd_args_method_name.to_s.delete_suffix("_args").tr("_", "-")
begin begin
if require?(cmd_path) if Homebrew.require?(cmd_path)
cmd = Homebrew::AbstractCommand.command(cmd_name) cmd = Homebrew::AbstractCommand.command(cmd_name)
if cmd if cmd
cmd.parser cmd.parser

View File

@ -425,6 +425,8 @@ end
require "extend/os/cmd/update-report" require "extend/os/cmd/update-report"
class Reporter class Reporter
include Utils::Output::Mixin
class ReporterRevisionUnsetError < RuntimeError class ReporterRevisionUnsetError < RuntimeError
sig { params(var_name: String).void } sig { params(var_name: String).void }
def initialize(var_name) def initialize(var_name)
@ -782,6 +784,8 @@ class Reporter
end end
class ReporterHub class ReporterHub
include Utils::Output::Mixin
sig { returns(T::Array[Reporter]) } sig { returns(T::Array[Reporter]) }
attr_reader :reporters attr_reader :reporters

View File

@ -1,6 +1,8 @@
# typed: true # rubocop:todo Sorbet/StrictSigil # typed: true # rubocop:todo Sorbet/StrictSigil
# frozen_string_literal: true # frozen_string_literal: true
require "utils"
# Helper functions for commands. # Helper functions for commands.
module Commands module Commands
HOMEBREW_CMD_PATH = (HOMEBREW_LIBRARY_PATH/"cmd").freeze HOMEBREW_CMD_PATH = (HOMEBREW_LIBRARY_PATH/"cmd").freeze
@ -34,11 +36,11 @@ module Commands
DESCRIPTION_SPLITTING_PATTERN = /\.(?>\s|$)/ DESCRIPTION_SPLITTING_PATTERN = /\.(?>\s|$)/
def self.valid_internal_cmd?(cmd) def self.valid_internal_cmd?(cmd)
require?(HOMEBREW_CMD_PATH/cmd) Homebrew.require?(HOMEBREW_CMD_PATH/cmd)
end end
def self.valid_internal_dev_cmd?(cmd) def self.valid_internal_dev_cmd?(cmd)
require?(HOMEBREW_DEV_CMD_PATH/cmd) Homebrew.require?(HOMEBREW_DEV_CMD_PATH/cmd)
end end
def self.valid_ruby_cmd?(cmd) def self.valid_ruby_cmd?(cmd)
@ -76,7 +78,7 @@ module Commands
# Ruby commands which can be `require`d without being run. # Ruby commands which can be `require`d without being run.
def self.external_ruby_v2_cmd_path(cmd) def self.external_ruby_v2_cmd_path(cmd)
path = which("#{cmd}.rb", tap_cmd_directories) path = which("#{cmd}.rb", tap_cmd_directories)
path if require?(path) path if Homebrew.require?(path)
end end
# Ruby commands which are run by being `require`d. # Ruby commands which are run by being `require`d.

View File

@ -8,6 +8,8 @@ require "erb"
module Homebrew module Homebrew
# Helper functions for generating shell completions. # Helper functions for generating shell completions.
module Completions module Completions
extend Utils::Output::Mixin
Variables = Struct.new( Variables = Struct.new(
:aliases, :aliases,
:builtin_command_descriptions, :builtin_command_descriptions,

View File

@ -8,6 +8,7 @@ require "formulary"
require "version" require "version"
require "development_tools" require "development_tools"
require "utils/shell" require "utils/shell"
require "utils/output"
require "system_config" require "system_config"
require "cask/caskroom" require "cask/caskroom"
require "cask/quarantine" require "cask/quarantine"
@ -16,6 +17,8 @@ require "system_command"
module Homebrew module Homebrew
# Module containing diagnostic checks. # Module containing diagnostic checks.
module Diagnostic module Diagnostic
extend Utils::Output::Mixin
sig { sig {
params(formulae: T::Array[Formula], hide: T::Array[String], _block: T.nilable( params(formulae: T::Array[Formula], hide: T::Array[String], _block: T.nilable(
T.proc.params(formula_name: String, missing_dependencies: T::Array[Formula]).void, T.proc.params(formula_name: String, missing_dependencies: T::Array[Formula]).void,

View File

@ -6,9 +6,12 @@ require "concurrent/promises"
require "concurrent/executors" require "concurrent/executors"
require "retryable_download" require "retryable_download"
require "resource" require "resource"
require "utils/output"
module Homebrew module Homebrew
class DownloadQueue class DownloadQueue
include Utils::Output::Mixin
sig { params(retries: Integer, force: T::Boolean, pour: T::Boolean).void } sig { params(retries: Integer, force: T::Boolean, pour: T::Boolean).void }
def initialize(retries: 1, force: false, pour: false) def initialize(retries: 1, force: false, pour: false)
@concurrency = T.let(EnvConfig.download_concurrency, Integer) @concurrency = T.let(EnvConfig.download_concurrency, Integer)

View File

@ -7,6 +7,7 @@ require "unpack_strategy"
require "lazy_object" require "lazy_object"
require "lock_file" require "lock_file"
require "system_command" require "system_command"
require "utils/output"
# Need to define this before requiring Mechanize to avoid: # Need to define this before requiring Mechanize to avoid:
# uninitialized constant Mechanize # uninitialized constant Mechanize
@ -27,6 +28,7 @@ class AbstractDownloadStrategy
include FileUtils include FileUtils
include Context include Context
include SystemCommand::Mixin include SystemCommand::Mixin
include Utils::Output::Mixin
abstract! abstract!

View File

@ -4,9 +4,11 @@
require "url" require "url"
require "checksum" require "checksum"
require "download_strategy" require "download_strategy"
require "utils/output"
module Downloadable module Downloadable
include Context include Context
include Utils::Output::Mixin
extend T::Helpers extend T::Helpers
abstract! abstract!

View File

@ -1,11 +1,16 @@
# typed: strict # typed: strict
# frozen_string_literal: true # frozen_string_literal: true
require "utils/output"
module Homebrew module Homebrew
# Helper module for querying Homebrew-specific environment variables. # Helper module for querying Homebrew-specific environment variables.
# #
# @api internal # @api internal
module EnvConfig module EnvConfig
include Utils::Output::Mixin
extend Utils::Output::Mixin
module_function module_function
ENVS = T.let({ ENVS = T.let({

View File

@ -1,6 +1,8 @@
# typed: true # rubocop:todo Sorbet/StrictSigil # typed: true # rubocop:todo Sorbet/StrictSigil
# frozen_string_literal: true # frozen_string_literal: true
require "utils/output"
# Raised when a command is used wrong. # Raised when a command is used wrong.
# #
# @api internal # @api internal
@ -463,6 +465,8 @@ end
# Raised when an error occurs during a formula build. # Raised when an error occurs during a formula build.
class BuildError < RuntimeError class BuildError < RuntimeError
include Utils::Output::Mixin
attr_reader :cmd, :args, :env attr_reader :cmd, :args, :env
attr_accessor :formula, :options attr_accessor :formula, :options

View File

@ -3,6 +3,7 @@
require "extend/ENV/shared" require "extend/ENV/shared"
require "development_tools" require "development_tools"
require "utils/output"
# ### Why `superenv`? # ### Why `superenv`?
# #
@ -16,6 +17,7 @@ require "development_tools"
# 8. Build-system agnostic configuration of the toolchain # 8. Build-system agnostic configuration of the toolchain
module Superenv module Superenv
include SharedEnvExtension include SharedEnvExtension
include Utils::Output::Mixin
attr_accessor :keg_only_deps, :deps, :run_time_deps attr_accessor :keg_only_deps, :deps, :run_time_deps

View File

@ -1,9 +1,7 @@
# typed: true # rubocop:todo Sorbet/StrictSigil # typed: true # rubocop:todo Sorbet/StrictSigil
# frozen_string_literal: true # frozen_string_literal: true
# Contains shorthand Homebrew utility methods like `ohai`, `opoo`, `odisabled`. require "utils/output"
# TODO: move these out of `Kernel` into `Homebrew::GlobalMethods` and add
# necessary Sorbet and global Kernel inclusions.
module Kernel module Kernel
sig { params(env: T.nilable(String)).returns(T::Boolean) } sig { params(env: T.nilable(String)).returns(T::Boolean) }
@ -14,279 +12,6 @@ module Kernel
end end
private :superenv? private :superenv?
sig { params(path: T.nilable(T.any(String, Pathname))).returns(T::Boolean) }
def require?(path)
return false if path.nil?
if defined?(Warnings)
# Work around require warning when done repeatedly:
# https://bugs.ruby-lang.org/issues/21091
Warnings.ignore(/already initialized constant/, /previous definition of/) do
require path.to_s
end
else
require path.to_s
end
true
rescue LoadError
false
end
sig { params(title: String).returns(String) }
def ohai_title(title)
verbose = if respond_to?(:verbose?)
T.unsafe(self).verbose?
else
Context.current.verbose?
end
title = Tty.truncate(title.to_s) if $stdout.tty? && !verbose
Formatter.headline(title, color: :blue)
end
sig { params(title: T.any(String, Exception), sput: T.anything).void }
def ohai(title, *sput)
puts ohai_title(title.to_s)
puts sput
end
sig { params(title: T.any(String, Exception), sput: T.anything, always_display: T::Boolean).void }
def odebug(title, *sput, always_display: false)
debug = if respond_to?(:debug)
T.unsafe(self).debug?
else
Context.current.debug?
end
return if !debug && !always_display
$stderr.puts Formatter.headline(title.to_s, color: :magenta)
$stderr.puts sput unless sput.empty?
end
sig { params(title: String, truncate: T.any(Symbol, T::Boolean)).returns(String) }
def oh1_title(title, truncate: :auto)
verbose = if respond_to?(:verbose?)
T.unsafe(self).verbose?
else
Context.current.verbose?
end
title = Tty.truncate(title.to_s) if $stdout.tty? && !verbose && truncate == :auto
Formatter.headline(title, color: :green)
end
sig { params(title: String, truncate: T.any(Symbol, T::Boolean)).void }
def oh1(title, truncate: :auto)
puts oh1_title(title, truncate:)
end
# Print a warning message.
#
# @api public
sig { params(message: T.any(String, Exception)).void }
def opoo(message)
require "utils/github/actions"
return if GitHub::Actions.puts_annotation_if_env_set!(:warning, message.to_s)
require "utils/formatter"
Tty.with($stderr) do |stderr|
stderr.puts Formatter.warning(message, label: "Warning")
end
end
# Print a warning message only if not running in GitHub Actions.
#
# @api public
sig { params(message: T.any(String, Exception)).void }
def opoo_outside_github_actions(message)
require "utils/github/actions"
return if GitHub::Actions.env_set?
opoo(message)
end
# Print an error message.
#
# @api public
sig { params(message: T.any(String, Exception)).void }
def onoe(message)
require "utils/github/actions"
return if GitHub::Actions.puts_annotation_if_env_set!(:error, message.to_s)
require "utils/formatter"
Tty.with($stderr) do |stderr|
stderr.puts Formatter.error(message, label: "Error")
end
end
# Print an error message and fail at the end of the program.
#
# @api public
sig { params(error: T.any(String, Exception)).void }
def ofail(error)
onoe error
Homebrew.failed = true
end
# Print an error message and fail immediately.
#
# @api public
sig { params(error: T.any(String, Exception)).returns(T.noreturn) }
def odie(error)
onoe error
exit 1
end
# Output a deprecation warning/error message.
sig {
params(method: String, replacement: T.nilable(T.any(String, Symbol)), disable: T::Boolean,
disable_on: T.nilable(Time), disable_for_developers: T::Boolean, caller: T::Array[String]).void
}
def odeprecated(method, replacement = nil,
disable: false,
disable_on: nil,
disable_for_developers: true,
caller: send(:caller))
replacement_message = if replacement
"Use #{replacement} instead."
else
"There is no replacement."
end
unless disable_on.nil?
if disable_on > Time.now
will_be_disabled_message = " and will be disabled on #{disable_on.strftime("%Y-%m-%d")}"
else
disable = true
end
end
verb = if disable
"disabled"
else
"deprecated#{will_be_disabled_message}"
end
# Try to show the most relevant location in message, i.e. (if applicable):
# - Location in a formula.
# - Location of caller of deprecated method (if all else fails).
backtrace = caller
# Don't throw deprecations at all for cached, .brew or .metadata files.
return if backtrace.any? do |line|
next true if line.include?(HOMEBREW_CACHE.to_s)
next true if line.include?("/.brew/")
next true if line.include?("/.metadata/")
next false unless line.match?(HOMEBREW_TAP_PATH_REGEX)
path = Pathname(line.split(":", 2).first)
next false unless path.file?
next false unless path.readable?
formula_contents = path.read
formula_contents.include?(" deprecate! ") || formula_contents.include?(" disable! ")
end
tap_message = T.let(nil, T.nilable(String))
backtrace.each do |line|
next unless (match = line.match(HOMEBREW_TAP_PATH_REGEX))
require "tap"
tap = Tap.fetch(match[:user], match[:repository])
tap_message = "\nPlease report this issue to the #{tap.full_name} tap"
tap_message += " (not Homebrew/* repositories)" unless tap.official?
tap_message += ", or even better, submit a PR to fix it" if replacement
tap_message << ":\n #{line.sub(/^(.*:\d+):.*$/, '\1')}\n\n"
break
end
file, line, = backtrace.first.split(":")
line = line.to_i if line.present?
message = "Calling #{method} is #{verb}! #{replacement_message}"
message << tap_message if tap_message
message.freeze
disable = true if disable_for_developers && Homebrew::EnvConfig.developer?
if disable || Homebrew.raise_deprecation_exceptions?
require "utils/github/actions"
GitHub::Actions.puts_annotation_if_env_set!(:error, message, file:, line:)
exception = MethodDeprecatedError.new(message)
exception.set_backtrace(backtrace)
raise exception
elsif !Homebrew.auditing?
opoo message
end
end
sig {
params(method: String, replacement: T.nilable(T.any(String, Symbol)),
disable_on: T.nilable(Time), disable_for_developers: T::Boolean, caller: T::Array[String]).void
}
def odisabled(method, replacement = nil,
disable_on: nil,
disable_for_developers: true,
caller: send(:caller))
# This odeprecated should stick around indefinitely.
odeprecated(method, replacement, disable: true, disable_on:, disable_for_developers:, caller:)
end
sig { params(string: String).returns(String) }
def pretty_installed(string)
if !$stdout.tty?
string
elsif Homebrew::EnvConfig.no_emoji?
Formatter.success("#{Tty.bold}#{string} (installed)#{Tty.reset}")
else
"#{Tty.bold}#{string} #{Formatter.success("")}#{Tty.reset}"
end
end
sig { params(string: String).returns(String) }
def pretty_outdated(string)
if !$stdout.tty?
string
elsif Homebrew::EnvConfig.no_emoji?
Formatter.error("#{Tty.bold}#{string} (outdated)#{Tty.reset}")
else
"#{Tty.bold}#{string} #{Formatter.warning("")}#{Tty.reset}"
end
end
sig { params(string: String).returns(String) }
def pretty_uninstalled(string)
if !$stdout.tty?
string
elsif Homebrew::EnvConfig.no_emoji?
Formatter.error("#{Tty.bold}#{string} (uninstalled)#{Tty.reset}")
else
"#{Tty.bold}#{string} #{Formatter.error("")}#{Tty.reset}"
end
end
sig { params(seconds: T.nilable(T.any(Integer, Float))).returns(String) }
def pretty_duration(seconds)
seconds = seconds.to_i
res = +""
if seconds > 59
minutes = seconds / 60
seconds %= 60
res = +Utils.pluralize("minute", minutes, include_count: true)
return res.freeze if seconds.zero?
res << " "
end
res << Utils.pluralize("second", seconds, include_count: true)
res.freeze
end
sig { params(formula: T.nilable(Formula)).void } sig { params(formula: T.nilable(Formula)).void }
def interactive_shell(formula = nil) def interactive_shell(formula = nil)
unless formula.nil? unless formula.nil?
@ -368,7 +93,7 @@ module Kernel
editor ||= "vim" editor ||= "vim"
unless silent unless silent
opoo <<~EOS Utils::Output.opoo <<~EOS
Using #{editor} because no editor was set in the environment. Using #{editor} because no editor was set in the environment.
This may change in the future, so we recommend setting `$EDITOR` This may change in the future, so we recommend setting `$EDITOR`
or `$HOMEBREW_EDITOR` to your preferred text editor. or `$HOMEBREW_EDITOR` to your preferred text editor.

View File

@ -1,6 +1,8 @@
# typed: strict # typed: strict
# frozen_string_literal: true # frozen_string_literal: true
require "utils/output"
module OS module OS
module Linux module Linux
module Install module Install
@ -108,7 +110,7 @@ module OS
::Kernel.system HOMEBREW_PREFIX/"opt/glibc/sbin/ldconfig" ::Kernel.system HOMEBREW_PREFIX/"opt/glibc/sbin/ldconfig"
end end
else else
::Kernel.odie "#{HOMEBREW_PREFIX}/lib does not exist!" unless (HOMEBREW_PREFIX/"lib").readable? Utils::Output.odie "#{HOMEBREW_PREFIX}/lib does not exist!" unless (HOMEBREW_PREFIX/"lib").readable?
end end
GCC_RUNTIME_LIBS.each do |library| GCC_RUNTIME_LIBS.each do |library|

View File

@ -1,11 +1,14 @@
# typed: strict # typed: strict
# frozen_string_literal: true # frozen_string_literal: true
require "utils/output"
module OS module OS
module Mac module Mac
module Readall module Readall
module ClassMethods module ClassMethods
extend T::Helpers extend T::Helpers
include ::Utils::Output::Mixin
requires_ancestor { Kernel } requires_ancestor { Kernel }

View File

@ -4,12 +4,14 @@
require "system_command" require "system_command"
require "extend/pathname/disk_usage_extension" require "extend/pathname/disk_usage_extension"
require "extend/pathname/observer_pathname_extension" require "extend/pathname/observer_pathname_extension"
require "utils/output"
# Homebrew extends Ruby's `Pathname` to make our code more readable. # Homebrew extends Ruby's `Pathname` to make our code more readable.
# @see https://ruby-doc.org/stdlib-2.6.3/libdoc/pathname/rdoc/Pathname.html Ruby's Pathname API # @see https://ruby-doc.org/stdlib-2.6.3/libdoc/pathname/rdoc/Pathname.html Ruby's Pathname API
class Pathname class Pathname
include SystemCommand::Mixin include SystemCommand::Mixin
include DiskUsageExtension include DiskUsageExtension
include Utils::Output::Mixin
# Moves a file from the original location to the {Pathname}'s. # Moves a file from the original location to the {Pathname}'s.
# #

View File

@ -2,8 +2,11 @@
# frozen_string_literal: true # frozen_string_literal: true
require "time" require "time"
require "utils/output"
class Time class Time
include Utils::Output::Mixin
# Backwards compatibility for formulae that used this ActiveSupport extension # Backwards compatibility for formulae that used this ActiveSupport extension
sig { returns(String) } sig { returns(String) }
def rfc3339 def rfc3339

View File

@ -41,6 +41,7 @@ require "utils/spdx"
require "on_system" require "on_system"
require "api" require "api"
require "api_hashable" require "api_hashable"
require "utils/output"
# A formula provides instructions and metadata for Homebrew to install a piece # A formula provides instructions and metadata for Homebrew to install a piece
# of software. Every Homebrew formula is a {Formula}. # of software. Every Homebrew formula is a {Formula}.
@ -75,6 +76,7 @@ class Formula
include FileUtils include FileUtils
include Utils::Shebang include Utils::Shebang
include Utils::Shell include Utils::Shell
include Utils::Output::Mixin
include Context include Context
include OnSystem::MacOSAndLinux include OnSystem::MacOSAndLinux
include Homebrew::Livecheck::Constants include Homebrew::Livecheck::Constants
@ -82,6 +84,7 @@ class Formula
extend Cachable extend Cachable
extend APIHashable extend APIHashable
extend T::Helpers extend T::Helpers
extend Utils::Output::Mixin
abstract! abstract!

View File

@ -1,10 +1,13 @@
# typed: strict # typed: strict
# frozen_string_literal: true # frozen_string_literal: true
require "utils/output"
module Homebrew module Homebrew
# Helper functions available in formula `test` blocks. # Helper functions available in formula `test` blocks.
module Assertions module Assertions
include Context include Context
include ::Utils::Output::Mixin
extend T::Helpers extend T::Helpers
requires_ancestor { Kernel } requires_ancestor { Kernel }

View File

@ -6,12 +6,14 @@ require "formula_versions"
require "formula_name_cask_token_auditor" require "formula_name_cask_token_auditor"
require "resource_auditor" require "resource_auditor"
require "utils/shared_audits" require "utils/shared_audits"
require "utils/output"
module Homebrew module Homebrew
# Auditor for checking common violations in {Formula}e. # Auditor for checking common violations in {Formula}e.
class FormulaAuditor class FormulaAuditor
include FormulaCellarChecks include FormulaCellarChecks
include Utils::Curl include Utils::Curl
include Utils::Output::Mixin
attr_reader :formula, :text, :problems, :new_formula_problems attr_reader :formula, :text, :problems, :new_formula_problems

View File

@ -4,10 +4,13 @@
require "digest" require "digest"
require "erb" require "erb"
require "utils/github" require "utils/github"
require "utils/output"
module Homebrew module Homebrew
# Class for generating a formula from a template. # Class for generating a formula from a template.
class FormulaCreator class FormulaCreator
include Utils::Output::Mixin
sig { returns(String) } sig { returns(String) }
attr_accessor :name attr_accessor :name

View File

@ -24,10 +24,12 @@ require "service"
require "attestation" require "attestation"
require "sbom" require "sbom"
require "utils/fork" require "utils/fork"
require "utils/output"
# Installer for a formula. # Installer for a formula.
class FormulaInstaller class FormulaInstaller
include FormulaCellarChecks include FormulaCellarChecks
include Utils::Output::Mixin
ETC_VAR_DIRS = T.let([HOMEBREW_PREFIX/"etc", HOMEBREW_PREFIX/"var"].freeze, T::Array[Pathname]) ETC_VAR_DIRS = T.let([HOMEBREW_PREFIX/"etc", HOMEBREW_PREFIX/"var"].freeze, T::Array[Pathname])

View File

@ -2,12 +2,14 @@
# frozen_string_literal: true # frozen_string_literal: true
require "formula" require "formula"
require "utils/output"
# Helper class for traversing a formula's previous versions. # Helper class for traversing a formula's previous versions.
# #
# @api internal # @api internal
class FormulaVersions class FormulaVersions
include Context include Context
include Utils::Output::Mixin
IGNORED_EXCEPTIONS = [ IGNORED_EXCEPTIONS = [
ArgumentError, NameError, SyntaxError, TypeError, ArgumentError, NameError, SyntaxError, TypeError,

View File

@ -6,6 +6,7 @@ require "cachable"
require "tab" require "tab"
require "utils" require "utils"
require "utils/bottles" require "utils/bottles"
require "utils/output"
require "service" require "service"
require "utils/curl" require "utils/curl"
require "deprecate_disable" require "deprecate_disable"
@ -18,6 +19,8 @@ require "tap"
module Formulary module Formulary
extend Context extend Context
extend Cachable extend Cachable
extend Utils::Output::Mixin
include Utils::Output::Mixin
ALLOWED_URL_SCHEMES = %w[file].freeze ALLOWED_URL_SCHEMES = %w[file].freeze
private_constant :ALLOWED_URL_SCHEMES private_constant :ALLOWED_URL_SCHEMES
@ -559,6 +562,7 @@ module Formulary
# Subclasses implement loaders for particular sources of formulae. # Subclasses implement loaders for particular sources of formulae.
class FormulaLoader class FormulaLoader
include Context include Context
include Utils::Output::Mixin
# The formula's name. # The formula's name.
sig { returns(String) } sig { returns(String) }
@ -609,6 +613,8 @@ module Formulary
# Loads a formula from a bottle. # Loads a formula from a bottle.
class FromBottleLoader < FormulaLoader class FromBottleLoader < FormulaLoader
include Utils::Output::Mixin
sig { sig {
params(ref: T.any(String, Pathname, URI::Generic), from: T.nilable(Symbol), warn: T::Boolean) params(ref: T.any(String, Pathname, URI::Generic), from: T.nilable(Symbol), warn: T::Boolean)
.returns(T.nilable(T.attached_class)) .returns(T.nilable(T.attached_class))

View File

@ -1,5 +0,0 @@
# typed: strict
module Formulary
include Kernel
end

View File

@ -3,6 +3,7 @@
require "utils/curl" require "utils/curl"
require "utils/gzip" require "utils/gzip"
require "utils/output"
require "json" require "json"
require "zlib" require "zlib"
require "extend/hash/keys" require "extend/hash/keys"
@ -12,6 +13,7 @@ require "system_command"
class GitHubPackages class GitHubPackages
include Context include Context
include SystemCommand::Mixin include SystemCommand::Mixin
include Utils::Output::Mixin
URL_DOMAIN = "ghcr.io" URL_DOMAIN = "ghcr.io"
URL_PREFIX = "https://#{URL_DOMAIN}/v2/".freeze URL_PREFIX = "https://#{URL_DOMAIN}/v2/".freeze

View File

@ -2,11 +2,13 @@
# frozen_string_literal: true # frozen_string_literal: true
require "utils/github" require "utils/github"
require "utils/output"
require "json" require "json"
# GitHub Releases client. # GitHub Releases client.
class GitHubReleases class GitHubReleases
include Context include Context
include Utils::Output::Mixin
URL_REGEX = %r{https://github\.com/([\w-]+)/([\w-]+)?/releases/download/(.+)} URL_REGEX = %r{https://github\.com/([\w-]+)/([\w-]+)?/releases/download/(.+)}

View File

@ -3,10 +3,13 @@
require "cli/parser" require "cli/parser"
require "commands" require "commands"
require "utils/output"
module Homebrew module Homebrew
# Helper module for printing help output. # Helper module for printing help output.
module Help module Help
extend Utils::Output::Mixin
sig { sig {
params( params(
cmd: T.nilable(String), cmd: T.nilable(String),

View File

@ -7,10 +7,13 @@ require "hardware"
require "development_tools" require "development_tools"
require "upgrade" require "upgrade"
require "download_queue" require "download_queue"
require "utils/output"
module Homebrew module Homebrew
# Helper module for performing (pre-)install checks. # Helper module for performing (pre-)install checks.
module Install module Install
extend Utils::Output::Mixin
class << self class << self
sig { params(all_fatal: T::Boolean).void } sig { params(all_fatal: T::Boolean).void }
def perform_preinstall_checks_once(all_fatal: false) def perform_preinstall_checks_once(all_fatal: false)

View File

@ -5,10 +5,12 @@ require "keg_relocate"
require "language/python" require "language/python"
require "lock_file" require "lock_file"
require "cachable" require "cachable"
require "utils/output"
# Installation prefix of a formula. # Installation prefix of a formula.
class Keg class Keg
extend Cachable extend Cachable
include Utils::Output::Mixin
# Error for when a keg is already linked. # Error for when a keg is already linked.
class AlreadyLinkedError < RuntimeError class AlreadyLinkedError < RuntimeError

View File

@ -1,11 +1,15 @@
# typed: strict # typed: strict
# frozen_string_literal: true # frozen_string_literal: true
require "utils/output"
module Language module Language
# Helper functions for Node formulae. # Helper functions for Node formulae.
# #
# @api public # @api public
module Node module Node
extend ::Utils::Output::Mixin
sig { returns(String) } sig { returns(String) }
def self.npm_cache_config def self.npm_cache_config
"cache=#{HOMEBREW_CACHE}/npm_cache" "cache=#{HOMEBREW_CACHE}/npm_cache"

View File

@ -5,9 +5,12 @@ require "keg"
require "formula" require "formula"
require "linkage_cache_store" require "linkage_cache_store"
require "fiddle" require "fiddle"
require "utils/output"
# Check for broken/missing linkage in a formula's keg. # Check for broken/missing linkage in a formula's keg.
class LinkageChecker class LinkageChecker
include Utils::Output::Mixin
attr_reader :undeclared_deps, :keg, :formula, :store attr_reader :undeclared_deps, :keg, :formula, :store
def initialize(keg, formula = nil, cache_db:, rebuild_cache: false) def initialize(keg, formula = nil, cache_db:, rebuild_cache: false)

View File

@ -7,12 +7,15 @@ require "livecheck/livecheck_version"
require "livecheck/skip_conditions" require "livecheck/skip_conditions"
require "livecheck/strategy" require "livecheck/strategy"
require "addressable" require "addressable"
require "utils/output"
module Homebrew module Homebrew
# The {Livecheck} module consists of methods used by the `brew livecheck` # The {Livecheck} module consists of methods used by the `brew livecheck`
# command. These methods print the requested livecheck information # command. These methods print the requested livecheck information
# for formulae. # for formulae.
module Livecheck module Livecheck
extend Utils::Output::Mixin
NO_CURRENT_VERSION_MSG = "Unable to identify current version" NO_CURRENT_VERSION_MSG = "Unable to identify current version"
NO_VERSIONS_MSG = "Unable to get versions" NO_VERSIONS_MSG = "Unable to get versions"

View File

@ -2,9 +2,12 @@
# frozen_string_literal: true # frozen_string_literal: true
require "fcntl" require "fcntl"
require "utils/output"
# A lock file to prevent multiple Homebrew processes from modifying the same path. # A lock file to prevent multiple Homebrew processes from modifying the same path.
class LockFile class LockFile
include Utils::Output::Mixin
class OpenFileChangedOnDisk < RuntimeError; end class OpenFileChangedOnDisk < RuntimeError; end
private_constant :OpenFileChangedOnDisk private_constant :OpenFileChangedOnDisk

View File

@ -1,9 +1,13 @@
# typed: strict # typed: strict
# frozen_string_literal: true # frozen_string_literal: true
require "utils/output"
# A {Messages} object collects messages that may need to be displayed together # A {Messages} object collects messages that may need to be displayed together
# at the end of a multi-step `brew` command run. # at the end of a multi-step `brew` command run.
class Messages class Messages
include ::Utils::Output::Mixin
sig { returns(T::Array[{ package: String, caveats: T.any(String, Caveats) }]) } sig { returns(T::Array[{ package: String, caveats: T.any(String, Caveats) }]) }
attr_reader :caveats attr_reader :caveats

View File

@ -4,10 +4,13 @@
require "lock_file" require "lock_file"
require "keg" require "keg"
require "tab" require "tab"
require "utils/output"
# Helper class for migrating a formula from an old to a new name. # Helper class for migrating a formula from an old to a new name.
class Migrator class Migrator
extend Utils::Output::Mixin
include Context include Context
include Utils::Output::Mixin
# Error for when a migration is necessary. # Error for when a migration is necessary.
class MigrationNeededError < RuntimeError class MigrationNeededError < RuntimeError

View File

@ -2,10 +2,13 @@
# frozen_string_literal: true # frozen_string_literal: true
require "formulary" require "formulary"
require "utils/output"
module Homebrew module Homebrew
# Helper module for checking if there is a reason a formula is missing. # Helper module for checking if there is a reason a formula is missing.
module MissingFormula module MissingFormula
extend Utils::Output::Mixin
class << self class << self
sig { params(name: String, silent: T::Boolean, show_info: T::Boolean).returns(T.nilable(String)) } sig { params(name: String, silent: T::Boolean, show_info: T::Boolean).returns(T.nilable(String)) }
def reason(name, silent: false, show_info: false) def reason(name, silent: false, show_info: false)

View File

@ -1,10 +1,14 @@
# typed: strict # typed: strict
# frozen_string_literal: true # frozen_string_literal: true
require "utils/output"
# Performs {Formula#mktemp}'s functionality and tracks the results. # Performs {Formula#mktemp}'s functionality and tracks the results.
# Each instance is only intended to be used once. # Each instance is only intended to be used once.
# Can also be used to create a temporary directory with the brew instance's group. # Can also be used to create a temporary directory with the brew instance's group.
class Mktemp class Mktemp
include Utils::Output::Mixin
# Path to the tmpdir used in this run # Path to the tmpdir used in this run
sig { returns(T.nilable(Pathname)) } sig { returns(T.nilable(Pathname)) }
attr_reader :tmpdir attr_reader :tmpdir

View File

@ -3,6 +3,7 @@
require "resource" require "resource"
require "erb" require "erb"
require "utils/output"
# Helper module for creating patches. # Helper module for creating patches.
module Patch module Patch
@ -96,6 +97,8 @@ end
# A file containing a patch. # A file containing a patch.
class ExternalPatch class ExternalPatch
include Utils::Output::Mixin
extend Forwardable extend Forwardable
attr_reader :resource, :strip attr_reader :resource, :strip

View File

@ -4,11 +4,13 @@
require "formula" require "formula"
require "cask/cask_loader" require "cask/cask_loader"
require "system_command" require "system_command"
require "utils/output"
# Helper module for validating syntax in taps. # Helper module for validating syntax in taps.
module Readall module Readall
extend Cachable extend Cachable
extend SystemCommand::Mixin extend SystemCommand::Mixin
extend Utils::Output::Mixin
# TODO: remove this once the `MacOS` module is undefined on Linux # TODO: remove this once the `MacOS` module is undefined on Linux
MACOS_MODULE_REGEX = /\b(MacOS|OS::Mac)(\.|::)\b/ MACOS_MODULE_REGEX = /\b(MacOS|OS::Mac)(\.|::)\b/

View File

@ -3,6 +3,7 @@
require "development_tools" require "development_tools"
require "messages" require "messages"
require "utils/output"
# Needed to handle circular require dependency. # Needed to handle circular require dependency.
# rubocop:disable Lint/EmptyClass # rubocop:disable Lint/EmptyClass
@ -11,6 +12,8 @@ class FormulaInstaller; end
module Homebrew module Homebrew
module Reinstall module Reinstall
extend Utils::Output::Mixin
class InstallationContext < T::Struct class InstallationContext < T::Struct
const :formula_installer, ::FormulaInstaller const :formula_installer, ::FormulaInstaller
const :keg, T.nilable(Keg) const :keg, T.nilable(Keg)

View File

@ -5,12 +5,14 @@ require "dependable"
require "dependency" require "dependency"
require "dependencies" require "dependencies"
require "build_environment" require "build_environment"
require "utils/output"
# A base class for non-formula requirements needed by formulae. # A base class for non-formula requirements needed by formulae.
# A fatal requirement is one that will fail the build if it is not present. # A fatal requirement is one that will fail the build if it is not present.
# By default, requirements are non-fatal. # By default, requirements are non-fatal.
class Requirement class Requirement
include Dependable include Dependable
include Utils::Output::Mixin
extend Cachable extend Cachable
extend T::Helpers extend T::Helpers

View File

@ -5,6 +5,7 @@ require "downloadable"
require "mktemp" require "mktemp"
require "livecheck" require "livecheck"
require "on_system" require "on_system"
require "utils/output"
# Resource is the fundamental representation of an external resource. The # Resource is the fundamental representation of an external resource. The
# primary formula download, along with other declared resources, are instances # primary formula download, along with other declared resources, are instances
@ -13,6 +14,7 @@ class Resource
include Downloadable include Downloadable
include FileUtils include FileUtils
include OnSystem::MacOSAndLinux include OnSystem::MacOSAndLinux
include Utils::Output::Mixin
attr_reader :source_modified_time, :patches, :owner attr_reader :source_modified_time, :patches, :owner
attr_writer :checksum attr_writer :checksum

View File

@ -3,10 +3,12 @@
require "bottle" require "bottle"
require "api/json_download" require "api/json_download"
require "utils/output"
module Homebrew module Homebrew
class RetryableDownload class RetryableDownload
include Downloadable include Downloadable
include Utils::Output::Mixin
sig { override.returns(T.any(NilClass, String, URL)) } sig { override.returns(T.any(NilClass, String, URL)) }
def url = downloadable.url def url = downloadable.url

View File

@ -6,9 +6,12 @@ require "io/console"
require "pty" require "pty"
require "tempfile" require "tempfile"
require "utils/fork" require "utils/fork"
require "utils/output"
# Helper class for running a sub-process inside of a sandboxed environment. # Helper class for running a sub-process inside of a sandboxed environment.
class Sandbox class Sandbox
include Utils::Output::Mixin
SANDBOX_EXEC = "/usr/bin/sandbox-exec" SANDBOX_EXEC = "/usr/bin/sandbox-exec"
# This is defined in the macOS SDK but Ruby unfortunately does not expose it. # This is defined in the macOS SDK but Ruby unfortunately does not expose it.

View File

@ -6,9 +6,12 @@ require "json"
require "development_tools" require "development_tools"
require "cachable" require "cachable"
require "utils/curl" require "utils/curl"
require "utils/output"
# Rather than calling `new` directly, use one of the class methods like {SBOM.create}. # Rather than calling `new` directly, use one of the class methods like {SBOM.create}.
class SBOM class SBOM
include Utils::Output::Mixin
FILENAME = "sbom.spdx.json" FILENAME = "sbom.spdx.json"
SCHEMA_FILE = (HOMEBREW_LIBRARY_PATH/"data/schemas/sbom.json").freeze SCHEMA_FILE = (HOMEBREW_LIBRARY_PATH/"data/schemas/sbom.json").freeze
@ -87,7 +90,7 @@ class SBOM
sig { params(bottling: T::Boolean).returns(T::Array[T::Hash[String, T.untyped]]) } sig { params(bottling: T::Boolean).returns(T::Array[T::Hash[String, T.untyped]]) }
def schema_validation_errors(bottling: false) def schema_validation_errors(bottling: false)
unless require? "json_schemer" unless Homebrew.require? "json_schemer"
error_message = "Need json_schemer to validate SBOM, run `brew install-bundler-gems --add-groups=bottle`!" error_message = "Need json_schemer to validate SBOM, run `brew install-bundler-gems --add-groups=bottle`!"
odie error_message if ENV["HOMEBREW_ENFORCE_SBOM"] odie error_message if ENV["HOMEBREW_ENFORCE_SBOM"]
return [] return []

View File

@ -2,10 +2,13 @@
# frozen_string_literal: true # frozen_string_literal: true
require "description_cache_store" require "description_cache_store"
require "utils/output"
module Homebrew module Homebrew
# Helper module for searching formulae or casks. # Helper module for searching formulae or casks.
module Search module Search
extend Utils::Output::Mixin
def self.query_regexp(query) def self.query_regexp(query)
if (m = query.match(%r{^/(.*)/$})) if (m = query.match(%r{^/(.*)/$}))
Regexp.new(m[1]) Regexp.new(m[1])

View File

@ -3,11 +3,13 @@
require "services/formula_wrapper" require "services/formula_wrapper"
require "fileutils" require "fileutils"
require "utils/output"
module Homebrew module Homebrew
module Services module Services
module Cli module Cli
extend FileUtils extend FileUtils
extend Utils::Output::Mixin
sig { returns(T.nilable(String)) } sig { returns(T.nilable(String)) }
def self.sudo_service_user def self.sudo_service_user

View File

@ -3,11 +3,14 @@
require "services/cli" require "services/cli"
require "services/formulae" require "services/formulae"
require "utils/output"
module Homebrew module Homebrew
module Services module Services
module Commands module Commands
module List module List
extend Utils::Output::Mixin
TRIGGERS = [nil, "list", "ls"].freeze TRIGGERS = [nil, "list", "ls"].freeze
sig { params(json: T::Boolean).void } sig { params(json: T::Boolean).void }

View File

@ -1,11 +1,15 @@
# typed: true # rubocop:todo Sorbet/StrictSigil # typed: true # rubocop:todo Sorbet/StrictSigil
# frozen_string_literal: true # frozen_string_literal: true
require "utils/output"
# Wrapper for a formula to handle service-related stuff like parsing and # Wrapper for a formula to handle service-related stuff like parsing and
# generating the service/plist files. # generating the service/plist files.
module Homebrew module Homebrew
module Services module Services
class FormulaWrapper class FormulaWrapper
include Utils::Output::Mixin
# Access the `Formula` instance. # Access the `Formula` instance.
sig { returns(Formula) } sig { returns(Formula) }
attr_reader :formula attr_reader :formula

View File

@ -2,10 +2,13 @@
# frozen_string_literal: true # frozen_string_literal: true
require_relative "system/systemctl" require_relative "system/systemctl"
require "utils/output"
module Homebrew module Homebrew
module Services module Services
module System module System
extend Utils::Output::Mixin
LAUNCHCTL_DOMAIN_ACTION_NOT_SUPPORTED = T.let(125, Integer) LAUNCHCTL_DOMAIN_ACTION_NOT_SUPPORTED = T.let(125, Integer)
# Path to launchctl binary. # Path to launchctl binary.

View File

@ -4,10 +4,12 @@
require "shellwords" require "shellwords"
require "source_location" require "source_location"
require "system_command" require "system_command"
require "utils/output"
module Homebrew module Homebrew
# Helper module for running RuboCop. # Helper module for running RuboCop.
module Style module Style
extend Utils::Output::Mixin
extend SystemCommand::Mixin extend SystemCommand::Mixin
# Checks style for a list of files, printing simple RuboCop output. # Checks style for a list of files, printing simple RuboCop output.

View File

@ -8,6 +8,7 @@ require "uri"
require "context" require "context"
require "readline_nonblock" require "readline_nonblock"
require "utils/timer" require "utils/timer"
require "utils/output"
# Class for running sub-processes and capturing their output and exit status. # Class for running sub-processes and capturing their output and exit status.
# #
@ -380,6 +381,7 @@ class SystemCommand
# Result containing the output and exit status of a finished sub-process. # Result containing the output and exit status of a finished sub-process.
class Result class Result
include Context include Context
include Utils::Output::Mixin
attr_accessor :command, :status, :exit_status attr_accessor :command, :status, :exit_status

View File

@ -4,6 +4,7 @@
require "api" require "api"
require "commands" require "commands"
require "settings" require "settings"
require "utils/output"
# A {Tap} is used to encapsulate Homebrew formulae, casks and custom commands. # A {Tap} is used to encapsulate Homebrew formulae, casks and custom commands.
# Usually, it's synced with a remote Git repository. And it's likely # Usually, it's synced with a remote Git repository. And it's likely
@ -13,6 +14,8 @@ require "settings"
# repository name without the leading `homebrew-`. # repository name without the leading `homebrew-`.
class Tap class Tap
extend Cachable extend Cachable
extend Utils::Output::Mixin
include Utils::Output::Mixin
HOMEBREW_TAP_CASK_RENAMES_FILE = "cask_renames.json" HOMEBREW_TAP_CASK_RENAMES_FILE = "cask_renames.json"
private_constant :HOMEBREW_TAP_CASK_RENAMES_FILE private_constant :HOMEBREW_TAP_CASK_RENAMES_FILE

View File

@ -103,7 +103,6 @@ RSpec.describe Homebrew::Bundle::Dsl do
end end
it ".sanitize_cask_name" do it ".sanitize_cask_name" do
allow_any_instance_of(Object).to receive(:opoo)
expect(described_class.send(:sanitize_cask_name, "homebrew/cask-versions/adoptopenjdk8")).to eql("adoptopenjdk8") expect(described_class.send(:sanitize_cask_name, "homebrew/cask-versions/adoptopenjdk8")).to eql("adoptopenjdk8")
expect(described_class.send(:sanitize_cask_name, "adoptopenjdk8")).to eql("adoptopenjdk8") expect(described_class.send(:sanitize_cask_name, "adoptopenjdk8")).to eql("adoptopenjdk8")
end end

View File

@ -22,9 +22,7 @@ RSpec.describe Cask::DSL, :cask, :no_api do
it "prints an error that it has encountered an unexpected method" do it "prints an error that it has encountered an unexpected method" do
expected = Regexp.compile(<<~EOS.lines.map(&:chomp).join) expected = Regexp.compile(<<~EOS.lines.map(&:chomp).join)
(?m) (?m)
Error: Error: Unexpected method 'future_feature' called on Cask unexpected-method-cask\\.
.*
Unexpected method 'future_feature' called on Cask unexpected-method-cask\\.
.* .*
https://github.com/Homebrew/homebrew-cask#reporting-bugs https://github.com/Homebrew/homebrew-cask#reporting-bugs
EOS EOS

View File

@ -3,92 +3,6 @@
RSpec.describe Kernel do RSpec.describe Kernel do
let(:dir) { mktmpdir } let(:dir) { mktmpdir }
def esc(code)
/(\e\[\d+m)*\e\[#{code}m/
end
describe "#ofail" do
it "sets Homebrew.failed to true" do
expect do
ofail "foo"
end.to output("Error: foo\n").to_stderr
expect(Homebrew).to have_failed
end
end
describe "#odie" do
it "exits with 1" do
expect do
odie "foo"
end.to output("Error: foo\n").to_stderr.and raise_error SystemExit
end
end
describe "#pretty_installed" do
subject(:pretty_installed_output) { pretty_installed("foo") }
context "when $stdout is a TTY" do
before { allow($stdout).to receive(:tty?).and_return(true) }
context "with HOMEBREW_NO_EMOJI unset" do
it "returns a string with a colored checkmark" do
expect(pretty_installed_output)
.to match(/#{esc 1}foo #{esc 32}#{esc 0}/)
end
end
context "with HOMEBREW_NO_EMOJI set" do
before { ENV["HOMEBREW_NO_EMOJI"] = "1" }
it "returns a string with colored info" do
expect(pretty_installed_output)
.to match(/#{esc 1}foo \(installed\)#{esc 0}/)
end
end
end
context "when $stdout is not a TTY" do
before { allow($stdout).to receive(:tty?).and_return(false) }
it "returns plain text" do
expect(pretty_installed_output).to eq("foo")
end
end
end
describe "#pretty_uninstalled" do
subject(:pretty_uninstalled_output) { pretty_uninstalled("foo") }
context "when $stdout is a TTY" do
before { allow($stdout).to receive(:tty?).and_return(true) }
context "with HOMEBREW_NO_EMOJI unset" do
it "returns a string with a colored checkmark" do
expect(pretty_uninstalled_output)
.to match(/#{esc 1}foo #{esc 31}#{esc 0}/)
end
end
context "with HOMEBREW_NO_EMOJI set" do
before { ENV["HOMEBREW_NO_EMOJI"] = "1" }
it "returns a string with colored info" do
expect(pretty_uninstalled_output)
.to match(/#{esc 1}foo \(uninstalled\)#{esc 0}/)
end
end
end
context "when $stdout is not a TTY" do
before { allow($stdout).to receive(:tty?).and_return(false) }
it "returns plain text" do
expect(pretty_uninstalled_output).to eq("foo")
end
end
end
describe "#interactive_shell" do describe "#interactive_shell" do
let(:shell) { dir/"myshell" } let(:shell) { dir/"myshell" }
@ -155,16 +69,6 @@ RSpec.describe Kernel do
expect(which_editor).to eq("vemate -w") expect(which_editor).to eq("vemate -w")
end end
describe "#pretty_duration" do
it "converts seconds to a human-readable string" do
expect(pretty_duration(1)).to eq("1 second")
expect(pretty_duration(2.5)).to eq("2 seconds")
expect(pretty_duration(42)).to eq("42 seconds")
expect(pretty_duration(240)).to eq("4 minutes")
expect(pretty_duration(252.45)).to eq("4 minutes 12 seconds")
end
end
specify "#disk_usage_readable" do specify "#disk_usage_readable" do
expect(disk_usage_readable(1)).to eq("1B") expect(disk_usage_readable(1)).to eq("1B")
expect(disk_usage_readable(1000)).to eq("1000B") expect(disk_usage_readable(1000)).to eq("1000B")
@ -198,22 +102,6 @@ RSpec.describe Kernel do
expect(s).to eq(("x" * (n - glue.length)) + glue) expect(s).to eq(("x" * (n - glue.length)) + glue)
end end
describe "#odeprecated" do
it "raises a MethodDeprecatedError when `disable` is true" do
ENV.delete("HOMEBREW_DEVELOPER")
expect do
odeprecated(
"method", "replacement",
caller: ["#{HOMEBREW_LIBRARY}/Taps/playbrew/homebrew-play/"],
disable: true
)
end.to raise_error(
MethodDeprecatedError,
%r{method.*replacement.*playbrew/homebrew-play.*/Taps/playbrew/homebrew-play/}m,
)
end
end
describe "#with_env" do describe "#with_env" do
it "sets environment variables within the block" do it "sets environment variables within the block" do
expect(ENV.fetch("PATH")).not_to eq("/bin") expect(ENV.fetch("PATH")).not_to eq("/bin")

View File

@ -2,6 +2,7 @@
# frozen_string_literal: true # frozen_string_literal: true
require "cli/parser" require "cli/parser"
require "utils/output"
UNDEFINED_CONSTANTS = %w[ UNDEFINED_CONSTANTS = %w[
AbstractDownloadStrategy AbstractDownloadStrategy
@ -74,7 +75,7 @@ parser.parse
UNDEFINED_CONSTANTS.each do |constant_name| UNDEFINED_CONSTANTS.each do |constant_name|
Object.const_get(constant_name) Object.const_get(constant_name)
ofail "#{constant_name} should not be defined at startup" Utils::Output.ofail "#{constant_name} should not be defined at startup"
rescue NameError rescue NameError
# We expect this to error as it should not be defined. # We expect this to error as it should not be defined.
end end

View File

@ -22,6 +22,9 @@ RSpec.describe Tap do
path.mkpath path.mkpath
(path/"audit_exceptions").mkpath (path/"audit_exceptions").mkpath
(path/"style_exceptions").mkpath (path/"style_exceptions").mkpath
# requiring utils/output in tap.rb should be enough but it's not for no apparent reason.
$stderr.extend(Utils::Output::Mixin)
end end
def setup_tap_files def setup_tap_files

View File

@ -0,0 +1,117 @@
# frozen_string_literal: true
require "utils/output"
RSpec.describe Utils::Output do
def esc(code)
/(\e\[\d+m)*\e\[#{code}m/
end
describe "#pretty_installed" do
subject(:pretty_installed_output) { described_class.pretty_installed("foo") }
context "when $stdout is a TTY" do
before { allow($stdout).to receive(:tty?).and_return(true) }
context "with HOMEBREW_NO_EMOJI unset" do
it "returns a string with a colored checkmark" do
expect(pretty_installed_output)
.to match(/#{esc 1}foo #{esc 32}#{esc 0}/)
end
end
context "with HOMEBREW_NO_EMOJI set" do
before { ENV["HOMEBREW_NO_EMOJI"] = "1" }
it "returns a string with colored info" do
expect(pretty_installed_output)
.to match(/#{esc 1}foo \(installed\)#{esc 0}/)
end
end
end
context "when $stdout is not a TTY" do
before { allow($stdout).to receive(:tty?).and_return(false) }
it "returns plain text" do
expect(pretty_installed_output).to eq("foo")
end
end
end
describe "#pretty_uninstalled" do
subject(:pretty_uninstalled_output) { described_class.pretty_uninstalled("foo") }
context "when $stdout is a TTY" do
before { allow($stdout).to receive(:tty?).and_return(true) }
context "with HOMEBREW_NO_EMOJI unset" do
it "returns a string with a colored checkmark" do
expect(pretty_uninstalled_output)
.to match(/#{esc 1}foo #{esc 31}#{esc 0}/)
end
end
context "with HOMEBREW_NO_EMOJI set" do
before { ENV["HOMEBREW_NO_EMOJI"] = "1" }
it "returns a string with colored info" do
expect(pretty_uninstalled_output)
.to match(/#{esc 1}foo \(uninstalled\)#{esc 0}/)
end
end
end
context "when $stdout is not a TTY" do
before { allow($stdout).to receive(:tty?).and_return(false) }
it "returns plain text" do
expect(pretty_uninstalled_output).to eq("foo")
end
end
end
describe "#pretty_duration" do
it "converts seconds to a human-readable string" do
expect(described_class.pretty_duration(1)).to eq("1 second")
expect(described_class.pretty_duration(2.5)).to eq("2 seconds")
expect(described_class.pretty_duration(42)).to eq("42 seconds")
expect(described_class.pretty_duration(240)).to eq("4 minutes")
expect(described_class.pretty_duration(252.45)).to eq("4 minutes 12 seconds")
end
end
describe "#ofail" do
it "sets Homebrew.failed to true" do
expect do
described_class.ofail "foo"
end.to output("Error: foo\n").to_stderr
expect(Homebrew).to have_failed
end
end
describe "#odie" do
it "exits with 1" do
expect do
described_class.odie "foo"
end.to output("Error: foo\n").to_stderr.and raise_error SystemExit
end
end
describe "#odeprecated" do
it "raises a MethodDeprecatedError when `disable` is true" do
ENV.delete("HOMEBREW_DEVELOPER")
expect do
described_class.odeprecated(
"method", "replacement",
caller: ["#{HOMEBREW_LIBRARY}/Taps/playbrew/homebrew-play/"],
disable: true
)
end.to raise_error(
MethodDeprecatedError,
%r{method.*replacement.*playbrew/homebrew-play.*/Taps/playbrew/homebrew-play/}m,
)
end
end
end

View File

@ -2,10 +2,13 @@
# frozen_string_literal: true # frozen_string_literal: true
require "installed_dependents" require "installed_dependents"
require "utils/output"
module Homebrew module Homebrew
# Helper module for uninstalling kegs. # Helper module for uninstalling kegs.
module Uninstall module Uninstall
extend ::Utils::Output::Mixin
def self.uninstall_kegs(kegs_by_rack, casks: [], force: false, ignore_dependencies: false, named_args: []) def self.uninstall_kegs(kegs_by_rack, casks: [], force: false, ignore_dependencies: false, named_args: [])
handle_unsatisfied_dependents(kegs_by_rack, handle_unsatisfied_dependents(kegs_by_rack,
casks:, casks:,
@ -122,6 +125,8 @@ module Homebrew
end end
class DependentsMessage class DependentsMessage
include ::Utils::Output::Mixin
attr_reader :reqs, :deps, :named_args attr_reader :reqs, :deps, :named_args
def initialize(requireds, dependents, named_args: []) def initialize(requireds, dependents, named_args: [])

View File

@ -3,11 +3,14 @@
require "mktemp" require "mktemp"
require "system_command" require "system_command"
require "utils/output"
# Module containing all available strategies for unpacking archives. # Module containing all available strategies for unpacking archives.
module UnpackStrategy module UnpackStrategy
extend T::Helpers extend T::Helpers
extend Utils::Output::Mixin
include SystemCommand::Mixin include SystemCommand::Mixin
include Utils::Output::Mixin
abstract! abstract!

View File

@ -3,6 +3,7 @@
require "tempfile" require "tempfile"
require "system_command" require "system_command"
require "utils/output"
module UnpackStrategy module UnpackStrategy
# Strategy for unpacking disk images. # Strategy for unpacking disk images.
@ -12,6 +13,7 @@ module UnpackStrategy
# Helper module for listing the contents of a volume mounted from a disk image. # Helper module for listing the contents of a volume mounted from a disk image.
module Bom module Bom
extend Utils::Output::Mixin
extend SystemCommand::Mixin extend SystemCommand::Mixin
DMG_METADATA = T.let(Set.new([ DMG_METADATA = T.let(Set.new([

View File

@ -5,12 +5,14 @@ require "bundle_version"
require "cask/cask" require "cask/cask"
require "cask/installer" require "cask/installer"
require "system_command" require "system_command"
require "utils/output"
module Homebrew module Homebrew
# Check unversioned casks for updates by extracting their # Check unversioned casks for updates by extracting their
# contents and guessing the version from contained files. # contents and guessing the version from contained files.
class UnversionedCaskChecker class UnversionedCaskChecker
include SystemCommand::Mixin include SystemCommand::Mixin
include Utils::Output::Mixin
sig { returns(Cask::Cask) } sig { returns(Cask::Cask) }
attr_reader :cask attr_reader :cask

View File

@ -7,10 +7,13 @@ require "development_tools"
require "messages" require "messages"
require "cleanup" require "cleanup"
require "utils/topological_hash" require "utils/topological_hash"
require "utils/output"
module Homebrew module Homebrew
# Helper functions for upgrading formulae. # Helper functions for upgrading formulae.
module Upgrade module Upgrade
extend Utils::Output::Mixin
class Dependents < T::Struct class Dependents < T::Struct
const :upgradeable, T::Array[Formula] const :upgradeable, T::Array[Formula]
const :pinned, T::Array[Formula] const :pinned, T::Array[Formula]

Some files were not shown because too many files have changed in this diff Show More