Thank you for considering contributing to python-template!
Either Nix -- nix develop gives every tool below, at the versions flake.lock pins -- or,
installed yourself:
- cargo-generate 0.25 or later, which the
template is for (
just generate,just smoke). - Python 3.11 or later, for
just render-check: it installstools/requirements.txtinto.venv. - just and jq -- the automation, and what it reads JSON with.
- ShellCheck and shfmt --
the shell's linter and formatter (its style is
.editorconfig's). - actionlint -- lints the workflows, this repository's and the template's.
- dprint -- formats the Markdown and TOML.
- cocogitto (
cog) -- enforces Conventional Commits. - git-cliff, for maintainers:
just changelogandjust releasewrite the changelog with it. - Nix for the last of
just checks, the flake's checks (just nix), and forjust smoke, which runs a generated project's checks in its own flake's shell.
- Fork this repository and create your branch from
main. - Clone your fork:
git clone https://github.com/<you>/python-template && cd python-template- Optionally,
just install-hooks:.githooks/then checks the shell, the workflows and dprint before each commit, the message as a Conventional Commit, and every pushed commit's message before a push -- what CI would refuse, minutes earlier. They needjust,jq,shellcheck,shfmt,actionlint,dprintandcogonPATH(insidenix develop, with Nix);git config --unset core.hooksPathturns them off.
Before you start, read AGENTS.md: how template/ and the rest differ, what
cargo-generate does with a file, which files are copies. It applies to human and AI-assisted
contributions alike.
- The Conventional Commits specification is enforced on
every commit. A commit's subject is its changelog entry -- git-cliff writes the changelog from
them at each release -- so write it for the template's users; a change to
template/takes the scopetemplate. - Run the checks. The
justfileruns the workflows' commands:
just checks # render-check, the shell, the workflows, dprint, cog, the flake's last
just render-check # every templated file rendered for every answer combination, checked
just smoke workspace # generated with cargo-generate, then its own `just checks` (each kind)
just generate bin # target/bin/demo, to look at, committed once
just shell-check # shellcheck and shfmt, this repository's (and template-shell-check)
just format # every formatter the checks hold the files to, `nix fmt` lastjust alone lists every recipe; nix fmt alone formats the Nix, the shell and the justfiles.
- Make sure
just checkspasses -- and, for a change undertemplate/,just smokefor each kind (bin,lib,workspace). - Open a pull request against
main, titled as a conventional commit: a squash merge makes the title its commit, and so a changelog entry (CI checks it). Describe the problem it solves, and link its issue (Fixes #123), if any. - Wait for a maintainer's review.
- The auto-merge workflow's settings are listed at the top of
.github/workflows/dependabot-automerge.yml. Without required status checks, it merges bumps that CI never gated. - Enable private vulnerability reporting (Settings → Code security): it is off by default, and SECURITY.md and the issue form point to it.
The template has no version file: a release is a tag, v<version>, which a user generates from with
cargo generate --git https://github.com/veralvx/python-template --tag v<version> template. From a
clean main equal to origin/main, CI green on it:
just changelog --unreleased # the section the release will write
just release <major|minor|patch>The recipe shows every step, in order, and runs no tests -- CI's verdict on that commit is the
release's: it refuses a release with nothing in it, writes the changelog (git-cliff), commits,
tags v<version> and pushes the commit and the tag together. The first release is 0.1.0,
whatever the level. The tag runs release.yml: the GitHub release, the changelog's new section as
its notes.
- The commits since the last tag must hold a change a user meets (
feat,fix,perf,build...):chore,ciandstylealone make no release. - The release pushes to
maindirectly: with "Require a pull request before merging" on, the maintainer cutting it needs the rule's bypass. A refused push leaves the commit and the tag local -- push them again withgit push --atomic origin main v<version>once allowed.
This flake follows nixos-unstable, for cargo-generate 0.25; a generated project's tools are its own
flake's (template/CONTRIBUTING.md) -- nixos-26.05's, whose shfmt and
just are older: a change to the template's shell passes just shell-check there too, in a generated
project's shell. The smoke test installs its own tools in the workflow: move those by hand.
Dependabot moves the actions, this repository's and the template's in one pull request (the copies
render-check holds equal move together), as build(deps), a changelog entry: they change what a
project is generated with. It moves render-check's Python packages too (tools/requirements.txt).