Put a blocked package upgrade back to whoever is updating (#6830)

Pacman answers its own conflict question with No under --noconfirm, so one
retired package can stop every update after it. Which package to drop is a
decision rather than a cleanup, so run the upgrade again with pacman asking
when there is a terminal to answer on, and report instead when -y promised
not to ask.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
David Heinemeier Hansson
2026-08-14 09:08:34 +02:00
committed by GitHub
co-authored by Claude Opus 5
parent 625c66301d
commit 5ca3030c5a
5 changed files with 307 additions and 4 deletions
+8
View File
@@ -21,6 +21,10 @@ trap 'omarchy-update-stay-awake stop' EXIT
omarchy-update-requires-free-space
# -y is a promise not to ask anything. Steps that would need an answer report
# and move on instead of waiting on a prompt nobody is here to give.
[[ ${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.
@@ -36,6 +40,10 @@ if [[ ${1:-} == "-y" ]] || omarchy-update-confirm; then
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
omarchy-migrate
omarchy-hook post-update
+11 -3
View File
@@ -5,6 +5,14 @@
set -e
# The conflict handler's last resort: pacman puts its questions to the person
# running the update instead of answering them itself. Nothing here may capture
# a stream, because an upgrade without --noconfirm prompts on stderr. The
# handler has already said why, so no heading here either.
if [[ ${OMARCHY_UPDATE_CONFLICT:-} == 1 && ${OMARCHY_UPDATE_INTERACTIVE:-} == 1 ]]; then
exec sudo env OMARCHY_UPDATE_PACMAN=1 pacman -Syu --overwrite '/usr/share/omarchy/*'
fi
echo -e "\e[32m\nUpdate system packages\e[0m"
errors=$(mktemp)
@@ -22,9 +30,9 @@ if sudo env LC_ALL=C OMARCHY_UPDATE_PACMAN=1 pacman -Syu --noconfirm \
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.
# The handler takes it from here: it clears files pacman doesn't own yet and
# runs this again, and puts a package conflict to whoever started the update.
# 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.
+25 -1
View File
@@ -1,6 +1,6 @@
#!/bin/bash
# omarchy:summary=Retry a system package update after resolving file conflicts
# omarchy:summary=Retry a system package update that hit a conflict
# omarchy:hidden=true
# Internal to omarchy-update-system-pkgs. Not a command to run by hand: it acts
@@ -11,6 +11,10 @@
# release's installer, an in-place edit -- belong to nobody, and pacman refuses
# to install over a file it doesn't own. This clears them and runs the upgrade
# again.
#
# Packages that conflict with each other are the other blocked upgrade, and that
# one is a decision rather than a cleanup, so it goes back to the person
# watching the update.
set -e
@@ -57,6 +61,26 @@ restore_moved() {
trap 'restore_moved; rm -f "$errors"' EXIT
trap 'exit 1' INT TERM
# Pacman answers its own conflict question with No under --noconfirm, so one
# retired package can stop every upgrade after it. Which package to drop is a
# decision -- an upstream split retires one, but so does a package the user
# picked on purpose -- so put the question to whoever started the update rather
# than guess at it. Pacman printed what conflicts just above.
if grep -q '^error: unresolvable package conflicts detected$' "$errors"; then
# -y promised not to ask anything, and without a terminal there is nowhere to
# ask; either way pacman would sit on a prompt nobody answers. An upgrade that
# isn't running --noconfirm puts its questions on stderr and reads the answers
# from stdin, so those are the two that have to be a terminal.
if [[ ${OMARCHY_UPDATE_UNATTENDED:-} == 1 || ! -t 0 || ! -t 2 ]]; then
echo -e "\e[33m\nThis upgrade needs an answer. Run omarchy update interactively to give it.\e[0m" >&2
exit 1
fi
echo -e "\e[33m\nA package conflict stopped this upgrade. Running it again so you can answer:\e[0m"
OMARCHY_UPDATE_INTERACTIVE=1 omarchy-update-system-pkgs
exit 0
fi
# Paths one of these packages installs that pacman doesn't own. Moving them out
# of the way is what lets the upgrade through, and works on a leftover directory
# as well as a file.