Skip to content

Fix Open Graph / Twitter image metadata for landing & guides and add tests#2428

Merged
andrew-bierman merged 4 commits into
developmentfrom
codex/fix-open-graph-images-and-add-tests
May 17, 2026
Merged

Fix Open Graph / Twitter image metadata for landing & guides and add tests#2428
andrew-bierman merged 4 commits into
developmentfrom
codex/fix-open-graph-images-and-add-tests

Conversation

@andrew-bierman
Copy link
Copy Markdown
Collaborator

@andrew-bierman andrew-bierman commented May 16, 2026

Motivation

  • Social previews were not receiving resolvable image URLs because the apps relied on implicit paths rather than explicit absolute Open Graph/Twitter image metadata.
  • Ensure crawlers and social platforms can always resolve OG/Twitter images by providing absolute URLs and add tests to prevent regressions.

Description

  • Extracted metadata into apps/landing/app/metadata.ts and apps/guides/app/metadata.ts and added explicit openGraph.images and twitter.images entries using new URL(..., siteConfig.url).toString().
  • Updated apps/landing/app/layout.tsx and apps/guides/app/layout.tsx to re-export the new metadata objects via export const metadata = ....
  • Added unit tests apps/landing/app/layout.metadata.test.ts and apps/guides/app/layout.metadata.test.ts to assert that OG and Twitter image URLs are present and absolute.
  • Ran automatic import/format fixes to satisfy linting rules so tests and pre-commit hooks run cleanly.

Testing

  • Ran bunx vitest run apps/landing/app/layout.metadata.test.ts apps/guides/app/layout.metadata.test.ts, and both tests passed (2 passed).
  • Ran bunx biome check --write ... to auto-fix import ordering and then bunx biome check --no-errors-on-unmatched ..., and lint checks passed.

Codex Task

Summary by CodeRabbit

  • Tests

    • Added metadata verification tests for guides and landing pages to ensure Open Graph and Twitter image URLs are correctly constructed.
  • Refactor

    • Extracted metadata configuration into dedicated modules for guides and landing apps, replacing inline definitions. Metadata now includes properly formatted Open Graph and Twitter card data with absolute URLs.

Review Change Stack

@cloudflare-workers-and-pages
Copy link
Copy Markdown
Contributor

cloudflare-workers-and-pages Bot commented May 16, 2026

Deploying packrat-guides with  Cloudflare Pages  Cloudflare Pages

Latest commit: c627e35
Status: ✅  Deploy successful!
Preview URL: https://8327ab15.packrat-guides-6gq.pages.dev
Branch Preview URL: https://codex-fix-open-graph-images.packrat-guides-6gq.pages.dev

View logs

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 16, 2026

Warning

Rate limit exceeded

@andrew-bierman has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 1 minute and 48 seconds before requesting another review.

You’ve run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 4003f1ab-6203-40a9-8bca-1882a3ab8f08

📥 Commits

Reviewing files that changed from the base of the PR and between 1a4ec3f and c627e35.

📒 Files selected for processing (4)
  • apps/guides/__tests__/layout.metadata.test.ts
  • apps/guides/lib/metadata.ts
  • apps/landing/__tests__/layout.metadata.test.ts
  • apps/landing/lib/metadata.ts

Walkthrough

This PR extracts inline metadata definitions from layout files into separate metadata modules for both guides and landing apps. Each app gains a metadata.ts module that centralizes SEO, Open Graph, and Twitter card configuration, with layout files now re-exporting this shared metadata. Tests verify correct Open Graph and Twitter image URL construction.

Changes

Metadata Extraction

