diff --git a/Library/Homebrew/formula.rb b/Library/Homebrew/formula.rb index 30072c2298cd7..1bea34ff0f161 100644 --- a/Library/Homebrew/formula.rb +++ b/Library/Homebrew/formula.rb @@ -2804,7 +2804,7 @@ def run_test(keep_tmp: false) mktemp("#{name}-test") do |staging| staging.retain! if keep_tmp - @testpath = staging.tmpdir + @testpath = T.must(staging.tmpdir) test_env[:HOME] = @testpath setup_home @testpath begin @@ -3125,8 +3125,16 @@ def eligible_kegs_for_cleanup(quiet: false) # recursively delete the temporary directory. Passing `opts[:retain]` # or calling `do |staging| ... staging.retain!` in the block will skip # the deletion and retain the temporary directory's contents. - def mktemp(prefix = name, opts = {}, &block) - Mktemp.new(prefix, opts).run(&block) + sig { + params( + prefix: String, + retain: T::Boolean, + retain_in_cache: T::Boolean, + block: T.proc.params(arg0: Mktemp).void, + ).void + } + def mktemp(prefix = name, retain: false, retain_in_cache: false, &block) + Mktemp.new(prefix, retain:, retain_in_cache:).run(&block) end # A version of `FileUtils.mkdir` that also changes to that folder in diff --git a/Library/Homebrew/mktemp.rb b/Library/Homebrew/mktemp.rb index 043bfc28b0d43..181f1d3aadba0 100644 --- a/Library/Homebrew/mktemp.rb +++ b/Library/Homebrew/mktemp.rb @@ -1,19 +1,23 @@ -# typed: true # rubocop:todo Sorbet/StrictSigil +# typed: strict # frozen_string_literal: true # Performs {Formula#mktemp}'s functionality and tracks the results. # Each instance is only intended to be used once. +# Can also be used to create a temporary directory with the brew instance's group. class Mktemp include FileUtils - # Path to the tmpdir used in this run, as a {Pathname}. + # Path to the tmpdir used in this run + sig { returns(T.nilable(Pathname)) } attr_reader :tmpdir - def initialize(prefix, opts = {}) + sig { params(prefix: String, retain: T::Boolean, retain_in_cache: T::Boolean).void } + def initialize(prefix, retain: false, retain_in_cache: false) @prefix = prefix - @retain_in_cache = opts[:retain_in_cache] - @retain = opts[:retain] || @retain_in_cache - @quiet = false + @retain_in_cache = T.let(retain_in_cache, T::Boolean) + @retain = T.let(retain || @retain_in_cache, T::Boolean) + @quiet = T.let(false, T::Boolean) + @tmpdir = T.let(nil, T.nilable(Pathname)) end # Instructs this {Mktemp} to retain the staged files. @@ -23,11 +27,13 @@ def retain! end # True if the staged temporary files should be retained. + sig { returns(T::Boolean) } def retain? @retain end # True if the source files should be retained. + sig { returns(T::Boolean) } def retain_in_cache? @retain_in_cache end @@ -43,7 +49,8 @@ def to_s "[Mktemp: #{tmpdir} retain=#{@retain} quiet=#{@quiet}]" end - def run + sig { params(chdir: T::Boolean, _block: T.proc.params(arg0: Mktemp).void).void } + def run(chdir: true, &_block) prefix_name = @prefix.tr "@", "AT" @tmpdir = if retain_in_cache? tmp_dir = HOMEBREW_CACHE/"Sources/#{prefix_name}" @@ -66,13 +73,24 @@ def run Process.gid end begin - chown(nil, group_id, @tmpdir) + @tmpdir.chown(nil, group_id) rescue Errno::EPERM - opoo "Failed setting group \"#{T.must(Etc.getgrgid(group_id)).name}\" on #{@tmpdir}" + require "etc" + group_name = begin + Etc.getgrgid(group_id)&.name + rescue ArgumentError + # Cover for misconfigured NSS setups + nil + end + opoo "Failed setting group \"#{group_name || group_id}\" on #{@tmpdir}" end begin - Dir.chdir(tmpdir) { yield self } + if chdir + Dir.chdir(@tmpdir) { yield self } + else + yield self + end ensure ignore_interrupts { chmod_rm_rf(@tmpdir) } unless retain? end @@ -85,6 +103,7 @@ def run private + sig { params(path: Pathname).void } def chmod_rm_rf(path) if path.directory? && !path.symlink? chmod("u+rw", path) if path.owned? # Need permissions in order to see the contents diff --git a/Library/Homebrew/unpack_strategy.rb b/Library/Homebrew/unpack_strategy.rb index 8f102c7509a2d..5faede3948356 100644 --- a/Library/Homebrew/unpack_strategy.rb +++ b/Library/Homebrew/unpack_strategy.rb @@ -1,6 +1,7 @@ # typed: strict # frozen_string_literal: true +require "mktemp" require "system_command" # Module containing all available strategies for unpacking archives. @@ -157,29 +158,8 @@ def extract(to: nil, basename: nil, verbose: false) ).returns(T.untyped) } def extract_nestedly(to: nil, basename: nil, verbose: false, prioritize_extension: false) - Dir.mktmpdir("homebrew-unpack", HOMEBREW_TEMP) do |tmp_unpack_dir| - tmp_unpack_dir = Pathname(tmp_unpack_dir) - - # Make sure files inside the temporary directory have the same group as the brew instance. - # - # @see https://github.com/Homebrew/brew/blob/4.4.0/Library/Homebrew/mktemp.rb#L57-L72 - group_id = if HOMEBREW_BREW_FILE.grpowned? - HOMEBREW_BREW_FILE.stat.gid - else - Process.gid - end - begin - tmp_unpack_dir.chown(nil, group_id) - rescue Errno::EPERM - require "etc" - group_name = begin - Etc.getgrgid(group_id)&.name - rescue ArgumentError - # Cover for misconfigured NSS setups - nil - end - opoo "Failed setting group \"#{group_name || group_id}\" on #{tmp_unpack_dir}" - end + Mktemp.new("homebrew-unpack").run(chdir: false) do |unpack_dir| + tmp_unpack_dir = T.must(unpack_dir.tmpdir) extract(to: tmp_unpack_dir, basename:, verbose:)