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
2 changes: 1 addition & 1 deletion .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ jobs:
git submodule update --remote
- run:
name: Install deps
command: make dev-install
command: make install
- run:
name: Linting
command: make format-check
Expand Down
61 changes: 46 additions & 15 deletions .github/workflows/database.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
name: Database
# These workflows are intended to check that various actions related to the database
# (such as building, exporting, and importing) work as expected.

on:
pull_request:
Expand All @@ -9,6 +11,8 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v5
- name: Install uv
uses: astral-sh/setup-uv@v7
- name: Lint
run: |
set -e
Expand All @@ -22,6 +26,8 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v5
- name: Install uv
uses: astral-sh/setup-uv@v7
- name: Generate OpenAPI schema
run: |
make install
Expand All @@ -33,6 +39,8 @@ jobs:
uses: actions/checkout@v5
with:
submodules: recursive
- name: Install uv
uses: astral-sh/setup-uv@v7
- name: Start pokeapi
run: |
make install
Expand All @@ -46,27 +54,50 @@ jobs:
run: curl -Ss http://localhost:8000/api/v2/pokemon/1/ | grep -q 'bulbasaur'
postgres:
runs-on: ubuntu-latest
services:
postgres:
image: postgres:16
env:
POSTGRES_USER: pokeapi
POSTGRES_PASSWORD: pokeapi
POSTGRES_DB: pokeapi
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
- 5432:5432
steps:
- name: Checkout
uses: actions/checkout@v5
with:
submodules: recursive
- name: Build
run: |
docker compose -f docker-compose.yml -f docker-compose-dev.yml up -d
make docker-migrate
make docker-build-db
- name: Install uv
uses: astral-sh/setup-uv@v7
- name: Install dependencies
run: make install
- name: Run migrations
run: uv run manage.py migrate --settings=config.local
- name: Build database
run: uv run manage.py shell --settings=config.local -c "from data.v2.build import build_all; build_all(); exit()"
- name: Dump DB
run: docker compose exec -T -u postgres db sh -c "cd /tmp && pg_dump -h localhost -Fc -U ash -N 'hdb_*' pokeapi > pokeapi.dump"
- name: Copy dump
run: docker compose cp db:/tmp/pokeapi.dump ./
- name: Down services
run: docker compose -f docker-compose.yml -f docker-compose-dev.yml down -v
- name: Start services
run: docker compose -f docker-compose.yml -f docker-compose-dev.yml up -d
run: pg_dump -h localhost -U pokeapi -Fc -N 'hdb_*' pokeapi > pokeapi.dump
env:
PGPASSWORD: pokeapi
- name: Drop and recreate database
run: |
psql -h localhost -U pokeapi -d postgres -c "DROP DATABASE pokeapi;"
psql -h localhost -U pokeapi -d postgres -c "CREATE DATABASE pokeapi;"
env:
PGPASSWORD: pokeapi
- name: Import database
run: pg_restore -h localhost -U pokeapi -d pokeapi pokeapi.dump
env:
PGPASSWORD: pokeapi
- name: Start server
run: |
docker compose cp ./pokeapi.dump db:/tmp/
docker compose exec -T -u postgres db sh -c "cd /tmp && pg_restore -h localhost -U ash -d pokeapi pokeapi.dump"
nohup uv run manage.py runserver 0.0.0.0:8000 --settings=config.local &
sleep 5
- name: Test data
run: curl -Ss http://localhost/api/v2/pokemon/1/ | grep -q 'bulbasaur'
run: curl -Ss http://localhost:8000/api/v2/pokemon/1/ | grep -q 'bulbasaur'
2 changes: 2 additions & 0 deletions .github/workflows/docker-build-and-push.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ jobs:
uses: actions/checkout@v5
with:
submodules: recursive
- name: Install uv
uses: astral-sh/setup-uv@v7
- name: Docker meta
id: meta
uses: docker/metadata-action@v5
Expand Down
6 changes: 5 additions & 1 deletion .github/workflows/docker-k8s.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
name: Container
# These workflows are intended to check that the Docker and Kubernetes
# configurations work as expected in a local development environment.

