pacman refuses to install over a file it doesn't own, so any path an Omarchy package starts shipping that a script had already written by hand aborts the whole upgrade: omarchy-settings-dev: /usr/lib/systemd/user/omarchy-fcitx5.service exists in filesystem Errors occurred, no packages were upgraded. The mitigation was a hand-maintained --overwrite allowlist, and it only worked with foresight: an entry had to ship a release before the package took the path, because pacman checks conflicts during transaction prepare, so the script running the upgrade is the one already on disk. A missed entry left people hard-stuck, since the upgrade that would deliver the entry is the one refusing to run. So react instead. omarchy-update-system-pkgs now does the ordinary thing and hands a failed transaction to omarchy-update-system-pkgs-when- conflicted, which moves the offending files out of the way and runs the upgrade again. Nothing has to be predicted, and the allowlist is gone. Moving rather than overwriting is what makes it small: no --overwrite argument to build, no glob escaping, no separate backup step, and a leftover directory is cleared too, which --overwrite cannot do at all. Files go to /var/lib/omarchy/replaced/<original path>, not next to the original. A sibling copy is not inert -- SDDM reads every file in sddm.conf.d whatever its extension, and systemd-sleep runs every executable in system-sleep -- and the directory a future path lives in is unknowable, which is the whole point of a mechanism for paths nobody predicted. What it will not do: - Take a file another package owns. pacman reports those with a "(owned by x)" suffix, so the end anchor excludes them, and pacman -Qo re-checks the live database before anything moves. - Move a subset. If any reported conflict isn't recoverable the retry is doomed anyway, and moving leaves that config inactive for nothing -- worse than the stuck-but-intact state. - Leave anything inactive that was live. A failed retry, a failed move partway through the loop, or an interrupt all put back whatever the upgrade didn't install. - Act on a report handed to it by hand. It is internal to the update, and an old report would clear live files for an upgrade that isn't happening. Also adds a test that fails when a script writes a path under /usr that no PKGBUILD installs, since not creating these is cheaper than recovering from them. It reads the destination off the command, so a path assembled from variables still slips through; the two known cases are recorded with their reasons. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
32 lines
1.1 KiB
Bash
Executable File
32 lines
1.1 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
# omarchy:summary=Update system packages with pacman
|
|
# omarchy:requires-sudo=true
|
|
|
|
set -e
|
|
|
|
echo -e "\e[32m\nUpdate system packages\e[0m"
|
|
|
|
errors=$(mktemp)
|
|
trap 'rm -f "$errors"' EXIT
|
|
|
|
# /usr/share/omarchy is wholly Omarchy's; files can land there unowned and would
|
|
# otherwise abort the upgrade. Packaged content wins there.
|
|
#
|
|
# Progress bars stay on stdout. Errors are on stderr, kept for the conflict
|
|
# handler below; LC_ALL=C is what keeps them parseable in any locale.
|
|
if sudo env LC_ALL=C OMARCHY_UPDATE_PACMAN=1 pacman -Syu --noconfirm \
|
|
--overwrite '/usr/share/omarchy/*' 2>"$errors"; then
|
|
cat "$errors" >&2
|
|
exit 0
|
|
fi
|
|
cat "$errors" >&2
|
|
|
|
# An upgrade blocked only by files pacman doesn't own yet is the one failure
|
|
# worth retrying: the handler clears them and runs this again. Anything else,
|
|
# including a second failure, is for a human.
|
|
[[ ${OMARCHY_UPDATE_RETRY:-} != 1 ]] || exit 1
|
|
# exec, so the EXIT trap above does not fire and the handler can still read the
|
|
# report. It takes over deleting it.
|
|
exec env OMARCHY_UPDATE_CONFLICT=1 omarchy-update-system-pkgs-when-conflicted "$errors"
|