Skip to content
Open
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
17 changes: 8 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,14 @@
<p>Templator in action</p>
</div>

You can install it directly from pypi with pip.
```bash
python3 -m pip install fastapi_template
python3 -m fastapi_template
# or fastapi_template
# Answer all the questions
# 🍪 Enjoy your new project 🍪
You can install and run it directly from pypi with uvx.
```shell
uvx fastapi_template
```
### 🍪 Enjoy your new project 🍪
```shell
cd new_project
docker-compose up --build
docker compose up --build
```

If you want to install it from sources, try this:
Expand Down Expand Up @@ -70,7 +69,7 @@ Generator features:
This project can handle arguments passed through command line.

```shell
$ python -m fastapi_template --help
$ uvx fastapi_template --help

Usage: fastapi_template [OPTIONS]

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,38 +6,53 @@ jobs:
lint:
strategy:
matrix:
cmd:
- black
- ruff
- mypy
include:
- name: Ruff format
cmd: ruff format --diff
- name: Ruff check
cmd: ruff check {{cookiecutter.project_name}} tests
- name: Mypy
cmd: mypy {{cookiecutter.project_name}} tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install poetry
run: pipx install poetry
- name: Set up Python
uses: actions/setup-python@v5
- uses: actions/checkout@v6

- name: Install the latest version of uv
uses: astral-sh/setup-uv@v7
with:
python-version: '3.11'
cache: 'poetry'
version: '0.9.17'
python-version: '3.13'
enable-cache: "auto"

- name: Install deps
run: poetry install
run: uv sync --locked --all-groups

- name: Run lint check
run: poetry run pre-commit run -a {{ '${{' }} matrix.cmd {{ '}}' }}
run: uv run {{ '${{' }} matrix.cmd {{ '}}' }}

pytest:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6

- name: Create .env
run: touch .env
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'

- name: Update docker-compose
uses: KengoTODA/actions-setup-docker-compose@v1
with:
version: "2.28.0"
- name: run tests
run: docker-compose run --rm api pytest -vv
uses: docker/setup-compose-action@v1

- name: Build api image (dev target)
run: |
docker compose \
-f docker-compose.yml \
-f deploy/docker-compose.dev.yml \
--project-directory . \
build api

- name: Run pytest
run: |
docker compose \
-f docker-compose.yml \
-f deploy/docker-compose.dev.yml \
--project-directory . \
run --rm api pytest -vv .
Original file line number Diff line number Diff line change
@@ -1,33 +1,56 @@
stages:
- "test"

variables:
UV_VERSION: "0.9.17"
PYTHON_VERSION: "3.13"
BASE_LAYER: bookworm-slim
UV_LINK_MODE: copy

.test-template:
stage: test
image: ghcr.io/astral-sh/uv:0.9.12-python3.13-bookworm-slim
image: ghcr.io/astral-sh/uv:$UV_VERSION-python$PYTHON_VERSION-$BASE_LAYER
tags:
- kubernetes-runner
- docker-runner
except:
- tags
variables:
UV_CACHE_DIR: .uv-cache
cache:
- key:
files:
- uv.lock
paths:
- $UV_CACHE_DIR
before_script:
- apt update && apt install -y git
- uv sync
- cd $CI_PROJECT_DIR
- uv sync --locked --all-extras --dev
after_script:
- uv cache prune --ci

black:
formatter:
extends:
- .test-template
script:
- pre-commit run ruff-format -a
- uv run ruff format --diff

ruff:
extends:
- .test-template
script:
- pre-commit run ruff -a
- uv run ruff check {{cookiecutter.project_name}} tests --output-format=gitlab --output-file=code-quality-report.json
artifacts:
reports:
codequality: $CI_PROJECT_DIR/code-quality-report.json

mypy:
extends:
- .test-template
script:
- pre-commit run mypy -a

