Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
name: Setup Python and Poetry
name: Setup Python and uv
description: >
Reusable composite action to set up Python, install Poetry, and restore
the project virtual environment from cache (keyed on poetry.lock hash).
Reusable composite action to set up Python, install uv, and restore
the project virtual environment from cache (keyed on uv.lock hash).

inputs:
python-version:
description: Python version to install
required: false
default: "3.12"
working-directory:
description: Directory containing pyproject.toml and poetry.lock
description: Directory containing pyproject.toml and uv.lock
required: false
default: "."

Expand All @@ -21,26 +21,20 @@ runs:
with:
python-version: ${{ inputs.python-version }}

- name: Install Poetry
- name: Install uv
shell: bash
run: pip install --quiet poetry

- name: Configure Poetry to create in-project virtual environments
shell: bash
working-directory: ${{ inputs.working-directory }}
run: poetry config virtualenvs.in-project true
run: pip install --quiet uv

- name: Cache virtual environment
id: cache-venv
uses: actions/cache@v4
with:
path: ${{ inputs.working-directory }}/.venv
key: venv-${{ runner.os }}-${{ inputs.python-version }}-${{ hashFiles(format('{0}/poetry.lock', inputs.working-directory)) }}
key: venv-${{ runner.os }}-${{ inputs.python-version }}-${{ hashFiles(format('{0}/uv.lock', inputs.working-directory)) }}
restore-keys: |
venv-${{ runner.os }}-${{ inputs.python-version }}-

- name: Install dependencies
if: steps.cache-venv.outputs.cache-hit != 'true'
shell: bash
working-directory: ${{ inputs.working-directory }}
run: poetry install --no-interaction
run: uv sync --frozen
18 changes: 9 additions & 9 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,8 @@ Before submitting any pull request, ensure your changes pass the same quality ch

This project uses the following Python tooling:

