Skip to content

Commit b8776d3

Browse files
committed
merge from master
2 parents 75d2e64 + 6545cc3 commit b8776d3

15 files changed

Lines changed: 670 additions & 1470 deletions

Makefile

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ test:
2020
--keep-project-on-failure -o /tmp .
2121

2222
cd /tmp/$(TARGET_NAME); \
23-
poetry run make lint && \
24-
poetry run make test && \
25-
poetry run make audit && \
26-
poetry run make smoke_test && \
23+
uv run make lint && \
24+
uv run make test && \
25+
uv run make audit && \
26+
uv run make smoke_test && \
2727
echo '\nSuccess!'

README.rst

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ Features
1515
--------
1616

1717
* FastAPI_ web framework
18-
* Python 3.12+ (configurable)
19-
* Poetry_ based dependency management
18+
* Python 3.14+ (configurable)
19+
* uv_ based dependency management
2020
* Development tasks registered in a ``Makefile`` for easy access and management
2121
* Custom Mercurial/Git hooks for ``pre-commit`` and ``pre-push`` events
2222
* Linting based on ruff_, mypy_ and others
@@ -35,17 +35,17 @@ Instructions
3535
============
3636

3737
To instantiate the template into a new project, you'll need cookiecutter_ (>=2.4.0).
38-
The best way to use it just once is through pipx_:
38+
The best way to use it just once is through uvx_:
3939

4040
.. code:: console
4141
42-
$ pipx run cookiecutter gh:andredias/perfect_python_project -c fastapi-minimum
42+
$ uvx run cookiecutter gh:andredias/perfect_python_project -c fastapi-minimum
4343
44-
If you prefer, use can install it throught `pip` instead:
44+
If you prefer, use can install it throught ``uv tool`` instead:
4545

4646
.. code:: console
4747
48-
$ pip install --user cookiecutter
48+
$ uv tool install cookiecutter
4949
5050
Next, run the following command:
5151

@@ -83,8 +83,8 @@ That's it!
8383
.. _HTTPX: https://www.python-httpx.org/
8484
.. _Loguru: https://github.com/Delgan/loguru
8585
.. _mypy: http://mypy-lang.org/
86-
.. _pipx: https://pypa.github.io/pipx/
87-
.. _Poetry: https://python-poetry.org/
8886
.. _pytest: https://pytest.org
8987
.. _python-dotenv: https://pypi.org/project/python-dotenv/
9088
.. _ruff: https://pypi.org/project/ruff/
89+
.. _uv: https://docs.astral.sh/uv/
90+
.. _uvx: https://docs.astral.sh/uv/guides/tools/

