This repository contains a production-ready Python project starter with uv dependency management, linting, formatting, testing, CI, and contribution guidelines all baked in. It represents a gold-standard foundation for building robust Python applications, designed to eliminate setup friction and enforce quality from the very first commit.
There are three recommended ways to use this scaffold, depending on your team's needs.
- GitHub Template Repository (Recommended) β For org-wide standards. Create a repository from this scaffold and enable the βTemplate repositoryβ setting. Team members can then click Use this template to generate new, fully compliant projects instantly.
- Cookiecutter (CLI-driven Templating) β For parameterized, automated scaffolding. Users can generate a new project by running
pipx run cookiecutter gh:your-org/your-template-repo.
This automatically renames files and fills in boilerplate details like author name and license. - Bootstrap Script (Onboarding Convenience) β For frictionless local setup. After cloning, a contributor can run a single script (
./scripts/bootstrap.shor.\scripts\bootstrap.ps1) to create the virtual environment, install all dependencies, and set up pre-commit hooks.
The template is structured as a Cookiecutter project for maximum reusability.
{{ cookiecutter.project_slug }}/
βββ .github/
β βββ workflows/
β βββ ci.yml
βββ hooks/
β βββ post_gen_project.py
βββ scripts/
β βββ bootstrap.ps1
β βββ bootstrap.sh
βββ src/
β βββ {{ cookiecutter.project_slug }}/
β βββ **init**.py
βββ tests/
β βββ test_example.py
βββ .gitignore
βββ .pre-commit-config.yaml
βββ CONTRIBUTING.md
βββ cookiecutter.json
βββ pyproject.toml
βββ README.md
{
"project_name": "My Python Project",
"project_slug": "{{ cookiecutter.project_name | lower | replace(' ', '_') | replace('-', '_') }}",
"project_description": "A short description of the project.",
"author_name": "Your Name",
"author_email": "[email protected]",
"org_name": "your-org",
"python_version": ["3.11", "3.12"],
"license": ["MIT", "Apache-2.0", "GPL-3.0"]
}#!/usr/bin/env python3
import os
import subprocess
from pathlib import Path
ROOT = Path.cwd()
def run(cmd, check=True):
print(f"β {cmd}")
subprocess.run(cmd, shell=True, check=check)
def maybe_run(cmd):
try:
run(cmd, check=True)
except Exception:
print(f"(skipped) {cmd}")
def main():
# Ensure scripts are executable on Unix
scripts = ["scripts/bootstrap.sh"]
for s in scripts:
p = ROOT / s
if p.exists():
try:
os.chmod(p, 0o755)
except Exception:
pass
# Initialize git if not already inside a repo
if not (ROOT / ".git").exists():
maybe_run("git init -b main")
maybe_run("git add .")
maybe_run('git commit -m "Initialize project from template"')
print("\nβ
Project generated.")
print("Next steps:")
print(" 1) ./scripts/bootstrap.sh # or scripts\\bootstrap.ps1 on Windows")
print(" 2) source .venv/bin/activate # or .venv\\Scripts\\activate on Windows")
print(" 3) uv run pytest")
if __name__ == "__main__":
main()#!/usr/bin/env bash
set -euo pipefail
usage() {
cat <<'USAGE'
bootstrap.sh [-h] [--prod] [--no-hooks]
Options:
--prod Sync only production deps (no dev/test/lint groups)
--no-hooks Skip installing pre-commit hooks
-h, --help Show this help
USAGE
}
PROD=false
HOOKS=true
for arg in "$@"; do
case "$arg" in
--prod) PROD=true ;;
--no-hooks) HOOKS=false ;;
-h|--help) usage; exit 0 ;;
*) echo "Unknown option: $arg"; usage; exit 2 ;;
esac
done
echo "π Checking for uv..."
if ! command -v uv >/dev/null 2>&1; then
echo "β 'uv' not found on PATH. Please install uv: https://github.com/astral-sh/uv"
exit 1
fi
echo "π΅ Creating virtual environment (.venv)..."
uv venv
if [ "$PROD" = true ]; then
echo "π΅ Syncing production dependencies (no dev/test/lint)..."
uv sync --no-dev
else
echo "π΅ Syncing dev/test/lint dependency groups..."
# Adjust groups to match your pyproject's [dependency-groups]
uv sync --group dev --group test --group lint
fi
if [ "$HOOKS" = true ]; then
echo "π΅ Ensuring pre-commit is available..."
if ! command -v pre-commit >/dev/null 2>&1; then
# Prefer using uv to run it from the venv if not globally installed
echo " Installing 'pre-commit' into the environment..."
uv add --group dev pre-commit >/dev/null 2>&1 || true
fi
echo "π΅ Installing pre-commit hooks..."
# Use uv run to ensure we use the env-local pre-commit
uv run pre-commit install
fi
ACTIVATE_HINT="source .venv/bin/activate"
if [[ "${OS:-}" = "Windows_NT" ]] || [[ "$(uname -s || true)" = *"MINGW"* ]]; then
ACTIVATE_HINT=".venv\\Scripts\\activate"
fi
echo ""
echo "β
Environment ready!"
echo " Activate: $ACTIVATE_HINT"
echo " Test: uv run pytest"
echo " Lint: uv run ruff check ."
echo " Format: uv run black ."#!/usr/bin/env pwsh
$ErrorActionPreference = "Stop"
param(
[switch]$Prod,
[switch]$NoHooks
)
function Check-Cmd($cmd) {
$null -ne (Get-Command $cmd -ErrorAction SilentlyContinue)
}
if (-not (Check-Cmd "uv")) {
Write-Error "'uv' not found. Install: https://github.com/astral-sh/uv"
}
Write-Host "π΅ Creating virtual environment (.venv)..."
uv venv
if ($Prod) {
Write-Host "π΅ Syncing production dependencies..."
uv sync --no-dev
} else {
Write-Host "π΅ Syncing dev/test/lint dependencies..."
uv sync --group dev --group test --group lint
}
if (-not $NoHooks) {
if (-not (Check-Cmd "pre-commit")) {
Write-Host " Installing pre-commit into the environment..."
uv add --group dev pre-commit | Out-Null
}
Write-Host "π΅ Installing pre-commit hooks..."
uv run pre-commit install
}
Write-Host ""
Write-Host "β
Environment ready!"
Write-Host " Activate: .venv\\Scripts\\Activate.ps1"
Write-Host " Test: uv run pytest"
Write-Host " Lint: uv run ruff check ."
Write-Host " Format: uv run black ."# {{ cookiecutter.project_name }}
[](https://github.com/{{ cookiecutter.org_name }}/{{ cookiecutter.project_slug }}/actions/workflows/ci.yml)
[](https://github.com/astral-sh/ruff)
[](https://github.com/psf/black)
[](pyproject.toml)
[](LICENSE)
{{ cookiecutter.project_description }}
## π οΈ Environment and Dependency Management
This project uses **[uv](https://github.com/astral-sh/uv)** for high-performance Python packaging and dependency management. The `uv.lock` file is the single source of truth for reproducible environments.
[](CONTRIBUTING.md#dependency-management)
### Quick Start
1. **Install `uv`**
Follow the [official installation instructions](https://github.com/astral-sh/uv#installation).
2. **Run the bootstrap script**
This will create your environment, install dependencies, and set up pre-commit hooks.
```bash
# On macOS/Linux
./scripts/bootstrap.sh
# On Windows (PowerShell)
.\scripts\bootstrap.ps1-
Activate your environment
# On macOS/Linux source .venv/bin/activate # On Windows .venv\Scripts\activate
For detailed rules on how to add, remove, or update dependencies, please see our Contributing Guide.
### `CONTRIBUTING.md`
```markdown
# Contributing to {{ cookiecutter.project_name }}
First off, thank you for considering contributing! This document outlines our development process and guidelines to make contributing as easy and transparent as possible.
## Getting Started
1. **Run the bootstrap script**
This single command sets up your entire local development environment.
```bash
# On macOS/Linux
./scripts/bootstrap.sh
# On Windows (PowerShell)
.\scripts\bootstrap.ps1
-
Activate your environment
# On macOS/Linux source .venv/bin/activate # On Windows .venv\Scripts\activate
This project uses uv for fast, reproducible dependency management. To maintain stability, all collaborators must adhere to the following rules.
Key Rule: DO NOT edit the [project.dependencies] section of pyproject.toml or the uv.lock file manually. Use the uv command-line tool for all changes.
-
To add a dependency:
uv add <package-name> uv add --group dev <package-name>
-
To remove a dependency:
uv remove <package-name>
-
To update a dependency:
uv lock --upgrade-package <package-name>
-
Linting: We use
Ruffto check for code quality.uv run ruff check . -
Formatting: We use
Blackto format our code.uv run black .
-
Run the full test suite:
uv run pytest
- Fork the repository and create a new branch from
main. - Make your changes and add/update tests as appropriate.
- Ensure the test suite passes (
uv run pytest). - Ensure your code is formatted and linted (
uv run black .anduv run ruff check .). Our pre-commit hooks should handle this automatically. - Push your branch and open a pull request.
### `pyproject.toml`
```toml
[project]
name = "{{ cookiecutter.project_slug }}"
version = "0.1.0"
description = "{{ cookiecutter.project_description }}"
authors = [
{ name = "{{ cookiecutter.author_name }}", email = "{{ cookiecutter.author_email }}" }
]
license = { text = "{{ cookiecutter.license }}" }
requires-python = ">=3.11,<3.13"
dependencies = [
# Add your main dependencies here using `uv add <package>`
]
[project.urls]
Homepage = "https://github.com/{{ cookiecutter.org_name }}/{{ cookiecutter.project_slug }}"
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[dependency-groups]
dev = [
"ruff",
"black",
"pre-commit"
]
test = [
"pytest"
]
lint = [
"ruff"
]
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.6.0
hooks:
- id: check-yaml
- id: end-of-file-fixer
- id: trailing-whitespace
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.5.0
hooks:
- id: ruff
args: [--fix]
- id: ruff-format
- repo: local
hooks:
- id: uv-lock-check
name: Check uv.lock freshness
entry: uv lock --check
language: python
types: [toml]
files: ^pyproject\.toml$name: CI
on:
push:
branches: ["main"]
pull_request:
branches: ["main"]
jobs:
build-and-test:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.11", "3.12"]
steps:
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Install uv
run: curl -LsSf https://astral.sh/uv/install.sh | sh
- name: Check lock file freshness
run: ~/.local/bin/uv lock --check
- name: Install dependencies
run: ~/.local/bin/uv sync --group dev --group test --group lint
- name: Lint with Ruff
run: ~/.local/bin/uv run ruff check .
- name: Run tests
run: ~/.local/bin/uv run pytest# Python
__pycache__/
*.py[cod]
*$py.class
*.so
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# Virtual Environment
.venv/
venv/
ENV/
env/
env.bak/
venv.bak/
# uv
.uv_cache/
# IDEs
.idea/
.vscode/
# OS
.DS_Store