Skip to content

FIX: Preserve word-selection parameters in converter identifiers #11804

FIX: Preserve word-selection parameters in converter identifiers

FIX: Preserve word-selection parameters in converter identifiers #11804

Workflow file for this run

name: build-book
on:
push:
branches:
- main
- "releases/v*"
paths:
- ".github/workflows/docs.yml"
- ".github/docs-versions.yml"
- "build_scripts/**"
- "doc/**"
- "pyrit/**"
- "assets/**"
- "tests/unit/build_scripts/**"
- "pyproject.toml"
- "uv.lock"
- ".python-version"
- "README.md"
- "LICENSE"
- "MANIFEST.in"
pull_request:
branches:
- main
- "releases/**"
paths:
- ".github/workflows/docs.yml"
- ".github/docs-versions.yml"
- "build_scripts/**"
- "doc/**"
- "pyrit/**"
- "assets/**"
- "tests/unit/build_scripts/**"
- "pyproject.toml"
- "uv.lock"
- ".python-version"
- "README.md"
- "LICENSE"
- "MANIFEST.in"
workflow_dispatch:
# PRs, including forks, only need read access. Deployment grants are job-scoped.
permissions:
contents: read
# Serialize the entire production pipeline, not just deployment, so a slow older
# build cannot overwrite a newer site. PRs and release validation never join it.
concurrency:
group: >-
${{ github.event_name == 'pull_request' && format('docs-pr-{0}', github.event.pull_request.number) ||
github.ref == 'refs/heads/main' && 'pages' || format('docs-validation-{0}', github.ref) }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
env:
# All versioned URLs live under /<repo-name>/<slug>/, e.g. /PyRIT/0.13.0/.
DOCS_BASE: /${{ github.event.repository.name }}
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 }}
compose: ${{ steps.matrix.outputs.compose }}
workflow_hash: ${{ steps.matrix.outputs.workflow_hash }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: ${{ github.sha }}
# The tested merge's first parent is the exact PR comparison base.
fetch-depth: 2
sparse-checkout: |
.github/docs-versions.yml
.github/workflows/docs.yml
/build_scripts/
sparse-checkout-cone-mode: false
- name: Set up Python 3.13
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: "3.13"
- name: Install uv
uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0
with:
version: "0.9.17"
enable-cache: false
- name: Compute matrix
id: matrix
env:
# Hash the tested workflow before any release-branch checkout.
WORKFLOW_HASH: ${{ hashFiles('.github/workflows/docs.yml') }}
run: |
uv run --no-project --with "pyyaml==6.0.3" python -m build_scripts.resolve_docs_matrix \
--config .github/docs-versions.yml \
--event-name "$GITHUB_EVENT_NAME" \
--ref "$GITHUB_REF" \
--sha "$GITHUB_SHA" \
--github-output "$GITHUB_OUTPUT"
echo "workflow_hash=$WORKFLOW_HASH" >> "$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@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: ${{ matrix.ref }}
- name: Resolve commit SHA
# PR previews use the immutable merge SHA. Other matrix refs may be
# release branches, so always key the cache by the actual checkout.
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@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: doc/_build/html
key: docs-${{ needs.versions.outputs.workflow_hash }}-${{ matrix.slug }}-${{ steps.sha.outputs.sha }}
- name: Set up Python 3.13
if: steps.cache.outputs.cache-hit != 'true'
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: "3.13"
- name: Install uv
if: steps.cache.outputs.cache-hit != 'true'
uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0
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: |
uv run --frozen --no-sync python -m build_scripts.pydoc2json pyrit --submodules -o doc/_api/pyrit_all.json
uv run --frozen --no-sync python -m build_scripts.gen_api_md
- 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: uv run --frozen --no-sync jupyter-book build --all --html
- name: Upload built site
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: site-${{ matrix.slug }}
path: doc/_build/html
retention-days: 7
if-no-files-found: error
# ------------------------------------------------------------------
# 3. Exercise composition on full matrices without deployment permissions.
# ------------------------------------------------------------------
compose:
name: Compose site
needs: [versions, build]
runs-on: ubuntu-latest
if: needs.versions.outputs.compose == 'true'
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: ${{ github.sha }}
sparse-checkout: |
.github/docs-versions.yml
/build_scripts/
sparse-checkout-cone-mode: false
- name: Set up Python 3.13
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: "3.13"
- name: Install uv
uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0
with:
version: "0.9.17"
enable-cache: false
- name: Download all version artifacts
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
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: |
uv run --no-project --with "pyyaml==6.0.3" python -m build_scripts.compose_docs_dist \
--artifacts-dir artifacts \
--dist-dir dist \
--config .github/docs-versions.yml \
--base "${DOCS_BASE}"
- name: Inject version picker
run: |
uv run --no-project python -m build_scripts.inject_version_picker \
--site-dir dist \
--base "${DOCS_BASE}"
- name: Upload Pages artifact
if: >-
${{ github.ref == 'refs/heads/main' &&
(github.event_name == 'push' || github.event_name == 'workflow_dispatch') }}
uses: actions/upload-pages-artifact@fc324d3547104276b827a68afc52ff2a11cc49c9 # v5.0.0
with:
path: dist
# ------------------------------------------------------------------
# 4. Only main push/manual runs can publish the complete composed site.
# ------------------------------------------------------------------
deploy:
name: Deploy
needs: compose
runs-on: ubuntu-latest
if: >-
${{ github.ref == 'refs/heads/main' &&
(github.event_name == 'push' || github.event_name == 'workflow_dispatch') }}
permissions:
pages: write
id-token: write
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@368f82528645a54fb793d4d04e342629a3f51346 # v5.0.1