Layer / File(s) Summary
Guides app metadata extraction
apps/guides/app/metadata.ts, apps/guides/app/layout.tsx, apps/guides/app/layout.metadata.test.ts
New guidesMetadata module exports SEO metadata (title templates, description, keywords, authors) and constructs absolute Open Graph and Twitter image URLs using siteConfig.url. Layout imports and re-exports this metadata. Tests verify image URLs and dimensions are correctly set.
Landing app metadata extraction
apps/landing/app/metadata.ts, apps/landing/app/layout.tsx, apps/landing/app/layout.metadata.test.ts
New landingMetadata module exports SEO metadata (title, description, keywords, authors, manifest) and constructs absolute Open Graph and Twitter image URLs from siteConfig. Layout imports and re-exports this metadata. Tests verify image URLs, dimensions (1200×630), and alt text are correctly formed.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Possibly related PRs

  • PackRat-AI/PackRat#2367: Complements this PR by adding OG image generator routes and expanding Guides layout metadata; this PR wires the metadata's image references to those endpoints via URL construction and tests.

Suggested labels

web

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main changes: extracting and fixing OG/Twitter image metadata for landing and guides apps, plus adding tests to prevent regressions.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/fix-open-graph-images-and-add-tests

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@andrew-bierman andrew-bierman marked this pull request as ready for review May 16, 2026 00:31
Copilot AI review requested due to automatic review settings May 16, 2026 00:31
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🤖 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 `@apps/guides/app/layout.metadata.test.ts`:
- Around line 2-3: Replace the relative imports in this test with the TS path
aliases: change the import of siteConfig (currently from '../lib/config') to
import from 'guides-app/lib/config' and change the import of guidesMetadata
(currently exported as guidesMetadata from './metadata') to import from
'guides-app/app/metadata' or the appropriate alias that maps to the metadata
file; update the import statements to use those 'guides-app/*' aliases so the
test imports siteConfig and guidesMetadata (metadata) via the project alias
policy.

In `@apps/guides/app/layout.tsx`:
- Line 8: Replace the relative import of metadata in layout.tsx with the app
alias: update the import statement that currently imports guidesMetadata from
'./metadata' to use 'guides-app/metadata' instead (i.e., import { guidesMetadata
} from 'guides-app/metadata';) so it conforms to the project's import-path
standard.

In `@apps/guides/app/metadata.ts`:
- Line 2: Replace the relative intra-app import in metadata.ts with the
repository path alias: change the import of siteConfig from '../lib/config' to
use the guides-app alias (import { siteConfig } from 'guides-app/lib/config') so
metadata.ts follows the guides-app/* import contract.

In `@apps/landing/app/layout.metadata.test.ts`:
- Around line 2-3: Replace the relative imports in the test so they use the
landing-app path alias: change the import of siteConfig (currently from
'../config/site') to import from 'landing-app/config/site' and change the import
of landingMetadata as metadata (currently from './metadata') to import from
'landing-app/metadata'; update the import statements that reference siteConfig
and landingMetadata to use those alias paths so the test follows the project's
path-alias guideline.

In `@apps/landing/app/layout.tsx`:
- Line 7: The import in layout.tsx currently pulls landingMetadata from a
relative path; update the import to use the landing-app alias so it reads from
"landing-app/metadata" instead of "./metadata". Locate the import statement that
references landingMetadata and replace the module specifier with the aliased
path to comply with the project's import guideline.

In `@apps/landing/app/metadata.ts`:
- Line 2: The import in metadata.ts uses a relative path; replace the relative
import of siteConfig with the landing-app path alias so it reads from the
aliased module (import siteConfig from "landing-app/config/site" or the
equivalent named export), ensuring you reference the same exported symbol
siteConfig and update any named/default import form to match the original export
in config/site.ts.
🪄 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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 78f01911-2d14-4467-958a-7aa4ba9ff336

📥 Commits

Reviewing files that changed from the base of the PR and between 1917ea7 and 1a4ec3f.

📒 Files selected for processing (6)
  • apps/guides/app/layout.metadata.test.ts
  • apps/guides/app/layout.tsx
  • apps/guides/app/metadata.ts
  • apps/landing/app/layout.metadata.test.ts
  • apps/landing/app/layout.tsx
  • apps/landing/app/metadata.ts

Comment thread apps/guides/app/layout.metadata.test.ts Outdated
Comment on lines +2 to +3
import { siteConfig } from '../lib/config';
import { guidesMetadata as metadata } from './metadata';
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

Use guides-app/* aliases in this test file.

Lines [2-3] use relative imports into the guides app; update both to guides-app/* aliases to stay aligned with the TS import policy.

As per coding guidelines **/*.{ts,tsx}: "Use path alias guides-app/* to reference files in apps/guides/*".

