diff --git a/bin/omarchy-update b/bin/omarchy-update index 22d9edbb..b479a9e8 100755 --- a/bin/omarchy-update +++ b/bin/omarchy-update @@ -73,6 +73,7 @@ run_update_pipeline() { disable_sleep_for_update disable_idle_for_update + omarchy-update-dev omarchy-update-keyring omarchy-update-system-pkgs omarchy-migrate diff --git a/bin/omarchy-update-available b/bin/omarchy-update-available index 2c824f5f..f1a70b25 100755 --- a/bin/omarchy-update-available +++ b/bin/omarchy-update-available @@ -1,9 +1,25 @@ #!/bin/bash -# omarchy:summary=Check whether Omarchy package updates are available. +# omarchy:summary=Check whether Omarchy updates are available. set -euo pipefail +updates=() + +if [[ $OMARCHY_PATH != "/usr/share/omarchy" ]]; then + upstream=$(git -C "$OMARCHY_PATH" rev-parse --abbrev-ref --symbolic-full-name '@{upstream}' 2>/dev/null || true) + if [[ -n $upstream ]]; then + GIT_TERMINAL_PROMPT=0 timeout 10 git -C "$OMARCHY_PATH" fetch --quiet 2>/dev/null || true + + behind=$(git -C "$OMARCHY_PATH" rev-list --count "HEAD..$upstream" 2>/dev/null || echo 0) + if (( behind > 0 )); then + commit_label=commits + (( behind == 1 )) && commit_label=commit + updates+=("omarchy-dev-checkout $behind new $commit_label on $upstream") + fi + fi +fi + package="" if pacman -Qq omarchy-dev >/dev/null 2>&1; then package=omarchy-dev @@ -16,7 +32,11 @@ if [[ -n $package ]]; then fi if [[ -n ${update:-} ]]; then - printf '%s\n' "$update" + updates+=("$update") +fi + +if (( ${#updates[@]} > 0 )); then + printf '%s\n' "${updates[@]}" exit 0 else echo "Omarchy is up to date" diff --git a/bin/omarchy-update-dev b/bin/omarchy-update-dev new file mode 100755 index 00000000..a973246e --- /dev/null +++ b/bin/omarchy-update-dev @@ -0,0 +1,21 @@ +#!/bin/bash + +# omarchy:summary=Update the active Omarchy dev checkout + +set -euo pipefail + +[[ $OMARCHY_PATH != "/usr/share/omarchy" ]] || exit 0 + +if ! git -C "$OMARCHY_PATH" rev-parse --is-inside-work-tree >/dev/null 2>&1; then + echo "Error: OMARCHY_PATH is not a git checkout: $OMARCHY_PATH" >&2 + exit 1 +fi + +upstream=$(git -C "$OMARCHY_PATH" rev-parse --abbrev-ref --symbolic-full-name '@{upstream}' 2>/dev/null || true) +if [[ -z $upstream ]]; then + echo -e "\e[33m\nSkip Omarchy dev checkout update (current branch has no upstream)\e[0m" + exit 0 +fi + +echo -e "\e[32m\nUpdate Omarchy dev checkout\e[0m" +git -C "$OMARCHY_PATH" pull --ff-only diff --git a/docs/update-process.md b/docs/update-process.md index 5581879c..200f1ffc 100644 --- a/docs/update-process.md +++ b/docs/update-process.md @@ -118,6 +118,7 @@ omarchy-update ├─ create snapper snapshot, if snapper is installed └─ run update pipeline ├─ block system sleep and temporarily enable shell stay-awake mode + ├─ omarchy-update-dev ├─ omarchy-update-keyring ├─ omarchy-update-system-pkgs ├─ omarchy-migrate @@ -133,6 +134,8 @@ omarchy-update Important behavior: +- In dev-link mode, `omarchy update` fast-forwards the active checkout from its + configured upstream before changing system packages or running migrations. - `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 @@ -172,11 +175,16 @@ The bar widget `omarchy.system-update` runs: omarchy-update-available ``` -`omarchy-update-available` checks the installed Omarchy package for updates: +`omarchy-update-available` checks the active Omarchy sources for updates: +- new upstream commits for the active dev-linked checkout - `omarchy-dev`, when installed - otherwise `omarchy`, when installed +The dev check fetches the checkout's configured upstream before comparing it +with `HEAD`. A failed fetch is quiet and falls back to the existing remote- +tracking state. + Exit codes: - `0` — Omarchy updates are available; stdout is the update list. @@ -196,6 +204,7 @@ scripts. | `omarchy-update` | Public user command. Adds transcript logging, lock, confirmation, snapshot, sleep/idle inhibitors, package updates, migrations, hooks, update-state refresh, and restart checks. | **Keep.** This is the blessed entry point and owns the update pipeline. | | `omarchy-update-perform` | Hidden compatibility wrapper for `omarchy-update -y`. | **Temporary.** Keep only for old callers; new code should call `omarchy-update` directly. | | `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-dev` | Fast-forwards the active dev-linked checkout from its configured upstream; no-ops for package-backed installs. | **Keep.** Runs before package updates so a checkout conflict stops the update before system mutation. | | `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` | 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`. | diff --git a/test/shell.d/update-available-test.sh b/test/shell.d/update-available-test.sh index 40cfbd1c..60158863 100644 --- a/test/shell.d/update-available-test.sh +++ b/test/shell.d/update-available-test.sh @@ -8,6 +8,7 @@ test_tmp=$(mktemp -d) trap 'rm -rf "$test_tmp"' EXIT stub_bin="$test_tmp/bin" +git_log="$test_tmp/git.log" mkdir -p "$stub_bin" cat >"$stub_bin/checkupdates" <<'SH' @@ -52,8 +53,48 @@ exit 0 SH chmod +x "$stub_bin/pacman" +cat >"$stub_bin/git" <<'SH' +#!/bin/bash + +printf '%s\n' "$*" >>"$TEST_GIT_LOG" + +[[ $1 == "-C" ]] || exit 1 +shift 2 + +case "$1" in + fetch) + [[ ${TEST_GIT_FETCH:-ok} == "ok" ]] + ;; + rev-parse) + case "$2" in + --is-inside-work-tree) + [[ ${TEST_GIT_CHECKOUT:-yes} == "yes" ]] || exit 1 + echo true + ;; + --abbrev-ref) + [[ ${TEST_GIT_UPSTREAM:-origin/quattro} != "none" ]] || exit 1 + echo "${TEST_GIT_UPSTREAM:-origin/quattro}" + ;; + *) + exit 1 + ;; + esac + ;; + rev-list) + echo "${TEST_GIT_BEHIND:-0}" + ;; + *) + exit 1 + ;; +esac +SH +chmod +x "$stub_bin/git" + run_checker() { - PATH="$stub_bin:$PATH" "$ROOT/bin/omarchy-update-available" + OMARCHY_PATH="${TEST_OMARCHY_PATH:-/usr/share/omarchy}" \ + TEST_GIT_LOG="$git_log" \ + PATH="$stub_bin:$PATH" \ + "$ROOT/bin/omarchy-update-available" } capture_checker() { @@ -124,3 +165,49 @@ fi [[ $status -eq 1 ]] || fail "update checker exits non-zero when no updates are available" grep -q '^Omarchy is up to date$' "$stdout" || fail "update checker prints up-to-date message" pass "update checker reports up-to-date Omarchy packages" + +: >"$git_log" +if capture_checker "$stdout" "$stderr" \ + TEST_CHECKUPDATES=none \ + TEST_INSTALLED_PACKAGE=none \ + TEST_OMARCHY_PATH="$test_tmp/checkout" \ + TEST_GIT_BEHIND=2; then + status=0 +else + status=$? +fi +[[ $status -eq 0 ]] || fail "update checker exits successfully when dev commits are available" +grep -Fx 'omarchy-dev-checkout 2 new commits on origin/quattro' "$stdout" >/dev/null || + fail "update checker reports available dev commits" "$(cat "$stdout")" +grep -Fx -- "-C $test_tmp/checkout fetch --quiet" "$git_log" >/dev/null || + fail "update checker fetches the dev checkout upstream" "$(cat "$git_log")" +pass "update checker detects new commits in the dev checkout" + +if capture_checker "$stdout" "$stderr" \ + TEST_CHECKUPDATES=none \ + TEST_INSTALLED_PACKAGE=none \ + TEST_OMARCHY_PATH="$test_tmp/checkout" \ + TEST_GIT_BEHIND=0; then + status=0 +else + status=$? +fi +[[ $status -eq 1 ]] || fail "update checker exits non-zero when the dev checkout is current" +grep -q '^Omarchy is up to date$' "$stdout" || fail "update checker reports a current dev checkout" +pass "update checker ignores a current dev checkout" + +if capture_checker "$stdout" "$stderr" \ + TEST_CHECKUPDATES=none \ + TEST_INSTALLED_PACKAGE=none \ + TEST_OMARCHY_PATH="$test_tmp/checkout" \ + TEST_GIT_BEHIND=1 \ + TEST_GIT_FETCH=fail; then + status=0 +else + status=$? +fi +[[ $status -eq 0 ]] || fail "update checker uses cached upstream state when fetch fails" +grep -Fx 'omarchy-dev-checkout 1 new commit on origin/quattro' "$stdout" >/dev/null || + fail "update checker reports cached dev commits after a fetch failure" "$(cat "$stdout")" +[[ ! -s $stderr ]] || fail "update checker keeps dev fetch failures quiet" "$(cat "$stderr")" +pass "update checker uses cached dev state when fetching is unavailable" diff --git a/test/shell.d/update-dev-test.sh b/test/shell.d/update-dev-test.sh new file mode 100644 index 00000000..68da418a --- /dev/null +++ b/test/shell.d/update-dev-test.sh @@ -0,0 +1,84 @@ +#!/bin/bash + +set -euo pipefail + +source "$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)/base-test.sh" + +test_tmp=$(mktemp -d) +trap 'rm -rf "$test_tmp"' EXIT + +stub_bin="$test_tmp/bin" +git_log="$test_tmp/git.log" +checkout="$test_tmp/checkout" +mkdir -p "$stub_bin" "$checkout" + +cat >"$stub_bin/git" <<'SH' +#!/bin/bash + +printf '%s\n' "$*" >>"$TEST_GIT_LOG" + +[[ $1 == "-C" ]] || exit 1 +shift 2 + +case "$1" in + rev-parse) + case "$2" in + --is-inside-work-tree) + [[ ${TEST_GIT_CHECKOUT:-yes} == "yes" ]] || exit 1 + echo true + ;; + --abbrev-ref) + [[ ${TEST_GIT_UPSTREAM:-origin/quattro} != "none" ]] || exit 1 + echo "${TEST_GIT_UPSTREAM:-origin/quattro}" + ;; + *) + exit 1 + ;; + esac + ;; + pull) + [[ $2 == "--ff-only" ]] + ;; + *) + exit 1 + ;; +esac +SH +chmod +x "$stub_bin/git" + +run_dev_update() { + OMARCHY_PATH="$1" \ + TEST_GIT_LOG="$git_log" \ + PATH="$stub_bin:$PATH" \ + "$ROOT/bin/omarchy-update-dev" +} + +: >"$git_log" +run_dev_update /usr/share/omarchy +[[ ! -s $git_log ]] || fail "package-backed updates do not invoke git" "$(cat "$git_log")" +pass "package-backed updates skip the dev checkout step" + +: >"$git_log" +run_dev_update "$checkout" +grep -Fx -- "-C $checkout pull --ff-only" "$git_log" >/dev/null || + fail "dev checkout update pulls its upstream with fast-forward only" "$(cat "$git_log")" +pass "dev checkout update pulls its configured upstream" + +: >"$git_log" +TEST_GIT_UPSTREAM=none run_dev_update "$checkout" +if grep -q ' pull ' "$git_log"; then + fail "dev checkout without an upstream is not pulled" "$(cat "$git_log")" +fi +pass "dev checkout without an upstream is skipped safely" + +: >"$git_log" +if TEST_GIT_CHECKOUT=no run_dev_update "$checkout" >"$test_tmp/invalid.out" 2>"$test_tmp/invalid.err"; then + fail "invalid dev checkout fails the update" +fi +grep -F "OMARCHY_PATH is not a git checkout: $checkout" "$test_tmp/invalid.err" >/dev/null || + fail "invalid dev checkout reports the configured path" "$(cat "$test_tmp/invalid.err")" +pass "invalid dev checkout fails with a useful error" + +grep -qE '^ *omarchy-update-dev$' "$ROOT/bin/omarchy-update" || + fail "top-level update includes the dev checkout step" +pass "top-level update includes the dev checkout step" diff --git a/test/shell.d/update-lock-test.sh b/test/shell.d/update-lock-test.sh index aef0b6fe..73895768 100644 --- a/test/shell.d/update-lock-test.sh +++ b/test/shell.d/update-lock-test.sh @@ -34,6 +34,7 @@ SH for command in \ omarchy-toggle-idle \ systemd-inhibit \ + omarchy-update-dev \ omarchy-update-keyring \ omarchy-update-system-pkgs \ omarchy-migrate \