on:
pull_request:
Expand All @@ -11,6 +13,8 @@ jobs:
uses: actions/checkout@v5
with:
submodules: recursive
- name: Install uv
uses: astral-sh/setup-uv@v7
- name: Docker meta
id: meta
uses: docker/metadata-action@v5
Expand Down Expand Up @@ -112,4 +116,4 @@ jobs:
- name: Assert containers running
run: |
last_command=$(docker ps | grep 'pokeapi-' | wc -l)
test "$last_command" -eq 5
test "$last_command" -eq 5
2 changes: 2 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ jobs:
uses: actions/checkout@v5
with:
submodules: recursive
- name: Install uv
uses: astral-sh/setup-uv@v7
- name: Start pokeapi (docker)
run: |
docker compose -f docker-compose.yml -f docker-compose-dev.yml up -d
Expand Down
1 change: 1 addition & 0 deletions .python-version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
3.14
43 changes: 18 additions & 25 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -4,66 +4,59 @@ docker_config = --settings=config.docker-compose
gql_compose_config = -f docker-compose.yml -f Resources/compose/docker-compose-prod-graphql.yml
gqlv1beta_compose_config = -f docker-compose.yml -f Resources/compose/docker-compose-prod-graphql.yml -f Resources/compose/docker-compose-prod-graphql-v1beta.yml

# Auto-detect Python and pip commands
PYTHON := $(shell which python3 2>/dev/null || which python 2>/dev/null || echo python3)
PIP := $(shell which pip3 2>/dev/null || which pip 2>/dev/null || echo pip3)

.PHONY: help
.SILENT:

help:
@grep -E '^[a-zA-Z_-]+:.*?# .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?# "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'

install: # Install base requirements to run project
$(PIP) install -r requirements.txt

dev-install: # Install developer requirements + base requirements
$(PIP) install -r test-requirements.txt
uv sync
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd like a target that checks if uv is installed and having it running before the other targets that requires it.

check-uv:
	command -v uv &>/dev/null || { echo
 '`uv` not detected'; }

setup: check-uv  # Set up the project database
	uv run manage.py migrate ${local_config}

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a good idea!


setup: # Set up the project database
$(PYTHON) manage.py migrate ${local_config}
uv run manage.py migrate ${local_config}

build-db: # Build database
echo "from data.v2.build import build_all; build_all()" | $(PYTHON) manage.py shell ${local_config}
echo "from data.v2.build import build_all; build_all()" | uv run manage.py shell ${local_config}

wipe-sqlite-db: # Delete's the project database
rm -rf db.sqlite3

serve: # Run the project locally
$(PYTHON) manage.py runserver ${local_config}
uv run manage.py runserver ${local_config}

test: # Run tests
$(PYTHON) manage.py test ${local_config}
uv run manage.py test ${local_config}

clean: # Remove any pyc files
find . -type f -name '*.pyc' -delete

migrate: # Run any outstanding migrations
$(PYTHON) manage.py migrate ${local_config}
uv run manage.py migrate ${local_config}

make-migrations: # Create migrations files if schema has changed
$(PYTHON) manage.py makemigrations ${local_config}
uv run manage.py makemigrations ${local_config}

shell: # Load a shell
$(PYTHON) manage.py shell ${local_config}
uv run manage.py shell ${local_config}

openapi-generate:
$(PYTHON) manage.py spectacular --color --file openapi.yml ${local_config}
uv run manage.py spectacular --color --file openapi.yml ${local_config}

docker-up: # (Docker) Create services/volumes/networks
docker compose up -d

docker-migrate: # (Docker) Run any pending migrations
docker compose exec -T app python manage.py migrate ${docker_config}
docker compose --verbose exec -T app uv run manage.py migrate ${docker_config}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can remove the --verbose


docker-build-db: # (Docker) Build the database
docker compose exec -T app sh -c 'echo "from data.v2.build import build_all; build_all()" | python manage.py shell ${docker_config}'
docker compose exec -T app sh -c 'echo "from data.v2.build import build_all; build_all()" | uv run manage.py shell ${docker_config}'

docker-make-migrations: # (Docker) Create migrations files if schema has changed
docker compose exec -T app sh -c 'python manage.py makemigrations ${docker_config}'
docker compose exec -T app sh -c 'uv run manage.py makemigrations ${docker_config}'

docker-flush-db: # (Docker) Removes all the data present in the database but preserves tables and migrations
docker compose exec -T app sh -c 'python manage.py flush --no-input ${docker_config}'
docker compose exec -T app sh -c 'uv run manage.py flush --no-input ${docker_config}'

docker-destroy-db: # (Docker) Removes the volume where the database is installed on, alongside to the container itself
docker rm -f pokeapi_db_1
Expand All @@ -79,18 +72,18 @@ docker-down: # (Docker) Stop and removes containers and networks
docker compose down

