Skip to content

Commit 3a0b340

Browse files
committed
Replace hypha docker setup with standard(ish) torchbox
1 parent 8967ebe commit 3a0b340

14 files changed

+585
-208
lines changed

.dockerignore

+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/hypha/static_compiled
2+
/hypha/settings/local.py
3+
4+
/Dockerfile
5+
/docker/Dockerfile*
6+
7+
.git
8+
**/__pycache__
9+
*.pyc
10+
.DS_Store
11+
*.swp
12+
.venv
13+
/venv/
14+
/static/
15+
/media/
16+
/tmp/
17+
/.vagrant/
18+
/Vagrantfile.local
19+
node_modules/
20+
coverage
21+
/npm-debug.log
22+
/.idea/
23+
/.devcontainer/
24+
/docker-compose.yml
25+
26+
# Distribution / packaging
27+
.Python
28+
env/
29+
build/
30+
develop-eggs/
31+
dist/
32+
downloads/
33+
eggs/
34+
.eggs/
35+
lib/
36+
lib64/
37+
parts/
38+
sdist/
39+
var/
40+
wheels/
41+
*.egg-info/
42+
.installed.cfg
43+
*.egg
44+
45+
/database_dumps

Dockerfile

+264
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,264 @@
1+
# syntax=docker/dockerfile:1.10
2+
# check=error=true
3+
4+
# NB: The above comments are special directives to Docker that enable us to use
5+
# more up-to-date Dockerfile syntax and will cause the build to fail if any
6+
# Docker build checks fail:
7+
# https://docs.docker.com/reference/build-checks/
8+
#
9+
# We've set it so that failing checks will cause `docker build .` to fail, but
10+
# when that happens the error message isn't very helpful. To get more
11+
# information, run `docker build --check .` instead.
12+
13+
# Build stage hierarchy:
14+
#
15+
# ┌────────┐ ┌──────────────┐
16+
# │ base │ │ frontend-* │
17+
# └────────┘ └──────────────┘
18+
# / \ /
19+
# ┌───────┐ ┌───────┐
20+
# │ dev │ │ web │
21+
# └───────┘ └───────┘
22+
# \
23+
# ┌──────┐
24+
# │ ci │
25+
# └──────┘
26+
27+
##############
28+
# base stage #
29+
##############
30+
31+
# This stage is the base for the web and dev stages. It contains the version of
32+
# Python we want to use and any OS-level dependencies that we need in all
33+
# environments. It also sets up the always-activated virtual environment and
34+
# installs uv.
35+
36+
FROM python:3.12-slim-bookworm AS base
37+
38+
WORKDIR /app
39+
40+
# Install common OS-level dependencies
41+
RUN --mount=type=cache,target=/var/lib/apt/lists,sharing=locked \
42+
--mount=type=cache,target=/var/cache/apt,sharing=locked \
43+
<<EOF
44+
apt-get --quiet --yes update
45+
apt-get --quiet --yes install --no-install-recommends \
46+
build-essential \
47+
curl \
48+
libpq-dev \
49+
git
50+
apt-get --quiet --yes autoremove
51+
EOF
52+
53+
# Create an unprivileged user and virtual environment for the app
54+
ARG UID=1000
55+
ARG GID=1000
56+
ARG USERNAME=hypha
57+
RUN <<EOF
58+
# Create the unprivileged user and group. If you have issues with file
59+
# ownership, you may need to adjust the UID and GID build args to match your
60+
# local user.
61+
groupadd --gid $GID $USERNAME
62+
useradd --gid $GID --uid $UID --create-home $USERNAME
63+
mkdir /app/.venv
64+
chown -R $UID:$GID /app
65+
EOF
66+
67+
# Install uv.
68+
COPY --from=ghcr.io/astral-sh/uv:0.6.8 /uv /uvx /usr/local/bin
69+
70+
# Enable bytecode compilation
71+
ENV UV_COMPILE_BYTECODE=1
72+
73+
# Copy from the cache instead of linking since it's a mounted volume
74+
ENV UV_LINK_MODE=copy
75+
76+
# Set common environment variables
77+
ENV \
78+
# Don't buffer Python output so that we don't lose logs in the event of a crash
79+
PYTHONUNBUFFERED=1 \
80+
# Make sure the virtual environment's bin directory and uv are on the PATH
81+
PATH=/app/.venv/bin:/bin:$PATH
82+
83+
# Install .bashrc for dj shortcuts
84+
COPY --chown=$UID:$GID ./docker/bashrc.sh ./docker/bashrc.sh
85+
RUN ln -sTf /app/docker/bashrc.sh /home/$USERNAME/.bashrc
86+
87+
# Switch to the unprivileged user
88+
USER $USERNAME
89+
90+
# Install the project's dependencies using the lockfile and settings
91+
RUN --mount=type=cache,target=/home/$USERNAME/.cache/uv,uid=$UID,gid=$GID \
92+
--mount=type=bind,source=uv.lock,target=uv.lock \
93+
--mount=type=bind,source=pyproject.toml,target=pyproject.toml \
94+
uv sync --frozen --no-install-project --no-dev
95+
96+
# Then, add the rest of the project source code and install it
97+
# Installing separately from its dependencies allows optimal layer caching
98+
ADD . /app
99+
RUN --mount=type=cache,target=/home/$USERNAME/.cache/uv,uid=$UID,gid=$GID \
100+
uv sync --frozen --no-dev
101+
102+
103+
###################
104+
# frontend stages #
105+
###################
106+
107+
FROM node:20-slim AS frontend-deps
108+
109+
# This stage is used to install the front-end build dependencies. It's separate
110+
# from the frontend-build stage so that we can initialise the node_modules
111+
# volume in the dev container from here without needing to run the production
112+
# build.
113+
114+
WORKDIR /build/
115+
116+
# Make any build & post-install scripts that respect this variable behave as if
117+
# we were in a CI environment (e.g. for logging verbosity purposes)
118+
ENV CI=true
119+
120+
# Install front-end dependencies
121+
COPY package.json package-lock.json ./
122+
RUN --mount=type=cache,target=/root/.npm \
123+
npm ci --no-audit --progress=false
124+
125+
126+
FROM frontend-deps AS frontend-build
127+
128+
# This stage is used to compile the front-end assets. The web stage copies the
129+
# compiled assets bundles from here, so it doesn't need to have the front-end
130+
# build dependencies installed.
131+
132+
# Compile static files
133+
COPY .stylelintrc.yaml ./
134+
COPY ./hypha/ ./hypha/
135+
RUN npm run build
136+
137+
138+
#############
139+
# web stage #
140+
#############
141+
142+
# This is the stage that actually gets run in staging and production.
143+
# It extends the base stage by installing production Python dependencies and
144+
# copying in the compiled front-end assets. It runs the WSGI server, gunicorn,
145+
# in its CMD.
146+
147+
FROM base AS web
148+
149+
# Set production environment variables
150+
ENV \
151+
# Django settings module
152+
DJANGO_SETTINGS_MODULE=hypha.settings.production \
153+
# Default port and number of workers for gunicorn to spawn
154+
PORT=8000 \
155+
WEB_CONCURRENCY=2
156+
157+
# Copy in built static files and the application code. Run collectstatic so
158+
# whitenoise can serve static files for us.
159+
COPY . .
160+
ARG UID
161+
ARG GID
162+
COPY --chown=$UID:$GID --from=frontend-build --link /build/hypha/static_compiled ./hypha/static_compiled
163+
RUN <<EOF
164+
python -m django collectstatic --noinput --clear
165+
EOF
166+
167+
# Run Gunicorn using the config in gunicorn.conf.py (the default location for
168+
# the config file). To change gunicorn settings without needing to make code
169+
# changes and rebuild this image, set the GUNICORN_CMD_ARGS environment variable.
170+
CMD ["gunicorn"]
171+
172+
173+
#############
174+
# dev stage #
175+
#############
176+
177+
# This stage is used in the development environment, either via `fab sh` etc. or
178+
# as the dev container in VS Code or PyCharm. It extends the base stage by
179+
# adding additional OS-level dependencies to allow things like using git and
180+
# psql. It also adds sudo and gives the unprivileged user passwordless sudo
181+
# access to make things like experimenting with different OS dependencies easier
182+
# without needing to rebuild the image or connect to the container as root.
183+
#
184+
# This stage does not include the application code at build time! Including the
185+
# code would result in this image needing to be rebuilt every time the code
186+
# changes at all which is unnecessary because we always bind mount the code at
187+
# /app/ anyway.
188+
189+
FROM base AS dev
190+
191+
# Switch to the root user and Install extra OS-level dependencies for
192+
# development, including Node.js and the correct version of the Postgres client
193+
# library (Debian's bundled version is normally too old)
194+
USER root
195+
ARG POSTGRES_VERSION=16
196+
RUN --mount=type=cache,target=/var/lib/apt/lists,sharing=locked \
197+
--mount=type=cache,target=/var/cache/apt,sharing=locked \
198+
<<EOF
199+
apt-get --quiet --yes update
200+
apt-get --quiet --yes install \
201+
git \
202+
gnupg \
203+
less \
204+
openssh-client \
205+
postgresql-common \
206+
sudo
207+
# Install the Postgres repo
208+
/usr/share/postgresql-common/pgdg/apt.postgresql.org.sh -y
209+
# Intall the Postgres client (make sure the version matches the one in production)
210+
apt-get --quiet --yes install \
211+
postgresql-client-${POSTGRES_VERSION}
212+
# Download and import the Nodesource GPG key
213+
mkdir -p /etc/apt/keyrings
214+
curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg
215+
# Create NodeSource repository
216+
echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_20.x nodistro main" > /etc/apt/sources.list.d/nodesource.list
217+
# Update lists again and install Node.js
218+
apt-get --quiet --yes update
219+
apt-get --quiet --yes install nodejs
220+
# Tidy up
221+
apt-get --quiet --yes autoremove
222+
EOF
223+
224+
# Give the unprivileged user passwordless sudo access
225+
ARG USERNAME
226+
RUN echo "$USERNAME ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers
227+
228+
# Make less the default pager for things like psql results and git logs
229+
ENV PAGER=less
230+
231+
# Flag that this is the dev container
232+
ENV DEVCONTAINER=1
233+
234+
# Switch back to the unprivileged user
235+
USER $USERNAME
236+
237+
# Copy in the node_modules directory from the frontend-deps stage to initialise
238+
# the volume that gets mounted here
239+
ARG UID
240+
ARG GID
241+
COPY --chown=$UID:$GID --from=frontend-deps --link /build/node_modules ./node_modules
242+
243+
# Install the dev dependencies (they're omitted in the base stage)
244+
RUN --mount=type=cache,target=/home/$USERNAME/.cache/uv,uid=$UID,gid=$GID \
245+
uv sync --frozen --dev
246+
247+
# Just do nothing forever - exec commands elsewhere
248+
CMD ["tail", "-f", "/dev/null"]
249+
250+
251+
############
252+
# ci stage #
253+
############
254+
255+
FROM dev AS ci
256+
257+
# This stage is used in the CI pipeline to run tests, linters, etc.
258+
# It extends the dev stage by adding in the collected static files from the web
259+
# stage so that we can use Whitenoise's manifest storage backend in tests
260+
# without needing to run collectstatic.
261+
262+
ARG UID
263+
ARG GID
264+
COPY --chown=$UID:$GID --from=web --link /app/static/ /app/static/

