Order the migration notifier after graphical-session.target

omarchy-migrate-notify.service is a Type=oneshot wanted by
graphical-session.target, and systemd complements a target's Wants= with an
implicit After=, so the target waited for the notifier to exit. The notifier
does not exit quickly: it sends the notification through systemd-run --scope,
which is synchronous, and omarchy-notification-send -a blocks until the user
clicks. The target stayed in activating for as long as the toast was up.

wayland-wm-app-daemon.service is After=graphical-session.target and nothing
wants it, so uwsm-app starts it on demand. Clicking the notification runs
omarchy-launch-floating-terminal-with-presentation, which execs uwsm-app,
which blocks on a systemctl --user restart of that daemon -- a job queued
behind the very target the clicked notifier was holding open. The terminal
never opened; uwsm-app gave up on its own pipe timeout instead.

Declaring After= on the wanted unit suppresses the implicit dependency rather
than forming a cycle, so the target is reached without waiting and the
notifier runs behind it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
David Heinemeier Hansson
2026-07-27 08:41:59 -07:00
co-authored by Claude Opus 5
parent 2fb33a2a6a
commit c7e327b05a
6 changed files with 18 additions and 6 deletions
+2 -2
View File
@@ -15,8 +15,8 @@ fi
notify_command=$(printf 'if [[ -n $(omarchy-notification-send -u critical -g  "Pending Omarchy Migrations" %q -a) ]]; then omarchy-launch-floating-terminal-with-presentation omarchy-migrate; fi' "$message") notify_command=$(printf 'if [[ -n $(omarchy-notification-send -u critical -g  "Pending Omarchy Migrations" %q -a) ]]; then omarchy-launch-floating-terminal-with-presentation omarchy-migrate; fi' "$message")
# This runs from omarchy-migrate-notify.service at graphical-session.target, # This runs from omarchy-migrate-notify.service after graphical-session.target,
# which the session can reach before the shell has claimed # but the target can be reached before the shell has claimed
# org.freedesktop.Notifications. Without the wait the toast is sent into the # org.freedesktop.Notifications. Without the wait the toast is sent into the
# void and the user never learns about their pending migrations. # void and the user never learns about their pending migrations.
omarchy-notification-wait || true omarchy-notification-wait || true
@@ -7,6 +7,10 @@ Description=Notify about pending Omarchy migrations
# already being applied in the visible update terminal. Checking once per login # already being applied in the visible update terminal. Checking once per login
# is the only trigger that cannot collide with a running update. # is the only trigger that cannot collide with a running update.
ConditionPathIsDirectory=/usr/share/omarchy/migrations ConditionPathIsDirectory=/usr/share/omarchy/migrations
# The actionable notification launches its terminal through uwsm-app, whose
# daemon starts after graphical-session.target. Run after the target too so this
# oneshot cannot hold the target open while waiting for that daemon.
After=graphical-session.target
[Service] [Service]
Type=oneshot Type=oneshot
+2 -1
View File
@@ -198,7 +198,8 @@ appropriate helper or privilege prompt. Migrations must be idempotent;
machine-wide repairs should no-op when another user already applied them. machine-wide repairs should no-op when another user already applied them.
Each graphical user has `omarchy-migrate-notify.service`, started once per login Each graphical user has `omarchy-migrate-notify.service`, started once per login
through `WantedBy=graphical-session.target`. The package also ships through `WantedBy=graphical-session.target` and ordered after that target so
notification actions can safely launch through UWSM. The package also ships
`omarchy-update-user-notify.service` as a symlink onto it, so users enabled `omarchy-update-user-notify.service` as a symlink onto it, so users enabled
under the old unit name keep working before they reach migration `1785095882`. under the old unit name keep working before they reach migration `1785095882`.
It runs `omarchy-migrate-notify` as It runs `omarchy-migrate-notify` as
+2 -1
View File
@@ -45,7 +45,8 @@ all pending migrations for the current user in the visible update terminal.
### At login ### At login
Every graphical login starts `omarchy-migrate-notify.service`, which checks: Every graphical login starts `omarchy-migrate-notify.service` after
`graphical-session.target`. The notifier checks:
```bash ```bash
omarchy-migrate --pending omarchy-migrate --pending
+5 -1
View File
@@ -150,7 +150,8 @@ sudo pacman -Syu
├─ pre-transaction guard aborts and tells the user to run omarchy update ├─ pre-transaction guard aborts and tells the user to run omarchy update
└─ if explicitly bypassed, upgrades omarchy and related packages └─ if explicitly bypassed, upgrades omarchy and related packages
└─ at that user's next login └─ at that user's next login
├─ omarchy-migrate-notify.service starts with graphical-session.target ├─ graphical-session.target starts
├─ omarchy-migrate-notify.service starts after it
├─ omarchy-migrate-notify checks omarchy-migrate --pending ├─ omarchy-migrate-notify checks omarchy-migrate --pending
├─ if this user has missing migration state, show notification ├─ if this user has missing migration state, show notification
└─ click opens terminal: omarchy-migrate └─ click opens terminal: omarchy-migrate
@@ -173,6 +174,9 @@ Fallbacks:
migration that repoints it only runs for users who run an update — the migration that repoints it only runs for users who run an update — the
opposite of who the notifier is for. The alias can be dropped once installs opposite of who the notifier is for. The alias can be dropped once installs
have run migration `1785095882`. have run migration `1785095882`.
- The notifier is ordered after `graphical-session.target`, so an action that
launches through `uwsm-app` cannot block the target that gates UWSM's app
daemon.
- The notifier waits for a live notification server before sending, because - The notifier waits for a live notification server before sending, because
`graphical-session.target` can be reached before the shell claims `graphical-session.target` can be reached before the shell claims
`org.freedesktop.Notifications`. `org.freedesktop.Notifications`.
+3 -1
View File
@@ -37,7 +37,9 @@ pass "no unit watches the migration directory, so package updates cannot trigger
notify_service="$ROOT/default/systemd/user/omarchy-migrate-notify.service" notify_service="$ROOT/default/systemd/user/omarchy-migrate-notify.service"
grep -Fx 'ExecStart=/usr/bin/omarchy-migrate-notify' "$notify_service" >/dev/null grep -Fx 'ExecStart=/usr/bin/omarchy-migrate-notify' "$notify_service" >/dev/null
grep -Fx 'WantedBy=graphical-session.target' "$notify_service" >/dev/null grep -Fx 'WantedBy=graphical-session.target' "$notify_service" >/dev/null
pass "migration notifier only checks once per login" grep -Fx 'After=graphical-session.target' "$notify_service" >/dev/null ||
fail "migration notifier can deadlock UWSM by blocking graphical-session.target"
pass "migration notifier checks once per login after the graphical session is ready"
grep -F 'omarchy-migrate-notify.service' "$first_run_units" >/dev/null || grep -F 'omarchy-migrate-notify.service' "$first_run_units" >/dev/null ||
fail "first-run does not enable the login migration notifier" fail "first-run does not enable the login migration notifier"