66 lines
		
	
	
		
			2.0 KiB
		
	
	
	
		
			Ruby
		
	
	
	
	
	
			
		
		
	
	
			66 lines
		
	
	
		
			2.0 KiB
		
	
	
	
		
			Ruby
		
	
	
	
	
	
| # typed: strict
 | |
| # frozen_string_literal: true
 | |
| 
 | |
| require "system_command"
 | |
| 
 | |
| module UnpackStrategy
 | |
|   # Strategy for unpacking tar archives.
 | |
|   class Tar
 | |
|     include UnpackStrategy
 | |
|     extend SystemCommand::Mixin
 | |
| 
 | |
|     sig { override.returns(T::Array[String]) }
 | |
|     def self.extensions
 | |
|       [
 | |
|         ".tar",
 | |
|         ".tbz", ".tbz2", ".tar.bz2",
 | |
|         ".tgz", ".tar.gz",
 | |
|         ".tlzma", ".tar.lzma",
 | |
|         ".txz", ".tar.xz",
 | |
|         ".tar.zst",
 | |
|         ".crate"
 | |
|       ]
 | |
|     end
 | |
| 
 | |
|     sig { override.params(path: Pathname).returns(T::Boolean) }
 | |
|     def self.can_extract?(path)
 | |
|       return true if path.magic_number.match?(/\A.{257}ustar/n)
 | |
| 
 | |
|       return false unless [Bzip2, Gzip, Lzip, Xz, Zstd].any? { |s| s.can_extract?(path) }
 | |
| 
 | |
|       # Check if `tar` can list the contents, then it can also extract it.
 | |
|       stdout, _, status = system_command("tar", args: ["--list", "--file", path], print_stderr: false).to_a
 | |
|       (status.success? && !stdout.empty?) || false
 | |
|     end
 | |
| 
 | |
|     private
 | |
| 
 | |
|     sig { override.params(unpack_dir: Pathname, basename: Pathname, verbose: T::Boolean).void }
 | |
|     def extract_to_dir(unpack_dir, basename:, verbose:)
 | |
|       Dir.mktmpdir("homebrew-tar", HOMEBREW_TEMP) do |tmpdir|
 | |
|         tar_path = if DependencyCollector.tar_needs_xz_dependency? && Xz.can_extract?(path)
 | |
|           subextract(Xz, Pathname(tmpdir), verbose)
 | |
|         elsif Zstd.can_extract?(path)
 | |
|           subextract(Zstd, Pathname(tmpdir), verbose)
 | |
|         else
 | |
|           path
 | |
|         end
 | |
| 
 | |
|         system_command! "tar",
 | |
|                         args:    ["--extract", "--no-same-owner",
 | |
|                                   "--file", tar_path,
 | |
|                                   "--directory", unpack_dir],
 | |
|                         verbose:
 | |
|       end
 | |
|     end
 | |
| 
 | |
|     sig {
 | |
|       params(extractor: T.any(T.class_of(Xz), T.class_of(Zstd)), dir: Pathname, verbose: T::Boolean).returns(Pathname)
 | |
|     }
 | |
|     def subextract(extractor, dir, verbose)
 | |
|       extractor.new(path).extract(to: dir, verbose:)
 | |
|       dir.children.fetch(0)
 | |
|     end
 | |
|   end
 | |
| end
 | 
