Unify Omarchy migrations

This commit is contained in:
Ryan Hughes
2026-06-12 13:49:46 -04:00
parent 04b0fab64d
commit c582e7133a
27 changed files with 259 additions and 681 deletions
+58 -208
View File
@@ -4,59 +4,30 @@ Omarchy migrations are one-time repair scripts for existing installs. They are
used when a package update needs to change state that pacman cannot safely own by
itself.
## The two migration scopes
## Migration model
Omarchy migrations are split by execution context:
Migrations live in:
```text
migrations/system/*.sh
migrations/user/*.sh
migrations/*.sh
```
### System migrations
They run as the current Omarchy user through `omarchy-migrate`, normally during
`omarchy update`. A migration may touch user/session state (`~/.config`,
`~/.local`, user systemd, browser/editor prefs, DBus/session state), and may also
perform machine-wide repairs when needed.
System migrations run as root and must be noninteractive.
Use system migrations for machine-wide state, for example:
- `/etc` drop-ins or legacy config cleanup
- `/boot` / bootloader cleanup
- systemd system units or service state
- hardware quirks
- package-owned layout transitions
Note that it's not necessary to write a migration for any file that will be package-owned.
State lives in:
Completion state is per-user:
```text
/var/lib/omarchy/migrations/system/<migration filename>
~/.local/state/omarchy/migrations/<migration filename>
```
### User migrations
User migrations run as the current graphical/login user and may be interactive.
Use user migrations for per-user or session-aware state, for example:
- `~/.config` changes
- `~/.local` state
- user systemd units
- browser/editor preferences
- DBus/session-dependent updates
- prompts that need to happen in a visible terminal
State lives in:
```text
~/.local/state/omarchy/migrations/user/<migration filename>
```
A user migration is pending for a given user only when the script exists under
`/usr/share/omarchy/migrations/user/` and that user's matching state file is
missing.
There is no global root-owned “user migrations required” queue.
That means every user gets a chance to run every migration. Migrations run as the
user; privileged operations should invoke the appropriate helper or privilege
prompt themselves. Migrations must be idempotent: if one user already applied a
machine-wide repair, the same migration running for another user should detect
that and no-op.
## When migrations run
@@ -69,15 +40,8 @@ omarchy-migrate
omarchy-hook post-update
```
`omarchy-migrate`:
1. waits for any active pacman transaction to finish
2. runs pending system migrations
3. runs pending user migrations for the current user
System migrations usually already ran during the pacman transaction via the
`omarchy` package `post_upgrade()`, but the second check is intentional and
idempotent.
`omarchy-migrate` waits for any active pacman transaction to finish, then runs
all pending migrations for the current user in the visible update terminal.
### During direct pacman updates
@@ -87,28 +51,21 @@ Raw `sudo pacman -Syu` is guarded. Users should normally run:
omarchy update
```
If a user explicitly bypasses the guard, the `omarchy` package still runs system
migrations from `post_upgrade()`:
If a user explicitly bypasses the guard, user sessions watch the packaged
migration directory and run a notifier. The notifier checks:
```bash
omarchy-migrate-system
omarchy-migrate --pending
```
User migrations are never run by pacman. Instead, the user session watches the
packaged user migration directory and runs a notifier. The notifier checks:
```bash
omarchy-migrate --pending user
```
If that user has pending user migrations, it shows a notification that opens a
If that user has pending migrations, it shows a notification that opens a
terminal for:
```bash
omarchy-migrate
```
The notifier never runs user migrations silently in the background.
The notifier never runs migrations silently in the background.
### Manually
@@ -126,179 +83,72 @@ Use:
```bash
omarchy-migrate --pending
omarchy-migrate --pending system
omarchy-migrate --pending user
```
Exit behavior:
- `0` — one or more matching migrations are pending
- non-zero — no matching migrations are pending
- `0` — one or more migrations are pending
- non-zero — no migrations are pending
Output is scope-prefixed:
Output is one pending migration per line:
```text
system/100-system.sh
user/200-user.sh
1781158082.sh
```
The lower-level runners also support `--pending` and print filenames without the
scope prefix:
## Creating a migration
Use the helper:
```bash
omarchy-migrate-system --pending
omarchy-migrate-user --pending
omarchy-dev-add-migration --no-edit
```
Prefer `omarchy-migrate --pending ...` in user-facing tools and watchers.
This creates:
## Writing migrations
Create a migration with:
```bash
omarchy-dev-add-migration system --no-edit
omarchy-dev-add-migration user --no-edit
```text
migrations/<unix timestamp>.sh
```
Choose the scope based on the state being changed, not on convenience.
New migration format:
### File format
- File permissions must be `0644` (`-rw-r--r--`). Migration runners execute them
with `bash -euo pipefail`, not through executable bits.
- No shebang line.
- Start with an `echo` describing what the migration does.
- Use `$OMARCHY_PATH` to reference the Omarchy directory.
- Be idempotent. Check existing state before changing it.
- Use helper commands such as `omarchy-cmd-present`, `omarchy-cmd-missing`,
`omarchy-pkg-add`, `omarchy-pkg-drop`, `omarchy-pkg-present`, and
`omarchy-pkg-missing` when appropriate.
Migrations are plain shell files:
- filename: unix timestamp, generated by `omarchy-dev-add-migration`
- mode: `0644`
- no shebang
- start with an `echo` describing the migration
- use `$OMARCHY_PATH` for Omarchy-owned files
- should be idempotent
- should be as simple as possible to accomplish the task
Example system migration:
Example:
```bash
echo "Remove legacy Limine config"
echo "Relink Neovim theme to Omarchy current state"
rm -f /boot/EFI/limine/limine.conf
theme_link="$HOME/.config/nvim/lua/plugins/theme.lua"
current_relative_target="../../../../.local/state/omarchy/current/theme/neovim.lua"
[[ -L $theme_link ]] || exit 0
ln -sfn "$current_relative_target" "$theme_link"
```
Example user migration:
## Testing migrations
Run a migration against a temporary home when possible:
```bash
echo "Refresh user app launchers"
omarchy-refresh-applications
HOME=$(mktemp -d) bash -euo pipefail migrations/<timestamp>.sh
```
### Execution model
Migration runners execute scripts with strict shell settings:
```bash
bash -euo pipefail <migration>
```
`$OMARCHY_PATH` is exported into the migration process.
A migration is marked complete only after the script exits successfully. If it
fails, the marker is not written and the migration will be retried later.
Because failed migrations can be retried after doing partial work, migrations
must be idempotent. Prefer operations that are safe to run more than once:
```bash
mkdir -p ~/.config/example
rm -f ~/.config/example/legacy.conf
install -Dm644 "$OMARCHY_PATH/default/example.conf" ~/.config/example/example.conf
systemctl --user enable --now some-unit.service || true
```
Use `|| true` only when a failure is genuinely acceptable.
## What belongs in a migration?
Good migration uses:
- deleting or moving legacy files that block the packaged layout
- marking or enabling new service state
- migrating old config paths to new config paths
- repairing known bad state from an earlier Omarchy release
- one-time compatibility for existing installs
Bad migration uses:
- fresh-install setup that belongs in `install/` or `/etc/skel`
- package installation that belongs in package dependencies or package lists
- recurring maintenance
- arbitrary cleanup that should be a user command
- pre-4 upgrade work that belongs in `omarchy-upgrade-to-4`
- hidden user/session work from pacman
## Fresh installs and new users
Fresh users created from the packaged layout should not have to run historical
user migrations. The package seeds user migration markers into `/etc/skel` so
new users start with shipped user migrations marked complete.
The ISO/finalization path also marks shipped user migrations complete for the
freshly-created install user when running:
```bash
omarchy-finalize-user --first-install
```
Existing users keep their own migration state and run only migrations whose
state files are missing.
## Troubleshooting
### See what is pending
```bash
omarchy-migrate --pending
```
### Retry failed migrations
Fix the underlying problem, then run:
To rerun a migration locally, remove its marker and run the migrator:
```bash
rm ~/.local/state/omarchy/migrations/<migration>.sh
omarchy-migrate
```
A failed migration is not marked complete, so it will retry.
### Re-run a completed migration manually
Remove its marker, then run the migration command again:
```bash
rm ~/.local/state/omarchy/migrations/user/<migration>.sh
omarchy-migrate
```
For system migrations:
```bash
sudo rm /var/lib/omarchy/migrations/system/<migration>.sh
omarchy-migrate
```
Be careful: migrations should be idempotent, but manually removing markers is an
advanced troubleshooting step.
## Agent checklist
Before adding a migration:
1. Is this a one-time repair for existing installs?
2. Is it system state or user/session state?
3. Can it run safely more than once?
4. Will it fail loudly if the important work fails?
5. Does it avoid hidden interactive work from pacman?
6. Does it belong in `omarchy-upgrade-to-4` instead?
7. Did you add or update tests if behavior changed?
If the answer to any of these is unclear, stop and ask before adding the
migration.
Omarchy 4.0 is upgraded through `bin/omarchy-upgrade-to-4`, not through the
normal migration runner. Do not add compatibility migrations for old installer
layouts; put pre-4 package-layout transition work in the upgrade command instead.