Skip to content
Latchkey

Documentation workflow (TorchIO-project/torchio)

The Documentation workflow from TorchIO-project/torchio, explained and optimized by Latchkey.

B

CI health: B - good

Point runs-on at Latchkey and get job timeouts, SHA-pinned actions, self-healing for flaky steps, and up to 58% lower cost, applied automatically.

Grade your own workflow free or run it on Latchkey →
Source: TorchIO-project/torchio.github/workflows/docs.ymlLicense Apache-2.0View source

What it does

This is the Documentation workflow from the TorchIO-project/torchio repository, a real project running GitHub Actions. It is shown here with attribution under its Apache-2.0 license.

Below, Latchkey shows a faster, safer version produced by its optimization engine.

The workflow

workflow (.yml)
# This workflow builds the documentation and either:
#   - deploys versioned docs to the `gh-pages` branch with mike (on `v1` and
#     `main`), or
#   - uploads a Smokeshow preview and comments the URL on the PR (other
#     branches), using a single "push" trigger so that secrets are available
#     even for PRs from forks.
#
# Versioning uses the Material team's fork of mike, integrated with Zensical
# (https://zensical.org/docs/setup/versioning/):
#   - `v1`   -> version "<major.minor>" with aliases `stable` + `latest`
#   - `main` (v2) -> version "<major.minor>" with alias `dev`
# `latest` is the default alias, so docs.torchio.org/ redirects to v1 while v2
# is a pre-release. Flip with `mike set-default` once v2 becomes stable.
#
# Both `v1` and `main` carry this workflow because Actions runs the version that
# lives on the branch that was pushed.
#
# One-time manual setup for production (cannot be done from CI):
#   1. Let `v1` deploy first so the `latest`/`stable`/default redirect exist
#      before the site goes live.
#   2. Settings > Pages > Source = "Deploy from a branch" -> `gh-pages` / root.
#   3. Settings > Pages > Custom domain = `docs.torchio.org`. GitHub writes a
#      CNAME file to `gh-pages`, which mike preserves on later deploys.

name: Documentation

on:
  push:
    branches: ['**'] # all branches, but not tags (avoids deploying on releases)

permissions:
  contents: write
  pull-requests: write # needed to post PR preview comments
  statuses: write # needed by smokeshow to set commit status

concurrency:
  group: docs-${{ github.ref }}
  cancel-in-progress: false

env:
  FORCE_COLOR: 1

