Skip to content

Publishing Django Orbit

This is the release checklist for publishing Django Orbit to GitHub, PyPI and the public MkDocs site.

Versioning Policy

Use Semantic Versioning with an explicit pre-1.0 project policy:

  • v0.MINOR.PATCH is for fixes, security corrections, dependency updates and release-process repairs that should not add a new product capability. For example, the next hotfix after v0.12.1 is v0.12.2.
  • v0.MINOR.0 is for a new capability line or substantial user-visible behavior. For example, v0.14.0 adds compact request-family MCP context after the published v0.13.0 Evidence API release.
  • v1.0.0 is reserved for the moment we explicitly commit to a stable public API, compatibility guarantees and a migration policy. A release being large does not automatically make it v1.0.0.

Do not skip a viable pending feature line. Check the published GitHub and PyPI versions before selecting the next version. Every version change follows the release PR, full preflight, merge, annotated tag, PyPI publication, GitHub release and post-publish verification sequence below.

Maintainer Checklist

Before Merge

  • [ ] Decide whether the change is a package release or documentation/CI-only.
  • [ ] Choose the next SemVer version; use a patch only for fixes and release-process changes.
  • [ ] Update pyproject.toml, orbit/__init__.py, CHANGELOG.md, README.md and docs/ when applicable.
  • [ ] Run python scripts/verify_release.py --metadata-only.
  • [ ] Confirm all required PR checks are green, including Quality / static checks.

Publish

  • [ ] Merge the release PR into main.
  • [ ] Pull the final main and confirm the version is unchanged.
  • [ ] Run python scripts/verify_release.py from the final commit.
  • [ ] Create and push the annotated tag vX.Y.Z.
  • [ ] Confirm the GitHub Release was created from that tag.
  • [ ] Run the Publish to PyPI workflow manually with the exact tag.
  • [ ] Confirm PyPI, GitHub Release and documentation URLs show the same version.

After Publish

  • [ ] Install the exact version in a clean environment, including [mcp] when relevant.
  • [ ] Exercise the documented quick start and MCP setup.
  • [ ] Confirm the scheduled maintenance workflow is healthy.
  • [ ] Close any release follow-up issue only after public verification.

Release Order

  1. Merge the release PR into main.
  2. Pull the final main locally and confirm the version.
  3. Run the full test suite and release preflight.
  4. Create and push an annotated tag matching the package version:
git tag -a vX.Y.Z -m "Release vX.Y.Z"
git push origin vX.Y.Z
  1. GitHub Actions verifies the tag is annotated, points at main, and matches every version surface. It then creates the GitHub release.
  2. Run Publish to PyPI manually with the exact annotated tag. The current PyPI trusted publisher is registered to .github/workflows/publish.yml and the workflow repeats the tag, main ancestry, metadata, build and Twine checks before uploading.
  3. The documentation workflow deploys the merged main documentation.
  4. Verify PyPI, GitHub release, docs and a fresh install.

Post-Merge Release Guard

Every push to main and every merged pull request targeting main is checked by the Release follow-up guard workflow. If pyproject.toml changes to a new version and the matching GitHub release is missing, the workflow opens one deduplicated issue with the exact tag and preflight commands.

The guard intentionally does not tag, upload to PyPI or create a release by itself. Those actions remain behind the annotated-tag, metadata, build and PyPI trusted-publishing checks. Close the follow-up issue only after the public package, release and docs have been verified.

Preflight

For release PRs and final publishing, run the local guard before pushing or uploading:

python scripts/verify_release.py

This checks release metadata, runs the test suite, builds docs in strict mode, rebuilds package artifacts and runs Twine checks.

If you only need the lightweight metadata check used by CI:

python scripts/verify_release.py --metadata-only

Confirm these files are aligned before publishing:

  • pyproject.toml project version
  • orbit/__init__.py __version__
  • CHANGELOG.md release section
  • README.md package landing page
  • docs/ and mkdocs.yml for user-visible changes

Build

python scripts/verify_release.py already cleans old artifacts, builds fresh distributions and runs Twine checks. To run the build steps manually:

rm -rf dist/ build/ *.egg-info/
python -m build
python -m twine check dist/*

Expected files for version X.Y.Z:

  • dist/django_orbit-X.Y.Z.tar.gz
  • dist/django_orbit-X.Y.Z-py3-none-any.whl

Pull Request Merge Guard

Release PRs must have these GitHub checks green before merge:

  • Release metadata
  • Tests / Python 3.11 / Django 5.2 / core
  • Tests / Python 3.12 / Django 5.2 / full+mcp
  • Tests / Python 3.12 / Django 6.0 / full+mcp
  • Tests / Python 3.13 / Django 6.1 / full+mcp
  • Tests / Python 3.14 / Django 6.1 / core
  • Documentation
  • Package build
  • Quality / static checks

In GitHub repository settings, configure branch protection for main to require those checks before merge and require branches to be up to date before merging.

Publish to PyPI

python -m twine upload dist/django_orbit-X.Y.Z*

Use a PyPI project token when prompted:

python -m twine upload dist/django_orbit-X.Y.Z* -u __token__ -p pypi-...

GitHub Release

The Create GitHub Release workflow verifies the package first, then creates the release automatically after the tag checks and package preflight pass. Run Publish to PyPI manually with the exact tag after that release. This is intentional: the current PyPI trusted publisher is registered to .github/workflows/publish.yml, while the tag workflow is .github/workflows/release.yml. To make the final upload fully automatic, register the release workflow in PyPI Trusted Publishers and add its publish job back only after that setting is verified. GitHub generates the initial notes; the matching CHANGELOG.md section remains the source of truth for the release contents.

Deploy Documentation

mkdocs build --strict
mkdocs gh-deploy

Documentation must be deployed from the same code that was released.

Post-Publish Verification

Check the public package page:

python -m pip index versions django-orbit

Test a clean install in a temporary environment:

python -m venv .venv-release-check
.venv-release-check\Scripts\python -m pip install --upgrade pip
.venv-release-check\Scripts\python -m pip install "django-orbit[mcp]==X.Y.Z"
.venv-release-check\Scripts\python -c "import orbit; print(orbit.__version__)"

Verify these public URLs:

  • PyPI: https://pypi.org/project/django-orbit/
  • GitHub release: https://github.com/astro-stack/django-orbit/releases/tag/vX.Y.Z
  • Docs: https://astro-stack.github.io/django-orbit/
  • MCP docs: https://astro-stack.github.io/django-orbit/mcp/

If Upload Fails

PyPI versions are immutable. If X.Y.Z was already uploaded, bump to the next patch version, update changelog/version files, rebuild and upload again.