Merge pull request #15025 from dduugg/enable-unpack-strategy-types

Enable UnpackStrategy types
This commit is contained in:
Douglas Eichelberger 2023-03-22 12:06:12 -07:00 committed by GitHub
commit 65d858da12
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
40 changed files with 115 additions and 210 deletions

View File

@ -0,0 +1,6 @@
# typed: strict
class Object
sig { returns(T::Boolean) }
def present?; end
end

View File

@ -11,8 +11,6 @@ module UnpackStrategy
include UnpackStrategy
include SystemCommand::Mixin
using Magic
sig { override.params(unpack_dir: Pathname, basename: Pathname, verbose: T::Boolean).returns(T.untyped) }
def extract_to_dir(unpack_dir, basename:, verbose:)
with_env(TZ: "UTC") do

View File

@ -1,9 +1,5 @@
# typed: strict
module UnpackStrategy
class Zip
module MacOSZipExtension
module UnpackStrategy::Zip::MacOSZipExtension
include Kernel
end
end
end

View File

@ -466,6 +466,32 @@ class Pathname
def rpaths
[]
end
sig { returns(String) }
def magic_number
@magic_number ||= if directory?
""
else
# Length of the longest regex (currently Tar).
max_magic_number_length = 262
# FIXME: The `T.let` is a workaround until we have https://github.com/sorbet/sorbet/pull/6865
T.let(binread(max_magic_number_length), T.nilable(String)) || ""
end
end
sig { returns(String) }
def file_type
@file_type ||= system_command("file", args: ["-b", self], print_stderr: false)
.stdout.chomp
end
sig { returns(T::Array[String]) }
def zipinfo
@zipinfo ||= system_command("zipinfo", args: ["-1", self], print_stderr: false)
.stdout
.encode(Encoding::UTF_8, invalid: :replace)
.split("\n")
end
end
require "extend/os/pathname"

View File