docker-test: # (Docker) Run tests
docker compose exec -T app python manage.py test ${local_config}
docker compose exec -T app uv run manage.py test ${local_config}

docker-prod:
docker compose -f docker-compose.yml -f docker-compose.override.yml -f Resources/compose/docker-compose-prod-graphql.yml up -d

docker-setup: docker-up docker-migrate docker-build-db # (Docker) Start services, prepare the latest DB schema, populate the DB

format: # Format the source code
black . --extend-exclude '.+/scripts/.+'
uv run ruff check . --fix --extend-exclude '.+/scripts/.+'

format-check: # Check the source code has been formatted
black . --check --extend-exclude '.+/scripts/.+'
uv run ruff check . --extend-exclude '.+/scripts/.+'

pull:
git checkout master
Expand Down Expand Up @@ -137,10 +130,10 @@ kustomize-ga-apply: # (Kustomize) Run kubectl apply -k on the connected k8s clu
kubectl apply -k Resources/k8s/kustomize/ga/

k8s-migrate: # (k8s) Run any pending migrations
kubectl exec --namespace pokeapi deployment/pokeapi -- python manage.py migrate ${docker_config}
kubectl exec --namespace pokeapi deployment/pokeapi -- uv run manage.py migrate ${docker_config}

k8s-build-db: # (k8s) Build the database
kubectl exec --namespace pokeapi deployment/pokeapi -- sh -c 'echo "from data.v2.build import build_all; build_all()" | python manage.py shell ${docker_config}'
kubectl exec --namespace pokeapi deployment/pokeapi -- sh -c 'echo "from data.v2.build import build_all; build_all()" | uv run manage.py shell ${docker_config}'

