diff --git a/upgrade.sh b/upgrade.sh index db0f2e2f..de614fae 100755 --- a/upgrade.sh +++ b/upgrade.sh @@ -89,6 +89,11 @@ trap ' echo "WROLPi upgrade has completed" else echo "WROLPi upgrade has FAILED (exit code: $EXIT_STATUS)" + if [ "$RESTART_SERVICES_ON_FAILURE" = true ]; then + echo "Restarting WROLPi services after failed upgrade..." + systemctl start wrolpi-api || : + systemctl start wrolpi-app || : + fi fi ' EXIT @@ -101,6 +106,18 @@ set -o pipefail # Units may not be installed yet if a previous install/repair failed; repair.sh installs them later. systemctl stop wrolpi-api || : systemctl stop wrolpi-app || : +RESTART_SERVICES_ON_FAILURE=true + +# Restore the previously-running commit after a failed checkout or verification. +# Services must only restart on that known-good commit, so if the rollback does +# not land there, leave them stopped. +rollback_or_no_restart() { + git -C /opt/wrolpi checkout -f -B "${BRANCH}" "${PREVIOUS_HEAD}" || : + if [ "$(git -C /opt/wrolpi rev-parse HEAD 2>/dev/null)" != "${PREVIOUS_HEAD}" ]; then + echo "ERROR: rollback to ${PREVIOUS_HEAD} failed; not restarting services." + RESTART_SERVICES_ON_FAILURE=false + fi +} # Fetch the latest commits without modifying the working tree. git -C /opt/wrolpi fetch || exit 4 @@ -125,15 +142,22 @@ if ! git -C /opt/wrolpi verify-commit origin/"${BRANCH}" 2>&1; then fi # Signature verified, safe to apply. +PREVIOUS_HEAD=$(git -C /opt/wrolpi rev-parse HEAD) # Force the checkout so local working-tree changes (e.g. app/package-lock.json # rewritten by npm install during a prior upgrade) cannot abort the switch. # Use `checkout -B origin/` so the ref can never be mistaken for a path. A tracked `release/` # directory otherwise collides with the `release` branch name, making a bare `git checkout release` ambiguous. -(cd /opt/wrolpi && git checkout -f -B "${BRANCH}" "origin/${BRANCH}") || exit 4 +# checkout -B moves the ref before checking out the tree, so a failure here may +# leave a mixed tree; roll back before the EXIT trap restarts anything. +(cd /opt/wrolpi && git checkout -f -B "${BRANCH}" "origin/${BRANCH}") || { + rollback_or_no_restart + exit 4 +} # Verify HEAD again after checkout in case the branch was swapped between fetch and checkout. if ! git -C /opt/wrolpi verify-commit HEAD 2>&1; then echo "ERROR: HEAD commit signature verification failed after checkout. Aborting upgrade." + rollback_or_no_restart rm -rf "$GNUPGHOME" exit 6 fi @@ -141,6 +165,10 @@ fi rm -rf "$GNUPGHOME" unset GNUPGHOME +# From here scripts/upgrade.sh mutates the system; a blind restart could run +# half-upgraded code, so recovery belongs to repair.sh. +RESTART_SERVICES_ON_FAILURE=false + /opt/wrolpi/scripts/upgrade.sh 2>&1 | tee /opt/wrolpi/upgrade.log set +x