🤖 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 `@apps/guides/app/layout.metadata.test.ts` around lines 2 - 3, Replace the
relative imports in this test with the TS path aliases: change the import of
siteConfig (currently from '../lib/config') to import from
'guides-app/lib/config' and change the import of guidesMetadata (currently
exported as guidesMetadata from './metadata') to import from
'guides-app/app/metadata' or the appropriate alias that maps to the metadata
file; update the import statements to use those 'guides-app/*' aliases so the
test imports siteConfig and guidesMetadata (metadata) via the project alias
policy.

Comment thread apps/guides/app/layout.tsx Outdated
Comment thread apps/guides/app/metadata.ts Outdated
@@ -0,0 +1,51 @@
import type { Metadata } from 'next';
import { siteConfig } from '../lib/config';
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

Use the guides-app/* alias for intra-app imports.

Line [2] uses a relative import into apps/guides/*; switch it to the guides-app/* alias to match the repository import contract.

As per coding guidelines **/*.{ts,tsx}: "Use path alias guides-app/* to reference files in apps/guides/*".

🤖 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 `@apps/guides/app/metadata.ts` at line 2, Replace the relative intra-app import
in metadata.ts with the repository path alias: change the import of siteConfig
from '../lib/config' to use the guides-app alias (import { siteConfig } from
'guides-app/lib/config') so metadata.ts follows the guides-app/* import
contract.

Comment on lines +2 to +3
import { siteConfig } from '../config/site';
import { landingMetadata as metadata } from './metadata';
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

Replace relative test imports with landing-app/* aliases.

Lines [2-3] should use the landing app path alias instead of relative imports.

As per coding guidelines **/*.{ts,tsx}: "Use path alias landing-app/* to reference files in apps/landing/*".

🤖 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 `@apps/landing/app/layout.metadata.test.ts` around lines 2 - 3, Replace the
relative imports in the test so they use the landing-app path alias: change the
import of siteConfig (currently from '../config/site') to import from
'landing-app/config/site' and change the import of landingMetadata as metadata
(currently from './metadata') to import from 'landing-app/metadata'; update the
import statements that reference siteConfig and landingMetadata to use those
alias paths so the test follows the project's path-alias guideline.

Comment thread apps/landing/app/layout.tsx Outdated
Comment thread apps/landing/app/metadata.ts Outdated
@@ -0,0 +1,43 @@
import type { Metadata } from 'next';
import { siteConfig } from '../config/site';
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

Switch to landing-app/* alias for siteConfig import.

Line [2] should use the landing app alias instead of a relative path to comply with the import boundary rule.

As per coding guidelines **/*.{ts,tsx}: "Use path alias landing-app/* to reference files in apps/landing/*".

🤖 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 `@apps/landing/app/metadata.ts` at line 2, The import in metadata.ts uses a
relative path; replace the relative import of siteConfig with the landing-app
path alias so it reads from the aliased module (import siteConfig from
"landing-app/config/site" or the equivalent named export), ensuring you
reference the same exported symbol siteConfig and update any named/default
import form to match the original export in config/site.ts.

@cloudflare-workers-and-pages
Copy link
Copy Markdown
Contributor

cloudflare-workers-and-pages Bot commented May 16, 2026

Deploying packrat-landing with  Cloudflare Pages  Cloudflare Pages

