Merge pull request #4656 from amyspark/com-apple-quarantine

Enable quarantining of Homebrew-Cask's downloads
This commit is contained in:
Claudia 2018-08-31 15:50:29 +02:00 committed by GitHub
commit 47d3bbee1c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 464 additions and 47 deletions

View File

@ -65,11 +65,8 @@ module Hbc
ohai "Backing #{self.class.english_name} '#{target.basename}' up to '#{source}'."
source.dirname.mkpath
if target.parent.writable?
FileUtils.cp_r(target, source)
else
command.run!("/bin/cp", args: ["-r", target, source], sudo: true)
end
# We need to preserve extended attributes between copies.
command.run!("/bin/cp", args: ["-pR", target, source], sudo: !target.parent.writable?)
delete(target, force: force, command: command, **options)
end

View File

@ -2,15 +2,16 @@ require "hbc/download"
module Hbc
class Auditor
def self.audit(cask, audit_download: false, check_token_conflicts: false, commit_range: nil)
new(cask, audit_download, check_token_conflicts, commit_range).audit
def self.audit(cask, audit_download: false, check_token_conflicts: false, quarantine: true, commit_range: nil)
new(cask, audit_download, check_token_conflicts, quarantine, commit_range).audit
end
attr_reader :cask, :commit_range
def initialize(cask, audit_download, check_token_conflicts, commit_range)
def initialize(cask, audit_download, check_token_conflicts, quarantine, commit_range)
@cask = cask
@audit_download = audit_download
@quarantine = quarantine
@commit_range = commit_range
@check_token_conflicts = check_token_conflicts
end
@ -19,6 +20,10 @@ module Hbc
@audit_download
end
def quarantine?
@quarantine
end
def check_token_conflicts?
@check_token_conflicts
end
@ -52,7 +57,7 @@ module Hbc
end
def audit_cask_instance(cask)
download = audit_download? && Download.new(cask)
download = audit_download? && Download.new(cask, quarantine: quarantine?)
audit = Audit.new(cask, download: download,
check_token_conflicts: check_token_conflicts?,
commit_range: commit_range)

View File

@ -7,11 +7,12 @@ module Hbc
include Options
include Homebrew::Search
option "--[no-]binaries", :binaries, true
option "--debug", :debug, false
option "--verbose", :verbose, false
option "--outdated", :outdated_only, false
option "--require-sha", :require_sha, false
option "--[no-]binaries", :binaries, true
option "--debug", :debug, false
option "--verbose", :verbose, false
option "--outdated", :outdated_only, false
option "--require-sha", :require_sha, false
option "--[no-]quarantine", :quarantine, true
def self.command_name
@command_name ||= name.sub(/^.*:/, "").gsub(/(.)([A-Z])/, '\1_\2').downcase

View File

@ -18,7 +18,9 @@ module Hbc
def audit(cask)
odebug "Auditing Cask #{cask}"
Auditor.audit(cask, audit_download: download?, check_token_conflicts: token_conflicts?)
Auditor.audit(cask, audit_download: download?,
check_token_conflicts: token_conflicts?,
quarantine: quarantine?)
end
end
end

View File

@ -14,7 +14,7 @@ module Hbc
casks.each do |cask|
Installer.print_caveats(cask)
ohai "Downloading external files for Cask #{cask}"
downloaded_path = Download.new(cask, force: force?).perform
downloaded_path = Download.new(cask, force: force?, quarantine: quarantine?).perform
Verify.all(cask, downloaded_path)
ohai "Success! Downloaded to -> #{downloaded_path}"
end

View File

@ -17,7 +17,8 @@ module Hbc
verbose: verbose?,
force: force?,
skip_cask_deps: skip_cask_deps?,
require_sha: require_sha?).install
require_sha: require_sha?,
quarantine: quarantine?).install
rescue CaskAlreadyInstalledError => e
opoo e.message
end

View File

@ -7,7 +7,8 @@ module Hbc
verbose: verbose?,
force: force?,
skip_cask_deps: skip_cask_deps?,
require_sha: require_sha?).reinstall
require_sha: require_sha?,
quarantine: quarantine?).reinstall
end
end

View File

