2024-07-05 17:49:32 +01:00
|
|
|
# typed: strict
|
2019-04-19 15:38:03 +09:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
2020-10-10 17:53:31 +02:00
|
|
|
require "system_command"
|
|
|
|
|
2020-08-09 06:09:05 +02:00
|
|
|
# Module containing all available strategies for unpacking archives.
|
2018-07-23 20:59:21 +02:00
|
|
|
module UnpackStrategy
|
2020-10-20 00:56:50 +02:00
|
|
|
extend T::Helpers
|
|
|
|
|
2024-07-05 17:49:32 +01:00
|
|
|
AnyStrategy = T.type_alias do # rubocop:disable Style/MutableConstant
|
|
|
|
T.any(
|
|
|
|
T.class_of(UnpackStrategy::Tar),
|
|
|
|
T.class_of(UnpackStrategy::Pax),
|
|
|
|
T.class_of(UnpackStrategy::Gzip),
|
|
|
|
T.class_of(UnpackStrategy::Dmg),
|
|
|
|
T.class_of(UnpackStrategy::Lzma),
|
|
|
|
T.class_of(UnpackStrategy::Xz),
|
|
|
|
T.class_of(UnpackStrategy::Zstd),
|
|
|
|
T.class_of(UnpackStrategy::Lzip),
|
|
|
|
T.class_of(UnpackStrategy::Air),
|
|
|
|
T.class_of(UnpackStrategy::Jar),
|
|
|
|
T.class_of(UnpackStrategy::LuaRock),
|
|
|
|
T.class_of(UnpackStrategy::MicrosoftOfficeXml),
|
|
|
|
T.class_of(UnpackStrategy::Zip),
|
|
|
|
T.class_of(UnpackStrategy::Pkg),
|
|
|
|
T.class_of(UnpackStrategy::Xar),
|
|
|
|
T.class_of(UnpackStrategy::Ttf),
|
|
|
|
T.class_of(UnpackStrategy::Otf),
|
|
|
|
T.class_of(UnpackStrategy::Git),
|
|
|
|
T.class_of(UnpackStrategy::Mercurial),
|
|
|
|
T.class_of(UnpackStrategy::Subversion),
|
|
|
|
T.class_of(UnpackStrategy::Cvs),
|
|
|
|
T.class_of(UnpackStrategy::SelfExtractingExecutable),
|
|
|
|
T.class_of(UnpackStrategy::Cab),
|
|
|
|
T.class_of(UnpackStrategy::Executable),
|
|
|
|
T.class_of(UnpackStrategy::Bzip2),
|
|
|
|
T.class_of(UnpackStrategy::Fossil),
|
|
|
|
T.class_of(UnpackStrategy::Bazaar),
|
|
|
|
T.class_of(UnpackStrategy::P7Zip),
|
|
|
|
T.class_of(UnpackStrategy::Sit),
|
|
|
|
T.class_of(UnpackStrategy::Rar),
|
|
|
|
T.class_of(UnpackStrategy::Lha),
|
|
|
|
)
|
|
|
|
end
|
|
|
|
|
2020-10-10 17:53:31 +02:00
|
|
|
include SystemCommand::Mixin
|
|
|
|
|
2024-07-05 17:49:32 +01:00
|
|
|
sig { returns(T.nilable(T::Array[AnyStrategy])) }
|
2018-07-01 23:35:29 +02:00
|
|
|
def self.strategies
|
2024-07-05 17:49:32 +01:00
|
|
|
@strategies ||= T.let([
|
2021-09-16 15:56:31 +01:00
|
|
|
Tar, # Needs to be before Bzip2/Gzip/Xz/Lzma/Zstd.
|
2018-09-11 19:21:18 +02:00
|
|
|
Pax,
|
2018-07-23 23:04:49 +02:00
|
|
|
Gzip,
|
2020-11-17 09:12:32 +00:00
|
|
|
Dmg, # Needs to be before Bzip2/Xz/Lzma.
|
2018-07-23 23:04:49 +02:00
|
|
|
Lzma,
|
2018-07-23 20:59:21 +02:00
|
|
|
Xz,
|
2021-09-16 15:56:31 +01:00
|
|
|
Zstd,
|
2018-07-23 20:59:21 +02:00
|
|
|
Lzip,
|
2020-08-09 06:09:05 +02:00
|
|
|
Air, # Needs to be before `Zip`.
|
|
|
|
Jar, # Needs to be before `Zip`.
|
|
|
|
LuaRock, # Needs to be before `Zip`.
|
|
|
|
MicrosoftOfficeXml, # Needs to be before `Zip`.
|
2018-07-29 10:33:47 +02:00
|
|
|
Zip,
|
2020-08-09 06:09:05 +02:00
|
|
|
Pkg, # Needs to be before `Xar`.
|
2018-07-29 10:33:47 +02:00
|
|
|
Xar,
|
|
|
|
Ttf,
|
|
|
|
Otf,
|
2018-07-23 20:59:21 +02:00
|
|
|
Git,
|
|
|
|
Mercurial,
|
|
|
|
Subversion,
|
|
|
|
Cvs,
|
2020-08-09 06:09:05 +02:00
|
|
|
SelfExtractingExecutable, # Needs to be before `Cab`.
|
2018-07-30 09:49:59 +02:00
|
|
|
Cab,
|
|
|
|
Executable,
|
2018-07-25 23:34:17 +02:00
|
|
|
Bzip2,
|
2018-07-23 20:59:21 +02:00
|
|
|
Fossil,
|
|
|
|
Bazaar,
|
2018-07-27 11:42:34 +02:00
|
|
|
Compress,
|
2018-07-23 20:59:21 +02:00
|
|
|
P7Zip,
|
2018-07-23 23:04:49 +02:00
|
|
|
Sit,
|
2018-07-23 20:59:21 +02:00
|
|
|
Rar,
|
|
|
|
Lha,
|
2024-07-05 17:49:32 +01:00
|
|
|
].freeze, T.nilable(T::Array[AnyStrategy]))
|
2018-07-01 23:35:29 +02:00
|
|
|
end
|
|
|
|
private_class_method :strategies
|
|
|
|
|
2024-07-05 17:49:32 +01:00
|
|
|
sig { params(type: Symbol).returns(T.nilable(T.any(T.class_of(UnpackStrategy::Uncompressed), AnyStrategy))) }
|
2018-07-23 23:04:49 +02:00
|
|
|
def self.from_type(type)
|
|
|
|
type = {
|
2018-11-02 17:18:07 +00:00
|
|
|
naked: :uncompressed,
|
|
|
|
nounzip: :uncompressed,
|
2018-07-23 23:04:49 +02:00
|
|
|
seven_zip: :p7zip,
|
|
|
|
}.fetch(type, type)
|
|
|
|
|
|
|
|
begin
|
2018-09-03 21:40:19 +02:00
|
|
|
const_get(type.to_s.split("_").map(&:capitalize).join.gsub(/\d+[a-z]/, &:upcase))
|
2018-07-23 23:04:49 +02:00
|
|
|
rescue NameError
|
|
|
|
nil
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2024-07-05 17:49:32 +01:00
|
|
|
sig { params(extension: String).returns(T.nilable(AnyStrategy)) }
|
2018-07-30 22:23:26 +02:00
|
|
|
def self.from_extension(extension)
|
2024-07-05 17:49:32 +01:00
|
|
|
strategies&.sort_by { |s| s.extensions.map(&:length).max || 0 }
|
|
|
|
&.reverse
|
|
|
|
&.find { |s| s.extensions.any? { |ext| extension.end_with?(ext) } }
|
2018-07-30 22:23:26 +02:00
|
|
|
end
|
2018-07-01 23:35:29 +02:00
|
|
|
|
2024-07-05 17:49:32 +01:00
|
|
|
sig { params(path: Pathname).returns(T.nilable(AnyStrategy)) }
|
2018-07-30 22:23:26 +02:00
|
|
|
def self.from_magic(path)
|
2024-07-05 17:49:32 +01:00
|
|
|
strategies&.find { |s| s.can_extract?(path) }
|
2018-07-30 22:23:26 +02:00
|
|
|
end
|
2018-07-30 09:49:59 +02:00
|
|
|
|
2024-07-05 17:49:32 +01:00
|
|
|
sig {
|
|
|
|
params(path: Pathname, prioritize_extension: T::Boolean, type: T.nilable(Symbol), ref_type: T.nilable(String),
|
|
|
|
ref: T.nilable(String), merge_xattrs: T.nilable(T::Boolean)).returns(T.untyped)
|
|
|
|
}
|
2023-02-22 22:15:26 +01:00
|
|
|
def self.detect(path, prioritize_extension: false, type: nil, ref_type: nil, ref: nil, merge_xattrs: nil)
|
2018-07-30 22:23:26 +02:00
|
|
|
strategy = from_type(type) if type
|
2018-07-01 23:35:29 +02:00
|
|
|
|
2023-02-22 22:15:26 +01:00
|
|
|
if prioritize_extension && path.extname.present?
|
2018-07-30 22:23:26 +02:00
|
|
|
strategy ||= from_extension(path.extname)
|
2024-07-05 17:49:32 +01:00
|
|
|
strategy ||= strategies&.select { |s| s < Directory || s == Fossil }
|
|
|
|
&.find { |s| s.can_extract?(path) }
|
2018-07-30 22:23:26 +02:00
|
|
|
else
|
|
|
|
strategy ||= from_magic(path)
|
|
|
|
strategy ||= from_extension(path.extname)
|
|
|
|
end
|
|
|
|
|
|
|
|
strategy ||= Uncompressed
|
2018-07-23 23:04:49 +02:00
|
|
|
|
2024-03-07 16:20:20 +00:00
|
|
|
strategy.new(path, ref_type:, ref:, merge_xattrs:)
|
2018-07-01 23:35:29 +02:00
|
|
|
end
|
|
|
|
|
2024-07-05 17:49:32 +01:00
|
|
|
sig { returns(Pathname) }
|
|
|
|
attr_reader :path
|
|
|
|
|
|
|
|
sig { returns(T.nilable(T::Boolean)) }
|
|
|
|
attr_reader :merge_xattrs
|
2018-07-01 23:35:29 +02:00
|
|
|
|
2024-07-05 17:49:32 +01:00
|
|
|
sig {
|
|
|
|
params(path: T.any(String, Pathname), ref_type: T.nilable(String), ref: T.nilable(String),
|
|
|
|
merge_xattrs: T.nilable(T::Boolean)).void
|
|
|
|
}
|
2019-04-09 21:45:35 +00:00
|
|
|
def initialize(path, ref_type: nil, ref: nil, merge_xattrs: nil)
|
2024-07-05 17:49:32 +01:00
|
|
|
@path = T.let(Pathname(path).expand_path, Pathname)
|
2018-07-09 22:11:26 +02:00
|
|
|
@ref_type = ref_type
|
|
|
|
@ref = ref
|
2019-04-09 21:45:35 +00:00
|
|
|
@merge_xattrs = merge_xattrs
|
2018-07-01 23:35:29 +02:00
|
|
|
end
|
|
|
|
|
2020-10-20 00:56:50 +02:00
|
|
|
abstract!
|
2024-07-05 17:49:32 +01:00
|
|
|
sig { abstract.params(unpack_dir: Pathname, basename: Pathname, verbose: T::Boolean).void }
|
2020-10-20 00:56:50 +02:00
|
|
|
def extract_to_dir(unpack_dir, basename:, verbose:); end
|
|
|
|
private :extract_to_dir
|
|
|
|
|
2023-02-22 22:15:26 +01:00
|
|
|
sig {
|
|
|
|
params(
|
|
|
|
to: T.nilable(Pathname), basename: T.nilable(T.any(String, Pathname)), verbose: T::Boolean,
|
2024-07-05 17:49:32 +01:00
|
|
|
).void
|
2023-02-22 22:15:26 +01:00
|
|
|
}
|
|
|
|
def extract(to: nil, basename: nil, verbose: false)
|
2018-07-09 20:04:33 +02:00
|
|
|
basename ||= path.basename
|
2018-07-01 23:35:29 +02:00
|
|
|
unpack_dir = Pathname(to || Dir.pwd).expand_path
|
|
|
|
unpack_dir.mkpath
|
2024-03-07 16:20:20 +00:00
|
|
|
extract_to_dir(unpack_dir, basename: Pathname(basename), verbose:)
|
2018-07-01 23:35:29 +02:00
|
|
|
end
|
2018-07-16 19:00:49 +02:00
|
|
|
|
2023-02-22 22:15:26 +01:00
|
|
|
sig {
|
|
|
|
params(
|
|
|
|
to: T.nilable(Pathname),
|
|
|
|
basename: T.nilable(T.any(String, Pathname)),
|
|
|
|
verbose: T::Boolean,
|
|
|
|
prioritize_extension: T::Boolean,
|
|
|
|
).returns(T.untyped)
|
|
|
|
}
|
|
|
|
def extract_nestedly(to: nil, basename: nil, verbose: false, prioritize_extension: false)
|
2024-02-26 16:58:39 +00:00
|
|
|
Dir.mktmpdir("homebrew-unpack", HOMEBREW_TEMP) do |tmp_unpack_dir|
|
2018-07-16 19:00:49 +02:00
|
|
|
tmp_unpack_dir = Pathname(tmp_unpack_dir)
|
|
|
|
|
2024-03-07 16:20:20 +00:00
|
|
|
extract(to: tmp_unpack_dir, basename:, verbose:)
|
2018-07-16 19:00:49 +02:00
|
|
|
|
|
|
|
children = tmp_unpack_dir.children
|
|
|
|
|
2023-11-05 08:55:58 -08:00
|
|
|
if children.size == 1 && !children.fetch(0).directory?
|
2024-07-05 17:49:32 +01:00
|
|
|
first_child = children.first
|
|
|
|
next if first_child.nil?
|
|
|
|
|
|
|
|
s = UnpackStrategy.detect(first_child, prioritize_extension:)
|
2018-07-16 19:00:49 +02:00
|
|
|
|
2024-03-07 16:20:20 +00:00
|
|
|
s.extract_nestedly(to:, verbose:, prioritize_extension:)
|
2023-02-21 12:36:58 +01:00
|
|
|
|
2018-07-16 19:00:49 +02:00
|
|
|
next
|
|
|
|
end
|
|
|
|
|
2023-02-21 12:36:58 +01:00
|
|
|
# Ensure all extracted directories are writable.
|
2023-03-20 13:15:43 -07:00
|
|
|
each_directory(tmp_unpack_dir) do |path|
|
2023-02-21 12:36:58 +01:00
|
|
|
next if path.writable?
|
|
|
|
|
2024-03-07 16:20:20 +00:00
|
|
|
FileUtils.chmod "u+w", path, verbose:
|
2023-02-21 12:36:58 +01:00
|
|
|
end
|
|
|
|
|
2024-03-07 16:20:20 +00:00
|
|
|
Directory.new(tmp_unpack_dir).extract(to:, verbose:)
|
2018-07-16 19:00:49 +02:00
|
|
|
end
|
|
|
|
end
|
2018-07-23 23:04:49 +02:00
|
|
|
|
2024-07-05 17:49:32 +01:00
|
|
|
sig { returns(T::Array[String]) }
|
2018-07-23 23:04:49 +02:00
|
|
|
def dependencies
|
|
|
|
[]
|
|
|
|
end
|
2023-03-20 13:15:43 -07:00
|
|
|
|
|
|
|
# Helper method for iterating over directory trees.
|
|
|
|
sig {
|
|
|
|
params(
|
2023-03-20 13:16:31 -07:00
|
|
|
pathname: Pathname,
|
|
|
|
_block: T.proc.params(path: Pathname).void,
|
|
|
|
).returns(T.nilable(Pathname))
|
2023-03-20 13:15:43 -07:00
|
|
|
}
|
|
|
|
def each_directory(pathname, &_block)
|
|
|
|
pathname.find do |path|
|
|
|
|
yield path if path.directory?
|
|
|
|
end
|
|
|
|
end
|
2018-07-01 23:35:29 +02:00
|
|
|
end
|
|
|
|
|
2018-07-23 23:04:49 +02:00
|
|
|
require "unpack_strategy/air"
|
2018-07-23 20:59:21 +02:00
|
|
|
require "unpack_strategy/bazaar"
|
|
|
|
require "unpack_strategy/bzip2"
|
2018-07-23 23:04:49 +02:00
|
|
|
require "unpack_strategy/cab"
|
2018-07-23 20:59:21 +02:00
|
|
|
require "unpack_strategy/compress"
|
|
|
|
require "unpack_strategy/cvs"
|
|
|
|
require "unpack_strategy/directory"
|
2018-07-23 23:04:49 +02:00
|
|
|
require "unpack_strategy/dmg"
|
|
|
|
require "unpack_strategy/executable"
|
2018-07-23 20:59:21 +02:00
|
|
|
require "unpack_strategy/fossil"
|
2018-07-23 23:04:49 +02:00
|
|
|
require "unpack_strategy/generic_unar"
|
2018-07-23 20:59:21 +02:00
|
|
|
require "unpack_strategy/git"
|
|
|
|
require "unpack_strategy/gzip"
|
|
|
|
require "unpack_strategy/jar"
|
|
|
|
require "unpack_strategy/lha"
|
|
|
|
require "unpack_strategy/lua_rock"
|
|
|
|
require "unpack_strategy/lzip"
|
2018-07-23 23:04:49 +02:00
|
|
|
require "unpack_strategy/lzma"
|
2018-07-23 20:59:21 +02:00
|
|
|
require "unpack_strategy/mercurial"
|
|
|
|
require "unpack_strategy/microsoft_office_xml"
|
2018-07-23 23:04:49 +02:00
|
|
|
require "unpack_strategy/otf"
|
2018-07-23 20:59:21 +02:00
|
|
|
require "unpack_strategy/p7zip"
|
2018-09-11 19:21:18 +02:00
|
|
|
require "unpack_strategy/pax"
|
2018-07-23 23:04:49 +02:00
|
|
|
require "unpack_strategy/pkg"
|
2018-07-23 20:59:21 +02:00
|
|
|
require "unpack_strategy/rar"
|
2018-07-23 23:04:49 +02:00
|
|
|
require "unpack_strategy/self_extracting_executable"
|
|
|
|
require "unpack_strategy/sit"
|
2018-07-23 20:59:21 +02:00
|
|
|
require "unpack_strategy/subversion"
|
|
|
|
require "unpack_strategy/tar"
|
2018-07-23 23:04:49 +02:00
|
|
|
require "unpack_strategy/ttf"
|
2018-07-23 20:59:21 +02:00
|
|
|
require "unpack_strategy/uncompressed"
|
|
|
|
require "unpack_strategy/xar"
|
|
|
|
require "unpack_strategy/xz"
|
|
|
|
require "unpack_strategy/zip"
|
2021-09-16 15:56:31 +01:00
|
|
|
require "unpack_strategy/zstd"
|