Skip to content
Permalink
Browse files

migrate: Upgrade base first, automatically restart

In case a macOS upgrade breaks uninstallation of ports or using the
database, we should upgrade MacPorts base before saving the list of
ports and starting port migration.

Move rebuilding MacPorts to the front of 'port migrate', guard it with
a check indicating whether a rebuild is necessary and add user
interaction questions for the base rebuild. Deal with the case of users
with a pre-release version of MacPorts installed and avoid trashing
their installations.

After base was upgraded, automatically re-invoke 'port migrate' with the
'--continue' flag that will pick up migration where it left off. If
'port migrate' is invoked without '--continue' (e.g. when a user
manually recompiled their base installation), ask for confirmation
before uninstalling all ports.

Change selfupdate to also update the ports tree just in case users have
not used MacPorts in a while and the new ports tree contains build fixes
for the new macOS release.
  • Loading branch information
neverpanic committed Jan 7, 2018
1 parent 6a58ee7 commit ff2840ff3aed2bea329ece8f1624afa89abd3f90
Showing with 108 additions and 40 deletions.
  1. +6 −8 src/macports1.0/macports.tcl
  2. +94 −32 src/macports1.0/migrate.tcl
  3. +8 −0 src/port/port.tcl
@@ -4559,15 +4559,13 @@ proc macports::restore_main {opts} {
return [restore::main $opts]
}

##
# Calls the main function for the 'port migrate' command.
#
# @returns 0 on success, -999 when MacPorts base has been upgraded and the
# caller should re-run itself and invoke migration with the --continue
# flag set.
proc macports::migrate_main {opts} {

# Calls the main function for the 'port migrate' command.
#
# Args:
# None
# Returns:
# 0 on successful execution.

return [migrate::main $opts]
}

@@ -37,16 +37,57 @@ package require restore 1.0
package require selfupdate 1.0