@ -36,7 +36,11 @@ module Hbc
old_cask = CaskLoader.load(old_cask.installed_caskfile)
old_cask_installer = Installer.new(old_cask, binaries: binaries?, verbose: verbose?, force: force?, upgrade: true)
old_cask_installer =
Installer.new(old_cask, binaries: binaries?,
verbose: verbose?,
force: force?,
upgrade: true)
new_cask = CaskLoader.load(old_cask.to_s)
@ -46,7 +50,8 @@ module Hbc
force: force?,
skip_cask_deps: skip_cask_deps?,
require_sha: require_sha?,
upgrade: true)
upgrade: true,
quarantine: quarantine?)
started_upgrade = false
new_artifacts_installed = false

View File

@ -1,18 +1,21 @@
require "fileutils"
require "hbc/quarantine"
require "hbc/verify"
module Hbc
class Download
attr_reader :cask
def initialize(cask, force: false)
def initialize(cask, force: false, quarantine: true)
@cask = cask
@force = force
@quarantine = quarantine
end
def perform
clear_cache
fetch
quarantine
downloaded_path
end
@ -38,5 +41,13 @@ module Hbc
rescue StandardError => e
raise CaskError, "Download failed on Cask '#{cask}' with message: #{e}"
end
def quarantine
return unless @quarantine
return unless Quarantine.available?
return if Quarantine.detect(@downloaded_path)
Quarantine.cask(cask: @cask, download_path: @downloaded_path)
end
end
end

View File

@ -32,13 +32,13 @@ module Hbc
class CaskUnavailableError < AbstractCaskErrorWithToken
def to_s
"Cask '#{token}' is unavailable" << (reason.empty? ? "." : ": #{reason}")
"Cask '#{token}' is unavailable#{reason.empty? ? "." : ": #{reason}"}"
end
end
class CaskUnreadableError < CaskUnavailableError
def to_s
"Cask '#{token}' is unreadable" << (reason.empty? ? "." : ": #{reason}")
"Cask '#{token}' is unreadable#{reason.empty? ? "." : ": #{reason}"}"
end
end
@ -73,7 +73,7 @@ module Hbc
class CaskCyclicDependencyError < AbstractCaskErrorWithToken
def to_s
"Cask '#{token}' includes cyclic dependencies on other Casks" << (reason.empty? ? "." : ": #{reason}")
"Cask '#{token}' includes cyclic dependencies on other Casks#{reason.empty? ? "." : ": #{reason}"}"
end
end
@ -91,7 +91,7 @@ module Hbc
class CaskInvalidError < AbstractCaskErrorWithToken
def to_s
"Cask '#{token}' definition is invalid" << (reason.empty? ? "." : ": #{reason}")
"Cask '#{token}' definition is invalid#{reason.empty? ? "." : ": #{reason}"}"
end
end
@ -149,4 +149,39 @@ module Hbc
EOS
end
end
class CaskQuarantineError < CaskError
attr_reader :path, :reason
def initialize(path, reason)
@path = path
@reason = reason
end
def to_s
s = "Failed to quarantine #{path}."
unless reason.empty?
s << " Here's the reason:\n"
s << Formatter.error(reason)
s << "\n" unless reason.end_with?("\n")
end
s
end
end
class CaskQuarantinePropagationError < CaskQuarantineError
def to_s
s = "Failed to quarantine one or more files within #{path}."
unless reason.empty?
s << " Here's the reason:\n"
s << Formatter.error(reason)
s << "\n" unless reason.end_with?("\n")
end
s
end
end
end

View File

@ -7,6 +7,7 @@ require "hbc/cask_dependencies"
require "hbc/download"
require "hbc/staged"
require "hbc/verify"
require "hbc/quarantine"
require "cgi"
@ -23,7 +24,10 @@ module Hbc
PERSISTENT_METADATA_SUBDIRS = ["gpg"].freeze
def initialize(cask, command: SystemCommand, force: false, skip_cask_deps: false, binaries: true, verbose: false, require_sha: false, upgrade: false, installed_as_dependency: false)
def initialize(cask, command: SystemCommand, force: false,
skip_cask_deps: false, binaries: true, verbose: false,
require_sha: false, upgrade: false,
installed_as_dependency: false, quarantine: true)
@cask = cask
@command = command
@force = force
@ -34,9 +38,12 @@ module Hbc
@reinstall = false
@upgrade = upgrade
@installed_as_dependency = installed_as_dependency
@quarantine = quarantine
end
attr_predicate :binaries?, :force?, :skip_cask_deps?, :require_sha?, :upgrade?, :verbose?, :installed_as_dependency?
attr_predicate :binaries?, :force?, :skip_cask_deps?, :require_sha?,
:upgrade?, :verbose?, :installed_as_dependency?,
:quarantine?
def self.print_caveats(cask)
odebug "Printing caveats"
@ -86,6 +93,7 @@ module Hbc
uninstall_existing_cask if @reinstall
oh1 "Installing Cask #{Formatter.identifier(@cask)}"
opoo "macOS's Gatekeeper has been disabled for this Cask" unless quarantine?
stage
install_artifacts
enable_accessibility_access
@ -137,7 +145,7 @@ module Hbc
def download
odebug "Downloading"
@downloaded_path = Download.new(@cask, force: false).perform
@downloaded_path = Download.new(@cask, force: false, quarantine: quarantine?).perform
odebug "Downloaded to -> #{@downloaded_path}"
@downloaded_path
end
@ -176,6 +184,11 @@ module Hbc
else
primary_container.extract_nestedly(to: @cask.staged_path, basename: basename, verbose: verbose?)
end
return unless quarantine?
return unless Quarantine.available?
Quarantine.propagate(from: @downloaded_path, to: @cask.staged_path, command: @command)
end
def install_artifacts

View File

@ -0,0 +1,80 @@
require "development_tools"
module Hbc
module Quarantine
module_function
QUARANTINE_ATTRIBUTE = "com.apple.quarantine".freeze
QUARANTINE_SCRIPT = (HOMEBREW_LIBRARY_PATH/"cask/lib/hbc/utils/quarantine.swift").freeze
# @private
def swift
@swift ||= DevelopmentTools.locate("swift")
end
def available?
status = !swift.nil?
odebug "Quarantine is #{status ? "available" : "not available"}."
status
end
def detect(file)
return if file.nil?
odebug "Verifying Gatekeeper status of #{file}"
quarantine_status = !status(file).empty?
odebug "#{file} is #{quarantine_status ? "quarantined" : "not quarantined"}"
quarantine_status
end
def status(file, command: SystemCommand)
command.run("/usr/bin/xattr",
args: ["-p", QUARANTINE_ATTRIBUTE, file],
print_stderr: false).stdout.rstrip
end
def cask(cask: nil, download_path: nil, command: SystemCommand)
return if cask.nil? || download_path.nil?
odebug "Quarantining #{download_path}"
quarantiner = command.run(swift,
args: [
QUARANTINE_SCRIPT,
download_path,
cask.url.to_s,
cask.homepage.to_s,
])
return if quarantiner.success?
case quarantiner.exit_status
when 2
raise CaskQuarantineError.new(download_path, "Insufficient parameters")
else
raise CaskQuarantineError.new(download_path, quarantiner.stderr)
end
end
def propagate(from: nil, to: nil, command: SystemCommand)
return if from.nil? || to.nil?
raise CaskError, "#{from} was not quarantined properly." unless detect(from)
odebug "Propagating quarantine from #{from} to #{to}"
quarantine_status = status(from, command: command)
quarantiner = command.run("/usr/bin/xattr",
args: ["-w", "-r", QUARANTINE_ATTRIBUTE, quarantine_status, to],
print_stderr: false)
return if quarantiner.success?
raise CaskQuarantinePropagationError.new(to, quarantiner.stderr)
end
end
end

View File

@ -0,0 +1,42 @@
#!/usr/bin/swift
import Foundation
struct swifterr: TextOutputStream {
public static var stream = swifterr()
mutating func write(_ string: String) { fputs(string, stderr) }
}
if (CommandLine.arguments.count < 4) {
exit(2)
}
let dataLocationUrl: NSURL = NSURL.init(fileURLWithPath: CommandLine.arguments[1])
var errorBag: NSError?
let quarantineProperties: [String: Any] = [
kLSQuarantineAgentNameKey as String: "Homebrew-Cask",
kLSQuarantineTypeKey as String: kLSQuarantineTypeWebDownload,
kLSQuarantineDataURLKey as String: CommandLine.arguments[2],
kLSQuarantineOriginURLKey as String: CommandLine.arguments[3]
]
if (dataLocationUrl.checkResourceIsReachableAndReturnError(&errorBag)) {
do {
try dataLocationUrl.setResourceValue(
quarantineProperties as NSDictionary,
forKey: URLResourceKey.quarantinePropertiesKey
)
}
catch {
print(error.localizedDescription, to: &swifterr.stream)
exit(1)
}
}
else {
print(errorBag!.localizedDescription, to: &swifterr.stream)
exit(3)
}
exit(0)

