Skip to content

Commit 68ea09a

Browse files
authored
Merge pull request #194 from sandiegopython/davidfischer/use-uv-ruff
Switch to uv/ruff setup
2 parents 2e6a7c6 + 5611d4b commit 68ea09a

File tree

22 files changed

+952
-175
lines changed

22 files changed

+952
-175
lines changed

.github/workflows/ci.yml

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,26 @@ on:
1111
jobs:
1212
build:
1313

14-
name: Unit tests and code checks
14+
name: Unit tests and code style checks
1515
runs-on: ubuntu-latest
1616

1717
steps:
18-
- uses: actions/checkout@v3
19-
- uses: actions/setup-python@v4
18+
- uses: actions/checkout@v5
19+
20+
# https://docs.astral.sh/uv/guides/integration/github/
21+
- uses: astral-sh/setup-uv@v4
22+
with:
23+
enable-cache: true
24+
25+
- uses: actions/setup-python@v6
2026
with:
21-
python-version: "3.14"
27+
python-version-file: "pyproject.toml"
28+
29+
- name: Install dependencies
30+
run: |
31+
uv sync --frozen --extra dev
32+
uv tool install tox --with tox-uv
2233
2334
- name: Run CI
2435
run: |
25-
pip install "tox>=4.0,<5.0"
2636
tox

.pre-commit-config.yaml

Lines changed: 26 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,27 @@
11
repos:
2-
- repo: https://github.com/pre-commit/pre-commit-hooks
3-
rev: v6.0.0
4-
hooks:
5-
- id: check-yaml
6-
- id: end-of-file-fixer
7-
- id: trailing-whitespace
8-
- repo: https://github.com/psf/black-pre-commit-mirror
9-
rev: 25.9.0
10-
hooks:
11-
- id: black
12-
# Since the pre-commit runs on a file by file basis rather than a whole project,
13-
# The excludes in pyproject.toml are ignored
14-
exclude: migrations
15-
language_version: python3.14
2+
- repo: https://github.com/pre-commit/pre-commit-hooks
3+
rev: v6.0.0
4+
hooks:
5+
- id: check-yaml
6+
- id: end-of-file-fixer
7+
- id: trailing-whitespace
8+
- repo: https://github.com/adamchainz/django-upgrade
9+
rev: "1.29.1"
10+
hooks:
11+
- id: django-upgrade
12+
args: [--target-version, "5.2"]
13+
- repo: https://github.com/astral-sh/ruff-pre-commit
14+
# Ruff version.
15+
rev: v0.14.6
16+
hooks:
17+
# Run the linter.
18+
- id: ruff
19+
args: [ --fix ]
20+
# Run the formatter.
21+
- id: ruff-format
22+
- repo: https://github.com/astral-sh/uv-pre-commit
23+
# uv version.
24+
rev: 0.9.11
25+
hooks:
26+
# Compile requirements to ensure uv.lock is up-to-date
27+
- id: uv-lock

Dockerfile

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,13 @@ LABEL maintainer="https://github.com/sandiegopython"
99
ENV PYTHONDONTWRITEBYTECODE 1
1010
ENV PYTHONUNBUFFERED 1
1111

12+
# uv environment variables
13+
# Copy (don't hardlink) files into /.venv. Avoid issues with Docker's FS
14+
# https://docs.astral.sh/uv/reference/environment/
15+
ENV UV_LINK_MODE=copy
16+
ENV UV_PYTHON_DOWNLOADS=never
17+
ENV UV_PROJECT_ENVIRONMENT=/.venv
18+
1219
RUN apt-get update
1320
RUN apt-get install -y --no-install-recommends curl
1421

@@ -25,28 +32,40 @@ RUN apt-get install -y --no-install-recommends \
2532
postgresql-client libpq-dev \
2633
git
2734

28-
RUN mkdir -p /code
29-
35+
RUN mkdir -p /code /home/www/
3036
WORKDIR /code
3137

32-
# Requirements are installed here to ensure they will be cached.
33-
# https://docs.docker.com/build/cache/#use-the-dedicated-run-cache
34-
COPY ./requirements /requirements
35-
RUN pip install --upgrade pip
36-
RUN --mount=type=cache,target=/root/.cache/pip pip install -r /requirements/deployment.txt
37-
RUN --mount=type=cache,target=/root/.cache/pip pip install -r /requirements/local.txt
38+
# Install uv for fast package management
39+
# https://docs.astral.sh/uv/guides/integration/docker/#installing-uv
40+
COPY --from=ghcr.io/astral-sh/uv:0.9.11 /uv /uvx /bin/
41+
42+
# Copy project files for dependency resolution
43+
# uv.lock ensures reproducible builds
44+
COPY pyproject.toml uv.lock ./
45+
RUN --mount=type=cache,target=/root/.cache/uv \
46+
uv sync --frozen --no-install-project --all-extras
3847

3948
COPY . /code/
4049

