Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Meticulous session recorder — https://app.meticulous.ai/docs/recorder-installation
# Project ID: Meticulous Dashboard → Project Settings
NEXT_PUBLIC_METICULOUS_PROJECT_ID=

# GitHub Actions only (Repository Settings → Secrets → Actions)
# METICULOUS_API_TOKEN=
54 changes: 54 additions & 0 deletions .github/workflows/meticulous.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# Meticulous visual regression tests — https://app.meticulous.ai/docs/github-actions-v2
# Required secret: METICULOUS_API_TOKEN (Meticulous Dashboard → Project Settings)
name: Meticulous Tests

on:
push:
branches: [main]
pull_request: {}
workflow_dispatch: {}

permissions:
actions: write
contents: read
issues: write
pull-requests: write
statuses: read

jobs:
test:
runs-on: ubuntu-latest
timeout-minutes: 30

steps:
- uses: actions/checkout@v4
Comment thread
coderabbitai[bot] marked this conversation as resolved.
with:
persist-credentials: false

- name: Setup pnpm
uses: pnpm/action-setup@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '22'

- name: Install dependencies
run: pnpm install --frozen-lockfile

- name: Build Next.js app
run: pnpm build
env:
NODE_ENV: production
Comment on lines +39 to +42

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Pass NEXT_PUBLIC_METICULOUS_PROJECT_ID into the CI build.

app/[locale]/layout.tsx only renders the recorder when NEXT_PUBLIC_METICULOUS_PROJECT_ID is set, but this workflow never exports that variable. The app built here will therefore omit the recorder snippet, so the Meticulous run is exercising an uninstrumented site.

Proposed fix
 jobs:
   test:
     runs-on: ubuntu-latest
     timeout-minutes: 30
+    env:
+      NEXT_PUBLIC_METICULOUS_PROJECT_ID: ${{ vars.NEXT_PUBLIC_METICULOUS_PROJECT_ID }}

     steps:
🤖 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 @.github/workflows/meticulous.yml around lines 37 - 40, The CI build is not
passing NEXT_PUBLIC_METICULOUS_PROJECT_ID, so the recorder gate in
app/[locale]/layout.tsx never activates and the built site is uninstrumented.
Update the Meticulous workflow build step to export
NEXT_PUBLIC_METICULOUS_PROJECT_ID during pnpm build, matching the environment
expected by the layout logic so the recorder snippet is included in the CI-built
app.

METICULOUS_BUILD: 'true'

- name: Start app
run: |
pnpm start &
npx wait-on http://localhost:3009/en --timeout 60000

- name: Run Meticulous tests
uses: alwaysmeticulous/report-diffs-action/cloud-compute@v1
with:
api-token: ${{ secrets.METICULOUS_API_TOKEN }}
app-url: 'http://localhost:3009'
13 changes: 13 additions & 0 deletions app/[locale]/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import { notFound } from 'next/navigation'
import { Analytics } from '@vercel/analytics/next'
import { routing } from '@/i18n/routing'
import { LayoutBody } from '@/components/LayoutBody'
import { METICULOUS_RECORDER_SCRIPT_URL } from '@/lib/constants/meticulous'
import { getMeticulousProjectId } from '@/lib/utils/getMeticulousProjectId'
import '../globals.css'

export const metadata: Metadata = {
Expand Down Expand Up @@ -134,9 +136,20 @@ export default async function LocaleLayout({
// side is the easiest way to get started
const messages = await getMessages()
const timeZone = await getTimeZone()
const meticulousProjectId = getMeticulousProjectId()

return (
<html lang={resolvedLocale} suppressHydrationWarning>
<head>
{meticulousProjectId ? (
// Meticulous requires a synchronous native script before other scripts load.
// eslint-disable-next-line @next/next/no-sync-scripts -- required by Meticulous recorder
<script
data-project-id={meticulousProjectId}
src={METICULOUS_RECORDER_SCRIPT_URL}
/>
) : null}
</head>
<body className="bg-bg-primary text-text-primary antialiased ambient-bg min-h-screen">
<LayoutBody
messages={messages}
Expand Down
3 changes: 3 additions & 0 deletions lib/constants/meticulous.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
/** Meticulous session recorder snippet URL. @see https://app.meticulous.ai/docs/recorder-installation */
export const METICULOUS_RECORDER_SCRIPT_URL =
'https://snippet.meticulous.ai/v1/meticulous.js' as const
10 changes: 10 additions & 0 deletions lib/utils/getMeticulousProjectId.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/**
* Returns the Meticulous project ID when `NEXT_PUBLIC_METICULOUS_PROJECT_ID` is set.
* Configure in `.env.local` for localhost session recording.
*
* @returns Project ID from the Meticulous dashboard, or undefined when not configured
*/
export function getMeticulousProjectId(): string | undefined {
const projectId = process.env.NEXT_PUBLIC_METICULOUS_PROJECT_ID?.trim()
return projectId ? projectId : undefined
}
Loading