Shaken Fist's release process¶
Shaken Fist uses GitHub Actions for automated releases. The process uses Sigstore for signing (no GPG keys required) and PyPI Trusted Publishers for authentication (no API tokens required).
Prerequisites¶
Before you can trigger a release, the following one-time setup must be
completed (see RELEASE-SETUP.md in the repository root for details):
- PyPI Trusted Publisher configured for the repository
- GitHub Environment named
releasewith required reviewers - (Optional) Protected tag rules for
v*tags
Testing¶
We only release things which have passed CI testing, and preferably have had a period running as the underlying cloud for the CI cluster as well. Sometimes in an emergency we will bend the rules for a hotfix, but we should try and avoid doing that.
Triggering a release¶
Step 1: Determine the version number¶
Check the existing tags to find the previous version:
Step 2: Create and push the version tag¶
git checkout develop # or the appropriate branch
git pull
git tag v0.X.Y -m "Release v0.X.Y"
git push origin v0.X.Y
This triggers the release workflow but does not publish anything yet.
Step 3: Approve the release¶
- Go to the repository on GitHub
- Navigate to Actions > Release (the running workflow)
- The workflow will pause at the
sign-tagjob waiting for approval - Click Review deployments
- Select the
releaseenvironment and click Approve and deploy
Step 4: Monitor the release¶
The workflow will:
- Build the Python package and archives (deploy.tgz, docs.tgz)
- Sign the release tag using Sigstore/gitsign
- Generate attestations for the built artifacts
- Publish to PyPI
- Create a GitHub Release with all artifacts attached
You can monitor progress in the Actions tab. If any step fails, the workflow stops and you can investigate the logs.
What gets released¶
- PyPI: The
shakenfistPython package - GitHub Releases: The package files plus:
deploy.tgz- Deployment tooling archivedocs.tgz- Documentation archive
Verifying a release¶
Verify the signed tag¶
Anyone can verify the release tag signature using gitsign:
git fetch --tags
gitsign verify \
--certificate-identity-regexp='.*' \
--certificate-oidc-issuer='https://token.actions.githubusercontent.com' \
v0.X.Y
Verify PyPI package attestation¶
PyPI shows attestation status on the package page under the "Provenance" section. You can also verify locally:
pip download shakenfist==0.X.Y --no-deps
cosign verify-attestation \
--certificate-identity-regexp='.*' \
--certificate-oidc-issuer='https://token.actions.githubusercontent.com' \
shakenfist-0.X.Y.tar.gz
Troubleshooting¶
Workflow doesn't trigger¶
- Ensure the tag matches the pattern
v*(e.g.,v0.8.0, not0.8.0) - Check that you pushed to the correct remote
Approval not requested¶
- Verify the
releaseenvironment exists in repository settings - Check that required reviewers are configured
PyPI publish fails¶
- Verify the Trusted Publisher is configured correctly in PyPI
- Ensure the workflow filename and environment name match exactly
Tag signing fails¶
- This usually indicates an issue with the Sigstore service or OIDC token
- Check the workflow logs for specific error messages
- Retry the workflow if it's a transient issue
Rolling back a release¶
If a release needs to be rolled back:
-
PyPI: You cannot delete releases from PyPI, but you can yank them:
- Go to pypi.org > Your project > Releases
- Click the version and select "Yank"
- Yanked versions won't be installed by default but remain available
-
GitHub: Delete the release and tag if needed:
Then delete the release from the GitHub Releases page.
Legacy process¶
The old release.sh script required a private VM with GPG keys and PyPI
credentials. This has been replaced by the GitHub Actions workflow which uses
keyless signing and OIDC authentication.