External Documentation Deployment Guide

This guide explains how to set up automatic documentation deployment from your project to zircote.github.io.

Overview

The external docs deployment system allows projects to automatically publish their generated documentation (rustdoc, typedoc, sphinx, etc.) and ADR viewers to the website without manual intervention or PR review.

Architecture:

Source Project                    zircote.github.io
     |                                   |
     | 1. Build docs (cargo doc, etc.)   |
     |                                   |
     | 2. Upload as artifact             |
     |                                   |
     | 3. Dispatch deploy-docs event --->|
     |                                   |
     |                    4. Validate project in registry
     |                    5. Download artifact
     |                    6. Deploy to projects/{id}/docs/
     |                    7. Trigger Jekyll rebuild
     |                                   |
     |<-- 8. Docs available at URL       |

Prerequisites

Before setting up deployment, ensure:

  1. Your project is registered in _data/external_docs.yml
  2. You have a Personal Access Token (PAT) with repo scope
  3. The PAT is stored as DOCS_DEPLOY_TOKEN in your repository secrets

Setup Steps

1. Register Your Project

Add your project to _data/external_docs.yml in zircote.github.io:

your-project:
  name: "Your Project"
  description: "Brief description"
  repository: "zircote/your-project"
  doc_type: "rustdoc"  # or typedoc, sphinx, custom
  supports_adrs: true
  enabled: true

2. Create a Deploy Token

  1. Go to https://github.com/settings/tokens
  2. Generate new token (classic)
  3. Select repo scope (full control of private repositories)
  4. Copy the token

3. Add Secret to Your Repository

  1. Go to your repository Settings > Secrets and variables > Actions
  2. Create new secret: DOCS_DEPLOY_TOKEN
  3. Paste the PAT value

4. Add Deployment Workflow

Create .github/workflows/deploy-docs.yml in your repository using one of the templates below.

Workflow Templates

Rust Project (cargo doc)

name: Deploy Documentation

on:
  push:
    branches: [main]
    paths:
      - "src/**"
      - "Cargo.toml"
      - ".github/workflows/deploy-docs.yml"
  release:
    types: [published]
  workflow_dispatch:
    inputs:
      version:
        description: "Documentation version label"
        required: false
        default: "latest"

permissions:
  contents: read
  actions: write

jobs:
  build-and-deploy:
    name: Build and Deploy Documentation
    runs-on: ubuntu-latest
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      - name: Setup Rust
        uses: dtolnay/rust-toolchain@stable

      - name: Build documentation
        run: |
          cargo doc --no-deps --all-features
          # Create redirect to main crate docs
          echo '<meta http-equiv="refresh" content="0; url=YOUR_CRATE_NAME/index.html">' > target/doc/index.html

      - name: Determine version
        id: version
        run: |
          if [ "$" == "release" ]; then
            echo "version=$" >> "$GITHUB_OUTPUT"
          elif [ "$" == "workflow_dispatch" ]; then
            echo "version=$" >> "$GITHUB_OUTPUT"
          else
            echo "version=latest" >> "$GITHUB_OUTPUT"
          fi

      - name: Deploy documentation
        uses: zircote/zircote.github.io/.github/actions/docs-deploy@main
        with:
          project-id: YOUR_PROJECT_ID
          docs-path: target/doc
          version: $
        env:
          DOCS_DEPLOY_TOKEN: $

TypeScript Project (TypeDoc)

name: Deploy Documentation

on:
  push:
    branches: [main]
    paths:
      - "src/**"
      - "package.json"
      - ".github/workflows/deploy-docs.yml"
  release:
    types: [published]
  workflow_dispatch:

permissions:
  contents: read
  actions: write

jobs:
  build-and-deploy:
    name: Build and Deploy Documentation
    runs-on: ubuntu-latest
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: "22"
          cache: "npm"

      - name: Install dependencies
        run: npm ci

      - name: Build documentation
        run: npx typedoc src/index.ts --out docs

      - name: Determine version
        id: version
        run: |
          if [ "$" == "release" ]; then
            echo "version=$" >> "$GITHUB_OUTPUT"
          else
            echo "version=latest" >> "$GITHUB_OUTPUT"
          fi

      - name: Deploy documentation
        uses: zircote/zircote.github.io/.github/actions/docs-deploy@main
        with:
          project-id: YOUR_PROJECT_ID
          docs-path: docs
          version: $
        env:
          DOCS_DEPLOY_TOKEN: $

Python Project (Sphinx)

name: Deploy Documentation