View File

@ -19,7 +19,7 @@ names, and other aspects of this manual are still subject to change.
## FREQUENTLY USED COMMANDS
* `install` [--force] [--skip-cask-deps] [--require-sha] [--language=<iso-language>[,<iso-language> ... ]] <token> [ <token> ... ]:
* `install` [--force] [--skip-cask-deps] [--require-sha] [--no-quarantine] [--language=<iso-language>[,<iso-language> ... ]] <token> [ <token> ... ]:
Install Cask identified by <token>.
* `uninstall` [--force] <token> [ <token> ... ]:
@ -56,10 +56,11 @@ names, and other aspects of this manual are still subject to change.
* `edit` <token>:
Open the given Cask definition file for editing.
* `fetch` [--force] <token> [ <token> ... ]:
* `fetch` [--force] [--no-quarantine] <token> [ <token> ... ]:
Download remote application files for the given Cask to the local
cache. With `--force`, force re-download even if the files are already
cached.
cached. `--no-quarantine` will prevent Gatekeeper from
enforcing its security restrictions on the Cask.
* `home` or `homepage` [ <token> ... ]:
Display the homepage associated with a given Cask in a browser.
@ -69,11 +70,12 @@ names, and other aspects of this manual are still subject to change.
* `info` or `abv` <token> [ <token> ... ]:
Display information about the given Cask.
* `install` [--force] [--skip-cask-deps] [--require-sha] <token> [ <token> ... ]:
* `install` [--force] [--skip-cask-deps] [--require-sha] [--no-quarantine] <token> [ <token> ... ]:
Install the given Cask. With `--force`, re-install even if the Cask
appears to be already present. With `--skip-cask-deps`, skip any Cask
dependencies. `--require-sha` will abort installation if the Cask does not
have a checksum defined.
have a checksum defined. `--no-quarantine` will prevent Gatekeeper from
enforcing its security restrictions on the Cask.
<token> is usually the ID of a Cask,
but see [OTHER WAYS TO SPECIFY A CASK][] for variations.
@ -97,7 +99,7 @@ names, and other aspects of this manual are still subject to change.
`--verbose` forces the display of the outdated and latest version.<br>
`--quiet` suppresses the display of versions.
* `reinstall` <token> [ <token> ... ]:
* `reinstall` [--no-quarantine] <token> [ <token> ... ]:
Reinstall the given Cask.
* `search` or `-S` [<text> | /<regexp>/]:
@ -162,6 +164,10 @@ in a future version.
* `--require-sha`:
Abort Cask installation if the Cask does not have a checksum defined.
* `--no-quarantine`:
Prevent Gatekeeper from enforcing its security restrictions on the Cask.
This will let you run it straightaway.
* `--verbose`:
Give additional feedback during installation.

View File