Latest commit: c627e35
Status: ✅  Deploy successful!
Preview URL: https://3350147c.packrat-landing.pages.dev
Branch Preview URL: https://codex-fix-open-graph-images.packrat-landing.pages.dev

View logs

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR extracts root metadata for the landing and guides Next.js apps, adds explicit Open Graph/Twitter image metadata, and adds regression tests for those metadata objects.

Changes:

  • Added metadata.ts modules for landing and guides.
  • Updated layouts to re-export those metadata objects.
  • Added Vitest tests asserting OG/Twitter image entries.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
apps/landing/app/metadata.ts Adds landing metadata with explicit OG/Twitter images.
apps/landing/app/layout.tsx Re-exports extracted landing metadata.
apps/landing/app/layout.metadata.test.ts Tests landing metadata image fields.
apps/guides/app/metadata.ts Adds guides metadata with explicit OG/Twitter images.
apps/guides/app/layout.tsx Re-exports extracted guides metadata.
apps/guides/app/layout.metadata.test.ts Tests guides metadata image fields.
Comments suppressed due to low confidence (3)

apps/landing/app/metadata.ts:35

  • This Twitter image uses the same og-image.jpg URL, but that asset is not present in the landing app; the app already defines a generated app/twitter-image.tsx route instead. The explicit Twitter metadata should reference a URL that the static export actually serves.
    images: [new URL(siteConfig.ogImage, siteConfig.url).toString()],

apps/landing/app/layout.metadata.test.ts:18

  • This test only inspects the exported metadata object, but the app also has app/opengraph-image.tsx and app/twitter-image.tsx; Next.js gives file-based metadata routes precedence when resolving the actual tags. Rendering/resolving the metadata output would catch whether the deployed OG/Twitter tags use the intended absolute URLs, while this assertion can pass even if production metadata is overridden.
    expect(metadata.openGraph?.images).toEqual([
      {
        url: expectedImageUrl,
        width: 1200,
        height: 630,
        alt: siteConfig.name,
      },
    ]);

    expect(metadata.twitter?.images).toEqual([expectedImageUrl]);

