Contributing to kmhelpers
Dev environment setup
kmindex and kmtricks must also be available, see Installation.
Project structure
pykmhelpers/
cli/ # Click commands (one file per command)
core/ # Low-level building blocks (index, bloom_filter, kmindex_wrapper, ...)
operations/ # Higher-level operations (builder, fof_validation, ...)
tests/ # Test suite
pipeline/ # Pipeline execution engine
Each CLI command in cli/ is a thin layer that delegates to operations/ or core/.
Running tests
Coverage is reported automatically (configured in pyproject.toml). HTML report is written to htmlcov/.
Code style
| Tool | Purpose |
|---|---|
black |
Formatting (line length 88) |
flake8 |
Linting |
mypy |
Type checking |
Branches
| Branch | Purpose | Updated by |
|---|---|---|
main |
Development trunk, always passing CI; releases are tags vX.Y.Z on it |
PR |
feature/<name>, fix/<name>, doc/<name>, ci/<name>, release/vX.Y.Z |
Short-lived work, deleted after merge | Direct push |
docs |
Hidden docs preview, for docs work in progress | Direct push |
paper |
JOSS paper; paper.pdf is committed by CI |
Direct push |
gh-pages |
Generated by the docs workflow, never edited by hand | CI |
main is protected: changes land through pull requests. Users install from
tags, PyPI or bioconda, not from main.
Workflow
git checkout main
git pull
git checkout -b feature/my-feature
# ... commit ...
git push -u origin feature/my-feature
gh pr create --base main
# after review and green CI:
gh pr merge --squash --delete-branch # or --merge to keep each commit
Rules:
- One branch per topic, from an up-to-date
main, deleted after merge. - To bring new
maincommits into a pushed branch, usegit merge main; rebase only before the first push. git push --force-with-leaseonly on your own, unshared branch.
Documentation
Docs are versioned with mike on the gh-pages branch, deployed by
.github/workflows/docs.yml:
| Trigger | Deployed as | URL |
|---|---|---|
Push to docs |
Hidden preview | /kmhelpers/preview/ |
Push to main |
Version main, titled main (<short hash>), overwritten each time |
/kmhelpers/main/ |
Tag vX.Y.Z |
Version X.Y.Z, alias latest (default) |
/kmhelpers/X.Y.Z/ |
Content specific to a release or to the development version goes between
release-only or dev-only HTML comment markers; @VERSION@ inside a
release block is replaced by the version (see scripts/mkdocs_hooks.py, used
in docs/getting-started/installation.md). Release builds get
DOCS_RELEASE=X.Y.Z from the workflow; to preview one locally:
To try docs changes before merging, push them to docs
(git push --force origin HEAD:docs) and open the preview.
Manual deploy from any branch or tag (the branch must be pushed):
# Redeploy a release from its tag
gh workflow run docs.yml --ref vX.Y.Z
# Deploy a branch as a given version, without moving "latest"
gh workflow run docs.yml --ref fix/docs-typo -f version=X.Y.Z -f latest=false
# Deploy and remove an old version
gh workflow run docs.yml --ref vX.Y.Z -f delete_version=<old>
# Follow the run
gh run watch $(gh run list --workflow docs.yml --limit 1 --json databaseId --jq '.[0].databaseId')
Without -f version, the version is the package version of the ref (main
on main). latest defaults to true. A release version gets the
release-only blocks, like a tag build.
Releasing
pykmhelpers/_version.py is the single source of the version. Commands below
use VERSION=X.Y.Z.
1. Prepare
- Set
__version__ = "X.Y.Z"inpykmhelpers/_version.py. - Add the
## [X.Y.Z] - YYYY-MM-DDsection toCHANGELOG.md. It becomes the GitHub release notes, so it must be written before tagging.
pytest
rm -rf dist && python -m build && twine check dist/*
git commit -am "release: v$VERSION"
git push -u origin release/v$VERSION
2. Merge into main
gh pr create --base main --title "Release v$VERSION" --body "See CHANGELOG.md"
gh pr checks --watch
gh pr merge --merge --delete-branch
3. Tag
git checkout main
git pull
git tag -a v$VERSION -m "kmhelpers v$VERSION" \
-m "Documentation: https://sebllns.github.io/kmhelpers/$VERSION/
Changelog: https://sebllns.github.io/kmhelpers/$VERSION/changelog/"
git push origin v$VERSION
Or with git create-release (scripts/git-create-release, requires
gh), which runs the commands above after checks,
then lists the release workflow runs:
VERSION defaults to pykmhelpers/_version.py; a different one asks for
confirmation. Before tagging, it checks that CHANGELOG.md has the
## [VERSION] section, that the tag does not exist, that the working tree is
clean and that HEAD is origin/main.
| Option | Effect |
|---|---|
--view |
Show the release and the workflow runs, no tagging |
--overwrite |
Delete the GitHub release and the tag, then tag again |
-h |
Help (--help is caught by git for aliases) |
The alias is defined in the versioned .gitconfig, enable it once per clone:
Never move or re-create a pushed tag, except with --overwrite when the
release failed before the PyPI upload.
4. Automatic steps
Pushing the tag runs:
release.yml: builds the sdist and wheel, creates the GitHub release with the files attached and notes made of.github/release-notes.md(docs links, install and update commands), the CHANGELOG section and the merged PRs, then uploads the files to PyPI (publishjob) and installs the published version in a fresh environment to checkkmhelpers --version(verifyjob).docs.yml: deploys the docs as versionX.Y.Zwith aliaslatest.
5. Approve the PyPI upload (browser)
The publish job runs in the pypi environment, which waits for a manual
approval:
- Open the "Release build" run of the tag in the Actions tab.
- Once
testsandbuildare green, check the release: notes, sdist and wheel attached. - Click "Review deployments", select
pypi, then "Approve and deploy". - When
publishandverifyare green, check the version on PyPI and the docs athttps://sebllns.github.io/kmhelpers/X.Y.Z/.
A PyPI version cannot be uploaded twice: a broken release is fixed by a new version.
6. Bioconda
After the PyPI upload, the bioconda bot opens a PR in
bioconda-recipes updating the
version and sha256. Check that runtime dependencies still match
pyproject.toml, then approve it.
Patch of an older version
Only when a fix must be released for a version older than main. The tag is
created manually, as git create-release only tags origin/main:
git checkout -b maint/vX.Y v$OLD_VERSION
# commit the fix, bump the version, add the CHANGELOG section
git push -u origin maint/vX.Y
git tag -a v$PATCH_VERSION -m "kmhelpers v$PATCH_VERSION"
git push origin v$PATCH_VERSION
Then bring the fix and the CHANGELOG section into main through a PR. This
tag gets the latest docs alias; redeploy the newest release afterwards with
gh workflow run docs.yml --ref v<newest>.