Skip to content
Open
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
1 change: 1 addition & 0 deletions .github/workflows/publish-docs-website.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
concurrency:
group: docs-website
cancel-in-progress: false
queue: max

Check failure on line 28 in .github/workflows/publish-docs-website.yml

View workflow job for this annotation

GitHub Actions / Actionlint (informational)

unexpected key "queue" for "concurrency" section. expected one of "cancel-in-progress", "group"
Comment thread
pimlock marked this conversation as resolved.
Dismissed

defaults:
run:
Expand Down
31 changes: 26 additions & 5 deletions .github/workflows/release-dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ jobs:
password: ${{ secrets.GITHUB_TOKEN }}
outputs:
python_version: ${{ steps.v.outputs.python }}
docs_version: ${{ steps.v.outputs.docs_version }}
cargo_version: ${{ steps.v.outputs.cargo }}
deb_version: ${{ steps.v.outputs.deb }}
rpm_version: ${{ steps.v.outputs.rpm_version }}
Expand All @@ -47,11 +48,15 @@ jobs:
id: v
run: |
set -euo pipefail
echo "python=$(uv run python tasks/scripts/release.py get-version --dev --python)" >> "$GITHUB_OUTPUT"
echo "cargo=$(uv run python tasks/scripts/release.py get-version --dev --cargo)" >> "$GITHUB_OUTPUT"
echo "deb=$(uv run python tasks/scripts/release.py get-version --dev --deb)" >> "$GITHUB_OUTPUT"
echo "rpm_version=$(uv run python tasks/scripts/release.py get-version --dev --rpm-version)" >> "$GITHUB_OUTPUT"
echo "rpm_release=$(uv run python tasks/scripts/release.py get-version --dev --rpm-release)" >> "$GITHUB_OUTPUT"
python_version=$(uv run python tasks/scripts/release.py get-version --dev --python)
{
echo "python=${python_version}"
echo "docs_version=${python_version%%+*}"
echo "cargo=$(uv run python tasks/scripts/release.py get-version --dev --cargo)"
echo "deb=$(uv run python tasks/scripts/release.py get-version --dev --deb)"
echo "rpm_version=$(uv run python tasks/scripts/release.py get-version --dev --rpm-version)"
echo "rpm_release=$(uv run python tasks/scripts/release.py get-version --dev --rpm-release)"
} >> "$GITHUB_OUTPUT"

build-cli:
needs: compute-versions
Expand Down Expand Up @@ -628,6 +633,22 @@ jobs:
release-kind: dev
pin-sha: ${{ github.sha }}

publish-fern-docs:
name: Sync and Publish Fern Docs
needs: [compute-versions, release-dev, release-helm, trigger-wheel-publish]
permissions:
contents: write
uses: ./.github/workflows/sync-docs.yml
with:
operation: sync
channel: dev
source_ref: ${{ github.sha }}
release_version: ${{ needs.compute-versions.outputs.docs_version }}
display_name: Dev (v${{ needs.compute-versions.outputs.docs_version }})
availability: beta
publish: true
secrets: inherit

trigger-wheel-publish:
name: Trigger Wheel Publish
needs: [compute-versions, release-dev]
Expand Down
38 changes: 14 additions & 24 deletions .github/workflows/release-tag.yml
Original file line number Diff line number Diff line change
Expand Up @@ -664,31 +664,21 @@ jobs:
if-no-files-found: error

publish-fern-docs:
name: Publish Fern Docs
needs: [compute-versions, release]
name: Sync and Publish Fern Docs
needs: [compute-versions, release, publish-sdk-typescript, release-helm, trigger-wheel-publish]
if: needs.compute-versions.outputs.is_prerelease != 'true'
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: ${{ inputs.tag || github.ref }}

- name: Setup Node.js
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: "24"

- name: Install Fern CLI
run: |
FERN_VERSION=$(node -p "require('./fern/fern.config.json').version")
npm install -g "fern-api@${FERN_VERSION}"

- name: Publish Fern docs
env:
FERN_TOKEN: ${{ secrets.FERN_TOKEN }}
working-directory: ./fern
run: fern generate --docs
permissions:
contents: write
uses: ./.github/workflows/sync-docs.yml
with:
operation: sync
channel: stable
source_ref: ${{ needs.compute-versions.outputs.source_sha }}
release_version: ${{ needs.compute-versions.outputs.semver }}
version_slug: v${{ needs.compute-versions.outputs.semver }}
display_name: Latest (v${{ needs.compute-versions.outputs.semver }})
publish: true
secrets: inherit

