mirror of
https://github.com/garrytan/gstack.git
synced 2026-05-05 05:05:08 +02:00
fix: split update check cache TTL + add --force flag
UP_TO_DATE cache now expires after 60 min (was 720 min / 12 hours). UPGRADE_AVAILABLE keeps 720 min TTL to keep nagging. --force flag deletes cache before checking, used by /gstack-upgrade standalone invocation to always get a fresh result from GitHub. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
+17
-10
@@ -20,6 +20,11 @@ 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 for standalone /gstack-upgrade) ──
|
||||
if [ "${1:-}" = "--force" ]; then
|
||||
rm -f "$CACHE_FILE"
|
||||
fi
|
||||
|
||||
# ─── Step 0: Check if updates are disabled ────────────────────
|
||||
_UC=$("$GSTACK_DIR/bin/gstack-config" get update_check 2>/dev/null || true)
|
||||
if [ "$_UC" = "false" ]; then
|
||||
@@ -97,24 +102,27 @@ if [ -f "$MARKER_FILE" ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# ─── Step 3: Check cache freshness (12h = 720 min) ──────────
|
||||
# ─── Step 3: Check cache freshness ──────────────────────────
|
||||
# UP_TO_DATE: 60 min TTL (detect new releases quickly)
|
||||
# UPGRADE_AVAILABLE: 720 min TTL (keep nagging)
|
||||
if [ -f "$CACHE_FILE" ]; then
|
||||
# Cache is fresh if modified within 720 minutes
|
||||
STALE=$(find "$CACHE_FILE" -mmin +720 2>/dev/null || true)
|
||||
if [ -z "$STALE" ]; then
|
||||
# Cache is fresh — read it
|
||||
CACHED="$(cat "$CACHE_FILE" 2>/dev/null || true)"
|
||||
CACHED="$(cat "$CACHE_FILE" 2>/dev/null || true)"
|
||||
case "$CACHED" in
|
||||
UP_TO_DATE*) CACHE_TTL=60 ;;
|
||||
UPGRADE_AVAILABLE*) CACHE_TTL=720 ;;
|
||||
*) CACHE_TTL=0 ;; # corrupt → force re-fetch
|
||||
esac
|
||||
|
||||
STALE=$(find "$CACHE_FILE" -mmin +$CACHE_TTL 2>/dev/null || true)
|
||||
if [ -z "$STALE" ] && [ "$CACHE_TTL" -gt 0 ]; then
|
||||
case "$CACHED" in
|
||||
UP_TO_DATE*)
|
||||
# Verify local version still matches cached version
|
||||
CACHED_VER="$(echo "$CACHED" | awk '{print $2}')"
|
||||
if [ "$CACHED_VER" = "$LOCAL" ]; then
|
||||
exit 0
|
||||
fi
|
||||
# Local version changed — fall through to re-check
|
||||
;;
|
||||
UPGRADE_AVAILABLE*)
|
||||
# Verify local version still matches cached old version
|
||||
CACHED_OLD="$(echo "$CACHED" | awk '{print $2}')"
|
||||
if [ "$CACHED_OLD" = "$LOCAL" ]; then
|
||||
CACHED_NEW="$(echo "$CACHED" | awk '{print $3}')"
|
||||
@@ -124,7 +132,6 @@ if [ -f "$CACHE_FILE" ]; then
|
||||
echo "$CACHED"
|
||||
exit 0
|
||||
fi
|
||||
# Local version changed (manual upgrade?) — fall through to re-check
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
*/
|
||||
|
||||
import { describe, test, expect, beforeEach, afterEach } from 'bun:test';
|
||||
import { mkdtempSync, writeFileSync, rmSync, existsSync, readFileSync, mkdirSync, symlinkSync } from 'fs';
|
||||
import { mkdtempSync, writeFileSync, rmSync, existsSync, readFileSync, mkdirSync, symlinkSync, utimesSync } from 'fs';
|
||||
import { join } from 'path';
|
||||
import { tmpdir } from 'os';
|
||||
|
||||
@@ -16,8 +16,8 @@ const SCRIPT = join(import.meta.dir, '..', '..', 'bin', 'gstack-update-check');
|
||||
let gstackDir: string;
|
||||
let stateDir: string;
|
||||
|
||||
function run(extraEnv: Record<string, string> = {}) {
|
||||
const result = Bun.spawnSync(['bash', SCRIPT], {
|
||||
function run(extraEnv: Record<string, string> = {}, args: string[] = []) {
|
||||
const result = Bun.spawnSync(['bash', SCRIPT, ...args], {
|
||||
env: {
|
||||
...process.env,
|
||||
GSTACK_DIR: gstackDir,
|
||||
@@ -412,4 +412,56 @@ describe('gstack-update-check', () => {
|
||||
expect(exitCode).toBe(0);
|
||||
expect(stdout).toBe('UPGRADE_AVAILABLE 0.3.3 0.4.0');
|
||||
});
|
||||
|
||||
// ─── --force flag tests ──────────────────────────────────────
|
||||
|
||||
test('--force busts fresh UP_TO_DATE cache', () => {
|
||||
writeFileSync(join(gstackDir, 'VERSION'), '0.3.3\n');
|
||||
writeFileSync(join(gstackDir, 'REMOTE_VERSION'), '0.4.0\n');
|
||||
writeFileSync(join(stateDir, 'last-update-check'), 'UP_TO_DATE 0.3.3');
|
||||
|
||||
// Without --force: cache hit, silent
|
||||
const cached = run();
|
||||
expect(cached.stdout).toBe('');
|
||||
|
||||
// With --force: cache busted, re-fetches, finds upgrade
|
||||
const forced = run({}, ['--force']);
|
||||
expect(forced.exitCode).toBe(0);
|
||||
expect(forced.stdout).toBe('UPGRADE_AVAILABLE 0.3.3 0.4.0');
|
||||
});
|
||||
|
||||
test('--force busts fresh UPGRADE_AVAILABLE cache', () => {
|
||||
writeFileSync(join(gstackDir, 'VERSION'), '0.3.3\n');
|
||||
writeFileSync(join(gstackDir, 'REMOTE_VERSION'), '0.3.3\n');
|
||||
writeFileSync(join(stateDir, 'last-update-check'), 'UPGRADE_AVAILABLE 0.3.3 0.4.0');
|
||||
|
||||
// Without --force: cache hit, outputs stale upgrade
|
||||
const cached = run();
|
||||
expect(cached.stdout).toBe('UPGRADE_AVAILABLE 0.3.3 0.4.0');
|
||||
|
||||
// With --force: cache busted, re-fetches, now up to date
|
||||
const forced = run({}, ['--force']);
|
||||
expect(forced.exitCode).toBe(0);
|
||||
expect(forced.stdout).toBe('');
|
||||
const cache = readFileSync(join(stateDir, 'last-update-check'), 'utf-8');
|
||||
expect(cache).toContain('UP_TO_DATE');
|
||||
});
|
||||
|
||||
// ─── Split TTL tests ─────────────────────────────────────────
|
||||
|
||||
test('UP_TO_DATE cache expires after 60 min (not 720)', () => {
|
||||
writeFileSync(join(gstackDir, 'VERSION'), '0.3.3\n');
|
||||
writeFileSync(join(gstackDir, 'REMOTE_VERSION'), '0.4.0\n');
|
||||
writeFileSync(join(stateDir, 'last-update-check'), 'UP_TO_DATE 0.3.3');
|
||||
|
||||
// Set cache mtime to 90 minutes ago (past 60-min TTL)
|
||||
const ninetyMinAgo = new Date(Date.now() - 90 * 60 * 1000);
|
||||
const cachePath = join(stateDir, 'last-update-check');
|
||||
utimesSync(cachePath, ninetyMinAgo, ninetyMinAgo);
|
||||
|
||||
// Cache should be stale at 60-min TTL, re-fetches and finds upgrade
|
||||
const { exitCode, stdout } = run();
|
||||
expect(exitCode).toBe(0);
|
||||
expect(stdout).toBe('UPGRADE_AVAILABLE 0.3.3 0.4.0');
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user