Merge pull request #4541 from reitermarkus/unpack-strategies
Merge `Hbc::Container` classes into `UnpackStrategy`.
This commit is contained in:
commit
def529ba3e
@ -10,7 +10,6 @@ require "hbc/caskroom"
|
||||
require "hbc/checkable"
|
||||
require "hbc/cli"
|
||||
require "hbc/cask_dependencies"
|
||||
require "hbc/container"
|
||||
require "hbc/download"
|
||||
require "hbc/download_strategy"
|
||||
require "hbc/exceptions"
|
||||
@ -18,7 +17,6 @@ require "hbc/installer"
|
||||
require "hbc/macos"
|
||||
require "hbc/pkg"
|
||||
require "hbc/staged"
|
||||
require "system_command"
|
||||
require "hbc/topological_hash"
|
||||
require "hbc/utils"
|
||||
require "hbc/verify"
|
||||
|
||||
@ -10,7 +10,6 @@ require "hbc/artifact/internet_plugin"
|
||||
require "hbc/artifact/audio_unit_plugin"
|
||||
require "hbc/artifact/vst_plugin"
|
||||
require "hbc/artifact/vst3_plugin"
|
||||
require "hbc/artifact/nested_container"
|
||||
require "hbc/artifact/pkg"
|
||||
require "hbc/artifact/postflight_block"
|
||||
require "hbc/artifact/preflight_block"
|
||||
|
||||
@ -29,9 +29,6 @@ module Hbc
|
||||
# The `uninstall` stanza should be run first, as it may
|
||||
# depend on other artifacts still being installed.
|
||||
Uninstall,
|
||||
# We want to extract nested containers before we
|
||||
# handle any other artifacts.
|
||||
NestedContainer,
|
||||
Installer,
|
||||
[
|
||||
App,
|
||||
|
||||
@ -1,36 +0,0 @@
|
||||
require "hbc/artifact/abstract_artifact"
|
||||
|
||||
module Hbc
|
||||
module Artifact
|
||||
class NestedContainer < AbstractArtifact
|
||||
attr_reader :path
|
||||
|
||||
def initialize(cask, path)
|
||||
super(cask)
|
||||
@path = cask.staged_path.join(path)
|
||||
end
|
||||
|
||||
def install_phase(**options)
|
||||
extract(**options)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def summarize
|
||||
path.relative_path_from(cask.staged_path).to_s
|
||||
end
|
||||
|
||||
def extract(verbose: nil, **_)
|
||||
container = Container.for_path(path)
|
||||
|
||||
unless container
|
||||
raise CaskError, "Aw dang, could not identify nested container at '#{source}'"
|
||||
end
|
||||
|
||||
ohai "Extracting nested container #{path.relative_path_from(cask.staged_path)}"
|
||||
container.new(cask, path).extract(to: cask.staged_path, verbose: verbose)
|
||||
FileUtils.remove_entry_secure(path)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -113,8 +113,7 @@ module Hbc
|
||||
end
|
||||
add_error "at least one name stanza is required" if cask.name.empty?
|
||||
# TODO: specific DSL knowledge should not be spread around in various files like this
|
||||
# TODO: nested_container should not still be a pseudo-artifact at this point
|
||||
installable_artifacts = cask.artifacts.reject { |k| [:uninstall, :zap, :nested_container].include?(k) }
|
||||
installable_artifacts = cask.artifacts.reject { |k| [:uninstall, :zap].include?(k) }
|
||||
add_error "at least one activatable artifact stanza is required" if installable_artifacts.empty?
|
||||
end
|
||||
|
||||
|
||||
@ -1,79 +0,0 @@
|
||||
require "hbc/utils"
|
||||
|
||||
require "hbc/container/base"
|
||||
require "hbc/container/air"
|
||||
require "hbc/container/bzip2"
|
||||
require "hbc/container/cab"
|
||||
require "hbc/container/dmg"
|
||||
require "hbc/container/self_extracting_executable"
|
||||
require "hbc/container/executable"
|
||||
require "hbc/container/generic_unar"
|
||||
require "hbc/container/gzip"
|
||||
require "hbc/container/lzma"
|
||||
require "hbc/container/naked"
|
||||
require "hbc/container/otf"
|
||||
require "hbc/container/pkg"
|
||||
require "hbc/container/seven_zip"
|
||||
require "hbc/container/sit"
|
||||
require "hbc/container/svn_repository"
|
||||
require "hbc/container/tar"
|
||||
require "hbc/container/ttf"
|
||||
require "hbc/container/rar"
|
||||
require "hbc/container/xar"
|
||||
require "hbc/container/xz"
|
||||
require "hbc/container/zip"
|
||||
|
||||
module Hbc
|
||||
class Container
|
||||
def self.autodetect_containers
|
||||
[
|
||||
Pkg,
|
||||
Ttf,
|
||||
Otf,
|
||||
Air,
|
||||
SelfExtractingExecutable,
|
||||
Cab,
|
||||
Dmg,
|
||||
SevenZip,
|
||||
Sit,
|
||||
Rar,
|
||||
Zip,
|
||||
Xar, # need to be before tar as tar can also list xar
|
||||
Tar, # or compressed tar (bzip2/gzip/lzma/xz)
|
||||
Bzip2, # pure bzip2
|
||||
Gzip, # pure gzip
|
||||
Lzma, # pure lzma
|
||||
Xz, # pure xz
|
||||
Executable,
|
||||
SvnRepository,
|
||||
]
|
||||
# for explicit use only (never autodetected):
|
||||
# Hbc::Container::Naked
|
||||
# Hbc::Container::GenericUnar
|
||||
end
|
||||
|
||||
def self.for_path(path)
|
||||
odebug "Determining which containers to use based on filetype"
|
||||
|
||||
magic_number = if path.directory?
|
||||
""
|
||||
else
|
||||
File.binread(path, 262) || ""
|
||||
end
|
||||
|
||||
autodetect_containers.find do |c|
|
||||
odebug "Checking container class #{c}"
|
||||
c.can_extract?(path: path, magic_number: magic_number)
|
||||
end
|
||||
end
|
||||
|
||||
def self.from_type(type)
|
||||
odebug "Determining which containers to use based on 'container :type'"
|
||||
begin
|
||||
const_get(type.to_s.split("_").map(&:capitalize).join)
|
||||
rescue NameError
|
||||
nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -1,22 +0,0 @@
|
||||
require "hbc/container/base"
|
||||
|
||||
module Hbc
|
||||
class Container
|
||||
class Air < Base
|
||||
def self.can_extract?(path:, magic_number:)
|
||||
path.extname == ".air"
|
||||
end
|
||||
|
||||
def extract_to_dir(unpack_dir, basename:, verbose:)
|
||||
system_command!(
|
||||
"/Applications/Utilities/Adobe AIR Application Installer.app/Contents/MacOS/Adobe AIR Application Installer",
|
||||
args: ["-silent", "-location", unpack_dir, path],
|
||||
)
|
||||
end
|
||||
|
||||
def dependencies
|
||||
@dependencies ||= [CaskLoader.load("adobe-air")]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -1,54 +0,0 @@
|
||||
module Hbc
|
||||
class Container
|
||||
class Base
|
||||
extend Predicable
|
||||
|
||||
attr_reader :path
|
||||
|
||||
def initialize(cask, path, nested: false)
|
||||
@cask = cask
|
||||
@path = path
|
||||
end
|
||||
|
||||
def extract(to: nil, basename: nil, verbose: false)
|
||||
basename ||= path.basename
|
||||
unpack_dir = Pathname(to || Dir.pwd).expand_path
|
||||
unpack_dir.mkpath
|
||||
extract_to_dir(unpack_dir, basename: basename, verbose: verbose)
|
||||
end
|
||||
|
||||
def extract_nested_inside(dir, to:, verbose: false)
|
||||
children = Pathname.new(dir).children
|
||||
|
||||
nested_container = children[0]
|
||||
|
||||
unless children.count == 1 &&
|
||||
!nested_container.directory? &&
|
||||
@cask.artifacts.none? { |a| a.is_a?(Artifact::NestedContainer) } &&
|
||||
extract_nested_container(nested_container, to: to, verbose: verbose)
|
||||
|
||||
children.each do |src|
|
||||
dest = @cask.staged_path.join(src.basename)
|
||||
FileUtils.rm_r(dest) if dest.exist?
|
||||
FileUtils.mv(src, dest)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def extract_nested_container(source, to:, verbose: false)
|
||||
container = Container.for_path(source)
|
||||
|
||||
return false unless container
|
||||
|
||||
ohai "Extracting nested container #{source.basename}"
|
||||
container.new(@cask, source).extract(to: to, verbose: verbose)
|
||||
|
||||
true
|
||||
end
|
||||
|
||||
def dependencies
|
||||
[]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -1,22 +0,0 @@
|
||||
require "hbc/container/base"
|
||||
|
||||
module Hbc
|
||||
class Container
|
||||
class Bzip2 < Base
|
||||
def self.can_extract?(path:, magic_number:)
|
||||
magic_number.match?(/\ABZh/n)
|
||||
end
|
||||
|
||||
def extract_to_dir(unpack_dir, basename:, verbose:)
|
||||
Dir.mktmpdir do |tmp_unpack_dir|
|
||||
tmp_unpack_dir = Pathname(tmp_unpack_dir)
|
||||
|
||||
FileUtils.cp path, tmp_unpack_dir/basename, preserve: true
|
||||
system_command!("bunzip2", args: ["--quiet", "--", tmp_unpack_dir/basename])
|
||||
|
||||
extract_nested_inside(tmp_unpack_dir, to: unpack_dir)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -1,21 +0,0 @@
|
||||
require "hbc/container/base"
|
||||
|
||||
module Hbc
|
||||
class Container
|
||||
class Cab < Base
|
||||
def self.can_extract?(path:, magic_number:)
|
||||
magic_number.match?(/\A(MSCF|MZ)/n)
|
||||
end
|
||||
|
||||
def extract_to_dir(unpack_dir, basename:, verbose:)
|
||||
system_command!("cabextract",
|
||||
args: ["-d", unpack_dir, "--", path],
|
||||
env: { "PATH" => PATH.new(Formula["cabextract"].opt_bin, ENV["PATH"]) })
|
||||
end
|
||||
|
||||
def dependencies
|
||||
@dependencies ||= [Formula["cabextract"]]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -1,152 +0,0 @@
|
||||
require "tempfile"
|
||||
|
||||
require "hbc/container/base"
|
||||
|
||||
module Hbc
|
||||
class Container
|
||||
class Dmg < Base
|
||||
def self.can_extract?(path:, magic_number:)
|
||||
imageinfo = system_command("/usr/bin/hdiutil",
|
||||
# realpath is a failsafe against unusual filenames
|
||||
args: ["imageinfo", path.realpath],
|
||||
print_stderr: false).stdout
|
||||
|
||||
!imageinfo.empty?
|
||||
end
|
||||
|
||||
def extract_to_dir(unpack_dir, basename:, verbose:)
|
||||
mount(verbose: verbose) do |mounts|
|
||||
begin
|
||||
raise "No mounts found in '#{path}'; perhaps it is a bad disk image?" if mounts.empty?
|
||||
mounts.each do |mount|
|
||||
extract_mount(mount, to: unpack_dir)
|
||||
end
|
||||
ensure
|
||||
mounts.each(&method(:eject))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def mount(verbose: false)
|
||||
# realpath is a failsafe against unusual filenames
|
||||
realpath = path.realpath
|
||||
path = realpath
|
||||
|
||||
Dir.mktmpdir do |unpack_dir|
|
||||
without_eula = system_command("/usr/bin/hdiutil",
|
||||
args: ["attach", "-plist", "-nobrowse", "-readonly", "-noidme", "-mountrandom", unpack_dir, path],
|
||||
input: "qn\n",
|
||||
print_stderr: false)
|
||||
|
||||
# If mounting without agreeing to EULA succeeded, there is none.
|
||||
plist = if without_eula.success?
|
||||
without_eula.plist
|
||||
else
|
||||
cdr_path = Pathname.new(unpack_dir).join("#{path.basename(".dmg")}.cdr")
|
||||
|
||||
system_command!("/usr/bin/hdiutil", args: ["convert", "-quiet", "-format", "UDTO", "-o", cdr_path, path])
|
||||
|
||||
with_eula = system_command!("/usr/bin/hdiutil",
|
||||
args: ["attach", "-plist", "-nobrowse", "-readonly", "-noidme", "-mountrandom", unpack_dir, cdr_path])
|
||||
|
||||
if verbose && !(eula_text = without_eula.stdout).empty?
|
||||
ohai "Software License Agreement for '#{path}':"
|
||||
puts eula_text
|
||||
end
|
||||
|
||||
with_eula.plist
|
||||
end
|
||||
|
||||
yield mounts_from_plist(plist)
|
||||
end
|
||||
end
|
||||
|
||||
def eject(mount)
|
||||
# realpath is a failsafe against unusual filenames
|
||||
mountpath = Pathname.new(mount).realpath
|
||||
|
||||
begin
|
||||
tries ||= 3
|
||||
|
||||
return unless mountpath.exist?
|
||||
|
||||
if tries > 1
|
||||
system_command!("/usr/sbin/diskutil",
|
||||
args: ["eject", mountpath],
|
||||
print_stderr: false)
|
||||
else
|
||||
system_command!("/usr/sbin/diskutil",
|
||||
args: ["unmount", "force", mountpath],
|
||||
print_stderr: false)
|
||||
end
|
||||
rescue ErrorDuringExecution => e
|
||||
raise e if (tries -= 1).zero?
|
||||
sleep 1
|
||||
retry
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def extract_mount(mount, to:)
|
||||
Tempfile.open(["", ".bom"]) do |bomfile|
|
||||
bomfile.close
|
||||
|
||||
Tempfile.open(["", ".list"]) do |filelist|
|
||||
filelist.puts(bom_filelist_from_path(mount))
|
||||
filelist.close
|
||||
|
||||
system_command!("/usr/bin/mkbom", args: ["-s", "-i", filelist.path, "--", bomfile.path])
|
||||
system_command!("/usr/bin/ditto", args: ["--bom", bomfile.path, "--", mount, to])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def bom_filelist_from_path(mount)
|
||||
# We need to use `find` here instead of Ruby in order to properly handle
|
||||
# file names containing special characters, such as “e” + “´” vs. “é”.
|
||||
system_command("/usr/bin/find", args: [".", "-print0"], chdir: mount, print_stderr: false)
|
||||
.stdout
|
||||
.split("\0")
|
||||
.reject { |path| skip_path?(mount, path) }
|
||||
.join("\n")
|
||||
end
|
||||
|
||||
def skip_path?(mount, path)
|
||||
path = Pathname(path.sub(%r{\A\./}, ""))
|
||||
dmg_metadata?(path) || system_dir_symlink?(mount, path)
|
||||
end
|
||||
|
||||
# unnecessary DMG metadata
|
||||
DMG_METADATA_FILES = Set.new %w[
|
||||
.background
|
||||
.com.apple.timemachine.donotpresent
|
||||
.com.apple.timemachine.supported
|
||||
.DocumentRevisions-V100
|
||||
.DS_Store
|
||||
.fseventsd
|
||||
.MobileBackups
|
||||
.Spotlight-V100
|
||||
.TemporaryItems
|
||||
.Trashes
|
||||
.VolumeIcon.icns
|
||||
].freeze
|
||||
|
||||
def dmg_metadata?(path)
|
||||
relative_root = path.sub(%r{/.*}, "")
|
||||
DMG_METADATA_FILES.include?(relative_root.basename.to_s)
|
||||
end
|
||||
|
||||
def system_dir_symlink?(mount, path)
|
||||
full_path = Pathname(mount).join(path)
|
||||
# symlinks to system directories (commonly to /Applications)
|
||||
full_path.symlink? && MacOS.system_dir?(full_path.readlink)
|
||||
end
|
||||
|
||||
def mounts_from_plist(plist)
|
||||
return [] unless plist.respond_to?(:fetch)
|
||||
plist.fetch("system-entities", []).map { |e| e["mount-point"] }.compact
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -1,18 +0,0 @@
|
||||
require "hbc/container/naked"
|
||||
require "vendor/macho/macho"
|
||||
|
||||
module Hbc
|
||||
class Container
|
||||
class Executable < Naked
|
||||
def self.can_extract?(path:, magic_number:)
|
||||
return true if magic_number.match?(/\A#!\s*\S+/n)
|
||||
|
||||
begin
|
||||
path.file? && MachO.open(path).header.executable?
|
||||
rescue MachO::MagicError
|
||||
false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -1,21 +0,0 @@
|
||||
require "hbc/container/base"
|
||||
|
||||
module Hbc
|
||||
class Container
|
||||
class GenericUnar < Base
|
||||
def self.can_extract?(path:, magic_number:)
|
||||
false
|
||||
end
|
||||
|
||||
def extract_to_dir(unpack_dir, basename:, verbose:)
|
||||
system_command!("unar",
|
||||
args: ["-force-overwrite", "-quiet", "-no-directory", "-output-directory", unpack_dir, "--", path],
|
||||
env: { "PATH" => PATH.new(Formula["unar"].opt_bin, ENV["PATH"]) })
|
||||
end
|
||||
|
||||
def dependencies
|
||||
@dependencies ||= [Formula["unar"]]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -1,22 +0,0 @@
|
||||
require "hbc/container/base"
|
||||
|
||||
module Hbc
|
||||
class Container
|
||||
class Gzip < Base
|
||||
def self.can_extract?(path:, magic_number:)
|
||||
magic_number.match?(/\A\037\213/n)
|
||||
end
|
||||
|
||||
def extract_to_dir(unpack_dir, basename:, verbose:)
|
||||
Dir.mktmpdir do |tmp_unpack_dir|
|
||||
tmp_unpack_dir = Pathname(tmp_unpack_dir)
|
||||
|
||||
FileUtils.cp path, tmp_unpack_dir/basename, preserve: true
|
||||
system_command!("gunzip", args: ["--quiet", "--name", "--", tmp_unpack_dir/basename])
|
||||
|
||||
extract_nested_inside(tmp_unpack_dir, to: unpack_dir)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -1,22 +0,0 @@
|
||||
require "hbc/container/base"
|
||||
|
||||
module Hbc
|
||||
class Container
|
||||
class Lzma < Base
|
||||
def self.can_extract?(path:, magic_number:)
|
||||
magic_number.match?(/\A\]\000\000\200\000/n)
|
||||
end
|
||||
|
||||
def extract_to_dir(unpack_dir, basename:, verbose:)
|
||||
system_command!("/usr/bin/ditto", args: ["--", path, unpack_dir])
|
||||
system_command!("unlzma",
|
||||
args: ["-q", "--", Pathname(unpack_dir).join(basename)],
|
||||
env: { "PATH" => PATH.new(Formula["unlzma"].opt_bin, ENV["PATH"]) })
|
||||
end
|
||||
|
||||
def dependencies
|
||||
@dependencies ||= [Formula["unlzma"]]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -1,15 +0,0 @@
|
||||
require "hbc/container/base"
|
||||
|
||||
module Hbc
|
||||
class Container
|
||||
class Naked < Base
|
||||
def self.can_extract?(path:, magic_number:)
|
||||
false
|
||||
end
|
||||
|
||||
def extract_to_dir(unpack_dir, basename:, verbose:)
|
||||
system_command!("/usr/bin/ditto", args: ["--", path, unpack_dir/basename])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -1,11 +0,0 @@
|
||||
require "hbc/container/naked"
|
||||
|
||||
module Hbc
|
||||
class Container
|
||||
class Otf < Naked
|
||||
def self.can_extract?(path:, magic_number:)
|
||||
magic_number.match?(/\AOTTO/n)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -1,12 +0,0 @@
|
||||
require "hbc/container/naked"
|
||||
|
||||
module Hbc
|
||||
class Container
|
||||
class Pkg < Naked
|
||||
def self.can_extract?(path:, magic_number:)
|
||||
path.extname.match?(/\A.m?pkg\Z/) &&
|
||||
(path.directory? || magic_number.match?(/\Axar!/n))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -1,21 +0,0 @@
|
||||
require "hbc/container/base"
|
||||
|
||||
module Hbc
|
||||
class Container
|
||||
class Rar < Base
|
||||
def self.can_extract?(path:, magic_number:)
|
||||
magic_number.match?(/\ARar!/n)
|
||||
end
|
||||
|
||||
def extract_to_dir(unpack_dir, basename:, verbose:)
|
||||
system_command!("unrar",
|
||||
args: ["x", "-inul", path, unpack_dir],
|
||||
env: { "PATH" => PATH.new(Formula["unrar"].opt_bin, ENV["PATH"]) })
|
||||
end
|
||||
|
||||
def dependencies
|
||||
@dependencies ||= [Formula["unrar"]]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -1,15 +0,0 @@
|
||||
require "hbc/container/generic_unar"
|
||||
|
||||
module Hbc
|
||||
class Container
|
||||
class SelfExtractingExecutable < GenericUnar
|
||||
def self.can_extract?(path:, magic_number:)
|
||||
return false unless magic_number.match?(/\AMZ/n)
|
||||
|
||||
SystemCommand.run("file",
|
||||
args: [path],
|
||||
print_stderr: false).stdout.include?("self-extracting")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -1,21 +0,0 @@
|
||||
require "hbc/container/base"
|
||||
|
||||
module Hbc
|
||||
class Container
|
||||
class SevenZip < Base
|
||||
def self.can_extract?(path:, magic_number:)
|
||||
magic_number.match?(/\A7z\xBC\xAF\x27\x1C/n)
|
||||
end
|
||||
|
||||
def extract_to_dir(unpack_dir, basename:, verbose:)
|
||||
system_command!("7zr",
|
||||
args: ["x", "-y", "-bd", "-bso0", path, "-o#{unpack_dir}"],
|
||||
env: { "PATH" => PATH.new(Formula["p7zip"].opt_bin, ENV["PATH"]) })
|
||||
end
|
||||
|
||||
def dependencies
|
||||
@dependencies ||= [Formula["p7zip"]]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -1,11 +0,0 @@
|
||||
require "hbc/container/generic_unar"
|
||||
|
||||
module Hbc
|
||||
class Container
|
||||
class Sit < GenericUnar
|
||||
def self.can_extract?(path:, magic_number:)
|
||||
magic_number.match?(/\AStuffIt/n)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -1,13 +0,0 @@
|
||||
module Hbc
|
||||
class Container
|
||||
class SvnRepository < Base
|
||||
def self.can_extract?(path:, magic_number:)
|
||||
(path/".svn").directory?
|
||||
end
|
||||
|
||||
def extract_to_dir(unpack_dir, basename:, verbose:)
|
||||
system_command!("svn", args: ["export", "--force", path, unpack_dir])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -1,20 +0,0 @@
|
||||
require "hbc/container/base"
|
||||
|
||||
module Hbc
|
||||
class Container
|
||||
class Tar < Base
|
||||
def self.can_extract?(path:, magic_number:)
|
||||
return true if magic_number.match?(/\A.{257}ustar/n)
|
||||
|
||||
# Check if `tar` can list the contents, then it can also extract it.
|
||||
IO.popen(["tar", "tf", path], err: File::NULL) do |stdout|
|
||||
!stdout.read(1).nil?
|
||||
end
|
||||
end
|
||||
|
||||
def extract_to_dir(unpack_dir, basename:, verbose:)
|
||||
system_command!("tar", args: ["xf", path, "-C", unpack_dir])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -1,14 +0,0 @@
|
||||
require "hbc/container/naked"
|
||||
|
||||
module Hbc
|
||||
class Container
|
||||
class Ttf < Naked
|
||||
def self.can_extract?(path:, magic_number:)
|
||||
# TrueType Font
|
||||
magic_number.match?(/\A\000\001\000\000\000/n) ||
|
||||
# Truetype Font Collection
|
||||
magic_number.match?(/\Attcf/n)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -1,15 +0,0 @@
|
||||
require "hbc/container/base"
|
||||
|
||||
module Hbc
|
||||
class Container
|
||||
class Xar < Base
|
||||
def self.can_extract?(path:, magic_number:)
|
||||
magic_number.match?(/\Axar!/n)
|
||||
end
|
||||
|
||||
def extract_to_dir(unpack_dir, basename:, verbose:)
|
||||
system_command!("xar", args: ["-x", "-f", @path, "-C", unpack_dir])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -1,22 +0,0 @@
|
||||
require "hbc/container/base"
|
||||
|
||||
module Hbc
|
||||
class Container
|
||||
class Xz < Base
|
||||
def self.can_extract?(path:, magic_number:)
|
||||
magic_number.match?(/\A\xFD7zXZ\x00/n)
|
||||
end
|
||||
|
||||
def extract_to_dir(unpack_dir, basename:, verbose:)
|
||||
system_command!("/usr/bin/ditto", args: ["--", path, unpack_dir])
|
||||
system_command!("unxz",
|
||||
args: ["-q", "--", unpack_dir/basename],
|
||||
env: { "PATH" => PATH.new(Formula["xz"].opt_bin, ENV["PATH"]) })
|
||||
end
|
||||
|
||||
def dependencies
|
||||
@dependencies ||= [Formula["xz"]]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -1,19 +0,0 @@
|
||||
require "hbc/container/base"
|
||||
|
||||
module Hbc
|
||||
class Container
|
||||
class Zip < Base
|
||||
def self.can_extract?(path:, magic_number:)
|
||||
magic_number.match?(/\APK(\003\004|\005\006)/n)
|
||||
end
|
||||
|
||||
def extract_to_dir(unpack_dir, basename:, verbose:)
|
||||
Dir.mktmpdir do |tmp_unpack_dir|
|
||||
system_command!("/usr/bin/ditto", args: ["-x", "-k", "--", path, tmp_unpack_dir])
|
||||
|
||||
extract_nested_inside(tmp_unpack_dir, to: unpack_dir)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -172,16 +172,8 @@ module Hbc
|
||||
end
|
||||
|
||||
def container(*args)
|
||||
# TODO: remove this constraint, and instead merge multiple container stanzas
|
||||
set_unique_stanza(:container, args.empty?) do
|
||||
begin
|
||||
DSL::Container.new(*args).tap do |container|
|
||||
# TODO: remove this backward-compatibility section after removing nested_container
|
||||
if container&.nested
|
||||
artifacts.add(Artifact::NestedContainer.new(cask, container.nested))
|
||||
end
|
||||
end
|
||||
end
|
||||
DSL::Container.new(*args)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@ -1,5 +1,3 @@
|
||||
require "system_command"
|
||||
|
||||
module Hbc
|
||||
class DSL
|
||||
class Appcast
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
require "hbc/container"
|
||||
require "unpack_strategy"
|
||||
|
||||
module Hbc
|
||||
class DSL
|
||||
@ -19,7 +19,7 @@ module Hbc
|
||||
end
|
||||
|
||||
return if type.nil?
|
||||
return unless Hbc::Container.from_type(type).nil?
|
||||
return unless UnpackStrategy.from_type(type).nil?
|
||||
raise "invalid container type: #{type.inspect}"
|
||||
end
|
||||
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
require "rubygems"
|
||||
|
||||
require "formula_installer"
|
||||
require "unpack_strategy"
|
||||
|
||||
require "hbc/cask_dependencies"
|
||||
require "hbc/staged"
|
||||
@ -148,17 +149,7 @@ module Hbc
|
||||
|
||||
def primary_container
|
||||
@primary_container ||= begin
|
||||
container = if @cask.container&.type
|
||||
Container.from_type(@cask.container.type)
|
||||
else
|
||||
Container.for_path(@downloaded_path)
|
||||
end
|
||||
|
||||
unless container
|
||||
raise CaskError, "Uh oh, could not figure out how to unpack '#{@downloaded_path}'."
|
||||
end
|
||||
|
||||
container.new(@cask, @downloaded_path)
|
||||
UnpackStrategy.detect(@downloaded_path, type: @cask.container&.type)
|
||||
end
|
||||
end
|
||||
|
||||
@ -168,7 +159,18 @@ module Hbc
|
||||
odebug "Using container class #{primary_container.class} for #{@downloaded_path}"
|
||||
|
||||
basename = CGI.unescape(File.basename(@cask.url.path))
|
||||
primary_container.extract(to: @cask.staged_path, basename: basename, verbose: verbose?)
|
||||
|
||||
if nested_container = @cask.container&.nested
|
||||
Dir.mktmpdir do |tmpdir|
|
||||
tmpdir = Pathname(tmpdir)
|
||||
primary_container.extract(to: tmpdir, basename: basename, verbose: verbose?)
|
||||
|
||||
UnpackStrategy.detect(tmpdir/nested_container)
|
||||
.extract_nestedly(to: @cask.staged_path, verbose: verbose?)
|
||||
end
|
||||
else
|
||||
primary_container.extract_nestedly(to: @cask.staged_path, basename: basename, verbose: verbose?)
|
||||
end
|
||||
end
|
||||
|
||||
def install_artifacts
|
||||
|
||||
@ -20,6 +20,8 @@ require "messages"
|
||||
require "pp"
|
||||
require "extend/ARGV"
|
||||
|
||||
require "system_command"
|
||||
|
||||
ARGV.extend(HomebrewArgvExtension)
|
||||
|
||||
HOMEBREW_PRODUCT = ENV["HOMEBREW_PRODUCT"]
|
||||
|
||||
@ -1,10 +1,12 @@
|
||||
require "open3"
|
||||
require "ostruct"
|
||||
require "vendor/plist/plist"
|
||||
require "shellwords"
|
||||
|
||||
require "extend/io"
|
||||
require "extend/hash_validator"
|
||||
using HashValidator
|
||||
require "extend/predicable"
|
||||
|
||||
def system_command(*args)
|
||||
SystemCommand.run(*args)
|
||||
@ -27,18 +29,15 @@ class SystemCommand
|
||||
|
||||
def run!
|
||||
@merged_output = []
|
||||
@processed_output = { stdout: "", stderr: "" }
|
||||
odebug command.shelljoin
|
||||
|
||||
each_output_line do |type, line|
|
||||
case type
|
||||
when :stdout
|
||||
puts line.chomp if print_stdout?
|
||||
processed_output[:stdout] << line
|
||||
@merged_output << [:stdout, line]
|
||||
when :stderr
|
||||
$stderr.puts Formatter.error(line.chomp) if print_stderr?
|
||||
processed_output[:stderr] << line
|
||||
@merged_output << [:stderr, line]
|
||||
end
|
||||
end
|
||||
@ -70,7 +69,7 @@ class SystemCommand
|
||||
|
||||
private
|
||||
|
||||
attr_reader :executable, :args, :input, :options, :processed_output, :processed_status, :env
|
||||
attr_reader :executable, :args, :input, :options, :env
|
||||
|
||||
attr_predicate :sudo?, :print_stdout?, :print_stderr?, :must_succeed?
|
||||
|
||||
@ -93,9 +92,9 @@ class SystemCommand
|
||||
end
|
||||
|
||||
def assert_success
|
||||
return if processed_status&.success?
|
||||
return if @status.success?
|
||||
raise ErrorDuringExecution.new(command,
|
||||
status: processed_status,
|
||||
status: @status,
|
||||
output: @merged_output)
|
||||
end
|
||||
|
||||
@ -121,7 +120,10 @@ class SystemCommand
|
||||
raw_stdin.close_write
|
||||
each_line_from [raw_stdout, raw_stderr], &b
|
||||
|
||||
@processed_status = raw_wait_thr.value
|
||||
@status = raw_wait_thr.value
|
||||
rescue SystemCallError => e
|
||||
@status = $CHILD_STATUS
|
||||
@merged_output << [:stderr, e.message]
|
||||
end
|
||||
|
||||
def write_input_to(raw_stdin)
|
||||
@ -151,10 +153,11 @@ class SystemCommand
|
||||
end
|
||||
|
||||
def result
|
||||
Result.new(command,
|
||||
processed_output[:stdout],
|
||||
processed_output[:stderr],
|
||||
processed_status.exitstatus)
|
||||
output = @merged_output.each_with_object(stdout: "", stderr: "") do |(type, line), hash|
|
||||
hash[type] << line
|
||||
end
|
||||
|
||||
Result.new(command, output[:stdout], output[:stderr], @status.exitstatus)
|
||||
end
|
||||
|
||||
class Result
|
||||
|
||||
@ -1,15 +0,0 @@
|
||||
describe Hbc::Artifact::NestedContainer, :cask do
|
||||
describe "install" do
|
||||
it "extracts the specified paths as containers" do
|
||||
cask = Hbc::CaskLoader.load(cask_path("nested-app")).tap do |c|
|
||||
InstallHelper.install_without_artifacts(c)
|
||||
end
|
||||
|
||||
cask.artifacts.select { |a| a.is_a?(described_class) }.each do |artifact|
|
||||
artifact.install_phase(command: NeverSudoSystemCommand, force: false)
|
||||
end
|
||||
|
||||
expect(cask.staged_path.join("MyNestedApp.app")).to be_a_directory
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -1,16 +0,0 @@
|
||||
describe Hbc::Container::Dmg, :cask do
|
||||
describe "#mount" do
|
||||
let(:transmission) { Hbc::CaskLoader.load(cask_path("local-transmission")) }
|
||||
subject(:dmg) { described_class.new(transmission, Pathname(transmission.url.path)) }
|
||||
|
||||
it "does not store nil mounts for dmgs with extra data" do
|
||||
dmg.mount do |mounts|
|
||||
begin
|
||||
expect(mounts).not_to include nil
|
||||
ensure
|
||||
mounts.each(&dmg.public_method(:eject))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -1,18 +0,0 @@
|
||||
describe Hbc::Container::Naked, :cask do
|
||||
it "saves files with spaces in them from uris with encoded spaces" do
|
||||
cask = Hbc::Cask.new("spacey") do
|
||||
url "http://example.com/kevin%20spacey.pkg"
|
||||
version "1.2"
|
||||
end
|
||||
|
||||
path = Pathname("/tmp/downloads/kevin-spacey.pkg")
|
||||
expected_destination = cask.staged_path.join("kevin spacey.pkg")
|
||||
|
||||
container = Hbc::Container::Naked.new(cask, path)
|
||||
|
||||
expect(container).to receive(:system_command!)
|
||||
.with("/usr/bin/ditto", args: ["--", path, expected_destination])
|
||||
|
||||
container.extract(to: cask.staged_path, basename: "kevin spacey.pkg")
|
||||
end
|
||||
end
|
||||
@ -208,4 +208,12 @@ describe SystemCommand do
|
||||
expect(described_class.run("tool", env: { "PATH" => path }).stdout).to include "Hello, world!"
|
||||
end
|
||||
end
|
||||
|
||||
describe "#run" do
|
||||
it "does not raise a `SystemCallError` when the executable does not exist" do
|
||||
expect {
|
||||
described_class.run("non_existent_executable")
|
||||
}.not_to raise_error
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
10
Library/Homebrew/test/unpack_strategy/dmg_spec.rb
Normal file
10
Library/Homebrew/test/unpack_strategy/dmg_spec.rb
Normal file
@ -0,0 +1,10 @@
|
||||
require_relative "shared_examples"
|
||||
|
||||
describe UnpackStrategy::Dmg, :needs_macos do
|
||||
describe "#mount" do
|
||||
let(:path) { TEST_FIXTURE_DIR/"cask/container.dmg" }
|
||||
|
||||
include_examples "UnpackStrategy::detect"
|
||||
include_examples "#extract", children: ["container"]
|
||||
end
|
||||
end
|
||||
@ -5,15 +5,23 @@ module UnpackStrategy
|
||||
|
||||
def self.strategies
|
||||
@strategies ||= [
|
||||
Pkg,
|
||||
Ttf,
|
||||
Otf,
|
||||
Air,
|
||||
Executable,
|
||||
SelfExtractingExecutable,
|
||||
Jar,
|
||||
LuaRock,
|
||||
MicrosoftOfficeXml,
|
||||
Zip,
|
||||
Dmg,
|
||||
Xar,
|
||||
Compress,
|
||||
Tar,
|
||||
Gzip,
|
||||
Bzip2,
|
||||
Gzip,
|
||||
Lzma,
|
||||
Xz,
|
||||
Lzip,
|
||||
Git,
|
||||
@ -22,14 +30,29 @@ module UnpackStrategy
|
||||
Cvs,
|
||||
Fossil,
|
||||
Bazaar,
|
||||
Cab,
|
||||
P7Zip,
|
||||
Sit,
|
||||
Rar,
|
||||
Lha,
|
||||
].freeze
|
||||
end
|
||||
private_class_method :strategies
|
||||
|
||||
def self.detect(path, ref_type: nil, ref: nil)
|
||||
def self.from_type(type)
|
||||
type = {
|
||||
naked: :uncompressed,
|
||||
seven_zip: :p7zip,
|
||||
}.fetch(type, type)
|
||||
|
||||
begin
|
||||
const_get(type.to_s.split("_").map(&:capitalize).join)
|
||||
rescue NameError
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
def self.from_path(path)
|
||||
magic_number = if path.directory?
|
||||
""
|
||||
else
|
||||
@ -50,6 +73,11 @@ module UnpackStrategy
|
||||
Uncompressed
|
||||
end
|
||||
|
||||
strategy
|
||||
end
|
||||
|
||||
def self.detect(path, type: nil, ref_type: nil, ref: nil)
|
||||
strategy = type ? from_type(type) : from_path(path)
|
||||
strategy.new(path, ref_type: ref_type, ref: ref)
|
||||
end
|
||||
|
||||
@ -86,26 +114,41 @@ module UnpackStrategy
|
||||
Directory.new(tmp_unpack_dir).extract(to: to, verbose: verbose)
|
||||
end
|
||||
end
|
||||
|
||||
def dependencies
|
||||
[]
|
||||
end
|
||||
end
|
||||
|
||||
require "unpack_strategy/air"
|
||||
require "unpack_strategy/bazaar"
|
||||
require "unpack_strategy/bzip2"
|
||||
require "unpack_strategy/cab"
|
||||
require "unpack_strategy/compress"
|
||||
require "unpack_strategy/cvs"
|
||||
require "unpack_strategy/directory"
|
||||
require "unpack_strategy/dmg"
|
||||
require "unpack_strategy/executable"
|
||||
require "unpack_strategy/fossil"
|
||||
require "unpack_strategy/generic_unar"
|
||||
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"
|
||||
require "unpack_strategy/lzma"
|
||||
require "unpack_strategy/mercurial"
|
||||
require "unpack_strategy/microsoft_office_xml"
|
||||
require "unpack_strategy/otf"
|
||||
require "unpack_strategy/p7zip"
|
||||
require "unpack_strategy/pkg"
|
||||
require "unpack_strategy/rar"
|
||||
require "unpack_strategy/self_extracting_executable"
|
||||
require "unpack_strategy/sit"
|
||||
require "unpack_strategy/subversion"
|
||||
require "unpack_strategy/tar"
|
||||
require "unpack_strategy/ttf"
|
||||
require "unpack_strategy/uncompressed"
|
||||
require "unpack_strategy/xar"
|
||||
require "unpack_strategy/xz"
|
||||
|
||||
22
Library/Homebrew/unpack_strategy/air.rb
Normal file
22
Library/Homebrew/unpack_strategy/air.rb
Normal file
@ -0,0 +1,22 @@
|
||||
module UnpackStrategy
|
||||
class Air
|
||||
include UnpackStrategy
|
||||
|
||||
def self.can_extract?(path:, magic_number:)
|
||||
path.extname == ".air"
|
||||
end
|
||||
|
||||
def dependencies
|
||||
@dependencies ||= [Hbc::CaskLoader.load("adobe-air")]
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def extract_to_dir(unpack_dir, basename:, verbose:)
|
||||
system_command!(
|
||||
"/Applications/Utilities/Adobe AIR Application Installer.app/Contents/MacOS/Adobe AIR Application Installer",
|
||||
args: ["-silent", "-location", unpack_dir, path],
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -11,7 +11,7 @@ module UnpackStrategy
|
||||
def extract_to_dir(unpack_dir, basename:, verbose:)
|
||||
FileUtils.cp path, unpack_dir/basename, preserve: true
|
||||
quiet_flags = verbose ? [] : ["-q"]
|
||||
safe_system "bunzip2", *quiet_flags, unpack_dir/basename
|
||||
system_command! "bunzip2", args: [*quiet_flags, unpack_dir/basename]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
19
Library/Homebrew/unpack_strategy/cab.rb
Normal file
19
Library/Homebrew/unpack_strategy/cab.rb
Normal file
@ -0,0 +1,19 @@
|
||||
module UnpackStrategy
|
||||
class Cab
|
||||
include UnpackStrategy
|
||||
|
||||
def self.can_extract?(path:, magic_number:)
|
||||
magic_number.match?(/\A(MSCF|MZ)/n)
|
||||
end
|
||||
|
||||
def extract_to_dir(unpack_dir, basename:, verbose:)
|
||||
system_command! "cabextract",
|
||||
args: ["-d", unpack_dir, "--", path],
|
||||
env: { "PATH" => PATH.new(Formula["cabextract"].opt_bin, ENV["PATH"]) }
|
||||
end
|
||||
|
||||
def dependencies
|
||||
@dependencies ||= [Formula["cabextract"]]
|
||||
end
|
||||
end
|
||||
end
|
||||
158
Library/Homebrew/unpack_strategy/dmg.rb
Normal file
158
Library/Homebrew/unpack_strategy/dmg.rb
Normal file
@ -0,0 +1,158 @@
|
||||
require "tempfile"
|
||||
|
||||
module UnpackStrategy
|
||||
class Dmg
|
||||
include UnpackStrategy
|
||||
|
||||
module Bom
|
||||
DMG_METADATA = Set.new %w[
|
||||
.background
|
||||
.com.apple.timemachine.donotpresent
|
||||
.com.apple.timemachine.supported
|
||||
.DocumentRevisions-V100
|
||||
.DS_Store
|
||||
.fseventsd
|
||||
.MobileBackups
|
||||
.Spotlight-V100
|
||||
.TemporaryItems
|
||||
.Trashes
|
||||
.VolumeIcon.icns
|
||||
].freeze
|
||||
private_constant :DMG_METADATA
|
||||
|
||||
refine Pathname do
|
||||
def dmg_metadata?
|
||||
DMG_METADATA.include?(cleanpath.ascend.to_a.last.to_s)
|
||||
end
|
||||
|
||||
# symlinks to system directories (commonly to /Applications)
|
||||
def system_dir_symlink?
|
||||
symlink? && MacOS.system_dir?(readlink)
|
||||
end
|
||||
|
||||
def bom
|
||||
# We need to use `find` here instead of Ruby in order to properly handle
|
||||
# file names containing special characters, such as “e” + “´” vs. “é”.
|
||||
system_command("find", args: [".", "-print0"], chdir: self, print_stderr: false)
|
||||
.stdout
|
||||
.split("\0")
|
||||
.reject { |path| Pathname(path).dmg_metadata? }
|
||||
.reject { |path| (self/path).system_dir_symlink? }
|
||||
.join("\n")
|
||||
end
|
||||
end
|
||||
end
|
||||
private_constant :Bom
|
||||
|
||||
using Bom
|
||||
|
||||
class Mount
|
||||
include UnpackStrategy
|
||||
|
||||
def eject
|
||||
tries ||= 3
|
||||
|
||||
return unless path.exist?
|
||||
|
||||
if tries > 1
|
||||
system_command! "diskutil",
|
||||
args: ["eject", path],
|
||||
print_stderr: false
|
||||
else
|
||||
system_command! "diskutil",
|
||||
args: ["unmount", "force", path],
|
||||
print_stderr: false
|
||||
end
|
||||
rescue ErrorDuringExecution => e
|
||||
raise e if (tries -= 1).zero?
|
||||
sleep 1
|
||||
retry
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def extract_to_dir(unpack_dir, basename:, verbose:)
|
||||
Tempfile.open(["", ".bom"]) do |bomfile|
|
||||
bomfile.close
|
||||
|
||||
Tempfile.open(["", ".list"]) do |filelist|
|
||||
filelist.puts(path.bom)
|
||||
filelist.close
|
||||
|
||||
system_command! "mkbom", args: ["-s", "-i", filelist.path, "--", bomfile.path]
|
||||
end
|
||||
|
||||
system_command! "ditto", args: ["--bom", bomfile.path, "--", path, unpack_dir]
|
||||
end
|
||||
end
|
||||
end
|
||||
private_constant :Mount
|
||||
|
||||
def self.can_extract?(path:, magic_number:)
|
||||
imageinfo = system_command("hdiutil",
|
||||
args: ["imageinfo", path],
|
||||
print_stderr: false).stdout
|
||||
|
||||
!imageinfo.empty?
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def extract_to_dir(unpack_dir, basename:, verbose:)
|
||||
mount(verbose: verbose) do |mounts|
|
||||
raise "No mounts found in '#{path}'; perhaps it is a bad disk image?" if mounts.empty?
|
||||
|
||||
mounts.each do |mount|
|
||||
mount.extract(to: unpack_dir)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def mount(verbose: false)
|
||||
Dir.mktmpdir do |mount_dir|
|
||||
mount_dir = Pathname(mount_dir)
|
||||
|
||||
without_eula = system_command("hdiutil",
|
||||
args: ["attach", "-plist", "-nobrowse", "-readonly", "-noidme", "-mountrandom", mount_dir, path],
|
||||
input: "qn\n",
|
||||
print_stderr: false)
|
||||
|
||||
# If mounting without agreeing to EULA succeeded, there is none.
|
||||
plist = if without_eula.success?
|
||||
without_eula.plist
|
||||
else
|
||||
cdr_path = mount_dir/path.basename.sub_ext(".cdr")
|
||||
|
||||
system_command!("hdiutil", args: ["convert", "-quiet", "-format", "UDTO", "-o", cdr_path, path])
|
||||
|
||||
with_eula = system_command!(
|
||||
"/usr/bin/hdiutil",
|
||||
args: ["attach", "-plist", "-nobrowse", "-readonly", "-noidme", "-mountrandom", mount_dir, cdr_path],
|
||||
)
|
||||
|
||||
if verbose && !(eula_text = without_eula.stdout).empty?
|
||||
ohai "Software License Agreement for '#{path}':"
|
||||
puts eula_text
|
||||
end
|
||||
|
||||
with_eula.plist
|
||||
end
|
||||
|
||||
mounts = if plist.respond_to?(:fetch)
|
||||
plist.fetch("system-entities", [])
|
||||
.map { |entity| entity["mount-point"] }
|
||||
.compact
|
||||
.map { |path| Mount.new(path) }
|
||||
else
|
||||
[]
|
||||
end
|
||||
|
||||
begin
|
||||
yield mounts
|
||||
ensure
|
||||
mounts.each(&:eject)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
17
Library/Homebrew/unpack_strategy/executable.rb
Normal file
17
Library/Homebrew/unpack_strategy/executable.rb
Normal file
@ -0,0 +1,17 @@
|
||||
require_relative "uncompressed"
|
||||
|
||||
require "vendor/macho/macho"
|
||||
|
||||
module UnpackStrategy
|
||||
class Executable < Uncompressed
|
||||
def self.can_extract?(path:, magic_number:)
|
||||
return true if magic_number.match?(/\A#!\s*\S+/n)
|
||||
|
||||
begin
|
||||
path.file? && MachO.open(path).header.executable?
|
||||
rescue MachO::NotAMachOError
|
||||
false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -7,7 +7,7 @@ module UnpackStrategy
|
||||
|
||||
# Fossil database is made up of artifacts, so the `artifact` table must exist.
|
||||
query = "select count(*) from sqlite_master where type = 'view' and name = 'artifact'"
|
||||
Utils.popen_read("sqlite3", path, query).to_i == 1
|
||||
system_command("sqlite3", args: [path, query]).stdout.to_i == 1
|
||||
end
|
||||
|
||||
private
|
||||
@ -19,9 +19,10 @@ module UnpackStrategy
|
||||
[]
|
||||
end
|
||||
|
||||
with_env "PATH" => PATH.new(Formula["fossil"].opt_bin, ENV["PATH"]) do
|
||||
safe_system "fossil", "open", path, *args, chdir: unpack_dir
|
||||
end
|
||||
system_command! "fossil",
|
||||
args: ["open", path, *args],
|
||||
chdir: unpack_dir,
|
||||
env: { "PATH" => PATH.new(Formula["fossil"].opt_bin, ENV["PATH"]) }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
21
Library/Homebrew/unpack_strategy/generic_unar.rb
Normal file
21
Library/Homebrew/unpack_strategy/generic_unar.rb
Normal file
@ -0,0 +1,21 @@
|
||||
module UnpackStrategy
|
||||
class GenericUnar
|
||||
include UnpackStrategy
|
||||
|
||||
def self.can_extract?(path:, magic_number:)
|
||||
false
|
||||
end
|
||||
|
||||
def dependencies
|
||||
@dependencies ||= [Formula["unar"]]
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def extract_to_dir(unpack_dir, basename:, verbose:)
|
||||
system_command! "unar",
|
||||
args: ["-force-overwrite", "-quiet", "-no-directory", "-output-directory", unpack_dir, "--", path],
|
||||
env: { "PATH" => PATH.new(Formula["unar"].opt_bin, ENV["PATH"]) }
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -11,7 +11,8 @@ module UnpackStrategy
|
||||
def extract_to_dir(unpack_dir, basename:, verbose:)
|
||||
FileUtils.cp path, unpack_dir/basename, preserve: true
|
||||
quiet_flags = verbose ? [] : ["-q"]
|
||||
safe_system "gunzip", *quiet_flags, "-N", unpack_dir/basename
|
||||
system_command! "gunzip",
|
||||
args: [*quiet_flags, "-N", "--", unpack_dir/basename]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -13,7 +13,9 @@ module UnpackStrategy
|
||||
private
|
||||
|
||||
def extract_to_dir(unpack_dir, basename:, verbose:)
|
||||
safe_system Formula["lha"].opt_bin/"lha", "xq2w=#{unpack_dir}", path
|
||||
system_command! "lha",
|
||||
args: ["xq2w=#{unpack_dir}", path],
|
||||
env: { "PATH" => PATH.new(Formula["lha"].opt_bin, ENV["PATH"]) }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -15,7 +15,9 @@ module UnpackStrategy
|
||||
def extract_to_dir(unpack_dir, basename:, verbose:)
|
||||
FileUtils.cp path, unpack_dir/basename, preserve: true
|
||||
quiet_flags = verbose ? [] : ["-q"]
|
||||
safe_system Formula["lzip"].opt_bin/"lzip", "-d", *quiet_flags, unpack_dir/basename
|
||||
system_command! "lzip",
|
||||
args: ["-d", *quiet_flags, unpack_dir/basename],
|
||||
env: { "PATH" => PATH.new(Formula["lzip"].opt_bin, ENV["PATH"]) }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
21
Library/Homebrew/unpack_strategy/lzma.rb
Normal file
21
Library/Homebrew/unpack_strategy/lzma.rb
Normal file
@ -0,0 +1,21 @@
|
||||
module UnpackStrategy
|
||||
class Lzma
|
||||
include UnpackStrategy
|
||||
|
||||
def self.can_extract?(path:, magic_number:)
|
||||
magic_number.match?(/\A\]\000\000\200\000/n)
|
||||
end
|
||||
|
||||
def extract_to_dir(unpack_dir, basename:, verbose:)
|
||||
FileUtils.cp path, unpack_dir/basename, preserve: true
|
||||
quiet_flags = verbose ? [] : ["-q"]
|
||||
system_command! "unlzma",
|
||||
args: [*quiet_flags, "--", unpack_dir/basename],
|
||||
env: { "PATH" => PATH.new(Formula["xz"].opt_bin, ENV["PATH"]) }
|
||||
end
|
||||
|
||||
def dependencies
|
||||
@dependencies ||= [Formula["xz"]]
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -9,9 +9,9 @@ module UnpackStrategy
|
||||
private
|
||||
|
||||
def extract_to_dir(unpack_dir, basename:, verbose:)
|
||||
with_env "PATH" => PATH.new(Formula["mercurial"].opt_bin, ENV["PATH"]) do
|
||||
safe_system "hg", "--cwd", path, "archive", "--subrepos", "-y", "-t", "files", unpack_dir
|
||||
end
|
||||
system_command! "hg",
|
||||
args: ["--cwd", path, "archive", "--subrepos", "-y", "-t", "files", unpack_dir],
|
||||
env: { "PATH" => PATH.new(Formula["mercurial"].opt_bin, ENV["PATH"]) }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
9
Library/Homebrew/unpack_strategy/otf.rb
Normal file
9
Library/Homebrew/unpack_strategy/otf.rb
Normal file
@ -0,0 +1,9 @@
|
||||
require_relative "uncompressed"
|
||||
|
||||
module UnpackStrategy
|
||||
class Otf < Uncompressed
|
||||
def self.can_extract?(path:, magic_number:)
|
||||
magic_number.match?(/\AOTTO/n)
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -13,7 +13,9 @@ module UnpackStrategy
|
||||
private
|
||||
|
||||
def extract_to_dir(unpack_dir, basename:, verbose:)
|
||||
safe_system "7zr", "x", "-y", "-bd", "-bso0", path, "-o#{unpack_dir}"
|
||||
system_command! "7zr",
|
||||
args: ["x", "-y", "-bd", "-bso0", path, "-o#{unpack_dir}"],
|
||||
env: { "PATH" => PATH.new(Formula["p7zip"].opt_bin, ENV["PATH"]) }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
10
Library/Homebrew/unpack_strategy/pkg.rb
Normal file
10
Library/Homebrew/unpack_strategy/pkg.rb
Normal file
@ -0,0 +1,10 @@
|
||||
require_relative "uncompressed"
|
||||
|
||||
module UnpackStrategy
|
||||
class Pkg < Uncompressed
|
||||
def self.can_extract?(path:, magic_number:)
|
||||
path.extname.match?(/\A.m?pkg\Z/) &&
|
||||
(path.directory? || magic_number.match?(/\Axar!/n))
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -13,7 +13,9 @@ module UnpackStrategy
|
||||
private
|
||||
|
||||
def extract_to_dir(unpack_dir, basename:, verbose:)
|
||||
safe_system Formula["unrar"].opt_bin/"unrar", "x", "-inul", path, unpack_dir
|
||||
system_command! "unrar",
|
||||
args: ["x", "-inul", path, unpack_dir],
|
||||
env: { "PATH" => PATH.new(Formula["unrar"].opt_bin, ENV["PATH"]) }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -0,0 +1,13 @@
|
||||
require_relative "generic_unar"
|
||||
|
||||
module UnpackStrategy
|
||||
class SelfExtractingExecutable < GenericUnar
|
||||
def self.can_extract?(path:, magic_number:)
|
||||
return false unless magic_number.match?(/\AMZ/n)
|
||||
|
||||
system_command("file",
|
||||
args: [path],
|
||||
print_stderr: false).stdout.include?("self-extracting")
|
||||
end
|
||||
end
|
||||
end
|
||||
9
Library/Homebrew/unpack_strategy/sit.rb
Normal file
9
Library/Homebrew/unpack_strategy/sit.rb
Normal file
@ -0,0 +1,9 @@
|
||||
require_relative "generic_unar"
|
||||
|
||||
module UnpackStrategy
|
||||
class Sit < GenericUnar
|
||||
def self.can_extract?(path:, magic_number:)
|
||||
magic_number.match?(/\AStuffIt/n)
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -9,7 +9,7 @@ module UnpackStrategy
|
||||
private
|
||||
|
||||
def extract_to_dir(unpack_dir, basename:, verbose:)
|
||||
safe_system "svn", "export", "--force", path, unpack_dir
|
||||
system_command! "svn", args: ["export", "--force", path, unpack_dir]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -14,7 +14,7 @@ module UnpackStrategy
|
||||
private
|
||||
|
||||
def extract_to_dir(unpack_dir, basename:, verbose:)
|
||||
safe_system "tar", "xf", path, "-C", unpack_dir
|
||||
system_command! "tar", args: ["xf", path, "-C", unpack_dir]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
12
Library/Homebrew/unpack_strategy/ttf.rb
Normal file
12
Library/Homebrew/unpack_strategy/ttf.rb
Normal file
@ -0,0 +1,12 @@
|
||||
require_relative "uncompressed"
|
||||
|
||||
module UnpackStrategy
|
||||
class Ttf < Uncompressed
|
||||
def self.can_extract?(path:, magic_number:)
|
||||
# TrueType Font
|
||||
magic_number.match?(/\A\000\001\000\000\000/n) ||
|
||||
# Truetype Font Collection
|
||||
magic_number.match?(/\Attcf/n)
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -9,7 +9,7 @@ module UnpackStrategy
|
||||
private
|
||||
|
||||
def extract_to_dir(unpack_dir, basename:, verbose:)
|
||||
safe_system "xar", "-x", "-f", path, "-C", unpack_dir
|
||||
system_command! "xar", args: ["-x", "-f", path, "-C", unpack_dir]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -15,7 +15,9 @@ module UnpackStrategy
|
||||
def extract_to_dir(unpack_dir, basename:, verbose:)
|
||||
FileUtils.cp path, unpack_dir/basename, preserve: true
|
||||
quiet_flags = verbose ? [] : ["-q"]
|
||||
safe_system Formula["xz"].opt_bin/"unxz", *quiet_flags, "-T0", unpack_dir/basename
|
||||
system_command! "unxz",
|
||||
args: [*quiet_flags, "-T0", "--", unpack_dir/basename],
|
||||
env: { "PATH" => PATH.new(Formula["xz"].opt_bin, ENV["PATH"]) }
|
||||
extract_nested_tar(unpack_dir)
|
||||
end
|
||||
|
||||
|
||||
@ -10,7 +10,7 @@ module UnpackStrategy
|
||||
|
||||
def extract_to_dir(unpack_dir, basename:, verbose:)
|
||||
quiet_flags = verbose ? [] : ["-qq"]
|
||||
safe_system "unzip", *quiet_flags, path, "-d", unpack_dir
|
||||
system_command! "unzip", args: [*quiet_flags, path, "-d", unpack_dir]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user