Merge pull request #14033 from ThatsJustCheesy/cask-install-adopt

Add `--adopt` switch
This commit is contained in:
Mike McQuaid 2022-10-26 12:16:49 +01:00 committed by GitHub
commit 7eb68835cc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 98 additions and 9 deletions

View File

@ -34,8 +34,33 @@ module Cask
private
def move(force: false, command: nil, **options)
def move(adopt: false, force: false, verbose: false, command: nil, **options)
unless source.exist?
raise CaskError, "It seems the #{self.class.english_name} source '#{source}' is not there."
end
if Utils.path_occupied?(target)
if adopt
ohai "Adopting existing #{self.class.english_name} at '#{target}'"
same = command.run(
"/usr/bin/diff",
args: ["--recursive", "--brief", source, target],
verbose: verbose,
print_stdout: verbose,
).success?
unless same
raise CaskError,
"It seems the existing #{self.class.english_name} is different from " \
"the one being installed."
end
# Remove the source as we don't need to move it to the target location
source.rmtree
return post_move(command)
end
message = "It seems there is already #{self.class.english_article} " \
"#{self.class.english_name} at '#{target}'"
raise CaskError, "#{message}." unless force
@ -44,10 +69,6 @@ module Cask
delete(target, force: force, command: command, **options)
end
unless source.exist?
raise CaskError, "It seems the #{self.class.english_name} source '#{source}' is not there."
end
ohai "Moving #{self.class.english_name} '#{source.basename}' to '#{target}'"
if target.dirname.ascend.find(&:directory?).writable?
target.dirname.mkpath
@ -61,6 +82,11 @@ module Cask
command.run!("/bin/mv", args: [source, target], sudo: true)
end
post_move(command)
end
# Performs any actions necessary after the source has been moved to the target location.
def post_move(command)
FileUtils.ln_sf target, source
add_altname_metadata(target, source.basename, command: command)

View File

@ -10,6 +10,10 @@ module Cask
extend T::Sig
OPTIONS = [
[:switch, "--adopt", {
description: "Adopt existing artifacts in the destination that are identical to those being installed. " \
"Cannot be combined with --force.",
}],
[:switch, "--skip-cask-deps", {
description: "Skip installing cask dependencies.",
}],
@ -40,6 +44,7 @@ module Cask
binaries: args.binaries?,
verbose: args.verbose?,
force: args.force?,
adopt: args.adopt?,
skip_cask_deps: args.skip_cask_deps?,
require_sha: args.require_sha?,
quarantine: args.quarantine?,
@ -52,6 +57,7 @@ module Cask
*casks,
verbose: nil,
force: nil,
adopt: nil,
binaries: nil,
skip_cask_deps: nil,
require_sha: nil,
@ -65,6 +71,7 @@ module Cask
options = {
verbose: verbose,
force: force,
adopt: adopt,
binaries: binaries,
skip_cask_deps: skip_cask_deps,
require_sha: require_sha,

View File

@ -20,7 +20,7 @@ module Cask
extend Predicable
def initialize(cask, command: SystemCommand, force: false,
def initialize(cask, command: SystemCommand, force: false, adopt: false,
skip_cask_deps: false, binaries: true, verbose: false,
zap: false, require_sha: false, upgrade: false,
installed_as_dependency: false, quarantine: true,
@ -28,6 +28,7 @@ module Cask
@cask = cask
@command = command
@force = force
@adopt = adopt
@skip_cask_deps = skip_cask_deps
@binaries = binaries
@verbose = verbose
@ -41,7 +42,7 @@ module Cask
@quiet = quiet
end
attr_predicate :binaries?, :force?, :skip_cask_deps?, :require_sha?,
attr_predicate :binaries?, :force?, :adopt?, :skip_cask_deps?, :require_sha?,
:reinstall?, :upgrade?, :verbose?, :zap?, :installed_as_dependency?,
:quarantine?, :quiet?
@ -237,7 +238,7 @@ module Cask
next if artifact.is_a?(Artifact::Binary) && !binaries?
artifact.install_phase(command: @command, verbose: verbose?, force: force?)
artifact.install_phase(command: @command, verbose: verbose?, adopt: adopt?, force: force?)
already_installed_artifacts.unshift(artifact)
end
@ -348,6 +349,7 @@ module Cask
Installer.new(
cask_or_formula,
adopt: adopt?,
binaries: binaries?,
verbose: verbose?,
installed_as_dependency: true,

View File

@ -140,6 +140,7 @@ module Homebrew
conflicts "--ignore-dependencies", "--only-dependencies"
conflicts "--build-from-source", "--build-bottle", "--force-bottle"
conflicts "--adopt", "--force"
named_args [:formula, :cask], min: 1
end
@ -193,6 +194,7 @@ module Homebrew
binaries: args.binaries?,
verbose: args.verbose?,
force: args.force?,
adopt: args.adopt?,
require_sha: args.require_sha?,
skip_cask_deps: args.skip_cask_deps?,
quarantine: args.quarantine?,

View File

@ -4,13 +4,14 @@
describe Cask::Artifact::App, :cask do
let(:cask) { Cask::CaskLoader.load(cask_path("local-caffeine")) }
let(:command) { SystemCommand }
let(:adopt) { false }
let(:force) { false }
let(:app) { cask.artifacts.find { |a| a.is_a?(described_class) } }
let(:source_path) { cask.staged_path.join("Caffeine.app") }
let(:target_path) { cask.config.appdir.join("Caffeine.app") }
let(:install_phase) { app.install_phase(command: command, force: force) }
let(:install_phase) { app.install_phase(command: command, adopt: adopt, force: force) }
let(:uninstall_phase) { app.uninstall_phase(command: command, force: force) }
before do
@ -79,6 +80,57 @@ describe Cask::Artifact::App, :cask do
expect(contents_path).not_to exist
end
describe "given the adopt option" do
let(:adopt) { true }
describe "when the target compares different from the source" do
it "avoids clobbering the existing app" do
stdout = <<~EOS
==> Adopting existing App at '#{target_path}'
EOS
expect { install_phase }
.to output(stdout).to_stdout
.and raise_error(
Cask::CaskError,
"It seems the existing App is different from the one being installed.",
)
expect(source_path).to be_a_directory
expect(target_path).to be_a_directory
expect(File.identical?(source_path, target_path)).to be false
contents_path = target_path.join("Contents/Info.plist")
expect(contents_path).not_to exist
end
end
describe "when the target compares the same as the source" do
before do
target_path.delete
FileUtils.cp_r source_path, target_path
end
it "adopts the existing app" do
stdout = <<~EOS
==> Adopting existing App at '#{target_path}'
EOS
stderr = ""
expect { install_phase }
.to output(stdout).to_stdout
.and output(stderr).to_stderr
expect(source_path).to be_a_symlink
expect(target_path).to be_a_directory
contents_path = target_path.join("Contents/Info.plist")
expect(contents_path).to exist
end
end
end
describe "given the force option" do
let(:force) { true }