From 5fed8f42102ee61ba90537dfc236f3a24a08923e Mon Sep 17 00:00:00 2001 From: Issy Long Date: Thu, 26 Jun 2025 23:21:54 +0100 Subject: [PATCH 1/3] Make the remaining Bundle file Sorbet `typed: true` --- Library/Homebrew/bundle/commands/exec.rb | 44 ++++++++++++++++-------- 1 file changed, 30 insertions(+), 14 deletions(-) diff --git a/Library/Homebrew/bundle/commands/exec.rb b/Library/Homebrew/bundle/commands/exec.rb index 588de100e2..35392248fb 100644 --- a/Library/Homebrew/bundle/commands/exec.rb +++ b/Library/Homebrew/bundle/commands/exec.rb @@ -1,4 +1,4 @@ -# typed: false # rubocop:todo Sorbet/TrueSigil +# typed: true # frozen_string_literal: true require "English" @@ -13,6 +13,16 @@ module Homebrew module Exec PATH_LIKE_ENV_REGEX = /.+#{File::PATH_SEPARATOR}/ + sig { + params( + args: String, + global: T::Boolean, + file: T.nilable(String), + subcommand: String, + services: T::Boolean, + check: T::Boolean, + ).void + } def self.run(*args, global: false, file: nil, subcommand: "", services: false, check: false) if check require "bundle/commands/check" @@ -22,12 +32,13 @@ module Homebrew # Store the old environment so we can check if things were already set # before we start mutating it. old_env = ENV.to_h + new_env = T.cast(ENV, Superenv) # Setup Homebrew's ENV extensions ENV.activate_extensions! - raise UsageError, "No command to execute was specified!" if args.blank? command = args.first + raise UsageError, "No command to execute was specified!" if command.blank? require "bundle/brewfile" @dsl = Brewfile.read(global:, file:) @@ -35,7 +46,7 @@ module Homebrew require "formula" require "formulary" - ENV.deps = @dsl.entries.filter_map do |entry| + new_env.deps = @dsl.entries.filter_map do |entry| next if entry.type != :brew Formulary.factory(entry.name) @@ -43,20 +54,20 @@ module Homebrew # Allow setting all dependencies to be keg-only # (i.e. should be explicitly in HOMEBREW_*PATHs ahead of HOMEBREW_PREFIX) - ENV.keg_only_deps = if ENV["HOMEBREW_BUNDLE_EXEC_ALL_KEG_ONLY_DEPS"].present? + new_env.keg_only_deps = if ENV["HOMEBREW_BUNDLE_EXEC_ALL_KEG_ONLY_DEPS"].present? ENV.delete("HOMEBREW_BUNDLE_EXEC_ALL_KEG_ONLY_DEPS") - ENV.deps + new_env.deps else - ENV.deps.select(&:keg_only?) + new_env.deps.select(&:keg_only?) end - ENV.setup_build_environment + new_env.setup_build_environment # Enable compiler flag filtering ENV.refurbish_args # Set up `nodenv`, `pyenv` and `rbenv` if present. env_formulae = %w[nodenv pyenv rbenv] - ENV.deps.each do |dep| + new_env.deps.each do |dep| dep_name = dep.name next unless env_formulae.include?(dep_name) @@ -74,7 +85,7 @@ module Homebrew end # Replace the formula versions from the environment variables - ENV.deps.each do |formula| + new_env.deps.each do |formula| formula_name = formula.name formula_version = Bundle.formula_versions_from_env(formula_name) next unless formula_version @@ -90,7 +101,12 @@ module Homebrew rejected_opts = [] path = PATH.new(ENV.fetch("PATH")) .reject do |path_value| - rejected_opts << path_value if path_value.match?(opt) + if path_value.match?(opt) + rejected_opts << path_value + true + else + false + end end rejected_opts.each do |path_value| path.prepend(path_value.gsub(opt, cellar)) @@ -181,7 +197,7 @@ module Homebrew if services require "bundle/brew_services" - exit_code = 0 + exit_code = T.let(0, Integer) run_services(@dsl.entries) do Kernel.system(*args) if (system_exit_code = $CHILD_STATUS&.exitstatus) @@ -199,9 +215,9 @@ module Homebrew entries: T::Array[Homebrew::Bundle::Dsl::Entry], _block: T.proc.params( entry: Homebrew::Bundle::Dsl::Entry, - info: T::Hash[String, T.anything], + info: T::Hash[String, T.untyped], service_file: Pathname, - conflicting_services: T::Array[T::Hash[String, T.anything]], + conflicting_services: T::Array[T::Hash[String, T.untyped]], ).void, ).void } @@ -279,7 +295,7 @@ module Homebrew map_service_info(entries) do |entry, info, service_file, conflicting_services| # Don't restart if already running this version loaded_file = Pathname.new(info["loaded_file"].to_s) - next if info["running"] && loaded_file&.file? && loaded_file&.realpath == service_file.realpath + next if info["running"] && loaded_file.file? && loaded_file.realpath == service_file.realpath if info["running"] && !Bundle::BrewServices.stop(info["name"], keep: true) opoo "Failed to stop #{info["name"]} service" From 004c0a2a6c01fccbb68d12a0e10f7deab1cf196a Mon Sep 17 00:00:00 2001 From: Douglas Eichelberger Date: Sun, 29 Jun 2025 08:05:23 -0700 Subject: [PATCH 2/3] Refactor exec.rb type fixes --- Library/Homebrew/PATH.rb | 4 ++-- Library/Homebrew/bundle/commands/exec.rb | 24 +++++++++--------------- Library/Homebrew/cmd/--env.rb | 2 +- Library/Homebrew/dev-cmd/sh.rb | 12 +++++++----- Library/Homebrew/extend/ENV.rbi | 4 ++++ 5 files changed, 23 insertions(+), 23 deletions(-) diff --git a/Library/Homebrew/PATH.rb b/Library/Homebrew/PATH.rb index 46fd38bb3f..099613ac95 100644 --- a/Library/Homebrew/PATH.rb +++ b/Library/Homebrew/PATH.rb @@ -39,12 +39,12 @@ class PATH self end - sig { params(block: T.proc.params(arg0: String).returns(T::Boolean)).returns(T.self_type) } + sig { params(block: T.proc.params(arg0: String).returns(BasicObject)).returns(T.self_type) } def select(&block) self.class.new(@paths.select(&block)) end - sig { params(block: T.proc.params(arg0: String).returns(T::Boolean)).returns(T.self_type) } + sig { params(block: T.proc.params(arg0: String).returns(BasicObject)).returns(T.self_type) } def reject(&block) self.class.new(@paths.reject(&block)) end diff --git a/Library/Homebrew/bundle/commands/exec.rb b/Library/Homebrew/bundle/commands/exec.rb index 35392248fb..b6c698ff28 100644 --- a/Library/Homebrew/bundle/commands/exec.rb +++ b/Library/Homebrew/bundle/commands/exec.rb @@ -32,7 +32,6 @@ module Homebrew # Store the old environment so we can check if things were already set # before we start mutating it. old_env = ENV.to_h - new_env = T.cast(ENV, Superenv) # Setup Homebrew's ENV extensions ENV.activate_extensions! @@ -46,7 +45,7 @@ module Homebrew require "formula" require "formulary" - new_env.deps = @dsl.entries.filter_map do |entry| + ENV.deps = @dsl.entries.filter_map do |entry| next if entry.type != :brew Formulary.factory(entry.name) @@ -54,20 +53,20 @@ module Homebrew # Allow setting all dependencies to be keg-only # (i.e. should be explicitly in HOMEBREW_*PATHs ahead of HOMEBREW_PREFIX) - new_env.keg_only_deps = if ENV["HOMEBREW_BUNDLE_EXEC_ALL_KEG_ONLY_DEPS"].present? + ENV.keg_only_deps = if ENV["HOMEBREW_BUNDLE_EXEC_ALL_KEG_ONLY_DEPS"].present? ENV.delete("HOMEBREW_BUNDLE_EXEC_ALL_KEG_ONLY_DEPS") - new_env.deps + ENV.deps else - new_env.deps.select(&:keg_only?) + ENV.deps.select(&:keg_only?) end - new_env.setup_build_environment + ENV.setup_build_environment # Enable compiler flag filtering ENV.refurbish_args # Set up `nodenv`, `pyenv` and `rbenv` if present. env_formulae = %w[nodenv pyenv rbenv] - new_env.deps.each do |dep| + ENV.deps.each do |dep| dep_name = dep.name next unless env_formulae.include?(dep_name) @@ -85,7 +84,7 @@ module Homebrew end # Replace the formula versions from the environment variables - new_env.deps.each do |formula| + ENV.deps.each do |formula| formula_name = formula.name formula_version = Bundle.formula_versions_from_env(formula_name) next unless formula_version @@ -101,12 +100,7 @@ module Homebrew rejected_opts = [] path = PATH.new(ENV.fetch("PATH")) .reject do |path_value| - if path_value.match?(opt) - rejected_opts << path_value - true - else - false - end + rejected_opts << path_value if path_value.match?(opt) end rejected_opts.each do |path_value| path.prepend(path_value.gsub(opt, cellar)) @@ -217,7 +211,7 @@ module Homebrew entry: Homebrew::Bundle::Dsl::Entry, info: T::Hash[String, T.untyped], service_file: Pathname, - conflicting_services: T::Array[T::Hash[String, T.untyped]], + conflicting_services: T::Array[T::Hash[String, T.anything]], ).void, ).void } diff --git a/Library/Homebrew/cmd/--env.rb b/Library/Homebrew/cmd/--env.rb index e0a2a50307..90f505942d 100644 --- a/Library/Homebrew/cmd/--env.rb +++ b/Library/Homebrew/cmd/--env.rb @@ -31,7 +31,7 @@ module Homebrew sig { override.void } def run ENV.activate_extensions! - T.cast(ENV, Superenv).deps = args.named.to_formulae if superenv?(nil) + ENV.deps = args.named.to_formulae if superenv?(nil) ENV.setup_build_environment shell = if args.plain? diff --git a/Library/Homebrew/dev-cmd/sh.rb b/Library/Homebrew/dev-cmd/sh.rb index 2ba18a56e9..df034059fe 100644 --- a/Library/Homebrew/dev-cmd/sh.rb +++ b/Library/Homebrew/dev-cmd/sh.rb @@ -16,10 +16,10 @@ module Homebrew in an Xcode-only configuration since it adds tools like `make` to your `$PATH` which build systems would not find otherwise. EOS - flag "--env=", - description: "Use the standard `$PATH` instead of superenv's when `std` is passed." - flag "-c=", "--cmd=", - description: "Execute commands in a non-interactive shell." + flag "--env=", + description: "Use the standard `$PATH` instead of superenv's when `std` is passed." + flag "-c=", "--cmd=", + description: "Execute commands in a non-interactive shell." named_args :file, max: 1 end @@ -29,7 +29,9 @@ module Homebrew ENV.activate_extensions!(env: args.env) if superenv?(args.env) - T.cast(ENV, Superenv).deps = Formula.installed.select { |f| f.keg_only? && f.opt_prefix.directory? } + ENV.deps = Formula.installed.select do |f| + f.keg_only? && f.opt_prefix.directory? + end end ENV.setup_build_environment if superenv?(args.env) diff --git a/Library/Homebrew/extend/ENV.rbi b/Library/Homebrew/extend/ENV.rbi index 24711f725f..b7727c58c9 100644 --- a/Library/Homebrew/extend/ENV.rbi +++ b/Library/Homebrew/extend/ENV.rbi @@ -10,6 +10,10 @@ class Sorbet module Static class ENVClass include EnvActivation + # NOTE: This is a bit misleading, as at most only one of these can be true + # See: EnvActivation#activate_extensions! + include Stdenv + include Superenv end end end From fb01c0fa961a62b400f271a208d8d8d1aef1f5c7 Mon Sep 17 00:00:00 2001 From: Douglas Eichelberger Date: Sun, 29 Jun 2025 11:30:06 -0700 Subject: [PATCH 3/3] Update another overly narrow return type --- Library/Homebrew/dependencies.rbi | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Library/Homebrew/dependencies.rbi b/Library/Homebrew/dependencies.rbi index 29a8508c06..5cdc3fcad4 100644 --- a/Library/Homebrew/dependencies.rbi +++ b/Library/Homebrew/dependencies.rbi @@ -13,8 +13,8 @@ class Dependencies < SimpleDelegator sig { returns(T::Enumerator[Dependency]) } def each(&blk); end - sig { params(blk: T.proc.params(arg0: Dependency).returns(T::Boolean)).returns(T::Array[Dependency]) } - sig { returns(T::Enumerator[Dependency]) } + sig { override.params(blk: T.proc.params(arg0: Dependency).returns(T.anything)).returns(T::Array[Dependency]) } + sig { override.returns(T::Enumerator[Dependency]) } def select(&blk); end end @@ -26,7 +26,7 @@ class Requirements < SimpleDelegator sig { returns(T::Enumerator[Requirement]) } def each(&blk); end - sig { params(blk: T.proc.params(arg0: Requirement).returns(T::Boolean)).returns(T::Array[Requirement]) } - sig { returns(T::Enumerator[Requirement]) } + sig { override.params(blk: T.proc.params(arg0: Requirement).returns(T.anything)).returns(T::Array[Requirement]) } + sig { override.returns(T::Enumerator[Requirement]) } def select(&blk); end end