k8s-delete: # (k8s) Delete pokeapi namespace
kubectl delete namespace pokeapi
Expand Down
12 changes: 7 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,9 @@ A RESTful API for Pokémon - [pokeapi.co](https://pokeapi.co)

- Download this source code into a working directory, be sure to use the flag `--recurse-submodules` to clone also our submodules.

- Install the requirements using pip:
- Install [uv](https://docs.astral.sh/uv/getting-started/installation/) for Python environment management.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't paricularily like adding new toold to projects. I don't see the benefits of using uv only the downside of forcing everyone to install it on their machines. It's true that it's a project with many stars and maybe a good community but for pokeapi I don't see any added benefits. If you @phalt or other contributors strongly advocate for using it, I'm ok with it, but if I could decide I wouldn't add it.

My argument is: Python officially already ships with uv-like features. It has venv, can install a pyprojet.toml, etc. uv does it faster from my understanding.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One very good added benefit of uv is having a lock file.

Copy link
Member

@sargunv sargunv Feb 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

re: "forcing everyone to install it on their machines"

I'd recommend actually configuring the core tools via mise. That way, any contributor only needs mise, and any other tools (uv, python, docker, anything) is managed + locked by mise in local dev, in CI, in devcontainers, AI agent workspaces, everywhere in a reproducible way.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Uv is becoming an industry standard, I use it at my company (3,000+ python engineers, one of the biggest Django projects in the world, we even have a few Python and Django core devs here).

Why uv?

  • It manages python versions you don't need to install and manage multiple versions yourself.
  • It wraps virtualenv and eliminates the need to understand it, reduces barrier to entry.
  • It has lock files to enforce specific versions - previously pip didn't fully support this.
  • It helps you run tools easily (in a future PR I am going to add uv run pokeapi build_data command to eliminate all the manual import data; data.build stuff we have).
  • It is fast.

For years PokeAPI has been a good example of a great API project, and it has lagged behind in being a great example of a Python project, and it could be one with a bit of TLC - which is what my aim is this year.

I think providing up to date tools is a win win for us (easier to maintain) and also people browsing our code (they see what is standard).


- Install the requirements using the Makefile commands:

```sh
make install
Expand Down Expand Up @@ -97,8 +99,8 @@ If you don't have `make` on your machine you can use the following commands

```sh
docker compose up -d
docker compose exec -T app python manage.py migrate --settings=config.docker-compose
docker compose exec -T app sh -c 'echo "from data.v2.build import build_all; build_all()" | python manage.py shell --settings=config.docker-compose'
docker compose exec -T app uv run manage.py migrate --settings=config.docker-compose
docker compose exec -T app sh -c 'echo "from data.v2.build import build_all; build_all()" | uv run manage.py shell --settings=config.docker-compose'
```

Browse [localhost/api/v2/](http://localhost/api/v2/) or [localhost/api/v2/pokemon/bulbasaur/](http://localhost/api/v2/pokemon/bulbasaur/) on port `80`.
Expand Down Expand Up @@ -149,8 +151,8 @@ Configure `kubectl` to point to a cluster and then run the following commands to
kubectl apply -k Resources/k8s/kustomize/base/
kubectl config set-context --current --namespace pokeapi # (Optional) Set pokeapi ns as the working ns
# Wait for the cluster to spin up
kubectl exec --namespace pokeapi deployment/pokeapi -- python manage.py migrate --settings=config.docker-compose # Migrate the DB
kubectl exec --namespace pokeapi deployment/pokeapi -- sh -c 'echo "from data.v2.build import build_all; build_all()" | python manage.py shell --settings=config.docker-compose' # Build the db
kubectl exec --namespace pokeapi deployment/pokeapi -- uv run manage.py migrate --settings=config.docker-compose # Migrate the DB
kubectl exec --namespace pokeapi deployment/pokeapi -- sh -c 'echo "from data.v2.build import build_all; build_all()" | uv run manage.py shell --settings=config.docker-compose' # Build the db
kubectl wait --namespace pokeapi --timeout=120s --for=condition=complete job/load-graphql # Wait for Graphql configuration job to finish
```

Expand Down
28 changes: 23 additions & 5 deletions Resources/docker/app/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,21 @@ ENV PYTHONUNBUFFERED=1
RUN mkdir /code
WORKDIR /code

ADD requirements.txt /code/
# Install UV
RUN apk add --no-cache curl
RUN curl -LsSf https://astral.sh/uv/install.sh | sh

# Add UV to PATH for this stage
ENV PATH="/root/.local/bin:$PATH"

# Copy dependency files
ADD pyproject.toml uv.lock* /code/

# Install dependencies
RUN apk add --no-cache postgresql-libs libstdc++
RUN apk add --no-cache --virtual .build-deps gcc g++ musl-dev \
postgresql-dev binutils rust cargo && \
python3 -m pip install -r requirements.txt --no-cache-dir
uv sync --frozen

FROM python:3.13.7-alpine

Expand All @@ -19,15 +29,23 @@ ENV DJANGO_SETTINGS_MODULE='config.docker-compose'
RUN mkdir /code
WORKDIR /code

COPY --from=builder /usr/local/lib/python3.13/site-packages /usr/local/lib/python3.13/site-packages
COPY --from=builder /usr/local/bin /usr/local/bin
# Copy UV and virtual environment from builder
COPY --from=builder /root/.local /root/.local
COPY --from=builder /code/.venv /code/.venv

# Add UV to PATH
ENV PATH="/root/.local/bin:$PATH"

# Install runtime dependencies
RUN apk add --no-cache postgresql-libs libstdc++
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are we sure we need this two packages? We didn't need them before. Only for building the app at line 9

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will try and drop them and see if it works


# Copy application code
ADD . /code/

RUN addgroup -g 1000 -S pokeapi && \
adduser -u 1000 -S pokeapi -G pokeapi
USER pokeapi

CMD ["gunicorn", "config.wsgi:application", "-c", "gunicorn.conf.py"]
CMD ["uv", "run", "gunicorn", "config.wsgi:application", "-c", "gunicorn.conf.py"]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is uv also necessary for running the app? Why can't I just use gunicorn?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This way it'll use the venv to run, without any activate step


EXPOSE 80
4 changes: 2 additions & 2 deletions Resources/docker/app/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,6 @@ Pokémon data isn't automatically present in this image. All Pokémon data is pe
When the container is up and running, run the following shell commands:

```sh
docker exec pokeapi python manage.py migrate --settings=config.docker-compose
docker exec pokeapi sh -c 'echo "from data.v2.build import build_all; build_all()" | python manage.py shell --settings=config.docker-compose'
docker exec pokeapi uv run manage.py migrate --settings=config.docker-compose
docker exec pokeapi sh -c 'echo "from data.v2.build import build_all; build_all()" | uv run manage.py shell --settings=config.docker-compose'
```
1 change: 1 addition & 0 deletions Resources/scripts/data/gen8/ability_names.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import csv
import os

from read_swsh import TextFile

# data_path contains the countents of the `message` folder found in sword/shield's romfs:/bin/
Expand Down
1 change: 1 addition & 0 deletions Resources/scripts/data/gen8/move_names.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import csv
import os

from read_swsh import TextFile

# data_path contains the countents of the `message` folder found in sword/shield's romfs:/bin/
Expand Down
Loading