From af7ab9812e61824e590e9c208e6477e035c2a211 Mon Sep 17 00:00:00 2001 From: genisis0x Date: Wed, 13 May 2026 14:33:22 +0530 Subject: [PATCH] fix(update-check): implement --apply to actually run the upgrade MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #1353. `gstack-update-check --apply` previously exited 0 and emitted `UPGRADE_AVAILABLE ` without touching the working tree. The flag was undocumented and the script's only flag handler was `--force`; unknown flags fell through to the report-only path, so `--apply` was indistinguishable from no flag. Add an `--apply` branch that, when an upgrade is detected, runs the minimal headless variant of `/gstack-upgrade` Step 4: git -C "$GSTACK_DIR" fetch origin main git -C "$GSTACK_DIR" reset --hard origin/main ( cd "$GSTACK_DIR" && ./setup ) On success the script emits `UPGRADED ` (distinct from the report-only `UPGRADE_AVAILABLE` token) and caches that line so a subsequent `--apply` re-run, after the VERSION already moved, prints the new `UP_TO_DATE` and exits cleanly. Scope is intentionally minimal: - Git installs only. Vendored installs need `mv` + clone-into-place and the local-vendored sync that `/gstack-upgrade` Step 4.5 handles — surface an explicit error pointing at `/gstack-upgrade` instead. - Snooze is ignored on `--apply`. The caller asked for the upgrade explicitly; honoring snooze would mean a CI gate appears to succeed while not actually upgrading. - Each git step's failure prints to stderr and exits non-zero so a scripted caller can react. A `git reset` that succeeded followed by a `./setup` that failed leaves the working tree at the new version and surfaces an explicit "re-run ./setup manually" hint. Refactor flag parsing from the single-arg `if "${1:-}" = "--force"` shape to a `for arg in "$@"; case` loop so the two flags compose (e.g. `--force --apply` is well-defined: both fire) and an unknown flag still falls through silently to preserve the existing zero-noise contract of the periodic-check path. Header doc grows three lines covering the new flag and the new output token so the file is self-describing. --- bin/gstack-update-check | 72 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 67 insertions(+), 5 deletions(-) diff --git a/bin/gstack-update-check b/bin/gstack-update-check index 31e9fdb6f..b2864d0ec 100755 --- a/bin/gstack-update-check +++ b/bin/gstack-update-check @@ -4,8 +4,18 @@ # Output (one line, or nothing): # JUST_UPGRADED — marker found from recent upgrade # UPGRADE_AVAILABLE — remote VERSION differs from local +# UPGRADED — `--apply` ran a successful upgrade # (nothing) — up to date, snoozed, disabled, or check skipped # +# Flags: +# --force Bust cache and snooze (used by standalone /gstack-upgrade). +# --apply When an upgrade is available, actually run it: `git fetch` + +# `git reset --hard origin/main` + `./setup` inside `$GSTACK_DIR`. +# For the full ceremony (stash, vendored-install handling, +# local-vendored copy sync, migrations), use /gstack-upgrade. +# `--apply` is the minimal headless variant for CI / scripted +# update gates. +# # Env overrides (for testing): # GSTACK_DIR — override auto-detected gstack root # GSTACK_REMOTE_URL — override remote VERSION URL @@ -20,11 +30,25 @@ SNOOZE_FILE="$STATE_DIR/update-snoozed" VERSION_FILE="$GSTACK_DIR/VERSION" REMOTE_URL="${GSTACK_REMOTE_URL:-https://raw.githubusercontent.com/garrytan/gstack/main/VERSION}" -# ─── Force flag (busts cache + snooze for standalone /gstack-upgrade) ── -if [ "${1:-}" = "--force" ]; then - rm -f "$CACHE_FILE" - rm -f "$SNOOZE_FILE" -fi +# ─── Parse flags ────────────────────────────────────────────── +APPLY=0 +for arg in "$@"; do + case "$arg" in + --force) + rm -f "$CACHE_FILE" + rm -f "$SNOOZE_FILE" + ;; + --apply) + APPLY=1 + # Force a fresh check so a stale cache doesn't suppress the upgrade. + rm -f "$CACHE_FILE" + ;; + *) + # Unknown flag — fall through silently to preserve the historical + # zero-noise contract of the periodic-check path. + ;; + esac +done # ─── Step 0: Check if updates are disabled ──────────────────── _UC=$("$GSTACK_DIR/bin/gstack-config" get update_check 2>/dev/null || true) @@ -197,6 +221,44 @@ fi # Versions differ — upgrade available echo "UPGRADE_AVAILABLE $LOCAL $REMOTE" > "$CACHE_FILE" + +# ─── Apply branch ──────────────────────────────────────────── +# `--apply` is the headless / scripted variant of /gstack-upgrade Step 4 +# for git installs. It does NOT cover vendored installs, local-vendored +# copy sync, or the migrations pipeline — point users at /gstack-upgrade +# for any of those. Snooze is intentionally ignored on --apply: the +# caller asked for the upgrade explicitly. +if [ "$APPLY" = "1" ]; then + if [ ! -d "$GSTACK_DIR/.git" ]; then + echo "ERROR: --apply requires a git install at $GSTACK_DIR" >&2 + echo " Run /gstack-upgrade for vendored installs." >&2 + exit 1 + fi + if ! git -C "$GSTACK_DIR" fetch origin main >/dev/null 2>&1; then + echo "ERROR: --apply failed to fetch origin/main from $GSTACK_DIR" >&2 + exit 1 + fi + if ! git -C "$GSTACK_DIR" reset --hard origin/main >/dev/null 2>&1; then + echo "ERROR: --apply failed to reset $GSTACK_DIR to origin/main" >&2 + exit 1 + fi + if [ -x "$GSTACK_DIR/setup" ]; then + if ! ( cd "$GSTACK_DIR" && ./setup >/dev/null 2>&1 ); then + echo "ERROR: --apply ran git reset but ./setup failed in $GSTACK_DIR" >&2 + echo " Working tree is at the new version; re-run ./setup manually." >&2 + exit 1 + fi + fi + # Confirm the version moved. + NEW_LOCAL="" + if [ -f "$VERSION_FILE" ]; then + NEW_LOCAL="$(cat "$VERSION_FILE" 2>/dev/null | tr -d '[:space:]')" + fi + echo "UPGRADED $LOCAL ${NEW_LOCAL:-$REMOTE}" > "$CACHE_FILE" + echo "UPGRADED $LOCAL ${NEW_LOCAL:-$REMOTE}" + exit 0 +fi + if check_snooze "$REMOTE"; then exit 0 # snoozed — stay quiet fi