Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
# Changelog

## [0.8.6] - 2026-03-19

### Fixed

- **Update checker no longer misses new versions after an upgrade.** When gstack detected a "just upgraded" marker, it used to write "up to date" to its cache and exit — never checking if an even newer version was already available on remote. Now it prints the upgrade confirmation and continues to check for newer versions, so you find out about 0.9.0 right after upgrading to 0.8.6.
- **`--force` now fully resets update state.** Previously, `--force` cleared the version cache but left the "just upgraded" marker intact, causing forced checks to short-circuit. Now it clears both, so `--force` always does a fresh remote check.
- **Fresh cache no longer suppresses post-upgrade remote check.** The marker handler now clears the cache file alongside the snooze file, ensuring the remote version check always runs after an upgrade — even if a recent cache entry would have otherwise suppressed it.

## [0.8.5] - 2026-03-19

### Fixed
Expand Down
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.8.5
0.8.6
12 changes: 8 additions & 4 deletions bin/gstack-update-check
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
#!/usr/bin/env bash
# gstack-update-check — periodic version check for all skills.
#
# Output (one line, or nothing):
# Output (zero, one, or two lines):
# JUST_UPGRADED <old> <new> — marker found from recent upgrade
# UPGRADE_AVAILABLE <old> <new> — remote VERSION differs from local
# (nothing) — up to date, snoozed, disabled, or check skipped
#
# Both lines may appear together when the user just upgraded but an even
# newer version is already available on remote.
#
# Env overrides (for testing):
# GSTACK_DIR — override auto-detected gstack root
# GSTACK_REMOTE_URL — override remote VERSION URL
Expand All @@ -23,6 +26,7 @@ REMOTE_URL="${GSTACK_REMOTE_URL:-https://raw.githubusercontent.com/garrytan/gsta
# ─── Force flag (busts cache for standalone /gstack-upgrade) ──
if [ "${1:-}" = "--force" ]; then
rm -f "$CACHE_FILE"
rm -f "$MARKER_FILE"
fi

# ─── Step 0: Check if updates are disabled ────────────────────
Expand Down Expand Up @@ -90,16 +94,16 @@ if [ -z "$LOCAL" ]; then
fi

# ─── Step 2: Check "just upgraded" marker ─────────────────────
# Print JUST_UPGRADED but don't exit — fall through to remote check
# so we can also detect if a newer version is already available.
if [ -f "$MARKER_FILE" ]; then
OLD="$(cat "$MARKER_FILE" 2>/dev/null | tr -d '[:space:]')"
rm -f "$MARKER_FILE"
rm -f "$SNOOZE_FILE"
mkdir -p "$STATE_DIR"
echo "UP_TO_DATE $LOCAL" > "$CACHE_FILE"
rm -f "$CACHE_FILE"
if [ -n "$OLD" ]; then
echo "JUST_UPGRADED $OLD $LOCAL"
fi
exit 0
fi

# ─── Step 3: Check cache freshness ──────────────────────────
Expand Down
62 changes: 62 additions & 0 deletions browse/test/gstack-update-check.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -447,6 +447,68 @@ describe('gstack-update-check', () => {
expect(cache).toContain('UP_TO_DATE');
});

test('--force clears marker file so remote check runs', () => {
writeFileSync(join(gstackDir, 'VERSION'), '0.4.0\n');
writeFileSync(join(gstackDir, 'REMOTE_VERSION'), '0.5.0\n');
writeFileSync(join(stateDir, 'just-upgraded-from'), '0.3.3\n');

// With --force: marker cleared before script runs, goes straight to remote
const { exitCode, stdout } = run({}, ['--force']);
expect(exitCode).toBe(0);
expect(stdout).toBe('UPGRADE_AVAILABLE 0.4.0 0.5.0');
// Marker should be gone
expect(existsSync(join(stateDir, 'just-upgraded-from'))).toBe(false);
});

// ─── Marker + remote check fall-through tests ────────────────

test('marker falls through to remote check when newer version exists', () => {
writeFileSync(join(gstackDir, 'VERSION'), '0.4.0\n');
writeFileSync(join(gstackDir, 'REMOTE_VERSION'), '0.5.0\n');
writeFileSync(join(stateDir, 'just-upgraded-from'), '0.3.3\n');

const { exitCode, stdout } = run();
expect(exitCode).toBe(0);
// Both lines should appear: upgraded notification + new version available
expect(stdout).toContain('JUST_UPGRADED 0.3.3 0.4.0');
expect(stdout).toContain('UPGRADE_AVAILABLE 0.4.0 0.5.0');
// Marker should be deleted
expect(existsSync(join(stateDir, 'just-upgraded-from'))).toBe(false);
// Cache should reflect the available upgrade, not UP_TO_DATE
const cache = readFileSync(join(stateDir, 'last-update-check'), 'utf-8');
expect(cache).toContain('UPGRADE_AVAILABLE 0.4.0 0.5.0');
});

test('marker falls through to remote check when up to date', () => {
writeFileSync(join(gstackDir, 'VERSION'), '0.4.0\n');
writeFileSync(join(gstackDir, 'REMOTE_VERSION'), '0.4.0\n');
writeFileSync(join(stateDir, 'just-upgraded-from'), '0.3.3\n');

const { exitCode, stdout } = run();
expect(exitCode).toBe(0);
// Only JUST_UPGRADED, no UPGRADE_AVAILABLE since remote == local
expect(stdout).toBe('JUST_UPGRADED 0.3.3 0.4.0');
const cache = readFileSync(join(stateDir, 'last-update-check'), 'utf-8');
expect(cache).toContain('UP_TO_DATE');
});

test('marker clears fresh cache so remote check always runs', () => {
writeFileSync(join(gstackDir, 'VERSION'), '0.4.0\n');
writeFileSync(join(gstackDir, 'REMOTE_VERSION'), '0.5.0\n');
writeFileSync(join(stateDir, 'just-upgraded-from'), '0.3.3\n');
// Fresh UP_TO_DATE cache that would normally suppress remote check
writeFileSync(join(stateDir, 'last-update-check'), 'UP_TO_DATE 0.4.0');

const { exitCode, stdout } = run();
expect(exitCode).toBe(0);
// Marker should bust the cache — remote check finds 0.5.0
expect(stdout).toContain('JUST_UPGRADED 0.3.3 0.4.0');
expect(stdout).toContain('UPGRADE_AVAILABLE 0.4.0 0.5.0');
// Cache overwritten with new result
const cache = readFileSync(join(stateDir, 'last-update-check'), 'utf-8');
expect(cache).toContain('UPGRADE_AVAILABLE 0.4.0 0.5.0');
});

// ─── Split TTL tests ─────────────────────────────────────────

test('UP_TO_DATE cache expires after 60 min (not 720)', () => {
Expand Down