* **[Poetry](https://python-poetry.org/)** for dependency management
(see [ADR-003](../meta/adr/ADR-003-use_poetry.md))
* **[uv](https://docs.astral.sh/uv/)** for dependency management
(see [ADR-015](../meta/adr/ADR-015-use_uv.md))
* **[Ruff](https://docs.astral.sh/ruff/)** for linting and formatting
(see [ADR-005](../meta/adr/ADR-005-use_ruff.md))
* **[pytest](https://docs.pytest.org/)** for testing
Expand All @@ -57,23 +57,23 @@ This project uses the following Python tooling:
# Navigate to an app or lib directory
cd apps/<app-name>

# Install dependencies with Poetry
poetry install
# Install dependencies with uv
uv sync

# Run linting
poetry run ruff check .
uv run ruff check .

# Run formatting
poetry run ruff format .
uv run ruff format .

# Run tests
poetry run pytest
uv run pytest

# Run tests with coverage
poetry run pytest --cov
uv run pytest --cov

# Run type checking (if mypy is configured)
poetry run mypy .
uv run mypy .
```

### Pre-Commit Checks
Expand Down
15 changes: 8 additions & 7 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -109,19 +109,20 @@ jobs:
- name: Checkout code
uses: actions/checkout@v7

- name: Set up Python and Poetry
uses: ./.github/actions/setup-python-poetry
with:
working-directory: ${{ matrix.project }}
- name: Set up Python and uv
uses: ./.github/actions/setup-python-uv
# We removed the 'working-directory' input here!
# This forces the action to run `uv sync --frozen` at the repo root
# so it can locate the single workspace uv.lock file.

- name: Ruff format check
working-directory: ${{ matrix.project }}
run: poetry run ruff format --check .
run: uv run ruff format --check .

- name: Ruff lint
working-directory: ${{ matrix.project }}
run: poetry run ruff check .
run: uv run ruff check .

- name: pytest
working-directory: ${{ matrix.project }}
run: poetry run pytest
run: uv run pytest
10 changes: 5 additions & 5 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,22 +21,22 @@ jobs:
- name: Checkout code
uses: actions/checkout@v7

- name: Set up Python and Poetry
uses: ./.github/actions/setup-python-poetry
- name: Set up Python and uv
uses: ./.github/actions/setup-python-uv
with:
working-directory: apps/example-app

- name: Build package
run: poetry build
run: uv build

- name: Verify build artifacts
run: ls -lh dist/

# Uncomment the following to enable PyPI publishing:
# - name: Publish to PyPI
# run: poetry publish --build
# run: uv publish
# env:
# POETRY_PYPI_TOKEN_PYPI: ${{ secrets.PYPI_TOKEN }}
# UV_PUBLISH_TOKEN: ${{ secrets.PYPI_TOKEN }}
#
# Prerequisites for publishing:
# 1. Add PYPI_TOKEN to your repository secrets
Expand Down
5 changes: 3 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -80,11 +80,12 @@ htmlcov/
.ruff_cache/

# =============================================================================
# Poetry
# uv
# =============================================================================
# poetry.lock is intentionally NOT ignored — lock files should be committed
# uv.lock is intentionally NOT ignored — lock files should be committed
# for applications. Libraries may choose to ignore them.


# =============================================================================
# Build artifacts and logs
# =============================================================================
Expand Down
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# python-project-blueprint

> A template for Python monorepo projects targeting Python 3.12+ with Poetry
> A template for Python monorepo projects targeting Python 3.12+ with uv
> dependency management.

## What Is This?
Expand Down Expand Up @@ -44,7 +44,7 @@ For more details on GitHub template repositories, see the
| ADR | Decision |
| :--- | :--- |
| [ADR-002](meta/adr/ADR-002-use_python312.md) | Python 3.12+ as minimum version |
| [ADR-003](meta/adr/ADR-003-use_poetry.md) | Poetry for dependency management |
| [ADR-015](meta/adr/ADR-015-use_uv.md) | uv for dependency management |
| [ADR-004](meta/adr/ADR-004-use_pytest.md) | pytest for testing |
| [ADR-005](meta/adr/ADR-005-use_ruff.md) | Ruff for linting and formatting |
| [ADR-006](meta/adr/ADR-006-use_docker.md) | Docker for containerization |
Expand Down Expand Up @@ -83,8 +83,8 @@ values appropriate for your project:
pyenv install 3.12
pyenv local 3.12

# Install Poetry
curl -sSL https://install.python-poetry.org | python3 -
# Install uv
pip install uv

# Install pre-commit hooks
pip install pre-commit
Expand All @@ -103,7 +103,7 @@ pip install -r docs-requirements.txt
```bash
mkdir -p apps/my-app
cd apps/my-app
poetry init
uv init
mkdir -p src/my_app tests
```

Expand All @@ -116,7 +116,7 @@ passes in your new repository.

* **Python 3.12+ only.** Take advantage of modern Python features and
performance improvements.
* **Poetry everywhere.** Consistent dependency management across all apps
* **uv everywhere.** Fast, reliable dependency management across all apps
and libraries.
* **Ruff for speed.** Fast linting and formatting that replaces multiple
tools.
Expand Down
28 changes: 14 additions & 14 deletions apps/example-app/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -11,34 +11,34 @@
# Resolves dependencies and pre-builds wheels for reproducible installs.
FROM python:3.12-slim AS builder

RUN pip install --no-cache-dir poetry poetry-plugin-export
RUN pip install --no-cache-dir uv

WORKDIR /build

COPY pyproject.toml uv.lock ./

# Copy the shared library (local path dependency)
COPY libs/example-lib ./libs/example-lib

# Copy application manifests before source code for Docker layer cache efficiency
COPY apps/example-app/pyproject.toml apps/example-app/poetry.lock \
./apps/example-app/

# Export pinned runtime dependencies from the Poetry lock file.
# The local path dependency (example-lib) is excluded and handled separately.
RUN cd apps/example-app \
&& poetry export \
--without dev \
--without-hashes \
--format requirements.txt \
--output /tmp/requirements.txt \
COPY apps/example-app/pyproject.toml ./apps/example-app/

# Use uv to generate requirements from the lock file for reproducible builds.
RUN uv export \
--package example-app \
--frozen \
--no-dev \
--no-hashes \
--output-file /tmp/requirements.txt \
&& grep -vE "^(-e |example-lib)" /tmp/requirements.txt \
> /tmp/app-requirements.txt || true
> /tmp/app-requirements.txt || true

# Pre-build wheels for reproducible, offline installation in the runtime stage
RUN pip wheel --no-cache-dir --wheel-dir /wheels -r /tmp/app-requirements.txt
RUN pip wheel --no-cache-dir --wheel-dir /wheels ./libs/example-lib

# ── Stage 2: Runtime ────────────────────────────────────────────────────────
# Minimal production image — no Poetry, no build tools, no source trees.
# Minimal production image — no uv, no build tools, no source trees.
FROM python:3.12-slim AS runtime

WORKDIR /app
Expand Down
16 changes: 8 additions & 8 deletions apps/example-app/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
This application provides a minimal example of how applications work in the
monorepo. It demonstrates:

* Application structure with `pyproject.toml` and Poetry
* Application structure with `pyproject.toml` and uv
* Click-based CLI (per [ADR-011](../../meta/adr/ADR-011-use_click.md))
* Path dependency on a shared library (`example-lib`)
* Testing CLI commands with `click.testing.CliRunner`
Expand All @@ -16,28 +16,28 @@ monorepo. It demonstrates:

```bash
cd apps/example-app
poetry install
uv sync
```

## Usage

```bash
# Run the CLI
poetry run example-app hello
uv run example-app hello
# Output: Hello, World!

poetry run example-app hello --name Python
uv run example-app hello --name Python
# Output: Hello, Python!
```

## Development

```bash
cd apps/example-app
poetry install
poetry run pytest
poetry run ruff check .
poetry run ruff format --check .
uv sync
uv run pytest
uv run ruff check .
uv run ruff format --check .
```

## Dependencies
Expand Down
Loading
Loading