Makefile Convention
The Python repositories of Entirius (libraries, Django modules, the service) run their development tasks through
make, with the same target names: make test means the same thing in a module and in the service.
The frontends and the documentation repository have no Makefile and use their package manager’s scripts.
Why Make
Section titled “Why Make”- No installation:
makeis on every development machine and CI runner;justwould be one more binary. - Familiar: no onboarding cost.
- One tool across the platform: the harness entirius-zeno runs on
maketoo, with targets of its own for the stack.
Make syntax (tabs, .PHONY, $$) is clumsier than just. For one-line wrappers around uv run ... that does not
matter.
The Makefiles to copy from: entirius-django-pim (module) and entirius-service-volkanos (service).
Targets
Section titled “Targets”A library or a module has the first five. The service adds the targets marked with it.
| Target | Where | Class | Does |
|---|---|---|---|
help (default) | everywhere | safe | lists the targets with their descriptions |
install | everywhere | safe | uv sync --all-extras in libraries and modules, uv sync in the service |
check | everywhere | safe | ruff check, ruff format --check, and the secret-scanning config guard |
fix | everywhere | dev | ruff check --fix and ruff format; modifies files |
test | everywhere | dev / CI | pytest -x -q; in the service, makemigrations --check --dry-run first |
test-legacy | modules that kept their own API keys | dev / CI | the same suite with the access module switched off |
health | service | prod-safe | manage.py check, check --database default, migrate --check |
run | service | dev | development server (runserver) |
migrate / makemigrations | service | dev | apply or create migrations |
shell | service | dev | manage.py shell |
schema | service | dev | OpenAPI file from drf-spectacular |
settings-example | service | dev | regenerates the module list of main/settings_example.py from modules.yaml |
first-channel | service | dev | creates the first channel from a TOML file (CONFIG=, default scripts/first-channel.example.toml) |
settings-local | service | dev-only | copies main/settings_example.py to main/settings_local.py when it is missing; the service does not boot without that file |
test-users | service | dev-only | creates the test users from scripts/test_users.yaml |
init | service | dev-only | settings-local, migrate, test-users |
reinstall | service | dev-only | rebuilds the virtual environment from the lock |
recreate-db | service | dev-only, destructive | deletes the local sqlite database file |
rebuild | service | dev-only | reinstall, recreate-db, init, check, test |
A new target name for libraries, modules or the service is added to this table first. Behaviour and end-to-end
tests do not live in the service or in a module: they are in the test package
entirius-test-package-emporium, whose Makefile keeps
help, install, check and fix and has test, bdd and e2e targets of its own.
make check test runs the same ruff and pytest commands as the CI lint and test jobs, with two differences:
the CI lint job also checks the license headers, and the service’s migration check runs in make test only.
check also runs a guard that needs the maintainers’ secret-scanning config and exits 1 without it; what to run
instead: Secret Scanning.
Prod-Safe vs Dev-Only
Section titled “Prod-Safe vs Dev-Only”Targets are classified by whether they change state:
- prod-safe (
health,check): safe to run in any environment, production included. They write nothing to the database or to files.migrate --checkexits non-zero when the database is missing migrations and applies nothing. - dev-only (
settings-local,test-users,init,reinstall,recreate-db,rebuild): they change the database, the settings file or the virtual environment. Development machines only.
pytest creates a throwaway test database, so test is not prod-safe and never part of health.
guard-dev
Section titled “guard-dev”Every dev-only target depends on a guard-dev target that refuses to run outside a development environment:
- Fail-closed:
ENVIRONMENTdefaults toproduction; a dev-only target runs only withdevelopmentorlocal. SetENVIRONMENT=developmentonce in.env(gitignored); the Makefile loads that file. - The guard is a first line, not a guarantee: anyone with a shell can set the variable. What protects a production
database is a database role without
DROPandCREATErights.
Test Users
Section titled “Test Users”Test users are declared in YAML. test-users and init create them through scripts/ensure_test_users.py, which
refuses to run unless the Django setting ENVIRONMENT is development or local: a second guard, independent of
make.
Real passwords are never committed. The repository carries scripts/test_users.example.yaml with placeholders;
scripts/test_users.yaml is gitignored. The script reads the local file and falls back to the example, so
make init works on a fresh clone.
Self-Documentation
Section titled “Self-Documentation”make with no arguments runs help (.DEFAULT_GOAL := help). Every public target carries a ## description
comment on its line, and help collects them with grep, so the list cannot drift from the Makefile.
- Git Flow: branches and pull requests
- CI/CD: the jobs
make check testmirrors - Python Toolchain: the tools the targets wrap