This document defines the standard Makefile targets used across Python services in Benjamin Borbe's development ecosystem. These commands ensure consistent build processes, code quality, and testing workflows using modern Python tooling (uv, ruff, mypy, pytest).
Purpose: Run full precommit checks - the comprehensive quality gate before committing code. What it does: Executes format, test, and check in sequence. When to use: Before every commit to ensure code meets all quality standards.
makePurpose: Explicit precommit workflow execution.
What it does: Same as default make target - runs all quality checks.
When to use: Explicitly when preparing code for commit or CI/CD.
make precommitOwner: python-quality-assistant
Applies when: a Python project's Makefile is missing a precommit target that runs format + test + check (or equivalent: lint, type-check, test). The default make target (no argument) MUST resolve to precommit so CI and pre-commit hooks can invoke make uniformly across projects.
Enforcement: judgment (Makefile presence + target-list inspection: grep -E '^precommit:' Makefile plus default-target check via make -np | grep '^precommit')
Trigger: Makefile
Why: Different Python projects use different tools (ruff vs pyright vs mypy; pytest vs unittest; uv vs poetry). make precommit is the project-agnostic uniform entry point — CI scripts, pre-commit hooks, and editor integrations call make precommit and don't have to know the tool choices behind it. Without the target, CI scripts hard-code tool names that drift per-project, and the "run all quality checks" command is unmemorable per-developer. The target is one line; the value is uniform tooling across the org.
# No precommit target — CI scripts must know each tool by name per project
test:
uv run pytest
lint:
uv run ruff check
typecheck:
uv run mypy src.PHONY: precommit format test check sync
precommit: format test check
@echo "All precommit checks passed"
# Alternative with dependency sync (recommended for CI/CD):
sync:
uv sync --all-extrasStandard implementation:
precommit: format test check
@echo "All precommit checks passed"Alternative with dependency sync (recommended for CI/CD):
precommit: sync format test check
@echo "All precommit checks passed"
sync:
uv sync --all-extrasPurpose: Execute comprehensive test suite with pytest. What it does:
- Runs all tests using pytest
- Executes tests with proper Python path configuration
- Generates test coverage reports (optional)
- Supports async test execution with pytest-asyncio
make testStandard implementation:
test:
uv run pytestWith coverage:
test:
uv run pytest --cov=src --cov-report=term-missingKey features:
- Fast test execution with pytest
- Async test support via pytest-asyncio
- Comprehensive assertion framework
- Coverage reporting and threshold enforcement
Owner: python-quality-assistant
Applies when: a Python project's Makefile (or CI script) uses pip install -r requirements.txt / poetry install / pipenv install instead of uv sync for dependency management.
Enforcement: judgment (Makefile + CI-config inspection for non-uv install commands; cross-check against pyproject.toml having [tool.uv] or uv.lock present)
Trigger: Makefile, pyproject.toml
Why: uv is 10-100× faster than pip/poetry/pipenv on dependency resolution and install; it reads the standard pyproject.toml PEP 621 metadata directly (no per-tool config block); and it produces a deterministic uv.lock that's portable across machines. pip is fine for installing a single package; for project dependency management it lacks lock files and resolves slowly. Poetry has lock files but its own non-standard [tool.poetry] section in pyproject.toml and is much slower. The org has consolidated on uv; new Python projects MUST start there. (SHOULD level because legacy projects mid-migration are acceptable.)
# pip — no lock file, slow resolution
install:
pip install -r requirements.txt
# poetry — non-standard pyproject.toml, slower than uv
install:
poetry install --with devinstall:
uv sync --all-extras
# CI:
test: install
uv run pytestPurpose: Install project dependencies using uv. What it does:
- Synchronizes dependencies from pyproject.toml
- Installs all optional dependencies (dev, test)
- Creates or updates virtual environment
- Ensures consistent dependency versions via uv.lock
make installStandard implementation:
install:
uv sync --all-extrasPurpose: Apply consistent code formatting and auto-fix linting issues. What it does:
- Runs
ruff formatto format Python source code - Runs
ruff check --fixto auto-fix linting issues - Applies consistent indentation and spacing
- Organizes imports
- Removes unused imports
make formatStandard implementation:
format:
uv run ruff format .
uv run ruff check --fix . || trueBenefits:
- Maintains consistent code style across team
- Reduces code review friction
- Automatically fixes common formatting issues
- Fast execution (Rust-based ruff)
Purpose: Run comprehensive static analysis and type checks. What it does: Executes lint and typecheck in sequence. When to use: Part of precommit workflow or standalone quality verification.
make checkStandard implementation:
check: lint typecheck
@echo "All checks passed"Purpose: Run linting checks without auto-fixing. What it does:
- Runs
ruff checkin check-only mode - Identifies code quality issues
- Reports potential bugs and anti-patterns
- Validates import organization
- Checks for common Python mistakes
make lintStandard implementation:
lint:
uv run ruff check .Critical for:
- Identifying code smells before commit
- Enforcing coding standards
- Catching potential bugs early
- Maintaining code quality consistency
Purpose: Run static type checking with mypy. What it does:
- Analyzes Python code for type correctness
- Validates type hints against actual usage
- Catches type-related bugs before runtime
- Enforces type safety standards
make typecheckStandard implementation:
typecheck:
uv run mypy srcAdvanced configurations:
# Strict type checking
typecheck:
uv run mypy src --strict
# With specific flags
typecheck:
uv run mypy src --no-error-summary --show-error-codesPurpose: Run the application in development mode. What it does: Executes the main application entry point.
make runExample implementations:
# For services with entry point
run:
uv run skeleton serve
# For FastAPI development
run:
uv run uvicorn main:app --reload --port 8000
# For CLI tools
run:
uv run python main.pyPurpose: Remove build artifacts and caches. What it does:
- Removes virtual environment
- Cleans Python cache directories (pycache)
- Removes test artifacts (.pytest_cache)
- Cleans type checker cache (.mypy_cache)
- Removes ruff cache (.ruff_cache)
- Cleans build directories (dist, *.egg-info)
make cleanStandard implementation:
clean:
rm -rf .venv dist *.egg-info .pytest_cache .mypy_cache .ruff_cache
find . -type d -name __pycache__ -exec rm -rf {} +For larger projects, split Makefile into logical modules:
Makefile # Main file with includes
Makefile.variables # Environment and build variables
Makefile.precommit # Quality check targets
Makefile.docker # Docker build targets
Makefile.k8s # Kubernetes deployment targets
Main Makefile structure:
include Makefile.variables
include Makefile.precommit
include Makefile.docker
include example.env
SERVICE = bborbe/my-service
.PHONY: all run install clean
all: precommit
install:
uv sync --all-extras
run:
uv run my-service serve
clean:
rm -rf .venv dist *.egg-info .pytest_cache .mypy_cache .ruff_cache
find . -type d -name __pycache__ -exec rm -rf {} +Makefile.precommit:
.PHONY: precommit format lint typecheck check test
precommit: format test check
@echo "All precommit checks passed"
format:
uv run ruff format .
uv run ruff check --fix . || true
lint:
uv run ruff check .
typecheck:
uv run mypy src
check: lint typecheck
test:
uv run pytestMakefile.variables:
BRANCH ?= $(shell git rev-parse --abbrev-ref HEAD)
HOSTNAME ?= $(shell hostname -s)
ROOTDIR ?= $(shell git rev-parse --show-toplevel)# Daily development cycle
make install # Install dependencies
make format # Format code changes
make test # Run tests
make check # Static analysis
make # Full precommit check# Before every commit (MANDATORY)
make precommit # Comprehensive quality gate
git add .
git commit -m "implement feature X"# Typical CI pipeline
make install # Install dependencies
make # Full quality check
make test # Test with coverageAll tool configurations should be in pyproject.toml:
[project]
name = "my-service"
version = "0.1.0"
requires-python = ">=3.12"
dependencies = [
"fastapi>=0.115.0",
"uvicorn>=0.32.0",
"pydantic>=2.10.0",
]
[project.optional-dependencies]
dev = [
"pytest>=8.3.0",
"pytest-asyncio>=0.24.0",
"ruff>=0.8.0",
"mypy>=1.13.0",
]
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[tool.hatch.build.targets.wheel]
packages = ["src/my_service"]
[tool.ruff]
line-length = 100
target-version = "py312"
[tool.ruff.lint]
select = [
"E", # pycodestyle errors
"W", # pycodestyle warnings
"F", # pyflakes
"I", # isort
"B", # flake8-bugbear
"C4", # flake8-comprehensions
"UP", # pyupgrade
"SIM", # flake8-simplify
"RUF", # ruff-specific rules
]
[tool.mypy]
python_version = "3.12"
strict = true
warn_return_any = true
warn_unused_ignores = true
disallow_untyped_defs = true
[tool.pytest.ini_options]
asyncio_mode = "auto"
asyncio_default_fixture_loop_scope = "function"
testpaths = ["tests"]
python_files = ["test_*.py"]
python_classes = ["Test*"]
python_functions = ["test_*"]Key configurations:
asyncio_mode = "auto"- Automatically detect and run async tests (required for pytest-asyncio)asyncio_default_fixture_loop_scope = "function"- Create new event loop per test functiontestpaths- Directory containing testspython_*- Test discovery patterns
Build Failures:
- Run
make installto fix dependency issues - Check Python version compatibility (>=3.12)
- Verify uv is installed:
curl -LsSf https://astral.sh/uv/install.sh | sh
Test Failures:
- Review test output for specific failures
- Check test dependencies in pyproject.toml
- Ensure virtual environment is active
Format Issues:
- Run
make formatbefore committing - Configure IDE to use ruff on save
- Check for syntax errors
Type Check Failures:
- Add missing type hints to functions
- Review mypy configuration strictness
- Check for incompatible type assignments
Lint Failures:
- Review ruff output for specific violations
- Use
make formatto auto-fix when possible - Update code to follow Python best practices
These Makefile commands integrate with the mandatory git workflow:
- Feature Development: Use
make testandmake formatduring development - Pre-Commit: ALWAYS run
make precommitbefore committing - Code Review: Reviewers expect all quality checks to pass
- Release: CI/CD relies on these commands for automated quality gates
Critical Requirements:
- Never commit without running
make precommit - All quality checks must pass before code review
- Dependencies must be locked in uv.lock
- Type hints must be present on all public functions
| Aspect | Python (uv/ruff/mypy) | Go (ginkgo/golangci-lint) |
|---|---|---|
| Formatting | ruff format |
gofmt + goimports |
| Linting | ruff check |
golangci-lint |
| Type checking | mypy |
Built into compiler |
| Testing | pytest |
ginkgo + gomega |
| Mocks | unittest.mock |
counterfeiter |
| Package manager | uv |
Go modules |
DO:
- Keep Makefile targets consistent across projects
- Use
.PHONYfor all non-file targets - Add descriptive echo messages for long-running targets
- Document non-standard targets with comments
- Use
uv runprefix for all tool executions - Fail fast with
set -ein shell commands
DON'T:
- Don't hardcode paths - use variables
- Don't skip
make precommitbefore committing - Don't mix pip/poetry/uv in same project
- Don't commit without passing tests
- Don't disable type checking or linting globally
This standardized approach ensures consistent code quality, security, and maintainability across all Python services in the development ecosystem.