docker-compose.yml

+64
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
services:
2+
web:
3+
build:
4+
context: .
5+
target: dev
6+
environment:
7+
DATABASE_URL: postgres://hypha:hypha@db/hypha # pragma: allowlist secret
8+
DJANGO_SETTINGS_MODULE: hypha.settings.dev
9+
PGHOST: db
10+
PGUSER: hypha
11+
REDIS_URL: redis://redis
12+
SECRET_KEY: local_dev_secret_key # pragma: allowlist secret
13+
PYTHONDONTWRITEBYTECODE: 1
14+
PYTHONUNBUFFERED: 1
15+
PROJECTS_ENABLED: 1
16+
ORG_SHORT_NAME: "WT.SH"
17+
env_file:
18+
- path: .env
19+
required: false
20+
ports:
21+
- 3000:3000 # Webpack dev server
22+
- 8000:9001 # Django development server
23+
- 8001:8001 # mkdocs server
24+
volumes:
25+
# Mount the repo at /app/
26+
- ./:/app/
27+
# Mount a volume at /app/node_modules to prevent Docker trying to sync the
28+
# contents between the container and the host. This means that the
29+
# node_modules directory can appear to have different contents depending
30+
# on whether you are looking at it from the host or the container, so make
31+
# sure you consistently run Node.js commands from inside the container or
32+
# the host, but not both.
33+
- node_modules:/app/node_modules
34+
init: true
35+
depends_on:
36+
- db
37+
- redis
38+
39+
db:
40+
image: postgres:16
41+
expose:
42+
- 5432
43+
environment:
44+
# Location of DB data folder
45+
PGDATA: /var/lib/postgresql/data/pgdata
46+
# Defaults for createdb/dropdb/pg_restore etc.
47+
PGDATABASE: hypha
48+
PGUSER: hypha
49+
# Values for initdb
50+
POSTGRES_HOST_AUTH_METHOD: trust
51+
POSTGRES_USER: hypha
52+
volumes:
53+
- pgdata:/var/lib/postgresql/data
54+
attach: false
55+
56+
redis:
57+
image: redis:7.2
58+
expose:
59+
- 6379
60+
attach: false
61+
62+
volumes:
63+
node_modules:
64+
pgdata:

0 commit comments

Comments
 (0)