@ -19,7 +19,7 @@ describe Hbc::CLI::Audit, :cask do
expect(Hbc::CaskLoader).to receive(:load).with(cask_token).and_return(cask)
expect(Hbc::Auditor).to receive(:audit)
.with(cask, audit_download: false, check_token_conflicts: false)
.with(cask, audit_download: false, check_token_conflicts: false, quarantine: true)
.and_return(true)
described_class.run(cask_token)
@ -30,7 +30,7 @@ describe Hbc::CLI::Audit, :cask do
it "does not download the Cask per default" do
allow(Hbc::CaskLoader).to receive(:load).and_return(cask)
expect(Hbc::Auditor).to receive(:audit)
.with(cask, audit_download: false, check_token_conflicts: false)
.with(cask, audit_download: false, check_token_conflicts: false, quarantine: true)
.and_return(true)
described_class.run("casktoken")
@ -39,7 +39,7 @@ describe Hbc::CLI::Audit, :cask do
it "download a Cask if --download flag is set" do
allow(Hbc::CaskLoader).to receive(:load).and_return(cask)
expect(Hbc::Auditor).to receive(:audit)
.with(cask, audit_download: true, check_token_conflicts: false)
.with(cask, audit_download: true, check_token_conflicts: false, quarantine: true)
.and_return(true)
described_class.run("casktoken", "--download")
@ -50,7 +50,7 @@ describe Hbc::CLI::Audit, :cask do
it "does not check for token conflicts per default" do
allow(Hbc::CaskLoader).to receive(:load).and_return(cask)
expect(Hbc::Auditor).to receive(:audit)
.with(cask, audit_download: false, check_token_conflicts: false)
.with(cask, audit_download: false, check_token_conflicts: false, quarantine: true)
.and_return(true)
described_class.run("casktoken")
@ -59,7 +59,7 @@ describe Hbc::CLI::Audit, :cask do
it "checks for token conflicts if --token-conflicts flag is set" do
allow(Hbc::CaskLoader).to receive(:load).and_return(cask)
expect(Hbc::Auditor).to receive(:audit)
.with(cask, audit_download: false, check_token_conflicts: true)
.with(cask, audit_download: false, check_token_conflicts: true, quarantine: true)
.and_return(true)
described_class.run("casktoken", "--token-conflicts")

View File

@ -51,7 +51,7 @@ describe Hbc::CLI::Info, :cask do
Not installed
From: https://github.com/Homebrew/homebrew-cask/blob/master/Casks/local-transmission.rb
==> Name
None
Transmission
==> Artifacts
Transmission.app (App)
EOS

View File

