A batteries-included starting point for Python projects, wired up with my preferred tooling so a new repo is lint-clean, tested, and CI-ready from the first commit.
- uv — dependency management, virtual environments, and Python version pinning
- pre-commit hooks — Ruff (lint + autofix), Black (format), mypy (type-check), hadolint (Dockerfile lint), plus a set of hygiene checks
- pytest with coverage via pytest-cov
- Docker — multi-stage Dockerfile + Compose for reproducible, containerized builds
- GitHub Actions — lints, tests, and builds the image on every push and pull request to
main
.
├── .github/workflows/tests.yml # CI: lint (pre-commit) + test (pytest + coverage)
├── src/ # your application code
│ ├── main.py # entry point
│ └── ui.py # example PySide6 window (optional — see below)
├── tests/ # pytest tests (test_*.py)
│ └── test_main.py
├── .pre-commit-config.yaml # ruff, black, mypy, hadolint, hygiene hooks
├── .python-version # pinned Python version (drives CI + uv)
├── Dockerfile # multi-stage, uv-based production image
├── compose.yaml # Docker Compose for local runs
├── pyproject.toml # project metadata + dependencies
└── uv.lock # pinned, reproducible lockfile
Prerequisite: install uv.
uv sync # create .venv and install everything from uv.lock
pre-commit install # run the hooks automatically on every commitSecrets and configuration go in a .env file (git-ignored, loaded automatically via
python-dotenv):
touch .envuv run python -m src.main # run the app
uv run pytest # run tests with coverage
uv run pre-commit run --all-files # run every hook manually
uv add <package> # add a runtime dependency
uv add --dev <package> # add a dev/tooling dependencyUsing the example GUI? src/ui.py is a small PySide6 window included for reference. PySide6 is not installed by default — run
uv add pyside6if you want to use it.
A multi-stage Dockerfile and compose.yaml are included. The
image installs dependencies from uv.lock, runs as a non-root user, and defaults to
python -m src.main.
docker build -t myapp . # build the image
docker run --rm myapp # run it
docker compose up --build # ...or via ComposeFor a web service, set an EXPOSE/port and update the CMD in the Dockerfile (and the
ports in compose.yaml). CI builds the image on every push/PR to catch a broken Dockerfile.
- Set
name,description, and an author in pyproject.toml - Replace the example code in src/ and write real tests in tests/ (named
test_*.py) - Adjust the pinned version in .python-version if needed
- Customize the hooks in .pre-commit-config.yaml
- Edit .gitignore
- Set the
CMD/ports in Dockerfile and compose.yaml for your app - Add your license text to the empty LICENSE file
- Update the CI badge URL and rewrite this README