Wait on the profile being repaired, not on every browser (#6837)

The migration asked a user to close every running browser before repairing
the Copy URL shortcut, but a browser only ever rewrites its own Preferences
on exit. Waiting on all browsers deadlocks `omarchy update` for anyone whose
main browser is effectively never closed: the pending ghosts commonly sit in
a stale profile nobody has open, yet the migration blocks on the always-open
browser until the prompt is declined, failing the whole update.

A running Chromium-family browser holds a SingletonLock (and socket) inside
its user-data-dir, so whether the profile being repaired is open is
mechanical. Gate on that instead of on the sheer presence of a browser
process — the repair proceeds where the affected profile is closed, and
browsers attached to other profiles no longer hold the update hostage.

The gate stays conservative while an affected profile actually is open, and
the existing post-repair verification still catches a browser that starts
mid-repair and restores stale Preferences on exit.

The migration test now simulates an open profile with its SingletonLock
instead of a pgrep stub; every prior scenario still passes.
This commit is contained in:
Shrijit Srivastav
2026-08-14 08:59:14 +02:00
committed by GitHub
parent dc698e5df0
commit d35f4b6d59
2 changed files with 128 additions and 69 deletions
+42 -16
View File
@@ -122,10 +122,36 @@ unverified_repairs_exist() {
return 1
}
# Only this user's browsers matter — another user's cannot rewrite these
# Preferences.
browsers_running() {
pgrep -x -u "$UID" 'chromium|chrome|brave|msedge|vivaldi-bin|vivaldi|opera|helium' >/dev/null 2>&1
# A browser only rewrites its own Preferences on exit, so a repair can only be
# reverted by a browser attached to a profile this migration has to touch.
# Whether a profile is open is mechanical: a running Chromium-family browser
# holds a SingletonLock (and socket) inside its user-data-dir.
profile_open() {
# -L catches a SingletonLock left as a dangling symlink; -e covers a plain
# file, and -S the socket — any of them means a browser is attached.
[[ -L $1/SingletonLock || -e $1/SingletonLock || -S $1/SingletonSocket ]]
}
# Gate on a pending — or to-be-verified — profile actually being open, not on
# browsers in general. Waiting on every browser process deadlocks the update on
# machines whose main browser is effectively never closed: the pending ghosts
# commonly sit in a stale profile nobody has open, yet the update blocks on the
# always-open one.
affected_profile_open() {
local preferences backup profile_root
for preferences in "${pending[@]}"; do
profile_open "$(dirname "$(dirname "$preferences")")" && return 0
done
for profile_root in "${profile_roots[@]}"; do
for backup in "$profile_root"/*/Preferences.omarchy-copy-url-repair.bak; do
[[ -f $backup ]] || continue
profile_open "$(dirname "$(dirname "$backup")")" && return 0
done
done
return 1
}
find_pending
@@ -134,16 +160,16 @@ if (( ! ${#pending[@]} )) && ! unverified_repairs_exist; then
fi
# A running browser holds Preferences in memory and rewrites the file on exit,
# reverting the repair, so ask for the windows to be closed first. gum draws
# the prompt on stderr, so it must stay attached: suppressing it leaves gum
# waiting for a keypress behind an unpainted screen. Without a terminal to ask
# in, gum fails; then — as on decline — fail so the migration stays pending,
# and the login notifier keeps prompting until a rerun goes through with
# browsers closed.
while browsers_running; do
if ! gum confirm "Close all browser windows to repair the Copy URL shortcut, then continue"; then
# reverting the repair, so ask for the affected windows to be closed first. gum
# draws the prompt on stderr, so it must stay attached: suppressing it leaves
# gum waiting for a keypress behind an unpainted screen. Without a terminal to
# ask in, gum fails; then — as on decline — fail so the migration stays
# pending, and the login notifier keeps prompting until a rerun goes through
# with the affected profiles closed.
while affected_profile_open; do
if ! gum confirm "Close the browser windows to repair the Copy URL shortcut, then continue"; then
echo "A running browser would undo the Copy URL shortcut repair." >&2
echo "Close all browser windows, then run: omarchy-migrate" >&2
echo "Close the browser windows, then run: omarchy-migrate" >&2
exit 1
fi
done
@@ -159,9 +185,9 @@ done
# A browser that started mid-repair read the stale Preferences and will write
# them back on exit; stay pending so the next browser-free run can verify the
# repair stuck.
if browsers_running; then
if affected_profile_open; then
echo "A browser started during the Copy URL shortcut repair and may undo it on exit." >&2
echo "Close all browser windows, then run: omarchy-migrate" >&2
echo "Close the browser windows, then run: omarchy-migrate" >&2
exit 1
fi
@@ -172,7 +198,7 @@ fi
for preferences in "${pending[@]}"; do
if python3 -c "$repair_py" "$preferences" "$pinned_id" check; then
echo "A browser undid the Copy URL shortcut repair on exit." >&2
echo "Close all browser windows, then run: omarchy-migrate" >&2
echo "Close the browser windows, then run: omarchy-migrate" >&2
exit 1
fi
done