🐛 Fixed post card assets going missing until restart (in-memory serving)#29128
🐛 Fixed post card assets going missing until restart (in-memory serving)#29128sagzy wants to merge 1 commit into
Conversation
ref https://linear.app/ghost/issue/ONC-1865 - cards.min.css/js were built into content/public and served by reading them back from disk, so a wiped or unwritable content folder meant a 404 on every request until reboot - the assets are rebuilt from theme sources on every boot anyway, so the disk copy is not a real cache; the serving middleware already held the body in memory permanently - card assets are now kept in memory by the assets service and served from there; the disk write is best-effort back-compat only, so write failures and deleted files can no longer take the assets offline - build policy (single-flight, retry backoff, rebuild-on-theme-change) now lives in one place on the service instead of per-route Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Important Review skippedDraft detected. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
| Command | Status | Duration | Result |
|---|---|---|---|
nx run ghost:test:ci:integration |
✅ Succeeded | 2m 40s | View ↗ |
nx run ghost:test:integration |
✅ Succeeded | 2m 54s | View ↗ |
nx run ghost:test:legacy |
✅ Succeeded | 2m 51s | View ↗ |
nx run ghost:test:e2e |
✅ Succeeded | 2m 26s | View ↗ |
nx run-many --target=build --projects=tag:publi... |
✅ Succeeded | <1s | View ↗ |
nx run-many -t test:unit -p ghost |
✅ Succeeded | 29s | View ↗ |
nx run-many -t lint -p ghost |
✅ Succeeded | 33s | View ↗ |
nx run @tryghost/admin:build |
✅ Succeeded | 9s | View ↗ |
Additional runs (2) |
✅ Succeeded | ... | View ↗ |
💡 Verify your cache is correct by running tasks in a sandbox. Read docs ↗
☁️ Nx Cloud last updated this comment at 2026-07-06 09:49:10 UTC

ref https://linear.app/ghost/issue/ONC-1865
Problem
/public/cards.min.cssand/public/cards.min.jsare minified at runtime intocontent/public/and served by reading them back from disk. If the built files go missing at runtime, or the content folder is unwritable (EACCES), every request 404s until the process restarts — ~486 production sites accumulated in this state during Jun 25–29.Solution: serve card assets from memory
The disk round-trip buys nothing: the assets are rebuilt from theme sources on every boot anyway, and the serving middleware already held the response body in memory permanently. The disk copy was never a real cache — it was just the failure surface.
CardAssets.load()now builds in memory (Minifier.minifyInMemory()) and stores the outputs on the service;getContent(filename)is the source of truth for serving. A successful build is always servable —ENOENT/EACCESon the destination can no longer take the assets offline. The disk write still happens as best-effort back-compat, with failures logged and ignored.createInMemoryAssetMiddleware()that serves straight from the service with the same header shape as before (md5 ETag, Content-Length, Cache-Control), memoized per build.AssetsMinificationBase.ensureLoaded(): concurrent callers join a single in-flight build; a 10s retry backoff stops a persistently failing build from minifying per request;invalidate()(theme activation) no longer forgets an in-flight build — a generation counter re-runsload()with the new config instead of allowing two concurrent minifier runs.serveMiddleware()no longer lets a rejectedload()escape as an unhandled rejection (which left requests hanging under Express 4) — this also fixes the hang forAdminAuthAssets.Testing
ensureLoaded()(join-in-flight, backoff + expiry, invalidate resets backoff, mid-build invalidate re-runs load with no concurrency),CardAssetsin-memory outputs (getContent,hasFile, ready + servable even when the disk write EACCESes), and the in-memory middleware (headers/ETag,{{blog-url}}replacement, 404 when the build legitimately produces no file, build trigger when not ready).ghost/coreunit suite: 7187 passed; the 3 failures are pre-existing onmain(verified by baseline run).🤖 Generated with Claude Code