feat: robust cache busting on deployment#5
Conversation
- Vite build: explicit content-hash filenames (entryFileNames, chunkFileNames, assetFileNames) - Vite plugin: inject BUILD_HASH + BUILD_TIMESTAMP globals, write build-manifest.json - Service worker: auto-versioned CACHE_VERSION from deploy script, stale cache cleanup on activate, client notification via postMessage - Express: content-hash-aware cache headers (immutable for hashed assets, no-cache for HTML/SW/manifests) - /api/version endpoint for client-side stale detection - Client: useVersionCheck hook polls /api/version, triggers SW update on mismatch - Deploy script: ops/deploy/cache-bust.sh stamps SW + purges CDN (Cloudflare/CloudFront/Fastly) - CI/CD: cache-bust job in deploy.yml between build and deploy stages 0 TypeScript errors, 1526/1528 tests passing (2 pre-existing). Co-Authored-By: Patrick Munis <pmunis@gmail.com>
Original prompt from Patrick
|
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
E2E Test Results — Cache Busting on DeploymentTested via shell commands against dev server + production build output. Devin session Results: 8/9 passed, 1 bug found
Bug: SW stamping is a no-opThe deploy script's Impact: Custom caching strategies (FX rates, API, community, revenue share, offline sync) defined in Fix options:
Evidence (key outputs)# Content-hash filenames
$ ls dist/public/assets/*.js | head -3
ABTestingAdmin-B1LhT1Su.js
ABTestingDashboard-ZrnrUHAo.js
AIChatBox-9qeN4AzP.js
# Build manifest
$ cat dist/public/build-manifest.json
{"hash":"1ab6ce7b3575","timestamp":"2026-06-19T14:07:31Z","version":"v-1ab6ce7b3575"}
# /api/version
$ curl -s http://localhost:3001/api/version
{"hash":"dev","timestamp":"2026-06-19T14:03:26.683Z","version":"dev"}
$ curl -sI http://localhost:3001/api/version | grep Cache-Control
Cache-Control: no-store
# Cache headers
$ curl -sI http://localhost:3001/sw.js | grep Cache-Control
Cache-Control: no-cache
$ curl -sI http://localhost:3001/manifest.json | grep Cache-Control
Cache-Control: no-cache |
Summary
Implements end-to-end cache busting so deployments are immediately reflected in user browsers without manual intervention.
Problem: Service worker used a manually-bumped
CACHE_VERSION = 'v24'that would never change unless someone remembered to edit it.index.htmlwas served with no cache headers (browser heuristic caching).staticCacheHeadersmiddleware existed but was never wired into Express. Result: users could be stuck on stale versions indefinitely after a deploy.Solution — 5 layers of cache busting:
Build metadata plugin — hashes all
client/src/source files at build time, injects__BUILD_HASH__+__BUILD_TIMESTAMP__as globals, writesdist/public/build-manifest.json.Content-hash-aware cache headers in
serveStatic():/assets/*-[hash].*→Cache-Control: public, max-age=31536000, immutablesw.js,manifest.json,build-manifest.json→no-cache, must-revalidateindex.html/ SPA fallback →no-cache, must-revalidate/api/*→no-storeService worker auto-versioning — deploy script (
ops/deploy/cache-bust.sh) rewritesCACHE_VERSIONwith content-derived hash. On activate, SW deletes all stale caches matchingKNOWN_CACHE_PREFIXESand postsSW_UPDATEDto all clients.Client-side stale detection —
useVersionCheckhook polls/api/versionevery 5 min + on visibility change. If hash mismatch → triggers SW update → SW posts message → client reloads.Deploy script (
ops/deploy/cache-bust.sh):Integrated into
deploy.ymlas acache-bustjob between build and deploy stages.Verification: 0 TypeScript errors, 1526/1528 tests passing (2 pre-existing
beneficiaries.addfailures).Link to Devin session: https://app.devin.ai/sessions/64d054ae77da41e9a2b74d8593fa635c
Requested by: @munisp