Files
omarchy/bin/omarchy-update
T
Afonso OliveiraandClaude Fable 5.1 13a4306a8e Run the refresh hook before its transaction and keep the inhibitor through user work
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>
2026-09-17 11:11:08 +01:00

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