fix: gstack-update-check resolves remote VERSION via SHA-pinned URL

Replace branch-raw fetch with git ls-remote + SHA-pinned raw URL. Add
semver-order guard via sort -V so REMOTE < LOCAL stays silent instead
of emitting a backwards UPGRADE_AVAILABLE line. Fence git ls-remote
with GIT_TERMINAL_PROMPT=0 + 5s low-speed timeout. Honor explicit
GSTACK_REMOTE_URL overrides for test fixtures and private mirrors.

3 new tests cover stale-CDN regression, multi-segment 1.9 vs 1.10
both directions.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Garry Tan
2026-05-13 10:20:31 -07:00
parent 0c88517a0f
commit 610c7cf778
2 changed files with 75 additions and 4 deletions
+34
View File
@@ -496,6 +496,40 @@ describe('gstack-update-check', () => {
// ─── Split TTL tests ─────────────────────────────────────────
// ─── Semver-order guard ─────────────────────────────────────
// When the upstream raw CDN serves a stale (older) VERSION right after a
// release, the script previously emitted a backwards UPGRADE_AVAILABLE
// line. The guard treats REMOTE < LOCAL as up-to-date.
test('remote older than local (stale CDN) → silent, cache UP_TO_DATE', () => {
writeFileSync(join(gstackDir, 'VERSION'), '1.34.0.0\n');
writeFileSync(join(gstackDir, 'REMOTE_VERSION'), '1.33.2.0\n');
const { exitCode, stdout } = run();
expect(exitCode).toBe(0);
expect(stdout).toBe('');
const cache = readFileSync(join(stateDir, 'last-update-check'), 'utf-8');
expect(cache).toContain('UP_TO_DATE 1.34.0.0');
});
test('multi-segment sort: 1.9.0.0 < 1.10.0.0', () => {
writeFileSync(join(gstackDir, 'VERSION'), '1.9.0.0\n');
writeFileSync(join(gstackDir, 'REMOTE_VERSION'), '1.10.0.0\n');
const { stdout } = run();
expect(stdout).toBe('UPGRADE_AVAILABLE 1.9.0.0 1.10.0.0');
});
test('multi-segment reverse sort: 1.10.0.0 > 1.9.0.0 → no rewind', () => {
writeFileSync(join(gstackDir, 'VERSION'), '1.10.0.0\n');
writeFileSync(join(gstackDir, 'REMOTE_VERSION'), '1.9.0.0\n');
const { stdout } = run();
expect(stdout).toBe('');
const cache = readFileSync(join(stateDir, 'last-update-check'), 'utf-8');
expect(cache).toContain('UP_TO_DATE 1.10.0.0');
});
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');