diff --git a/Library/Homebrew/brew.rb b/Library/Homebrew/brew.rb
index 41120b0b04..27b78a0acd 100644
--- a/Library/Homebrew/brew.rb
+++ b/Library/Homebrew/brew.rb
@@ -107,6 +107,24 @@ begin
end
end
+ developer_mode = if cmd == "developer" && ARGV.include?("on")
+ true
+ elsif cmd == "developer" && ARGV.include?("off")
+ false
+ else
+ Homebrew::EnvConfig.developer? || Homebrew::Settings.read("devcmdrun") == "true"
+ end
+
+ if internal_dev_cmd && Homebrew::EnvConfig.install_from_api?
+ odie "Developer commands cannot be run while HOMEBREW_INSTALL_FROM_API is set!"
+ elsif Homebrew::EnvConfig.install_from_api? && developer_mode
+ opoo <<~MESSAGE
+ Developers should not have HOMEBREW_INSTALL_FROM_API set!
+ Please unset HOMEBREW_INSTALL_FROM_API or turn developer mode off by running:
+ brew developer off
+ MESSAGE
+ end
+
unless internal_cmd
# Add contributed commands to PATH before checking.
homebrew_path.append(Tap.cmd_directories)
diff --git a/Library/Homebrew/cask/cask.rb b/Library/Homebrew/cask/cask.rb
index bf274ec823..616a7bff43 100644
--- a/Library/Homebrew/cask/cask.rb
+++ b/Library/Homebrew/cask/cask.rb
@@ -149,7 +149,7 @@ module Cask
return []
end
- latest_version = if ENV["HOMEBREW_INSTALL_FROM_API"].present? &&
+ latest_version = if Homebrew::EnvConfig.install_from_api? &&
(latest_cask_version = Homebrew::API::Versions.latest_cask_version(token))
DSL::Version.new latest_cask_version.to_s
else
diff --git a/Library/Homebrew/cli/named_args.rb b/Library/Homebrew/cli/named_args.rb
index 9a53f77cb5..bab7899a98 100644
--- a/Library/Homebrew/cli/named_args.rb
+++ b/Library/Homebrew/cli/named_args.rb
@@ -94,7 +94,7 @@ module Homebrew
unreadable_error = nil
if only != :cask
- if prefer_loading_from_api && ENV["HOMEBREW_INSTALL_FROM_API"].present? &&
+ if prefer_loading_from_api && Homebrew::EnvConfig.install_from_api? &&
Homebrew::API::Bottle.available?(name)
Homebrew::API::Bottle.fetch_bottles(name)
end
@@ -132,7 +132,7 @@ module Homebrew
end
if only != :formula
- if prefer_loading_from_api && ENV["HOMEBREW_INSTALL_FROM_API"].present? &&
+ if prefer_loading_from_api && Homebrew::EnvConfig.install_from_api? &&
Homebrew::API::CaskSource.available?(name)
contents = Homebrew::API::CaskSource.fetch(name)
end
diff --git a/Library/Homebrew/cmd/info.rb b/Library/Homebrew/cmd/info.rb
index bcba02b574..0c36d9b9d0 100644
--- a/Library/Homebrew/cmd/info.rb
+++ b/Library/Homebrew/cmd/info.rb
@@ -252,7 +252,7 @@ module Homebrew
def info_formula(f, args:)
specs = []
- if ENV["HOMEBREW_INSTALL_FROM_API"].present? && Homebrew::API::Bottle.available?(f.name)
+ if Homebrew::EnvConfig.install_from_api? && Homebrew::API::Bottle.available?(f.name)
info = Homebrew::API::Bottle.fetch(f.name)
latest_version = info["pkg_version"].split("_").first
diff --git a/Library/Homebrew/cmd/outdated.rb b/Library/Homebrew/cmd/outdated.rb
index 17f52b1f68..80013e1856 100644
--- a/Library/Homebrew/cmd/outdated.rb
+++ b/Library/Homebrew/cmd/outdated.rb
@@ -98,7 +98,7 @@ module Homebrew
if verbose?
outdated_kegs = f.outdated_kegs(fetch_head: args.fetch_HEAD?)
- current_version = if !f.head? && ENV["HOMEBREW_INSTALL_FROM_API"].present? &&
+ current_version = if !f.head? && Homebrew::EnvConfig.install_from_api? &&
(f.core_formula? || f.tap.blank?)
Homebrew::API::Versions.latest_formula_version(f.name)&.to_s || f.pkg_version.to_s
elsif f.alias_changed? && !f.latest_formula.latest_version_installed?
diff --git a/Library/Homebrew/cmd/reinstall.rb b/Library/Homebrew/cmd/reinstall.rb
index 08e197b5e3..c29675f3f7 100644
--- a/Library/Homebrew/cmd/reinstall.rb
+++ b/Library/Homebrew/cmd/reinstall.rb
@@ -88,7 +88,7 @@ module Homebrew
# We need to use the bottle API instead of just using the formula file
# from an installed keg because it will not contain bottle information.
# As a consequence, `brew reinstall` will also upgrade outdated formulae
- if ENV["HOMEBREW_INSTALL_FROM_API"].present?
+ if Homebrew::EnvConfig.install_from_api?
args.named.each do |name|
formula = Formulary.factory(name)
next unless formula.any_version_installed?
diff --git a/Library/Homebrew/cmd/untap.rb b/Library/Homebrew/cmd/untap.rb
index 6e076bba81..3c51d60dfa 100644
--- a/Library/Homebrew/cmd/untap.rb
+++ b/Library/Homebrew/cmd/untap.rb
@@ -25,7 +25,7 @@ module Homebrew
args = untap_args.parse
args.named.to_installed_taps.each do |tap|
- odie "Untapping #{tap} is not allowed" if tap.core_tap? && ENV["HOMEBREW_INSTALL_FROM_API"].blank?
+ odie "Untapping #{tap} is not allowed" if tap.core_tap? && !Homebrew::EnvConfig.install_from_api?
installed_tap_formulae = Formula.installed.select { |formula| formula.tap == tap }
installed_tap_casks = Cask::Caskroom.casks.select { |cask| cask.tap == tap }
diff --git a/Library/Homebrew/cmd/update-report.rb b/Library/Homebrew/cmd/update-report.rb
index 49fa06d917..15baa2695f 100644
--- a/Library/Homebrew/cmd/update-report.rb
+++ b/Library/Homebrew/cmd/update-report.rb
@@ -136,9 +136,7 @@ module Homebrew
updated_taps = []
Tap.each do |tap|
next unless tap.git?
- if (tap.core_tap? || tap == "homebrew/cask") && ENV["HOMEBREW_INSTALL_FROM_API"].present? && args.preinstall?
- next
- end
+ next if (tap.core_tap? || tap == "homebrew/cask") && Homebrew::EnvConfig.install_from_api? && args.preinstall?
if ENV["HOMEBREW_MIGRATE_LINUXBREW_FORMULAE"].present? && tap.core_tap? &&
Settings.read("linuxbrewmigrated") != "true"
@@ -266,7 +264,7 @@ module Homebrew
def install_core_tap_if_necessary
return if ENV["HOMEBREW_UPDATE_TEST"]
- return if ENV["HOMEBREW_INSTALL_FROM_API"].present?
+ return if Homebrew::EnvConfig.install_from_api?
core_tap = CoreTap.instance
return if core_tap.installed?
diff --git a/Library/Homebrew/cmd/upgrade.rb b/Library/Homebrew/cmd/upgrade.rb
index aad5f5aa4a..6c4514acd1 100644
--- a/Library/Homebrew/cmd/upgrade.rb
+++ b/Library/Homebrew/cmd/upgrade.rb
@@ -158,7 +158,7 @@ module Homebrew
puts pinned.map { |f| "#{f.full_specified_name} #{f.pkg_version}" } * ", "
end
- if ENV["HOMEBREW_INSTALL_FROM_API"].present?
+ if Homebrew::EnvConfig.install_from_api?
formulae_to_install.map! do |formula|
next formula if formula.head?
next formula if formula.tap.present? && !formula.core_formula?
@@ -223,7 +223,7 @@ module Homebrew
def upgrade_outdated_casks(casks, args:)
return false if args.formula?
- if ENV["HOMEBREW_INSTALL_FROM_API"].present?
+ if Homebrew::EnvConfig.install_from_api?
casks = casks.map do |cask|
next cask if cask.tap.present? && cask.tap != "homebrew/cask"
next cask unless Homebrew::API::CaskSource.available?(cask.token)
diff --git a/Library/Homebrew/env_config.rb b/Library/Homebrew/env_config.rb
index 8d85b31197..b0039ae23f 100644
--- a/Library/Homebrew/env_config.rb
+++ b/Library/Homebrew/env_config.rb
@@ -189,6 +189,11 @@ module Homebrew
default_text: 'The "Beer Mug" emoji.',
default: "🍺",
},
+ HOMEBREW_INSTALL_FROM_API: {
+ description: "If set, install formulae and casks in homebrew/core and homebrew/cask taps using Homebrew's " \
+ "API instead of needing (large, slow) local checkouts of these repositories.",
+ boolean: true,
+ },
HOMEBREW_LIVECHECK_WATCHLIST: {
description: "Consult this file for the list of formulae to check by default when no formula argument " \
"is passed to `brew livecheck`.",
diff --git a/Library/Homebrew/extend/os/mac/tap.rb b/Library/Homebrew/extend/os/mac/tap.rb
index d88902a1d3..b6a9e7b63e 100644
--- a/Library/Homebrew/extend/os/mac/tap.rb
+++ b/Library/Homebrew/extend/os/mac/tap.rb
@@ -4,7 +4,7 @@
class Tap
def self.install_default_cask_tap_if_necessary(force: false)
return false if default_cask_tap.installed?
- return false if ENV["HOMEBREW_INSTALL_FROM_API"].present?
+ return false if Homebrew::EnvConfig.install_from_api?
return false if !force && Tap.untapped_official_taps.include?(default_cask_tap.name)
default_cask_tap.install
diff --git a/Library/Homebrew/formula.rb b/Library/Homebrew/formula.rb
index 6573a85262..2c8a856e3a 100644
--- a/Library/Homebrew/formula.rb
+++ b/Library/Homebrew/formula.rb
@@ -529,7 +529,7 @@ class Formula
# exists and is not empty.
# @private
def latest_version_installed?
- latest_prefix = if !head? && ENV["HOMEBREW_INSTALL_FROM_API"].present? &&
+ latest_prefix = if !head? && Homebrew::EnvConfig.install_from_api? &&
(latest_pkg_version = Homebrew::API::Versions.latest_formula_version(name))
prefix latest_pkg_version
else
@@ -1349,7 +1349,7 @@ class Formula
Formula.cache[:outdated_kegs][cache_key] ||= begin
all_kegs = []
current_version = T.let(false, T::Boolean)
- latest_version = if !head? && ENV["HOMEBREW_INSTALL_FROM_API"].present? && (core_formula? || tap.blank?)
+ latest_version = if !head? && Homebrew::EnvConfig.install_from_api? && (core_formula? || tap.blank?)
Homebrew::API::Versions.latest_formula_version(name) || pkg_version
else
pkg_version
diff --git a/Library/Homebrew/formulary.rb b/Library/Homebrew/formulary.rb
index dc88595f48..589fa5373b 100644
--- a/Library/Homebrew/formulary.rb
+++ b/Library/Homebrew/formulary.rb
@@ -361,7 +361,7 @@ module Formulary
end
def get_formula(*)
- if !CoreTap.instance.installed? && ENV["HOMEBREW_INSTALL_FROM_API"].present?
+ if !CoreTap.instance.installed? && Homebrew::EnvConfig.install_from_api?
raise CoreTapFormulaUnavailableError, name
end
@@ -399,7 +399,7 @@ module Formulary
)
raise ArgumentError, "Formulae must have a ref!" unless ref
- if ENV["HOMEBREW_INSTALL_FROM_API"].present? &&
+ if Homebrew::EnvConfig.install_from_api? &&
@formula_name_local_bottle_path_map.present? &&
@formula_name_local_bottle_path_map.key?(ref)
ref = @formula_name_local_bottle_path_map[ref]
@@ -431,7 +431,7 @@ module Formulary
# @param formula_name the formula name string to map.
# @param local_bottle_path a path pointing to the target bottle archive.
def self.map_formula_name_to_local_bottle_path(formula_name, local_bottle_path)
- if ENV["HOMEBREW_INSTALL_FROM_API"].blank?
+ unless Homebrew::EnvConfig.install_from_api?
raise UsageError, "HOMEBREW_INSTALL_FROM_API not set but required for #{__method__}!"
end
diff --git a/Library/Homebrew/sorbet/rbi/hidden-definitions/hidden.rbi b/Library/Homebrew/sorbet/rbi/hidden-definitions/hidden.rbi
index fd59852672..2cd77dd314 100644
--- a/Library/Homebrew/sorbet/rbi/hidden-definitions/hidden.rbi
+++ b/Library/Homebrew/sorbet/rbi/hidden-definitions/hidden.rbi
@@ -4588,6 +4588,8 @@ module Homebrew::EnvConfig
def self.install_badge(); end
+ def self.install_from_api?(); end
+
def self.livecheck_watchlist(); end
def self.logs(); end
diff --git a/Library/Homebrew/tap.rb b/Library/Homebrew/tap.rb
index 01eb2a37c4..671272713f 100644
--- a/Library/Homebrew/tap.rb
+++ b/Library/Homebrew/tap.rb
@@ -772,7 +772,7 @@ class CoreTap < Tap
def self.ensure_installed!
return if instance.installed?
- return if ENV["HOMEBREW_INSTALL_FROM_API"].present?
+ return if Homebrew::EnvConfig.install_from_api?
safe_system HOMEBREW_BREW_FILE, "tap", instance.name
end
@@ -795,7 +795,7 @@ class CoreTap < Tap
# @private
sig { params(manual: T::Boolean).void }
def uninstall(manual: false)
- raise "Tap#uninstall is not available for CoreTap" if ENV["HOMEBREW_INSTALL_FROM_API"].blank?
+ raise "Tap#uninstall is not available for CoreTap" unless Homebrew::EnvConfig.install_from_api?
super
end
diff --git a/docs/Manpage.md b/docs/Manpage.md
index 53be4196d4..2a564d6bd1 100644
--- a/docs/Manpage.md
+++ b/docs/Manpage.md
@@ -2029,6 +2029,9 @@ example, run `export HOMEBREW_NO_INSECURE_REDIRECT=1` rather than just
*Default:* The "Beer Mug" emoji.
+- `HOMEBREW_INSTALL_FROM_API`
+
If set, install formulae and casks using Homebrew's formulae.brew.sh API instead of using local definitions. This allows formulae and casks in homebrew/core and homebrew/cask to be installed even if their respective tap is not installed locally.
+
- `HOMEBREW_LIVECHECK_WATCHLIST`
Consult this file for the list of formulae to check by default when no formula argument is passed to `brew livecheck`.
diff --git a/manpages/brew.1 b/manpages/brew.1
index 41bcc00e10..dabd715357 100644
--- a/manpages/brew.1
+++ b/manpages/brew.1
@@ -2928,6 +2928,12 @@ Print this text before the installation summary of each successful build\.
\fIDefault:\fR The "Beer Mug" emoji\.
.
.TP
+\fBHOMEBREW_INSTALL_FROM_API\fR
+.
+.br
+If set, install formulae and casks using Homebrew\'s formulae\.brew\.sh API instead of using local definitions\. This allows formulae and casks in homebrew/core and homebrew/cask to be installed even if their respective tap is not installed locally\.
+.
+.TP
\fBHOMEBREW_LIVECHECK_WATCHLIST\fR
.
.br