Skip to content
Merged
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
11 changes: 11 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Virtual environments
venv/
.venv/

# IDE files
.idea/
.vscode/

# Caches
**/__pycache__/
.ruff_cache/
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -474,3 +474,5 @@ pip-selfcheck.json
# End of https://www.toptal.com/developers/gitignore/api/django,pycharm+all,venv,visualstudiocode,macos,node

!src/web/static/lib/

.env.docker
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,13 @@ Lastly, a new [release](https://github.com/MAKENTNU/web/releases) must be create
(MAKENTNU/web#766)
- Replaced `local_settings.py` with `.env` (MAKENTNU/web#767)
- Developers must create a `.env` file locally - see the "Setup" section of the README
- Renamed and added some more `make` commands (MAKENTNU/web#768)
- Improved Docker setup (MAKENTNU/web#768)
- Environment variables specific to Docker can now be provided using a `.env.docker` file
- The image is now based on the latest stable Ubuntu release, like the prod server
- Static and media files are persisted between runs (through a volume)
- Added Docker-specific `make` commands, all prefixed with `d-`
- Stopped always running migrations when starting the container; it's more useful having manual control, using e.g. `make d-migrate`


## 2025-05-03 (MAKENTNU/web#757)
Expand Down
9 changes: 0 additions & 9 deletions Dockerfile

This file was deleted.

104 changes: 90 additions & 14 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,20 +1,96 @@
run:
uv run manage.py runserver 0.0.0.0:8000
sync: ## Makes your locally installed packages match the ones specified in `pyproject.toml`.
uv sync --group dev

migrations:
uv run manage.py makemigrations ${A}
manage: ## Runs the Django management command specified by passing `args`.
uv run $(uv_args) manage.py $(args)

migrate:
uv run manage.py migrate
shell: ## Enters a Django shell, with most common classes automatically imported.
make manage args="shell_plus"

superuser:
uv run manage.py createsuperuser
migrate: ## Updates the database schema from the migration files.
make manage args="migrate $(args)"

startapp:
uv run manage.py startapp
makemigrations: ## Creates migration files from the models, if there are any changes.
make manage args="makemigrations $(args)"

shell:
uv run manage.py shell
makemessages: ## Extracts all translatable strings from the codebase and updates the `.po` files with them.
make manage args="makemessages $(args)"

test:
uv run manage.py test
makemessages-all: ## Runs `makemessages` for all languages and domains.
make makemessages args="-a"
make makemessages args="-a -d djangojs"

compilemessages: ## Updates the `.mo` files from the `.po` files.
make manage args="compilemessages $(args)"

collectstatic: ## "Compiles" the static files (CSS and JS files, images, etc.) into the `STATIC_ROOT` folder.
make manage args="collectstatic --no-input"

update: ## Updates the installed packages, database and static files.
make sync
make migrate
make collectstatic

createsuperuser: ## Creates a superuser.
make manage args="createsuperuser"

start: ## Starts the Django webserver.
make manage args="runserver $(args)"

test: ## Runs the test suite. Pass extra arguments with `args`, e.g. `args="-k 'test_function'"`.
make manage args="test $(args)"

### Docker ###

COMPOSE_FILE := docker/compose.dev.yaml

d-compose: ## Run Docker Compose for development with `make_ntnu` as project name. Helper command to run any compose command.
docker compose -f '$(COMPOSE_FILE)' -p make_ntnu $(args)

d-build: ## Rebuilds the container image. Use when changes are made to the Dockerfile or the dependencies in `pyproject.toml`.
make d-compose args="build"

d-down: ## Stops and removes the container.
make d-compose args="down"

d-bash: ## Enters a bash shell in an already-running `web` container.
make d-compose args="exec web bash"

d-manage: ## Runs the Django management command specified by passing `args`.
make d-compose args="run --rm web uv run manage.py $(args)"

d-shell: ## Enters a Django shell, with most common classes automatically imported.
make d-manage args="shell_plus"

d-migrate: ## Updates the database schema from the migration files.
make d-manage args="migrate $(args)"

d-makemigrations: ## Creates migration files from the models, if there are any changes.
make d-manage args="makemigrations $(args)"

d-makemessages: ## Extracts all translatable strings from the codebase and updates the `.po` files with them.
make d-manage args="makemessages $(args)"

d-makemessages-all: ## Runs `makemessages` for all languages and domains.
make d-makemessages args="-a"
make d-makemessages args="-a -d djangojs"

d-compilemessages: ## Updates the `.mo` files from the `.po` files.
make d-manage args="compilemessages $(args)"

d-collectstatic: ## "Compiles" the static files (CSS and JS files, images, etc.) into the `STATIC_ROOT` folder.
make d-manage args="collectstatic --no-input"

d-update: ## Updates the container, database and static files.
make d-build
make d-migrate
make d-collectstatic

d-createsuperuser: ## Creates a superuser.
make d-manage args="createsuperuser"

d-start: ## Starts the Django webserver.
make d-compose args="up $(args)"

d-test: ## Runs the test suite. Pass extra arguments with `args`, e.g. `args="-k 'test_function'"`.
make d-manage args="test $(args)"
53 changes: 52 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,16 @@
```
1. Create an empty `.env` file directly inside the repository folder, and fill it by
copying the contents of [`.env.example`](.env.example)
1. To be able to run commands in the `Makefile`:
* If using Windows:
* Ensure that you have a program installed that can run makefiles.
This can be done by e.g. installing
[GnuWin's Make](https://gnuwin32.sourceforge.net/packages/make.htm) using
[WinGet](https://learn.microsoft.com/en-us/windows/package-manager/winget/):
```bash
winget install GnuWin32.Make
```
* If using Linux/macOS: You don't need to do anything.

#### PyCharm

Expand All @@ -47,6 +57,9 @@ and set the following settings (File → Settings...):

### 🚀 Starting the webserver

_We recommend developing using locally installed dependencies, as it's a lot faster and easier to debug, but if you'd like to use Docker (e.g. for
testing stuff in a prod-like environment), follow the instructions under [the "Using Docker" section](#-using-docker) instead._

1. Create an SQLite database file with the proper tables:
```shell
uv run manage.py migrate
Expand All @@ -65,12 +78,50 @@ and set the following settings (File → Settings...):
uv run manage.py runserver
```

### 🐋 Using Docker

1. [Install Docker desktop](https://www.docker.com/products/docker-desktop/)
1. Create a `.env.docker` file:

This will contain environment variables that override the ones in `.env`, which will
be used by code running inside Docker.

The following file contents is a good basis:
```dotenv
STATIC_AND_MEDIA_FILES__PARENT_DIR='/vol/web/'
```
1. Build the Docker image and create the database:
```shell
make d-update
```
1. Create an admin user for local development:
```shell
make d-createsuperuser
```
1. Run the server:
* If using PyCharm:
1. [Add a Docker-based Python interpreter](https://www.jetbrains.com/help/pycharm/using-docker-compose-as-a-remote-interpreter.html#docker-compose-remote)
with `make_ntnu` as project name (this should match the `-p` argument in [the `Makefile`](Makefile))
1. Create a "Django Server" [run configuration](https://www.jetbrains.com/help/pycharm/run-debug-configuration.html) using the newly created
Python interpreter, with `0.0.0.0` (instead of `localhost`) as host (this should match the IP address specified by the `command` key in
[`compose.dev.yaml`](docker/compose.dev.yaml))
1. Press the green "play" button in the top right corner
* Otherwise, run:
```shell
make d-start
```

If you encounter any hard-to-fix Docker-related problems, an easy (but drastic) fix can sometimes be to delete the container (`make d-down`)
and follow the steps above again.

### 🧳 Developing offline

When running uv commands, pass [the `--offline` flag](https://docs.astral.sh/uv/reference/cli/#uv-run--offline).
For example:
```shell
uv run --offline manage.py runserver
# Using the make command:
make start uv_args="--offline"
```
</details>

Expand All @@ -89,4 +140,4 @@ See [CONTRIBUTING.md](CONTRIBUTING.md) for the following topics:
## 📝 Changelog

[View the changelog](CHANGELOG.md) to see a list of changes made to the website over time,
as well as a superficial description of the release process.
as well as a high-level description of the release process.
14 changes: 0 additions & 14 deletions docker-compose.yml

This file was deleted.

39 changes: 39 additions & 0 deletions docker/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Latest stable release - should be the same version as the prod server is running
FROM ubuntu:rolling

ENV PYTHONDONTWRITEBYTECODE=1 \
# Immediately write logs to the console / log file
PYTHONUNBUFFERED=1 \
# https://docs.astral.sh/uv/guides/integration/docker/#compiling-bytecode
UV_COMPILE_BYTECODE=1 \
# https://docs.astral.sh/uv/guides/integration/docker/#caching
UV_LINK_MODE=copy \
# https://docs.astral.sh/uv/concepts/projects/config/#project-environment-path
# (Prevents using/creating a `.venv` folder inside `PROJECT_DIR` when starting
# the container)
UV_PROJECT_ENVIRONMENT=/venv/

# From https://docs.astral.sh/uv/guides/integration/docker/#installing-uv
COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/

ARG PROJECT_DIR="/web"
WORKDIR ${PROJECT_DIR}

RUN apt update --fix-missing && \
apt install --yes \
# Necessary for `python-ldap`
build-essential python3-dev libldap2-dev libsasl2-dev libssl-dev \
# Necessary for running `makemessages`/`compilemessages`
gettext libgettextpo-dev && \
# Clean up apt cache lists to reduce image size by removing temporary installation files
rm -rf /var/lib/apt/lists/*

COPY pyproject.toml uv.lock ${PROJECT_DIR}
# https://docs.astral.sh/uv/guides/integration/docker/#caching
RUN --mount=type=cache,target=/root/.cache/uv \
uv sync --locked --group dev

# Create the directories used by `STATIC_ROOT` and `MEDIA_ROOT`
ARG STATIC_DATA_DIR="/vol/web"
RUN mkdir -p ${STATIC_DATA_DIR}/static ${STATIC_DATA_DIR}/media && \
chmod -R 755 ${STATIC_DATA_DIR}
24 changes: 24 additions & 0 deletions docker/compose.dev.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
services:
web:
platform: linux/amd64
image: make_web
build:
context: ../
dockerfile: ./docker/Dockerfile
command: uv run manage.py runserver 0.0.0.0:8000
ports:
- "8000:8000"
env_file:
# The bottom-most envvars will override the ones above
- ../.env
- ../.env.docker
volumes:
# Mounts the parent folder (i.e. the repo folder) as a volume with the same path
# as `PROJECT_DIR` in the Dockerfile, to enable hot reloading of changed local
# files
- ../:/web
# Makes the files inside `STATIC_DATA_DIR` (see the Dockerfile) persistent
- static-data:/vol/web

volumes:
static-data:
2 changes: 1 addition & 1 deletion src/web/management/commands/runserver.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ def inner_run(self, *args, **options):
if not settings.DEBUG:
super().inner_run(*args, **options)

addr_regex = r"(?:127\.0\.0\.1|localhost)"
addr_regex = r"(?:localhost|127\.0\.0\.1|0\.0\.0\.0)"
dev_server_addr_regex = re.compile(
rf"(Starting .*development server at https?://)({addr_regex}):(\d+)",
re.IGNORECASE
Expand Down