Cleanup extend/ directory usage.

- move some things out of `extend` that don't really fit there e.g.
  `Module`s that are included but not doing any
  overriding/monkeypatching
- move some code into `extend/os` to fix all remaining
  `rubocop:todo Homebrew/MoveToExtendOS`s
- remove some unneeded `bundle` skipper code that doesn't really make
  sense given our current bottling strategy
- extract some `Pathname` extensions to `extend/pathname` for separate
  files
- move a `ENV` `Kernel` extension into `kernel.rb`
- `odeprecate` a seemingly unused backwards compatibility method
- move `readline_nonblock` from a monkeypatch to a
  `ReadlineNonblock.read` method as its only used in one place
- fix up a link in documentation
This commit is contained in:
Mike McQuaid 2025-06-09 19:06:16 +01:00
parent 8ebb2cd65b
commit dc71b7c8f6
No known key found for this signature in database
44 changed files with 368 additions and 273 deletions

View File

@ -1,7 +1,7 @@
# typed: true # rubocop:todo Sorbet/StrictSigil
# frozen_string_literal: true
require "extend/cachable"
require "cachable"
require "api/download"
module Homebrew

View File

@ -1,7 +1,7 @@
# typed: strict
# frozen_string_literal: true
require "extend/cachable"
require "cachable"
require "api/download"
module Homebrew

View File

@ -130,6 +130,9 @@ module Homebrew
@formula_versions_from_env[formula_env_name]
end
sig { void }
def prepend_pkgconf_path_if_needed!; end
sig { void }
def reset!
@mas_installed = T.let(nil, T.nilable(T::Boolean))

View File

@ -64,14 +64,8 @@ module Homebrew
ENV.prepend_path "PATH", Pathname.new(dep_root)/"shims"
end
# Setup pkg-config, if present, to help locate packages
# Only need this on Linux as Homebrew provides a shim on macOS
# TODO: use extend/OS here
# rubocop:todo Homebrew/MoveToExtendOS
if OS.linux? && (pkgconf = Formulary.factory("pkgconf")) && pkgconf.any_version_installed?
ENV.prepend_path "PATH", pkgconf.opt_bin.to_s
end
# rubocop:enable Homebrew/MoveToExtendOS
# Setup pkgconf, if needed, to help locate packages
Bundle.prepend_pkgconf_path_if_needed!
# For commands which aren't either absolute or relative
# Add the command directory to PATH, since it may get blown away by superenv

View File

@ -11,19 +11,6 @@ module Homebrew
def skip?(entry, silent: false)
require "bundle/brew_dumper"
# TODO: use extend/OS here
# rubocop:todo Homebrew/MoveToExtendOS
if (Hardware::CPU.arm? || OS.linux?) &&
Homebrew.default_prefix? &&
entry.type == :brew && entry.name.exclude?("/") &&
(formula = BrewDumper.formulae_by_full_name(entry.name)) &&
formula[:official_tap] &&
!formula[:bottled]
reason = Hardware::CPU.arm? ? "Apple Silicon" : "Linux"
puts Formatter.warning "Skipping #{entry.name} (no bottle for #{reason})" unless silent
return true
end
# rubocop:enable Homebrew/MoveToExtendOS
return true if @failed_taps&.any? do |tap|
prefix = "#{tap}/"
entry.name.start_with?(prefix) || entry.options[:full_name]&.start_with?(prefix)

View File

@ -8,7 +8,7 @@ require "cask/dsl"
require "cask/metadata"
require "cask/tab"
require "utils/bottles"
require "extend/api_hashable"
require "api_hashable"
module Cask
# An instance of a cask.

View File

@ -26,7 +26,7 @@ require "cask/dsl/version"
require "cask/url"
require "cask/utils"
require "extend/on_system"
require "on_system"
module Cask
# Class representing the domain-specific language used for casks.

View File

@ -2,7 +2,7 @@
# frozen_string_literal: true
require "cask/utils"
require "extend/on_system"
require "on_system"
module Cask
class DSL