@ -1,29 +1,8 @@
# typed: false
# typed: true
# frozen_string_literal: true
require "system_command"
# Helper module for iterating over directory trees.
#
# @api private
module PathnameEachDirectory
refine Pathname do
extend T::Sig
sig {
type_parameters(:T)
.params(
_block: T.proc.params(path: Pathname).returns(T.type_parameter(:T)),
).returns(T.type_parameter(:T))
}
def each_directory(&_block)
find do |path|
yield path if path.directory?
end
end
end
end
# Module containing all available strategies for unpacking archives.
#
# @api private
@ -33,38 +12,6 @@ module UnpackStrategy
include SystemCommand::Mixin
using PathnameEachDirectory
# Helper module for identifying the file type.
module Magic
# Length of the longest regex (currently Tar).
MAX_MAGIC_NUMBER_LENGTH = 262
private_constant :MAX_MAGIC_NUMBER_LENGTH
refine Pathname do
def magic_number
@magic_number ||= if directory?
""
else
binread(MAX_MAGIC_NUMBER_LENGTH) || ""
end
end
def file_type
@file_type ||= system_command("file", args: ["-b", self], print_stderr: false)
.stdout.chomp
end
def zipinfo
@zipinfo ||= system_command("zipinfo", args: ["-1", self], print_stderr: false)
.stdout
.encode(Encoding::UTF_8, invalid: :replace)
.split("\n")
end
end
end
private_constant :Magic
def self.strategies
@strategies ||= [
Tar, # Needs to be before Bzip2/Gzip/Xz/Lzma/Zstd.
@ -195,7 +142,7 @@ module UnpackStrategy
end
# Ensure all extracted directories are writable.
tmp_unpack_dir.each_directory do |path|
each_directory(tmp_unpack_dir) do |path|
next if path.writable?
FileUtils.chmod "u+w", path, verbose: verbose
@ -208,6 +155,19 @@ module UnpackStrategy
def dependencies
[]
end
# Helper method for iterating over directory trees.
sig {
params(
pathname: Pathname,
_block: T.proc.params(path: Pathname).void,
).returns(T.nilable(Pathname))
}
def each_directory(pathname, &_block)
pathname.find do |path|
yield path if path.directory?
end
end
end
require "unpack_strategy/air"

View File

@ -3,14 +3,3 @@
module UnpackStrategy
include Kernel
end
class Pathname
sig { returns(String) }
def magic_number; end
sig { returns(String) }
def file_type; end
sig { returns(T::Array[String]) }
def zipinfo; end
end

View File

@ -8,8 +8,6 @@ module UnpackStrategy
include UnpackStrategy
using Magic
sig { returns(T::Array[String]) }
def self.extensions
[".air"]

View File

@ -8,8 +8,6 @@ module UnpackStrategy
class Bazaar < Directory
extend T::Sig
using Magic
def self.can_extract?(path)
super && (path/".bzr").directory?
end

View File

@ -8,8 +8,6 @@ module UnpackStrategy
include UnpackStrategy
using Magic
sig { returns(T::Array[String]) }
def self.extensions
[".bz2"]

View File

@ -8,8 +8,6 @@ module UnpackStrategy
include UnpackStrategy
using Magic
sig { returns(T::Array[String]) }
def self.extensions
[".cab"]

View File

@ -8,8 +8,6 @@ module UnpackStrategy
class Compress < Tar
extend T::Sig
using Magic
sig { returns(T::Array[String]) }
def self.extensions
[".Z"]

View File

@ -6,8 +6,6 @@ require_relative "directory"
module UnpackStrategy
# Strategy for unpacking CVS repositories.
class Cvs < Directory
using Magic
def self.can_extract?(path)
super && (path/"CVS").directory?
end

View File

@ -8,8 +8,6 @@ module UnpackStrategy
include UnpackStrategy
using Magic
sig { returns(T::Array[String]) }
def self.extensions
[]

View File

@ -1,4 +1,4 @@
# typed: false
# typed: true
# frozen_string_literal: true
require "tempfile"
@ -12,6 +12,8 @@ module UnpackStrategy
# Helper module for listing the contents of a volume mounted from a disk image.
module Bom
extend T::Sig
DMG_METADATA = Set.new(%w[
.background
.com.apple.timemachine.donotpresent
@ -35,28 +37,25 @@ module UnpackStrategy
end
end
refine Pathname do
extend T::Sig
# Check if path is considered disk image metadata.
sig { returns(T::Boolean) }
def dmg_metadata?
DMG_METADATA.include?(cleanpath.ascend.to_a.last.to_s)
sig { params(pathname: Pathname).returns(T::Boolean) }
def self.dmg_metadata?(pathname)
DMG_METADATA.include?(pathname.cleanpath.ascend.to_a.last.to_s)
end
# Check if path is a symlink to a system directory (commonly to /Applications).
sig { returns(T::Boolean) }
def system_dir_symlink?
symlink? && MacOS.system_dir?(dirname.join(readlink))
sig { params(pathname: Pathname).returns(T::Boolean) }
def self.system_dir_symlink?(pathname)
pathname.symlink? && MacOS.system_dir?(pathname.dirname.join(pathname.readlink))
end
sig { returns(String) }
def bom
sig { params(pathname: Pathname).returns(String) }
def self.bom(pathname)
tries = 0
result = loop do
# We need to use `find` here instead of Ruby in order to properly handle
# file names containing special characters, such as “e” + “´” vs. “é”.
r = system_command("find", args: [".", "-print0"], chdir: self, print_stderr: false)
r = system_command("find", args: [".", "-print0"], chdir: pathname, print_stderr: false)
tries += 1
# Spurious bug on CI, which in most cases can be worked around by retrying.
@ -65,31 +64,28 @@ module UnpackStrategy
raise "Command `#{r.command.shelljoin}` was interrupted." if tries >= 3
end
odebug "Command `#{result.command.shelljoin}` in '#{self}' took #{tries} tries." if tries > 1
odebug "Command `#{result.command.shelljoin}` in '#{pathname}' took #{tries} tries." if tries > 1
bom_paths = result.stdout.split("\0")
raise EmptyError, self if bom_paths.empty?
raise EmptyError, pathname if bom_paths.empty?
bom_paths
.reject { |path| Pathname(path).dmg_metadata? }
.reject { |path| (self/path).system_dir_symlink? }
.reject { |path| dmg_metadata?(Pathname(path)) }
.reject { |path| system_dir_symlink?(pathname/path) }
.join("\n")
end
end
end
private_constant :Bom
# Strategy for unpacking a volume mounted from a disk image.
class Mount
extend T::Sig
using Bom
include UnpackStrategy
def eject(verbose: false)
tries ||= 3
tries = 3
begin
return unless path.exist?
if tries > 1
@ -126,15 +122,15 @@ module UnpackStrategy
sleep 1
retry
end
end
private
sig { override.params(unpack_dir: Pathname, basename: Pathname, verbose: T::Boolean).returns(T.untyped) }
def extract_to_dir(unpack_dir, basename:, verbose:)
tries = 3
bom = begin
tries ||= 3
path.bom
Bom.bom(path)
rescue Bom::EmptyError => e
raise e if (tries -= 1).zero?

View File

@ -8,8 +8,6 @@ module UnpackStrategy
class Executable < Uncompressed
extend T::Sig
using Magic
sig { returns(T::Array[String]) }
def self.extensions
[".sh", ".bash"]

View File

@ -11,8 +11,6 @@ module UnpackStrategy
include UnpackStrategy
extend SystemCommand::Mixin
using Magic
sig { returns(T::Array[String]) }
def self.extensions
[]

View File

@ -8,8 +8,6 @@ module UnpackStrategy
include UnpackStrategy
using Magic
sig { returns(T::Array[String]) }
def self.extensions
[]

View File

@ -6,8 +6,6 @@ require_relative "directory"
module UnpackStrategy
# Strategy for unpacking Git repositories.
class Git < Directory
using Magic
def self.can_extract?(path)
super && (path/".git").directory?
end

View File

@ -8,8 +8,6 @@ module UnpackStrategy
include UnpackStrategy
using Magic
sig { returns(T::Array[String]) }
def self.extensions
[".gz"]

View File

@ -8,8 +8,6 @@ module UnpackStrategy
class Jar < Uncompressed
extend T::Sig
using Magic
sig { returns(T::Array[String]) }
def self.extensions
[".apk", ".jar"]

View File

@ -8,8 +8,6 @@ module UnpackStrategy
include UnpackStrategy
using Magic
sig { returns(T::Array[String]) }
def self.extensions
[".lha", ".lzh"]

View File

@ -8,8 +8,6 @@ module UnpackStrategy
class LuaRock < Uncompressed
extend T::Sig
using Magic
sig { returns(T::Array[String]) }
def self.extensions
[".rock"]

View File

@ -8,8 +8,6 @@ module UnpackStrategy
include UnpackStrategy
using Magic
sig { returns(T::Array[String]) }
def self.extensions
[".lz"]

View File

@ -8,8 +8,6 @@ module UnpackStrategy
include UnpackStrategy
using Magic
sig { returns(T::Array[String]) }
def self.extensions
[".lzma"]

View File

@ -6,8 +6,6 @@ require_relative "directory"
module UnpackStrategy
# Strategy for unpacking Mercurial repositories.
class Mercurial < Directory
using Magic
def self.can_extract?(path)
super && (path/".hg").directory?
end

View File

@ -8,8 +8,6 @@ module UnpackStrategy
class MicrosoftOfficeXml < Uncompressed
extend T::Sig
using Magic
sig { returns(T::Array[String]) }
def self.extensions
[

View File

@ -8,8 +8,6 @@ module UnpackStrategy
class Otf < Uncompressed
extend T::Sig
using Magic
sig { returns(T::Array[String]) }
def self.extensions
[".otf"]

View File

@ -8,8 +8,6 @@ module UnpackStrategy
include UnpackStrategy
using Magic
sig { returns(T::Array[String]) }
def self.extensions
[".7z"]

View File

@ -8,8 +8,6 @@ module UnpackStrategy
include UnpackStrategy
using Magic
sig { returns(T::Array[String]) }
def self.extensions
[".pax"]

View File

@ -8,8 +8,6 @@ module UnpackStrategy
class Pkg < Uncompressed
extend T::Sig
using Magic
sig { returns(T::Array[String]) }
def self.extensions
[".pkg", ".mkpg"]

View File

@ -8,8 +8,6 @@ module UnpackStrategy
include UnpackStrategy
using Magic
sig { returns(T::Array[String]) }
def self.extensions
[".rar"]

View File

@ -8,8 +8,6 @@ module UnpackStrategy
class SelfExtractingExecutable < GenericUnar
extend T::Sig
using Magic
sig { returns(T::Array[String]) }
def self.extensions
[]

View File

@ -8,8 +8,6 @@ module UnpackStrategy
class Sit < GenericUnar
extend T::Sig
using Magic
sig { returns(T::Array[String]) }
def self.extensions
[".sit"]

View File

@ -6,8 +6,6 @@ require_relative "directory"
module UnpackStrategy
# Strategy for unpacking Subversion repositories.
class Subversion < Directory
using Magic
def self.can_extract?(path)
super && (path/".svn").directory?
end

View File

@ -11,8 +11,6 @@ module UnpackStrategy
include UnpackStrategy
extend SystemCommand::Mixin
using Magic
sig { returns(T::Array[String]) }
def self.extensions
[

View File

@ -8,8 +8,6 @@ module UnpackStrategy
class Ttf < Uncompressed
extend T::Sig
using Magic
sig { returns(T::Array[String]) }
def self.extensions
[".ttc", ".ttf"]

View File

@ -8,8 +8,6 @@ module UnpackStrategy
include UnpackStrategy
using Magic
sig { returns(T::Array[String]) }
def self.extensions
[".xar"]

View File

@ -8,8 +8,6 @@ module UnpackStrategy
include UnpackStrategy
using Magic
sig { returns(T::Array[String]) }
def self.extensions
[".xz"]

View File

@ -8,8 +8,6 @@ module UnpackStrategy
include UnpackStrategy
using Magic
sig { returns(T::Array[String]) }
def self.extensions
[".zip"]

View File

@ -8,8 +8,6 @@ module UnpackStrategy
include UnpackStrategy
using Magic
sig { returns(T::Array[String]) }
def self.extensions
[".zst"]