From 5a921f99e2d8a1dc87161b46604facedfc3ac8bf Mon Sep 17 00:00:00 2001 From: Mike McQuaid Date: Tue, 7 Jan 2025 15:31:06 +0000 Subject: [PATCH] Add support for Homebrew wrappers Allow the ability for a system administrator to use `HOMEBREW_BREW_WRAPPER` and `HOMEBREW_FORCE_BREW_WRAPPER` variables to enforce the usage of a particular `brew` command for non-trivial (e.g. `brew --prefix` is considered trivial, it doesn't need to write to the prefix) Homebrew commands. This also introduces a `HOMEBREW_ORIGINAL_BREW_FILE` variable for some internal usage; `HOMEBREW_BREW_FILE` was being used internally for both "how should we shell out to Homebrew" and "what should we use to check permissions on Homebrew". `HOMEBREW_ORIGINAL_BREW_FILE` is now used just for the latter case. Inspired by conversation in https://github.com/Homebrew/homebrew-bundle/pull/1551 which suggested this was worth fixing in wider than just `brew bundle`. --- Library/Homebrew/brew.sh | 50 ++++++++++++++++--- Library/Homebrew/env_config.rb | 7 +++ Library/Homebrew/formula.rb | 2 +- Library/Homebrew/global.rb | 2 +- Library/Homebrew/mktemp.rb | 4 +- Library/Homebrew/sandbox.rb | 2 +- .../sorbet/rbi/dsl/homebrew/env_config.rbi | 6 +++ Library/Homebrew/startup/config.rb | 5 ++ Library/Homebrew/style.rb | 2 +- .../test/support/lib/startup/config.rb | 1 + bin/brew | 35 +++++++++++-- 11 files changed, 98 insertions(+), 18 deletions(-) diff --git a/Library/Homebrew/brew.sh b/Library/Homebrew/brew.sh index 7c59fd881e8aa..31314fd3c83e2 100644 --- a/Library/Homebrew/brew.sh +++ b/Library/Homebrew/brew.sh @@ -127,12 +127,6 @@ case "$1" in homebrew-shellenv "$1" exit 0 ;; - setup-ruby) - source "${HOMEBREW_LIBRARY}/Homebrew/cmd/setup-ruby.sh" - shift - homebrew-setup-ruby "$1" - exit 0 - ;; esac source "${HOMEBREW_LIBRARY}/Homebrew/help.sh" @@ -184,8 +178,50 @@ case "$@" in ;; esac +source "${HOMEBREW_LIBRARY}/Homebrew/utils/helpers.sh" +if [[ -n "${HOMEBREW_FORCE_BREW_WRAPPER}" ]] +then + if [[ -z "${HOMEBREW_BREW_WRAPPER:-}" ]] + then + odie < When a new file is created, it is given the group of the directory which # contains it. - group_id = if HOMEBREW_BREW_FILE.grpowned? - HOMEBREW_BREW_FILE.stat.gid + group_id = if HOMEBREW_ORIGINAL_BREW_FILE.grpowned? + HOMEBREW_ORIGINAL_BREW_FILE.stat.gid else Process.gid end diff --git a/Library/Homebrew/sandbox.rb b/Library/Homebrew/sandbox.rb index d668288b0f71c..6b3ad1dabde5f 100644 --- a/Library/Homebrew/sandbox.rb +++ b/Library/Homebrew/sandbox.rb @@ -103,7 +103,7 @@ def allow_write_log(formula) sig { void } def deny_write_homebrew_repository - deny_write path: HOMEBREW_BREW_FILE + deny_write path: HOMEBREW_ORIGINAL_BREW_FILE if HOMEBREW_PREFIX.to_s == HOMEBREW_REPOSITORY.to_s deny_write_path HOMEBREW_LIBRARY deny_write_path HOMEBREW_REPOSITORY/".git" diff --git a/Library/Homebrew/sorbet/rbi/dsl/homebrew/env_config.rbi b/Library/Homebrew/sorbet/rbi/dsl/homebrew/env_config.rbi index e86443e2a7a62..2cdeb8ed6aba5 100644 --- a/Library/Homebrew/sorbet/rbi/dsl/homebrew/env_config.rbi +++ b/Library/Homebrew/sorbet/rbi/dsl/homebrew/env_config.rbi @@ -49,6 +49,9 @@ module Homebrew::EnvConfig sig { returns(String) } def brew_git_remote; end + sig { returns(T.nilable(::String)) } + def brew_wrapper; end + sig { returns(T.nilable(::String)) } def browser; end @@ -139,6 +142,9 @@ module Homebrew::EnvConfig sig { returns(T::Boolean) } def force_api_auto_update?; end + sig { returns(T::Boolean) } + def force_brew_wrapper?; end + sig { returns(T::Boolean) } def force_brewed_ca_certificates?; end diff --git a/Library/Homebrew/startup/config.rb b/Library/Homebrew/startup/config.rb index 7ef65a1eaae05..225a3a04a7c02 100644 --- a/Library/Homebrew/startup/config.rb +++ b/Library/Homebrew/startup/config.rb @@ -4,6 +4,11 @@ raise "HOMEBREW_BREW_FILE was not exported! Please call bin/brew directly!" unless ENV["HOMEBREW_BREW_FILE"] # Path to `bin/brew` main executable in `HOMEBREW_PREFIX` +# Used for e.g. permissions checks. +HOMEBREW_ORIGINAL_BREW_FILE = Pathname(ENV.fetch("HOMEBREW_ORIGINAL_BREW_FILE")).freeze + +# Path to the executable that should be used to run `brew`. +# This may be HOMEBREW_ORIGINAL_BREW_FILE or HOMEBREW_BREW_WRAPPER. HOMEBREW_BREW_FILE = Pathname(ENV.fetch("HOMEBREW_BREW_FILE")).freeze # Where we link under diff --git a/Library/Homebrew/style.rb b/Library/Homebrew/style.rb index 493d4fc37d4fb..e25271138b5d6 100644 --- a/Library/Homebrew/style.rb +++ b/Library/Homebrew/style.rb @@ -304,7 +304,7 @@ def self.json_result!(result) def self.shell_scripts [ - HOMEBREW_BREW_FILE, + HOMEBREW_ORIGINAL_BREW_FILE, HOMEBREW_REPOSITORY/"completions/bash/brew", HOMEBREW_REPOSITORY/"Dockerfile", *HOMEBREW_REPOSITORY.glob(".devcontainer/**/*.sh"), diff --git a/Library/Homebrew/test/support/lib/startup/config.rb b/Library/Homebrew/test/support/lib/startup/config.rb index a5c858723bceb..4a1019b265242 100644 --- a/Library/Homebrew/test/support/lib/startup/config.rb +++ b/Library/Homebrew/test/support/lib/startup/config.rb @@ -3,6 +3,7 @@ raise "HOMEBREW_BREW_FILE was not exported! Please call bin/brew directly!" unless ENV["HOMEBREW_BREW_FILE"] +HOMEBREW_ORIGINAL_BREW_FILE = Pathname.new(ENV.fetch("HOMEBREW_ORIGINAL_BREW_FILE")).freeze HOMEBREW_BREW_FILE = Pathname.new(ENV.fetch("HOMEBREW_BREW_FILE")).freeze TEST_TMPDIR = ENV.fetch("HOMEBREW_TEST_TMPDIR") do |k| diff --git a/bin/brew b/bin/brew index 14c135f0f0c23..294091fe816a6 100755 --- a/bin/brew +++ b/bin/brew @@ -116,6 +116,23 @@ unset BREW_FILE_DIRECTORY # keg_relocate.rb, formula_cellar_checks.rb, and test/global_spec.rb need to change. HOMEBREW_LIBRARY="${HOMEBREW_REPOSITORY}/Library" +# Use HOMEBREW_BREW_WRAPPER if set. +export HOMEBREW_ORIGINAL_BREW_FILE="${HOMEBREW_BREW_FILE}" +if [[ -n "${HOMEBREW_BREW_WRAPPER:-}" ]] +then + HOMEBREW_BREW_FILE="${HOMEBREW_BREW_WRAPPER}" +fi + +# These variables are exported in this file and are not allowed to be overridden by the user. +BIN_BREW_EXPORTED_VARS=( + HOMEBREW_BREW_FILE + HOMEBREW_PREFIX + HOMEBREW_REPOSITORY + HOMEBREW_LIBRARY + HOMEBREW_USER_CONFIG_HOME + HOMEBREW_ORIGINAL_BREW_FILE +) + # Load Homebrew's variable configuration files from disk. export_homebrew_env_file() { local env_file @@ -126,6 +143,15 @@ export_homebrew_env_file() { do # only load HOMEBREW_* lines [[ "${line}" = "HOMEBREW_"* ]] || continue + + # forbid overriding variables that are set in this file + local invalid_variable + for VAR in "${BIN_BREW_EXPORTED_VARS[@]}" + do + [[ "${line}" = "${VAR}"* ]] && invalid_variable="${VAR}" + done + [[ -n "${invalid_variable}" ]] && continue + export "${line?}" done <"${env_file}" } @@ -212,11 +238,10 @@ done unset VAR VAR_NEW MANPAGE_VARS USED_BY_HOMEBREW_VARS -export HOMEBREW_BREW_FILE -export HOMEBREW_PREFIX -export HOMEBREW_REPOSITORY -export HOMEBREW_LIBRARY -export HOMEBREW_USER_CONFIG_HOME +for VAR in "${BIN_BREW_EXPORTED_VARS[@]}" +do + export "${VAR?}" +done # set from user environment # shellcheck disable=SC2154