publish-sdk-typescript:
name: Publish TypeScript SDK
Expand Down
99 changes: 95 additions & 4 deletions .github/workflows/sync-docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,49 @@
name: Sync Docs Website

on:
workflow_call:
inputs:
operation:
description: "Whether to sync or remove a docs snapshot"
required: true
type: string
channel:
description: "Docs channel to update or remove"
required: true
type: string
source_ref:
description: "Source commit SHA, branch, or tag to snapshot when operation=sync"
required: false
type: string
release_version:
description: "Release version used to order mutable channels"
required: false
type: string
version_slug:
description: "Version slug when channel=version"
required: false
type: string
display_name:
description: "Optional version selector display name"
required: false
type: string
availability:
description: "Optional Fern availability status"
required: false
type: string
publish:
description: "Publish production docs after syncing"
required: false
default: false
type: boolean
allow_rollback:
description: "Allow an explicitly requested mutable-channel rollback"
required: false
default: false
type: boolean
secrets:
FERN_TOKEN:
required: false
workflow_dispatch:
inputs:
operation:
Expand All @@ -21,26 +64,46 @@
options:
- dev
- latest
- stable
- version
source_ref:
description: "Source commit SHA, branch, or tag to snapshot when operation=sync"
required: false
type: string
release_version:
description: "Release version, e.g. 0.1.2 or 0.1.3.dev4"
required: false
type: string
version_slug:
description: "Version slug when channel=version, e.g. v0.0.36"
required: false
type: string
display_name:
description: "Optional version selector display name"
description: "Optional selector name, e.g. Dev (v0.0.117.dev56)"
required: false
type: string
availability:
description: "Optional Fern status: beta, deprecated, ga, or stable"
required: false
type: string
publish:
description: "Publish production docs after syncing"
required: false
default: false
type: boolean
allow_rollback:
description: "Allow an explicitly requested mutable-channel rollback"
required: false
default: false
type: boolean

permissions:
contents: write

concurrency:
group: docs-website
cancel-in-progress: false
queue: max

Check failure on line 106 in .github/workflows/sync-docs.yml

View workflow job for this annotation

GitHub Actions / Actionlint (informational)

unexpected key "queue" for "concurrency" section. expected one of "cancel-in-progress", "group"
Comment thread
pimlock marked this conversation as resolved.
Dismissed

defaults:
run:
Expand All @@ -65,15 +128,20 @@
OPERATION: ${{ inputs.operation }}
CHANNEL: ${{ inputs.channel }}
SOURCE_REF: ${{ inputs.source_ref }}
RELEASE_VERSION: ${{ inputs.release_version }}
VERSION_SLUG: ${{ inputs.version_slug }}
run: |
set -euo pipefail
if [[ "$OPERATION" == "sync" && -z "$SOURCE_REF" ]]; then
echo "source_ref is required when operation=sync" >&2
exit 1
fi
if [[ "$CHANNEL" == "version" && -z "$VERSION_SLUG" ]]; then
echo "version_slug is required when channel=version" >&2
if [[ "$CHANNEL" =~ ^(dev|latest|stable)$ && -z "$RELEASE_VERSION" ]]; then
echo "release_version is required for dev, latest, and stable channels" >&2
exit 1
fi
if [[ "$CHANNEL" =~ ^(stable|version)$ && -z "$VERSION_SLUG" ]]; then
echo "version_slug is required for stable and version channels" >&2
exit 1
fi

Expand All @@ -96,6 +164,7 @@
uses: astral-sh/setup-uv@ae62891fec2bb8e7d6c99fc78c9fec3a63790f8d # v10.0.0
with:
version: "0.10.12"
python-version: "3.13"

