This document is the release checklist for maintainers. rlaopt uses a static version in pyproject.toml, uv for building and validation, GitHub Releases for release notes, and PyPI Trusted Publishing for deployment.
Use PEP 440 versions and choose increments according to Semantic Versioning.
[project].version in pyproject.toml is the only version maintainers edit. The installed rlaopt.__version__ and the Sphinx documentation version are derived from package metadata.
Use uv to update the version and lockfile together:
uv version X.Y.Z --no-syncAn example of a valid release version and tag is 0.2.0 / v0.2.0.
-
Start from an up-to-date
mainand create a short-lived release branch:git switch main git pull --ff-only git switch -c release/X.Y.Z
-
Update the version and review both files changed by uv:
uv version X.Y.Z --no-sync git diff -- pyproject.toml uv.lock
-
Update user-facing documentation for any changed installation, compatibility, or migration instructions.
-
Run the local packaging checks:
uv lock --check uv build --no-sources
-
Commit the release preparation, open a pull request, and merge it after the regular test and Read the Docs checks pass.
The release branch prepares the version; it never triggers a deployment.
GitHub Release notes are the canonical changelog. On the new release form, choose Generate release notes, then edit the generated content so the top contains the information users need:
## Highlights
- The most important user-visible changes.
## Breaking changes and migrations
- Required code or configuration changes, or "None."
## Compatibility and installation
- Supported Python changes, dependency constraints, and installation notes.Retain GitHub's generated pull-request list, contributor list, and full comparison link below those sections.
- Confirm the version commit is on
mainand all required checks have passed. - In GitHub, draft a new release targeting that exact commit on
main. - Create the tag
vX.Y.Zand use the same value as the release title. - Generate and edit the release notes as described above.
- Publish the GitHub Release. This is the irreversible deployment trigger.
- Approve the
pypienvironment deployment after checking the tag, commit, and version shown in the workflow run.
The release workflow verifies that the tag matches pyproject.toml and the tagged commit belongs to main. It then builds and smoke-tests the distributions in an unprivileged job. A separate job obtains a short-lived OIDC credential, generates PEP 740 attestations, and publishes the exact artifacts to PyPI. After publication, the wheel and source distribution are attached to the GitHub Release.
After the workflow completes:
-
Check the project page on
https://pypi.org/project/rlaopt/. -
Test a clean install from PyPI:
uv run --isolated --no-project --with rlaopt==X.Y.Z \ python -c 'import rlaopt; print(rlaopt.__version__)' -
Confirm the versioned documentation builds on Read the Docs. Activate the version if an automation rule has not done so.
-
Confirm the wheel and source distribution are attached to the GitHub Release.
- If a transient infrastructure failure occurs before PyPI accepts any files, rerun the unchanged workflow.
- If the tagged source fails validation, do not move or reuse the tag. Fix the problem on
main, increment the version, and publish a new release. - If publishing is interrupted after PyPI accepts only some files, rerun the same workflow.
uv publishsafely skips identical files already present on PyPI. - PyPI artifacts cannot be replaced. If a bad release was published, yank it when appropriate, fix the problem, increment the version, and publish a new release.
- Never delete and recreate a released tag or attempt to upload different artifacts for an existing version.