# Release Process ## Pre-release checklist Before tagging a release, verify every item: - [ ] All tests pass: `pytest --tb=short -q` - [ ] Lint clean: `ruff check .` - [ ] Type-check clean: `mypy src/` - [ ] Corpus regression passes: `markidocx test` - [ ] SBOM is current (re-run after any dependency change — see CLAUDE.md) - [ ] `CHANGELOG.md` updated: move items from `[Unreleased]` to a new versioned section - [ ] Version bumped consistently: - `src/markidocx/__init__.py` — `__version__ = "X.Y.Z"` - `pyproject.toml` — `version = "X.Y.Z"` - [ ] Build artefacts clean: `python -m build && twine check dist/*` ## Tagging and releasing ```bash # Verify version strings match python -c "import markidocx; print(markidocx.__version__)" # Create a signed tag git tag -s vX.Y.Z -m "Release vX.Y.Z" # Push tag — triggers publish.yml (PyPI) and docker.yml (ghcr.io) automatically git push origin vX.Y.Z ``` Pushing the tag triggers two GitHub Actions workflows: - **`publish.yml`** — builds and publishes to PyPI via OIDC trusted publishing - **`docker.yml`** — builds and pushes Docker image to `ghcr.io` ## Post-release - Create a GitHub release from the tag and paste the relevant CHANGELOG section - Verify installation: `pip install markidocx==X.Y.Z && markidocx --version` - Verify Docker image: `docker run --rm ghcr.io/tegwick/marki-docx:vX.Y.Z --version` ## Versioning policy markidocx follows [Semantic Versioning](https://semver.org/): | Change type | Version bump | |------------|-------------| | Breaking change to CLI/API/round-trip contract | MAJOR | | New feature, backwards-compatible | MINOR | | Bug fix, docs, tooling | PATCH | ## Setting up PyPI trusted publishing (one-time) In the PyPI project settings, add a trusted publisher: - Publisher: GitHub Actions - Repository owner: `tegwick` - Repository name: `marki-docx` - Workflow filename: `publish.yml` - Environment name: (leave blank)