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
33 changes: 31 additions & 2 deletions .github/workflows/publish-fern-docs.yaml
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0

# Publishes the Fern documentation site when a docs tag is pushed or manually triggered.
# Publishes the Fern documentation site from the default branch when docs
# change, a release tag is pushed, a docs republish tag is pushed, or the
# workflow is manually triggered.
#
# To publish: git tag docs/v1.2.0 && git push origin docs/v1.2.0
# To publish a release version: push a stable SemVer release tag, for example
# `git tag 1.2.0 && git push origin 1.2.0`.
# To republish docs without creating a release version: push a docs tag, for
# example `git tag docs/v1.2.0 && git push origin docs/v1.2.0`.
# Or use the "Run workflow" button in the Actions tab.
#
# Required configuration:
Expand All @@ -18,6 +23,7 @@ on:
paths:
- 'docs/**'
tags:
- '*.*.*'
- 'docs/v*'
workflow_dispatch: {}

Expand All @@ -35,14 +41,37 @@ jobs:
- name: Checkout repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
ref: ${{ github.event.repository.default_branch }}
fetch-depth: 0
persist-credentials: false

- name: Setup Node.js
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
node-version: '20'

- name: Install Fern dependencies
working-directory: ./docs/fern
run: npm ci

- name: Check release tag eligibility
id: release-tag
if: ${{ github.ref_type == 'tag' && !startsWith(github.ref_name, 'docs/') }}
working-directory: ./docs/fern
run: |
node scripts/materialize-release-versions.mjs --check-tag "$GITHUB_REF_NAME" >> "$GITHUB_OUTPUT"
Comment thread
mckornfield marked this conversation as resolved.

- name: Materialize release versions
if: ${{ steps.release-tag.outputs.eligible != 'false' }}
working-directory: ./docs/fern
run: npm run materialize:versions
Comment thread
coderabbitai[bot] marked this conversation as resolved.

- name: Check Fern docs
if: ${{ steps.release-tag.outputs.eligible != 'false' }}
run: make docs-check
Comment thread
mckornfield marked this conversation as resolved.

- name: Publish Docs
if: ${{ steps.release-tag.outputs.eligible != 'false' }}
env:
FERN_TOKEN: ${{ secrets.DOCS_FERN_TOKEN }}
working-directory: ./docs/fern
Expand Down
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -146,11 +146,13 @@ site/

# Generated Fern-only public API reference spec
docs/fern/openapi/openapi.public.yaml
docs/fern/generated/
docs/fern/versions/[0-9]*.yml
docs/fern/versions/v[0-9]*.yml

# Generated from k8s/helm/values.yaml + README.md at docs build time (docs/fern/scripts/sync-helm-docs.mjs)
docs/helm/index.mdx

# nektos/act files commonly used
.act-variables
.act-secrets

6 changes: 3 additions & 3 deletions docs/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,14 @@ Run these from the repo root (they wrap `cd docs/fern && npm run …`):
| `make docs-broken-links` | Report broken links |
| `make docs-fix-links` | Auto-delink references into gated pages |

Local preview and the published site read the **same** `docs/fern/versions/latest.yml`, so what you see locally is what ships.
Normal authoring and local preview read `docs/fern/versions/latest.yml`. During production publish, `.github/workflows/publish-fern-docs.yaml` also runs `npm run materialize:versions` to generate frozen release-version entries from stable SemVer tags that already contain Fern config. Tags from before the Fern migration, such as early `0.1.x` tags, cannot become Fern versions unless those docs are backported or migrated.

Use `make docs` when you are only editing `docs/fern/` config. Use `make docs-watch` when you are editing page content elsewhere under `docs/`, since it restarts the Fern dev server when repo-level docs files change outside `docs/fern/`.

## Rules that bite if you miss them