@ -0,0 +1,213 @@
describe Hbc::Quarantine, :cask do
matcher :be_quarantined do
match do |path|
expect(
described_class.detect(path),
).to be true
end
end
describe "by default" do
it "quarantines a nice fresh Cask" do
Hbc::CLI::Install.run("local-transmission")
expect(
Hbc::CaskLoader.load(cask_path("local-transmission")),
).to be_installed
expect(
Hbc::Config.global.appdir.join("Transmission.app"),
).to be_quarantined
end
it "quarantines Cask fetches" do
Hbc::CLI::Fetch.run("local-transmission")
local_transmission = Hbc::CaskLoader.load(cask_path("local-transmission"))
cached_location = Hbc::Download.new(local_transmission, force: false, quarantine: false).perform
expect(cached_location).to be_quarantined
end
it "quarantines Cask audits" do
Hbc::CLI::Audit.run("local-transmission", "--download")
local_transmission = Hbc::CaskLoader.load(cask_path("local-transmission"))
cached_location = Hbc::Download.new(local_transmission, force: false, quarantine: false).perform
expect(cached_location).to be_quarantined
end
it "quarantines dmg-based Casks" do
Hbc::CLI::Install.run("container-dmg")
expect(
Hbc::CaskLoader.load(cask_path("container-dmg")),
).to be_installed
expect(Hbc::Config.global.appdir.join("container")).to be_quarantined
end
it "quarantines tar-gz-based Casks" do
Hbc::CLI::Install.run("container-tar-gz")
expect(
Hbc::CaskLoader.load(cask_path("container-tar-gz")),
).to be_installed
expect(Hbc::Config.global.appdir.join("container")).to be_quarantined
end
it "quarantines xar-based Casks" do
Hbc::CLI::Install.run("container-xar")
expect(
Hbc::CaskLoader.load(cask_path("container-xar")),
).to be_installed
expect(Hbc::Config.global.appdir.join("container")).to be_quarantined
end
it "quarantines pure bzip2-based Casks" do
Hbc::CLI::Install.run("container-bzip2")
expect(
Hbc::CaskLoader.load(cask_path("container-bzip2")),
).to be_installed
expect(Hbc::Config.global.appdir.join("container")).to be_quarantined
end
it "quarantines pure gzip-based Casks" do
Hbc::CLI::Install.run("container-gzip")
expect(
Hbc::CaskLoader.load(cask_path("container-gzip")),
).to be_installed
expect(Hbc::Config.global.appdir.join("container")).to be_quarantined
end
it "quarantines the pkg in naked-pkg-based Casks" do
Hbc::CLI::Install.run("container-pkg")
naked_pkg = Hbc::CaskLoader.load(cask_path("container-pkg"))
expect(naked_pkg).to be_installed
expect(
Hbc::Caskroom.path.join("container-pkg", naked_pkg.version, "container.pkg"),
).to be_quarantined
end
it "quarantines a nested container" do
Hbc::CLI::Install.run("nested-app")
expect(
Hbc::CaskLoader.load(cask_path("nested-app")),
).to be_installed
expect(Hbc::Config.global.appdir.join("MyNestedApp.app")).to be_quarantined
end
end
describe "when disabled" do
it "does not quarantine even a nice, fresh Cask" do
Hbc::CLI::Install.run("local-transmission", "--no-quarantine")
expect(
Hbc::CaskLoader.load(cask_path("local-transmission")),
).to be_installed
expect(Hbc::Config.global.appdir.join("Transmission.app")).to_not be_quarantined
end
it "does not quarantine Cask fetches" do
Hbc::CLI::Fetch.run("local-transmission", "--no-quarantine")
local_transmission = Hbc::CaskLoader.load(cask_path("local-transmission"))
cached_location = Hbc::Download.new(local_transmission, force: false, quarantine: false).perform
expect(cached_location).to_not be_quarantined
end
it "does not quarantine dmg-based Casks" do
Hbc::CLI::Install.run("container-dmg", "--no-quarantine")
expect(
Hbc::CaskLoader.load(cask_path("container-dmg")),
).to be_installed
expect(Hbc::Config.global.appdir.join("container")).to_not be_quarantined
end
it "does not quarantine tar-gz-based Casks" do
Hbc::CLI::Install.run("container-tar-gz", "--no-quarantine")
expect(
Hbc::CaskLoader.load(cask_path("container-tar-gz")),
).to be_installed
expect(Hbc::Config.global.appdir.join("container")).to_not be_quarantined
end
it "does not quarantine xar-based Casks" do
Hbc::CLI::Install.run("container-xar", "--no-quarantine")
expect(
Hbc::CaskLoader.load(cask_path("container-xar")),
).to be_installed
expect(Hbc::Config.global.appdir.join("container")).to_not be_quarantined
end
it "does not quarantine pure bzip2-based Casks" do
Hbc::CLI::Install.run("container-bzip2", "--no-quarantine")
expect(
Hbc::CaskLoader.load(cask_path("container-bzip2")),
).to be_installed
expect(Hbc::Config.global.appdir.join("container")).to_not be_quarantined
end
it "does not quarantine pure gzip-based Casks" do
Hbc::CLI::Install.run("container-gzip", "--no-quarantine")
expect(
Hbc::CaskLoader.load(cask_path("container-gzip")),
).to be_installed
expect(Hbc::Config.global.appdir.join("container")).to_not be_quarantined
end
it "does not quarantine the pkg in naked-pkg-based Casks" do
Hbc::CLI::Install.run("container-pkg", "--no-quarantine")
naked_pkg = Hbc::CaskLoader.load(cask_path("container-pkg"))
expect(naked_pkg).to be_installed
expect(
Hbc::Caskroom.path.join("container-pkg", naked_pkg.version, "container.pkg"),
).to_not be_quarantined
end
it "does not quarantine a nested container" do
Hbc::CLI::Install.run("nested-app", "--no-quarantine")
expect(
Hbc::CaskLoader.load(cask_path("nested-app")),
).to be_installed
expect(Hbc::Config.global.appdir.join("MyNestedApp.app")).to_not be_quarantined
end
it "does not quarantine Cask audits" do
Hbc::CLI::Audit.run("local-transmission", "--download", "--no-quarantine")
local_transmission = Hbc::CaskLoader.load(cask_path("local-transmission"))
cached_location = Hbc::Download.new(local_transmission, force: false, quarantine: false).perform
expect(cached_location).to_not be_quarantined
end
end
end

View File

@ -1,4 +1,5 @@
cask 'local-transmission' do
name 'Transmission'
version '2.61'
sha256 'e44ffa103fbf83f55c8d0b1bea309a43b2880798dae8620b1ee8da5e1095ec68'

View File

