From c2122395a4d28705c04a5767a3764eecb8e3726f Mon Sep 17 00:00:00 2001 From: Garry Tan Date: Thu, 2 Apr 2026 18:29:25 -0700 Subject: [PATCH] =?UTF-8?q?feat:=20upgrade=20migration=20system=20?= =?UTF-8?q?=E2=80=94=20versioned=20fix=20scripts=20for=20broken=20state?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds gstack-upgrade/migrations/ directory with version-keyed bash scripts that run automatically during /gstack-upgrade (Step 4.75, after ./setup). Each script is idempotent and handles state fixes that setup alone can't cover. First migration: v0.15.2.0.sh runs gstack-relink to fix stale directory symlinks from pre-v0.15.2.0 installs. Co-Authored-By: Claude Opus 4.6 --- gstack-upgrade/SKILL.md | 26 ++++++++++++++++++++++++++ gstack-upgrade/SKILL.md.tmpl | 26 ++++++++++++++++++++++++++ gstack-upgrade/migrations/v0.15.2.0.sh | 20 ++++++++++++++++++++ 3 files changed, 72 insertions(+) create mode 100755 gstack-upgrade/migrations/v0.15.2.0.sh diff --git a/gstack-upgrade/SKILL.md b/gstack-upgrade/SKILL.md index f97f11fb..d357e552 100644 --- a/gstack-upgrade/SKILL.md +++ b/gstack-upgrade/SKILL.md @@ -170,6 +170,32 @@ mv "$LOCAL_GSTACK.bak" "$LOCAL_GSTACK" ``` Tell user: "Sync failed — restored previous version at `$LOCAL_GSTACK`. Run `/gstack-upgrade` manually to retry." +### Step 4.75: Run version migrations + +After `./setup` completes, run any migration scripts for versions between the old +and new version. Migrations handle state fixes that `./setup` alone can't cover +(stale config, orphaned files, directory structure changes). + +```bash +MIGRATIONS_DIR="$INSTALL_DIR/gstack-upgrade/migrations" +if [ -d "$MIGRATIONS_DIR" ]; then + for migration in $(find "$MIGRATIONS_DIR" -maxdepth 1 -name 'v*.sh' -type f 2>/dev/null | sort -V); do + # Extract version from filename: v0.15.2.0.sh → 0.15.2.0 + m_ver="$(basename "$migration" .sh | sed 's/^v//')" + # Run if this migration version is newer than old version + # (simple string compare works for dotted versions with same segment count) + if [ "$OLD_VERSION" != "unknown" ] && [ "$(printf '%s\n%s' "$OLD_VERSION" "$m_ver" | sort -V | head -1)" = "$OLD_VERSION" ] && [ "$OLD_VERSION" != "$m_ver" ]; then + echo "Running migration $m_ver..." + bash "$migration" || echo " Warning: migration $m_ver had errors (non-fatal)" + fi + done +fi +``` + +Migrations are idempotent bash scripts in `gstack-upgrade/migrations/`. Each is named +`v{VERSION}.sh` and runs only when upgrading from an older version. See CONTRIBUTING.md +for how to add new migrations. + ### Step 5: Write marker + clear cache ```bash diff --git a/gstack-upgrade/SKILL.md.tmpl b/gstack-upgrade/SKILL.md.tmpl index ac25894b..7204c75b 100644 --- a/gstack-upgrade/SKILL.md.tmpl +++ b/gstack-upgrade/SKILL.md.tmpl @@ -168,6 +168,32 @@ mv "$LOCAL_GSTACK.bak" "$LOCAL_GSTACK" ``` Tell user: "Sync failed — restored previous version at `$LOCAL_GSTACK`. Run `/gstack-upgrade` manually to retry." +### Step 4.75: Run version migrations + +After `./setup` completes, run any migration scripts for versions between the old +and new version. Migrations handle state fixes that `./setup` alone can't cover +(stale config, orphaned files, directory structure changes). + +```bash +MIGRATIONS_DIR="$INSTALL_DIR/gstack-upgrade/migrations" +if [ -d "$MIGRATIONS_DIR" ]; then + for migration in $(find "$MIGRATIONS_DIR" -maxdepth 1 -name 'v*.sh' -type f 2>/dev/null | sort -V); do + # Extract version from filename: v0.15.2.0.sh → 0.15.2.0 + m_ver="$(basename "$migration" .sh | sed 's/^v//')" + # Run if this migration version is newer than old version + # (simple string compare works for dotted versions with same segment count) + if [ "$OLD_VERSION" != "unknown" ] && [ "$(printf '%s\n%s' "$OLD_VERSION" "$m_ver" | sort -V | head -1)" = "$OLD_VERSION" ] && [ "$OLD_VERSION" != "$m_ver" ]; then + echo "Running migration $m_ver..." + bash "$migration" || echo " Warning: migration $m_ver had errors (non-fatal)" + fi + done +fi +``` + +Migrations are idempotent bash scripts in `gstack-upgrade/migrations/`. Each is named +`v{VERSION}.sh` and runs only when upgrading from an older version. See CONTRIBUTING.md +for how to add new migrations. + ### Step 5: Write marker + clear cache ```bash diff --git a/gstack-upgrade/migrations/v0.15.2.0.sh b/gstack-upgrade/migrations/v0.15.2.0.sh new file mode 100755 index 00000000..ebee442e --- /dev/null +++ b/gstack-upgrade/migrations/v0.15.2.0.sh @@ -0,0 +1,20 @@ +#!/usr/bin/env bash +# Migration: v0.15.2.0 — Fix skill directory structure for unprefixed discovery +# +# What changed: setup now creates real directories with SKILL.md symlinks +# inside instead of directory symlinks. The old pattern (qa -> gstack/qa) +# caused Claude Code to auto-prefix skills as "gstack-qa" even with +# --no-prefix, because Claude sees the symlink target's parent dir name. +# +# What this does: runs gstack-relink to recreate all skill entries using +# the new real-directory pattern. Idempotent — safe to run multiple times. +# +# Affected: users who installed gstack before v0.15.2.0 with --no-prefix +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "$0")/../.." && pwd)" + +if [ -x "$SCRIPT_DIR/bin/gstack-relink" ]; then + echo " [v0.15.2.0] Fixing skill directory structure..." + "$SCRIPT_DIR/bin/gstack-relink" 2>/dev/null || true +fi