Three review findings on the update-hook boundary: The pre-refresh-pacman hook had been moved after the refresh transaction and, during a channel switch, deferred to the very end. That defeated the hook's purpose: custom repositories and IgnorePkg entries were not in place when the downgrade-capable -Syyuu ran. Run the hook where it used to run, after the package config is re-synced and before the transaction, but cold: revoke the timestamp, run it behind the no-update wrapper with the caller's original PATH, and revoke again before continuing. Every later privileged command authenticates with --no-update, so a detached child left by the hook has no reusable timestamp to wait for. Channel switching hands the caller's PATH to the refresh the same way the updater receives it, and no longer defers or re-runs the hook. Stay Awake was released before AUR builds, hooks and mise, so the machine could sleep during the longest part of an update. Releasing the inhibitor needs no privilege because the held command already dropped to the user, so stop it after mise and before the reboot prompt, as before. A packaged channel destination cannot be inspected before its package is installed, and a transaction can replace the running tree with a release that predates the command-scoped wrapper; from then on a bare sudo would resolve to /usr/bin/sudo and publish a timestamp, and the destination's own updater authenticates the same way. The switch used to abort only after the packages had changed, with generic rerun advice. Now it checks for the wrapper after each transaction before any further privileged step, completes what it safely can, and stops cold with instructions to run that release's update from a fresh session instead of launching it. Boundary tests pin the hook between the config copies and the transaction with a cold timestamp on both sides, the older-destination stop with its guidance and no launched updater, the new inhibitor position, and the post-update hook staying unreached on failures and signals. Docs, the manual and the sample hook describe the restored timing. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
121 lines
5.0 KiB
Bash
Executable File
121 lines
5.0 KiB
Bash
Executable File
#!/bin/bash -p
|
|
|
|
# omarchy:summary=Update Omarchy and system packages
|
|
# omarchy:args=[-y]
|
|
# omarchy:examples=omarchy update | omarchy update -y
|
|
# omarchy:requires-sudo=true
|
|
|
|
if [[ $- != *p* ]]; then
|
|
echo "Refusing an unsafe Bash startup." >&2
|
|
exit 126
|
|
fi
|
|
|
|
security_entrypoint=$(/usr/bin/readlink -e -- "${BASH_SOURCE[0]}") || exit 126
|
|
source "${security_entrypoint%/*}/omarchy-security-functions" || exit 126
|
|
omarchy_security_require_privileged_bash_startup || exit 126
|
|
set -e
|
|
omarchy_security_sanitize_bash_environment "$0" "$@"
|
|
omarchy_security_require_source_root "$0"
|
|
# Logging and lock acquisition re-exec this command with a sanitized PATH.
|
|
# Preserve the caller's path only for the later unprivileged hook/mise phases.
|
|
user_path=${OMARCHY_UPDATE_USER_PATH:-$PATH}
|
|
unset OMARCHY_UPDATE_USER_PATH
|
|
omarchy_security_revoke_sudo_timestamp || exit 1
|
|
omarchy_security_install_sudo_cleanup_traps
|
|
omarchy_security_enable_no_update_sudo
|
|
|
|
update_stay_awake_stopped=0
|
|
cleanup_update() {
|
|
local status=$?
|
|
trap - EXIT HUP INT TERM
|
|
if ! omarchy_security_revoke_sudo_timestamp; then
|
|
echo "Could not invalidate sudo before update cleanup." >&2
|
|
omarchy_security_exit_with_revoked_sudo 1
|
|
fi
|
|
if (( update_stay_awake_stopped == 0 )); then
|
|
omarchy-update-stay-awake stop || status=1
|
|
fi
|
|
omarchy_security_exit_with_revoked_sudo "$status"
|
|
}
|
|
|
|
if [[ -z ${OMARCHY_UPDATE_LOGGED:-} ]]; then
|
|
script_command=$(printf '%q ' "$0" "$@")
|
|
exec env OMARCHY_UPDATE_LOGGED=1 OMARCHY_UPDATE_USER_PATH="$user_path" script -qefc "$script_command" "/tmp/omarchy-update.log"
|
|
fi
|
|
|
|
if ! omarchy-update-lock held; then
|
|
exec env OMARCHY_UPDATE_USER_PATH="$user_path" omarchy-update-lock run "$0" "$@"
|
|
fi
|
|
|
|
trap 'echo ""; echo -e "\033[0;31mSomething went wrong during the update!\n\nPlease review the output above carefully, correct the error, and retry the update.\n\nIf you need assistance, get help from the community at https://omarchy.org/discord\033[0m"' ERR
|
|
trap cleanup_update EXIT
|
|
omarchy_security_install_signal_exit_traps
|
|
|
|
omarchy-update-requires-free-space
|
|
|
|
# -y suppresses Omarchy confirmation prompts; sudo authorization is still
|
|
# required. Interactive review steps report and move on instead of waiting.
|
|
[[ ${1:-} != "-y" ]] || export OMARCHY_UPDATE_UNATTENDED=1
|
|
|
|
if [[ ${1:-} == "-y" ]] || omarchy-update-confirm; then
|
|
# Before the snapshot: the cache is on the snapshotted subvolume, so pruning
|
|
# after it frees nothing until that snapshot ages out.
|
|
omarchy-update-pkg-prune
|
|
|
|
# 127 means Snapper is deliberately absent. Any other failure already said
|
|
# what went wrong, and a missing snapshot is not worth blocking an update
|
|
# over, but it must not pass for one either.
|
|
omarchy-snapshot create || (($? == 127)) ||
|
|
echo -e "\e[33mContinuing the update without a snapshot.\e[0m" >&2
|
|
|
|
omarchy-update-stay-awake start
|
|
|
|
# Preserve the established development-checkout update ordering.
|
|
omarchy-update-dev
|
|
omarchy-update-keyring
|
|
|
|
# Migrations ship with the packages installed here and are written against
|
|
# them, so everything below waits on this finishing. An upgrade that stopped
|
|
# takes the update with it rather than migrating against what is still on disk.
|
|
omarchy-update-system-pkgs
|
|
|
|
# Historical migrations are strictly ordered and mix user hooks/downloaded
|
|
# tooling with privileged repairs. The no-update sudo wrapper has covered the
|
|
# whole update, so neither the package transaction nor a later repair can
|
|
# publish a timestamp to a detached migration child.
|
|
omarchy_security_revoke_sudo_timestamp
|
|
omarchy-migrate
|
|
omarchy-update-orphan-pkgs
|
|
|
|
omarchy-update-analyze-logs
|
|
omarchy-update-status
|
|
|
|
# Service restart helpers can need sudo. Run them before any user-controlled
|
|
# update tooling; the reboot-only phase below performs no privileged work.
|
|
omarchy-update-restart --services-only
|
|
|
|
# AUR package installation must also use the no-update wrapper. Finish
|
|
# update-owned system work before build code, hooks, or mise can run.
|
|
omarchy_security_revoke_sudo_timestamp
|
|
omarchy-update-aur-pkgs
|
|
omarchy_security_revoke_sudo_timestamp
|
|
|
|
# Hooks and mise execute user-controlled code. Give each a cold credential
|
|
# boundary and run mise last so it cannot wait for a legitimate hook sudo.
|
|
# Only the unprivileged reboot prompt follows them.
|
|
PATH="$OMARCHY_PATH/default/omarchy/sudo-no-update:$user_path" "$OMARCHY_PATH/bin/omarchy-hook" post-update
|
|
omarchy_security_revoke_sudo_timestamp
|
|
PATH="$OMARCHY_PATH/default/omarchy/sudo-no-update:$user_path" "$OMARCHY_PATH/bin/omarchy-update-mise"
|
|
omarchy_security_revoke_sudo_timestamp
|
|
|
|
# The sleep inhibitor covers AUR builds, hooks and mise as well; releasing it
|
|
# needs no privilege because the held command already dropped to this user.
|
|
# Release it before offering a reboot: a confirmed reboot can terminate this
|
|
# process before its EXIT trap gets a chance to remove the persistent Stay
|
|
# Awake marker.
|
|
omarchy-update-stay-awake stop
|
|
update_stay_awake_stopped=1
|
|
|
|
"$OMARCHY_PATH/bin/omarchy-update-restart" --reboot-only
|
|
fi
|