Documentation publication¶
The documentation site follows the MkDocs Material and mike setup used by spark-on-k8s. The existing Markdown guides remain readable on GitHub. Search, navigation, code copying, light/dark mode, and the version selector are provided by Material.
Build and preview¶
From the repository root:
uv sync --locked --no-default-groups --python 3.12 --group docs
uv run --no-sync python -m unittest discover -s docs/tests -p 'test_*.py'
uv run --no-sync python -m unittest discover -s .github/scripts -p '*docs*.py'
uv run --no-sync mkdocs build --strict
uv run --no-sync python .github/scripts/check_docs.py site --version-preview
uv run --no-sync mkdocs serve
These commands do not publish. The docs group adds no core runtime dependencies
and does not need optional backend clients. Every Markdown guide must appear in
mkdocs.yml; missing pages, broken internal links, and unknown anchors fail the
strict build or the rendered-site checker. The checker also validates local
assets and redirects; it does not make network requests to external sites.
Keep normal repository-relative links in Markdown. docs/hooks.py converts links
that leave docs/ into GitHub source links at DOCS_SOURCE_SHA during rendering.
A local preview without that variable links to main. The recipe checkout command in the installation guide is pinned to that source SHA; other code samples
are not rewritten. Every PR builds the site in CI and retains a documentation-preview
artifact, including when only documentation changes.
Versions and source identity¶
| Trigger | Published location | Meaning |
|---|---|---|
Push to main |
develop/ |
Unreleased development documentation |
Stable numeric tag X.Y.Z |
X.Y.Z/ |
Documentation from that exact tag |
Prerelease tag such as X.Y.Zrc1 |
X.Y.Zrc1/ |
Candidate documentation, not stable |
Local mkdocs build or serve |
Local output | Unpublished preview |
Each generated page names its channel and links its exact source commit.
versions.json records the source SHA and channel for each published version.
Only the highest stable numeric Git tag may move latest; prereleases and lower
maintenance releases leave it unchanged. The root initially opens develop and
switches to latest once stable documentation is published. Old version trees
are preserved rather than rebuilt with new tooling.
Older package releases that predate this workflow are not relabeled with current content. Their documentation remains in their tagged source. A documentation tag and successful site deployment do not prove that the corresponding PyPI upload succeeded: verify the package release separately. Pushing any tag also triggers the existing PyPI release workflow. Never push a test tag to exercise docs.
How publishing works¶
.github/workflows/deploy-doc.yml runs on main and tag pushes. Its manual action
can recover a deployment for an existing main or tag ref; it does publish. It uses
Python 3.12 and the locked docs group. The workflow:
- Fetches the existing
gh-pagesarchive and builds the selected version locally with mike. Mike's temporary staging commits never enter published history. - Checks the complete versioned tree's links, anchors, assets, redirects, and version manifest before making an archive update.
- Writes the checked tree to
gh-pagesthrough GitHub'screateCommitOnBranchAPI and requires a valid GitHub signature. The expected parent SHA prevents overwriting a concurrent update. No local mike commit is pushed. - Uploads that same checked tree to GitHub Pages and deploys it through the
github-pagesenvironment. Thedocumentation-siteartifact retains the checked tree for 14 days; permanent version history lives ongh-pages.
Unlike spark-on-k8s's legacy branch publishing, this repository uses GitHub's Pages artifact deployment. This permits signed archive commits without a stored private signing key and does not rely on a bot branch push triggering another workflow. Alias pages are regular redirect files, not symlinks.
Deployment runs share a concurrency group. An active run is not cancelled, but GitHub retains only one pending run: rapid pushes can replace a pending tag run. Inspect the run list after releases and rerun a displaced tag publication when needed. Archive compare-and-swap failures stop rather than force-pushing.
Initial repository setup¶
This is a one-time maintainer setup, not part of ordinary documentation edits:
- Enable GitHub Pages with GitHub Actions as the source. The project inherits
the account's
hussein.awala.frdomain; do not add a projectCNAMEfile. - Create an empty
gh-pagesbranch with a signed commit and verify its signature before pushing it. Do not branch it from main with the package source files. - Allow the
github-pagesenvironment to deploy from main and intended version tags, keeping any existing maintainer protection rules. The workflow requires contents write permission for its archive job and Pages/OIDC permissions for its deployment job.
The workflow deliberately fails if the archive or Pages configuration is absent.
It does not silently create unsigned history or change repository settings.
No additional token or signing-key secret is needed: publication uses the workflow's
scoped GITHUB_TOKEN and GitHub's commit signature.
Verify or recover a deployment¶
Check both jobs of the exact Deploy documentation run. Then verify the public
version manifest at https://hussein.awala.fr/async-batcher/versions.json, the new
version's home page, channel label, and source link against the workflow SHA.
The latest/ alias changes only for an eligible stable version.
A successful archive update can precede a failed Pages deployment. In that case,
the public site may still show its previous content. Inspect the failed job and
rerun the existing workflow for the intended ref after fixing the cause. A deployment
job refuses an artifact superseded by a newer archive commit; use the current main
run to publish the complete updated archive in that case. Do not
move release tags or create a new package release to repair documentation. If an
archive API request has an ambiguous outcome, inspect gh-pages and its manifest
before retrying. A conflicting or unsigned archive result is a publishing failure,
not permission to force-push or bypass signature verification.
Shared release notes¶
Release notes are a project-wide
page independent of the selected documentation version. Every version's navigation
links to that permanent URL. Main deployments rebuild it from release-notes/*.md;
tag deployments preserve its existing archive bytes, so an older release cannot
replace newer history. It is not an entry in the documentation version selector.
Each file contains metadata followed by the canonical GitHub release body. Published 0.2.0, 0.2.1, and 0.2.2 bodies were copied from GitHub, preserving their wording and normalizing line endings. Planned releases are labeled In preparation. To preview:
uv run --no-sync python .github/scripts/release_notes.py build --output-dir release-notes-site
uv run --no-sync python .github/scripts/check_docs.py release-notes-site --base-path /async-batcher/release-notes/
Before creating an authorized GitHub release, export its reviewed body rather than maintaining a second copy. For example:
uv run --no-sync python .github/scripts/release_notes.py extract 0.3.0 --output /tmp/async-batcher-0.3.0-notes.md
Use that file with the existing release process's --notes-file argument. Extraction
only writes a local file; it does not publish. After package and GitHub publication
are confirmed, update the file's status, date, and github_url to the actual
release in a signed documentation PR. Its main deployment updates the shared page
without rebuilding archived version docs. Preserve historical bodies; release-note
corrections should be deliberate and reviewed.