rama 0.3.0

modular service framework
Documentation
# Builds & deploys the mdBook site + per-target rustdoc trees to GitHub Pages.
#
# - Linux is the default (root /docs/).
# - macOS and Windows each get their own subdirectory under /docs/.
# - A `target-picker.html` is injected into every rustdoc page via
#   `--html-before-content` so users can switch platform from any deep link.
#
# `deploy` only runs on `main`; on feature branches the build still runs so the
# Pages artifact is downloadable for debugging.
name: Deploy mdBook site to Pages

env:
  CARGO_TERM_COLOR: always
  RUST_TOOLCHAIN: stable
  # Rustdoc uses `--cfg docsrs`, which enables nightly-only `doc_cfg`
  # annotations. Use a dated nightly with a small lag instead of tracking
  # today's nightly.
  RUSTDOC_NIGHTLY_LAG_DAYS: 14
  SEGMENT_DOWNLOAD_TIMEOUT_MINS: 5
  # rustdoc env shared by every doc job. Each job appends nothing platform-
  # specific; the platform comes from `--target` (or native target for
  # ubuntu/macos/windows-latest runners).
  RUSTDOCFLAGS_BASE: --cfg docsrs --html-before-content docs/target-picker.html

on:
  push:
    branches: ["main"]
  pull_request:
    branches: ["main"]
  workflow_dispatch:

permissions:
  contents: read

concurrency:
  group: "pages"
  cancel-in-progress: false

jobs:
  select-rustdoc-toolchain:
    runs-on: ubuntu-latest
    outputs:
      toolchain: ${{ steps.rustdoc-toolchain.outputs.toolchain }}
    steps:
      - name: Select rustdoc toolchain
        id: rustdoc-toolchain
        shell: bash
        run: |
          python3 - <<'PY' >> "$GITHUB_OUTPUT"
          from datetime import datetime, timedelta, timezone

          lag_days = int("${{ env.RUSTDOC_NIGHTLY_LAG_DAYS }}")
          nightly_date = datetime.now(timezone.utc) - timedelta(days=lag_days)
          print(f"toolchain=nightly-{nightly_date:%Y-%m-%d}")
          PY

  # ---- Per-target rustdoc builds ------------------------------------------
  # Each job runs `cargo +${RUSTDOC_TOOLCHAIN} doc --all-features --no-deps -p rama`
  # for one target and uploads its `target/[<triple>/]doc/` tree as an artifact.
  # The merge `build` job assembles them into a single site.

  doc-linux:
    needs: select-rustdoc-toolchain
    runs-on: ubuntu-latest
    env:
      RUSTDOC_TOOLCHAIN: ${{ needs.select-rustdoc-toolchain.outputs.toolchain }}
    steps:
      - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
      - uses: dtolnay/rust-toolchain@5b842231ba77f5c045dba54ac5560fed2db780e2
        with:
          toolchain: ${{ env.RUSTDOC_TOOLCHAIN }}
      - uses: ./.github/actions/rust-cache
        with:
          key: doc-linux
          env-vars: RUSTDOC_TOOLCHAIN
          cache-bin: "false"
      - name: cargo doc (linux)
        run: cargo +${{ env.RUSTDOC_TOOLCHAIN }} doc --all-features --no-deps -p rama
        env:
          RUSTDOCFLAGS: ${{env.RUSTDOCFLAGS_BASE}}
      - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
        with:
          name: doc-linux
          path: target/doc
          retention-days: 1

  doc-macos:
    needs: select-rustdoc-toolchain
    runs-on: macos-latest
    env:
      RUSTDOC_TOOLCHAIN: ${{ needs.select-rustdoc-toolchain.outputs.toolchain }}
    steps:
      - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
      - uses: dtolnay/rust-toolchain@5b842231ba77f5c045dba54ac5560fed2db780e2
        with:
          toolchain: ${{ env.RUSTDOC_TOOLCHAIN }}
      - uses: ./.github/actions/rust-cache
        with:
          key: doc-macos
          env-vars: RUSTDOC_TOOLCHAIN
          cache-bin: "false"
      - name: cargo doc (macos)
        run: cargo +${{ env.RUSTDOC_TOOLCHAIN }} doc --all-features --no-deps -p rama
        env:
          RUSTDOCFLAGS: ${{env.RUSTDOCFLAGS_BASE}}
      - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
        with:
          name: doc-macos
          path: target/doc
          retention-days: 1

  doc-windows:
    needs: select-rustdoc-toolchain
    runs-on: windows-latest
    env:
      RUSTDOC_TOOLCHAIN: ${{ needs.select-rustdoc-toolchain.outputs.toolchain }}
    steps:
      - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
      - uses: ilammy/setup-nasm@72793074d3c8cdda771dba85f6deafe00623038b # v1.5.2
      - uses: dtolnay/rust-toolchain@5b842231ba77f5c045dba54ac5560fed2db780e2
        with:
          toolchain: ${{ env.RUSTDOC_TOOLCHAIN }}
      - uses: ./.github/actions/rust-cache
        with:
          key: doc-windows
          env-vars: RUSTDOC_TOOLCHAIN
          cache-bin: "false"
      - name: cargo doc (windows)
        run: cargo +${{ env.RUSTDOC_TOOLCHAIN }} doc --all-features --no-deps -p rama
        env:
          RUSTDOCFLAGS: ${{env.RUSTDOCFLAGS_BASE}}
      - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
        with:
          name: doc-windows
          path: target/doc
          retention-days: 1

  # ---- Compose the site --------------------------------------------------
  build:
    runs-on: ubuntu-latest
    needs: [doc-linux, doc-macos, doc-windows]
    permissions:
      contents: read
      pages: write
    env:
      MDBOOK_VERSION: 0.5.2
      MDBOOK_GRAPHVIZ_VERSION: 0.3.1
    steps:
      - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
      - uses: dtolnay/rust-toolchain@5b842231ba77f5c045dba54ac5560fed2db780e2
        with:
          toolchain: ${{ env.RUST_TOOLCHAIN }}
      - name: Install Graphviz
        run: sudo apt-get install -y graphviz
      - name: Install mdBook
        run: |
          cargo install --version "${MDBOOK_VERSION}" mdbook 2>/dev/null || true
          cargo install --version "${MDBOOK_GRAPHVIZ_VERSION}" mdbook-graphviz 2>/dev/null || true
      - name: Setup Pages
        id: pages
        uses: actions/configure-pages@45bfe0192ca1faeb007ade9deae92b16b8254a0d # v6.0.0
      - name: Build with mdBook
        working-directory: ./docs/book
        run: mdbook build
      - name: Download per-target rustdoc artifacts
        uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
        with:
          path: doc-artifacts
      - name: Compose site layout
        working-directory: ./docs
        run: |
          set -eux
          mkdir -p www/book www/docs www/static
          cp -r book/book/* www/book/
          # Linux at the root of /docs/ — the canonical default target.
          cp -r ../doc-artifacts/doc-linux/. www/docs/
          # Other targets in their own subdirectory.
          for t in macos windows; do
            mkdir -p "www/docs/${t}"
            cp -r "../doc-artifacts/doc-${t}/." "www/docs/${t}/"
          done
          cp -r ./static/* www/static/
          cp index.html www/index.html
          cp trademark.html www/trademark.html
          cp sitemap.xml www/sitemap.xml
          cp robots.txt www/robots.txt
      - name: Report Pages payload size
        run: |
          set -eu
          bytes="$(du -sb ./docs/www | cut -f1)"
          files="$(find ./docs/www -type f | wc -l | tr -d ' ')"
          echo "Pages payload: ${bytes} bytes across ${files} files"
          if [ "${bytes}" -gt 1000000000 ]; then
            echo "::warning::GitHub Pages published sites may be no larger than 1 GB; this payload is ${bytes} bytes before artifact compression."
          fi
      - name: Upload artifact
        uses: actions/upload-pages-artifact@fc324d3547104276b827a68afc52ff2a11cc49c9 # v5.0.0
        with:
          path: ./docs/www

  # Deployment only runs from `main`. On feature branches & PRs the build job
  # still runs (so the Pages artifact is downloadable for debugging), but the
  # actual deploy is skipped because it would fail without the required
  # environment / permissions.
  deploy:
    if: github.ref == 'refs/heads/main'
    permissions:
      pages: write
      id-token: write
    environment:
      name: github-pages
      url: ${{ steps.deployment_1.outputs.page_url || steps.deployment_2.outputs.page_url || steps.deployment_3.outputs.page_url || steps.deployment_4.outputs.page_url || steps.deployment_5.outputs.page_url }}
    runs-on: ubuntu-latest
    needs: build
    steps:
      - name: Deploy to GitHub Pages (attempt 1)
        id: deployment_1
        uses: actions/deploy-pages@cd2ce8fcbc39b97be8ca5fce6e763baed58fa128 # v5.0.0
        continue-on-error: true

      - name: Wait before Pages deployment retry 2
        if: steps.deployment_1.outcome == 'failure'
        run: sleep 60
      - name: Deploy to GitHub Pages (attempt 2)
        if: steps.deployment_1.outcome == 'failure'
        id: deployment_2
        uses: actions/deploy-pages@cd2ce8fcbc39b97be8ca5fce6e763baed58fa128 # v5.0.0
        continue-on-error: true

      - name: Wait before Pages deployment retry 3
        if: steps.deployment_2.outcome == 'failure'
        run: sleep 60
      - name: Deploy to GitHub Pages (attempt 3)
        if: steps.deployment_2.outcome == 'failure'
        id: deployment_3
        uses: actions/deploy-pages@cd2ce8fcbc39b97be8ca5fce6e763baed58fa128 # v5.0.0
        continue-on-error: true

      - name: Wait before Pages deployment retry 4
        if: steps.deployment_3.outcome == 'failure'
        run: sleep 60
      - name: Deploy to GitHub Pages (attempt 4)
        if: steps.deployment_3.outcome == 'failure'
        id: deployment_4
        uses: actions/deploy-pages@cd2ce8fcbc39b97be8ca5fce6e763baed58fa128 # v5.0.0
        continue-on-error: true

      - name: Wait before Pages deployment retry 5
        if: steps.deployment_4.outcome == 'failure'
        run: sleep 60
      - name: Deploy to GitHub Pages (attempt 5)
        if: steps.deployment_4.outcome == 'failure'
        id: deployment_5
        uses: actions/deploy-pages@cd2ce8fcbc39b97be8ca5fce6e763baed58fa128 # v5.0.0