Skip to content

MAINT: Strongly Typed Identifiers #8030

MAINT: Strongly Typed Identifiers

MAINT: Strongly Typed Identifiers #8030

Workflow file for this run

name: build-book
on:
push:
branches:
- main
- "releases/v*"
paths-ignore:
- "**/*.md"
- ".github/ISSUE_TEMPLATE/**"
pull_request:
branches:
- main
- "releases/**"
paths:
- ".github/workflows/docs.yml"
- ".github/docs-versions.yml"
- "build_scripts/inject_version_picker.py"
- "build_scripts/version_picker_assets/**"
- "doc/**"
- "build_scripts/pydoc2json.py"
- "build_scripts/gen_api_md.py"
- "pyrit/**"
workflow_dispatch:
# Permissions for the GH Pages deployment.
permissions:
contents: read
pages: write
id-token: write
# One deploy at a time. Don't cancel in-progress production deploys.
concurrency:
group: pages
cancel-in-progress: false
env:
# All versioned URLs live under /<repo-name>/<slug>/, e.g. /PyRIT/0.13.0/.
DOCS_BASE: /${{ github.event.repository.name }}
# Manual cache-bust knob. Bump this when the build pipeline changes in a
# way that affects the rendered HTML output but isn't captured by the
# source-ref SHA -- e.g. bumping python-version, uv version, or changing
# build flags in the steps below. Forgetting to bump is harmless (you
# just serve a stale cached build for one push); bumping unnecessarily
# is also harmless (one extra rebuild).
CACHE_VERSION: "v1"
jobs:
# ------------------------------------------------------------------
# 1. Read .github/docs-versions.yml and turn it into a build matrix.
# ------------------------------------------------------------------
versions:
name: Resolve version matrix
runs-on: ubuntu-latest
outputs:
matrix: ${{ steps.matrix.outputs.matrix }}
default: ${{ steps.matrix.outputs.default }}
stable: ${{ steps.matrix.outputs.stable }}
versions_json: ${{ steps.matrix.outputs.versions_json }}
steps:
- uses: actions/checkout@v6
with:
sparse-checkout: |
.github/docs-versions.yml
build_scripts/resolve_docs_matrix.py
sparse-checkout-cone-mode: false
- name: Set up Python 3.13
uses: actions/setup-python@v6
with:
python-version: "3.13"
- name: Install PyYAML
run: pip install --quiet "pyyaml>=6.0"
- name: Compute matrix
id: matrix
run: |
python build_scripts/resolve_docs_matrix.py \
--config .github/docs-versions.yml \
--github-output "$GITHUB_OUTPUT"
# ------------------------------------------------------------------
# 2. Build each version in parallel.
# ------------------------------------------------------------------
build:
name: Build ${{ matrix.slug }}
needs: versions
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix: ${{ fromJson(needs.versions.outputs.matrix) }}
env:
BASE_URL: ${{ format('/{0}/{1}', github.event.repository.name, matrix.slug) }}
steps:
- name: Checkout ${{ matrix.ref }}
uses: actions/checkout@v6
with:
ref: ${{ matrix.ref }}
- name: Resolve commit SHA
# matrix.ref is a branch name (e.g. "releases/v0.13.0") that can move,
# so resolve to the actual commit SHA we just checked out. This is the
# primary cache key component: frozen release branches that don't move
# produce identical SHAs and hit the cache forever; main produces a
# new SHA on every push so it always rebuilds.
id: sha
run: echo "sha=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT"
- name: Restore built site from cache
# Cache hit -> we skip the entire install + build pipeline and just
# upload the restored doc/_build/html as the matrix artifact. Cache
# miss -> we do a fresh build and actions/cache saves the result on
# job success, so the next run with the same SHA hits.
id: cache
uses: actions/cache@v5
with:
path: doc/_build/html
key: docs-${{ env.CACHE_VERSION }}-${{ matrix.slug }}-${{ steps.sha.outputs.sha }}
- name: Set up Python 3.13
if: steps.cache.outputs.cache-hit != 'true'
uses: actions/setup-python@v6
with:
python-version: "3.13"
- name: Install uv
if: steps.cache.outputs.cache-hit != 'true'
uses: astral-sh/setup-uv@v7
with:
version: "0.9.17"
enable-cache: true
cache-dependency-glob: |
**/pyproject.toml
**/uv.lock
- name: Install PyRIT with dev dependencies
if: steps.cache.outputs.cache-hit != 'true'
# Use `uv sync --frozen` so each release branch installs the exact
# versions pinned in its own uv.lock. Frozen branch = frozen deps =
# bit-for-bit reproducible rebuild forever.
#
# Newer release branches (incl. main) use PEP 735 [dependency-groups];
# older releases use legacy [project.optional-dependencies]. We pick
# the right flag up front by reading pyproject.toml (via stdlib
# tomllib) so any uv sync failure surfaces normally instead of being
# swallowed by `|| fallback` with stderr redirected to /dev/null.
run: |
set -euo pipefail
choice=$(python -c "
import sys, tomllib
d = tomllib.loads(open('pyproject.toml').read())
if 'dev' in (d.get('dependency-groups') or {}):
print('--group dev')
elif 'dev' in (d.get('project', {}).get('optional-dependencies') or {}):
print('--extra dev')
else:
print('error: no dev group/extra in pyproject.toml', file=sys.stderr); sys.exit(1)
")
echo "Installing with: uv sync --frozen $choice"
uv sync --frozen $choice
- name: Generate API reference (pydoc2json + gen_api_md)
if: steps.cache.outputs.cache-hit != 'true'
run: |
source .venv/bin/activate
python build_scripts/pydoc2json.py pyrit --submodules -o doc/_api/pyrit_all.json
python build_scripts/gen_api_md.py
- name: Build the static HTML site
if: steps.cache.outputs.cache-hit != 'true'
# --all builds frontmatter exports (PDF, per doc/myst.yml).
# --html is required to produce the static HTML site we deploy.
# NOT using --strict because older release branches have known
# warnings (e.g. unresolved cross-refs) that aren't worth fixing
# retroactively just to satisfy strict mode.
working-directory: doc
run: |
source ../.venv/bin/activate
jupyter-book build --all --html
- name: Upload built site
uses: actions/upload-artifact@v7
with:
name: site-${{ matrix.slug }}
path: doc/_build/html
retention-days: 7
if-no-files-found: error
# ------------------------------------------------------------------
# 3. Compose dist/, inject picker, deploy to GH Pages.
# ------------------------------------------------------------------
deploy:
name: Deploy
needs: [versions, build]
runs-on: ubuntu-latest
# Deploy on push to main, or when triggered manually from main. Skipped
# for pushes to release branches (those builds are validation-only) and
# for pull requests.
if: >-
${{ github.ref == 'refs/heads/main' &&
(github.event_name == 'push' || github.event_name == 'workflow_dispatch') }}
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- uses: actions/checkout@v6
with:
sparse-checkout: |
.github/docs-versions.yml
build_scripts/inject_version_picker.py
build_scripts/version_picker_assets
build_scripts/generate_pages_manifest.py
build_scripts/compose_docs_dist.py
sparse-checkout-cone-mode: false
- name: Set up Python 3.13
uses: actions/setup-python@v6
with:
python-version: "3.13"
- name: Install PyYAML
run: pip install --quiet "pyyaml>=6.0"
- name: Download all version artifacts
uses: actions/download-artifact@v8
with:
path: artifacts
pattern: site-*
- name: Compose dist/
# Compose script does everything: stage artifacts into dist/<slug>/,
# write per-version pages.json manifests, write top-level versions.json,
# write the root + /stable/ redirects, and write 404.html (with the
# auto-redirect script that uses the manifest to find the closest
# sibling page in the same version).
run: |
python build_scripts/compose_docs_dist.py \
--artifacts-dir artifacts \
--dist-dir dist \
--config .github/docs-versions.yml \
--base "${DOCS_BASE}"
- name: Inject version picker
run: |
python build_scripts/inject_version_picker.py \
--site-dir dist \
--base "${DOCS_BASE}"
- name: Upload Pages artifact
uses: actions/upload-pages-artifact@v5
with:
path: dist
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v5