- uv run mypy {{cookiecutter.project_name}} tests --output=json > mypy-out.json || true
- uv run mypy-gitlab-code-quality < mypy-out.json > codequality.json
artifacts:
when: always
reports:
codequality: $CI_PROJECT_DIR/codequality.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@
# See https://pre-commit.com for more information
# See https://pre-commit.com/hooks.html for more hooks
repos:
- repo: https://github.com/astral-sh/uv-pre-commit
rev: 0.9.17
hooks:
- id: uv-lock

- repo: local
hooks:

Expand Down
67 changes: 43 additions & 24 deletions fastapi_template/template/{{cookiecutter.project_name}}/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
FROM ghcr.io/astral-sh/uv:0.9.12-bookworm AS uv

# -----------------------------------
# STAGE 1: prod stage
# STAGE BUILDER: Prepare builder image
# Only install main dependencies
# -----------------------------------
FROM python:3.13-slim-bookworm AS prod
FROM ghcr.io/astral-sh/uv:0.9.17-python3.13-bookworm-slim AS builder

{%- if cookiecutter.db_info.name == "mysql" %}
RUN apt-get update && apt-get install -y \
Expand All @@ -21,37 +19,58 @@ RUN apt-get update && apt-get install -y \
&& rm -rf /var/lib/apt/lists/*
{%- endif %}

ENV UV_COMPILE_BYTECODE=1
ENV UV_LINK_MODE=copy
ENV UV_PROJECT_ENVIRONMENT=/usr/local
ENV UV_PYTHON_DOWNLOADS=never
ENV UV_NO_MANAGED_PYTHON=1
{%- if cookiecutter.orm == 'piccolo' %}
ENV PICCOLO_CONF="{{cookiecutter.project_name}}.piccolo_conf"
{%- endif %}
ENV UV_COMPILE_BYTECODE=1 \
UV_LINK_MODE=copy \
UV_PYTHON_DOWNLOADS=0 \
UV_PROJECT_ENVIRONMENT=/opt/.venv \
VIRTUAL_ENV="/opt/.venv" \
PATH="/opt/.venv/bin:$PATH" \
{%- if cookiecutter.orm == 'piccolo' %}
PICCOLO_CONF="{{cookiecutter.project_name}}.piccolo_conf" \
{%- endif %}
UV_NO_DEV=1

WORKDIR /app/src

RUN --mount=from=uv,source=/usr/local/bin/uv,target=/bin/uv \
--mount=type=cache,target=/root/.cache/uv \
RUN --mount=type=cache,target=/root/.cache/uv \
--mount=type=bind,source=uv.lock,target=uv.lock \
--mount=type=bind,source=pyproject.toml,target=pyproject.toml \
uv sync --locked --no-install-project --no-dev
uv sync --locked --no-install-project

COPY . .

RUN --mount=from=uv,source=/usr/local/bin/uv,target=/bin/uv \
--mount=type=cache,target=/root/.cache/uv \
uv sync --locked --no-dev
RUN --mount=type=cache,target=/root/.cache/uv \
uv sync --locked

# -----------------------------------
# STAGE PROD: Production image
# Copy dependencies and environment from builder image
# -----------------------------------
FROM python:3.13-slim-bookworm AS prod

CMD ["/usr/local/bin/python", "-m", "{{cookiecutter.project_name}}"]
RUN groupadd --system --gid 999 nonroot \
&& useradd --system --gid 999 --uid 999 --create-home nonroot

ENV VIRTUAL_ENV="/opt/.venv" \
{%- if cookiecutter.orm == 'piccolo' %}
PICCOLO_CONF="{{cookiecutter.project_name}}.piccolo_conf" \
{%- endif %}
PATH="/opt/.venv/bin:$PATH"

COPY --from=builder --chown=nonroot:nonroot /app/src /app/src
COPY --from=builder --chown=nonroot:nonroot /opt/.venv /opt/.venv

USER nonroot

WORKDIR /app/src

# -----------------------------------
# STAGE 3: development build
# Includes dev dependencies
# STAGE DEVELOPMENT: Development image
# Copy dependencies and environment from builder image and add dev dependencies
# -----------------------------------
FROM prod AS dev
FROM builder AS dev

ENV UV_NO_DEV=0

RUN --mount=from=uv,source=/usr/local/bin/uv,target=/bin/uv \
--mount=type=cache,target=/root/.cache/uv \
RUN --mount=type=cache,target=/root/.cache/uv \
uv sync --locked --all-groups
16 changes: 5 additions & 11 deletions fastapi_template/template/{{cookiecutter.project_name}}/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,24 +25,18 @@ You can read more about uv here: https://docs.astral.sh/ruff/
You can start the project with docker using this command:

```bash
docker-compose up --build
docker compose up --build
```

If you want to develop in docker with autoreload and exposed ports add `-f deploy/docker-compose.dev.yml` to your docker command.
Like this:

```bash
docker-compose -f docker-compose.yml -f deploy/docker-compose.dev.yml --project-directory . up --build
docker compose -f docker-compose.yml -f deploy/docker-compose.dev.yml --project-directory . up --build --watch
```

This command exposes the web application on port 8000, mounts current directory and enables autoreload.

But you have to rebuild image every time you modify `uv.lock` or `pyproject.toml` with this command:

```bash
docker-compose build
```

## Project structure

```bash
Expand Down Expand Up @@ -98,7 +92,7 @@ you can add `-f ./deploy/docker-compose.otlp.yml` to your docker command.
Like this:

```bash
docker-compose -f docker-compose.yml -f deploy/docker-compose.otlp.yml --project-directory . up
docker compose -f docker-compose.yml -f deploy/docker-compose.otlp.yml --project-directory . up
```

This command will start OpenTelemetry collector and jaeger.
Expand Down Expand Up @@ -189,8 +183,8 @@ aerich migrate
If you want to run it in docker, simply run:

```bash
docker-compose run --build --rm api pytest -vv .
docker-compose down
docker compose -f docker-compose.yml -f deploy/docker-compose.dev.yml run --build --rm api pytest -vv .
docker compose down
```

For running tests on your local machine.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,26 @@ services:
ports:
# Exposes application port.
- "8000:8000"
build:
build: &build
context: .
dockerfile: ./Dockerfile
target: dev
volumes:
# Adds current directory as volume.
- .:/app/src/
environment:
# Enables autoreload.
{{cookiecutter.project_name | upper}}_RELOAD: "True"
develop:
watch:
- action: rebuild
path: uv.lock

{%- if cookiecutter.enable_taskiq == "True" %}

taskiq-worker:
build:
<<: *build
volumes:
# Adds current directory as volume.
- .:/app/src/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ services:
build:
context: .
dockerfile: ./Dockerfile
target: prod
image: {{cookiecutter.project_name}}:{{"${" }}{{cookiecutter.project_name | upper }}_VERSION:-latest{{"}"}}
restart: always
env_file:
Expand Down Expand Up @@ -78,6 +79,10 @@ services:
volumes:
- {{cookiecutter.project_name}}-db-data:/db_data/
{%- endif %}
command:
- python
- -m
- {{cookiecutter.project_name}}

{%- if cookiecutter.enable_taskiq == "True" %}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ dependencies = [
"aiokafka >=0.12.0,<1",
{%- endif %}
{%- if cookiecutter.enable_taskiq == "True" %}
"taskiq >=0.12.0,<1",
"taskiq[reload] >=0.12.0,<1",
"taskiq-fastapi >=0.3.6,<1",
{%- if cookiecutter.enable_redis == "True" %}
"taskiq-redis >=1.1.2,<2",
Expand Down Expand Up @@ -166,9 +166,7 @@ dev = [
"nest-asyncio >=1.6.0,<2",
{%- endif %}
"httpx >=0.28.1,<1",
{%- if cookiecutter.enable_taskiq == "True" %}
"taskiq[reload] >=0.12.0,<1",
{%- endif %}
"mypy-gitlab-code-quality>=1.3.0,<2",
]


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ def main() -> None:
host=settings.host,
port=settings.port,
reload=settings.reload,
reload_excludes=[".venv/*"],
log_level=settings.log_level.value.lower(),
factory=True,
)
Expand Down
Loading
Loading