apps/guides/app/layout.metadata.test.ts:18

  • This test only inspects the exported metadata object, but the app also has app/opengraph-image.tsx and app/twitter-image.tsx; Next.js gives file-based metadata routes precedence when resolving the actual tags. Rendering/resolving the metadata output would catch whether the deployed OG/Twitter tags use the intended absolute URLs, while this assertion can pass even if production metadata is overridden.
    expect(metadata.openGraph?.images).toEqual([
      {
        url: expectedImageUrl,
        width: 1200,
        height: 630,
        alt: 'PackRat Guides | Hiking & Outdoor Adventures',
      },
    ]);

    expect(metadata.twitter?.images).toEqual([expectedImageUrl]);

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +5 to +6
describe('landing metadata', () => {
it('includes absolute Open Graph and Twitter image URLs', () => {
Comment on lines +5 to +6
describe('guides metadata', () => {
it('includes absolute Open Graph and Twitter image URLs', () => {
Comment thread apps/landing/app/metadata.ts Outdated
Comment on lines +21 to +23
images: [
{
url: new URL(siteConfig.ogImage, siteConfig.url).toString(),
Comment thread apps/guides/app/metadata.ts Outdated
Comment on lines +30 to +32
images: [
{
url: new URL('/opengraph-image', siteConfig.url).toString(),
andrew-bierman and others added 3 commits May 15, 2026 18:51
Resolve conflicts in apps/guides/app/layout.tsx and apps/landing/app/layout.tsx
by adopting development's lib/metadata.ts location while preserving this PR's
absolute-URL improvements (using Next metadata image routes /opengraph-image.png
and /twitter-image.png as canonical OG/Twitter URLs via new URL(...)).

- Drop redundant app/metadata.ts files (dev moved canonical to lib/metadata.ts)
- Update lib/metadata.ts in both guides + landing with absolute image URLs
- Update layout.metadata.test.ts imports to point at lib/metadata
The guides + landing vitest configs scope tests to __tests__/**, so the
layout.metadata.test.ts files added under app/ were silently skipped.
@cloudflare-workers-and-pages
Copy link
Copy Markdown
Contributor

cloudflare-workers-and-pages Bot commented May 16, 2026

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
packrat-admin c627e35 Commit Preview URL

Branch Preview URL
May 16 2026, 10:29 PM

@github-actions github-actions Bot added the web label May 16, 2026
@andrew-bierman
Copy link
Copy Markdown
Collaborator Author

Rebased onto current origin/development.

Conflicts resolved:

  • apps/guides/app/layout.tsx and apps/landing/app/layout.tsx — development moved canonical metadata from app/metadata.ts to lib/metadata.ts and added Next image route handlers (opengraph-image.tsx, twitter-image.tsx) plus the OG-image generation scripts.
  • Adopted development's lib/metadata.ts location while preserving this PR's improvements: absolute OG/Twitter URLs constructed via new URL('/opengraph-image.png', siteConfig.url) / new URL('/twitter-image.png', siteConfig.url) (referencing the Next metadata image routes rather than static files).
  • Removed the now-redundant apps/{guides,landing}/app/metadata.ts files.

Additional fix: moved this PR's layout.metadata.test.ts files from app/ to __tests__/ since the vitest configs scope tests to __tests__/** (they were silently skipped at the old location).

Verification (locally, in worktree):

  • bun check-types — passes (0 errors)
  • bun check — Biome clean
  • bunx vitest run __tests__/layout.metadata.test.ts in both apps/guides and apps/landing — passes
  • bun run generate-og-images in apps/guides — generated 1 root + 39 post OG images successfully

The earlier Cloudflare Pages failures were on the pre-rebase state which referenced /og-image.png without development's image generation in place. With the merged Next image routes + OG image scripts (and absolute URLs now pointing at those routes), Pages builds should pass.

Push: c627e35ab

@andrew-bierman andrew-bierman merged commit 4c342a8 into development May 17, 2026
13 of 14 checks passed
@andrew-bierman andrew-bierman deleted the codex/fix-open-graph-images-and-add-tests branch May 17, 2026 00:43
andrew-bierman added a commit that referenced this pull request May 17, 2026
…(after #2363/#2428 landed)

Conflicts:
- packages/api/src/routes/trips/index.ts: dev added inline LocationSchema/CreateTripRequestSchema/UpdateTripRequestSchema; this PR already imports the equivalents from @packrat/schemas/trips. Kept HEAD (the extracted-schemas import + body refs to CreateTripBodySchema/UpdateTripBodySchema).
- bun.lock: regenerated via bun install.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
andrew-bierman added a commit that referenced this pull request May 17, 2026
#2428 landed)

Conflicts:
- apps/expo/features/packs/hooks/usePackOwnershipCheck.ts: HEAD used new single-object obs({store,id}) signature with .peek() (non-reactive); dev rewrote to use$(() => !!obs(packsStore, id).get()) for reactivity. Blended: kept dev's reactive use$ pattern, applied HEAD's single-object obs call form.

Post-merge fixes (dev added new files that violated max-params=1):
- apps/expo/atoms/atomWithSecureStorage.web.ts: refactor to {key, initialValue} to match native sibling.
- apps/expo/features/packs/utils/uploadImage.web.ts: refactor uploadImage/getPresignedUrl/urlToBlob to single-object params; align with native sibling signature.
- apps/expo/lib/utils/ImageCacheManager.web.ts: refactor cacheRemoteImage and cacheLocalTempImage to single-object params, matching native ImageCacheManager.ts.
- scripts/lint/no-owned-max-params.ts: add /playwright/ to EXCLUDED_PATH_PARTS (parity with /test/ and /__tests__/; playwright fixtures are test code with signatures dictated by Playwright).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants