chore(og): add og generation and backfilll old ones#489
Conversation
Screenshots each doc's primary Storybook story into a branded 1200x630 card,
uploads to Cloudflare R2 via wrangler, and writes a committed slug->{url,hash}
manifest read at build time. Run locally (pnpm og:build), not in CI.
Keys are content-addressed (og/<cat>/<name>.<v>.<hash>.<ext>) so a changed
component yields a new immutable URL the CDN/scrapers refetch. Still PNG by
default; --animate opts into looping GIFs where motion is detected. Hybrid
template fallback for components that don't capture well as a still. Full runs
prune stale manifest entries; preflight checks ffmpeg/wrangler and the command
exits non-zero on failures.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Look up the per-component OG image by slug and fall back to the site-wide image for slugs without one (and for local builds where the manifest is empty). Also set the OG/Twitter image alt to the doc title. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add a "Social (OG) image" section to the component contributing guide covering the per-component flow (storybook:build, --only with --dry-run, then upload), the comma-separated multi-component form, content-hash skipping, and that the card is a still PNG by default with --animate opting into a looping GIF. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Render every card into .og-out/og/<key> (the local path mirrors the R2 key) and write the manifest URLs, but skip the wrangler upload — for a first bulk backfill where the objects are uploaded to R2 by hand. Sync .og-out/og/ to the bucket root and the committed manifest already points at the matching keys. Skips the wrangler auth preflight; documents that objects must be uploaded before the manifest is deployed (a committed entry is used even if its URL 404s). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add content-addressed URLs for all 145 component docs, plus og:migrate and og:upload scripts to re-key flat uploads and bulk-sync to R2. Co-authored-by: Cursor <cursoragent@cursor.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
📝 WalkthroughWalkthroughAdds a full Open Graph image pipeline: deterministic card templates, Playwright rendering (still/GIF), R2 upload with content-addressed keys and manifest tracking, CLI scripts for build/upload/migrate, a preview server and font sync, and per-doc metadata integration in the docs page. ChangesOpen Graph Image Generation System
Estimated code review effort🎯 4 (Complex) | ⏱️ ~50 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 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 |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (3)
scripts/og-template-preview.mjs (1)
46-46: ⚡ Quick winExpand preview coverage to all supported branded flavors.
The harness currently skips
d-h, so regressions in generative variants won’t be visible during design review.Proposed change
- for (const flavor of ["a", "b", "c"]) { + for (const flavor of ["a", "b", "c", "d", "e", "f", "g", "h"]) {🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@scripts/og-template-preview.mjs` at line 46, The preview harness currently only iterates flavors ["a","b","c"] in the for loop (for (const flavor of ["a", "b", "c"])), skipping branded variants d–h; update that loop to cover all supported branded flavors (e.g., ["a","b","c","d","e","f","g","h"]) or, better, replace the hardcoded array with the canonical SUPPORTED_FLAVORS (or equivalent constant) so the preview generates outputs for d through h as well.scripts/build-og-images.mjs (1)
665-709: 💤 Low valueWorker queue is safe but could be clearer with explicit atomic dequeue.
The
queue.shift()inside the async worker loop is safe because JavaScript's event loop ensures the synchronousshift()completes atomically before anyawaityields. However, this relies on implicit JS semantics that may confuse readers.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@scripts/build-og-images.mjs` around lines 665 - 709, Extract the synchronous dequeue operation into a small helper and use it at the top of the worker loop to make the atomic pop explicit: add a function like getNext() { return queue.shift(); } and in the worker replace "const item = queue.shift();" with "const item = getNext(); if (!item) break;" (keep the rest of the loop using hashItem, renderWithRetry, contentAddressedKey, uploadToR2, manifest, etc.). This makes the dequeue intent explicit and avoids readers having to reason about implicit JS event-loop atomicity.scripts/upload-og-r2.mjs (1)
30-55: 💤 Low valueMinor inconsistency: uses
npx wranglerwhilebuild-og-images.mjsresolves to local binary.
build-og-images.mjschecks fornode_modules/.bin/wranglerand falls back towrangler, while this script always usesnpx wrangler. Both work, but using the local binary when available (as in the other script) is slightly faster and more deterministic.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@scripts/upload-og-r2.mjs` around lines 30 - 55, The uploadOne function currently invokes the wrangler CLI via "npx wrangler" which differs from build-og-images.mjs's approach of preferring the local node_modules binary; update uploadOne to resolve and prefer the local binary (same logic as build-og-images.mjs) before falling back to "wrangler" so pexec is called with the resolved wrangler path (referencing uploadOne and the pexec call), ensuring deterministic, faster execution when node_modules/.bin/wrangler exists.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@scripts/lib/og-template.mjs`:
- Around line 34-36: The FONTS constant in scripts/lib/og-template.mjs currently
embeds external Google Fonts links which make OG rendering network-dependent;
replace that by removing the preconnect and stylesheet URLs and instead define
local `@font-face` declarations in FONTS that point to self-hosted font files
checked into the repo (e.g., WOFF/WOFF2 assets you add), update any references
to the Outfit/Young Serif/IBM Plex Sans families so they resolve to those local
faces, and ensure the font files are copied into the build/output path used by
the OG renderer; also remove reliance on external fonts in
scripts/og-template-preview.mjs and scripts/build-og-images.mjs by ensuring
document.fonts.ready will resolve with the self-hosted fonts.
---
Nitpick comments:
In `@scripts/build-og-images.mjs`:
- Around line 665-709: Extract the synchronous dequeue operation into a small
helper and use it at the top of the worker loop to make the atomic pop explicit:
add a function like getNext() { return queue.shift(); } and in the worker
replace "const item = queue.shift();" with "const item = getNext(); if (!item)
break;" (keep the rest of the loop using hashItem, renderWithRetry,
contentAddressedKey, uploadToR2, manifest, etc.). This makes the dequeue intent
explicit and avoids readers having to reason about implicit JS event-loop
atomicity.
In `@scripts/og-template-preview.mjs`:
- Line 46: The preview harness currently only iterates flavors ["a","b","c"] in
the for loop (for (const flavor of ["a", "b", "c"])), skipping branded variants
d–h; update that loop to cover all supported branded flavors (e.g.,
["a","b","c","d","e","f","g","h"]) or, better, replace the hardcoded array with
the canonical SUPPORTED_FLAVORS (or equivalent constant) so the preview
generates outputs for d through h as well.
In `@scripts/upload-og-r2.mjs`:
- Around line 30-55: The uploadOne function currently invokes the wrangler CLI
via "npx wrangler" which differs from build-og-images.mjs's approach of
preferring the local node_modules binary; update uploadOne to resolve and prefer
the local binary (same logic as build-og-images.mjs) before falling back to
"wrangler" so pexec is called with the resolved wrangler path (referencing
uploadOne and the pexec call), ensuring deterministic, faster execution when
node_modules/.bin/wrangler exists.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: af304139-c7af-4968-a819-79390658b68f
📒 Files selected for processing (9)
app/(main)/docs/[[...slug]]/page.tsxcontent/docs/contributing/components.mdxlib/og-manifest.jsonpackage.jsonscripts/build-og-images.mjsscripts/lib/og-template.mjsscripts/lib/preview-server.mjsscripts/og-template-preview.mjsscripts/upload-og-r2.mjs
Resolve CodeQL path traversal in the preview server and serve Outfit, Young Serif, and IBM Plex Sans from public/og-fonts/ so OG renders no longer depend on Google Fonts CDN. Co-authored-by: Cursor <cursoragent@cursor.com>
Deploying animata with
|
| Latest commit: |
348fd4d
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://8bec29fc.animata.pages.dev |
| Branch Preview URL: | https://feat-og-image-generation.animata.pages.dev |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@scripts/lib/preview-server.mjs`:
- Around line 47-48: The request URL decoding in the createServer callback uses
decodeURIComponent((req.url || "/").split("?")[0]) which will throw on malformed
percent-escapes; wrap that decode in a try/catch inside the createServer handler
(or in startPreviewServer if that wrapper exists), and on catch fall back to a
safe path (e.g., raw pathname or "/") and/or return a 400 response rather than
letting the exception bubble and crash the preview server; update the reference
to urlPath used later in the handler so it uses the safely-decoded value.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 19acdffa-4c9e-4c77-82a1-be3fe700ac3e
⛔ Files ignored due to path filters (9)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yamlpublic/og-fonts/ibm-plex-sans-latin-400-normal.woff2is excluded by!**/*.woff2public/og-fonts/ibm-plex-sans-latin-500-normal.woff2is excluded by!**/*.woff2public/og-fonts/ibm-plex-sans-latin-600-normal.woff2is excluded by!**/*.woff2public/og-fonts/outfit-latin-400-normal.woff2is excluded by!**/*.woff2public/og-fonts/outfit-latin-500-normal.woff2is excluded by!**/*.woff2public/og-fonts/outfit-latin-600-normal.woff2is excluded by!**/*.woff2public/og-fonts/outfit-latin-700-normal.woff2is excluded by!**/*.woff2public/og-fonts/young-serif-latin-400-normal.woff2is excluded by!**/*.woff2
📒 Files selected for processing (7)
content/docs/contributing/components.mdxpackage.jsonscripts/build-og-images.mjsscripts/lib/og-template.mjsscripts/lib/preview-server.mjsscripts/og-template-preview.mjsscripts/sync-og-fonts.mjs
✅ Files skipped from review due to trivial changes (2)
- scripts/sync-og-fonts.mjs
- content/docs/contributing/components.mdx
🚧 Files skipped from review as they are similar to previous changes (3)
- scripts/og-template-preview.mjs
- scripts/lib/og-template.mjs
- scripts/build-og-images.mjs
Use URL pathname parsing with 400 on malformed input, path.relative containment, and realpathSync before serving files from the localhost OG preview server. Co-authored-by: Cursor <cursoragent@cursor.com>
Summary by CodeRabbit