commoner
One front door for everything a repo can do. Test tiers, dev servers, the docker
stack, migrations and the little chains of steps between them are named in a
commoner.yaml — and commoner <thing> runs them.
The alternative it replaces is a scripts/ folder: fifteen four-line shell
files that each cd to the repo root and exec one command, where adding a test
tier means writing another one and the only way to know what a repo can do is to
ls them.
pip install commoner-cli
# commoner.yaml
tests:
unit: {marker: unit}
e2e:
marker: e2e
args: --api-mode=live_server
ui:
path: ui
package_manager: pnpm
tests:
unit: {script: test}
run:
api:
script: uvicorn app.api:app --reload --port 8000
docker:
path: infrastructure
local:
compose: compose.yaml
profiles: {api: the API in a container}
migration-providers: [alembic]
tasks:
init-db:
steps: [up --detach, migrate]
commoner help # what this repo defines — generated from the config
commoner test unit # poetry run pytest -m unit
commoner test ui:unit # pnpm run test, in ui/
commoner test # everything, across every project
commoner api # poetry run uvicorn app.api:app --reload --port 8000
commoner up api --detach # docker compose --profile api up --detach
commoner init-db # compose up --detach, then alembic upgrade head
commoner --dry-run <any> # print the commands, run nothing
Nothing above is built in. unit, api, init-db and the rest exist because
the config names them; the tool only knows how to run a kind of thing.
The config
Top-level keys are either a known section or a project — ui: is a project
because it isn't reserved. example.commoner.yaml is the annotated schema; the
short version:
| Section | What it defines |
|---|---|
project |
the repo's name, for help output |
python |
the root project's toolchain: runner (poetry/uv/none), install_args, tests_path |
tests |
test tiers, as commoner test <tier> |
run |
long-lived processes; each is also a top-level command |
docker |
one compose file and its profiles, as commoner up [profile...] |
migration-providers |
gives you commoner migrate / commoner revision |
tasks |
a named sequence of other commoner commands |
env |
the env file commoner bootstrap creates from its example |
| anything else | another project, with its own path and package manager |
What an entry runs
Every entry — a tier, a process, a step — says what to run in exactly one of
four ways, and where with project::
unit: {path: unit} # <runner> pytest tests/unit
unit: {marker: unit} # <runner> pytest -m unit (module: also works)
unit: {script: pytest -m unit} # through the project's runner / package manager
unit: {cmd: poetry run pytest} # literally, as written
Plus args: (always appended, before the caller's own), description: (shown
in help), and todo: (say it isn't built yet and skip, rather than fail).
Extra arguments on the command line are appended to whatever the entry resolves
to, so the "$@" you would write in a shell script is implicit — and stripped
if you write it anyway. commoner test e2e -k login and
commoner test ui:e2e --tags '@auth' both land where you expect.
Conventions worth knowing
- A tier named
allstands in for its whole project, socommoner testis onepytestrun rather than one per marker. - A node project with no
node_modulesmakes its tiers skip, not fail — a partial checkout stays testable. - Unprofiled compose services always come up; profiles layer the rest on top.
commoner upis your backing services,commoner up api workersthe lot. - Names in
run:andtasks:become top-level commands, and the config is rejected if one shadows a built-in.
Built-in commands
| Command | What it does |
|---|---|
bootstrap |
install every project's dependencies, create the env file |
test [tier] [args...] |
run a tier, a project's tiers, or everything |
run <name> [args...] |
run a configured process |
up [profile...] / down |
the docker stack |
build |
build the configured dockerfile |
compose <args...> |
raw docker compose against the stack |
migrate [target] / revision <message> |
the configured migration provider |
help |
the repo's own command surface |
Global flags: --config PATH, --dry-run, -q/--quiet, --version.
How it works
config.py— the only module that knows the file format: loading, validation, and turning an entry into an argv.cli.py— the dispatcher. Help text is generated from the config, so it can't drift from it.process.py— how a command leaves the process:exec_hands over the terminal (signals, exit codes and Ctrl-C belong to the real command),runwaits. A multi-step caller setsConsole.replace = False, which turns the former into the latter for everything nested under it — so a command written to hand over still composes inside a task.commands/— one module per built-in;migrations/— one per provider.
A project is a unit of source with its own toolchain: it knows how to install itself, how to prefix a command so it runs in its environment, and when it isn't ready.
Wiring it into a repo
Installing puts commoner on your PATH. Many repos also keep a one-line dev
shim at the root so a fresh clone needs no explanation:
#!/usr/bin/env bash
set -euo pipefail
cd "$(dirname "$0")"
exec commoner "$@"
Status
Early. The schema is settled enough to use and documented in
example.commoner.yaml; alembic is the only migration provider so far, and
new kinds of thing are the only reason to change the code — a new entry point
is a yaml edit.
MIT licensed.
Release files for commoner-cli 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| commoner_cli-0.1.0.tar.gz | 18.3 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| commoner_cli-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 40.8 kB
Release files / commoner_cli-0.1.0.tar.gz
| Download URL | commoner_cli-0.1.0.tar.gz |
|---|---|
| Size | 18.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
0ede430d3d41e5bf3e1b294b39abff2fb02ab95fb57c05b5e79259aef809dded
|
|
BLAKE2b-256 checksum How to use checksums |
e12fa2cbfb1d008c1b0f1b68644c6cd72a8e4c9515e21ca14ff207dacff2442f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 14, 2026.
Transparency logRelease files / commoner_cli-0.1.0-py3-none-any.whl
| Download URL | commoner_cli-0.1.0-py3-none-any.whl |
|---|---|
| Size | 22.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
af0a74079f8627119d11797641c5dd8066dedc1f863e5a496047c3d292e4eb4b
|
|
BLAKE2b-256 checksum How to use checksums |
5b1bcb5d168367d687c62c4d2d3dd4698239075a41f2abb6a35ee7e6990eaea0
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 14, 2026.
Transparency log