From c582e7133a7a6e118c02a152b6b1479b6badd337 Mon Sep 17 00:00:00 2001 From: Ryan Hughes Date: Fri, 12 Jun 2026 13:49:39 -0400 Subject: [PATCH] Unify Omarchy migrations --- AGENTS.md | 7 +- bin/omarchy-dev-add-migration | 46 +-- bin/omarchy-finalize-user | 8 +- bin/omarchy-first-run | 2 +- bin/omarchy-migrate | 82 +++--- bin/omarchy-migrate-notify | 10 +- bin/omarchy-migrate-system | 74 ----- bin/omarchy-migrate-user | 65 ----- .../user/omarchy-update-user-notify.path | 6 +- .../user/omarchy-update-user-notify.service | 4 +- docs/file-layout.md | 49 ++-- docs/migrations.md | 266 ++++-------------- docs/update-process.md | 144 +++------- migrations/{user => }/1778623107.sh | 0 migrations/{user => }/1780057136.sh | 0 migrations/{user => }/1780294774.sh | 0 migrations/{user => }/1780517689.sh | 0 migrations/{user => }/1780739888.sh | 0 migrations/{user => }/1781043107.sh | 0 migrations/{user => }/1781158082.sh | 0 migrations/system/.gitkeep | 0 migrations/user/.gitkeep | 0 test/cli | 4 +- test/shell.d/config-test.sh | 7 +- test/shell.d/migrate-notify-test.sh | 12 +- test/shell.d/migrate-scope-test.sh | 104 +++---- test/shell.d/migrate-wrapper-test.sh | 50 +--- 27 files changed, 259 insertions(+), 681 deletions(-) delete mode 100755 bin/omarchy-migrate-system delete mode 100755 bin/omarchy-migrate-user rename migrations/{user => }/1778623107.sh (100%) rename migrations/{user => }/1780057136.sh (100%) rename migrations/{user => }/1780294774.sh (100%) rename migrations/{user => }/1780517689.sh (100%) rename migrations/{user => }/1780739888.sh (100%) rename migrations/{user => }/1781043107.sh (100%) rename migrations/{user => }/1781158082.sh (100%) delete mode 100644 migrations/system/.gitkeep delete mode 100644 migrations/user/.gitkeep diff --git a/AGENTS.md b/AGENTS.md index 3f86870d..d90046f1 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -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/.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/.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/.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 diff --git a/bin/omarchy-dev-add-migration b/bin/omarchy-dev-add-migration index 079e7f08..bed8af3f 100755 --- a/bin/omarchy-dev-add-migration +++ b/bin/omarchy-dev-add-migration @@ -1,32 +1,40 @@ #!/bin/bash # omarchy:summary=Create a new Omarchy migration in the current source tree. -# omarchy:args= [--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 [--no-edit]" - exit 0 - ;; - *) - echo "Unknown migration scope: $scope" >&2 - echo "Usage: omarchy-dev-add-migration [--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 diff --git a/bin/omarchy-finalize-user b/bin/omarchy-finalize-user index 1775c3f2..2dc32c3d 100755 --- a/bin/omarchy-finalize-user +++ b/bin/omarchy-finalize-user @@ -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 diff --git a/bin/omarchy-first-run b/bin/omarchy-first-run index 6f719d53..b1d3fd1e 100755 --- a/bin/omarchy-first-run +++ b/bin/omarchy-first-run @@ -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 diff --git a/bin/omarchy-migrate b/bin/omarchy-migrate index e087624f..d3d4b725 100755 --- a/bin/omarchy-migrate +++ b/bin/omarchy-migrate @@ -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) diff --git a/bin/omarchy-migrate-notify b/bin/omarchy-migrate-notify index ab2a365f..3fb99788 100755 --- a/bin/omarchy-migrate-notify +++ b/bin/omarchy-migrate-notify @@ -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" diff --git a/bin/omarchy-migrate-system b/bin/omarchy-migrate-system deleted file mode 100755 index 8cf109af..00000000 --- a/bin/omarchy-migrate-system +++ /dev/null @@ -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 diff --git a/bin/omarchy-migrate-user b/bin/omarchy-migrate-user deleted file mode 100755 index cd9a2149..00000000 --- a/bin/omarchy-migrate-user +++ /dev/null @@ -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 diff --git a/default/systemd/user/omarchy-update-user-notify.path b/default/systemd/user/omarchy-update-user-notify.path index 2eb100eb..2325c828 100644 --- a/default/systemd/user/omarchy-update-user-notify.path +++ b/default/systemd/user/omarchy-update-user-notify.path @@ -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] diff --git a/default/systemd/user/omarchy-update-user-notify.service b/default/systemd/user/omarchy-update-user-notify.service index ae4c98b8..7a389bc5 100644 --- a/default/systemd/user/omarchy-update-user-notify.service +++ b/default/systemd/user/omarchy-update-user-notify.service @@ -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 diff --git a/docs/file-layout.md b/docs/file-layout.md index 4559a3bf..6bdc674b 100644 --- a/docs/file-layout.md +++ b/docs/file-layout.md @@ -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/.sh` | -| User/session fix for existing installs | `migrations/user/.sh` | +| One-time fix for existing installs | `migrations/.sh` | | User-facing `omarchy-*` command | `bin/omarchy--` — see `GROUP_DESCRIPTIONS` in `bin/omarchy` | | New stock theme | `themes//` (+ matching templates under `default/themed/` if they need theme colors) | | User-installed theme | `~/.config/omarchy/themes//` | diff --git a/docs/migrations.md b/docs/migrations.md index 7e0e58fb..4c572431 100644 --- a/docs/migrations.md +++ b/docs/migrations.md @@ -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/ +~/.local/state/omarchy/migrations/ ``` -### 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/ -``` - -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/.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/.sh ``` -### Execution model - -Migration runners execute scripts with strict shell settings: - -```bash -bash -euo pipefail -``` - -`$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/.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/.sh -omarchy-migrate -``` - -For system migrations: - -```bash -sudo rm /var/lib/omarchy/migrations/system/.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. diff --git a/docs/update-process.md b/docs/update-process.md index 6bc31d64..27bd5c1f 100644 --- a/docs/update-process.md +++ b/docs/update-process.md @@ -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/ -``` - -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/ +~/.local/state/omarchy/migrations/ ``` -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 diff --git a/migrations/user/1778623107.sh b/migrations/1778623107.sh similarity index 100% rename from migrations/user/1778623107.sh rename to migrations/1778623107.sh diff --git a/migrations/user/1780057136.sh b/migrations/1780057136.sh similarity index 100% rename from migrations/user/1780057136.sh rename to migrations/1780057136.sh diff --git a/migrations/user/1780294774.sh b/migrations/1780294774.sh similarity index 100% rename from migrations/user/1780294774.sh rename to migrations/1780294774.sh diff --git a/migrations/user/1780517689.sh b/migrations/1780517689.sh similarity index 100% rename from migrations/user/1780517689.sh rename to migrations/1780517689.sh diff --git a/migrations/user/1780739888.sh b/migrations/1780739888.sh similarity index 100% rename from migrations/user/1780739888.sh rename to migrations/1780739888.sh diff --git a/migrations/user/1781043107.sh b/migrations/1781043107.sh similarity index 100% rename from migrations/user/1781043107.sh rename to migrations/1781043107.sh diff --git a/migrations/user/1781158082.sh b/migrations/1781158082.sh similarity index 100% rename from migrations/user/1781158082.sh rename to migrations/1781158082.sh diff --git a/migrations/system/.gitkeep b/migrations/system/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/migrations/user/.gitkeep b/migrations/user/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/test/cli b/test/cli index 73c67be0..2269c9cf 100755 --- a/test/cli +++ b/test/cli @@ -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" diff --git a/test/shell.d/config-test.sh b/test/shell.d/config-test.sh index 72c47fb0..7c516646 100755 --- a/test/shell.d/config-test.sh +++ b/test/shell.d/config-test.sh @@ -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' diff --git a/test/shell.d/migrate-notify-test.sh b/test/shell.d/migrate-notify-test.sh index 87b21ce8..58850225 100644 --- a/test/shell.d/migrate-notify-test.sh +++ b/test/shell.d/migrate-notify-test.sh @@ -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" diff --git a/test/shell.d/migrate-scope-test.sh b/test/shell.d/migrate-scope-test.sh index 533f638f..f877847b 100644 --- a/test/shell.d/migrate-scope-test.sh +++ b/test/shell.d/migrate-scope-test.sh @@ -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" diff --git a/test/shell.d/migrate-wrapper-test.sh b/test/shell.d/migrate-wrapper-test.sh index 1c670105..c067de15 100644 --- a/test/shell.d/migrate-wrapper-test.sh +++ b/test/shell.d/migrate-wrapper-test.sh @@ -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"