refactor(parsers): switch fixture format from JSON to YAML #12750
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. | |
| # SPDX-License-Identifier: Apache-2.0 | |
| # | |
| # Licensed under the Apache License, Version 2.0 (the "License"); | |
| # you may not use this file except in compliance with the License. | |
| # You may obtain a copy of the License at | |
| # | |
| # http://www.apache.org/licenses/LICENSE-2.0 | |
| # | |
| # Unless required by applicable law or agreed to in writing, software | |
| # distributed under the License is distributed on an "AS IS" BASIS, | |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| # See the License for the specific language governing permissions and | |
| # limitations under the License. | |
| # Consolidated Fern Documentation Workflow | |
| # | |
| # This workflow handles all Fern documentation automation: | |
| # | |
| # 1. LINT (PRs): Validates Fern configuration and checks for broken links | |
| # - Triggers on PRs when docs/** files change | |
| # - Runs `fern check` and `fern docs broken-links` | |
| # | |
| # 2. SYNC & PUBLISH/PREVIEW: Syncs docs/ from source branch to fern/ on docs-website | |
| # - Triggers on push to main or PRs when docs/** files change | |
| # - On main: commits and pushes to docs-website, then publishes via `fern generate --docs` | |
| # - On PRs: generates a preview URL via `fern generate --docs --preview` and comments on PR | |
| # - Preserves versioned documentation (products[0]) from docs-website's docs.yml | |
| # | |
| # 3. VERSION RELEASE (tags): Creates versioned documentation snapshot | |
| # - Triggers on new version tags (vX.Y.Z format) | |
| # - Creates fern/pages-vX.Y.Z/ directory on docs-website branch | |
| # - Updates fern/docs.yml with new version entry | |
| # - Publishes docs to Fern after releasing | |
| # | |
| # Note: The publish step is included inline because pushes made with GITHUB_TOKEN | |
| # do not trigger other workflows (GitHub's anti-recursion guard), so we cannot | |
| # rely on a separate publish-fern-docs.yml workflow for bot-initiated pushes. | |
| name: Fern Docs | |
| on: | |
| push: | |
| branches: | |
| - main | |
| - "pull-request/[0-9]+" | |
| tags: | |
| # Match only clean semver tags: vX.Y.Z | |
| - 'v[0-9]+.[0-9]+.[0-9]+' | |
| workflow_dispatch: | |
| inputs: | |
| tag: | |
| description: 'Version tag to release (e.g., v0.9.0). Leave empty to sync dev docs.' | |
| required: false | |
| type: string | |
| permissions: | |
| contents: write | |
| pull-requests: write | |
| jobs: | |
| # Detect changed files for conditional job execution | |
| changed-files: | |
| runs-on: ubuntu-latest | |
| # Skip for tag pushes - version release doesn't need changed-files check | |
| if: github.ref_type != 'tag' | |
| outputs: | |
| docs: ${{ steps.changes.outputs.docs }} | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| # Do not use fetch-depth: 0 — changed-files now works with shallow clone | |
| - name: Check for changes | |
| id: changes | |
| uses: ./.github/actions/changed-files | |
| with: | |
| gh_token: ${{ github.token }} | |
| ############################################################################# | |
| # LINT JOBS - Run on PRs when docs/** files change | |
| ############################################################################# | |
| fern-check: | |
| name: Fern Configuration Check | |
| needs: changed-files | |
| if: | | |
| github.ref_type != 'tag' && | |
| needs.changed-files.outputs.docs == 'true' && | |
| (github.event_name == 'pull_request' || startsWith(github.ref, 'refs/heads/pull-request/')) | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: '22' | |
| - name: Install Fern CLI | |
| run: npm install -g fern-api | |
| - name: Validate Fern configuration | |
| run: fern check | |
| fern-broken-links: | |
| name: Fern Broken Links Check | |
| needs: changed-files | |
| if: | | |
| github.ref_type != 'tag' && | |
| needs.changed-files.outputs.docs == 'true' && | |
| (github.event_name == 'pull_request' || startsWith(github.ref, 'refs/heads/pull-request/')) | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: '22' | |
| - name: Install Fern CLI | |
| run: npm install -g fern-api | |
| - name: Check for broken links | |
| run: fern docs broken-links | |
| ############################################################################# | |
| # SYNC & PUBLISH/PREVIEW - Syncs docs content to docs-website structure | |
| # On main: commits, pushes, and publishes to Fern | |
| # On PRs: generates a preview URL and comments on the PR | |
| ############################################################################# | |
| preview-or-publish-docs: | |
| name: Preview or publish docs | |
| needs: changed-files | |
| if: | | |
| github.ref_type != 'tag' && | |
| (needs.changed-files.outputs.docs == 'true' || github.event_name == 'workflow_dispatch') && | |
| (github.event.inputs.tag == '' || github.event.inputs.tag == null) | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Determine context | |
| id: ctx | |
| run: | | |
| if [ "$GITHUB_REF" = "refs/heads/main" ]; then | |
| echo "is_main=true" >> $GITHUB_OUTPUT | |
| else | |
| echo "is_main=false" >> $GITHUB_OUTPUT | |
| fi | |
| - name: Checkout source branch | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| with: | |
| path: source-checkout | |
| fetch-depth: 1 | |
| - name: Checkout docs-website branch | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| with: | |
| ref: docs-website | |
| path: docs-checkout | |
| fetch-depth: 1 | |
| token: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Sync dev content from main | |
| run: | | |
| # Sync content directories from docs/ to fern/pages-dev/ on docs-website | |
| # Excludes non-content items that are synced separately or not needed | |
| echo "Syncing content pages to docs-website branch..." | |
| rm -rf docs-checkout/fern/pages-dev | |
| mkdir -p docs-checkout/fern/pages-dev | |
| rsync -a \ | |
| --exclude='blogs' \ | |
| --exclude='index.yml' \ | |
| source-checkout/docs/ docs-checkout/fern/pages-dev/ | |
| # Sync index.yml as versions/dev.yml and transform paths for docs-website layout | |
| echo "Syncing index.yml to docs-website branch as versions/dev.yml..." | |
| cp source-checkout/docs/index.yml docs-checkout/fern/versions/dev.yml | |
| # Sync fern.config.json | |
| echo "Syncing fern.config.json to docs-website branch..." | |
| cp source-checkout/fern/fern.config.json docs-checkout/fern/fern.config.json | |
| # Sync docs/README.md (developer guide, referenced from dev.yml) | |
| if [ -f source-checkout/docs/README.md ]; then | |
| cp source-checkout/docs/README.md docs-checkout/fern/README.md | |
| fi | |
| # Sync .gitignore if it exists | |
| if [ -f source-checkout/fern/.gitignore ]; then | |
| cp source-checkout/fern/.gitignore docs-checkout/fern/.gitignore | |
| fi | |
| # Sync convert_callouts.py script | |
| if [ -f source-checkout/fern/convert_callouts.py ]; then | |
| cp source-checkout/fern/convert_callouts.py docs-checkout/fern/convert_callouts.py | |
| fi | |
| # Sync components/ directory (e.g., CustomFooter.tsx) | |
| if [ -d source-checkout/fern/components ]; then | |
| echo "Syncing components/ to docs-website branch..." | |
| rm -rf docs-checkout/fern/components | |
| cp -r source-checkout/fern/components docs-checkout/fern/components | |
| fi | |
| # Sync blogs/ directory | |
| if [ -d source-checkout/docs/blogs ]; then | |
| echo "Syncing blogs/ to docs-website branch..." | |
| rm -rf docs-checkout/fern/blogs | |
| cp -r source-checkout/docs/blogs docs-checkout/fern/blogs | |
| fi | |
| # Sync main.css | |
| if [ -f source-checkout/fern/main.css ]; then | |
| echo "Syncing main.css to docs-website branch..." | |
| cp source-checkout/fern/main.css docs-checkout/fern/main.css | |
| fi | |
| - name: Transform paths in dev.yml for docs-website layout | |
| run: | | |
| # In the source repo, index.yml uses paths relative to docs/ (e.g. getting-started/quickstart.md). | |
| # On docs-website, fern/versions/dev.yml needs ../pages-dev/ prefix for content | |
| # and ../ prefix for blogs (which are a sibling of pages/, not inside it). | |
| # | |
| # Order matters: transform blogs/ first so the second sub() won't re-match them | |
| # (they'll start with ../ which doesn't match ^[a-zA-Z]). | |
| yq -i '(.. | select(has("path")).path) |= sub("^blogs/", "../blogs/")' docs-checkout/fern/versions/dev.yml | |
| yq -i '(.. | select(has("path")).path) |= sub("^([a-zA-Z])", "../pages-dev/${1}")' docs-checkout/fern/versions/dev.yml | |
| - name: Convert GitHub callouts to Fern format | |
| run: | | |
| echo "Converting GitHub-style callouts to Fern format in pages/..." | |
| python3 docs-checkout/fern/convert_callouts.py --dir docs-checkout/fern/pages-dev | |
| echo "Callout conversion complete." | |
| - name: Update docs.yml preserving products | |
| run: | | |
| cd docs-checkout/fern | |
| # Save the full products[0] block from docs-website (versions, path, etc.) | |
| yq '.products[0]' docs.yml > /tmp/preserved_product.yml | |
| echo "Preserved products[0] block:" | |
| cat /tmp/preserved_product.yml | |
| # Copy docs.yml from source to get config updates (redirects, layout, etc.) | |
| cp ../../source-checkout/fern/docs.yml docs.yml | |
| # Fix asset paths: source uses ../docs/assets/ (relative to fern/ in source repo), | |
| # but on docs-website assets live at fern/assets/ so we need ./assets/ | |
| sed -i 's|\.\./docs/assets/|./assets/|g' docs.yml | |
| # Restore the preserved products[0] block | |
| yq -i '.products[0] = load("/tmp/preserved_product.yml")' docs.yml | |
| echo "Updated docs.yml:" | |
| cat docs.yml | |
| - name: Check for changes | |
| id: changes | |
| run: | | |
| cd docs-checkout | |
| if git diff --quiet && git diff --cached --quiet; then | |
| echo "has_changes=false" >> $GITHUB_OUTPUT | |
| echo "No changes detected" | |
| else | |
| echo "has_changes=true" >> $GITHUB_OUTPUT | |
| echo "Changes detected:" | |
| git status --short | |
| fi | |
| - name: Setup Node.js | |
| if: steps.changes.outputs.has_changes == 'true' | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: '22' | |
| - name: Install Fern CLI | |
| if: steps.changes.outputs.has_changes == 'true' | |
| run: npm install -g fern-api | |
| ########################################################################## | |
| # PREVIEW - Generate a preview URL for docs changes | |
| ########################################################################## | |
| - name: Generate docs preview | |
| if: steps.ctx.outputs.is_main != 'true' && steps.changes.outputs.has_changes == 'true' | |
| id: preview | |
| working-directory: docs-checkout/fern | |
| env: | |
| FERN_TOKEN: ${{ secrets.FERN_TOKEN }} | |
| run: | | |
| if OUTPUT=$(fern generate --docs --preview 2>&1); then | |
| FERN_EXIT=0 | |
| else | |
| FERN_EXIT=$? | |
| fi | |
| echo "$OUTPUT" | |
| if [ $FERN_EXIT -ne 0 ]; then | |
| echo "::error::Fern docs preview generation failed (exit $FERN_EXIT)" | |
| exit 1 | |
| fi | |
| URL=$(echo "$OUTPUT" | grep -oP 'Published docs to \K\S+') || true | |
| if [ -n "$URL" ]; then | |
| echo "url=$URL" >> $GITHUB_OUTPUT | |
| fi | |
| - name: Comment preview URL on PR | |
| if: steps.ctx.outputs.is_main != 'true' && steps.preview.outputs.url != '' && startsWith(github.ref, 'refs/heads/pull-request/') | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| PR_NUM="${GITHUB_REF##*/}" | |
| gh pr comment "https://github.com/${{ github.repository }}/pull/${PR_NUM}" \ | |
| --edit-last --create-if-none \ | |
| --body "🌿 **Fern Docs Preview:** ${{ steps.preview.outputs.url }}/dev" | |
| ########################################################################## | |
| # PUSH AND PUBLISH - push changes to docs-website branch and publish docs | |
| ########################################################################## | |
| - name: Setup Git | |
| if: steps.ctx.outputs.is_main == 'true' && steps.changes.outputs.has_changes == 'true' | |
| run: | | |
| cd docs-checkout | |
| git config user.name "github-actions[bot]" | |
| git config user.email "github-actions[bot]@users.noreply.github.com" | |
| - name: Commit and push changes | |
| if: steps.ctx.outputs.is_main == 'true' && steps.changes.outputs.has_changes == 'true' | |
| run: | | |
| cd docs-checkout | |
| git add -A | |
| git commit -m "docs(fern): sync dev from main | |
| Automated sync of docs/ directory from main branch. | |
| Preserves versioned documentation snapshots. | |
| Source commit: ${{ github.sha }}" | |
| git push origin docs-website | |
| echo "Successfully synced dev docs to docs-website branch" | |
| - name: Publish Docs | |
| if: steps.ctx.outputs.is_main == 'true' && steps.changes.outputs.has_changes == 'true' | |
| env: | |
| FERN_TOKEN: ${{ secrets.FERN_TOKEN }} | |
| working-directory: docs-checkout/fern | |
| run: fern generate --docs | |
| ############################################################################# | |
| # VERSION RELEASE - Run on new version tags (vX.Y.Z) | |
| ############################################################################# | |
| release-version: | |
| name: Release Version to docs-website | |
| # Run on tag push OR manual dispatch with a tag specified | |
| if: | | |
| github.ref_type == 'tag' || | |
| (github.event_name == 'workflow_dispatch' && github.event.inputs.tag != '' && github.event.inputs.tag != null) | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Determine version tag | |
| id: version | |
| run: | | |
| if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then | |
| TAG="${{ github.event.inputs.tag }}" | |
| else | |
| TAG="${GITHUB_REF#refs/tags/}" | |
| fi | |
| # Validate tag format (must be vX.Y.Z exactly) | |
| if ! echo "$TAG" | grep -qE '^v[0-9]+\.[0-9]+\.[0-9]+$'; then | |
| echo "::error::Invalid tag format: $TAG. Must be vX.Y.Z (e.g., v0.9.0)" | |
| exit 1 | |
| fi | |
| # Extract version without 'v' prefix | |
| VERSION="${TAG#v}" | |
| echo "tag=$TAG" >> $GITHUB_OUTPUT | |
| echo "version=$VERSION" >> $GITHUB_OUTPUT | |
| echo "Processing version: $VERSION (tag: $TAG)" | |
| - name: Checkout docs-website branch | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| with: | |
| ref: docs-website | |
| token: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Check if version already exists | |
| run: | | |
| TAG="${{ steps.version.outputs.tag }}" | |
| if [ -d "fern/pages-$TAG" ]; then | |
| echo "::error::Version $TAG already exists (fern/pages-$TAG directory found)" | |
| exit 1 | |
| fi | |
| if [ -f "fern/versions/$TAG.yml" ]; then | |
| echo "::error::Version $TAG already exists (fern/versions/$TAG.yml found)" | |
| exit 1 | |
| fi | |
| echo "Version $TAG does not exist yet, proceeding with release" | |
| - name: Setup Git | |
| run: | | |
| git config user.name "github-actions[bot]" | |
| git config user.email "github-actions[bot]@users.noreply.github.com" | |
| - name: Create versioned pages directory | |
| run: | | |
| TAG="${{ steps.version.outputs.tag }}" | |
| echo "Creating fern/pages-$TAG/ from fern/pages-dev/..." | |
| # Copy current pages-dev/ to pages-vX.Y.Z/ | |
| cp -r fern/pages-dev "fern/pages-$TAG" | |
| echo "Created fern/pages-$TAG/" | |
| ls -la "fern/pages-$TAG/" | head -20 | |
| - name: Update GitHub links to 'main' to version tag | |
| run: | | |
| TAG="${{ steps.version.outputs.tag }}" | |
| echo "Updating GitHub links from 'tree/main' to 'tree/$TAG' in fern/pages-$TAG/..." | |
| # Find all markdown files and replace tree/main with tree/vX.Y.Z | |
| find "fern/pages-$TAG" -name "*.md" -o -name "*.mdx" | while read file; do | |
| if grep -q "github.com/ai-dynamo/dynamo/tree/main" "$file"; then | |
| echo "Updating: $file" | |
| sed -i "s|github.com/ai-dynamo/dynamo/tree/main|github.com/ai-dynamo/dynamo/tree/$TAG|g" "$file" | |
| fi | |
| done | |
| # Also update blob/main references (for direct file links) | |
| find "fern/pages-$TAG" -name "*.md" -o -name "*.mdx" | while read file; do | |
| if grep -q "github.com/ai-dynamo/dynamo/blob/main" "$file"; then | |
| echo "Updating blob links: $file" | |
| sed -i "s|github.com/ai-dynamo/dynamo/blob/main|github.com/ai-dynamo/dynamo/blob/$TAG|g" "$file" | |
| fi | |
| done | |
| echo "GitHub link update complete." | |
| - name: Convert GitHub callouts to Fern format | |
| run: | | |
| TAG="${{ steps.version.outputs.tag }}" | |
| echo "Converting GitHub-style callouts to Fern format in pages-$TAG/..." | |
| python3 fern/convert_callouts.py --dir "fern/pages-$TAG" | |
| echo "Callout conversion complete." | |
| - name: Create version config file | |
| run: | | |
| TAG="${{ steps.version.outputs.tag }}" | |
| VERSION="${{ steps.version.outputs.version }}" | |
| VERSION_FILE="fern/versions/$TAG.yml" | |
| echo "Creating version config: $VERSION_FILE" | |
| # Copy dev.yml as template | |
| cp fern/versions/dev.yml "$VERSION_FILE" | |
| # Update the comment at the top | |
| sed -i "s/# Navigation structure for Latest version/# Navigation structure for $TAG version/" "$VERSION_FILE" | |
| sed -i "s|# Matching https://docs.nvidia.com/dynamo/latest/|# Snapshot from tag $TAG|" "$VERSION_FILE" | |
| # Update all page paths from ../pages-dev/ to ../pages-vX.Y.Z/ | |
| sed -i "s|path: \.\./pages-dev/|path: ../pages-$TAG/|g" "$VERSION_FILE" | |
| echo "Created $VERSION_FILE" | |
| echo "First 30 lines:" | |
| head -30 "$VERSION_FILE" | |
| - name: Update docs.yml with new version | |
| run: | | |
| TAG="${{ steps.version.outputs.tag }}" | |
| DOCS_FILE="fern/docs.yml" | |
| echo "Updating $DOCS_FILE to include $TAG..." | |
| # Check if version already in docs.yml | |
| if yq ".products[0].versions[] | select(.display-name == \"$TAG\")" "$DOCS_FILE" | grep -q .; then | |
| echo "Version $TAG already in docs.yml, skipping update" | |
| exit 0 | |
| fi | |
| # Find the index of the "dev" entry and insert new version right after it | |
| DEV_IDX=$(yq '.products[0].versions | to_entries | map(select(.value.display-name == "dev")) | .[0].key' "$DOCS_FILE") | |
| INSERT_IDX=$((DEV_IDX + 1)) | |
| yq -i " | |
| .products[0].versions |= ( | |
| .[:$INSERT_IDX] + | |
| [{\"display-name\": \"$TAG\", \"path\": \"./versions/$TAG.yml\", \"slug\": \"$TAG\", \"availability\": \"stable\"}] + | |
| .[$INSERT_IDX:] | |
| ) | |
| " "$DOCS_FILE" | |
| # Update the top-level entry to point to the new version | |
| yq -i ".products[0].path = \"./versions/$TAG.yml\"" "$DOCS_FILE" | |
| # Update the "Latest" entry to point to the new version | |
| yq -i ".products[0].versions[0].path = \"./versions/$TAG.yml\"" "$DOCS_FILE" | |
| yq -i ".products[0].versions[0].display-name = \"Latest ($TAG)\"" "$DOCS_FILE" | |
| echo "Updated docs.yml products/versions section:" | |
| yq '.products[0].versions' "$DOCS_FILE" | |
| - name: Commit and push changes | |
| run: | | |
| TAG="${{ steps.version.outputs.tag }}" | |
| git add "fern/pages-$TAG/" | |
| git add "fern/versions/$TAG.yml" | |
| git add fern/docs.yml | |
| git commit -m "docs(fern): release version $TAG | |
| - Created fern/pages-$TAG/ with documentation snapshot | |
| - Created fern/versions/$TAG.yml version navigation config | |
| - Updated fern/docs.yml to include $TAG in version list | |
| Automated by fern-docs workflow | |
| Source tag: $TAG" | |
| git push origin docs-website | |
| echo "Successfully released documentation for $TAG on docs-website branch" | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: '22' | |
| - name: Install Fern CLI | |
| run: npm install -g fern-api | |
| - name: Publish Docs | |
| env: | |
| FERN_TOKEN: ${{ secrets.FERN_TOKEN }} | |
| working-directory: ./fern | |
| run: fern generate --docs |