Release Process¶
This document describes the complete release process for strapi-kit.
Overview¶
strapi-kit uses a label-based release workflow:
- Production Releases → PyPI (
release.ymlon a merged PR tomainthat already hasrelease:major/release:minor/release:patch) - Dev Releases → TestPyPI (
dev-release.ymlon push todev) - Release PR →
release-pr.ymlopens or updatesrelease/dev-to-mainwhendevis pushed
There is no publish-testpypi.yml. A PR to main does not publish a TestPyPI build by itself.
Current Setup Status¶
✅ What's Working¶
- Automated Workflows:
release.yml— production tag + GitHub Release + PyPIdev-release.yml— TestPyPI on every push todev-
release-pr.yml— keep adev→mainPR open -
Build Configuration:
hatchling+hatch-vcs(version from git tags) -
Trusted Publishing: Workflows use OIDC (
id-token: write)
⚠️ What Needs Setup¶
- PyPI Trusted Publishing: Confirm the publisher on PyPI.org (
release.yml, environmentpypiif you set one) - TestPyPI Trusted Publishing: Confirm the publisher for
dev-release.yml(environmenttestpypi)
Version Strategy¶
Version comes from git tags via hatch-vcs. pyproject.toml uses
dynamic = ["version"]; the hook writes src/strapi_kit/_version.py.
src/strapi_kit/__version__.py imports that file and falls back to
0.0.0.dev0+local for an editable install that has not been built.
# pyproject.toml (already in the repo)
[build-system]
requires = ["hatchling", "hatch-vcs"]
build-backend = "hatchling.build"
[project]
name = "strapi-kit"
dynamic = ["version"]
[tool.hatch.version]
source = "vcs"
[tool.hatch.build.hooks.vcs]
version-file = "src/strapi_kit/_version.py"
Do not put a static version = "..." back in [project]. The release
workflow tags main after a labeled dev → main merge; that tag is
the published version.
Release Types¶
1. Production Release (PyPI)¶
Trigger: Merge PR from dev → main with release label
Labels:
- release:major - Breaking changes (1.0.0 → 2.0.0)
- release:minor - New features (1.0.0 → 1.1.0)
- release:patch - Bug fixes (1.0.0 → 1.0.1)
Process:
graph LR
A[Create PR: dev → main] --> B[Add release label]
B --> C[Merge PR]
C --> D[Workflow runs]
D --> E[Calculate version]
E --> F[Create git tag]
F --> G[Build package]
G --> H[Create GitHub Release]
H --> I[Publish to PyPI]
Steps:
- Create PR from
devtomain - Add appropriate release label (
release:major,release:minor, orrelease:patch) - Merge the PR
- Workflow automatically:
- Calculates new version from latest tag + label
- Creates and pushes git tag
- Builds wheel and sdist
- Creates GitHub Release with notes
- Publishes to PyPI
Example:
2. Test Release (TestPyPI)¶
There is no separate PR-triggered TestPyPI workflow. Use the dev
release below (push to dev publishes {next}.dev{commit_count}).
To exercise a specific candidate, install that TestPyPI version:
pip install -i https://test.pypi.org/simple/ \
--extra-index-url https://pypi.org/simple/ \
strapi-kit==0.2.0.dev68
python -c "import strapi_kit; print(strapi_kit.__version__)"
3. Dev Release (TestPyPI)¶
Trigger: Push to dev branch
Process:
- Push commits to
devbranch - Workflow automatically:
- Checks for open PR to main with release labels
- Calculates version:
{new_version}.dev{commit_count} - Builds and publishes to TestPyPI
Version Format: 0.2.0.dev5 (where 5 is commit count since last tag)
Setting Up Trusted Publishing¶
PyPI Setup (Production)¶
- Go to https://pypi.org/manage/account/publishing/
- Add a new publisher:
- PyPI Project Name:
strapi-kit - Owner:
mehdizare - Repository name:
strapi-kit - Workflow name:
release.yml -
Environment name: (leave blank)
-
Save the configuration
TestPyPI Setup (Testing)¶
- Go to https://test.pypi.org/manage/account/publishing/
- Add a publisher:
- PyPI Project Name:
strapi-kit - Owner:
MehdiZare - Repository name:
strapi-kit - Workflow name:
dev-release.yml -
Environment name:
testpypi -
Save the configuration
v0.1.0 is already on PyPI, so a first-upload bootstrap is not needed
for 0.2.0. If Trusted Publishing is missing, add the release.yml
publisher (environment pypi) before merging the release PR.
Manual Release Process¶
Prerequisites¶
Build Package¶
# Clean previous builds
rm -rf dist/ build/ *.egg-info
# Build wheel and source distribution
python -m build
# Verify contents
tar -tzf dist/*.tar.gz
unzip -l dist/*.whl
Test Locally¶
# Create test environment
python -m venv test-env
source test-env/bin/activate
# Install from wheel
pip install dist/*.whl
# Test
python -c "import strapi_kit; print(strapi_kit.__version__)"
pytest
# Deactivate
deactivate
rm -rf test-env
Upload to TestPyPI¶
# Upload to TestPyPI
twine upload --repository testpypi dist/*
# Test installation
pip install -i https://test.pypi.org/simple/ strapi-kit
Upload to PyPI¶
Hotfix Process¶
For critical bug fixes that need immediate release:
-
Create hotfix branch from
main: -
Make fixes and test thoroughly
-
Create PR from
hotfix-critical-bug→main -
Add
release:patchlabel -
Merge PR → automatic release
-
Merge back to
dev:
Version Numbering¶
Follow Semantic Versioning:
- MAJOR (X.0.0): Breaking changes
- API changes that break backward compatibility
- Removing features
-
Major refactoring
-
MINOR (0.X.0): New features
- New functionality
- New APIs
-
Deprecations (but not removals)
-
PATCH (0.0.X): Bug fixes
- Bug fixes
- Documentation updates
- Performance improvements (no API changes)
Troubleshooting¶
Build Fails¶
# Check build locally
python -m build
# Common issues:
# - Missing __init__.py files
# - Incorrect package structure
# - Syntax errors
Version Mismatch¶
If built package has wrong version:
# Check git tags
git tag -l
# Check latest tag
git describe --tags --abbrev=0
# Check built version
tar -xzOf dist/*.tar.gz "*/PKG-INFO" | grep "^Version:"
Upload Fails¶
# Check package on PyPI
# https://pypi.org/project/strapi-kit/
# Check Trusted Publishing setup
# https://pypi.org/manage/account/publishing/
# Verify workflow permissions
# - id-token: write (required for OIDC)
# - contents: write (for creating releases)
TestPyPI Installation Issues¶
TestPyPI doesn't host all dependencies, so use:
# Install dependencies from PyPI, package from TestPyPI
pip install -i https://test.pypi.org/simple/ \
--extra-index-url https://pypi.org/simple/ \
strapi-kit==VERSION
Changelog Management¶
Update CHANGELOG.md before each release:
## [Unreleased]
## [0.2.0] - 2026-08-16
### Added
- New feature X
- New API Y
### Changed
- Improved performance of Z
### Fixed
- Bug in component A
Cut [Unreleased] into the new version heading (and empty [Unreleased])
in both CHANGELOG.md and docs/changelog.md before opening the
dev → main PR. Update the compare links at the bottom of
CHANGELOG.md.
Checklist for Releases¶
Before creating release PR:
- [ ] Unit tests pass (
make test) - [ ] Type checking passes (
make type-check) - [ ] Linting passes (
make lint) - [ ] Coverage ≥ 85% (
make coverage) — or an explicit waiver if export/import paths stay below the target - [ ]
CHANGELOG.mdanddocs/changelog.mdcut to the new version - [ ] User-facing docs match shipped behavior (README, MkDocs,
LLM.md) - [ ] Upgrade notes listed for behavior changes (stream/export default, etc.)
- [ ] Live e2e (
make e2e) run against Strapi 5 if D&P or export changed - [ ] Version bump decided (
release:minorfor 0.2.0) - [ ] Release PR is labeled before merge (
release.ymlreads labels on the merged PR)