View File

@ -5,7 +5,7 @@ require "dependency"
require "dependencies"
require "requirement"
require "requirements"
require "extend/cachable"
require "cachable"
# A dependency is a formula that another formula needs to install.
# A requirement is something other than a formula that another formula

View File

@ -227,7 +227,7 @@ module Homebrew
private
sig {
params(string: String, keg: Keg, ignores: T::Array[String],
params(string: String, keg: Keg, ignores: T::Array[Regexp],
formula_and_runtime_deps_names: T.nilable(T::Array[String])).returns(T::Boolean)
}
def keg_contain?(string, keg, ignores, formula_and_runtime_deps_names = nil)
@ -373,35 +373,17 @@ module Homebrew
[gnu_tar(gnu_tar_formula), reproducible_gnutar_args(mtime)].freeze
end
sig { params(formula: T.untyped).returns(T::Array[T.untyped]) }
sig { params(formula: Formula).returns(T::Array[Regexp]) }
def formula_ignores(formula)
ignores = []
cellar_regex = Regexp.escape(HOMEBREW_CELLAR)
prefix_regex = Regexp.escape(HOMEBREW_PREFIX)
# Ignore matches to go keg, because all go binaries are statically linked.
any_go_deps = formula.deps.any? do |dep|
Version.formula_optionally_versioned_regex(:go).match?(dep.name)
end
if any_go_deps
go_regex = Version.formula_optionally_versioned_regex(:go, full: false)
ignores << %r{#{cellar_regex}/#{go_regex}/[\d.]+/libexec}
end
return [] unless any_go_deps
# TODO: Refactor and move to extend/os
# rubocop:disable Homebrew/MoveToExtendOS
ignores << case formula.name
# On Linux, GCC installation can be moved so long as the whole directory tree is moved together:
# https://gcc-help.gcc.gnu.narkive.com/GnwuCA7l/moving-gcc-from-the-installation-path-is-it-allowed.
when Version.formula_optionally_versioned_regex(:gcc)
Regexp.union(%r{#{cellar_regex}/gcc}, %r{#{prefix_regex}/opt/gcc}) if OS.linux?
# binutils is relocatable for the same reason: https://github.com/Homebrew/brew/pull/11899#issuecomment-906804451.
when Version.formula_optionally_versioned_regex(:binutils)
%r{#{cellar_regex}/binutils} if OS.linux?
end
# rubocop:enable Homebrew/MoveToExtendOS
ignores.compact
cellar_regex = Regexp.escape(HOMEBREW_CELLAR)
go_regex = Version.formula_optionally_versioned_regex(:go, full: false)
Array(%r{#{cellar_regex}/#{go_regex}/[\d.]+/libexec})
end
sig { params(formula: Formula).void }

View File

@ -120,31 +120,14 @@ module Homebrew
]
bundle_args << "--fail-fast" if args.fail_fast?
bundle_args << "--profile" << args.profile if args.profile
# TODO: Refactor and move to extend/os
# rubocop:disable Homebrew/MoveToExtendOS
unless OS.mac?
bundle_args << "--tag" << "~needs_macos" << "--tag" << "~cask"
bundle_args << "--tag" << "~needs_homebrew_core" if ENV["CI"]
bundle_args << "--tag" << "~needs_svn" unless args.online?
files = files.grep_v(%r{^test/(os/mac|cask)(/.*|_spec\.rb)$})
end
unless OS.linux?
bundle_args << "--tag" << "~needs_linux" << "--tag" << "~needs_systemd"
files = files.grep_v(%r{^test/os/linux(/.*|_spec\.rb)$})
end
# rubocop:enable Homebrew/MoveToExtendOS
bundle_args << "--tag" << "~needs_arm" unless Hardware::CPU.arm?
bundle_args << "--tag" << "~needs_intel" unless Hardware::CPU.intel?
bundle_args << "--tag" << "~needs_network" unless args.online?
bundle_args << "--tag" << "~needs_ci" unless ENV["CI"]
bundle_args = os_bundle_args(bundle_args)
files = os_files(files)
puts "Randomized with seed #{seed}"
ENV["HOMEBREW_DEBUG"] = "1" if args.debug? # Used in spec_helper.rb to require the "debug" gem.
@ -171,6 +154,41 @@ module Homebrew
private
sig { params(bundle_args: T::Array[String]).returns(T::Array[String]) }
def os_bundle_args(bundle_args)
# for generic tests, remove macOS or Linux specific tests
non_linux_bundle_args(non_macos_bundle_args(bundle_args))
end
sig { params(bundle_args: T::Array[String]).returns(T::Array[String]) }
def non_macos_bundle_args(bundle_args)
bundle_args << "--tag" << "~needs_homebrew_core" if ENV["CI"]
bundle_args << "--tag" << "~needs_svn" unless args.online?
bundle_args << "--tag" << "~needs_macos" << "--tag" << "~cask"
end
sig { params(bundle_args: T::Array[String]).returns(T::Array[String]) }
def non_linux_bundle_args(bundle_args)
bundle_args << "--tag" << "~needs_linux" << "--tag" << "~needs_systemd"
end
sig { params(files: T::Array[String]).returns(T::Array[String]) }
def os_files(files)
# for generic tests, remove macOS or Linux specific files
non_linux_files(non_macos_files(files))
end
sig { params(files: T::Array[String]).returns(T::Array[String]) }
def non_macos_files(files)
files.grep_v(%r{^test/(os/mac|cask)(/.*|_spec\.rb)$})
end
sig { params(files: T::Array[String]).returns(T::Array[String]) }
def non_linux_files(files)
files.grep_v(%r{^test/os/linux(/.*|_spec\.rb)$})
end
sig { returns(T::Array[String]) }
def changed_test_files
changed_files = Utils.popen_read("git", "diff", "--name-only", "master")
@ -250,3 +268,5 @@ module Homebrew
end
end
end
require "extend/os/dev-cmd/tests"

View File

@ -7,16 +7,6 @@ require "extend/ENV/shared"
require "extend/ENV/std"
require "extend/ENV/super"
module Kernel
sig { params(env: T.nilable(String)).returns(T::Boolean) }
def superenv?(env)
return false if env == "std"
!Superenv.bin.nil?
end
private :superenv?
end
# <!-- vale off -->
# @!parse
# # `ENV` is not actually a class, but this makes YARD happy

View File

@ -5,6 +5,14 @@
# TODO: move these out of `Kernel`.
module Kernel
sig { params(env: T.nilable(String)).returns(T::Boolean) }
def superenv?(env)
return false if env == "std"
!Superenv.bin.nil?
end
private :superenv?
def require?(path)
return false if path.nil?

View File

@ -0,0 +1,5 @@
# typed: strict
# frozen_string_literal: true
require "extend/os/linux/dev-cmd/tests" if OS.linux?
require "extend/os/mac/dev-cmd/tests" if OS.mac?

View File

@ -9,6 +9,16 @@ module OS
def mas_installed?
false
end
# Setup pkg-config, if present, to help locate packages
# Only need this on Linux as Homebrew provides a shim on macOS
sig { void }
def prepend_pkgconf_path_if_needed!
pkgconf = Formulary.factory("pkgconf")
return unless pkgconf.any_version_installed?
ENV.prepend_path "PATH", pkgconf.opt_bin.to_s
end
end
end
end

View File

@ -0,0 +1,23 @@
# typed: strict
# frozen_string_literal: true
module OS
module Linux
module DevCmd
module Tests
extend T::Helpers
requires_ancestor { Homebrew::DevCmd::Tests }
private
sig { params(bundle_args: T::Array[String]).returns(T::Array[String]) }
def os_bundle_args(bundle_args)
non_macos_bundle_args(bundle_args)
end
end
end
end
end
Homebrew::DevCmd::Tests.prepend(OS::Linux::DevCmd::Tests)

View File

@ -18,6 +18,26 @@ module OS
def gnu_tar(gnu_tar_formula)
"#{gnu_tar_formula.opt_bin}/gtar"
end
sig { params(formula: Formula).returns(T::Array[Regexp]) }
def formula_ignores(formula)
ignores = super
cellar_regex = Regexp.escape(HOMEBREW_CELLAR)
prefix_regex = Regexp.escape(HOMEBREW_PREFIX)
ignores << case formula.name
# On Linux, GCC installation can be moved so long as the whole directory tree is moved together:
# https://gcc-help.gcc.gnu.narkive.com/GnwuCA7l/moving-gcc-from-the-installation-path-is-it-allowed.
when Version.formula_optionally_versioned_regex(:gcc)
Regexp.union(%r{#{cellar_regex}/gcc}, %r{#{prefix_regex}/opt/gcc}) if OS.linux?
# binutils is relocatable for the same reason: https://github.com/Homebrew/brew/pull/11899#issuecomment-906804451.
when Version.formula_optionally_versioned_regex(:binutils)
%r{#{cellar_regex}/binutils} if OS.linux?
end
ignores.compact
end
end
end
end

View File

@ -0,0 +1,23 @@
# typed: strict
# frozen_string_literal: true
module OS
module Mac
module DevCmd
module Tests
extend T::Helpers
requires_ancestor { Homebrew::DevCmd::Tests }
private
sig { params(bundle_args: T::Array[String]).returns(T::Array[String]) }
def os_bundle_args(bundle_args)
non_linux_bundle_args(bundle_args)
end
end
end
end
end
Homebrew::DevCmd::Tests.prepend(OS::Mac::DevCmd::Tests)

View File

@ -425,8 +425,8 @@ module OS
end
def check_deprecated_caskroom_taps
tapped_caskroom_taps = Tap.select { |t| t.user == "caskroom" || t.name == "phinze/cask" }
.map(&:name)
tapped_caskroom_taps = ::Tap.select { |t| t.user == "caskroom" || t.name == "phinze/cask" }
.map(&:name)
return if tapped_caskroom_taps.empty?
<<~EOS

View File

@ -8,7 +8,7 @@ module OS
requires_ancestor { Kernel }
sig { params(tap: Tap, os_name: T.nilable(Symbol), arch: T.nilable(Symbol)).returns(T::Boolean) }
sig { params(tap: ::Tap, os_name: T.nilable(Symbol), arch: T.nilable(Symbol)).returns(T::Boolean) }
def valid_casks?(tap, os_name: nil, arch: ::Hardware::CPU.type)
return true if os_name == :linux

View File

@ -0,0 +1,17 @@
# typed: strict
# frozen_string_literal: true
module OS
module Mac
module Tap
module ClassMethods
sig { returns(T::Array[::Tap]) }
def core_taps
[CoreTap.instance, CoreCaskTap.instance].freeze
end
end
end
end
end
Tap.singleton_class.prepend(OS::Mac::Tap::ClassMethods)

View File

@ -0,0 +1,4 @@
# typed: strict
# frozen_string_literal: true
require "extend/os/mac/tap" if OS.mac?

View File

@ -1,79 +1,9 @@
# typed: true # rubocop:todo Sorbet/StrictSigil
# frozen_string_literal: true
module DiskUsageExtension
extend T::Helpers
requires_ancestor { Pathname }
sig { returns(Integer) }
def disk_usage
return @disk_usage if defined?(@disk_usage)
compute_disk_usage
@disk_usage
end
sig { returns(Integer) }
def file_count
return @file_count if defined?(@file_count)
compute_disk_usage
@file_count
end
sig { returns(String) }
def abv
out = +""
compute_disk_usage
out << "#{number_readable(@file_count)} files, " if @file_count > 1
out << disk_usage_readable(@disk_usage).to_s
out.freeze
end
private
sig { void }
def compute_disk_usage
if symlink? && !exist?
@file_count = 1
@disk_usage = 0
return
end
path = if symlink?
resolved_path
else
self
end
if path.directory?
scanned_files = Set.new
@file_count = 0
@disk_usage = 0
path.find do |f|
if f.directory?
@disk_usage += f.lstat.size
else
@file_count += 1 if f.basename.to_s != ".DS_Store"
# use Pathname#lstat instead of Pathname#stat to get info of symlink itself.
stat = f.lstat
file_id = [stat.dev, stat.ino]
# count hardlinks only once.
unless scanned_files.include?(file_id)
@disk_usage += stat.size
scanned_files.add(file_id)
end
end
end
else
@file_count = 1
@disk_usage = path.lstat.size
end
end
end
require "system_command"
require "extend/pathname/disk_usage_extension"
require "extend/pathname/observer_pathname_extension"
# 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
@ -524,92 +454,3 @@ class Pathname
end
end
require "extend/os/pathname"
require "context"
module ObserverPathnameExtension
extend T::Helpers
requires_ancestor { Pathname }
class << self
include Context
sig { returns(Integer) }
attr_accessor :n, :d
sig { void }
def reset_counts!
@n = @d = 0
@put_verbose_trimmed_warning = false
end
sig { returns(Integer) }
def total
n + d
end
sig { returns([Integer, Integer]) }
def counts
[n, d]
end
MAXIMUM_VERBOSE_OUTPUT = 100
private_constant :MAXIMUM_VERBOSE_OUTPUT
sig { returns(T::Boolean) }
def verbose?
return super unless ENV["CI"]
return false unless super
if total < MAXIMUM_VERBOSE_OUTPUT
true
else
unless @put_verbose_trimmed_warning
puts "Only the first #{MAXIMUM_VERBOSE_OUTPUT} operations were output."
@put_verbose_trimmed_warning = true
end
false
end
end
end
sig { void }
def unlink
super
puts "rm #{self}" if ObserverPathnameExtension.verbose?
ObserverPathnameExtension.n += 1
end
sig { void }
def mkpath
super
puts "mkdir -p #{self}" if ObserverPathnameExtension.verbose?
end
sig { void }
def rmdir
super
puts "rmdir #{self}" if ObserverPathnameExtension.verbose?
ObserverPathnameExtension.d += 1
end
sig { params(src: Pathname).void }
def make_relative_symlink(src)
super
puts "ln -s #{src.relative_path_from(dirname)} #{basename}" if ObserverPathnameExtension.verbose?
ObserverPathnameExtension.n += 1
end
sig { void }
def install_info
super
puts "info #{self}" if ObserverPathnameExtension.verbose?
end
sig { void }
def uninstall_info
super
puts "uninfo #{self}" if ObserverPathnameExtension.verbose?
end
end

View File

@ -0,0 +1,74 @@
# typed: true # rubocop:todo Sorbet/StrictSigil
# frozen_string_literal: true
module DiskUsageExtension
extend T::Helpers
requires_ancestor { Pathname }
sig { returns(Integer) }
def disk_usage
return @disk_usage if defined?(@disk_usage)
compute_disk_usage
@disk_usage
end
sig { returns(Integer) }
def file_count
return @file_count if defined?(@file_count)
compute_disk_usage
@file_count
end
sig { returns(String) }
def abv
out = +""
compute_disk_usage
out << "#{number_readable(@file_count)} files, " if @file_count > 1
out << disk_usage_readable(@disk_usage).to_s
out.freeze
end
private
sig { void }
def compute_disk_usage
if symlink? && !exist?
@file_count = 1
@disk_usage = 0
return
end
path = if symlink?
resolved_path
else
self
end
if path.directory?
scanned_files = Set.new
@file_count = 0
@disk_usage = 0
path.find do |f|
if f.directory?
@disk_usage += f.lstat.size
else
@file_count += 1 if f.basename.to_s != ".DS_Store"
# use Pathname#lstat instead of Pathname#stat to get info of symlink itself.
stat = f.lstat
file_id = [stat.dev, stat.ino]
# count hardlinks only once.
unless scanned_files.include?(file_id)
@disk_usage += stat.size
scanned_files.add(file_id)
end
end
end
else
@file_count = 1
@disk_usage = path.lstat.size
end
end
end

View File

@ -0,0 +1,91 @@
# typed: true # rubocop:todo Sorbet/StrictSigil
# frozen_string_literal: true
require "context"
module ObserverPathnameExtension
extend T::Helpers
requires_ancestor { Pathname }
class << self
include Context
sig { returns(Integer) }
attr_accessor :n, :d
sig { void }
def reset_counts!
@n = @d = 0
@put_verbose_trimmed_warning = false
end
sig { returns(Integer) }
def total
n + d
end
sig { returns([Integer, Integer]) }
def counts
[n, d]
end
MAXIMUM_VERBOSE_OUTPUT = 100
private_constant :MAXIMUM_VERBOSE_OUTPUT
sig { returns(T::Boolean) }
def verbose?
return super unless ENV["CI"]
return false unless super
if total < MAXIMUM_VERBOSE_OUTPUT
true
else
unless @put_verbose_trimmed_warning
puts "Only the first #{MAXIMUM_VERBOSE_OUTPUT} operations were output."
@put_verbose_trimmed_warning = true
end
false
end
end
end
sig { void }
def unlink
super
puts "rm #{self}" if ObserverPathnameExtension.verbose?
ObserverPathnameExtension.n += 1
end
sig { void }
def mkpath
super
puts "mkdir -p #{self}" if ObserverPathnameExtension.verbose?
end
sig { void }
def rmdir
super
puts "rmdir #{self}" if ObserverPathnameExtension.verbose?
ObserverPathnameExtension.d += 1
end
sig { params(src: Pathname).void }
def make_relative_symlink(src)
super
puts "ln -s #{src.relative_path_from(dirname)} #{basename}" if ObserverPathnameExtension.verbose?
ObserverPathnameExtension.n += 1
end
sig { void }
def install_info
super
puts "info #{self}" if ObserverPathnameExtension.verbose?
end
sig { void }
def uninstall_info
super
puts "uninfo #{self}" if ObserverPathnameExtension.verbose?
end
end

View File

@ -5,5 +5,9 @@ require "time"
class Time
# Backwards compatibility for formulae that used this ActiveSupport extension
alias rfc3339 xmlschema
sig { returns(String) }
def rfc3339
odeprecated "Time#rfc3339", "Time#xmlschema"
xmlschema
end
end

View File

@ -38,9 +38,9 @@ require "tab"
require "mktemp"
require "find"
require "utils/spdx"
require "extend/on_system"
require "on_system"
require "api"
require "extend/api_hashable"
require "api_hashable"
# A formula provides instructions and metadata for Homebrew to install a piece
# of software. Every Homebrew formula is a {Formula}.

View File

@ -2,7 +2,7 @@
# frozen_string_literal: true
require "digest/sha2"
require "extend/cachable"
require "cachable"
require "tab"
require "utils"
require "utils/bottles"

View File

@ -138,7 +138,7 @@ require "extend/kernel"
require "os"
require "extend/array"
require "extend/cachable"
require "cachable"
require "extend/enumerable"
require "extend/string"
require "extend/pathname"

View File

@ -4,7 +4,7 @@
require "keg_relocate"
require "language/python"
require "lock_file"
require "extend/cachable"
require "cachable"
# Installation prefix of a formula.
class Keg

View File

@ -1,17 +1,17 @@
# typed: strict
# frozen_string_literal: true
class IO
sig { params(sep: String).returns(String) }
def readline_nonblock(sep = $INPUT_RECORD_SEPARATOR)
class ReadlineNonblock
sig { params(io: IO).returns(String) }
def self.read(io)
line = +""
buffer = +""
begin
loop do
break if buffer == sep
break if buffer == $INPUT_RECORD_SEPARATOR
read_nonblock(1, buffer)
io.read_nonblock(1, buffer)
line.concat(buffer)
end

View File

@ -4,7 +4,7 @@
require "downloadable"
require "mktemp"
require "livecheck"
require "extend/on_system"
require "on_system"
# Resource is the fundamental representation of an external resource. The
# primary formula download, along with other declared resources, are instances

View File

@ -4,7 +4,7 @@
require "cxxstdlib"
require "json"
require "development_tools"
require "extend/cachable"
require "cachable"
require "utils/curl"
# Rather than calling `new` directly, use one of the class methods like {SBOM.create}.

View File

@ -2,7 +2,7 @@
# frozen_string_literal: true
require "ipaddr"
require "extend/on_system"
require "on_system"
require "utils/service"
module Homebrew

View File

@ -12,7 +12,7 @@ require "utils/bottles"
require "patch"
require "compilers"
require "macos_version"
require "extend/on_system"
require "on_system"
class SoftwareSpec
include Downloadable

View File

@ -6,7 +6,7 @@ require "shellwords"
require "uri"
require "context"
require "extend/io"
require "readline_nonblock"
require "utils/timer"
# Class for running sub-processes and capturing their output and exit status.
@ -361,7 +361,7 @@ class SystemCommand
readable_sources.each do |source|
loop do
line = source.readline_nonblock || ""
line = ReadlineNonblock.read(source)
yield(sources.fetch(source), line)
end
rescue EOFError

View File

@ -5,7 +5,7 @@ require "cxxstdlib"
require "options"
require "json"
require "development_tools"
require "extend/cachable"
require "cachable"
# Rather than calling `new` directly, use one of the class methods like {Tab.create}.
class AbstractTab

View File

@ -1079,15 +1079,12 @@ class Tap
# All locally installed and core taps. Core taps might not be installed locally when using the API.
sig { returns(T::Array[Tap]) }
def self.all
cache[:all] ||= begin
core_taps = [
CoreTap.instance,
# The conditional is valid here because we only want the cask tap on macOS.
(CoreCaskTap.instance if OS.mac?), # rubocop:disable Homebrew/MoveToExtendOS
].compact
cache[:all] ||= installed | core_taps
end
installed | core_taps
end
sig { returns(T::Array[Tap]) }
def self.core_taps
[CoreTap.instance].freeze
end
# Enumerate all available {Tap}s.
@ -1517,3 +1514,5 @@ class TapConfig
Homebrew::Settings.delete key, repo: tap.path
end
end
require "extend/os/tap"

View File

@ -4,7 +4,7 @@
require "context"
require "erb"
require "settings"
require "extend/cachable"
require "cachable"
module Utils
# Helper module for fetching and reporting analytics data.

View File

@ -34,7 +34,7 @@ For more information on how to express more complex types, refer to the official
### Ruby interface files (`.rbi`)
[RBI files](https://sorbet.org/docs/rbi) help Sorbet learn about constants, ancestors and methods defined in ways it doesn't understand natively. We can also create an RBI file to help Sorbet understand dynamic definitions. Some of these files are automatically generated (see the next section) and some are manually written, e.g. [`extend/on_system.rbi`](https://github.com/Homebrew/brew/blob/975fe8a83fd57a8d8e790ec6fb10c2f13f705d02/Library/Homebrew/extend/on_system.rbi).
[RBI files](https://sorbet.org/docs/rbi) help Sorbet learn about constants, ancestors and methods defined in ways it doesn't understand natively. We can also create an RBI file to help Sorbet understand dynamic definitions. Some of these files are automatically generated (see the next section) and some are manually written, e.g. [`on_system.rbi`](https://github.com/Homebrew/brew/blob/HEAD/Library/Homebrew/on_system.rbi).
There are also a very small number of files that Homebrew loads before `sorbet-runtime`, such as `utils/gems.rb`. Those files cannot have type signatures alongside the code itself, so RBI files are used there instead to retain static type checking.