-
Notifications
You must be signed in to change notification settings - Fork 1
Releasing
How to cut a new openbadgeslib release: bump the version in the source, update the changelog, tag, and let CI publish to PyPI. This is for maintainers; see Contributing for the day-to-day development workflow.
The version is single-sourced from openbadgeslib/util.py (__version__).
pyproject.toml reads it dynamically ([tool.setuptools.dynamic]), so you only
ever edit one place. The test_version_is_single_sourced test fails CI if a
static version is reintroduced.
Replace X.Y.Z with the new version throughout.
-
Bump the version in
openbadgeslib/util.py(__version__). That is the only place to edit.python -c "import openbadgeslib.util as u; print(u.__version__)" -
Add a
Changelog.txtentry. Entries are newest-first; follow the existing format (* vX.Y.Z - YYYY-MM-DDheader, then-dash-prefixed bullets, withSECURITY:markers on security-relevant lines):* vX.Y.Z - 2026-06-27 - Short description of each change.README.mdlinks toChangelog.txtrather than duplicating it, so there is nothing to mirror there.The entry is curated by hand for now, but the commit history is the source material: commits follow the convention in Contributing, so
git log --pretty='%s%n%b' vX.Y.Z..HEADlists the changelog-worthy changes (feat/fix/security/perf), and aChangelog:trailer on a commit carries the polished wording to copy. This keeps a future auto-generator a small step away without locking it in now. -
Run the checks locally before tagging — CI runs the same
flake8+mypy+pytestgate (see Contributing):pip install -e ".[dev]" flake8 openbadgeslib tests mypy pytest --cov=openbadgeslib --cov-report=term-missing -
Commit the version bump and changelog:
git add openbadgeslib/util.py Changelog.txt git commit -m "Release vX.Y.Z" git push origin master -
Tag the release with an annotated tag and push it. Pushing a
vX.Y.Ztag is what triggers the build, test and PyPI upload — no further step is required:git tag -a vX.Y.Z -m "openbadgeslib vX.Y.Z" git push origin vX.Y.Z -
(Optional) Create the GitHub Release for release notes. The publish already runs from the tag push (step 5), so a Release is no longer required to ship; create one only to attach human-readable notes. Publishing it re-runs the workflow, but
skip-existingmakes the duplicate upload a harmless no-op.gh release create vX.Y.Z --verify-tag --title "vX.Y.Z" --notes-file -
The workflow is .github/workflows/ci.yml. It has two jobs:
-
test runs on every
pushtomasterand every pull request, across the Python3.10,3.11,3.12, and3.13matrix (fail-fast: false). Each leg installs".[dev]", runsflake8 openbadgeslib tests, thenmypy, thenpytest --cov. -
publish runs when a
vX.Y.Ztag is pushed, when a GitHub Release is published, or the workflow is triggered manually (workflow_dispatch), andneeds: test, so it runs only if the whole test matrix passed. It builds the sdist and wheel withpython -m buildand uploads them to PyPI viapypa/gh-action-pypi-publish(withskip-existing: true, so re-running or a tag+Release double trigger never fails).
on:
push:
tags: ['v*']
publish:
needs: test
if: >-
github.event_name == 'release'
|| github.event_name == 'workflow_dispatch'
|| startsWith(github.ref, 'refs/tags/v')This means: pushing the tag publishes (the test matrix still gates it, so a
tag whose tests fail will not publish); a GitHub Release is optional; and you
can still publish the current master on demand from Actions → CI → Run
workflow (or gh workflow run ci.yml).
The publish job authenticates with secrets.PYPI_API_TOKEN:
- name: Publish to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
password: ${{ secrets.PYPI_API_TOKEN }}This must exist as a repository (or environment) secret named PYPI_API_TOKEN, holding a PyPI API token scoped to the openbadgeslib project. If it is missing or expired the upload step fails even when every test passes. Rotate it in PyPI and update the GitHub secret when needed — the token never lives in the repo.
- Confirm the publish job is green in the Actions tab.
- Confirm the new version appears on PyPI and installs cleanly:
pip install openbadgeslib==X.Y.Z. - Confirm
python -c "import openbadgeslib.util as u; print(u.__version__)"reportsX.Y.Zfrom the published wheel.
openbadgeslib · LGPLv3 (library) / BSD (CLI) · Issues
Getting Started
Concepts
Reference
Guides
Project