4150
# Build JS/static assets
4251
RUN --mount=type=cache,target=/root/.npm npm install
4352
RUN npm run dist
4453

45-
RUN python manage.py collectstatic --noinput --clear
54+
RUN uv run python manage.py collectstatic --noinput --clear
55+
56+
# Launches the application (gunicorn) with this script
57+
COPY ./docker/start /start
58+
RUN chmod +x /start
59+
60+
# Launch a shell within the container with this script
61+
COPY ./docker/shell /shell
62+
RUN chmod +x /shell
4663

4764
# Run the container unprivileged
4865
RUN addgroup www && useradd -g www www
4966
RUN chown -R www:www /code
67+
# Needed for the uv cache
68+
RUN chown -R www:www /home/www
5069
USER www
5170

5271
# Output information about the build
@@ -56,4 +75,4 @@ RUN date -u +'%Y-%m-%dT%H:%M:%SZ' > BUILD_DATE
5675

5776
EXPOSE 8000
5877

59-
CMD ["gunicorn", "--timeout", "15", "--bind", ":8000", "--workers", "2", "--max-requests", "10000", "--max-requests-jitter", "100", "--log-file", "-", "--access-logfile", "-", "config.wsgi"]
78+
CMD ["/start"]

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ dockerserve:
3535
# or run anything else on the Django container. It does expect the
3636
# container to already be running
3737
dockershell:
38-
docker compose -f $(DOCKER_CONFIG) exec django /bin/bash
38+
docker compose -f $(DOCKER_CONFIG) run --rm django /shell
3939

4040
# Build and deploy the production container
4141
deploy:

README.md

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,19 +7,28 @@ This is the repository for the San Diego Python website at [sandiegopython.org](
77

88
### Prerequisites
99

10-
* Python v3.14
10+
* [uv](https://docs.astral.sh) (see [uv installation](https://docs.astral.sh/uv/getting-started/installation/))
1111
* Node v20
1212

13+
1314
### Getting started
1415

16+
Install Python 3.14 with uv:
17+
18+
```shell
19+
uv python install
20+
```
21+
22+
Install dependencies and dev setup
23+
1524
```shell
16-
pip install -r requirements/local.txt # Install local Python requirements
17-
npm install # Install JS dependencies for frontend CSS/JS
18-
npm run build # Build CSS (continuously with `npm run watch`)
19-
pre-commit install # Setup code standard pre-commit hook
20-
./manage.py migrate # Create a local development database
21-
./manage.py createsuperuser # Create a local development administrator user
22-
./manage.py runserver # Starts a local development server at http://localhost:8000
25+
uv sync --all-extras # Install local Python requirements
26+
npm install # Install JS dependencies for frontend CSS/JS
27+
npm run build # Build CSS (continuously with `npm run watch`)
28+
uv run pre-commit install # Setup code standard pre-commit hook
29+
uv run ./manage.py migrate # Create a local development database
30+
uv run ./manage.py createsuperuser # Create a local development administrator user
31+
uv run ./manage.py runserver # Starts a local development server at http://localhost:8000
2332
```
2433

2534

@@ -28,6 +37,7 @@ pre-commit install # Setup code standard pre-commit hook
2837
The entire test suite can be run with tox:
2938

3039
```shell
40+
uv tool install tox --with tox-uv # Install tox-uv (only needs to be run once ever)
3141
tox
3242
```
3343

config/settings/base.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
import dj_database_url
1515

16+
1617
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
1718
BASE_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "../..")
1819

config/settings/test.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,11 @@
2020
},
2121
}
2222

23+
# Disable logging during tests to keep the output clean
24+
for logger in LOGGING["loggers"].values():
25+
logger["handlers"] = ["null"]
26+
logger["level"] = "CRITICAL"
27+
2328

2429
# Ignore whitenoise message about no static directory
2530
warnings.filterwarnings("ignore", message="No directory at", module="whitenoise.base")

config/urls.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
from django.conf import settings
2+
from django.conf.urls.static import static
23
from django.contrib import admin
34
from django.urls import include
45
from django.urls import path
5-
from django.conf.urls.static import static
66

77

88
urlpatterns = [

docker/shell

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#!/bin/bash
2+
3+
set -o errexit
4+
set -o pipefail
5+
set -o nounset
6+
7+
8+
# Use the venv created by `uv sync` in the Dockerfile
9+
source /.venv/bin/activate
10+
11+
# Start shell with the venv activated
12+
/bin/bash

docker/start

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
#!/bin/bash
2+
3+
set -o errexit
4+
set -o pipefail
5+
set -o nounset
6+
7+
8+
# Launch gunicorn - the production web server
9+
# https://docs.gunicorn.org/en/stable/settings.html
10+
uv run gunicorn \
11+
--timeout 15 \
12+
--bind :8000 \
13+
--workers 2 \
14+
--max-requests 10000 \
15+
--max-requests-jitter 100 \
16+
--log-file - \
17+
--access-logfile - \
18+
config.wsgi

0 commit comments

Comments
 (0)