Set up Fumadocs documentation site with Cloudflare Workers deploy - #16
Conversation
Scaffold a Fumadocs (Next.js static-export) documentation site in docs/ from the existing guide MDX. Includes client-side Orama static search, generated OG images, and llms.txt routes. Content moves to docs/content/docs with meta.json driving nav order; dangling design-note links in index.mdx are fixed. Add .github/workflows/docs.yml: builds the static export and deploys it to Cloudflare Workers Static Assets on push to main (docs changes), and uploads a preview version on pull requests. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI 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 new ChangesDocs Site Bootstrap
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
The pnpm/action-setup step runs at the repo root (uses: steps ignore defaults.run.working-directory), where there is no package.json, so it cannot read the packageManager field. Pin the version explicitly. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 6
🧹 Nitpick comments (2)
docs/components/search.tsx (1)
28-33: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick winMemoize the ORama client per locale.
This recreates the search client on every render. If
useDocsSearch()keys offclientidentity, each keystroke can trigger needless reinitialization and extra work. Memoizing it bylocalekeeps the dialog stable.♻️ Proposed change
import { useDocsSearch } from 'fumadocs-core/search/client'; import { oramaStaticClient } from 'fumadocs-core/search/client/orama-static'; import { create } from '`@orama/orama`'; import { useI18n } from 'fumadocs-ui/contexts/i18n'; +import { useMemo } from 'react'; @@ export default function DefaultSearchDialog(props: SharedProps) { const { locale } = useI18n(); // (optional) for i18n + const client = useMemo( + () => + oramaStaticClient({ + initOrama, + locale, + }), + [locale], + ); + const { search, setSearch, query } = useDocsSearch({ - client: oramaStaticClient({ - initOrama, - locale, - }), + client, });🤖 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 `@docs/components/search.tsx` around lines 28 - 33, The ORama client passed into useDocsSearch is recreated on every render, causing unstable client identity and unnecessary reinitialization. Memoize the result of oramaStaticClient(initOrama, locale) by locale before passing it into useDocsSearch, so the search dialog stays stable across keystrokes. Update the search component to keep the client instance stable unless locale changes.docs/.gitignore (1)
23-26: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winIgnore Wrangler’s local state directory.
Running Wrangler from this package will leave a
.wrangler/directory underdocs/, and it is easy to commit by accident. Add it here with the other generated artifacts.Suggested diff
# others .env*.local .vercel next-env.d.ts +/.wrangler🤖 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 `@docs/.gitignore` around lines 23 - 26, The docs .gitignore is missing Wrangler’s generated local state directory, so add the .wrangler/ entry alongside the other generated artifacts in this file. Update the ignore list near the existing .vercel and next-env.d.ts entries so running Wrangler from docs does not leave a trackable .wrangler/ directory behind.
🤖 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 @.github/workflows/docs.yml:
- Around line 40-47: The Cloudflare deploy step in the docs workflow currently
runs for every pull_request, which causes forked PRs to fail because they cannot
access secrets. Update the Deploy to Cloudflare Workers job to only run for
trusted PRs, using the existing github.event_name logic in the
wrangler-action@v3 step and the surrounding workflow conditions so preview
uploads/deploys are skipped for forked contributors.
- Around line 30-34: The docs workflow currently runs the deploy-related setup
unconditionally, which can break forked pull_request runs when Cloudflare
secrets are unavailable. Update the workflow around the wrangler-action/deploy
step in docs.yml to run only when the PR is not from a fork or when the required
credentials are present, so preview deploys are skipped safely for external
contributors.
In `@docs/app/`(home)/page.tsx:
- Around line 5-13: The root docs landing page still shows the scaffold
placeholder in the page component, so replace the “Hello World” content in the
home page with production-ready branded copy or change the page behavior to
redirect to /docs. Update the existing landing page component in page.tsx so the
default / route no longer exposes the unfinished placeholder and instead matches
the migrated documentation experience.
In `@docs/app/docs/`[[...slug]]/page.tsx:
- Around line 30-33: The GitHub source link in ViewOptionsPopover is built with
the wrong repository path, causing broken “view on GitHub” links for docs pages.
Update the githubUrl construction in the docs page component to include the
correct docs content prefix from the repo root, using the existing page.path
value so it points to docs/content/docs/... instead of content/docs/... .
In `@docs/app/layout.tsx`:
- Line 13: The metadataBase setup in the app layout currently only falls back on
nullish values, so an empty string or malformed NEXT_PUBLIC_SITE_URL can still
crash the docs app when new URL is constructed. Update the metadataBase
initialization in the layout logic to validate the env value first, treat
blank/invalid values as missing, and fall back safely to the localhost URL
before calling new URL. Keep the fix localized to the metadataBase assignment so
the layout remains resilient to bad configuration.
In `@docs/public/_redirects`:
- Line 1: The root redirect in the _redirects file is overriding the new home
route and making the landing page unreachable. Update the redirect rule so / is
not always sent to /docs, and verify it does not conflict with the newly added
app/(home)/page.tsx route; if the home page should ship, remove or narrow this
redirect so the root path can resolve to the home page.
---
Nitpick comments:
In `@docs/.gitignore`:
- Around line 23-26: The docs .gitignore is missing Wrangler’s generated local
state directory, so add the .wrangler/ entry alongside the other generated
artifacts in this file. Update the ignore list near the existing .vercel and
next-env.d.ts entries so running Wrangler from docs does not leave a trackable
.wrangler/ directory behind.
In `@docs/components/search.tsx`:
- Around line 28-33: The ORama client passed into useDocsSearch is recreated on
every render, causing unstable client identity and unnecessary reinitialization.
Memoize the result of oramaStaticClient(initOrama, locale) by locale before
passing it into useDocsSearch, so the search dialog stays stable across
keystrokes. Update the search component to keep the client instance stable
unless locale changes.
🪄 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: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: f3c5db7f-62c6-4990-8b1a-79b8a27b512c
⛔ Files ignored due to path filters (1)
docs/pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (40)
.github/workflows/docs.ymldocs/.gitignoredocs/README.mddocs/app/(home)/layout.tsxdocs/app/(home)/page.tsxdocs/app/api/search/route.tsdocs/app/docs/[[...slug]]/page.tsxdocs/app/docs/layout.tsxdocs/app/global.cssdocs/app/layout.tsxdocs/app/llms-full.txt/route.tsdocs/app/llms.mdx/docs/[[...slug]]/route.tsdocs/app/llms.txt/route.tsdocs/app/og/docs/[...slug]/route.tsxdocs/components/mdx.tsxdocs/components/provider.tsxdocs/components/search.tsxdocs/content/docs/cli-and-operations.mdxdocs/content/docs/core-concepts.mdxdocs/content/docs/declarations.mdxdocs/content/docs/extending-astrolabe.mdxdocs/content/docs/getting-started.mdxdocs/content/docs/index.mdxdocs/content/docs/meta.jsondocs/content/docs/modifiers.mdxdocs/content/docs/state-and-persistence.mdxdocs/content/docs/telemetry-and-updates.mdxdocs/lib/cn.tsdocs/lib/layout.shared.tsxdocs/lib/shared.tsdocs/lib/source.tsdocs/loop-is-retry.mddocs/next.config.mjsdocs/package.jsondocs/postcss.config.mjsdocs/public/_redirectsdocs/source.config.tsdocs/storage-persistence.mddocs/tsconfig.jsondocs/wrangler.toml
💤 Files with no reviewable changes (2)
- docs/storage-persistence.md
- docs/loop-is-retry.md
📜 Review details
⚠️ CI failures not shown inline (2)
GitHub Actions: Docs / deploy: Set up Fumadocs documentation site with Cloudflare Workers deploy
Conclusion: failure
##[group]Running self-installer...
Error: No pnpm version is specified.
Please specify it by one of the following ways:
- in the GitHub Action config with the key "version"
- in the package.json with the key "packageManager"
at readTarget (/home/runner/work/_actions/pnpm/action-setup/v4/dist/index.js:1:8195)
at runSelfInstaller (/home/runner/work/_actions/pnpm/action-setup/v4/dist/index.js:1:6702)
at async install (/home/runner/work/_actions/pnpm/action-setup/v4/dist/index.js:1:5706)
at async runMain (/home/runner/work/_actions/pnpm/action-setup/v4/dist/index.js:1:2804)
at async main (/home/runner/work/_actions/pnpm/action-setup/v4/dist/index.js:1:2726)
##[error]Error: No pnpm version is specified.
GitHub Actions: Docs / 0_deploy.txt: Set up Fumadocs documentation site with Cloudflare Workers deploy
Conclusion: failure
##[group]Running self-installer...
Error: No pnpm version is specified.
Please specify it by one of the following ways:
- in the GitHub Action config with the key "version"
- in the package.json with the key "packageManager"
at readTarget (/home/runner/work/_actions/pnpm/action-setup/v4/dist/index.js:1:8195)
at runSelfInstaller (/home/runner/work/_actions/pnpm/action-setup/v4/dist/index.js:1:6702)
at async install (/home/runner/work/_actions/pnpm/action-setup/v4/dist/index.js:1:5706)
at async runMain (/home/runner/work/_actions/pnpm/action-setup/v4/dist/index.js:1:2804)
at async main (/home/runner/work/_actions/pnpm/action-setup/v4/dist/index.js:1:2726)
##[error]Error: No pnpm version is specified.
🧰 Additional context used
🪛 zizmor (1.26.1)
.github/workflows/docs.yml
[warning] 26-26: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false
(artipacked)
[error] 30-30: runtime artifacts potentially vulnerable to a cache poisoning attack (cache-poisoning): this step
(cache-poisoning)
🔇 Additional comments (13)
docs/components/mdx.tsx (1)
1-15: LGTM!docs/components/provider.tsx (1)
1-8: LGTM!docs/app/global.css (1)
1-12: LGTM!docs/app/(home)/layout.tsx (1)
1-6: LGTM!docs/app/docs/layout.tsx (1)
1-11: LGTM!docs/package.json (1)
15-16: 🩺 Stability & AvailabilityRemove this dependency warning.
docs/pnpm-lock.yamlalready resolvesfumadocs-core@16.10.6andfumadocs-mdx@15.0.13, so these ranges are not a bootstrap blocker; the suggested downgrade is unnecessary.> Likely an incorrect or invalid review comment.docs/app/og/docs/[...slug]/route.tsx (1)
23-27: 🗄️ Data Integrity & IntegrationNo change needed here.
langisn’t part of this route, sogenerateStaticParams()ignores it.> Likely an incorrect or invalid review comment.docs/app/llms.txt/route.ts (1)
4-7: 🩺 Stability & AvailabilityNo
dynamic = 'force-static'needed here
output: 'export'already requires this handler to be build-time renderable, and this route doesn’t use request-time APIs.revalidate = falseis fine; addingdynamic = 'force-static'here isn’t necessary.> Likely an incorrect or invalid review comment.docs/app/docs/[[...slug]]/page.tsx (1)
16-29: LGTM!Also applies to: 35-63
docs/app/api/search/route.ts (1)
1-9: LGTM!docs/content/docs/meta.json (1)
1-13: LGTM!docs/content/docs/index.mdx (1)
120-127: LGTM!docs/README.md (1)
1-46: LGTM!
| <div className="flex flex-col justify-center text-center flex-1"> | ||
| <h1 className="text-2xl font-bold mb-4">Hello World</h1> | ||
| <p> | ||
| You can open{' '} | ||
| <Link href="/docs" className="font-medium underline"> | ||
| /docs | ||
| </Link>{' '} | ||
| and see the documentation. | ||
| </p> |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Replace the scaffold placeholder before shipping.
The root docs landing page currently renders Hello World, which looks unfinished for a production docs site. Either redirect / to /docs or swap this for branded copy that matches the migrated documentation.
🤖 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 `@docs/app/`(home)/page.tsx around lines 5 - 13, The root docs landing page
still shows the scaffold placeholder in the page component, so replace the
“Hello World” content in the home page with production-ready branded copy or
change the page behavior to redirect to /docs. Update the existing landing page
component in page.tsx so the default / route no longer exposes the unfinished
placeholder and instead matches the migrated documentation experience.
| <ViewOptionsPopover | ||
| markdownUrl={markdownUrl} | ||
| githubUrl={`https://github.com/${gitConfig.user}/${gitConfig.repo}/blob/${gitConfig.branch}/content/docs/${page.path}`} | ||
| /> |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Fix the repository path in the GitHub source link.
The docs files in this PR live under docs/content/docs/..., but this URL points at content/docs/... from the repo root, so the "view on GitHub" action will 404 for every page.
Suggested fix
<ViewOptionsPopover
markdownUrl={markdownUrl}
- githubUrl={`https://github.com/${gitConfig.user}/${gitConfig.repo}/blob/${gitConfig.branch}/content/docs/${page.path}`}
+ githubUrl={`https://github.com/${gitConfig.user}/${gitConfig.repo}/blob/${gitConfig.branch}/docs/content/docs/${page.path}`}
/>📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <ViewOptionsPopover | |
| markdownUrl={markdownUrl} | |
| githubUrl={`https://github.com/${gitConfig.user}/${gitConfig.repo}/blob/${gitConfig.branch}/content/docs/${page.path}`} | |
| /> | |
| <ViewOptionsPopover | |
| markdownUrl={markdownUrl} | |
| githubUrl={`https://github.com/${gitConfig.user}/${gitConfig.repo}/blob/${gitConfig.branch}/docs/content/docs/${page.path}`} | |
| /> |
🤖 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 `@docs/app/docs/`[[...slug]]/page.tsx around lines 30 - 33, The GitHub source
link in ViewOptionsPopover is built with the wrong repository path, causing
broken “view on GitHub” links for docs pages. Update the githubUrl construction
in the docs page component to include the correct docs content prefix from the
repo root, using the existing page.path value so it points to
docs/content/docs/... instead of content/docs/... .
| // Set NEXT_PUBLIC_SITE_URL (e.g. https://docs.example.com) in CI so OpenGraph | ||
| // image URLs resolve to the deployed origin instead of localhost. | ||
| export const metadata: Metadata = { | ||
| metadataBase: new URL(process.env.NEXT_PUBLIC_SITE_URL ?? 'http://localhost:3000'), |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
Guard metadataBase against empty or invalid env values.
new URL(process.env.NEXT_PUBLIC_SITE_URL ?? ...) still throws when the variable is set to '' or any malformed URL. That turns a config typo into a hard build/runtime failure for the whole docs app.
🛡️ Proposed change
+function getMetadataBase() {
+ const siteUrl = process.env.NEXT_PUBLIC_SITE_URL?.trim();
+
+ try {
+ return new URL(siteUrl || 'http://localhost:3000');
+ } catch {
+ return new URL('http://localhost:3000');
+ }
+}
+
export const metadata: Metadata = {
- metadataBase: new URL(process.env.NEXT_PUBLIC_SITE_URL ?? 'http://localhost:3000'),
+ metadataBase: getMetadataBase(),
};📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| metadataBase: new URL(process.env.NEXT_PUBLIC_SITE_URL ?? 'http://localhost:3000'), | |
| function getMetadataBase() { | |
| const siteUrl = process.env.NEXT_PUBLIC_SITE_URL?.trim(); | |
| try { | |
| return new URL(siteUrl || 'http://localhost:3000'); | |
| } catch { | |
| return new URL('http://localhost:3000'); | |
| } | |
| } | |
| export const metadata: Metadata = { | |
| metadataBase: getMetadataBase(), | |
| }; |
🤖 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 `@docs/app/layout.tsx` at line 13, The metadataBase setup in the app layout
currently only falls back on nullish values, so an empty string or malformed
NEXT_PUBLIC_SITE_URL can still crash the docs app when new URL is constructed.
Update the metadataBase initialization in the layout logic to validate the env
value first, treat blank/invalid values as missing, and fall back safely to the
localhost URL before calling new URL. Keep the fix localized to the metadataBase
assignment so the layout remains resilient to bad configuration.
| @@ -0,0 +1 @@ | |||
| / /docs 302 | |||
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
This redirect makes the new home page unreachable.
The stack context says this PR also adds docs/app/(home)/page.tsx, but this rule sends every / request straight to /docs. If the landing page is meant to ship, drop this redirect; otherwise the home route is dead in deployed environments.
🤖 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 `@docs/public/_redirects` at line 1, The root redirect in the _redirects file
is overriding the new home route and making the landing page unreachable. Update
the redirect rule so / is not always sent to /docs, and verify it does not
conflict with the newly added app/(home)/page.tsx route; if the home page should
ship, remove or narrow this redirect so the root path can resolve to the home
page.
Summary
docs/, turning the loose collection of.mdxfiles into a buildable, searchable docs app with a static export.docs/content/docs/and addsmeta.jsonto control sidebar ordering.main, ephemeral preview URLs on pull requests.docs/loop-is-retry.md,docs/storage-persistence.md); their content now lives in the structured docs, andindex.mdxlinks to the in-site pages and the rootCONSTITUTION.mdinstead.llms.txt/llms-full.txt/ per-page markdown routes for LLM consumption.Prerequisites
The deploy workflow expects two repository secrets:
CLOUDFLARE_API_TOKENwranglerto deploy/uploadCLOUDFLARE_ACCOUNT_IDFor correct OpenGraph image URLs in production, set
NEXT_PUBLIC_SITE_URL(e.g.https://docs.example.com) in the build environment; it falls back tohttp://localhost:3000.Usage
Changes
CI / deploy
.github/workflows/docs.yml— pnpm + Node 24 build;wrangler deployonmain/manual,wrangler versions upload(preview) on PRs. Path-filtered todocs/**.docs/wrangler.toml— static-assets-only Worker serving./out.docs/public/_redirects—/→/docs.App & routes (
docs/app/)layout.tsx,(home)/landing page,docs/[[...slug]]/page.tsx+docs/layout.tsxdocs shell.api/search/route.tsstatic search endpoint;og/docs/[...slug]OG images.llms.txt,llms-full.txt,llms.mdx/docs/[[...slug]]LLM/markdown routes.Components & lib
components/— MDX components, root provider, Orama search dialog.lib/source.ts,lib/shared.ts,lib/layout.shared.tsx,lib/cn.ts— content source, shared config, layout options.source.config.ts,next.config.mjs,postcss.config.mjs,tsconfig.json,package.json,pnpm-lock.yaml,.gitignore,app/global.css.Content
cli-and-operations,core-concepts,declarations,extending-astrolabe,getting-started,index,modifiers,state-and-persistence,telemetry-and-updates.mdxintodocs/content/docs/.docs/content/docs/meta.jsonfor navigation order.index.mdx"Design anchors" to link to in-site pages and the rootCONSTITUTION.md.Removed
docs/loop-is-retry.md,docs/storage-persistence.md(superseded by the structured docs).Test plan
cd docs && pnpm install --frozen-lockfilesucceeds.pnpm buildproduces a static export indocs/out.pnpm types:checkpasses.pnpm devrenders every doc page; sidebar order matchesmeta.json; search returns results.CONSTITUTION.mdlink resolve./llms.txt,/llms-full.txt, and a per-pagecontent.mdroute return markdown.🤖 Generated with Claude Code
Need help on this PR? Tag
/codesmithwith what you need. Autofix is disabled.Summary by CodeRabbit