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>
This commit is contained in:
1 parent
99ddf35138
commit
13a4306a8e
12 files changed
+160
-87
No files matched your search
+24
-7
@@ -61,6 +61,25 @@ link_dev_checkout() {
|
||||
omarchy-dev-link "$checkout" --no-reboot
|
||||
}
|
||||
|
||||
# A packaged destination cannot be inspected before its package is installed,
|
||||
# and a package transaction can replace the running tree with a release that
|
||||
# predates the command-scoped wrapper. After that, a bare sudo would resolve to
|
||||
# /usr/bin/sudo and publish a timestamp, and the destination's own updater
|
||||
# authenticates the same way. Neither may run from a flow that has just run
|
||||
# user hooks: stop at a consistent point and say how to finish from a fresh
|
||||
# session.
|
||||
stop_for_older_destination() {
|
||||
cat >&2 <<EOF
|
||||
The destination predates command-scoped sudo, so this switch stops before its update.
|
||||
Packages are switched. Run 'omarchy update' from a new terminal to finish, then reboot if prompted.
|
||||
EOF
|
||||
exit 3
|
||||
}
|
||||
|
||||
wrapper_present() {
|
||||
[[ -f $OMARCHY_PATH/default/omarchy/sudo-no-update/sudo && -x $OMARCHY_PATH/default/omarchy/sudo-no-update/sudo ]]
|
||||
}
|
||||
|
||||
(( $# > 0 )) || { usage; exit 1; }
|
||||
|
||||
dev_checkout=""
|
||||
@@ -109,23 +128,21 @@ if [[ -n $dev_checkout ]]; then
|
||||
omarchy-state set reboot-required
|
||||
fi
|
||||
|
||||
omarchy-refresh-pacman "$pacman_channel" defer-hook
|
||||
OMARCHY_UPDATE_USER_PATH="$user_path" omarchy-refresh-pacman "$pacman_channel"
|
||||
# Each transaction can have replaced this tree; check before the next sudo.
|
||||
wrapper_present || stop_for_older_destination
|
||||
# --ask 4 accepts omarchy <-> omarchy-dev replacement prompts without file overwrites.
|
||||
omarchy-update-pacman -S --needed --noconfirm --ask 4 "${packages[@]}"
|
||||
wrapper_present || stop_for_older_destination
|
||||
|
||||
if [[ -z $dev_checkout ]]; then
|
||||
omarchy-dev-unlink --no-reboot
|
||||
export OMARCHY_PATH=/usr/share/omarchy
|
||||
omarchy_security_enable_no_update_sudo
|
||||
|
||||
if (( leaving_dev )); then
|
||||
omarchy-state set reboot-required
|
||||
fi
|
||||
omarchy_security_enable_no_update_sudo 2>/dev/null || stop_for_older_destination
|
||||
fi
|
||||
|
||||
OMARCHY_UPDATE_USER_PATH="$user_path" "$OMARCHY_PATH/bin/omarchy-update" -y
|
||||
|
||||
# No channel-owned privileged work follows the historical refresh hook.
|
||||
omarchy_security_revoke_sudo_timestamp
|
||||
PATH="$OMARCHY_PATH/default/omarchy/sudo-no-update:$user_path" \
|
||||
"$OMARCHY_PATH/bin/omarchy-refresh-pacman" "$pacman_channel" run-deferred
|
||||
+21
-22
@@ -14,36 +14,35 @@ omarchy_security_require_privileged_bash_startup || exit 126
|
||||
set -e
|
||||
omarchy_security_sanitize_bash_environment "$0" "$@"
|
||||
omarchy_security_require_source_root "$0"
|
||||
user_path=$PATH
|
||||
# Channel switching calls this behind its own sanitized PATH and hands the
|
||||
# caller's original path over the same way the updater receives it.
|
||||
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
|
||||
|
||||
channel="${1:-stable}"
|
||||
hook_mode="${2:-normal}"
|
||||
if [[ $channel != "stable" && $channel != "rc" && $channel != "edge" ]]; then
|
||||
echo "Invalid channel: $channel" >&2
|
||||
exit 2
|
||||
fi
|
||||
if [[ $hook_mode != "normal" && $hook_mode != "defer-hook" && $hook_mode != "run-deferred" ]]; then
|
||||
echo "Invalid refresh hook mode: $hook_mode" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
if [[ $hook_mode != "run-deferred" ]]; then
|
||||
sudo cp -f /etc/pacman.conf /etc/pacman.conf.bak
|
||||
sudo cp -f /etc/pacman.d/mirrorlist /etc/pacman.d/mirrorlist.bak
|
||||
echo "Setting channel to $channel"
|
||||
sudo cp -f "$OMARCHY_PATH/default/pacman/pacman-$channel.conf" /etc/pacman.conf
|
||||
sudo cp -f "$OMARCHY_PATH/default/pacman/mirrorlist-$channel" /etc/pacman.d/mirrorlist
|
||||
# Reset all package DBs and then update
|
||||
omarchy-update-pacman -Syyuu --noconfirm
|
||||
fi
|
||||
sudo cp -f /etc/pacman.conf /etc/pacman.conf.bak
|
||||
sudo cp -f /etc/pacman.d/mirrorlist /etc/pacman.d/mirrorlist.bak
|
||||
echo "Setting channel to $channel"
|
||||
sudo cp -f "$OMARCHY_PATH/default/pacman/pacman-$channel.conf" /etc/pacman.conf
|
||||
sudo cp -f "$OMARCHY_PATH/default/pacman/mirrorlist-$channel" /etc/pacman.d/mirrorlist
|
||||
|
||||
# Keep the historical hook name, but finish every privileged refresh operation
|
||||
# before running user code. Callers with later root work can defer the hook.
|
||||
if [[ $hook_mode != "defer-hook" ]]; then
|
||||
omarchy_security_revoke_sudo_timestamp
|
||||
PATH="$OMARCHY_PATH/default/omarchy/sudo-no-update:$user_path" \
|
||||
"$OMARCHY_PATH/bin/omarchy-hook" pre-refresh-pacman
|
||||
fi
|
||||
# Allow user customization of /etc/pacman.conf before the upgrade runs, so
|
||||
# custom repositories and IgnorePkg entries shape the downgrade-capable
|
||||
# transaction below. The hook is user code: it runs cold behind the no-update
|
||||
# wrapper, and the timestamp is revoked again afterwards so any authorization
|
||||
# the hook obtained for itself cannot carry into the transaction.
|
||||
omarchy_security_revoke_sudo_timestamp
|
||||
PATH="$OMARCHY_PATH/default/omarchy/sudo-no-update:$user_path" \
|
||||
"$OMARCHY_PATH/bin/omarchy-hook" pre-refresh-pacman
|
||||
omarchy_security_revoke_sudo_timestamp
|
||||
|
||||
# Reset all package DBs and then update
|
||||
omarchy-update-pacman -Syyuu --noconfirm
|
||||
+9
-7
@@ -94,15 +94,9 @@ if [[ ${1:-} == "-y" ]] || omarchy-update-confirm; then
|
||||
# update tooling; the reboot-only phase below performs no privileged work.
|
||||
omarchy-update-restart --services-only
|
||||
|
||||
# Release update-owned inhibitors 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_security_revoke_sudo_timestamp
|
||||
omarchy-update-stay-awake stop
|
||||
update_stay_awake_stopped=1
|
||||
|
||||
# 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
|
||||
|
||||
@@ -114,5 +108,13 @@ if [[ ${1:-} == "-y" ]] || omarchy-update-confirm; then
|
||||
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
|
||||
Reference in new issue
Block a user