Contributing¶
Checks before a change¶
pip install ruff pytest pytest-asyncio aiohttp cryptography
ruff check .
PYTHONPATH=src python -m pytest -q
The tests include the documentation checks: relative links, the generated reference files, the code blocks in the pages and, when ProperDocs is installed, a strict build of the site. After changing code that the docs show, regenerate what is derived:
python tools/gen_docs.py # endpoint reference and signature index
python tools/docs_runner.py --fill # the printed output of the runnable blocks
pip install rich && python tools/screenshots.py # the terminal screenshots
How these fit together is in How it works.
Building the site locally¶
The site is built with ProperDocs, the maintained continuation of MkDocs 1.6, and the Material theme. Its configuration is properdocs.yml at the repository root. The versions are pinned in docs/requirements.txt.
python -m venv .venv-docs
.venv-docs/bin/pip install -r docs/requirements.txt
.venv-docs/bin/properdocs build --strict # writes site/
.venv-docs/bin/properdocs serve # preview at http://127.0.0.1:8000
--strict turns every warning (a broken link or anchor, a page missing from the navigation) into an error, so a clean strict build is the bar. site/ is generated and not committed.
Note
mkdocs-material depends on the original mkdocs package, so both are installed. Use the properdocs command to build; it reads properdocs.yml.
Pages in docs/ link to the examples with the repository's address (https://github.com/qisback/fakepve/blob/main/...), because the site cannot reach files outside docs/. When the repository moves, change that prefix, repo_url in properdocs.yml, and the one in tests/test_docs.py.
Publishing the site¶
The project site, https://qisback.github.io/fakepve/, is built and published by .github/workflows/pages.yml on every push to main (repository setting: Pages, source "GitHub Actions"). .github/workflows/ci.yml runs ruff, the tests and the strict build on pull requests.
The build is plain static files in site/, so a fork can publish it anywhere:
- GitHub Pages: the workflow above, or
properdocs gh-deployfrom a checkout with push access, which pushessite/to agh-pagesbranch. - Forgejo or Gitea Pages: push the contents of
site/to the branch or repository your Pages setup serves. - Any static host: copy
site/to the web root, for examplersync -a --delete site/ host:/var/www/fakepve/.
Set site_url in properdocs.yml to the address the site is served from, so that links and the search index resolve.
Releasing¶
A release is a version in pyproject.toml, a section in CHANGELOG.md and an annotated tag vX.Y.Z. Day-to-day changes add a line under ## [Unreleased] in CHANGELOG.md (Keep a Changelog headings: Added, Changed, Deprecated, Removed, Fixed, Security). The version follows Semantic Versioning and lives only in pyproject.toml; fakepve.__version__ and fakepve --version read it from the installed package.
- On the branch to release, run
python tools/release.py X.Y.Z. It renames[Unreleased]to[X.Y.Z] - <today>, starts a new empty[Unreleased], bumpspyproject.toml, updates the compare links at the end of the changelog and prints the release notes. (--date YYYY-MM-DDoverrides the date.python tools/release.py --notes X.Y.Zprints an existing version's notes again, and--check vX.Y.Zis what the release workflow uses to compare the tag with the version and the changelog.) - Commit the two files (
docs: release X.Y.Z) and merge them tomain. - Tag that commit with an annotated tag:
git tag -a vX.Y.Z -m "fakepve X.Y.Z", as the project owner. - Publish
mainand the tag to GitHub with the publishing script (publish_github.py). Pushing the tag starts.github/workflows/release.yml; nothing else is needed.
The workflow, on a pushed v* tag:
- build checks that the tag equals the version and that the changelog has the section, builds the sdist and wheel, runs
twine check, installs the wheel in a clean virtual environment and runs the test suite against it, and keepsdist/as an artefact. - pypi (after build) uploads
dist/to PyPI by trusted publishing, in thepypienvironment. There is no token. - image (after build) builds the container image, runs
tools/smoke_image.shon it (start, wait for it to answer, log in, list the VMs), and pushesghcr.io/qisback/fakepve:X.Y.Zand:latest, labelled with the source, version and revision. - release (after build and image) creates the GitHub release for the tag with
dist/*attached and the changelog section as its notes.
The same container build and smoke test run on every pull request and push in ci.yml, together with a python -m build and twine check, so a release is not the first time they run. Release branches (release/**) are tested there too.
One-off PyPI setup¶
Done once, by the owner, before the first release:
- On pypi.org, add a pending trusted publisher (Account settings, Publishing): PyPI project name
fakepve, ownerqisback, repositoryfakepve, workflowrelease.yml, environmentpypi. The project is created by the first upload; there is no API token to make or store. - On GitHub, in the repository's Settings, Environments, create
pypiand restrict its deployment to tags matchingv*(Deployment branches and tags, Selected branches and tags, add a tag rulev*). Required reviewers may be added if a pause before each upload is wanted. - After the first release, make the
ghcr.io/qisback/fakepvepackage public (Packages, the package's settings, Change visibility), so thatdocker pullneeds no login. It is linked to the repository by the image's source label.