- name: Update docs snapshot
# Inputs flow in as quoted env vars to avoid shell injection; see the
Expand All @@ -104,17 +173,32 @@
OPERATION: ${{ inputs.operation }}
CHANNEL: ${{ inputs.channel }}
SOURCE_REF: ${{ inputs.source_ref }}
RELEASE_VERSION: ${{ inputs.release_version }}
VERSION_SLUG: ${{ inputs.version_slug }}
DISPLAY_NAME: ${{ inputs.display_name }}
AVAILABILITY: ${{ inputs.availability }}
ALLOW_ROLLBACK: ${{ inputs.allow_rollback }}
run: |
SOURCE_SHA=""
if [[ "$OPERATION" == "sync" ]]; then
SOURCE_SHA=$(git -C source rev-parse HEAD)
fi
rollback_args=()
if [[ "$ALLOW_ROLLBACK" == "true" ]]; then
rollback_args+=(--allow-rollback)
fi
uv run automation/tasks/scripts/sync_docs_website.py \
--operation "$OPERATION" \
--source-root source \
--docs-website-root docs-website \
--channel "$CHANNEL" \
--source-ref "$SOURCE_REF" \
--source-sha "$SOURCE_SHA" \
--release-version "$RELEASE_VERSION" \
--version-slug "$VERSION_SLUG" \
--display-name "$DISPLAY_NAME"
--display-name "$DISPLAY_NAME" \
--availability "$AVAILABILITY" \
"${rollback_args[@]}"

- name: Setup Node.js
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
Expand Down Expand Up @@ -160,3 +244,10 @@
git commit -m "docs(website): remove ${target} docs"
fi
git push origin HEAD:docs-website

- name: Publish Fern docs
if: ${{ inputs.publish }}
env:
FERN_TOKEN: ${{ secrets.FERN_TOKEN }}
working-directory: docs-website/fern
run: fern generate --docs
4 changes: 2 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -257,9 +257,9 @@ When behavior, commands, or development workflows change, review the related age
- When making changes, update the relevant documentation in the `architecture/` directory.
- When changes affect user-facing behavior, update the relevant published docs pages under `docs/` and navigation in `docs/index.yml`.
- When changing gateway TOML fields, driver-specific config options, config defaults, or Helm rendering of `gateway.toml`, update `docs/reference/gateway-config.mdx` in the same branch.
- `fern/` contains the Fern site config, components, preview workflow inputs, and publish settings.
- `fern/` contains the Fern site config, components, preview workflow inputs, publish settings, and publishing documentation in `fern/README.md`.
- Follow the docs style guide in [docs/CONTRIBUTING.mdx](docs/CONTRIBUTING.mdx): active voice, minimal formatting, no filler introductions, `shell` fences for copyable commands, and no duplicate body H1.
- Fern PR previews run through `.github/workflows/branch-docs.yml`, and production publish runs through the `publish-fern-docs` job in `.github/workflows/release-tag.yml` for stable release tags.
- Fern PR previews run through `.github/workflows/branch-docs.yml`. Release Dev publishes `dev`, and Release Tag publishes an immutable stable version plus `latest`. Both production paths call `.github/workflows/sync-docs.yml` once.
- Use the `update-docs-from-commits` skill to scan recent commits and draft doc updates.

### Architecture Docs
Expand Down
4 changes: 2 additions & 2 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -470,9 +470,9 @@ mise run docs

PRs that touch `docs/**` or `fern/**` are validated by `.github/workflows/branch-docs.yml`, and they get a preview when `FERN_TOKEN` is available to the workflow.

Fern docs publishing is handled by the `publish-fern-docs` job in `.github/workflows/release-tag.yml` when a stable release tag is created.
Release Dev publishes the `dev` docs version from `main`. Release Tag publishes an immutable stable version and updates `latest`. See [fern/README.md](fern/README.md) for the source layout, version model, and publishing workflows.

`docs/` is the source-of-truth docs tree. `fern/` contains the site config, components, and theme assets that publish those pages.
`docs/` is the source-of-truth docs tree. `fern/` contains the site configuration, components, theme assets, and its README.

See [docs/CONTRIBUTING.mdx](docs/CONTRIBUTING.mdx) for the current docs authoring guide.

Expand Down
8 changes: 1 addition & 7 deletions architecture/build.md
Original file line number Diff line number Diff line change
Expand Up @@ -402,13 +402,7 @@ See `CI.md` for the contributor workflow, labels, and maintainer merge-queue wor

## Docs Site

Published docs live in `docs/`. Navigation lives in `docs/index.yml`. Fern site
configuration, components, theme assets, and publish settings live in `fern/`.

Use `mise run docs` for strict validation and `mise run docs:serve` for local
preview. PR previews are produced by `.github/workflows/branch-docs.yml` when
Fern credentials are available. Production docs publish from the release tag
workflow.
Published docs live in `docs/`, and Fern site configuration lives in `fern/`. See [fern/README.md](../fern/README.md) for the source layout, local development commands, version model, and publishing workflows.

