.github/workflows/:
-
prepare-release.yml— Run first. Validates the version, checks that CI passed, updates.version, and pushes everything to the dispatching branch in a single commit. CI then runs automatically on the resulting commit (forrelease/**branches). The CI check can be skipped withskip-ci-check. -
release.yml— Run after CI passes on the prepared commit. Builds installers and wheels for all platforms (Linux x86/ARM, macOS Intel/ARM, Windows), and can optionally sign macOS/Windows artifacts, create a draft GitHub release, publish wheels to TestPyPI, and publish wheels to PyPI.
workflow_dispatch and share a release concurrency group so
they cannot run simultaneously.
Version format
Versions follow calendar versioning with PEP 440:YY.MM for stable releases
(e.g. 26.04), with optional .patch (e.g. 26.04.1) and pre-release
suffixes (b1, rc1, a1). Months must be zero-padded.
Examples: 26.05b1 (beta), 26.05rc1 (release candidate), 26.05 (stable),
26.05.1 (patch).
Release branch workflow
All releases are cut from arelease/YY.MM branch. The branch name uses only
the major version (YY.MM), not the full pre-release suffix — betas, release
candidates, and the stable release all come from the same branch.
Standard release
-
Create a release branch from
main: -
CI runs automatically on push to
release/**branches. -
Prepare the release (updates
.versionon the branch): -
Pull the version bump commit:
-
Verify on TestPyPI:
-
Publish the full release:
-
For subsequent pre-releases or the stable release from the same cycle,
repeat steps 3-6 with the new version (e.g.
26.05b2,26.05rc1,26.05). -
After the stable release, merge the release branch back to
mainto pick up the.versionbump and any cherry-picked fixes: - Delete the release branch after the stable release is published.
Security and hotfix releases
For security fixes, an admin should first create a security advisory with a temporary private fork. Work on the fix in the private fork via the normal PR workflow. Do not open a public PR or publish the advisory until the fix is ready for release. Once the fix is ready:-
Create a release branch from the latest release tag:
- Cherry-pick the fix onto the release branch.
-
Push the branch and wait for CI:
-
Prepare and publish:
-
Merge the release branch back to
main. - For security patches, publish the advisory and credit the reporter if applicable.
Release process overview
Release workflow jobs
Workflow inputs
prepare-release: takes aversion string and an optional skip-ci-check
boolean (default false).
release: takes a version (must match .version for public release
operations) and five boolean inputs:
signsigns macOS and Windows artifacts.draft-releasecreates the draft GitHub release.publish-testpypipublishes wheels to TestPyPI.publish-pypipublishes wheels to PyPI.skip-ci-checkskips the CI status check.
false. Non-release runs use the .version
already in the repo, so builds work without a prepare step.
For a normal public release, enable the first four booleans: sign=true,
draft-release=true, publish-testpypi=true, and publish-pypi=true.
Environment gates
The release workflow uses GitHub environments as manual approval gates. Jobs that access signing credentials or publish artifacts require a reviewer to approve the deployment before they run:release— Required whensign,draft-release,publish-testpypi, orpublish-pypiis enabled. Protects code-signing secrets, the release token, and PyPI/TestPyPI trusted publishing/OIDC.
sign is disabled, the macOS and Windows build jobs run without the
release environment so they do not require approval and cannot access signing
secrets.
Workflow input behavior
Therelease.yml workflow uses independent boolean inputs to control what gets
signed and published:
| Input | Effect |
|---|---|
sign | Signs macOS and Windows artifacts. Requires the release environment. When false, those jobs upload unsigned artifacts and do not access signing secrets. |
draft-release | Creates a draft GitHub release with generated release notes and installer artifacts. Requires sign=true, the release environment, passing CI (unless skipped), no duplicate tag/release, and version matching .version. |
publish-testpypi | Publishes wheels to TestPyPI. Requires the release environment. |
publish-pypi | Publishes wheels to PyPI. Requires the release environment, passing CI (unless skipped), and version matching .version. It also runs and waits for the TestPyPI publish job first. It does not require signing unless draft-release=true. |
skip-ci-check | Skips the CI status check. Useful for hotfix releases. |
version | For draft-release or publish-pypi: must match .version. For build-only, signed-only, or TestPyPI-only runs: ignored (.version from the branch is used automatically). |
Dispatching with just
Release workflows can be dispatched viajust using the release module
defined in release.just. All recipes require an explicit --ref argument
pointing to the release branch.
Run just --list --list-submodules to see all available recipes and their
arguments.
Testing the release workflow from a feature branch
release.yml can be dispatched from any branch for testing. The release guards
only apply when draft-release or publish-pypi is enabled. To run a test
build:
- Dispatch
release.ymlfrom your branch with all boolean inputs left false: - The workflow reads
.versionfrom the branch as-is (the version input is ignored for non-release runs), so no prepare step is needed. - All release guards (CI check, duplicate tag check) are skipped.
- Artifacts are uploaded to the workflow run but nothing is published or tagged.
Testing with code signing
To test the signing flow from a feature branch:- In the repo’s Settings → Environments →
release, temporarily add your branch to the allowed deployment branches. - Dispatch the workflow:
- Approve the environment deployment when prompted.
- After testing, remove your branch from the environment’s allowed branches.
Note:workflow_dispatchworkflows only appear in the GitHub Actions UI if the workflow file exists on the default branch. Ifrelease.ymlis new or modified on your branch, usegh workflow runto trigger it — the UI dropdown won’t show it until it’s merged to main.
Important notes
- The release workflow builds the exact commit at
github.sha. It does not write.version— that is done by the prepare workflow. If you dispatch release before prepare’s commit has propagated, the build will use whatever.versionwas HEAD at dispatch time. draft-release=truewithsign=falseis rejected — draft releases must use signed installer artifacts.- When
publish-pypi=true, wheels are published to TestPyPI first, then to PyPI after the TestPyPI job succeeds. Ifdraft-release=trueis also set, PyPI publishing waits for the draft GitHub release to succeed too. - Pre-release versions (e.g.
26.05b1) are automatically marked as pre-releases on the GitHub draft release.
Announcements
- Once a GitHub release draft is created, modify the generated changelog if necessary then click Publish release.
- Create a forum topic on the Beta Testing category. For stable releases, lock the topic and ask users to report issues on a new topic.
- For stable releases, update the version in ankitects/anki-landing-page (See example).