on:
  push:
    branches: [main]
    paths:
      - "src/**"
      - "docs/**"
      - "pyproject.toml"
      - ".github/workflows/deploy-docs.yml"
  release:
    types: [published]
  workflow_dispatch:

permissions:
  contents: read
  actions: write

jobs:
  build-and-deploy:
    name: Build and Deploy Documentation
    runs-on: ubuntu-latest
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      - name: Setup Python
        uses: actions/setup-python@v5
        with:
          python-version: "3.12"

      - name: Install dependencies
        run: |
          pip install -e ".[docs]"
          # Or: pip install sphinx sphinx-rtd-theme

      - name: Build documentation
        run: sphinx-build -b html docs/source docs/_build/html

      - name: Determine version
        id: version
        run: |
          if [ "$" == "release" ]; then
            echo "version=$" >> "$GITHUB_OUTPUT"
          else
            echo "version=latest" >> "$GITHUB_OUTPUT"
          fi

      - name: Deploy documentation
        uses: zircote/zircote.github.io/.github/actions/docs-deploy@main
        with:
          project-id: YOUR_PROJECT_ID
          docs-path: docs/_build/html
          version: $
        env:
          DOCS_DEPLOY_TOKEN: $

With ADR Viewer (using adrscope)

name: Deploy Documentation

on:
  push:
    branches: [main]
    paths:
      - "src/**"
      - "docs/decisions/**"
      - "Cargo.toml"
  release:
    types: [published]
  workflow_dispatch:

permissions:
  contents: read
  actions: write

jobs:
  build-and-deploy:
    name: Build and Deploy Documentation
    runs-on: ubuntu-latest
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      - name: Setup Rust
        uses: dtolnay/rust-toolchain@stable

      - name: Build documentation
        run: |
          cargo doc --no-deps --all-features
          echo '<meta http-equiv="refresh" content="0; url=YOUR_CRATE_NAME/index.html">' > target/doc/index.html

      - name: Generate ADR viewer
        uses: zircote/adrscope/.github/actions/adrscope@v0
        with:
          command: generate
          input-dir: docs/decisions
          output: adr-viewer.html
          theme: system

      - name: Determine version
        id: version
        run: |
          if [ "$" == "release" ]; then
            echo "version=$" >> "$GITHUB_OUTPUT"
          else
            echo "version=latest" >> "$GITHUB_OUTPUT"
          fi

      - name: Deploy documentation
        uses: zircote/zircote.github.io/.github/actions/docs-deploy@main
        with:
          project-id: YOUR_PROJECT_ID
          docs-path: target/doc
          version: $
          include-adrs: adr-viewer.html
        env:
          DOCS_DEPLOY_TOKEN: $

Action Reference

Inputs

Input Required Default Description
project-id Yes - Project identifier (must match registry)
docs-path Yes - Path to generated documentation directory
version Yes - Documentation version label
include-adrs No "" Path to ADR viewer HTML file
target-repo No zircote/zircote.github.io Target repository
dry-run No false Validate without deploying

Outputs

Output Description
deployed-url URL of the deployed documentation
artifact-name Name of the uploaded artifact

Environment Variables

Variable Required Description
DOCS_DEPLOY_TOKEN Yes PAT with repo scope

URL Structure

After deployment, documentation is available at:

Troubleshooting

“Project not found in registry”

Your project is not registered in _data/external_docs.yml. Submit a PR to add it or contact the repository owner.

“Source repository mismatch”

The workflow is running from a repository that does not match the registered repository for this project ID. Check that the repository field in the registry matches your repository.

“Failed to dispatch deployment”

Common causes:

“No index.html found”

Your documentation output must include an index.html file at the root. For Rust projects, you may need to create a redirect file:

echo '<meta http-equiv="refresh" content="0; url=crate_name/index.html">' > target/doc/index.html

Rollback

If a deployment causes issues, you can rollback to a previous version:

# List available backups
git tag --list 'backup/docs-PROJECT_ID-*'

# Restore from specific backup
git checkout backup/docs-PROJECT_ID-TIMESTAMP -- projects/PROJECT_ID/docs/

# Commit the restoration
git commit -m "revert(PROJECT_ID): rollback docs to previous version"
git push origin main

Security

Adding a New Project

To add a new project to the deployment system:

  1. Add entry to _data/external_docs.yml
  2. Add entry to keep_files in _config.yml
  3. Create PAT and add as secret in source repository
  4. Add deployment workflow to source repository
  5. Test with dry-run: true first