Skip to content

fix(shell): resolve og:image through the asset pipeline; image is site content#50

Merged
mhenrixon merged 1 commit into
mainfrom
fix-og-image-site-owned
Jul 3, 2026
Merged

fix(shell): resolve og:image through the asset pipeline; image is site content#50
mhenrixon merged 1 commit into
mainfrom
fix-og-image-site-owned

Conversation

@mhenrixon

Copy link
Copy Markdown
Owner

The bug

The og:image on the live docs site pointed at https://docs-kit.zoolutions.llc/og/og.png — which 404s. Propshaft serves the digested asset under /assets/…, not the raw logical path, so a shared link showed a broken image.

Root cause

Two mistakes in the original SEO feature (#49):

  1. Wrong URL construction. DocsUI::MetaTags base-joined the raw og_image path onto the host ("#{base}/#{og_image}") instead of resolving it through the Rails asset pipeline. The result was never a served URL.
  2. Wrong ownership. The gem shipped a default OG image and defaulted og_image to "og/og.png". But the social-share image is each site's own branding — not docs-kit's to render. A site that predates the feature (like the deployed dogfood site) had no such asset precompiled at all, so even a correct URL would 404.

The fix

  • Resolve through the asset pipeline. A relative og_image now goes through image_url → the digested /assets/og/og-<digest>.png URL Propshaft actually serves. An absolute URL passes through untouched. image_url uses Propshaft's Static resolver (needs the asset precompiled), so a misconfigured og_image fails loudly at deploy (assets:precompile) rather than shipping a silent 404 — deliberately not rescued.
  • The image is site content. Removed the gem-shipped image, the generator's copy_file, and the guard spec. c.seo.og_image now defaults to nil → unset emits no og:image tag (a valid card, never a 404, same shape as favicon/robots/theme-color). The docs_kit:og task writes into the site's app/assets/images/.
  • The dogfood /docs site gets its own OG image + c.seo config, so its live card works once deployed.

Why the original tests missed it

The gem's isolated component specs can't exercise the asset pipeline — only a booted app has the Propshaft resolver that turns a logical path into a served /assets URL. So this adds a real integration test in the dogfood site.

Test plan

  • New: docs/test/integration/seo_meta_tags_test.rb — boots the real docs app, GET /, and asserts og:image is a /assets URL and that GETting it returns 200 image/png. That last assertion is the one that reproduces and guards the production 404. Self-precompiles in setup, so it's run-order independent.
  • Updated spec/docs_ui/meta_tags_spec.rb: og:image resolves via the (stubbed) pipeline, is omitted when unset, absolute passes through, and the raw path is never emitted.
  • bundle exec rake714 examples, 0 failures (coverage 94.48%), rubocop clean.
  • docs/ integration test → 4 runs, 22 assertions, 0 failures from a clean state.
  • gem build --strict → no image shipped (gem shrank 171K → 134K); component/task/generator still present.
  • Live-parity render: the /docs landing now emits og:image = https://docs-kit.zoolutions.llc/assets/og/og-<digest>.png, which serves 200.

Fixes the 404 reported on https://docs-kit.zoolutions.llc/og/og.png. Refs #48.

https://claude.ai/code/session_01FPQb6z3YwcKRMbvoJhdxnX

…e content

The og:image tag pointed at the raw config path
(https://docs-kit.zoolutions.llc/og/og.png), which 404s — Propshaft serves the
DIGESTED asset under /assets, not the logical path. A shared link showed a broken
image.

Root cause: DocsUI::MetaTags base-joined the raw og_image path onto the host
instead of resolving it through the Rails asset pipeline. And the gem shipped a
default OG image + defaulted og_image to "og/og.png" — but the social-share image
is each SITE's own branding, not the gem's, and a stale site had no such asset
precompiled at all.

Fix:
- A relative og_image now resolves via image_url → the digested
  /assets/og/og-<digest>.png URL Propshaft actually serves. Absolute URLs pass
  through. image_url uses the Static resolver (needs the asset precompiled), so a
  misconfigured og_image fails loudly at deploy (assets:precompile) rather than
  shipping a silent 404 — NOT rescued.
- The OG image is SITE content: removed the gem-shipped image + the generator
  copy + the guard spec. c.seo.og_image now defaults to nil → unset emits NO
  og:image (a valid card, never a 404). docs_kit:og writes into the SITE's
  app/assets/images/.
- The dogfood /docs site gets its own OG image + c.seo config, so its live card
  works once deployed (verified: og:image renders as the /assets digest URL).

Test coverage:
- New booted-app integration test in the dogfood site
  (docs/test/integration/seo_meta_tags_test.rb): GET /, assert og:image is a
  /assets URL AND that GETting it returns 200 image/png — the exact regression
  that would have caught this. Self-precompiles so it's run-order independent.
  Isolated component specs can't catch a pipeline/precompile break; only a booted
  app can.
- Updated meta_tags_spec: og:image resolves via the (stubbed) pipeline, is
  omitted when unset, absolute passes through, never emits the raw path.

## Verification
- [x] bundle exec rake (714 examples, 0 failures; coverage 94.48%)
- [x] bundle exec rubocop — no offenses
- [x] docs/ integration test — 4 runs, 22 assertions, 0 failures (from clean state)
- [x] gem build --strict — no image shipped; component/task/generator present
- [x] live-parity render — og:image = https://…/assets/og/og-<digest>.png (served 200)

Refs #48

Claude-Session: https://claude.ai/code/session_01FPQb6z3YwcKRMbvoJhdxnX
@mhenrixon mhenrixon self-assigned this Jul 3, 2026
@mhenrixon mhenrixon added the bug Something isn't working label Jul 3, 2026
@mhenrixon mhenrixon merged commit 13095ca into main Jul 3, 2026
4 checks passed
@mhenrixon mhenrixon deleted the fix-og-image-site-owned branch July 4, 2026 14:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant