fix(sdk): make generated SDK docs reproducible to unblock CI - #231
Conversation
The `sdk (24)` CI job runs `npm run docs` then asserts `git diff --exit-code -- docs/sdk/reference`. PR #219 hand-edited the TypeDoc-generated MDX to add SEO titles/descriptions, but the generator (typedoc + postprocess-generated-docs.mjs) couldn't reproduce them, so the up-to-date guard has failed on every run since May 18. Restore reproducibility without losing the SEO work: - Add seo-overrides.json — the 46 hand-authored title/description pairs, keyed by route-relative .mdx path. - Teach postprocess-generated-docs.mjs to apply those overrides when writing frontmatter, so `npm run docs` regenerates the committed files byte-for-byte. Verified: `npm run docs` now leaves docs/sdk/reference with no diff. Edit seo-overrides.json (not the generated MDX) for future SEO tweaks. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
| const SEO_OVERRIDES = fs.existsSync(seoOverridesPath) | ||
| ? JSON.parse(fs.readFileSync(seoOverridesPath, 'utf8')) | ||
| : {}; |
There was a problem hiding this comment.
If
seo-overrides.json contains a syntax error (stray comma, misquoted string, etc.), JSON.parse throws a bare SyntaxError with a character-offset message and no filename context, making it hard to diagnose during CI. Wrapping with a try-catch surfaces the file path alongside the parse error.
| const SEO_OVERRIDES = fs.existsSync(seoOverridesPath) | |
| ? JSON.parse(fs.readFileSync(seoOverridesPath, 'utf8')) | |
| : {}; | |
| const SEO_OVERRIDES = (() => { | |
| if (!fs.existsSync(seoOverridesPath)) return {}; | |
| try { | |
| return JSON.parse(fs.readFileSync(seoOverridesPath, 'utf8')); | |
| } catch (err) { | |
| throw new Error(`Failed to parse ${seoOverridesPath}: ${err.message}`); | |
| } | |
| })(); |
Prompt To Fix With AI
This is a comment left during a code review.
Path: sdks/typescript-sdk/scripts/postprocess-generated-docs.mjs
Line: 27-29
Comment:
If `seo-overrides.json` contains a syntax error (stray comma, misquoted string, etc.), `JSON.parse` throws a bare `SyntaxError` with a character-offset message and no filename context, making it hard to diagnose during CI. Wrapping with a try-catch surfaces the file path alongside the parse error.
```suggestion
const SEO_OVERRIDES = (() => {
if (!fs.existsSync(seoOverridesPath)) return {};
try {
return JSON.parse(fs.readFileSync(seoOverridesPath, 'utf8'));
} catch (err) {
throw new Error(`Failed to parse ${seoOverridesPath}: ${err.message}`);
}
})();
```
How can I resolve this? If you propose a fix, please make it concise.Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
| function ensureFrontmatter(content, filePath) { | ||
| if (content.startsWith('---\n')) return content; | ||
| const title = frontmatterTitle(content, filePath); | ||
| return `---\ntitle: ${JSON.stringify(title)}\n---\n\n${content}`; | ||
| const relKey = path.relative(outputDir, filePath).split(path.sep).join(path.posix.sep); | ||
| const override = SEO_OVERRIDES[relKey]; | ||
| const title = override?.title ?? frontmatterTitle(content, filePath); | ||
| const lines = [`title: ${JSON.stringify(title)}`]; | ||
| if (override?.description) lines.push(`description: ${JSON.stringify(override.description)}`); | ||
| return `---\n${lines.join('\n')}\n---\n\n${content}`; | ||
| } |
There was a problem hiding this comment.
SEO overrides silently skipped when frontmatter already exists
ensureFrontmatter returns early on line 81 if the file already starts with ---\n, so if TypeDoc ever starts emitting its own YAML frontmatter (or if a developer runs npm run docs without docs:clean and the file happens to have been written with frontmatter from a prior run that omitted overrides), the SEO title/description is silently not applied and no error is raised. The CI diff guard would eventually catch the mismatch, but only after committing the wrong output. This is low risk given the current TypeDoc output, but the silent skip could be confusing in the future if the generator changes.
Prompt To Fix With AI
This is a comment left during a code review.
Path: sdks/typescript-sdk/scripts/postprocess-generated-docs.mjs
Line: 80-88
Comment:
**SEO overrides silently skipped when frontmatter already exists**
`ensureFrontmatter` returns early on line 81 if the file already starts with `---\n`, so if TypeDoc ever starts emitting its own YAML frontmatter (or if a developer runs `npm run docs` without `docs:clean` and the file happens to have been written with frontmatter from a prior run that omitted overrides), the SEO title/description is silently not applied and no error is raised. The CI diff guard would eventually catch the mismatch, but only after committing the wrong output. This is low risk given the current TypeDoc output, but the silent skip could be confusing in the future if the generator changes.
How can I resolve this? If you propose a fix, please make it concise.|
Closing: this was cut from a stale local This PR's |
Problem
The
sdk (24)CI job has failed on every run since May 18 (independent of any recent PR). The job runsnpm run docsand then asserts the generated output is committed:#219 ("improve SEO titles and descriptions for SDK reference pages") hand-edited the TypeDoc-generated MDX to add SEO
title/descriptionfrontmatter on 46 pages. But the generator (typedoc+scripts/postprocess-generated-docs.mjs) only emits a generictitle: "Type Alias: X"and no description — so it can no longer reproduce the committed files, and the up-to-date guard fails on every run.The drift is purely in
title/descriptionfrontmatter; page bodies regenerate byte-identical.Fix
Restore reproducibility without reverting the SEO work:
seo-overrides.json— the 46 hand-authored{title, description}pairs, keyed by route-relative.mdxpath.postprocess-generated-docs.mjs— loads the sidecar and applies the overrides when writing frontmatter, sonpm run docsregenerates the committed files exactly.Going forward, SEO metadata is edited in
seo-overrides.jsonrather than in the generated MDX (whichdocs:cleanwipes on every run).Verification
Locally, on a clean checkout:
npm ci && npm run docs→git diff --exit-code -- docs/sdk/referencereturns no diff and no untracked files (both CI assertions pass).Note
This is unrelated to the Claude workflow PR (#230); it's a pre-existing
mainbreakage. Worth merging on its own to turn CI green again.🤖 Generated with Claude Code
Greptile Summary
This PR fixes a broken CI job by extracting the 46 hand-edited SEO
title/descriptionvalues from the generated MDX files into a versionedseo-overrides.jsonsidecar, then applying them in the post-processing script sonpm run docsregenerates the committed output byte-for-byte.seo-overrides.json— new file holding all hand-authored frontmatter pairs keyed by route-relative MDX path; future SEO edits go here instead of in generated files.postprocess-generated-docs.mjs— loads the sidecar at startup and mergestitle/descriptioninto YAML frontmatter inensureFrontmatter, falling back to the auto-derived title when no override exists.Confidence Score: 4/5
Safe to merge; the script change is minimal and the sidecar JSON is straightforward — the only risks are cosmetic edge cases in the post-processor.
The core logic is correct and the fix squarely addresses the described CI breakage. The two minor concerns are: JSON.parse for the sidecar file has no error handling (a malformed JSON produces a cryptic message with no filename), and ensureFrontmatter's early return silently skips SEO overrides whenever a file already carries frontmatter, which could be surprising if TypeDoc's output ever changes.
sdks/typescript-sdk/scripts/postprocess-generated-docs.mjs — the JSON loading and the early-return guard in ensureFrontmatter are worth a quick second look.
Important Files Changed
Flowchart
%%{init: {'theme': 'neutral'}}%% flowchart TD A["npm run docs"] --> B["TypeDoc generates MDX files\n(no frontmatter)"] B --> C["postprocess-generated-docs.mjs"] C --> D["Load seo-overrides.json\n(keyed by route-relative path)"] D --> E["For each .mdx file"] E --> F["rewriteMdxLinks()\nrelative → absolute Mintlify routes"] F --> G{"content starts\nwith ---?"} G -- "Yes (already has frontmatter)" --> H["Return unchanged\n⚠ SEO overrides NOT applied"] G -- "No" --> I["Look up relKey in SEO_OVERRIDES"] I --> J{"override\nexists?"} J -- "Yes" --> K["Use override.title\nAppend override.description"] J -- "No" --> L["Derive title from H1 / filename"] K --> M["Write frontmatter + body to file"] L --> M M --> N["git diff --exit-code\n✅ CI passes"]Prompt To Fix All With AI
Reviews (1): Last reviewed commit: "fix(sdk): make generated SDK docs reprod..." | Re-trigger Greptile