Skip to content

Commit 2e13f59

Browse files
clay-goodclaude
andcommitted
docs(website): mirror docs/*.md into the site + auto-deploy on a cadence
Make the repository's docs/*.md the single source of truth for the docs site instead of maintaining a parallel set of hand-written MDX pages that silently drift. - scripts/sync-docs.mjs mirrors ../docs into content/docs/ on every build: derives title/description, injects Fumadocs frontmatter (+ githubSource), rewrites internal *.md links to /docs routes, and emits meta.json. Pages are written as .md so <placeholders>/{braces} in the docs stay literal and never break the MDX build. - docs.sync.config.mjs is the one manifest deciding which docs publish and their slug/section/icon. content/docs/ is now generated + git-ignored; the curated .mdx pages are removed. The marketing landing page stays hand-authored. - build/dev/types:check run sync:docs first, so the site is always current. - .github/workflows/deploy-docs.yml rebuilds and deploys to Cloudflare Pages via Wrangler on push to docs/**|website/**, daily on a schedule, on demand, and as a build-only check on PRs. Needs CLOUDFLARE_API_TOKEN + CLOUDFLARE_ACCOUNT_ID secrets and the DOCS_SITE_URL variable. - source.config.ts carries githubSource so "edit this page" opens the real docs/*.md; website/README.md documents the pipeline. Verified: clean build, 23 pages generated, 78 static pages, no warnings; all internal doc links resolve; MDX-hazard docs (cli, customization) build. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent a726e91 commit 2e13f59

30 files changed

Lines changed: 387 additions & 2276 deletions

.github/workflows/deploy-docs.yml

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
name: Docs site
2+
3+
# The documentation site (website/) mirrors docs/*.md via scripts/sync-docs.mjs,
4+
# which runs as the first step of `npm run build`. This workflow rebuilds that
5+
# mirror and deploys the static export to Cloudflare Pages:
6+
# - on every push to main that touches docs/ or website/ (deploy immediately),
7+
# - on a daily schedule (re-mirror the latest docs even if nothing pushed),
8+
# - manually via the Actions tab,
9+
# - and as a build-only check on pull requests.
10+
#
11+
# Deploys require two repository secrets: CLOUDFLARE_API_TOKEN and
12+
# CLOUDFLARE_ACCOUNT_ID. Set the site's public URL via the DOCS_SITE_URL
13+
# repository variable (used for OG/sitemap absolute URLs).
14+
15+
on:
16+
push:
17+
branches: [main]
18+
paths:
19+
- 'docs/**'
20+
- 'website/**'
21+
- '.github/workflows/deploy-docs.yml'
22+
pull_request:
23+
paths:
24+
- 'docs/**'
25+
- 'website/**'
26+
- '.github/workflows/deploy-docs.yml'
27+
schedule:
28+
# Daily at 06:00 UTC — picks up any docs changes merged since the last run.
29+
- cron: '0 6 * * *'
30+
workflow_dispatch:
31+
32+
# Never run two deploys at once; let an in-flight deploy finish.
33+
concurrency:
34+
group: deploy-docs
35+
cancel-in-progress: false
36+
37+
jobs:
38+
build-and-deploy:
39+
runs-on: ubuntu-latest
40+
permissions:
41+
contents: read
42+
steps:
43+
- uses: actions/checkout@v4
44+
45+
- uses: actions/setup-node@v4
46+
with:
47+
node-version: '20'
48+
cache: npm
49+
cache-dependency-path: website/package-lock.json
50+
51+
- name: Install dependencies
52+
working-directory: website
53+
run: npm ci
54+
55+
- name: Build site (mirrors docs/*.md, then next build)
56+
working-directory: website
57+
env:
58+
NEXT_PUBLIC_SITE_URL: ${{ vars.DOCS_SITE_URL }}
59+
run: npm run build
60+
61+
- name: Deploy to Cloudflare Pages
62+
# Skip on pull requests (build-only check) and on forks without secrets.
63+
if: ${{ github.event_name != 'pull_request' && github.repository == 'Fission-AI/OpenSpec' }}
64+
uses: cloudflare/wrangler-action@v3
65+
with:
66+
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
67+
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
68+
workingDirectory: website
69+
command: pages deploy out --project-name=openspec-docs --branch=main

website/.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44
# generated content
55
.source
66

7+
# docs pages are generated from ../docs by scripts/sync-docs.mjs (npm run build)
8+
/content/docs
9+
710
# test & build
811
/coverage
912
/.next/

website/README.md

Lines changed: 45 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
The marketing and documentation site for [OpenSpec](https://github.com/Fission-AI/OpenSpec), built with [Fumadocs](https://fumadocs.dev) and [Next.js](https://nextjs.org). It is configured as a **static export**, so it deploys to Cloudflare Pages (or any static host) with no server.
44

5+
> **The doc pages are generated, not authored here.** The repository's `docs/*.md` files are the single source of truth. `scripts/sync-docs.mjs` mirrors them into `content/docs/` (as `.md`) on every build, so the site stays current automatically — locally and in CI. Edit `../docs`, not `content/docs/`. Only the marketing landing page (`app/(home)/page.tsx`) is hand-authored. See [Keeping docs in sync](#keeping-docs-in-sync).
6+
57
## Quick start
68

79
```bash
@@ -12,10 +14,13 @@ npm run dev # http://localhost:3000
1214

1315
| Script | What it does |
1416
|--------|--------------|
15-
| `npm run dev` | Start the dev server with hot reload |
16-
| `npm run build` | Produce the static site in `out/` |
17+
| `npm run sync:docs` | Mirror `../docs/*.md` into `content/docs/` |
18+
| `npm run dev` | Sync docs, then start the dev server with hot reload |
19+
| `npm run build` | Sync docs, then produce the static site in `out/` |
1720
| `npm run start` | Serve the built `out/` directory locally |
18-
| `npm run types:check` | Generate types and run `tsc --noEmit` |
21+
| `npm run types:check` | Sync docs, generate types, and run `tsc --noEmit` |
22+
23+
`sync:docs` runs automatically inside `dev`, `build`, and `types:check`, so you rarely call it directly.
1924

2025
## Deploy to Cloudflare Pages
2126

@@ -49,27 +54,46 @@ npm run build
4954
npx wrangler pages deploy out --project-name openspec-docs
5055
```
5156

52-
## Editing the docs
57+
## Keeping docs in sync
58+
59+
The doc pages are a **mechanical mirror** of the repository's `docs/*.md`. There
60+
is nothing to hand-edit under `content/docs/` — those files are generated and
61+
git-ignored.
62+
63+
**To change a page's content:** edit the corresponding file in `../docs`. The
64+
next `npm run build`/`npm run dev` regenerates the site from it.
65+
66+
**To add, remove, reorder, or re-slug a page, or change its sidebar section or
67+
icon:** edit `docs.sync.config.mjs`. That manifest is the single place that
68+
decides which docs are published and how they appear. `scripts/sync-docs.mjs`
69+
then:
5370

54-
All content lives in `content/docs/` as MDX. To add a page:
71+
- derives each page's title from its leading `# H1` and a description from its
72+
first paragraph, and injects Fumadocs frontmatter (including `githubSource`, so
73+
the "edit this page" link opens the real `docs/*.md`);
74+
- rewrites internal `*.md` links to their on-site `/docs/...` routes;
75+
- writes each page as `.md` (Fumadocs parses `.md` as plain Markdown, so
76+
`<placeholders>` and `{braces}` in the docs are treated literally and never
77+
break the build);
78+
- regenerates `content/docs/meta.json` and `content/docs/reference/meta.json`.
5579

56-
1. Create `content/docs/my-page.mdx` with frontmatter:
80+
Because the docs are the source, the site cannot drift from them: every build
81+
re-mirrors, and CI redeploys on a schedule (see below).
5782

58-
```mdx
59-
---
60-
title: My Page
61-
description: One-line summary used for search and previews.
62-
icon: Sparkles # optional: any lucide-react icon name
63-
---
83+
## Automated deploys
6484

65-
Your content here.
66-
```
85+
`.github/workflows/deploy-docs.yml` rebuilds the mirror and deploys to Cloudflare
86+
Pages via Wrangler:
6787

68-
2. Add its slug to the `pages` array in `content/docs/meta.json` to place it in
69-
the sidebar. Use `"---Section name---"` entries to add section separators.
88+
- on every push to `main` that touches `docs/**` or `website/**`,
89+
- daily on a schedule (so docs merged elsewhere still go live),
90+
- manually via the Actions tab,
91+
- and as a build-only check on pull requests.
7092

71-
MDX pages can use these components without importing them: `Callout`, `Card`,
72-
`Cards`, `Tabs`/`Tab`, `Steps`/`Step`, and `Accordions`/`Accordion`.
93+
It needs two repository **secrets**`CLOUDFLARE_API_TOKEN` and
94+
`CLOUDFLARE_ACCOUNT_ID` — and one optional repository **variable**,
95+
`DOCS_SITE_URL` (the site's public URL, used for OG/sitemap absolute links). The
96+
Cloudflare Pages project is named `openspec-docs`.
7397

7498
## Project structure
7599

@@ -81,7 +105,9 @@ website/
81105
│ ├── api/search/ # static search index route
82106
│ ├── llms.txt / llms-full.txt / llms.mdx/ # machine-readable docs for AI
83107
│ └── og/ # generated Open Graph images per page
84-
├── content/docs/ # ← all documentation MDX lives here
108+
├── content/docs/ # ← GENERATED from ../docs (git-ignored, do not edit)
109+
├── docs.sync.config.mjs # which docs publish + their slug/section/icon
110+
├── scripts/sync-docs.mjs # mirrors ../docs/*.md -> content/docs/
85111
├── lib/
86112
│ ├── shared.ts # site name, URLs, GitHub/Discord links
87113
│ ├── source.ts # Fumadocs content source + sidebar icons

website/app/docs/[[...slug]]/page.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,9 @@ export default async function Page(props: PageProps<'/docs/[[...slug]]'>) {
2929
<MarkdownCopyButton markdownUrl={markdownUrl} />
3030
<ViewOptionsPopover
3131
markdownUrl={markdownUrl}
32-
githubUrl={`https://github.com/${gitConfig.user}/${gitConfig.repo}/blob/${gitConfig.branch}/website/content/docs/${page.path}`}
32+
githubUrl={`https://github.com/${gitConfig.user}/${gitConfig.repo}/blob/${gitConfig.branch}/${
33+
page.data.githubSource ?? `website/content/docs/${page.path}`
34+
}`}
3335
/>
3436
</div>
3537
<DocsBody>

website/content/docs/core-concepts.mdx

Lines changed: 0 additions & 168 deletions
This file was deleted.

0 commit comments

Comments
 (0)