jobs:
  preview:
    # Build + Smokeshow preview for branches/PRs that are not deployed versions.
    if: github.ref != 'refs/heads/main' && github.ref != 'refs/heads/v1'
    runs-on: ubuntu-latest
    steps:
      - name: Checkout repository
        uses: actions/checkout@v7

      - name: Install mise-en-place
        uses: jdx/mise-action@v4
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}

      - name: Restore TorchIO cached data
        id: cache-torchio-data-restore
        uses: actions/cache/restore@v6
        with:
          path: ~/.cache/torchio
          key: ${{ runner.os }}-torchio-data

      - name: Build docs
        run: mise run docs:build

      - name: Save TorchIO cached data
        if: steps.cache-torchio-data-restore.outputs.cache-hit != 'true'
        uses: actions/cache@v6
        with:
          path: ~/.cache/torchio
          key: ${{ steps.cache-torchio-data-restore.outputs.cache-primary-key }}

      - name: Upload docs to smokeshow
        id: smokeshow
        env:
          SMOKESHOW_AUTH_KEY: ${{ secrets.SMOKESHOW_AUTH_KEY || env.SMOKESHOW_AUTH_KEY }}
          SMOKESHOW_GITHUB_STATUS_DESCRIPTION: Docs preview
          SMOKESHOW_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          SMOKESHOW_GITHUB_PR_HEAD_SHA: ${{ github.sha }}
        run: |
          # Smokeshow limit: 50 MB total, 25 MB per file
          # Remove large GIFs to stay under the limit
          find ./site -type f -size +25M -delete
          find ./site -type f -name '*.gif' -size +2M -delete
          uvx smokeshow upload ./site 2>&1 | tee /tmp/smokeshow_output.txt
          URL=$(grep -oE 'https://smokeshow[^ ]+' /tmp/smokeshow_output.txt | tail -1 | sed 's/,$//')
          echo "preview_url=$URL" >> "$GITHUB_OUTPUT"

      # sticky-pull-request-comment auto-detects the PR number only on
      # pull_request events. Since this workflow triggers on push, we look up the
      # PR number ourselves.
      - name: Find PR number
        if: steps.smokeshow.outputs.preview_url != ''
        id: find-pr
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          PR_NUMBER=$(gh pr list --head "${{ github.ref_name }}" --json number --jq '.[0].number' 2>/dev/null || echo "")
          echo "pr_number=$PR_NUMBER" >> "$GITHUB_OUTPUT"

      - name: Comment PR with preview URL
        if: steps.smokeshow.outputs.preview_url != '' && steps.find-pr.outputs.pr_number != ''
        uses: marocchino/sticky-pull-request-comment@v3
        with:
          header: docs-preview
          number: ${{ steps.find-pr.outputs.pr_number }}
          message: |
            ## πŸ“– Docs Preview

            Preview of the documentation for this PR:

            πŸ”— **${{ steps.smokeshow.outputs.preview_url }}**

            <sub>Built from ${{ github.sha }}</sub>

  deploy:
    # Deploy versioned docs to the gh-pages branch with mike (production).
    if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/v1'
    runs-on: ubuntu-latest
    # Serialize deploys across branches so concurrent main/v1 runs don't race on
    # the gh-pages branch.
    concurrency:
      group: docs-deploy
      cancel-in-progress: false
    steps:
      - name: Checkout repository
        uses: actions/checkout@v7
        with:
          fetch-depth: 0

      - name: Install mise-en-place
        uses: jdx/mise-action@v4
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}

      - name: Restore TorchIO cached data
        id: cache-torchio-data-restore
        uses: actions/cache/restore@v6
        with:
          path: ~/.cache/torchio
          key: ${{ runner.os }}-torchio-data

      - name: Configure git and fetch gh-pages
        run: |
          git config user.name "github-actions[bot]"
          git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
          # Make the existing versioned docs available locally so mike updates
          # them instead of recreating the branch from scratch.
          git fetch origin gh-pages --depth=1 || true

      - name: Deploy versioned docs with mike
        run: |
          VERSION=$(grep -m1 -E '^version = ' pyproject.toml | sed -E 's/^version = "(.*)"/\1/')
          MAJOR_MINOR=$(printf '%s' "$VERSION" | sed -E 's/^([0-9]+\.[0-9]+).*/\1/')
          echo "Deploying docs for $VERSION (mike version id: $MAJOR_MINOR)"
          if [ "${{ github.ref }}" = "refs/heads/v1" ]; then
            mise run docs:deploy -- --push "$MAJOR_MINOR" stable latest
            mise run docs:set-default -- --push latest
          else
            mise run docs:deploy -- --push "$MAJOR_MINOR" dev
          fi

      - name: Save TorchIO cached data
        if: steps.cache-torchio-data-restore.outputs.cache-hit != 'true'
        uses: actions/cache@v6
        with:
          path: ~/.cache/torchio
          key: ${{ steps.cache-torchio-data-restore.outputs.cache-primary-key }}

The same workflow, on Latchkey

Removes redundant runs and caps runaway jobs. Added and changed lines are highlighted.

# This workflow builds the documentation and either:
#   - deploys versioned docs to the `gh-pages` branch with mike (on `v1` and
#     `main`), or
#   - uploads a Smokeshow preview and comments the URL on the PR (other
#     branches), using a single "push" trigger so that secrets are available
#     even for PRs from forks.
#
# Versioning uses the Material team's fork of mike, integrated with Zensical
# (https://zensical.org/docs/setup/versioning/):
#   - `v1`   -> version "<major.minor>" with aliases `stable` + `latest`
#   - `main` (v2) -> version "<major.minor>" with alias `dev`
# `latest` is the default alias, so docs.torchio.org/ redirects to v1 while v2
# is a pre-release. Flip with `mike set-default` once v2 becomes stable.
#
# Both `v1` and `main` carry this workflow because Actions runs the version that
# lives on the branch that was pushed.
#
# One-time manual setup for production (cannot be done from CI):
#   1. Let `v1` deploy first so the `latest`/`stable`/default redirect exist
#      before the site goes live.
#   2. Settings > Pages > Source = "Deploy from a branch" -> `gh-pages` / root.
#   3. Settings > Pages > Custom domain = `docs.torchio.org`. GitHub writes a
#      CNAME file to `gh-pages`, which mike preserves on later deploys.
 
name: Documentation
 
on:
  push:
    branches: ['**'] # all branches, but not tags (avoids deploying on releases)
 
permissions:
  contents: write
  pull-requests: write # needed to post PR preview comments
  statuses: write # needed by smokeshow to set commit status
 
concurrency:
  group: docs-${{ github.ref }}
  cancel-in-progress: false
 
env:
  FORCE_COLOR: 1
 
jobs:
  preview:
    timeout-minutes: 30
    # Build + Smokeshow preview for branches/PRs that are not deployed versions.
    if: github.ref != 'refs/heads/main' && github.ref != 'refs/heads/v1'
    runs-on: latchkey-small
    steps:
      - name: Checkout repository
        uses: actions/checkout@v7
 
      - name: Install mise-en-place
        uses: jdx/mise-action@v4
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}
 
      - name: Restore TorchIO cached data
        id: cache-torchio-data-restore
        uses: actions/cache/restore@v6
        with:
          path: ~/.cache/torchio
          key: ${{ runner.os }}-torchio-data
 
      - name: Build docs
        run: mise run docs:build
 
      - name: Save TorchIO cached data
        if: steps.cache-torchio-data-restore.outputs.cache-hit != 'true'
        uses: actions/cache@v6
        with:
          path: ~/.cache/torchio
          key: ${{ steps.cache-torchio-data-restore.outputs.cache-primary-key }}
 
      - name: Upload docs to smokeshow
        id: smokeshow
        env:
          SMOKESHOW_AUTH_KEY: ${{ secrets.SMOKESHOW_AUTH_KEY || env.SMOKESHOW_AUTH_KEY }}
          SMOKESHOW_GITHUB_STATUS_DESCRIPTION: Docs preview
          SMOKESHOW_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          SMOKESHOW_GITHUB_PR_HEAD_SHA: ${{ github.sha }}
        run: |
          # Smokeshow limit: 50 MB total, 25 MB per file
          # Remove large GIFs to stay under the limit
          find ./site -type f -size +25M -delete
          find ./site -type f -name '*.gif' -size +2M -delete
          uvx smokeshow upload ./site 2>&1 | tee /tmp/smokeshow_output.txt
          URL=$(grep -oE 'https://smokeshow[^ ]+' /tmp/smokeshow_output.txt | tail -1 | sed 's/,$//')
          echo "preview_url=$URL" >> "$GITHUB_OUTPUT"
 
      # sticky-pull-request-comment auto-detects the PR number only on
      # pull_request events. Since this workflow triggers on push, we look up the
      # PR number ourselves.
      - name: Find PR number
        if: steps.smokeshow.outputs.preview_url != ''
        id: find-pr
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          PR_NUMBER=$(gh pr list --head "${{ github.ref_name }}" --json number --jq '.[0].number' 2>/dev/null || echo "")
          echo "pr_number=$PR_NUMBER" >> "$GITHUB_OUTPUT"
 
      - name: Comment PR with preview URL
        if: steps.smokeshow.outputs.preview_url != '' && steps.find-pr.outputs.pr_number != ''
        uses: marocchino/sticky-pull-request-comment@v3
        with:
          header: docs-preview
          number: ${{ steps.find-pr.outputs.pr_number }}
          message: |
            ## πŸ“– Docs Preview
 
            Preview of the documentation for this PR:
 
            πŸ”— **${{ steps.smokeshow.outputs.preview_url }}**
 
            <sub>Built from ${{ github.sha }}</sub>
 
  deploy:
    timeout-minutes: 30
    # Deploy versioned docs to the gh-pages branch with mike (production).
    if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/v1'
    runs-on: latchkey-small
    # Serialize deploys across branches so concurrent main/v1 runs don't race on
    # the gh-pages branch.
    concurrency:
      group: docs-deploy
      cancel-in-progress: false
    steps:
      - name: Checkout repository
        uses: actions/checkout@v7
        with:
          fetch-depth: 0
 
      - name: Install mise-en-place
        uses: jdx/mise-action@v4
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}
 
      - name: Restore TorchIO cached data
        id: cache-torchio-data-restore
        uses: actions/cache/restore@v6
        with:
          path: ~/.cache/torchio
          key: ${{ runner.os }}-torchio-data
 
      - name: Configure git and fetch gh-pages
        run: |
          git config user.name "github-actions[bot]"
          git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
          # Make the existing versioned docs available locally so mike updates
          # them instead of recreating the branch from scratch.
          git fetch origin gh-pages --depth=1 || true
 
      - name: Deploy versioned docs with mike
        run: |
          VERSION=$(grep -m1 -E '^version = ' pyproject.toml | sed -E 's/^version = "(.*)"/\1/')
          MAJOR_MINOR=$(printf '%s' "$VERSION" | sed -E 's/^([0-9]+\.[0-9]+).*/\1/')
          echo "Deploying docs for $VERSION (mike version id: $MAJOR_MINOR)"
          if [ "${{ github.ref }}" = "refs/heads/v1" ]; then
            mise run docs:deploy -- --push "$MAJOR_MINOR" stable latest
            mise run docs:set-default -- --push latest
          else
            mise run docs:deploy -- --push "$MAJOR_MINOR" dev
          fi
 
      - name: Save TorchIO cached data
        if: steps.cache-torchio-data-restore.outputs.cache-hit != 'true'
        uses: actions/cache@v6
        with:
          path: ~/.cache/torchio
          key: ${{ steps.cache-torchio-data-restore.outputs.cache-primary-key }}
 

What changed

2 third-party actions are referenced by a movable tag. Pin them to the commit SHA (Latchkey resolves and applies this automatically) so a repointed tag cannot change what runs.

This workflow runs 2 jobs per trigger. On Latchkey the same minutes cost up to 58% less than GitHub-hosted, with zero queue time.

Actions used in this workflow