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. 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. To create a new migration, run `omarchy-dev-add-migration --no-edit`.
- `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.
New migration format: New migration format:
- File permissions must be `0644` (`-rw-r--r--`); migration runners execute them with `bash -euo pipefail`, not through executable bits - File permissions must be `0644` (`-rw-r--r--`); migration runners execute them with `bash -euo pipefail`, not through executable bits
+20 -12
View File
@@ -1,32 +1,40 @@
#!/bin/bash #!/bin/bash
# omarchy:summary=Create a new Omarchy migration in the current source tree. # 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 set -euo pipefail
scope="${1:-}" no_edit=0
case "$scope" in
system|user) while (($#)); do
case "$1" in
--no-edit)
no_edit=1
shift shift
;; ;;
-h|--help|"") system|user)
echo "Usage: omarchy-dev-add-migration <system|user> [--no-edit]" 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 exit 0
;; ;;
*) *)
echo "Unknown migration scope: $scope" >&2 echo "Unknown option: $1" >&2
echo "Usage: omarchy-dev-add-migration <system|user> [--no-edit]" >&2 echo "Usage: omarchy-dev-add-migration [--no-edit]" >&2
exit 1 exit 1
;; ;;
esac esac
done
cd "${OMARCHY_PATH:-$(pwd)}" cd "${OMARCHY_PATH:-$(pwd)}"
mkdir -p "migrations/$scope" mkdir -p migrations
migration_file="migrations/$scope/$(git log -1 --format=%cd --date=unix).sh" migration_file="migrations/$(git log -1 --format=%cd --date=unix).sh"
touch "$migration_file" touch "$migration_file"
if [[ ${1:-} != "--no-edit" ]]; then if (( ! no_edit )); then
nvim "$migration_file" nvim "$migration_file"
fi 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). (existing users explicitly resyncing).
--first-install is used by the ISO in the target chroot. It marks shipped --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 Idempotency marker: ~/.local/state/omarchy/finalize-user.done
USAGE USAGE
@@ -123,9 +123,9 @@ xdg-settings set default-web-browser chromium.desktop
xdg-mime default HEY.desktop x-scheme-handler/mailto xdg-mime default HEY.desktop x-scheme-handler/mailto
if (( first_install )); then if (( first_install )); then
mkdir -p "$state_dir/migrations/user" mkdir -p "$state_dir/migrations"
for migration in "$OMARCHY_PATH"/migrations/user/*.sh; do for migration in "$OMARCHY_PATH"/migrations/*.sh; do
[[ -f $migration ]] && touch "$state_dir/migrations/user/$(basename "$migration")" [[ -f $migration ]] && touch "$state_dir/migrations/$(basename "$migration")"
done done
fi fi
+1 -1
View File
@@ -101,7 +101,7 @@ if [[ ! -f $MIGRATION_NOTIFY_WATCH_MARKER || $force -eq 1 ]]; then
fi fi
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" USER_MARKER="$state_dir/first-run-user.done"
if [[ ! -f $USER_MARKER || $force -eq 1 ]]; then if [[ ! -f $USER_MARKER || $force -eq 1 ]]; then
+40 -40
View File
@@ -1,15 +1,14 @@
#!/bin/bash #!/bin/bash
# omarchy:summary=Run pending Omarchy system and user migrations. # omarchy:summary=Run pending Omarchy migrations.
# omarchy:args=[--pending [all|system|user]] # omarchy:args=[--pending]
set -euo pipefail set -euo pipefail
mode="run" mode="run"
pending_scope="all"
usage() { usage() {
echo "Usage: omarchy-migrate [--pending [all|system|user]]" echo "Usage: omarchy-migrate [--pending]"
} }
while (($#)); do while (($#)); do
@@ -17,15 +16,6 @@ while (($#)); do
--pending|--check) --pending|--check)
mode="pending" mode="pending"
shift 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) -h|--help)
usage usage
@@ -38,38 +28,37 @@ while (($#)); do
esac esac
done 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() { migration_entries() {
local scope="$1" [[ -d $MIGRATIONS_DIR ]] || return 0
local command="$2"
local output=""
local migration=""
if output=$("$command" --pending 2>/dev/null); then local file filename
while IFS= read -r migration; do
[[ -n $migration ]] || continue for file in "$MIGRATIONS_DIR"/*.sh; do
pending_output+="$scope/$migration"$'\n' [[ -f $file ]] || continue
done <<<"$output" 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 fi
done < <(migration_entries)
} }
if [[ $mode == "pending" ]]; then if [[ $mode == "pending" ]]; then
case "$pending_scope" in pending_output=$(pending_migrations)
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
if [[ -n $pending_output ]]; then if [[ -n $pending_output ]]; then
printf '%s' "$pending_output" printf '%s\n' "$pending_output"
exit 0 exit 0
else else
exit 1 exit 1
@@ -93,5 +82,16 @@ wait_for_pacman_transaction() {
wait_for_pacman_transaction wait_for_pacman_transaction
omarchy-migrate-system mkdir -p "$STATE_DIR"
omarchy-migrate-user [[ -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 #!/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 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) pending_count=$(printf '%s\n' "$pending_migrations" | sed '/^[[:space:]]*$/d' | wc -l)
if (( pending_count == 1 )); then 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 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 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") 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 fi
print_pending_migrations() { 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 while IFS= read -r migration; do
[[ -n $migration ]] || continue [[ -n $migration ]] || continue
printf ' %s\n' "$migration" 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] [Unit]
Description=Watch for Omarchy user migrations Description=Watch for Omarchy migrations
[Path] [Path]
PathExistsGlob=/usr/share/omarchy/migrations/user/*.sh PathExistsGlob=/usr/share/omarchy/migrations/*.sh
PathModified=/usr/share/omarchy/migrations/user PathModified=/usr/share/omarchy/migrations
Unit=omarchy-update-user-notify.service Unit=omarchy-update-user-notify.service
[Install] [Install]
@@ -1,6 +1,6 @@
[Unit] [Unit]
Description=Notify about pending Omarchy user migrations Description=Notify about pending Omarchy migrations
ConditionPathExistsGlob=/usr/share/omarchy/migrations/user/*.sh ConditionPathIsDirectory=/usr/share/omarchy/migrations
[Service] [Service]
Type=oneshot Type=oneshot
+18 -31
View File
@@ -63,12 +63,11 @@ default/libalpm/hooks/*.hook
──► omarchy /usr/share/libalpm/hooks/*.hook ──► omarchy /usr/share/libalpm/hooks/*.hook
install/** ──► omarchy /usr/share/omarchy/install/ install/** ──► omarchy /usr/share/omarchy/install/
migrations/system/** ──► omarchy /usr/share/omarchy/migrations/system/ migrations/** ──► omarchy /usr/share/omarchy/migrations/
migrations/user/** ──► omarchy /usr/share/omarchy/migrations/user/
themes/** ──► omarchy /usr/share/omarchy/themes/ themes/** ──► omarchy /usr/share/omarchy/themes/
shell/** ──► omarchy /usr/share/omarchy/shell/ shell/** ──► omarchy /usr/share/omarchy/shell/
version ──► omarchy /usr/share/omarchy/version 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) config/** ──► omarchy-settings /etc/skel/.config/** (seeds new users)
/usr/share/omarchy/config/** (resync source) /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 See [`migrations.md`](migrations.md) for the full migration model, authoring
guidelines, and troubleshooting notes. guidelines, and troubleshooting notes.
Omarchy migrations are split by scope: Omarchy migrations live in `migrations/*.sh` and run per-user through
`omarchy-migrate`. Completion state lives in
- `migrations/system/*.sh` — root, noninteractive, safe to run from pacman. `~/.local/state/omarchy/migrations/`, so every user gets a chance to run every
The `omarchy` package `post_upgrade()` runs `omarchy-migrate-system` after an migration. Migrations run as the user; privileged work should invoke the
`omarchy` package upgrade, so even explicit direct-pacman bypasses still get appropriate helper or privilege prompt. Migrations must be idempotent;
system migrations applied. Completion state lives in machine-wide repairs should no-op when another user already applied them.
`/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.
Each graphical user has `omarchy-update-user-notify.path` watching the packaged 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 started on login, `omarchy-update-user-notify.service` runs
`omarchy-migrate-notify` as that user. The notifier checks `omarchy-migrate-notify` as that user. The notifier checks
`omarchy-migrate --pending user`. If this user has missing migration state, it `omarchy-migrate --pending`. If this user has missing migration state, it shows a
shows a notification that opens a terminal for `omarchy-migrate`. The notifier notification that opens a terminal for `omarchy-migrate`. The notifier never runs
never runs user migrations in the background. migrations in the background.
`omarchy-migrate` waits for any active pacman transaction to finish, runs `omarchy-migrate` waits for any active pacman transaction to finish, then runs
pending system migrations, then runs pending user migrations. It does not need pending migrations. It does not need `--force`; migrations happen when state
`--force`; migrations happen when state files are missing. For watchers and files are missing. `omarchy update` runs `omarchy-migrate` after the package
diagnostics, `omarchy-migrate --pending [all|system|user]` prints transaction in the already-visible update terminal, then runs
scope-prefixed pending migration filenames and exits `0` when any are pending. `omarchy-hook post-update`.
`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`) ## 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 | | 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` | | 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` | | 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` | | One-time fix for existing installs | `migrations/<unix-timestamp>.sh` |
| User/session fix for existing installs | `migrations/user/<unix-timestamp>.sh` |
| User-facing `omarchy-*` command | `bin/omarchy-<group>-<verb>` — see `GROUP_DESCRIPTIONS` in `bin/omarchy` | | 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) | | New stock theme | `themes/<name>/` (+ matching templates under `default/themed/` if they need theme colors) |
| User-installed theme | `~/.config/omarchy/themes/<name>/` | | 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 used when a package update needs to change state that pacman cannot safely own by
itself. itself.
## The two migration scopes ## Migration model
Omarchy migrations are split by execution context: Migrations live in:
```text ```text
migrations/system/*.sh migrations/*.sh
migrations/user/*.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. Completion state is per-user:
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:
```text ```text
/var/lib/omarchy/migrations/system/<migration filename> ~/.local/state/omarchy/migrations/<migration filename>
``` ```
### User migrations 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
User migrations run as the current graphical/login user and may be interactive. prompt themselves. Migrations must be idempotent: if one user already applied a
machine-wide repair, the same migration running for another user should detect
Use user migrations for per-user or session-aware state, for example: that and no-op.
- `~/.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.
## When migrations run ## When migrations run
@@ -69,15 +40,8 @@ omarchy-migrate
omarchy-hook post-update omarchy-hook post-update
``` ```
`omarchy-migrate`: `omarchy-migrate` waits for any active pacman transaction to finish, then runs
all pending migrations for the current user in the visible update terminal.
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.
### During direct pacman updates ### During direct pacman updates
@@ -87,28 +51,21 @@ Raw `sudo pacman -Syu` is guarded. Users should normally run:
omarchy update omarchy update
``` ```
If a user explicitly bypasses the guard, the `omarchy` package still runs system If a user explicitly bypasses the guard, user sessions watch the packaged
migrations from `post_upgrade()`: migration directory and run a notifier. The notifier checks:
```bash ```bash
omarchy-migrate-system omarchy-migrate --pending
``` ```
User migrations are never run by pacman. Instead, the user session watches the If that user has pending migrations, it shows a notification that opens a
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
terminal for: terminal for:
```bash ```bash
omarchy-migrate omarchy-migrate
``` ```
The notifier never runs user migrations silently in the background. The notifier never runs migrations silently in the background.
### Manually ### Manually
@@ -126,179 +83,72 @@ Use:
```bash ```bash
omarchy-migrate --pending omarchy-migrate --pending
omarchy-migrate --pending system
omarchy-migrate --pending user
``` ```
Exit behavior: Exit behavior:
- `0` — one or more matching migrations are pending - `0` — one or more migrations are pending
- non-zero — no matching migrations are pending - non-zero — no migrations are pending
Output is scope-prefixed: Output is one pending migration per line:
```text ```text
system/100-system.sh 1781158082.sh
user/200-user.sh
``` ```
The lower-level runners also support `--pending` and print filenames without the ## Creating a migration
scope prefix:
Use the helper:
```bash ```bash
omarchy-migrate-system --pending omarchy-dev-add-migration --no-edit
omarchy-migrate-user --pending
``` ```
Prefer `omarchy-migrate --pending ...` in user-facing tools and watchers. This creates:
## Writing migrations ```text
migrations/<unix timestamp>.sh
Create a migration with:
```bash
omarchy-dev-add-migration system --no-edit
omarchy-dev-add-migration user --no-edit
``` ```
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: Example:
- 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:
```bash ```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 ```bash
echo "Refresh user app launchers" HOME=$(mktemp -d) bash -euo pipefail migrations/<timestamp>.sh
omarchy-refresh-applications
``` ```
### Execution model To rerun a migration locally, remove its marker and run the migrator:
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:
```bash ```bash
rm ~/.local/state/omarchy/migrations/<migration>.sh
omarchy-migrate omarchy-migrate
``` ```
A failed migration is not marked complete, so it will retry. 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
### Re-run a completed migration manually layouts; put pre-4 package-layout transition work in the upgrade command instead.
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.
+40 -104
View File
@@ -10,13 +10,12 @@ bypass it:
The design goal is: The design goal is:
- System/root migrations should run automatically from package transactions when - `omarchy update` owns the visible update pipeline: package transaction,
safe. migrations, post-update hooks, update-state refresh, and restart checks.
- User/session migrations should never run invisibly from pacman because they may - Migrations run per-user after pacman finishes, because they may need `$HOME`,
need `$HOME`, DBus/session state, a graphical session, or user interaction. DBus/session state, a graphical session, sudo, or user interaction.
- Users who bypass `omarchy update` should still get system migrations from - Users who bypass `omarchy update` are nudged back by the pacman guard; if they
pacman and should be notified only when their own user migration state is explicitly bypass it, their session is notified when migrations are pending.
missing a shipped user migration.
## State and coordination files ## 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`. | | `/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/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. | | `~/.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 | Per-user migration markers. |
| `~/.local/state/omarchy/migrations/user/` | user | User migration markers. |
| `~/.local/state/omarchy/reboot-required` | user | Optional reboot marker checked by `omarchy-update-restart`. | | `~/.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`. | | `~/.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 See [`migrations.md`](migrations.md) for the full migration model, authoring
guidelines, and troubleshooting notes. guidelines, and troubleshooting notes.
Migrations are scoped by directory: Migrations live in:
```text ```text
migrations/system/*.sh migrations/*.sh
migrations/user/*.sh
``` ```
### System migrations They run as the current user through:
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:
```bash ```bash
omarchy-migrate omarchy-migrate
``` ```
Completion state: Completion state is per-user:
```text ```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 Every user gets a chance to run every migration. Migrations run as the user;
that user's matching state file is missing. There is no global root-owned queue privileged work should invoke the appropriate helper or privilege prompt.
for user migrations. 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 For watchers and diagnostics, `omarchy-migrate --pending` prints pending
transaction to finish, runs pending system migrations, then runs pending user migration names and exits `0` when any are pending. When no migrations are
migrations. It does not need `--force`; migrations should happen when they are pending, it prints nothing and exits non-zero.
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.
## Raw pacman guard ## Raw pacman guard
@@ -187,10 +134,8 @@ omarchy-update
Important behavior: Important behavior:
- `omarchy update` checks/runs system and user migrations in the same visible - `omarchy update` checks/runs migrations in the same visible terminal via
terminal via `omarchy-migrate`. `omarchy-migrate` after pacman finishes.
- `omarchy update` still benefits from pacman-triggered system migrations
because the system migration hook runs as part of the pacman transaction.
- A failure should leave enough output in `/tmp/omarchy-update.log` and the - A failure should leave enough output in `/tmp/omarchy-update.log` and the
terminal transcript to debug. terminal transcript to debug.
@@ -202,10 +147,9 @@ High-level flow:
sudo pacman -Syu sudo pacman -Syu
├─ pre-transaction guard aborts and tells the user to run omarchy update ├─ pre-transaction guard aborts and tells the user to run omarchy update
└─ if explicitly bypassed, upgrades omarchy and related packages └─ if explicitly bypassed, upgrades omarchy and related packages
├─ omarchy post_upgrade runs omarchy-migrate-system as root
└─ user session notices migration directory changes └─ user session notices migration directory changes
├─ omarchy-update-user-notify.path triggers, if enabled ├─ 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 ├─ if this user has missing migration state, show notification
└─ click opens terminal: omarchy-migrate └─ click opens terminal: omarchy-migrate
``` ```
@@ -215,12 +159,11 @@ Fallbacks:
- `omarchy-first-run` enables the user notification path unit. - `omarchy-first-run` enables the user notification path unit.
- `omarchy-first-run` also invokes `omarchy-migrate-notify` on graphical - `omarchy-first-run` also invokes `omarchy-migrate-notify` on graphical
startup, so users who updated before the path unit existed still get prompted startup, so users who updated before the path unit existed still get prompted
if they have missing user migration state. if they have missing migration state.
- The notifier is only a prompt. It does not run user migrations in the - The notifier is only a prompt. It does not run migrations in the background.
background.
- Direct pacman updates do not run `omarchy-hook post-update` unless the user - 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 explicitly runs that hook; without a package-update marker, the only pending
pending state we can derive is missing user migration markers. state we can derive is missing per-user migration markers.
## Shell update indicator ## 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-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-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-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` | 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-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-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-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-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-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. | | `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 ## Closed decisions
1. **System migrations run from the `omarchy` package** 1. **Migrations run per-user from the update pipeline**
- `omarchy.install post_upgrade()` calls `omarchy-migrate-system`. - `omarchy update` runs `omarchy-migrate` after pacman finishes.
- We do not need a separate system-migration ALPM hook from - Package-time migration runners do not apply migrations inside pacman.
`omarchy-settings`. - 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** 2. **Migration notification naming**
- 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**
- The real helper is `omarchy-migrate-notify`. - The real helper is `omarchy-migrate-notify`.
- `omarchy-update-user-notify` remains only as a hidden compatibility wrapper. - `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` owns the full update pipeline now.
- `omarchy-update-perform` is only a hidden compatibility wrapper for - `omarchy-update-perform` is only a hidden compatibility wrapper for
`omarchy-update -y`. `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`. - `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. - 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. - Direct `sudo pacman -Syu` no longer uses a fake user-update marker.
- User notifications are shown only when `omarchy-migrate --pending user` - User notifications are shown only when `omarchy-migrate --pending` finds
finds missing per-user migration state. missing per-user migration state.
## Remaining concerns ## Remaining concerns
View File
View File
+2 -2
View File
@@ -393,7 +393,7 @@ package.path = os.getenv("HOME")
.. package.path .. package.path
LUA LUA
ln -s "$MIGRATION_TMPDIR/.config/omarchy/current/theme/btop.theme" "$MIGRATION_TMPDIR/.config/btop/themes/current.theme" 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" [[ -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" [[ ! -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" 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" \ "../../../omarchy/current/theme/neovim.lua" \
"$NVIM_MIGRATION_TMPDIR/.config/omarchy/current/theme/neovim.lua"; do "$NVIM_MIGRATION_TMPDIR/.config/omarchy/current/theme/neovim.lua"; do
ln -sfn "$nvim_legacy_target" "$nvim_theme_link" 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_actual_target=$(readlink "$nvim_theme_link")
nvim_resolved_target=$(readlink -f "$nvim_theme_link") nvim_resolved_target=$(readlink -f "$nvim_theme_link")
[[ $nvim_actual_target == $nvim_expected_target ]] || fail "nvim theme migration updates theme symlink" [[ $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 ' "$TMPDIR/home/.config/omarchy/shell.json" >/dev/null
pass "shell config removes widgets with remove alias" pass "shell config removes widgets with remove alias"
mapfile -t migrations < <(find "$ROOT/migrations" -maxdepth 1 -type f -name '*.sh' -printf '%f\n' | sort) if grep -RIl 'upgrade-to-4\|Omarchy 4\.0 is upgraded' "$ROOT/migrations" >/dev/null; then
[[ ${#migrations[@]} -eq 0 ]] || fail "4.0 upgrade is not modeled as a migration" fail "4.0 upgrade is not modeled as a migration"
fi
pass "4.0 upgrade is handled outside the migration runner" 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" [[ -n $clock_migration ]] || fail "clock date format user migration exists"
cat >"$TMPDIR/home/.config/omarchy/shell.json" <<'JSON' 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" test_home="$test_tmp/home"
mkdir -p "$stub_bin" "$test_home" mkdir -p "$stub_bin" "$test_home"
cat >"$stub_bin/omarchy-migrate-user" <<'SH' cat >"$stub_bin/omarchy-migrate" <<'SH'
#!/bin/bash #!/bin/bash
if [[ ${1:-} == "--pending" && ${OMARCHY_TEST_PENDING_MIGRATIONS:-0} == 1 ]]; then if [[ ${1:-} == "--pending" && ${OMARCHY_TEST_PENDING_MIGRATIONS:-0} == 1 ]]; then
echo 200-user.sh echo 200-migration.sh
exit 0 exit 0
else else
exit 1 exit 1
fi fi
SH SH
chmod +x "$stub_bin/omarchy-migrate-user" chmod +x "$stub_bin/omarchy-migrate"
cat >"$stub_bin/systemd-run" <<'SH' cat >"$stub_bin/systemd-run" <<'SH'
#!/bin/bash #!/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" pass "migration notifier ignores users with no pending migrations"
run_notify 1 >"$test_tmp/pending.out" 2>"$test_tmp/pending.err" 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 'Omarchy has pending 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" grep -q '200-migration.sh' "$test_tmp/pending.err" || fail "migration notifier lists pending migration names"
pass "migration notifier reports pending user migrations" 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_root="$test_tmp/omarchy"
test_home="$test_tmp/home" test_home="$test_tmp/home"
system_state="$test_tmp/system-state" mkdir -p "$test_root/migrations" "$test_home"
mkdir -p "$test_root/migrations/system" "$test_root/migrations/user" "$test_home" "$system_state"
cat >"$test_root/migrations/system/100-system.sh" <<'SH' cat >"$test_root/migrations/100-first.sh" <<'SH'
[[ $OMARCHY_PATH == "$TEST_EXPECTED_OMARCHY_PATH" ]] [[ $OMARCHY_PATH == "$TEST_EXPECTED_OMARCHY_PATH" ]]
echo system >>"$TEST_CALLS" echo first >>"$TEST_CALLS"
SH SH
cat >"$test_root/migrations/user/200-user.sh" <<'SH' cat >"$test_root/migrations/200-second.sh" <<'SH'
[[ $OMARCHY_PATH == "$TEST_EXPECTED_OMARCHY_PATH" ]] [[ $OMARCHY_PATH == "$TEST_EXPECTED_OMARCHY_PATH" ]]
echo user >>"$TEST_CALLS" echo second >>"$TEST_CALLS"
SH SH
calls="$test_tmp/calls" calls="$test_tmp/calls"
if ! HOME="$test_home" OMARCHY_PATH="$test_root" "$ROOT/bin/omarchy-migrate-user" --pending >"$test_tmp/user-pending.out"; then if ! HOME="$test_home" OMARCHY_PATH="$test_root" "$ROOT/bin/omarchy-migrate" --pending >"$test_tmp/pending.out"; then
fail "user migration runner reports pending migrations before state exists" fail "migration runner reports pending migrations before state exists"
fi fi
grep -q '^200-user\.sh$' "$test_tmp/user-pending.out" || fail "user migration runner lists pending migration filename" grep -q '^100-first\.sh$' "$test_tmp/pending.out" || fail "migration runner lists first pending migration filename"
pass "user migration runner detects pending migrations" 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" \ HOME="$test_home" \
OMARCHY_PATH="$test_root" \ OMARCHY_PATH="$test_root" \
TEST_EXPECTED_OMARCHY_PATH="$test_root" \ TEST_EXPECTED_OMARCHY_PATH="$test_root" \
TEST_CALLS="$calls" \ TEST_CALLS="$calls" \
"$ROOT/bin/omarchy-migrate-user" >"$test_tmp/user-first.out" "$ROOT/bin/omarchy-migrate" >"$test_tmp/first-run.out"
[[ $(grep -c '^user$' "$calls") -eq 1 ]] || fail "user migration runner runs user migrations" [[ $(sed -n '1p' "$calls") == "first" ]] || fail "migration runner runs first migration"
! grep -q '^system$' "$calls" || fail "user migration runner does not run system migrations" [[ $(sed -n '2p' "$calls") == "second" ]] || fail "migration runner runs second migration"
[[ -f $test_home/.local/state/omarchy/migrations/user/200-user.sh ]] || fail "user migration runner records user migration marker" [[ -f $test_home/.local/state/omarchy/migrations/100-first.sh ]] || fail "migration runner records first migration marker"
pass "user migration runner only runs user migrations" [[ -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" \ HOME="$test_home" \
OMARCHY_PATH="$test_root" \ OMARCHY_PATH="$test_root" \
TEST_EXPECTED_OMARCHY_PATH="$test_root" \ TEST_EXPECTED_OMARCHY_PATH="$test_root" \
TEST_CALLS="$calls" \ TEST_CALLS="$calls" \
"$ROOT/bin/omarchy-migrate-user" >"$test_tmp/user-second.out" "$ROOT/bin/omarchy-migrate" >"$test_tmp/second-run.out"
[[ $(grep -c '^user$' "$calls") -eq 1 ]] || fail "user migration runner skips completed migrations" [[ $(wc -l <"$calls") -eq 2 ]] || fail "migration runner skips completed migrations"
pass "user 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 if HOME="$test_home" OMARCHY_PATH="$test_root" "$ROOT/bin/omarchy-migrate" --pending >"$test_tmp/not-pending.out"; then
fail "user migration runner reports no pending migrations after state exists" fail "migration runner reports no pending migrations after state exists"
fi fi
pass "user migration runner detects no pending migrations" pass "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"
failure_root="$test_tmp/failure-omarchy" failure_root="$test_tmp/failure-omarchy"
failure_home="$test_tmp/failure-home" failure_home="$test_tmp/failure-home"
failure_system_state="$test_tmp/failure-system-state" mkdir -p "$failure_root/migrations" "$failure_home"
mkdir -p "$failure_root/migrations/system" "$failure_root/migrations/user" "$failure_home" "$failure_system_state"
cat >"$failure_root/migrations/user/300-fail-user.sh" <<'SH' cat >"$failure_root/migrations/500-fail.sh" <<'SH'
echo user-before-fail >>"$TEST_CALLS" echo before-fail >>"$TEST_CALLS"
false false
echo user-after-fail >>"$TEST_CALLS" echo after-fail >>"$TEST_CALLS"
SH SH
set +e set +e
HOME="$failure_home" \ HOME="$failure_home" \
OMARCHY_PATH="$failure_root" \ OMARCHY_PATH="$failure_root" \
TEST_CALLS="$calls" \ TEST_CALLS="$calls" \
"$ROOT/bin/omarchy-migrate-user" >"$test_tmp/user-failure.out" 2>"$test_tmp/user-failure.err" "$ROOT/bin/omarchy-migrate" >"$test_tmp/failure.out" 2>"$test_tmp/failure.err"
user_failure_status=$? failure_status=$?
set -e set -e
[[ $user_failure_status -ne 0 ]] || fail "user migration runner exits non-zero when a migration fails" [[ $failure_status -ne 0 ]] || fail "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" [[ ! -f $failure_home/.local/state/omarchy/migrations/500-fail.sh ]] || fail "migration runner does not mark failed migration complete"
grep -q '^user-before-fail$' "$calls" || fail "user migration runner started failing migration" grep -q '^before-fail$' "$calls" || fail "migration runner started failing migration"
! grep -q '^user-after-fail$' "$calls" || fail "user migration runner stops failing migration under strict mode" ! grep -q '^after-fail$' "$calls" || fail "migration runner stops failing migration under strict mode"
pass "user migration runner does not mark failed migrations complete" pass "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"
+13 -37
View File
@@ -7,57 +7,33 @@ source "$(dirname "$0")/base-test.sh"
test_tmp=$(mktemp -d) test_tmp=$(mktemp -d)
trap 'rm -rf "$test_tmp"' EXIT trap 'rm -rf "$test_tmp"' EXIT
stub_bin="$test_tmp/bin" test_root="$test_tmp/omarchy"
test_home="$test_tmp/home" 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' cat >"$test_root/migrations/100-migration.sh" <<'SH'
#!/bin/bash echo migration >>"$TEST_CALLS"
if [[ ${1:-} == "--pending" ]]; then
[[ ${OMARCHY_TEST_PENDING_SYSTEM:-1} == 1 ]] || exit 1
echo 100-system.sh
exit 0
fi
echo system >>"$TEST_CALLS"
SH 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() { run_migrate() {
HOME="$test_home" \ HOME="$test_home" \
PATH="$stub_bin:$PATH" \ OMARCHY_PATH="$test_root" \
TEST_CALLS="$test_tmp/calls" \ TEST_CALLS="$test_tmp/calls" \
"$ROOT/bin/omarchy-migrate" "$@" "$ROOT/bin/omarchy-migrate" "$@"
} }
: >"$test_tmp/calls" : >"$test_tmp/calls"
run_migrate >"$test_tmp/migrate.out" run_migrate >"$test_tmp/migrate.out"
[[ $(sed -n '1p' "$test_tmp/calls") == "system" ]] || fail "omarchy-migrate runs system migrations first" [[ $(sed -n '1p' "$test_tmp/calls") == "migration" ]] || fail "omarchy-migrate runs pending migrations"
[[ $(sed -n '2p' "$test_tmp/calls") == "user" ]] || fail "omarchy-migrate runs user migrations second" pass "omarchy-migrate runs migrations without force"
[[ $(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"
run_migrate --pending >"$test_tmp/pending-all.out" rm -rf "$test_home/.local/state/omarchy/migrations"
grep -q '^system/100-system\.sh$' "$test_tmp/pending-all.out" || fail "omarchy-migrate --pending lists pending system migrations" run_migrate --pending >"$test_tmp/pending.out"
grep -q '^user/200-user\.sh$' "$test_tmp/pending-all.out" || fail "omarchy-migrate --pending lists pending user migrations" grep -q '^100-migration\.sh$' "$test_tmp/pending.out" || fail "omarchy-migrate --pending lists pending migrations"
pass "omarchy-migrate --pending lists all pending migrations" pass "omarchy-migrate --pending lists pending migrations"
run_migrate --pending user >"$test_tmp/pending-user.out" run_migrate >"$test_tmp/migrate-second.out"
grep -q '^user/200-user\.sh$' "$test_tmp/pending-user.out" || fail "omarchy-migrate --pending user lists user migrations" if run_migrate --pending >"$test_tmp/not-pending.out"; then
! 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
fail "omarchy-migrate --pending exits non-zero without pending migrations" fail "omarchy-migrate --pending exits non-zero without pending migrations"
fi fi
[[ ! -s $test_tmp/not-pending.out ]] || fail "omarchy-migrate --pending stays quiet without pending migrations" [[ ! -s $test_tmp/not-pending.out ]] || fail "omarchy-migrate --pending stays quiet without pending migrations"