v0.7.7 — two releases shipped to a browser that never received them
buildStamp()'s no-git fallback was the literal "nogit", and the .s9pk Dockerfile copies the tree in without .git — so git rev-parse fails on every packaged build. That string is also the cache-bust key every module URL carries, so v0.7.4, v0.7.5 and v0.7.6 all published ./web/main.js?v=nogit, byte-identical, and returning browsers refetched nothing. v0.7.5's setup screen and v0.7.6's door fix were both correct and neither arrived. The fallback is now the package version plus the build timestamp, always distinct. And serveStatic sent no Cache-Control at all, which is the other half — a cached play.html pins a player to the whole build it names. A request carrying ?v= is now immutable for a year; everything else is no-cache. ?v= rather than "not HTML" because build-web.ts tags the modules and nothing else. Neither half is sufficient alone. 864 tests pass, two new. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AdG46Ja2PEDBkpqiDazMoX
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
b4f09f05cb
commit
af68aac78d
@@ -2628,6 +2628,36 @@ describe('the static build', () => {
|
||||
assert.match(splash, /href="\.\/replays\.html"/, 'the splash does not link to the replays');
|
||||
});
|
||||
|
||||
it('cache-busts with a tag that varies per build even where there is no git', () => {
|
||||
/**
|
||||
* THE BUG THIS PINS COST TWO RELEASES. `buildStamp`'s no-git fallback was the literal `nogit`,
|
||||
* and the `.s9pk` Dockerfile copies the working tree in WITHOUT `.git` — so every packaged
|
||||
* release published `?v=nogit`, byte-identical to the one before it, and a returning player's
|
||||
* browser refetched nothing. v0.7.5's setup screen and v0.7.6's fix to it both installed
|
||||
* correctly on `phoenix.local` and neither reached the browser that asked for them.
|
||||
*
|
||||
* Asserted against the SCRIPT rather than a built page, because the property is about what the
|
||||
* fallback does when `git rev-parse` fails, which a normal build here never exercises.
|
||||
*/
|
||||
const src = readFileSync(join(root, 'scripts/build-web.ts'), 'utf8');
|
||||
const fallback = /let git = ([^;]+);/.exec(src)?.[1] ?? '';
|
||||
assert.ok(fallback !== '', 'the no-git fallback moved and this test cannot see it any more');
|
||||
assert.doesNotMatch(fallback, /^'nogit'$|^"nogit"$/, 'the no-git fallback is a constant again');
|
||||
assert.match(fallback, /Date\.now\(\)/, 'the no-git fallback carries nothing that varies per build');
|
||||
});
|
||||
|
||||
it('lets a build-tagged URL be cached and nothing else', () => {
|
||||
// The other half of the same bug: the pages carry the `?v=` tags but cannot be versioned in
|
||||
// their own URL, so a cached `play.html` pins a player to the whole build it names. Only a
|
||||
// request that actually carries `?v=` may be stored — an untagged image or the replay manifest
|
||||
// has no way to announce a change.
|
||||
const src = readFileSync(join(root, 'src/server/http.ts'), 'utf8');
|
||||
assert.match(src, /'Cache-Control':\s*buildTagged\s*\?/, 'static responses no longer vary their caching');
|
||||
assert.match(src, /immutable/, 'a tagged asset is not allowed to be cached at all');
|
||||
assert.match(src, /serveStatic\(opts\.distDir, url\.pathname, res, url\.searchParams\.has\('v'\)\)/,
|
||||
'the ?v= tag is not reaching serveStatic, so every response falls back to no-cache');
|
||||
});
|
||||
|
||||
it('opens the multiplayer door from the splash, straight into the lobby', () => {
|
||||
// This door sat `disabled` and labelled "Coming soon" from before the server existed until
|
||||
// v0.5.2 — Phases 2-4 built a working lobby and nothing ever linked to it, so a player with a
|
||||
|
||||
Reference in New Issue
Block a user