mirror of https://github.com/garrytan/gstack.git
fix(update-check): surface failed preamble checks
This commit is contained in:
parent
cab774cced
commit
d7478031f9
|
|
@ -4,6 +4,7 @@
|
|||
# Output (one line, or nothing):
|
||||
# JUST_UPGRADED <old> <new> — marker found from recent upgrade
|
||||
# UPGRADE_AVAILABLE <old> <new> — remote VERSION differs from local
|
||||
# CHECK_FAILED exit=<n> cmd=<cmd> — abnormal script failure; do not infer up-to-date
|
||||
# (nothing) — up to date, snoozed, disabled, or check skipped
|
||||
#
|
||||
# Env overrides (for testing):
|
||||
|
|
@ -11,7 +12,19 @@
|
|||
# GSTACK_REMOTE_URL — override remote VERSION URL (branch-pinned fallback)
|
||||
# GSTACK_REMOTE_REPO — override remote git URL for ls-remote SHA resolution
|
||||
# GSTACK_STATE_DIR — override ~/.gstack state directory
|
||||
set -euo pipefail
|
||||
set -Eeuo pipefail
|
||||
|
||||
report_check_failed() {
|
||||
local status="$?"
|
||||
local cmd="${BASH_COMMAND:-unknown}"
|
||||
cmd="${cmd//$'\n'/ }"
|
||||
echo "CHECK_FAILED exit=$status cmd=$cmd"
|
||||
# Keep CHECK_FAILED as a stdout sentinel, not a failing process status.
|
||||
# The generated preamble runs this command inside an `||` fallback chain;
|
||||
# nonzero here would run the fallback and can duplicate or obscure the marker.
|
||||
exit 0
|
||||
}
|
||||
trap report_check_failed ERR
|
||||
|
||||
GSTACK_DIR="${GSTACK_DIR:-$(cd "$(dirname "$0")/.." && pwd)}"
|
||||
STATE_DIR="${GSTACK_STATE_DIR:-$HOME/.gstack}"
|
||||
|
|
@ -172,12 +185,14 @@ _SUPA_KEY="${GSTACK_SUPABASE_ANON_KEY:-}"
|
|||
_TEL_TIER="$("$GSTACK_DIR/bin/gstack-config" get telemetry 2>/dev/null || true)"
|
||||
if [ -n "$_SUPA_URL" ] && [ -n "$_SUPA_KEY" ] && [ "${_TEL_TIER:-off}" != "off" ]; then
|
||||
_OS="$(uname -s | tr '[:upper:]' '[:lower:]')"
|
||||
curl -sf --max-time 5 \
|
||||
-X POST "${_SUPA_URL}/functions/v1/update-check" \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "apikey: ${_SUPA_KEY}" \
|
||||
-d "{\"version\":\"$LOCAL\",\"os\":\"$_OS\"}" \
|
||||
>/dev/null 2>&1 &
|
||||
(
|
||||
curl -sf --max-time 5 \
|
||||
-X POST "${_SUPA_URL}/functions/v1/update-check" \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "apikey: ${_SUPA_KEY}" \
|
||||
-d "{\"version\":\"$LOCAL\",\"os\":\"$_OS\"}" \
|
||||
>/dev/null 2>&1 || true
|
||||
) &
|
||||
fi
|
||||
|
||||
# Resolve VERSION via a SHA-pinned raw URL. GitHub's branch-raw CDN
|
||||
|
|
@ -241,8 +256,10 @@ fi
|
|||
# Log upgrade_prompted event (only on slow-path fetch, not cached replays)
|
||||
TEL_CMD="$GSTACK_DIR/bin/gstack-telemetry-log"
|
||||
if [ -x "$TEL_CMD" ]; then
|
||||
"$TEL_CMD" --event-type upgrade_prompted --skill "" --duration 0 \
|
||||
--outcome success --session-id "update-$$-$(date +%s)" 2>/dev/null &
|
||||
(
|
||||
"$TEL_CMD" --event-type upgrade_prompted --skill "" --duration 0 \
|
||||
--outcome success --session-id "update-$$-$(date +%s)" 2>/dev/null || true
|
||||
) &
|
||||
fi
|
||||
|
||||
echo "UPGRADE_AVAILABLE $LOCAL $REMOTE"
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
*/
|
||||
|
||||
import { describe, test, expect, beforeEach, afterEach } from 'bun:test';
|
||||
import { mkdtempSync, writeFileSync, rmSync, existsSync, readFileSync, mkdirSync, symlinkSync, utimesSync } from 'fs';
|
||||
import { mkdtempSync, writeFileSync, rmSync, existsSync, readFileSync, mkdirSync, symlinkSync, utimesSync, chmodSync } from 'fs';
|
||||
import { join } from 'path';
|
||||
import { tmpdir } from 'os';
|
||||
|
||||
|
|
@ -476,6 +476,23 @@ describe('gstack-update-check', () => {
|
|||
expect(cache).toContain('UP_TO_DATE');
|
||||
});
|
||||
|
||||
test('--force emits CHECK_FAILED when state files cannot be removed', () => {
|
||||
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');
|
||||
writeFileSync(join(stateDir, 'update-snoozed'), `0.4.0 1 ${nowEpoch()}`);
|
||||
|
||||
chmodSync(stateDir, 0o555);
|
||||
try {
|
||||
const { exitCode, stdout } = run({}, ['--force']);
|
||||
expect(exitCode).toBe(0);
|
||||
expect(stdout).toContain('CHECK_FAILED');
|
||||
expect(stdout).toContain('rm');
|
||||
} finally {
|
||||
chmodSync(stateDir, 0o755);
|
||||
}
|
||||
});
|
||||
|
||||
test('--force clears snooze so user can upgrade after snoozing', () => {
|
||||
writeFileSync(join(gstackDir, 'VERSION'), '0.3.3\n');
|
||||
writeFileSync(join(gstackDir, 'REMOTE_VERSION'), '0.4.0\n');
|
||||
|
|
|
|||
|
|
@ -296,6 +296,49 @@ describe('Update check preamble', () => {
|
|||
expect(result.exitCode).toBe(0);
|
||||
expect(result.stdout.toString().trim()).toBe('UPGRADE_AVAILABLE 0.3.3 0.4.0');
|
||||
});
|
||||
|
||||
test('update check bash block surfaces CHECK_FAILED when script crashes', () => {
|
||||
const tempRoot = fs.mkdtempSync(path.join(ROOT, '.tmp-update-check-'));
|
||||
const gstackDir = path.join(tempRoot, 'gstack');
|
||||
const stateDir = path.join(tempRoot, 'state');
|
||||
const binDir = path.join(gstackDir, 'bin');
|
||||
const fallbackBinDir = path.join(tempRoot, '.agents', 'skills', 'gstack', 'bin');
|
||||
fs.mkdirSync(binDir, { recursive: true });
|
||||
fs.mkdirSync(fallbackBinDir, { recursive: true });
|
||||
fs.mkdirSync(stateDir, { recursive: true });
|
||||
fs.writeFileSync(path.join(gstackDir, 'VERSION'), '0.3.3\n');
|
||||
fs.writeFileSync(path.join(gstackDir, 'REMOTE_VERSION'), '0.4.0\n');
|
||||
fs.writeFileSync(path.join(stateDir, '.codex-desc-healed'), '');
|
||||
fs.writeFileSync(path.join(stateDir, 'just-upgraded-from'), '0.2.0\n');
|
||||
fs.symlinkSync(path.join(ROOT, 'bin', 'gstack-config'), path.join(binDir, 'gstack-config'));
|
||||
fs.symlinkSync(path.join(ROOT, 'bin', 'gstack-update-check'), path.join(binDir, 'gstack-update-check'));
|
||||
fs.symlinkSync(path.join(ROOT, 'bin', 'gstack-update-check'), path.join(fallbackBinDir, 'gstack-update-check'));
|
||||
|
||||
fs.chmodSync(stateDir, 0o555);
|
||||
try {
|
||||
const result = Bun.spawnSync(['bash', '-c',
|
||||
'_UPD=$($GSTACK_BIN/gstack-update-check 2>/dev/null || .agents/skills/gstack/bin/gstack-update-check 2>/dev/null || true); [ -n "$_UPD" ] && echo "$_UPD" || true'
|
||||
], {
|
||||
cwd: tempRoot,
|
||||
env: {
|
||||
...process.env,
|
||||
GSTACK_BIN: binDir,
|
||||
GSTACK_DIR: gstackDir,
|
||||
GSTACK_STATE_DIR: stateDir,
|
||||
GSTACK_REMOTE_URL: `file://${path.join(gstackDir, 'REMOTE_VERSION')}`,
|
||||
},
|
||||
stdout: 'pipe',
|
||||
stderr: 'pipe',
|
||||
});
|
||||
|
||||
expect(result.exitCode).toBe(0);
|
||||
expect(result.stdout.toString().trim()).toContain('CHECK_FAILED');
|
||||
expect(result.stdout.toString().trim()).toContain('rm');
|
||||
} finally {
|
||||
fs.chmodSync(stateDir, 0o755);
|
||||
fs.rmSync(tempRoot, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// --- Part 7: Cross-skill path consistency (A1) ---
|
||||
|
|
|
|||
Loading…
Reference in New Issue