- **Navigation is the build.** Fern only builds pages listed in `docs/fern/versions/latest.yml`. A `.mdx` not in the nav is **not built** (404, not indexed) — that is how unready features are gated. Do **not** use `hidden: true` for gating (it still builds/serves the page).
- **Publication state is nav-derived.** Do not maintain or rely on a hard-coded list of gated directories. Check `docs/fern/versions/latest.yml`: listed pages are published, and omitted pages are gated. `docs/fern/gated-nav.yml` contains reference blocks for some gated features. To publish one: move its block into `latest.yml`, re-add inbound links, run `make docs-check && make docs-broken-links`.
- **Navigation is the build.** Fern only builds pages listed in a version nav file. For ordinary docs work, that is `docs/fern/versions/latest.yml`. A `.mdx` not in the nav is **not built** (404, not indexed) — that is how unready features are gated. Do **not** use `hidden: true` for gating (it still builds/serves the page).
- **Publication state is nav-derived.** Do not maintain or rely on a hard-coded list of gated directories. Check `docs/fern/versions/latest.yml`: listed pages are published in Latest, and omitted pages are gated. `docs/fern/gated-nav.yml` contains reference blocks for some gated features. To publish one: move its block into `latest.yml`, re-add inbound links, run `make docs-check && make docs-broken-links`.
- **Don't link into gated pages.** A link from a published page into a gated page is a dead link. `make docs-check` fails on it; `make docs-fix-links` delinks it to plain text. (Replaces the old MkDocs `hide_unready_docs` auto-delinking.)
- **Internal links** use canonical nav URLs like `/documentation/get-started/core-concepts/workspaces`, not relative `.md`/source paths. `make docs-broken-links` is the check.
- **No `{{variable}}` substitutions.** Fern has no substitution step; product names are inlined as literal text. (Prompt-template tokens like `` `{{input}}` `` inside backticks are real content — leave them.)
Expand Down
24 changes: 21 additions & 3 deletions docs/fern/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,11 @@ docs/ # page content (.mdx), one tree per product area
├── snippets/ # reusable <Markdown src> fragments
├── scripts/ # validate-mdx.mjs, delink-gated.mjs, ipynb-to-fern-json.py
├── gated-nav.yml # reference nav blocks for gated (unready) features
└── versions/latest.yml # navigation tree (defines what gets built)
├── generated/ # ignored release snapshots created during publish
└── versions/latest.yml # Latest navigation tree (ordinary docs authoring)
```

The site uses a single `Latest` version. `versions/latest.yml` defines the sidebar and maps each page file to its canonical route — and, because Fern only builds pages listed there, it is also what gates unready content (see below).
Normal docs authoring targets `Latest`. `versions/latest.yml` defines the sidebar and maps each page file to its canonical route — and, because Fern only builds pages listed there, it is also what gates unready content (see below). Production publish additionally materializes frozen release versions from stable SemVer tags that already contain Fern config.

## Authoring

Expand Down Expand Up @@ -89,6 +90,22 @@ The REST API reference is generated natively by Fern from the OpenAPI spec — n

Fern groups endpoints by their OpenAPI tag in the sidebar (Customizer, Evaluator, Guardrails, …), which replaces the old per-service filter chips. Link to it from other pages with the nav URL `/documentation/reference/api-reference`.

## Release versioning

Fern does not infer docs versions from git tags. The version selector is built from `docs.yml` `versions:` entries, and each entry points at a version-specific nav file.

For public publishing, `.github/workflows/publish-fern-docs.yaml` runs:

```bash
npm run materialize:versions
```

That script discovers stable SemVer tags in the full checkout, skips tags that predate `docs/fern/versions/latest.yml`, exports each tag's `docs/` tree into ignored `docs/fern/generated/release-versions/<version>/` snapshots, rewrites the tag's `versions/latest.yml` to point at the snapshot, and injects generated entries into `docs.yml` before `npm run generate` publishes. Tag pushes are publish signals; the workflow checks out the default branch so the current Fern publishing code and `Latest` docs remain the source for the live site.

This means early `0.1.x` tags that were cut before the Fern migration cannot appear as true Fern versions unless someone deliberately backports/migrates those docs into Fern. A release tag such as `0.2.0`, which already contains `docs/fern`, can be materialized.

To preview the release selector locally, run `npm run materialize:versions` from `docs/fern/`, then `npm run dev`. The generated snapshots and generated version YAMLs are gitignored; restore `docs/fern/docs.yml` afterward if you only needed a local preview.
Comment thread
mckornfield marked this conversation as resolved.

## Gated (unready) features

Some features are not shipped yet and must be **fully excluded from the build** — not just hidden from the sidebar. Fern's `hidden: true` still builds and serves the page (reachable by direct URL and indexable), so it is **not** used for this. Instead, the gated pages are simply **left out of `versions/latest.yml`**: Fern only builds pages referenced in the navigation, so an omitted page is never built (it 404s and is not indexed). This matches the old MkDocs `hide_unready_docs` hook, which dropped the same files from the build.
Expand Down Expand Up @@ -117,7 +134,7 @@ One difference from the old MkDocs hook: that hook ran at build time and kept th
| `fern-docs-ci.yaml` | `pull_request` touching `docs/**` | `npm run check` (fern check + MDX + NotebookViewer artifacts + gated links) and `npm run broken-links` |
| `fern-docs-preview-build.yaml` | `pull_request` touching `docs/**` | Upload PR `docs/` sources as an artifact (no secrets — fork-safe) |
| `fern-docs-preview-comment.yaml` | successful preview build (`workflow_run`) | Build a Fern preview with `DOCS_FERN_TOKEN` and post/update the PR comment |
| `publish-fern-docs.yaml` | push to `main` touching `docs/**`, `docs/v*` tag, or manual dispatch | Publish the Fern docs site |
| `publish-fern-docs.yaml` | push to `main` touching `docs/**`, stable SemVer tag, `docs/v*` republish tag, or manual dispatch | Materialize release versions and publish the Fern docs site |

Required secret: `DOCS_FERN_TOKEN` (org-level), from `fern token` for an account that can publish to the NVIDIA Fern organization.

Expand All @@ -132,3 +149,4 @@ PRs that touch `docs/**` get a shared preview URL posted as a comment after the
| Page 404 in preview | Check that `versions/latest.yml` lists the page (gated pages are intentionally omitted and *will* 404) |
| Broken internal link | Rewrite to the nav URL `/documentation/...`; if it targets a gated page, run `make docs-fix-links` to delink it. `make docs-broken-links` reports them all |
| JSX or MDX parse error | Escape raw `{}`, `<`, or `>` in prose, and use Fern components instead of raw MkDocs syntax |
| Release tag missing from version selector | Confirm the tag is stable SemVer and contains `docs/fern/versions/latest.yml`; pre-Fern tags are skipped by `npm run materialize:versions` |
19 changes: 18 additions & 1 deletion docs/fern/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion docs/fern/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"check:fern": "npm run prepare && npx -y fern-api@latest check",
"validate-mdx": "node scripts/validate-mdx.mjs",
"validate-notebook-viewer": "node scripts/validate-notebook-viewer.mjs",
"materialize:versions": "node scripts/materialize-release-versions.mjs",
"check:gated-links": "node scripts/delink-gated.mjs",
"fix:gated-links": "node scripts/delink-gated.mjs --fix",
"broken-links": "npm run prepare && npx -y fern-api@latest docs broken-links",
Expand All @@ -17,6 +18,7 @@
"preview": "npm run prepare && npx -y fern-api@latest generate --docs --preview"
},
"devDependencies": {
"@mdx-js/mdx": "^3.1.0"
"@mdx-js/mdx": "^3.1.0",
"yaml": "^2.9.0"
}
}
Loading
Loading