From 61bb2f6225ed597235c8a1a527793afcec848d37 Mon Sep 17 00:00:00 2001 From: Mike McQuaid Date: Mon, 24 Mar 2025 15:00:16 +0000 Subject: [PATCH] cmd/*env-sync: add `HOMEBREW_ENV_SYNC_STRICT` mode. If this variable is set, `brew *env-sync` will only sync the exact installed versions of formulae rather than all the patch (or, for node, minor and patch) versions. --- Library/Homebrew/cmd/nodenv-sync.rb | 16 ++++++++++--- Library/Homebrew/cmd/pyenv-sync.rb | 23 +++++++++++++++---- Library/Homebrew/cmd/rbenv-sync.rb | 14 +++++++++-- Library/Homebrew/env_config.rb | 4 ++++ .../sorbet/rbi/dsl/homebrew/env_config.rbi | 3 +++ 5 files changed, 50 insertions(+), 10 deletions(-) diff --git a/Library/Homebrew/cmd/nodenv-sync.rb b/Library/Homebrew/cmd/nodenv-sync.rb index 5ae54f19ad..6c6d1d61de 100644 --- a/Library/Homebrew/cmd/nodenv-sync.rb +++ b/Library/Homebrew/cmd/nodenv-sync.rb @@ -55,14 +55,24 @@ module Homebrew minor_version = version.minor.to_i || 0 patch_version = version.patch.to_i || 0 - (0..minor_version).each do |minor| - (0..patch_version).each do |patch| + minor_version_range, patch_version_range = if Homebrew::EnvConfig.env_sync_strict? + # Only create symlinks for the exact installed patch version. + # e.g. 23.9.0 => 23.9.0 + [[minor_version], [patch_version]] + else + # Create folder symlinks for all patch versions to the latest patch version + # e.g. 23.9.0 => 23.10.1 + [0..minor_version, 0..patch_version] + end + + minor_version_range.each do |minor| + patch_version_range.each do |patch| link_path = nodenv_versions/"#{major_version}.#{minor}.#{patch}" # Don't clobber existing user installations. next if link_path.exist? && !link_path.symlink? FileUtils.rm_f link_path - FileUtils.ln_sf path, link_path + FileUtils.ln_s path, link_path end end end diff --git a/Library/Homebrew/cmd/pyenv-sync.rb b/Library/Homebrew/cmd/pyenv-sync.rb index 4b1a84bdd2..d1d5b59ab3 100644 --- a/Library/Homebrew/cmd/pyenv-sync.rb +++ b/Library/Homebrew/cmd/pyenv-sync.rb @@ -54,22 +54,29 @@ module Homebrew minor_version = version.minor.to_i patch_version = version.patch.to_i - (0..patch_version).each do |patch| + patch_version_range = if Homebrew::EnvConfig.env_sync_strict? + # Only create symlinks for the exact installed patch version. + # e.g. 3.11.0 => 3.11.0 + [patch_version] + else # Create folder symlinks for all patch versions to the latest patch version - # (eg. 3.11.0 -> 3.11.3). + # e.g. 3.11.0 => 3.11.3 + 0..patch_version + end + + patch_version_range.each do |patch| link_path = pyenv_versions/"#{major_version}.#{minor_version}.#{patch}" # Don't clobber existing user installations. next if link_path.exist? && !link_path.symlink? FileUtils.rm_f link_path - FileUtils.ln_sf path, link_path + FileUtils.ln_s path, link_path # Create an unversioned symlinks # This is what pyenv expects to find in ~/.pyenv/versions/___/bin'. # Without this, `python3`, `pip3` do not exist and pyenv falls back to system Python. # (eg. python3 -> python3.11, pip3 -> pip3.11) - executables = %w[python3 pip3 wheel3 idle3 pydoc3] executables.each do |executable| major_link_path = link_path/"bin/#{executable}" @@ -77,8 +84,14 @@ module Homebrew # Don't clobber existing user installations. next if major_link_path.exist? && !major_link_path.symlink? + executable_link_path = link_path/"bin/#{executable}.#{minor_version}" FileUtils.rm_f major_link_path - FileUtils.ln_s link_path/"bin/#{executable}.#{minor_version}", major_link_path + + begin + FileUtils.ln_s executable_link_path, major_link_path + rescue => e + opoo "Failed to link #{executable_link_path} to #{major_link_path}: #{e}" + end end end end diff --git a/Library/Homebrew/cmd/rbenv-sync.rb b/Library/Homebrew/cmd/rbenv-sync.rb index 4a9bf777d1..41aa7a6f9a 100644 --- a/Library/Homebrew/cmd/rbenv-sync.rb +++ b/Library/Homebrew/cmd/rbenv-sync.rb @@ -55,13 +55,23 @@ module Homebrew minor_version = version.minor.to_i patch_version = version.patch.to_i || 0 - (0..patch_version).each do |patch| + patch_version_range = if Homebrew::EnvConfig.env_sync_strict? + # Only create symlinks for the exact installed patch version. + # e.g. 3.4.0 => 3.4.0 + [patch_version] + else + # Create folder symlinks for all patch versions to the latest patch version + # e.g. 3.4.0 => 3.4.2 + 0..patch_version + end + + patch_version_range.each do |patch| link_path = rbenv_versions/"#{major_version}.#{minor_version}.#{patch}" # Don't clobber existing user installations. next if link_path.exist? && !link_path.symlink? FileUtils.rm_f link_path - FileUtils.ln_sf path, link_path + FileUtils.ln_s path, link_path end end end diff --git a/Library/Homebrew/env_config.rb b/Library/Homebrew/env_config.rb index 213a0fdae8..2b6cd5981c 100644 --- a/Library/Homebrew/env_config.rb +++ b/Library/Homebrew/env_config.rb @@ -195,6 +195,10 @@ module Homebrew "editors will do strange things in this case.", default_text: "`$EDITOR` or `$VISUAL`.", }, + HOMEBREW_ENV_SYNC_STRICT: { + description: "If set, `brew *env-sync` will only sync the exact installed versions of formulae.", + boolean: true, + }, HOMEBREW_EVAL_ALL: { description: "If set, `brew` commands evaluate all formulae and casks, executing their arbitrary code, by " \ "default without requiring `--eval-all`. Required to cache formula and cask descriptions.", diff --git a/Library/Homebrew/sorbet/rbi/dsl/homebrew/env_config.rbi b/Library/Homebrew/sorbet/rbi/dsl/homebrew/env_config.rbi index 80238758ce..a257500a8f 100644 --- a/Library/Homebrew/sorbet/rbi/dsl/homebrew/env_config.rbi +++ b/Library/Homebrew/sorbet/rbi/dsl/homebrew/env_config.rbi @@ -115,6 +115,9 @@ module Homebrew::EnvConfig sig { returns(T.nilable(::String)) } def editor; end + sig { returns(T::Boolean) } + def env_sync_strict?; end + sig { returns(T::Boolean) } def eval_all?; end