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>
1.7 KiB
Automation Hooks
Read this before setting up scripts that run on system events (theme changes, updates, boot, low battery, etc.).
Hooks live in ~/.config/omarchy/hooks/<name>.d/ — one directory per event,
holding any number of independent scripts. Install with
omarchy hook install <name> <script> (copies the script in and makes it
executable). The runner also executes a flat ~/.config/omarchy/hooks/<name>
file first, if one exists.
~/.config/omarchy/hooks/
├── battery-low.d/ # Low battery (percentage in $1)
├── font-set.d/ # After font change (font name in $1)
├── post-boot.d/ # After the desktop starts
├── post-update.d/ # At the end of `omarchy update`, after privileged work
├── pre-refresh-pacman.d/ # After `omarchy refresh pacman` re-syncs the package config, before it updates packages
└── theme-set.d/ # After theme change (theme slug in $1)
Example hook script:
#!/bin/bash
THEME_NAME=$1
echo "Theme changed to: $THEME_NAME"
# Add custom actions here
Update-related hooks run as your user after Omarchy invalidates its sudo timestamp, behind the no-update wrapper. A hook that invokes sudo must therefore request its own explicit authorization, and Omarchy revokes the timestamp again before continuing. post-update runs after every sudo-capable update stage. pre-refresh-pacman runs after omarchy refresh pacman re-syncs the package config and before the package transaction, so custom repositories and IgnorePkg entries shape that transaction; every later privileged command still authenticates without publishing a reusable timestamp, so a detached child left behind by the hook has nothing to wait for.