namespace eval migrate {
##
# The main function. Calls each individual step in order.
#
# @returns 0 on success, -999 when MacPorts base has been upgraded and the
# caller should re-run itself and invoke migration with the --continue
# flag set.
proc main {opts} {
# The main function. Calls each individual step in order.
#
# Args:
# opts - options array.
# Returns:
# 0 if success

array set options $opts

if {[needs_migration]} {
if {[info exists macports::ui_options(questions_yesno)]} {
set msg "Migration will first upgrade MacPorts and then reinstall all installed ports."
set retvalue [$macports::ui_options(questions_yesno) $msg "MigrationPrompt" "" {y} 0 "Would you like to continue?"]
if {$retvalue == 1} {
# quit as user answered 'no'
ui_msg "Aborting migration. You can re-run 'sudo port migrate' later or follow the migration instructions: https://trac.macports.org/wiki/Migration"
return 0
}
}

ui_msg "Upgrading MacPorts..."
set success no
if {[catch {set success [upgrade_port_command]} result]} {
ui_debug $::errorInfo
ui_error "Upgrading port command failed. Try running 'sudo port -v selfupdate' and then 'sudo port migrate'."
return 1
}
if {!$success} {
ui_error "Upgrading port command failed or was not attempted. Please re-install MacPorts manually and then run 'sudo port migrate' again."
return 1
}

# MacPorts successfully upgraded, automatically re-run migration
# from the new MacPorts installation
return -999
}

# If port migrate was not called with --continue, the user probably did
# that manually and we do not have confirmation to run migration yet;
# do that now.
set continuation [expr {[info exists options(ports_migration_continue)] && $options(ports_migration_continue)}]
if {!$continuation && [info exists macports::ui_options(questions_yesno)]} {
set msg "Migration will reinstall all installed ports."
set retvalue [$macports::ui_options(questions_yesno) $msg "MigrationContinuationPrompt" "" {y} 0 "Would you like to continue?"]
if {$retvalue == 1} {
# quit as user answered 'no'
ui_msg "Aborting migration. You can re-run 'sudo port migrate' later or follow the migration instructions: https://trac.macports.org/wiki/Migration"
return 0
}
}

# create a snapshot
ui_msg "Taking a snapshot of the current state..."
set snapshot [snapshot::main $opts]
@@ -55,50 +96,71 @@ namespace eval migrate {
set datetime [$snapshot created_at]
ui_msg "Done: Snapshot '$id' : '$note' created at $datetime"

if {[info exists macports::ui_options(questions_yesno)]} {
set msg "Migration will first uninstall all the installed ports, upgrade MacPorts and then reinstall them again."
set retvalue [$macports::ui_options(questions_yesno) $msg "MigrationPrompt" "" {y} 0 "Would you like to continue?"]
if {$retvalue == 1} {
# quit as user answered 'no'
ui_msg "Not uninstalling ports."
return 0
}
}

ui_msg "Uninstalling all ports..."
uninstall_installed

ui_msg "Upgrading MacPorts..."
if {[catch {upgrade_port_command} result]} {
ui_debug $::errorInfo
ui_msg "Upgrading port command failed. Try running 'sudo port -v selfupdate' and then, 'sudo port restore --last'"
ui_msg "Restoring ports..."
return [restore_snapshot]
}

##
# Check whether the current platform is the one this installation was
# configured for. Returns true, if migration is needed, false otherwise.
#
# @return true iff the migration procedure is needed
proc needs_migration {} {
if {$macports::os_platform ne $macports::autoconf::os_platform} {
return 1
}
if {$macports::os_major != $macports::autoconf::os_major} {
return 1
}

ui_msg "You need to run 'port restore --last' to complete the migration."
return 0
}

##
# Uninstall all installed ports for migration
#
# @return void on success, raises an error on failure
proc uninstall_installed {} {
set options {}
set portlist [restore::portlist_sort_dependencies_later [registry::entry imaged]]
foreach port $portlist {
ui_msg "Uninstalling: [$port name]"
if {[registry::run_target $port uninstall $options]} {
continue
} else {
if {![registry::run_target $port uninstall $options]} {
ui_error "Error uninstalling [$port name]"
}
}
}

##
# Restore the list of ports from the latest snapshot using the equivalent
# of 'port restore --last'
#
# @return 0 on success, an error on failure
proc restore_snapshot {} {
array set options {}
set options(ports_restore_last) yes

return [restore::main [array get options]]
}

##
# Run MacPorts selfupdate, but avoid downgrading pre-release installations
#
# Will return true on success, false if no error occured but MacPorts was
# not re-installed (e.g. because the currently installed version is newer
# than the downloaded release). If reinstallation fails, an error is
# raised.
#
# @return true on success, false if no update was performed, an error on
# failure.
proc upgrade_port_command {} {
array set optionslist {}
# forced selfupdate
set optionslist(ports_force) 1
# shouldn't sync ports tree
set optionslist(ports_selfupdate_nosync) 1
set updatestatusvar {}
return [uplevel [list selfupdate::main [array get optionslist] $updatestatusvar]]
# Force rebuild, but do not allow downgrade
set optionslist(ports_selfupdate_migrate) 1

uplevel [list selfupdate::main [array get optionslist] base_updated]
return $base_updated
}
}
@@ -2840,6 +2840,13 @@ proc action_restore { action portlist opts } {

proc action_migrate { action portlist opts } {
return [macports::migrate_main $opts]
set result [macports::migrate_main $opts]
if {$result = -999} {
# MacPorts base was upgraded, re-execute migrate with the --continue flag
execl $::argv0 [list {*}$::argv "--continue"]
ui_debug "Would have executed $::argv0 $::argv --continue"
ui_error "Failed to re-execute MacPorts migration, please run 'sudo port migrate' manually."
}
}

proc action_upgrade { action portlist opts } {
@@ -4487,6 +4494,7 @@ array set cmd_opts_array {
fetch {no-mirrors}
snapshot {{note 1}}
restore {{snapshot-id 1} last}
migrate {continue}
}

##

0 comments on commit ff2840f

Please sign in to comment.
You can’t perform that action at this time.