cookiecutter.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
{
22
"author": "",
33
"email": "",
4-
"project_name": "Perfect Python Project",
4+
"project_name": "Perfect_Minimum_FastAPI_Project",
55
"project_slug": "{{ cookiecutter.project_name.lower().replace(' ', '_').replace('-', '_') }}",
6-
"python_version": "3.12",
6+
"python_version": "3.14",
77
"line_length": 100,
88
"version_control": [
99
"hg",

hooks/post_gen_project.sh

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,8 @@
22

33
set -xuo pipefail
44

5-
poetry env use {{cookiecutter.python_version}}
6-
poetry lock --no-update
7-
poetry install
8-
poetry run make format
5+
uv sync --no-install-project
6+
uv run make format
97

108
commit_message="Initial project structure based on https://github.com/andredias/perfect_python_project/tree/fastapi-minimum"
119

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
*
2+
!entrypoint.sh
23
!pyproject.toml
3-
!poetry.lock
4+
!README.rst
5+
!uv.lock
46
!{{cookiecutter.project_slug}}/
57
**/__pycache__

{{cookiecutter.project_slug}}/.github/workflows/continuous_integration.yml

Lines changed: 20 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -8,38 +8,37 @@ jobs:
88
runs-on: ubuntu-latest
99
steps:
1010

11-
- name: Set up python
12-
uses: actions/setup-python@v4
13-
with:
14-
python-version: '{{cookiecutter.python_version}}'
15-
1611
- name: Check out repository
17-
uses: actions/checkout@v3
12+
uses: actions/checkout@v5
1813

19-
- name: Install Poetry
20-
uses: snok/install-poetry@v1
14+
- name: Set up python
15+
uses: actions/setup-python@v6
2116
with:
22-
virtualenvs-in-project: true
17+
python-version-file: "pyproject.toml"
2318

24-
- name: Load cached venv
25-
id: cached-poetry-dependencies
26-
uses: actions/cache@v3
19+
- name: Install uv
20+
uses: astral-sh/setup-uv@v6
2721
with:
28-
path: .venv
29-
key: venv-{{ "${{ hashFiles('**/poetry.lock') }}" }}
22+
version: "0.9.8"
23+
enable-cache: true
3024

3125
- name: Install dependencies
32-
if: steps.cached-poetry-dependencies.outputs.cache-hit != 'true'
33-
run: poetry install --no-interaction
26+
run: uv sync --locked --no-install-project
27+
28+
- name: Testing
29+
run: uv run --locked make test
3430

3531
- name: Linting
36-
run: poetry run make lint
32+
run: uv run --locked make lint
3733

3834
- name: Auditing
39-
run: poetry run make audit
35+
run: uv run make audit
4036

41-
- name: Test
42-
run: poetry run make test
37+
- name: Building
38+
run: docker compose build
4339

4440
- name: Smoke Test
45-
run: poetry run make smoke_test
41+
run: uv run make smoke_test
42+
43+
- name: Minimize uv cache
44+
run: uv cache prune --ci
Lines changed: 63 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,77 @@
1-
FROM python:{{cookiecutter.python_version}}-slim as builder
1+
# syntax=docker/dockerfile:1
2+
3+
FROM python:3.14-slim AS builder
4+
LABEL maintainer="André Felipe Dias <andref.dias@gmail.com>"
5+
6+
RUN DEBIAN_FRONTEND=noninteractive apt-get update && \
7+
apt-get install -y --no-install-recommends build-essential libffi-dev libxml2-dev \
8+
libxslt-dev curl libpq-dev && \
9+
apt-get clean && \
10+
rm -rf /var/lib/apt/lists/*
11+
12+
# ref: https://github.com/astral-sh/uv-docker-example/blob/main/multistage.Dockerfile
13+
14+
COPY --from=ghcr.io/astral-sh/uv:0.9.8 /uv /uvx /bin/
15+
16+
ENV UV_COMPILE_BYTECODE=1 UV_LINK_MODE=copy UV_PYTHON_DOWNLOADS=0
17+
18+
WORKDIR /app
19+
20+
COPY pyproject.toml uv.lock ./
21+
RUN uv sync --locked --no-install-project --no-dev
22+
23+
# ---------------------------------------------------------
24+
25+
FROM python:3.14-slim AS final
26+
27+
RUN DEBIAN_FRONTEND=noninteractive apt-get update && \
28+
apt-get install -y --no-install-recommends libpq-dev && \
29+
apt-get clean && \
30+
rm -rf /var/lib/apt/lists/*
31+
32+
WORKDIR /app
33+
COPY --from=builder --chown=nobody:nogroup /app/.venv ./.venv
34+
ENV PATH=/app/.venv/bin:${PATH}
35+
36+
COPY --chown=nobody:nogroup --exclude=pyproject.toml --exclude=uv.lock . ./
37+
38+
USER nobody
39+
40+
EXPOSE 5000
41+
42+
CMD ["./entrypoint.sh"]
43+
44+
# ---------------------------------------------------------
45+
46+
FROM python:{{cookiecutter.python_version}}-slim AS builder
247
LABEL maintainer="{{cookiecutter.author}} <{{cookiecutter.email}}>"
348

449
RUN DEBIAN_FRONTEND=noninteractive apt-get update && \
5-
apt-get install -y --no-install-recommends build-essential curl
50+
apt-get install -y --no-install-recommends build-essential libffi-dev libxml2-dev \
51+
libxslt-dev curl && \
52+
apt-get clean && \
53+
rm -rf /var/lib/apt/lists/*
654

7-
ENV PYTHONDONTWRITEBYTECODE=1
8-
ENV PYTHONUNBUFFERED=1
9-
RUN python -m venv /venv
55+
COPY --from=ghcr.io/astral-sh/uv:0.9.8 /uv /uvx /bin/
1056

11-
ENV POETRY_VERSION=1.8.2
12-
ENV POETRY_HOME=/opt/poetry
13-
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
14-
RUN curl -sSL https://install.python-poetry.org | python -
57+
ENV UV_COMPILE_BYTECODE=1 UV_LINK_MODE=copy UV_PYTHON_DOWNLOADS=0
1558

1659
WORKDIR /app
17-
COPY pyproject.toml poetry.lock ./
18-
RUN . /venv/bin/activate; \
19-
$POETRY_HOME/bin/poetry install --only main --no-interaction
2060

61+
COPY pyproject.toml uv.lock ./
62+
RUN uv sync --locked --no-install-project --no-dev
2163

22-
FROM python:{{cookiecutter.python_version}}-slim as final
64+
# ---------------------------------------------------------
2365

24-
COPY --from=builder /venv /venv
25-
ENV PATH=/venv/bin:${PATH}
66+
FROM python:{{cookiecutter.python_version}}-slim AS final
2667

2768
WORKDIR /app
69+
COPY --from=builder --chown=nobody:nogroup /app/.venv ./.venv
70+
ENV PATH=/app/.venv/bin:${PATH}
71+
72+
COPY --chown=nobody:nogroup --exclude=pyproject.toml --exclude=uv.lock . ./
73+
2874
USER nobody
29-
COPY --chown=nobody:nogroup {{cookiecutter.project_slug}}/ ./{{cookiecutter.project_slug}}
75+
EXPOSE 5000
3076

31-
CMD ["hypercorn", "--worker-class={{cookiecutter.worker_class}}", "--bind=0.0.0.0:5000", "--error-logfile=-", "{{cookiecutter.project_slug}}.main:app"]
77+
CMD ["./entrypoint.sh"]

{{cookiecutter.project_slug}}/Makefile

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,4 +46,7 @@ install_hooks:
4646

4747

4848
check_env:
49-
@ if [ ! -f ".env" ]; then echo '.env not found'; exit 1; fi
49+
@ if [ ! -f ".env" ]; then \
50+
cp sample.env .env; \
51+
echo '.env created from sample.env'; \
52+
fi

{{cookiecutter.project_slug}}/docker-compose.dev.yml

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,5 @@
11
services:
22
app:
3-
command:
4-
[
5-
"hypercorn",
6-
"--worker-class={{ cookiecutter.worker_class }}",
7-
"--bind=0.0.0.0:5000",
8-
"--error-logfile=-",
9-
"--reload",
10-
"--root-path=/api",
11-
"{{cookiecutter.project_slug}}.main:app"
12-
]
133
volumes:
144
- ./{{cookiecutter.project_slug}}/:/app/{{cookiecutter.project_slug}}/
155
environment:

{{cookiecutter.project_slug}}/docker-compose.yml

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -30,15 +30,6 @@ services:
3030
- .env
3131
environment:
3232
ENV: ${ENV:-production}
33-
command:
34-
[
35-
"hypercorn",
36-
"--worker-class={{cookiecutter.worker_class}}",
37-
"--bind=0.0.0.0:5000",
38-
"--error-logfile=-",
39-
"--root-path=/api",
40-
"{{cookiecutter.project_slug}}.main:app"
41-
]
4233

4334
volumes:
4435
caddy_data:

0 commit comments

Comments
 (0)