Make reinstall transaction safe.

* Aborting during reinstall will now restore the originally installed
  keg.
  - Change install code to pass on CannotInstallFormulaError exception
    to caller so it can be reused in reinstall.
* Add "--force-new-install" flag to force installing a new formula.

Closes Homebrew/homebrew#22190.

Signed-off-by: Samuel John <github@SamuelJohn.de>
This commit is contained in:
Xiyue Deng 2013-08-28 04:51:24 -07:00 committed by Samuel John
parent e278135156
commit 8aae1dac7b
2 changed files with 67 additions and 14 deletions

View File

@ -19,7 +19,13 @@ module Homebrew extend self
end unless ARGV.force? end unless ARGV.force?
perform_preinstall_checks perform_preinstall_checks
ARGV.formulae.each { |f| install_formula(f) } ARGV.formulae.each do |f|
begin
install_formula(f)
rescue CannotInstallFormulaError => e
ofail e.message
end
end
end end
def check_ppc def check_ppc
@ -80,7 +86,6 @@ module Homebrew extend self
# another formula. In that case, don't generate an error, just move on. # another formula. In that case, don't generate an error, just move on.
rescue FormulaAlreadyInstalledError => e rescue FormulaAlreadyInstalledError => e
opoo e.message opoo e.message
rescue CannotInstallFormulaError => e # Ignore CannotInstallFormulaError and let caller handle it.
ofail e.message
end end
end end

View File

@ -1,4 +1,3 @@
require 'cmd/uninstall'
require 'cmd/install' require 'cmd/install'
module Homebrew extend self module Homebrew extend self
@ -6,9 +5,6 @@ module Homebrew extend self
# At first save the named formulae and remove them from ARGV # At first save the named formulae and remove them from ARGV
named = ARGV.named named = ARGV.named
ARGV.delete_if { |arg| named.include? arg } ARGV.delete_if { |arg| named.include? arg }
# We add --force because then uninstall always succeeds and so reinstall
# works for formulae not yet installed.
ARGV << "--force"
clean_ARGV = ARGV.clone clean_ARGV = ARGV.clone
# Add the used_options for each named formula separately so # Add the used_options for each named formula separately so
@ -21,13 +17,65 @@ module Homebrew extend self
if tab.built_as_bottle and not tab.poured_from_bottle if tab.built_as_bottle and not tab.poured_from_bottle
ARGV << '--build-bottle' ARGV << '--build-bottle'
end end
# Todo: Be as smart as upgrade to restore the old state if reinstall fails.
self.uninstall canonical_name = Formula.canonical_name(name)
# Don't display --force in options; user didn't request it so a bit scary. formula = Formula.factory(canonical_name)
options_only = ARGV.options_only
options_only.delete "--force" if not formula.installed?
oh1 "Reinstalling #{name} #{options_only*' '}" if force_new_install?
self.install oh1 "Force installing new formula: #{name}"
self.install_formula formula
next
else
raise <<-EOS.undent
#{formula} is not installed. Please install it first or use
"--force-new-install" flag.
EOS
end
end
linked_keg_ref = HOMEBREW_REPOSITORY/'opt'/canonical_name
keg = Keg.new(linked_keg_ref.realpath)
begin
oh1 "Reinstalling #{name} #{ARGV.options_only*' '}"
quarantine keg
self.install_formula formula
rescue Exception => e
ofail e.message unless e.message.empty?
restore_quarantine keg, formula
raise 'Reinstallation abort.'
else
remove_quarantine keg
end
end end
end end
def force_new_install?
ARGV.include? '--force-new-install'
end
def quarantine keg
keg.unlink
path = Pathname.new(keg.to_s)
path.rename quarantine_path(path)
end
def restore_quarantine keg, formula
path = Pathname.new(quarantine_path(keg))
if path.directory?
path.rename keg.to_s
keg.link unless formula.keg_only?
end
end
def remove_quarantine keg
path = Pathname.new(quarantine_path(keg))
path.rmtree
end
def quarantine_path path
path.to_s + '.reinstall'
end
end end