@ -90,7 +90,7 @@ module UnpackStrategy
args: ["--bom", bomfile.path, "--", path, unpack_dir],
verbose: verbose
FileUtils.chmod "u+w", Pathname.glob(unpack_dir/"**/*").reject(&:symlink?)
FileUtils.chmod "u+w", Pathname.glob(unpack_dir/"**/*", File::FNM_DOTMATCH).reject(&:symlink?)
end
end
end

View File

@ -18,7 +18,7 @@ Homebrew\-Cask works robustly enough that we welcome new users, but the project
.SH "FREQUENTLY USED COMMANDS"
.
.TP
\fBinstall\fR [\-\-force] [\-\-skip\-cask\-deps] [\-\-require\-sha] [\-\-language=\fIiso\-language\fR[,\fIiso\-language\fR \.\.\. ]] \fItoken\fR [ \fItoken\fR \.\.\. ]
\fBinstall\fR [\-\-force] [\-\-skip\-cask\-deps] [\-\-require\-sha] [\-\-no\-quarantine] [\-\-language=\fIiso\-language\fR[,\fIiso\-language\fR \.\.\. ]] \fItoken\fR [ \fItoken\fR \.\.\. ]
Install Cask identified by \fItoken\fR\.
.
.TP
@ -59,8 +59,8 @@ Check for configuration issues\. Can be useful to upload as a gist for developer
Open the given Cask definition file for editing\.
.
.TP
\fBfetch\fR [\-\-force] \fItoken\fR [ \fItoken\fR \.\.\. ]
Download remote application files for the given Cask to the local cache\. With \fB\-\-force\fR, force re\-download even if the files are already cached\.
\fBfetch\fR [\-\-force] [\-\-no\-quarantine] \fItoken\fR [ \fItoken\fR \.\.\. ]
Download remote application files for the given Cask to the local cache\. With \fB\-\-force\fR, force re\-download even if the files are already cached\. \fB\-\-no\-quarantine\fR will prevent Gatekeeper from enforcing its security restrictions on the Cask\.
.
.TP
\fBhome\fR or \fBhomepage\fR [ \fItoken\fR \.\.\. ]
@ -74,8 +74,8 @@ With no arguments, display the project page \fIhttps://caskroom\.github\.io/\fR\
Display information about the given Cask\.
.
.TP
\fBinstall\fR [\-\-force] [\-\-skip\-cask\-deps] [\-\-require\-sha] \fItoken\fR [ \fItoken\fR \.\.\. ]
Install the given Cask\. With \fB\-\-force\fR, re\-install even if the Cask appears to be already present\. With \fB\-\-skip\-cask\-deps\fR, skip any Cask dependencies\. \fB\-\-require\-sha\fR will abort installation if the Cask does not have a checksum defined\.
\fBinstall\fR [\-\-force] [\-\-skip\-cask\-deps] [\-\-require\-sha] [\-\-no\-quarantine] \fItoken\fR [ \fItoken\fR \.\.\. ]
Install the given Cask\. With \fB\-\-force\fR, re\-install even if the Cask appears to be already present\. With \fB\-\-skip\-cask\-deps\fR, skip any Cask dependencies\. \fB\-\-require\-sha\fR will abort installation if the Cask does not have a checksum defined\. \fB\-\-no\-quarantine\fR will prevent Gatekeeper from enforcing its security restrictions on the Cask\.
.
.IP
\fItoken\fR is usually the ID of a Cask, but see \fIOTHER WAYS TO SPECIFY A CASK\fR for variations\.
@ -98,7 +98,7 @@ Without token arguments, display all the installed Casks that have newer version
\fB\-\-quiet\fR suppresses the display of versions\.
.
.TP
\fBreinstall\fR \fItoken\fR [ \fItoken\fR \.\.\. ]
\fBreinstall\fR [\-\-no\-quarantine] \fItoken\fR [ \fItoken\fR \.\.\. ]
Reinstall the given Cask\.
.
.TP
@ -158,6 +158,10 @@ Skip Cask dependencies when installing\.
Abort Cask installation if the Cask does not have a checksum defined\.
.
.TP
\fB\-\-no\-quarantine\fR
Prevent Gatekeeper from enforcing its security restrictions on the Cask\. This will let you run it straightaway\.
.
.TP
\fB\-\-verbose\fR
Give additional feedback during installation\.
.