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
+2 -5
View File
@@ -177,12 +177,9 @@ This copies `/etc/skel/.config/hypr/hyprlock.conf` to `~/.config/hypr/hyprlock.c
Read `docs/migrations.md` before creating or changing migrations.
Migrations are split by execution scope:
Migrations are per-user and run through `omarchy-migrate` during `omarchy update` or from the migration notification. Put migrations directly under `migrations/<timestamp>.sh`. Pending state is per-user under `~/.local/state/omarchy/migrations/`, so every user gets a chance to run every migration. Migrations run as the user; privileged work should invoke the appropriate helper or privilege prompt, and no-op when another user already applied it.
- `migrations/system/<timestamp>.sh` — root, noninteractive, safe to run from pacman via `omarchy-migrate-system` (`omarchy` `post_upgrade` calls it). Use for `/etc`, `/usr`, `/boot`, services, hardware quirks, and other system state. Do not prompt.
- `migrations/user/<timestamp>.sh` — current user/session, may touch `~/.config`, `~/.local`, user systemd, browser prefs, DBus/session state, and may prompt if necessary. Runs through `omarchy-migrate` / `omarchy-migrate-user`; pending state is per-user based on missing files under `~/.local/state/omarchy/migrations/user/`.
To create a new migration, run `omarchy-dev-add-migration system --no-edit` or `omarchy-dev-add-migration user --no-edit` based on scope.
To create a new migration, run `omarchy-dev-add-migration --no-edit`.
New migration format:
- File permissions must be `0644` (`-rw-r--r--`); migration runners execute them with `bash -euo pipefail`, not through executable bits
+27 -19
View File
@@ -1,32 +1,40 @@
#!/bin/bash
# omarchy:summary=Create a new Omarchy migration in the current source tree.
# omarchy:args=<system|user> [--no-edit]
# omarchy:args=[--no-edit]
set -euo pipefail
scope="${1:-}"
case "$scope" in
system|user)
shift
;;
-h|--help|"")
echo "Usage: omarchy-dev-add-migration <system|user> [--no-edit]"
exit 0
;;
*)
echo "Unknown migration scope: $scope" >&2
echo "Usage: omarchy-dev-add-migration <system|user> [--no-edit]" >&2
exit 1
;;
esac
no_edit=0
while (($#)); do
case "$1" in
--no-edit)
no_edit=1
shift
;;
system|user)
echo "omarchy-dev-add-migration: migration scopes are no longer used; creating a regular migration." >&2
shift
;;
-h|--help)
echo "Usage: omarchy-dev-add-migration [--no-edit]"
exit 0
;;
*)
echo "Unknown option: $1" >&2
echo "Usage: omarchy-dev-add-migration [--no-edit]" >&2
exit 1
;;
esac
done
cd "${OMARCHY_PATH:-$(pwd)}"
mkdir -p "migrations/$scope"
migration_file="migrations/$scope/$(git log -1 --format=%cd --date=unix).sh"
mkdir -p migrations
migration_file="migrations/$(git log -1 --format=%cd --date=unix).sh"
touch "$migration_file"
if [[ ${1:-} != "--no-edit" ]]; then
if (( ! no_edit )); then
nvim "$migration_file"
fi
+4 -4
View File
@@ -18,7 +18,7 @@ For shipped configs see /etc/skel (new users) and omarchy-reinstall-configs
(existing users explicitly resyncing).
--first-install is used by the ISO in the target chroot. It marks shipped
user migrations complete for the freshly-created user.
migrations complete for the freshly-created user.
Idempotency marker: ~/.local/state/omarchy/finalize-user.done
USAGE
@@ -123,9 +123,9 @@ xdg-settings set default-web-browser chromium.desktop
xdg-mime default HEY.desktop x-scheme-handler/mailto
if (( first_install )); then
mkdir -p "$state_dir/migrations/user"
for migration in "$OMARCHY_PATH"/migrations/user/*.sh; do
[[ -f $migration ]] && touch "$state_dir/migrations/user/$(basename "$migration")"
mkdir -p "$state_dir/migrations"
for migration in "$OMARCHY_PATH"/migrations/*.sh; do
[[ -f $migration ]] && touch "$state_dir/migrations/$(basename "$migration")"
done
fi
+1 -1
View File
@@ -101,7 +101,7 @@ if [[ ! -f $MIGRATION_NOTIFY_WATCH_MARKER || $force -eq 1 ]]; then
fi
fi
run_first_run_step "notify about pending user migrations" omarchy-migrate-notify
run_first_run_step "notify about pending migrations" omarchy-migrate-notify
USER_MARKER="$state_dir/first-run-user.done"
if [[ ! -f $USER_MARKER || $force -eq 1 ]]; then
+41 -41
View File
@@ -1,15 +1,14 @@
#!/bin/bash
# omarchy:summary=Run pending Omarchy system and user migrations.
# omarchy:args=[--pending [all|system|user]]
# omarchy:summary=Run pending Omarchy migrations.
# omarchy:args=[--pending]
set -euo pipefail
mode="run"
pending_scope="all"
usage() {
echo "Usage: omarchy-migrate [--pending [all|system|user]]"
echo "Usage: omarchy-migrate [--pending]"
}
while (($#)); do
@@ -17,15 +16,6 @@ while (($#)); do
--pending|--check)
mode="pending"
shift
if [[ ${1:-} == "all" || ${1:-} == "system" || ${1:-} == "user" ]]; then
pending_scope="$1"
shift
fi
;;
--pending=all|--pending=system|--pending=user)
mode="pending"
pending_scope="${1#--pending=}"
shift
;;
-h|--help)
usage
@@ -38,38 +28,37 @@ while (($#)); do
esac
done
pending_output=""
OMARCHY_PATH="${OMARCHY_PATH:-/usr/share/omarchy}"
STATE_DIR="${OMARCHY_MIGRATION_STATE:-$HOME/.local/state/omarchy/migrations}"
MIGRATIONS_DIR="$OMARCHY_PATH/migrations"
append_pending() {
local scope="$1"
local command="$2"
local output=""
local migration=""
migration_entries() {
[[ -d $MIGRATIONS_DIR ]] || return 0
if output=$("$command" --pending 2>/dev/null); then
while IFS= read -r migration; do
[[ -n $migration ]] || continue
pending_output+="$scope/$migration"$'\n'
done <<<"$output"
fi
local file filename
for file in "$MIGRATIONS_DIR"/*.sh; do
[[ -f $file ]] || continue
filename=$(basename "$file")
printf '%s\t%s\t%s\n' "$filename" "$file" "$STATE_DIR/$filename"
done
}
pending_migrations() {
local name file marker
while IFS=$'\t' read -r name file marker; do
[[ -n $name ]] || continue
if [[ ! -f $marker ]]; then
printf '%s\n' "$name"
fi
done < <(migration_entries)
}
if [[ $mode == "pending" ]]; then
case "$pending_scope" in
all)
append_pending system omarchy-migrate-system
append_pending user omarchy-migrate-user
;;
system)
append_pending system omarchy-migrate-system
;;
user)
append_pending user omarchy-migrate-user
;;
esac
pending_output=$(pending_migrations)
if [[ -n $pending_output ]]; then
printf '%s' "$pending_output"
printf '%s\n' "$pending_output"
exit 0
else
exit 1
@@ -93,5 +82,16 @@ wait_for_pacman_transaction() {
wait_for_pacman_transaction
omarchy-migrate-system
omarchy-migrate-user
mkdir -p "$STATE_DIR"
[[ -d $MIGRATIONS_DIR ]] || exit 0
while IFS=$'\t' read -r name file marker; do
[[ -n $name ]] || continue
if [[ ! -f $marker ]]; then
echo -e "\e[32m\nRunning migration (${name%.sh})\e[0m"
OMARCHY_PATH="$OMARCHY_PATH" bash -euo pipefail "$file"
mkdir -p "$(dirname "$marker")"
touch "$marker"
fi
done < <(migration_entries)
+5 -5
View File
@@ -1,16 +1,16 @@
#!/bin/bash
# omarchy:summary=Notify the user when Omarchy has pending user migrations
# omarchy:summary=Notify the user when Omarchy has pending migrations
set -euo pipefail
pending_migrations=$(omarchy-migrate --pending user 2>/dev/null) || exit 0
pending_migrations=$(omarchy-migrate --pending 2>/dev/null) || exit 0
pending_count=$(printf '%s\n' "$pending_migrations" | sed '/^[[:space:]]*$/d' | wc -l)
if (( pending_count == 1 )); then
message="1 new Omarchy user migration is available. Click to run it in a terminal."
message="1 new Omarchy migration is available. Click to run it in a terminal."
else
message="$pending_count new Omarchy user migrations are available. Click to run them in a terminal."
message="$pending_count new Omarchy migrations are available. Click to run them in a terminal."
fi
notify_command=$(printf 'if [[ -n $(omarchy-notification-send -u critical -g  "Run Omarchy Migrations" %q -a) ]]; then omarchy-launch-floating-terminal-with-presentation omarchy-migrate; fi' "$message")
@@ -21,7 +21,7 @@ if command -v systemd-run >/dev/null 2>&1; then
fi
print_pending_migrations() {
echo "Omarchy has pending user migrations. Run omarchy-migrate in a terminal to apply them:"
echo "Omarchy has pending migrations. Run omarchy-migrate in a terminal to apply them:"
while IFS= read -r migration; do
[[ -n $migration ]] || continue
printf ' %s\n' "$migration"
-74
View File
@@ -1,74 +0,0 @@
#!/bin/bash
# omarchy:summary=Run pending Omarchy system migrations as root.
# omarchy:args=[--pending]
# omarchy:requires-sudo=true
set -euo pipefail
mode="run"
while (($#)); do
case "$1" in
--pending|--check)
mode="pending"
shift
;;
-h|--help)
echo "Usage: omarchy-migrate-system [--pending]"
exit 0
;;
*)
echo "Unknown option: $1" >&2
exit 1
;;
esac
done
OMARCHY_PATH="${OMARCHY_PATH:-/usr/share/omarchy}"
STATE_DIR="${OMARCHY_SYSTEM_MIGRATION_STATE:-/var/lib/omarchy/migrations/system}"
MIGRATIONS_DIR="$OMARCHY_PATH/migrations/system"
pending_migrations() {
[[ -d $MIGRATIONS_DIR ]] || return 0
for file in "$MIGRATIONS_DIR"/*.sh; do
[[ -f $file ]] || continue
filename=$(basename "$file")
if [[ ! -f $STATE_DIR/$filename ]]; then
printf '%s\n' "$filename"
fi
done
}
if [[ $mode == "pending" ]]; then
pending_output=$(pending_migrations)
if [[ -n $pending_output ]]; then
printf '%s\n' "$pending_output"
exit 0
else
exit 1
fi
fi
if (( EUID != 0 )) && [[ -z ${OMARCHY_SYSTEM_MIGRATION_STATE:-} ]]; then
if ! "$0" --pending >/dev/null; then
exit 0
fi
exec sudo --preserve-env=OMARCHY_PATH "$0" "$@"
fi
mkdir -p "$STATE_DIR"
[[ -d $MIGRATIONS_DIR ]] || exit 0
for file in "$MIGRATIONS_DIR"/*.sh; do
[[ -f $file ]] || continue
filename=$(basename "$file")
if [[ ! -f $STATE_DIR/$filename ]]; then
echo -e "\e[32m\nRunning system migration (${filename%.sh})\e[0m"
OMARCHY_PATH="$OMARCHY_PATH" bash -euo pipefail "$file"
touch "$STATE_DIR/$filename"
fi
done
-65
View File
@@ -1,65 +0,0 @@
#!/bin/bash
# omarchy:summary=Run pending Omarchy migrations for the current user.
# omarchy:args=[--pending]
set -euo pipefail
mode="run"
while (($#)); do
case "$1" in
--pending|--check)
mode="pending"
shift
;;
-h|--help)
echo "Usage: omarchy-migrate-user [--pending]"
exit 0
;;
*)
echo "Unknown option: $1" >&2
exit 1
;;
esac
done
OMARCHY_PATH="${OMARCHY_PATH:-/usr/share/omarchy}"
STATE_DIR="${OMARCHY_USER_MIGRATION_STATE:-$HOME/.local/state/omarchy/migrations/user}"
MIGRATIONS_DIR="$OMARCHY_PATH/migrations/user"
pending_migrations() {
[[ -d $MIGRATIONS_DIR ]] || return 0
for file in "$MIGRATIONS_DIR"/*.sh; do
[[ -f $file ]] || continue
filename=$(basename "$file")
if [[ ! -f $STATE_DIR/$filename ]]; then
printf '%s\n' "$filename"
fi
done
}
if [[ $mode == "pending" ]]; then
pending_output=$(pending_migrations)
if [[ -n $pending_output ]]; then
printf '%s\n' "$pending_output"
exit 0
else
exit 1
fi
fi
mkdir -p "$STATE_DIR"
[[ -d $MIGRATIONS_DIR ]] || exit 0
for file in "$MIGRATIONS_DIR"/*.sh; do
[[ -f $file ]] || continue
filename=$(basename "$file")
if [[ ! -f $STATE_DIR/$filename ]]; then
echo -e "\e[32m\nRunning user migration (${filename%.sh})\e[0m"
OMARCHY_PATH="$OMARCHY_PATH" bash -euo pipefail "$file"
touch "$STATE_DIR/$filename"
fi
done
@@ -1,9 +1,9 @@
[Unit]
Description=Watch for Omarchy user migrations
Description=Watch for Omarchy migrations
[Path]
PathExistsGlob=/usr/share/omarchy/migrations/user/*.sh
PathModified=/usr/share/omarchy/migrations/user
PathExistsGlob=/usr/share/omarchy/migrations/*.sh
PathModified=/usr/share/omarchy/migrations
Unit=omarchy-update-user-notify.service
[Install]
@@ -1,6 +1,6 @@
[Unit]
Description=Notify about pending Omarchy user migrations
ConditionPathExistsGlob=/usr/share/omarchy/migrations/user/*.sh
Description=Notify about pending Omarchy migrations
ConditionPathIsDirectory=/usr/share/omarchy/migrations
[Service]
Type=oneshot
+18 -31
View File
@@ -63,12 +63,11 @@ default/libalpm/hooks/*.hook
──► omarchy /usr/share/libalpm/hooks/*.hook
install/** ──► omarchy /usr/share/omarchy/install/
migrations/system/** ──► omarchy /usr/share/omarchy/migrations/system/
migrations/user/** ──► omarchy /usr/share/omarchy/migrations/user/
migrations/** ──► omarchy /usr/share/omarchy/migrations/
themes/** ──► omarchy /usr/share/omarchy/themes/
shell/** ──► omarchy /usr/share/omarchy/shell/
version ──► omarchy /usr/share/omarchy/version
+ /etc/skel/.local/state/omarchy/migrations/user/*
+ /etc/skel/.local/state/omarchy/migrations/*
config/** ──► omarchy-settings /etc/skel/.config/** (seeds new users)
/usr/share/omarchy/config/** (resync source)
@@ -185,37 +184,26 @@ the root-side work.
See [`migrations.md`](migrations.md) for the full migration model, authoring
guidelines, and troubleshooting notes.
Omarchy migrations are split by scope:
- `migrations/system/*.sh` — root, noninteractive, safe to run from pacman.
The `omarchy` package `post_upgrade()` runs `omarchy-migrate-system` after an
`omarchy` package upgrade, so even explicit direct-pacman bypasses still get
system migrations applied. Completion state lives in
`/var/lib/omarchy/migrations/system/`.
- `migrations/user/*.sh` — current user/session, may be interactive, and may
touch `~/.config`, `~/.local`, user systemd, DBus, browser prefs, etc.
Completion state lives in `~/.local/state/omarchy/migrations/user/`.
Package upgrades happen as root, but user migrations must run as each user and
may be interactive. There is no root-owned "user migration required" marker. A
user migration is pending only when a script exists in `migrations/user/` and
that user's matching state file is missing.
Omarchy migrations live in `migrations/*.sh` and run per-user through
`omarchy-migrate`. Completion state lives in
`~/.local/state/omarchy/migrations/`, so every user gets a chance to run every
migration. Migrations run as the user; privileged work should invoke the
appropriate helper or privilege prompt. Migrations must be idempotent;
machine-wide repairs should no-op when another user already applied them.
Each graphical user has `omarchy-update-user-notify.path` watching the packaged
user migration directory. When that directory changes, or when the path unit is
migration directory. When that directory changes, or when the path unit is
started on login, `omarchy-update-user-notify.service` runs
`omarchy-migrate-notify` as that user. The notifier checks
`omarchy-migrate --pending user`. If this user has missing migration state, it
shows a notification that opens a terminal for `omarchy-migrate`. The notifier
never runs user migrations in the background.
`omarchy-migrate --pending`. If this user has missing migration state, it shows a
notification that opens a terminal for `omarchy-migrate`. The notifier never runs
migrations in the background.
`omarchy-migrate` waits for any active pacman transaction to finish, runs
pending system migrations, then runs pending user migrations. It does not need
`--force`; migrations happen when state files are missing. For watchers and
diagnostics, `omarchy-migrate --pending [all|system|user]` prints
scope-prefixed pending migration filenames and exits `0` when any are pending.
`omarchy update` runs `omarchy-migrate` after the package transaction in the
already-visible update terminal, then runs `omarchy-hook post-update`.
`omarchy-migrate` waits for any active pacman transaction to finish, then runs
pending migrations. It does not need `--force`; migrations happen when state
files are missing. `omarchy update` runs `omarchy-migrate` after the package
transaction in the already-visible update terminal, then runs
`omarchy-hook post-update`.
## First-run (`omarchy-first-run`)
@@ -286,8 +274,7 @@ return to the packaged default.
| Per-user file that's static but lives outside `~/.config` | `default/`, then add `install -Dm644 ... $pkgdir/etc/skel/...` in `omarchy-settings` PKGBUILD |
| Runtime tweak that needs `$HOME` or live system state | extend `omarchy-finalize-user`, or add a per-user leaf under `install/user/` and wire into `install/user/all.sh` |
| One-time root-side setup step | `install/config/*.sh` or `install/hardware/*.sh`, wire into `omarchy-setup-system` or `install/hardware/all.sh` |
| Noninteractive root fix for existing installs | `migrations/system/<unix-timestamp>.sh` |
| User/session fix for existing installs | `migrations/user/<unix-timestamp>.sh` |
| One-time fix for existing installs | `migrations/<unix-timestamp>.sh` |
| User-facing `omarchy-*` command | `bin/omarchy-<group>-<verb>` — see `GROUP_DESCRIPTIONS` in `bin/omarchy` |
| New stock theme | `themes/<name>/` (+ matching templates under `default/themed/` if they need theme colors) |
| User-installed theme | `~/.config/omarchy/themes/<name>/` |
+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.
+40 -104
View File
@@ -10,13 +10,12 @@ bypass it:
The design goal is:
- System/root migrations should run automatically from package transactions when
safe.
- User/session migrations should never run invisibly from pacman because they may
need `$HOME`, DBus/session state, a graphical session, or user interaction.
- Users who bypass `omarchy update` should still get system migrations from
pacman and should be notified only when their own user migration state is
missing a shipped user migration.
- `omarchy update` owns the visible update pipeline: package transaction,
migrations, post-update hooks, update-state refresh, and restart checks.
- Migrations run per-user after pacman finishes, because they may need `$HOME`,
DBus/session state, a graphical session, sudo, or user interaction.
- Users who bypass `omarchy update` are nudged back by the pacman guard; if they
explicitly bypass it, their session is notified when migrations are pending.
## State and coordination files
@@ -26,8 +25,7 @@ The design goal is:
| `/tmp/omarchy-update.log` | user | Transcript of `omarchy update`, used by `omarchy-update-analyze-logs`. |
| `~/.local/state/omarchy/updates/` | user | Update-check state for the shell widget (`packages`, `aur`, `available`, `checked-at`, `error`). |
| `~/.local/state/omarchy/current/` | user | Generated active theme, selected theme name, and current background symlink. |
| `/var/lib/omarchy/migrations/system/` | root | System migration markers. |
| `~/.local/state/omarchy/migrations/user/` | user | User migration markers. |
| `~/.local/state/omarchy/migrations/` | user | Per-user migration markers. |
| `~/.local/state/omarchy/reboot-required` | user | Optional reboot marker checked by `omarchy-update-restart`. |
| `~/.local/state/omarchy/restart-*-required` | user | Optional service/app restart markers checked by `omarchy-update-restart`. |
@@ -36,83 +34,32 @@ The design goal is:
See [`migrations.md`](migrations.md) for the full migration model, authoring
guidelines, and troubleshooting notes.
Migrations are scoped by directory:
Migrations live in:
```text
migrations/system/*.sh
migrations/user/*.sh
migrations/*.sh
```
### System migrations
System migrations are root-owned, noninteractive fixes for existing installs.
They may touch `/etc`, `/usr`, `/boot`, system services, hardware configuration,
and other machine-wide state.
Runner:
```bash
omarchy-migrate-system
```
Completion state:
```text
/var/lib/omarchy/migrations/system/<migration filename>
```
Pacman integration:
```text
pkgbuilds/omarchy/omarchy.install post_upgrade()
```
The `omarchy` package calls `omarchy-migrate-system` from `post_upgrade()`.
The runner is idempotent, so repeated calls only check the state files.
### User migrations
User migrations are current-user/session fixes. They may touch `~/.config`,
`~/.local`, user systemd units, browser/editor preferences, DBus/session state,
or ask questions.
Runner:
```bash
omarchy-migrate-user
```
Public command:
They run as the current user through:
```bash
omarchy-migrate
```
Completion state:
Completion state is per-user:
```text
~/.local/state/omarchy/migrations/user/<migration filename>
~/.local/state/omarchy/migrations/<migration filename>
```
A user migration is pending only when a script exists in `migrations/user/` and
that user's matching state file is missing. There is no global root-owned queue
for user migrations.
Every user gets a chance to run every migration. Migrations run as the user;
privileged work should invoke the appropriate helper or privilege prompt.
Migrations must be idempotent; if one user already applied a machine-wide repair,
the migration should no-op for other users.
`omarchy-migrate` is the public command. It waits for any active pacman
transaction to finish, runs pending system migrations, then runs pending user
migrations. It does not need `--force`; migrations should happen when they are
pending.
For watchers and diagnostics, `omarchy-migrate --pending [all|system|user]`
prints pending migration names and exits `0` when any are pending. Output is
scope-prefixed, for example:
```text
system/100-system.sh
user/200-user.sh
```
When no matching migrations are pending, it prints nothing and exits non-zero.
For watchers and diagnostics, `omarchy-migrate --pending` prints pending
migration names and exits `0` when any are pending. When no migrations are
pending, it prints nothing and exits non-zero.
## Raw pacman guard
@@ -187,10 +134,8 @@ omarchy-update
Important behavior:
- `omarchy update` checks/runs system and user migrations in the same visible
terminal via `omarchy-migrate`.
- `omarchy update` still benefits from pacman-triggered system migrations
because the system migration hook runs as part of the pacman transaction.
- `omarchy update` checks/runs migrations in the same visible terminal via
`omarchy-migrate` after pacman finishes.
- A failure should leave enough output in `/tmp/omarchy-update.log` and the
terminal transcript to debug.
@@ -202,10 +147,9 @@ High-level flow:
sudo pacman -Syu
├─ pre-transaction guard aborts and tells the user to run omarchy update
└─ if explicitly bypassed, upgrades omarchy and related packages
├─ omarchy post_upgrade runs omarchy-migrate-system as root
└─ user session notices migration directory changes
├─ omarchy-update-user-notify.path triggers, if enabled
├─ omarchy-migrate-notify checks omarchy-migrate --pending user
├─ omarchy-migrate-notify checks omarchy-migrate --pending
├─ if this user has missing migration state, show notification
└─ click opens terminal: omarchy-migrate
```
@@ -215,12 +159,11 @@ Fallbacks:
- `omarchy-first-run` enables the user notification path unit.
- `omarchy-first-run` also invokes `omarchy-migrate-notify` on graphical
startup, so users who updated before the path unit existed still get prompted
if they have missing user migration state.
- The notifier is only a prompt. It does not run user migrations in the
background.
if they have missing migration state.
- The notifier is only a prompt. It does not run migrations in the background.
- Direct pacman updates do not run `omarchy-hook post-update` unless the user
explicitly runs that hook; without a package-update marker, the only user-side
pending state we can derive is missing user migration markers.
explicitly runs that hook; without a package-update marker, the only pending
state we can derive is missing per-user migration markers.
## Shell update indicator
@@ -265,11 +208,9 @@ scripts.
| `omarchy-update-confirm` | Gum confirmation copy for `omarchy update`. | **Question.** Could be inlined into `omarchy-update`; separate file only helps keep copy isolated. |
| `omarchy-update-keyring` | Ensures Omarchy keyring and Arch keyring are current before the main transaction. | **Keep, but review.** It uses targeted `pacman -Sy` for keyring bootstrapping; acceptable for this special case but should remain tightly scoped. |
| `omarchy-update-system-pkgs` | Runs `sudo env OMARCHY_UPDATE_PACMAN=1 pacman -Syu --noconfirm` with targeted transition `--overwrite` entries so the ALPM guard allows the transaction and early package-layout conflicts are handled. | **Keep for now.** Small leaf command, clear/testable. |
| `omarchy-migrate-system` | Runs root/system migrations from `migrations/system`. Called by `omarchy` package `post_upgrade()` and by `omarchy-migrate` when system migration state is missing. | **Keep.** This is the important direct-`pacman -Syu` integration. |
| `omarchy-migrate-user` | Checks `migrations/user` against this user's state and runs only missing user migrations. Supports `--pending` and prints pending filenames. | **Keep internal.** Public users should generally run `omarchy-migrate`. |
| `omarchy-migrate` | Public migration command. Waits for pacman, then runs pending system and user migrations. Supports `--pending [all|system|user]` and prints scope-prefixed pending filenames. | **Keep.** This replaces the discarded `omarchy-update-user-finalize` name and no longer needs `--force`. |
| `omarchy-migrate` | Public migration command. Waits for pacman, then runs all pending migrations for the current user. Supports `--pending`. | **Keep.** This replaces the discarded `omarchy-update-user-finalize` name and no longer needs `--force`. |
| `omarchy-update-pacman-guard` | ALPM pre-transaction guard that aborts direct `pacman -Syu` style upgrades unless Omarchy set `OMARCHY_UPDATE_PACMAN=1` or the user explicitly set `OMARCHY_ALLOW_DIRECT_PACMAN=1`. | **Keep internal/hidden.** This is what nudges users back to `omarchy update`. |
| `omarchy-migrate-notify` | Internal notification helper for direct pacman updates. Uses `omarchy-migrate --pending user` and shows notification only when this user has pending migrations. | **Keep internal/hidden.** Clear name now that the public command is `omarchy-migrate`. |
| `omarchy-migrate-notify` | Internal notification helper for direct pacman updates. Uses `omarchy-migrate --pending` and shows notification only when this user has pending migrations. | **Keep internal/hidden.** Clear name now that the public command is `omarchy-migrate`. |
| `omarchy-update-user-notify` | Hidden compatibility wrapper for `omarchy-migrate-notify`. | **Temporary.** Keep only for old callers. |
| `omarchy-update-available` | Update checker for shell widget and post-update refresh. Writes update state. | **Keep.** Could eventually be renamed `omarchy-update-check`, but current name matches widget semantics. |
| `omarchy-update-aur-pkgs` | Updates AUR packages with `yay -Sua` if foreign packages exist and AUR is reachable. | **Question.** Omarchy is package-backed now, but users may still install AUR packages. Keep for now. |
@@ -282,36 +223,31 @@ scripts.
## Closed decisions
1. **System migrations run from the `omarchy` package**
- `omarchy.install post_upgrade()` calls `omarchy-migrate-system`.
- We do not need a separate system-migration ALPM hook from
`omarchy-settings`.
1. **Migrations run per-user from the update pipeline**
- `omarchy update` runs `omarchy-migrate` after pacman finishes.
- Package-time migration runners do not apply migrations inside pacman.
- Every user has per-user migration markers, and migrations must be
idempotent when they repair machine-wide state.
2. **Root vs user migrations stay separate**
- Do not use `su {user}` from pacman to run user migrations.
- User migrations need the user's real session and should be visible.
- Pending user work is determined only by comparing `migrations/user/*.sh`
against `~/.local/state/omarchy/migrations/user/`.
3. **Migration notification naming**
2. **Migration notification naming**
- The real helper is `omarchy-migrate-notify`.
- `omarchy-update-user-notify` remains only as a hidden compatibility wrapper.
4. **Update pipeline ownership**
3. **Update pipeline ownership**
- `omarchy-update` owns the full update pipeline now.
- `omarchy-update-perform` is only a hidden compatibility wrapper for
`omarchy-update -y`.
5. **Mise remains in the blessed update path**
4. **Mise remains in the blessed update path**
- `omarchy-update-mise` intentionally runs as part of `omarchy update`.
6. **Orphan cleanup stays in the update path for now**
5. **Orphan cleanup stays in the update path for now**
- It is prompt-only and never removes packages noninteractively.
7. **Direct pacman user follow-up is based on actual migration state**
6. **Direct pacman user follow-up is based on actual migration state**
- Direct `sudo pacman -Syu` no longer uses a fake user-update marker.
- User notifications are shown only when `omarchy-migrate --pending user`
finds missing per-user migration state.
- User notifications are shown only when `omarchy-migrate --pending` finds
missing per-user migration state.
## Remaining concerns
View File
View File
+2 -2
View File
@@ -393,7 +393,7 @@ package.path = os.getenv("HOME")
.. package.path
LUA
ln -s "$MIGRATION_TMPDIR/.config/omarchy/current/theme/btop.theme" "$MIGRATION_TMPDIR/.config/btop/themes/current.theme"
HOME="$MIGRATION_TMPDIR" bash -euo pipefail "$ROOT/migrations/user/1781043107.sh" >/dev/null
HOME="$MIGRATION_TMPDIR" bash -euo pipefail "$ROOT/migrations/1781043107.sh" >/dev/null
[[ -f $MIGRATION_TMPDIR/.local/state/omarchy/current/theme.name ]] || fail "current theme migration moves theme name"
[[ ! -e $MIGRATION_TMPDIR/.config/omarchy/current ]] || fail "current theme migration removes legacy config state"
grep -Fq '~/.local/state/omarchy/current/theme/alacritty.toml' "$MIGRATION_TMPDIR/.config/alacritty/alacritty.toml" || fail "current theme migration updates alacritty import"
@@ -415,7 +415,7 @@ for nvim_legacy_target in \
"../../../omarchy/current/theme/neovim.lua" \
"$NVIM_MIGRATION_TMPDIR/.config/omarchy/current/theme/neovim.lua"; do
ln -sfn "$nvim_legacy_target" "$nvim_theme_link"
HOME="$NVIM_MIGRATION_TMPDIR" bash -euo pipefail "$ROOT/migrations/user/1781158082.sh" >/dev/null
HOME="$NVIM_MIGRATION_TMPDIR" bash -euo pipefail "$ROOT/migrations/1781158082.sh" >/dev/null
nvim_actual_target=$(readlink "$nvim_theme_link")
nvim_resolved_target=$(readlink -f "$nvim_theme_link")
[[ $nvim_actual_target == $nvim_expected_target ]] || fail "nvim theme migration updates theme symlink"
+4 -3
View File
@@ -276,11 +276,12 @@ jq -e '
' "$TMPDIR/home/.config/omarchy/shell.json" >/dev/null
pass "shell config removes widgets with remove alias"
mapfile -t migrations < <(find "$ROOT/migrations" -maxdepth 1 -type f -name '*.sh' -printf '%f\n' | sort)
[[ ${#migrations[@]} -eq 0 ]] || fail "4.0 upgrade is not modeled as a migration"
if grep -RIl 'upgrade-to-4\|Omarchy 4\.0 is upgraded' "$ROOT/migrations" >/dev/null; then
fail "4.0 upgrade is not modeled as a migration"
fi
pass "4.0 upgrade is handled outside the migration runner"
clock_migration=$(grep -rl 'Remove leading zero from bar clock date' "$ROOT/migrations/user" | head -n 1 || true)
clock_migration=$(grep -rl 'Remove leading zero from bar clock date' "$ROOT/migrations" | head -n 1 || true)
[[ -n $clock_migration ]] || fail "clock date format user migration exists"
cat >"$TMPDIR/home/.config/omarchy/shell.json" <<'JSON'
+6 -6
View File
@@ -11,16 +11,16 @@ stub_bin="$test_tmp/bin"
test_home="$test_tmp/home"
mkdir -p "$stub_bin" "$test_home"
cat >"$stub_bin/omarchy-migrate-user" <<'SH'
cat >"$stub_bin/omarchy-migrate" <<'SH'
#!/bin/bash
if [[ ${1:-} == "--pending" && ${OMARCHY_TEST_PENDING_MIGRATIONS:-0} == 1 ]]; then
echo 200-user.sh
echo 200-migration.sh
exit 0
else
exit 1
fi
SH
chmod +x "$stub_bin/omarchy-migrate-user"
chmod +x "$stub_bin/omarchy-migrate"
cat >"$stub_bin/systemd-run" <<'SH'
#!/bin/bash
@@ -41,6 +41,6 @@ run_notify 0 >"$test_tmp/not-pending.out" 2>"$test_tmp/not-pending.err"
pass "migration notifier ignores users with no pending migrations"
run_notify 1 >"$test_tmp/pending.out" 2>"$test_tmp/pending.err"
grep -q 'Omarchy has pending user migrations' "$test_tmp/pending.err" || fail "migration notifier explains pending migrations without notification system"
grep -q 'user/200-user.sh' "$test_tmp/pending.err" || fail "migration notifier lists pending migration names"
pass "migration notifier reports pending user migrations"
grep -q 'Omarchy has pending migrations' "$test_tmp/pending.err" || fail "migration notifier explains pending migrations without notification system"
grep -q '200-migration.sh' "$test_tmp/pending.err" || fail "migration notifier lists pending migration names"
pass "migration notifier reports pending migrations"
+33 -71
View File
@@ -9,107 +9,69 @@ trap 'rm -rf "$test_tmp"' EXIT
test_root="$test_tmp/omarchy"
test_home="$test_tmp/home"
system_state="$test_tmp/system-state"
mkdir -p "$test_root/migrations/system" "$test_root/migrations/user" "$test_home" "$system_state"
mkdir -p "$test_root/migrations" "$test_home"
cat >"$test_root/migrations/system/100-system.sh" <<'SH'
cat >"$test_root/migrations/100-first.sh" <<'SH'
[[ $OMARCHY_PATH == "$TEST_EXPECTED_OMARCHY_PATH" ]]
echo system >>"$TEST_CALLS"
echo first >>"$TEST_CALLS"
SH
cat >"$test_root/migrations/user/200-user.sh" <<'SH'
cat >"$test_root/migrations/200-second.sh" <<'SH'
[[ $OMARCHY_PATH == "$TEST_EXPECTED_OMARCHY_PATH" ]]
echo user >>"$TEST_CALLS"
echo second >>"$TEST_CALLS"
SH
calls="$test_tmp/calls"
if ! HOME="$test_home" OMARCHY_PATH="$test_root" "$ROOT/bin/omarchy-migrate-user" --pending >"$test_tmp/user-pending.out"; then
fail "user migration runner reports pending migrations before state exists"
if ! HOME="$test_home" OMARCHY_PATH="$test_root" "$ROOT/bin/omarchy-migrate" --pending >"$test_tmp/pending.out"; then
fail "migration runner reports pending migrations before state exists"
fi
grep -q '^200-user\.sh$' "$test_tmp/user-pending.out" || fail "user migration runner lists pending migration filename"
pass "user migration runner detects pending migrations"
grep -q '^100-first\.sh$' "$test_tmp/pending.out" || fail "migration runner lists first pending migration filename"
grep -q '^200-second\.sh$' "$test_tmp/pending.out" || fail "migration runner lists second pending migration filename"
pass "migration runner detects pending migrations"
HOME="$test_home" \
OMARCHY_PATH="$test_root" \
TEST_EXPECTED_OMARCHY_PATH="$test_root" \
TEST_CALLS="$calls" \
"$ROOT/bin/omarchy-migrate-user" >"$test_tmp/user-first.out"
[[ $(grep -c '^user$' "$calls") -eq 1 ]] || fail "user migration runner runs user migrations"
! grep -q '^system$' "$calls" || fail "user migration runner does not run system migrations"
[[ -f $test_home/.local/state/omarchy/migrations/user/200-user.sh ]] || fail "user migration runner records user migration marker"
pass "user migration runner only runs user migrations"
"$ROOT/bin/omarchy-migrate" >"$test_tmp/first-run.out"
[[ $(sed -n '1p' "$calls") == "first" ]] || fail "migration runner runs first migration"
[[ $(sed -n '2p' "$calls") == "second" ]] || fail "migration runner runs second migration"
[[ -f $test_home/.local/state/omarchy/migrations/100-first.sh ]] || fail "migration runner records first migration marker"
[[ -f $test_home/.local/state/omarchy/migrations/200-second.sh ]] || fail "migration runner records second migration marker"
pass "migration runner runs all migrations"
HOME="$test_home" \
OMARCHY_PATH="$test_root" \
TEST_EXPECTED_OMARCHY_PATH="$test_root" \
TEST_CALLS="$calls" \
"$ROOT/bin/omarchy-migrate-user" >"$test_tmp/user-second.out"
[[ $(grep -c '^user$' "$calls") -eq 1 ]] || fail "user migration runner skips completed migrations"
pass "user migration runner skips completed migrations"
"$ROOT/bin/omarchy-migrate" >"$test_tmp/second-run.out"
[[ $(wc -l <"$calls") -eq 2 ]] || fail "migration runner skips completed migrations"
pass "migration runner skips completed migrations"
if HOME="$test_home" OMARCHY_PATH="$test_root" "$ROOT/bin/omarchy-migrate-user" --pending >"$test_tmp/user-not-pending.out"; then
fail "user migration runner reports no pending migrations after state exists"
if HOME="$test_home" OMARCHY_PATH="$test_root" "$ROOT/bin/omarchy-migrate" --pending >"$test_tmp/not-pending.out"; then
fail "migration runner reports no pending migrations after state exists"
fi
pass "user migration runner detects no pending migrations"
if ! OMARCHY_PATH="$test_root" OMARCHY_SYSTEM_MIGRATION_STATE="$system_state" "$ROOT/bin/omarchy-migrate-system" --pending >"$test_tmp/system-pending.out"; then
fail "system migration runner reports pending migrations before state exists"
fi
grep -q '^100-system\.sh$' "$test_tmp/system-pending.out" || fail "system migration runner lists pending migration filename"
OMARCHY_PATH="$test_root" \
OMARCHY_SYSTEM_MIGRATION_STATE="$system_state" \
TEST_EXPECTED_OMARCHY_PATH="$test_root" \
TEST_CALLS="$calls" \
"$ROOT/bin/omarchy-migrate-system" >"$test_tmp/system-first.out"
[[ $(grep -c '^system$' "$calls") -eq 1 ]] || fail "system migration runner runs system migrations"
[[ -f $system_state/100-system.sh ]] || fail "system migration runner records system migration marker"
pass "system migration runner only runs system migrations"
if OMARCHY_PATH="$test_root" OMARCHY_SYSTEM_MIGRATION_STATE="$system_state" "$ROOT/bin/omarchy-migrate-system" --pending >"$test_tmp/system-not-pending.out"; then
fail "system migration runner reports no pending migrations after state exists"
fi
pass "system migration runner detects no pending migrations"
pass "migration runner detects no pending migrations"
failure_root="$test_tmp/failure-omarchy"
failure_home="$test_tmp/failure-home"
failure_system_state="$test_tmp/failure-system-state"
mkdir -p "$failure_root/migrations/system" "$failure_root/migrations/user" "$failure_home" "$failure_system_state"
mkdir -p "$failure_root/migrations" "$failure_home"
cat >"$failure_root/migrations/user/300-fail-user.sh" <<'SH'
echo user-before-fail >>"$TEST_CALLS"
cat >"$failure_root/migrations/500-fail.sh" <<'SH'
echo before-fail >>"$TEST_CALLS"
false
echo user-after-fail >>"$TEST_CALLS"
echo after-fail >>"$TEST_CALLS"
SH
set +e
HOME="$failure_home" \
OMARCHY_PATH="$failure_root" \
TEST_CALLS="$calls" \
"$ROOT/bin/omarchy-migrate-user" >"$test_tmp/user-failure.out" 2>"$test_tmp/user-failure.err"
user_failure_status=$?
"$ROOT/bin/omarchy-migrate" >"$test_tmp/failure.out" 2>"$test_tmp/failure.err"
failure_status=$?
set -e
[[ $user_failure_status -ne 0 ]] || fail "user migration runner exits non-zero when a migration fails"
[[ ! -f $failure_home/.local/state/omarchy/migrations/user/300-fail-user.sh ]] || fail "user migration runner does not mark failed migration complete"
grep -q '^user-before-fail$' "$calls" || fail "user migration runner started failing migration"
! grep -q '^user-after-fail$' "$calls" || fail "user migration runner stops failing migration under strict mode"
pass "user migration runner does not mark failed migrations complete"
cat >"$failure_root/migrations/system/400-fail-system.sh" <<'SH'
echo system-before-fail >>"$TEST_CALLS"
false
echo system-after-fail >>"$TEST_CALLS"
SH
set +e
OMARCHY_PATH="$failure_root" \
OMARCHY_SYSTEM_MIGRATION_STATE="$failure_system_state" \
TEST_CALLS="$calls" \
"$ROOT/bin/omarchy-migrate-system" >"$test_tmp/system-failure.out" 2>"$test_tmp/system-failure.err"
system_failure_status=$?
set -e
[[ $system_failure_status -ne 0 ]] || fail "system migration runner exits non-zero when a migration fails"
[[ ! -f $failure_system_state/400-fail-system.sh ]] || fail "system migration runner does not mark failed migration complete"
grep -q '^system-before-fail$' "$calls" || fail "system migration runner started failing migration"
! grep -q '^system-after-fail$' "$calls" || fail "system migration runner stops failing migration under strict mode"
pass "system migration runner does not mark failed migrations complete"
[[ $failure_status -ne 0 ]] || fail "migration runner exits non-zero when a migration fails"
[[ ! -f $failure_home/.local/state/omarchy/migrations/500-fail.sh ]] || fail "migration runner does not mark failed migration complete"
grep -q '^before-fail$' "$calls" || fail "migration runner started failing migration"
! grep -q '^after-fail$' "$calls" || fail "migration runner stops failing migration under strict mode"
pass "migration runner does not mark failed migrations complete"
+13 -37
View File
@@ -7,57 +7,33 @@ source "$(dirname "$0")/base-test.sh"
test_tmp=$(mktemp -d)
trap 'rm -rf "$test_tmp"' EXIT
stub_bin="$test_tmp/bin"
test_root="$test_tmp/omarchy"
test_home="$test_tmp/home"
mkdir -p "$stub_bin" "$test_home"
mkdir -p "$test_root/migrations" "$test_home"
cat >"$stub_bin/omarchy-migrate-system" <<'SH'
#!/bin/bash
if [[ ${1:-} == "--pending" ]]; then
[[ ${OMARCHY_TEST_PENDING_SYSTEM:-1} == 1 ]] || exit 1
echo 100-system.sh
exit 0
fi
echo system >>"$TEST_CALLS"
cat >"$test_root/migrations/100-migration.sh" <<'SH'
echo migration >>"$TEST_CALLS"
SH
chmod +x "$stub_bin/omarchy-migrate-system"
cat >"$stub_bin/omarchy-migrate-user" <<'SH'
#!/bin/bash
if [[ ${1:-} == "--pending" ]]; then
[[ ${OMARCHY_TEST_PENDING_USER:-1} == 1 ]] || exit 1
echo 200-user.sh
exit 0
fi
echo user >>"$TEST_CALLS"
SH
chmod +x "$stub_bin/omarchy-migrate-user"
run_migrate() {
HOME="$test_home" \
PATH="$stub_bin:$PATH" \
OMARCHY_PATH="$test_root" \
TEST_CALLS="$test_tmp/calls" \
"$ROOT/bin/omarchy-migrate" "$@"
}
: >"$test_tmp/calls"
run_migrate >"$test_tmp/migrate.out"
[[ $(sed -n '1p' "$test_tmp/calls") == "system" ]] || fail "omarchy-migrate runs system migrations first"
[[ $(sed -n '2p' "$test_tmp/calls") == "user" ]] || fail "omarchy-migrate runs user migrations second"
[[ $(wc -l <"$test_tmp/calls") -eq 2 ]] || fail "omarchy-migrate only delegates to system and user migration runners"
pass "omarchy-migrate runs system and user migrations without force"
[[ $(sed -n '1p' "$test_tmp/calls") == "migration" ]] || fail "omarchy-migrate runs pending migrations"
pass "omarchy-migrate runs migrations without force"
run_migrate --pending >"$test_tmp/pending-all.out"
grep -q '^system/100-system\.sh$' "$test_tmp/pending-all.out" || fail "omarchy-migrate --pending lists pending system migrations"
grep -q '^user/200-user\.sh$' "$test_tmp/pending-all.out" || fail "omarchy-migrate --pending lists pending user migrations"
pass "omarchy-migrate --pending lists all pending migrations"
rm -rf "$test_home/.local/state/omarchy/migrations"
run_migrate --pending >"$test_tmp/pending.out"
grep -q '^100-migration\.sh$' "$test_tmp/pending.out" || fail "omarchy-migrate --pending lists pending migrations"
pass "omarchy-migrate --pending lists pending migrations"
run_migrate --pending user >"$test_tmp/pending-user.out"
grep -q '^user/200-user\.sh$' "$test_tmp/pending-user.out" || fail "omarchy-migrate --pending user lists user migrations"
! grep -q '^system/' "$test_tmp/pending-user.out" || fail "omarchy-migrate --pending user omits system migrations"
pass "omarchy-migrate --pending user scopes pending output"
if OMARCHY_TEST_PENDING_SYSTEM=0 OMARCHY_TEST_PENDING_USER=0 run_migrate --pending >"$test_tmp/not-pending.out"; then
run_migrate >"$test_tmp/migrate-second.out"
if run_migrate --pending >"$test_tmp/not-pending.out"; then
fail "omarchy-migrate --pending exits non-zero without pending migrations"
fi
[[ ! -s $test_tmp/not-pending.out ]] || fail "omarchy-migrate --pending stays quiet without pending migrations"