## Validation Expectations

Expand Down
68 changes: 68 additions & 0 deletions fern/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# Fern documentation site

OpenShell uses [Fern](https://buildwithfern.com/) to validate, preview, and publish the documentation at [docs.nvidia.com/openshell](https://docs.nvidia.com/openshell/). This directory contains the site configuration and presentation files. The documentation content lives in `docs/`.

## Repository layout

| Path | Purpose |
|---|---|
| `docs/` | MDX pages, navigation in `docs/index.yml`, and page-specific components. |
| `fern/docs.yml` | Site, theme, version, redirect, and navigation configuration. |
| `fern/fern.config.json` | Fern organization and pinned CLI version. |
| `fern/components/` | Shared site components. |
| `fern/assets/` | Logos and other shared assets. |
| `fern/main.css` | Site-wide styles. |

In a normal source checkout, `fern/docs.yml` points the `latest` version at `docs/index.yml`. Release automation builds the multi-version configuration on the generated `docs-website` branch.

## Local development

Start a local Fern server from the repository root:

```shell
mise run docs:serve
```

Validate the configuration, navigation, and links without starting a server:

```shell
mise run docs
```

The tasks read the Fern CLI version from `fern/fern.config.json`, so local checks and GitHub Actions use the same version. See [docs/CONTRIBUTING.mdx](../docs/CONTRIBUTING.mdx) for the authoring and style guide.

## Pull request previews

`.github/workflows/branch-docs.yml` validates pull requests that change documentation or Fern configuration. When the workflow can access `FERN_TOKEN`, it publishes a Fern preview from the pull request checkout and adds the preview URL to the pull request. This path does not use or update the `docs-website` branch.

## Versioned production site

The generated `docs-website` branch contains the complete production input for Fern. Each version has an exact copy of its source commit's `docs/` tree under `fern/pages-<slug>/` and a navigation file under `fern/versions/`. `fern/.docs-snapshots.yml` records the original source ref, resolved source commit, and release version for each managed snapshot.

The site uses these version types:

| Version | Source | Update policy | Fern status |
|---|---|---|---|
| `dev` | The most recent successful Release Dev run from `main`. | Mutable. Automation rejects an older version or the same version from a different commit unless a maintainer explicitly allows a rollback. | Beta. |
| `latest` | The newest stable release. | Mutable. A maintenance release older than the current stable release cannot move it backward unless a maintainer explicitly allows a rollback. | Stable starting with v0.1.0. |
| `vX.Y.Z` | The matching stable release tag. | Immutable. Repeating the sync is allowed only when the tag resolves to the same commit. | Stable starting with v0.1.0. |

Release Dev waits for the development artifacts and Helm chart, then calls `.github/workflows/sync-docs.yml` once. The reusable workflow updates `dev`, validates the generated site, commits and pushes the branch when needed, and publishes the production site once.

Release Tag follows the same sequence for a non-prerelease tag after the release artifacts, SDK package, Helm chart, and wheel publication complete. It creates the immutable `vX.Y.Z` snapshot and updates `latest` when the release is not older than the current version. One call to `.github/workflows/sync-docs.yml` performs both changes and publishes the production site once.

The sync and publish workflows share the `docs-website` concurrency group. This serializes writes and publication. Queued runs remain pending instead of replacing one another.

The `dev` snapshot also owns the shared Fern configuration, components, assets, and CSS on `docs-website`. Stable snapshots copy their documentation and navigation but do not replace those shared files. This keeps the site configuration aligned with `main` while preserving the content captured for each release.

## Manual maintenance and publishing

Maintainers can run `.github/workflows/sync-docs.yml` manually to add, refresh, or remove a snapshot. The workflow preserves snapshots that were not selected. Production publishing is disabled by default for a manual sync.

`.github/workflows/publish-docs-website.yml` validates and publishes the existing `docs-website` branch without syncing content. Its default mode creates a preview. Selecting production mode publishes the live site, so use it only for an intentional production republish.

Run the automated sync tests after changing the version model or either publishing workflow:

```shell
mise run test:docs-website
```
2 changes: 1 addition & 1 deletion fern/fern.config.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{
"organization": "nvidia",
"version": "5.40.0"
"version": "5.112.0"
}
Loading
Loading