Skip to content

Commit 581b827

Browse files
authored
Merge pull request #1 from hotosm/feat/set_up
Merge project setup with appropriate local dev environment settings
2 parents 4aa9df0 + e411466 commit 581b827

22 files changed

+1887
-705
lines changed

.dockerignore

+12-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
# Django project
22
/media/
3-
/static/
4-
*.sqlite3
53

64
# Python and others
75
__pycache__
@@ -37,3 +35,15 @@ wheels/
3735
*.egg-info/
3836
.installed.cfg
3937
*.egg
38+
39+
# Github workflows
40+
.github
41+
42+
# Git
43+
.git
44+
45+
# Docker compose
46+
docker-compose*
47+
48+
# Makefile
49+
Makefile

.env.example

+15-7
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,18 @@
1-
SECRET_KEY = ""
1+
ENV=${ENV:-dev}
2+
SECRET_KEY=${SECRET_KEY:-somesuperdupersecretkeyfortesting}
3+
24
# SECURITY WARNING: define the correct hosts in production!
3-
ALLOWED_HOSTS = ["*"]
5+
ALLOWED_HOSTS=${ALLOWED_HOSTS:-["*"]}
6+
7+
# Superuser creation (override in production)
8+
DJANGO_SUPERUSER_USERNAME=${DJANGO_SUPERUSER_USERNAME:-admin}
9+
DJANGO_SUPERUSER_EMAIL=${DJANGO_SUPERUSER_EMAIL:[email protected]}
10+
DJANGO_SUPERUSER_PASSWORD=${DJANGO_SUPERUSER_PASSWORD:-adminpassword}
411

512
# Postgres DB settings
6-
DB_NAME = ""
7-
DB_USER = ""
8-
DB_PASSWORD = ""
9-
DB_HOST = ""
10-
DB_PORT = 5432
13+
# port, name, host, allowed hosts
14+
DB_NAME=${DB_NAME:-postgres}
15+
DB_USER=${DB_USER:-postgres}
16+
DB_PASSWORD=${DB_PASSWORD:-postgres}
17+
DB_HOST=${DB_HOST:-db}
18+
DB_PORT=${DB_PORT:-5432}
+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Use hotosm workflow immage build fors pushes to main or release branches
2+
name: Image Build
3+
4+
on:
5+
push:
6+
branches:
7+
- 'main'
8+
- 'release/*'
9+
- 'dev'
10+
11+
jobs:
12+
pytest:
13+
uses: hotosm/gh-workflows/.github/workflows/test_pytest.yml@main
14+
secrets: inherit
15+
with:
16+
image_name: ghcr.io/${{ github.repository }}
17+
build_target: prod
18+
environment: dev

.github/workflows/pytest.yml

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# Use hotosm workflow test_compose for pull requests to main or release branches
2+
name: PyTest
3+
4+
on:
5+
pull_request:
6+
branches:
7+
- 'main'
8+
- 'release/*'
9+
- 'dev'
10+
jobs:
11+
pytest:
12+
uses: hotosm/gh-workflows/.github/workflows/test_compose.yml@main
13+
secrets: inherit
14+
with:
15+
image_name: ghcr.io/${{ github.repository }}/backend
16+
build_dockerfile: Dockerfile
17+
build_target: test
18+
compose_service: web
19+
compose_command: "pytest"
20+
compose_file: "docker-compose.dev.yml"
21+
cache_extra_imgs: |
22+
"docker.io/postgres:16-alpine3.18"

.gitignore

+5
Original file line numberDiff line numberDiff line change
@@ -1 +1,6 @@
1+
.env.dev
2+
.env.prod
13
.env
4+
**/__pycache__/
5+
**/.pytest_cache/
6+
**/.venv/

COPYING

+201
Large diffs are not rendered by default.

Dockerfile

+127-40
Original file line numberDiff line numberDiff line change
@@ -1,58 +1,145 @@
1-
# Use an official Python runtime based on Debian 10 "buster" as a parent image.
2-
FROM python:3.10-slim-buster
1+
ARG PYTHON_IMG_TAG=3.11
32

4-
# Add user that will be used in the container.
5-
RUN useradd wagtail
63

7-
# Port used by this container to serve HTTP.
8-
EXPOSE 8000
4+
# Define the base stage
5+
FROM docker.io/python:${PYTHON_IMG_TAG}-slim-bookworm as base
6+
ARG APP_VERSION
7+
ARG COMMIT_REF
8+
ARG PYTHON_IMG_TAG
9+
LABEL org.hotosm.fmtm.app-name="backend" \
10+
org.hotosm.fmtm.app-version="${APP_VERSION}" \
11+
org.hotosm.fmtm.git-commit-ref="${COMMIT_REF:-none}" \
12+
org.hotosm.fmtm.python-img-tag="${PYTHON_IMG_TAG}" \
13+
org.hotosm.fmtm.maintainer="[email protected]" \
14+
org.hotosm.fmtm.api-port="8000"
15+
RUN set -ex \
16+
&& apt-get update \
17+
&& DEBIAN_FRONTEND=noninteractive apt-get install \
18+
-y --no-install-recommends "locales" "ca-certificates" \
19+
&& DEBIAN_FRONTEND=noninteractive apt-get upgrade -y \
20+
&& rm -rf /var/lib/apt/lists/* \
21+
&& update-ca-certificates
22+
# Set locale
23+
RUN sed -i '/en_US.UTF-8/s/^# //g' /etc/locale.gen && locale-gen
24+
ENV LANG en_US.UTF-8
25+
ENV LANGUAGE en_US:en
26+
ENV LC_ALL en_US.UTF-8
927

10-
# Set environment variables.
11-
# 1. Force Python stdout and stderr streams to be unbuffered.
12-
# 2. Set PORT variable that is used by Gunicorn. This should match "EXPOSE"
13-
# command.
14-
ENV PYTHONDONTWRITEBYTECODE=1 PYTHONUNBUFFERED=1\
15-
PORT=8000
1628

17-
# Install system packages required by Wagtail and Django.
18-
RUN apt-get update --yes --quiet && apt-get install --yes --quiet --no-install-recommends \
19-
build-essential \
20-
libpq-dev \
21-
libmariadbclient-dev \
22-
libjpeg62-turbo-dev \
23-
zlib1g-dev \
24-
libwebp-dev \
25-
&& rm -rf /var/lib/apt/lists/*
29+
# Extract dependencies using poetry (to requirements.txt)
30+
FROM base as extract-deps
31+
WORKDIR /opt/python
32+
COPY pyproject.toml poetry.lock* /opt/python/
33+
RUN pip install --no-cache-dir --upgrade pip \
34+
&& pip install --no-cache-dir \
35+
poetry==1.7.1 poetry-plugin-export==1.6.0 \
36+
&& poetry config warnings.export false
37+
RUN poetry export --without dev --output requirements.txt
38+
RUN poetry export --only dev --output requirements-dev.txt
2639

27-
# Install pipenv
28-
RUN pip install pipenv
2940

30-
# Install the application server.
31-
RUN pip install "gunicorn==20.0.4"
41+
# Define build stage (install deps)
42+
FROM base as build
43+
RUN set -ex \
44+
&& apt-get update \
45+
&& DEBIAN_FRONTEND=noninteractive apt-get install \
46+
-y --no-install-recommends \
47+
"build-essential" \
48+
"gcc" \
49+
"libpq-dev" \
50+
"libmariadb-dev" \
51+
"libjpeg62-turbo-dev" \
52+
"zlib1g-dev" \
53+
"libwebp-dev" \
54+
&& rm -rf /var/lib/apt/lists/*
55+
COPY --from=extract-deps \
56+
/opt/python/requirements.txt /opt/python/
57+
RUN pip install --user --no-warn-script-location \
58+
--no-cache-dir -r /opt/python/requirements.txt
3259

33-
# Copy Pipfile and Pipfile.lock
34-
COPY Pipfile Pipfile.lock ./
35-
36-
# Install the project requirements.
37-
RUN pipenv install --system --deploy
3860

61+
# Define run stage
62+
FROM base as runtime
63+
ARG PYTHON_IMG_TAG
64+
ENV PORT=8000 \
65+
PYTHONDONTWRITEBYTECODE=1 \
66+
PYTHONUNBUFFERED=1 \
67+
PYTHONFAULTHANDLER=1 \
68+
PATH="/home/wagtail/.local/bin:$PATH" \
69+
PYTHONPATH="/app" \
70+
PYTHON_LIB="/home/wagtail/.local/lib/python$PYTHON_IMG_TAG/site-packages" \
71+
SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt \
72+
REQUESTS_CA_BUNDLE=/etc/ssl/certs/ca-certificates.crt \
73+
CURL_CA_BUNDLE=/etc/ssl/certs/ca-certificates.crt
74+
# Install non-dev versions of packages (smaller)
75+
RUN set -ex \
76+
&& apt-get update \
77+
&& DEBIAN_FRONTEND=noninteractive apt-get install \
78+
-y --no-install-recommends \
79+
"postgresql-client" \
80+
"libmariadb3" \
81+
"libjpeg62-turbo" \
82+
"zlib1g" \
83+
"libwebp-dev" \
84+
&& rm -rf /var/lib/apt/lists/*
85+
# Copy the entrypoint script into the Docker image
86+
COPY --chown=wagtail:wagtail entrypoint.dev.sh /app/entrypoint.dev.sh
87+
# Copy pip dependencies from build stage
88+
COPY --from=build \
89+
/root/.local \
90+
/home/wagtail/.local
3991
# Use /app folder as a directory where the source code is stored.
4092
WORKDIR /app
93+
# Copy project
94+
COPY . /app/
95+
# Add non-root user, permissions
96+
RUN useradd -u 1001 -m -c "hotosm account" -d /home/wagtail -s /bin/false wagtail \
97+
&& chown -R wagtail:wagtail /app /home/wagtail \
98+
&& chmod +x /app/entrypoint.dev.sh
99+
# Change to non-root user
100+
USER wagtail
41101

42-
# Set this directory to be owned by the "wagtail" user. This Wagtail project
43-
# uses SQLite, the folder needs to be owned by the user that
44-
# will be writing to the database file.
45-
RUN chown wagtail:wagtail /app
46102

47-
# Copy the source code of the project into the container.
48-
COPY --chown=wagtail:wagtail . .
103+
# Define test (ci) stage
104+
FROM runtime as test
105+
USER root
106+
ARG PYTHON_IMG_TAG
107+
COPY --from=extract-deps \
108+
/opt/python/requirements-dev.txt /opt/python/
109+
# Copy packages from user to root dirs (run ci as root)
110+
# && install dev dependencies (pytest)
111+
RUN mv /home/wagtail/.local/bin/* /usr/local/bin/ \
112+
&& mv /home/wagtail/.local/lib/python${PYTHON_IMG_TAG}/site-packages/* \
113+
/usr/local/lib/python${PYTHON_IMG_TAG}/site-packages/ \
114+
&& pip install --upgrade --no-warn-script-location \
115+
--no-cache-dir -r \
116+
/opt/python/requirements-dev.txt \
117+
&& rm -r /opt/python \
118+
# Pre-compile packages to .pyc (init speed gains)
119+
&& python -c "import compileall; compileall.compile_path(maxlevels=10, quiet=1)"
120+
CMD [ "pytest" ]
49121

50-
# Use user "wagtail" to run the build commands below and the server itself.
51-
USER wagtail
52122

53-
# Collect static files.
54-
RUN python manage.py collectstatic --noinput --clear
123+
# Define debug (development) stage
124+
FROM runtime as debug
125+
# Add Healthcheck
126+
HEALTHCHECK --start-period=10s --interval=5s --retries=20 --timeout=5s \
127+
CMD curl --fail http://localhost:8000 || exit 1
128+
# Use the entrypoint script as the Docker entrypoint
129+
ENTRYPOINT ["/app/entrypoint.dev.sh"]
130+
CMD ["./wait-for-it.sh", "db:5432", "--", \
131+
"python", "manage.py", "runserver", "0.0.0.0:8000"]
132+
55133

134+
# Define prod stage
135+
FROM runtime as prod
136+
# Add Healthcheck
137+
HEALTHCHECK --start-period=10s --interval=5s --retries=20 --timeout=5s \
138+
CMD curl --fail http://localhost:8000 || exit 1
139+
# Pre-compile packages to .pyc (init speed gains)
140+
RUN python -c "import compileall; compileall.compile_path(maxlevels=10, quiet=1)" \
141+
# Collect static files
142+
&& python manage.py collectstatic --noinput --clear
56143
# Runtime command that executes when "docker run" is called, it does the
57144
# following:
58145
# 1. Migrate the database.

LICENSE

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
Copyright (C) 2023 Humanitarian OpenStreetMap Team
2+
3+
This program is free software: you can redistribute it and/or modify
4+
it under the terms of the GNU Affero General Public License as
5+
published by the Free Software Foundation, either version 3 of the
6+
License, or (at your option) any later version.
7+
8+
This program is distributed in the hope that it will be useful,
9+
but WITHOUT ANY WARRANTY; without even the implied warranty of
10+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+
GNU Affero General Public License for more details.
12+
13+
You should have received a copy of the GNU Affero General Public License
14+
along with this program. If not, see <https://www.gnu.org/licenses/>.

Makefile

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
.PHONY: build-dev build-prod test up-dev up-prod down-dev down-prod
2+
3+
build-dev:
4+
@docker compose -f docker-compose.dev.yml build
5+
@docker compose -f docker-compose.dev.yml up -d
6+
7+
build-prod:
8+
@docker compose -f docker-compose.prod.yml build
9+
@docker compose -f docker-compose.prod.yml up -d
10+
11+
test:
12+
@docker build --target test -t myproject:test -f Dockerfile .
13+
@docker run --rm myproject:test
14+
@docker rmi myproject:test
15+
16+
up-dev:
17+
@docker compose -f docker-compose.dev.yml up -d
18+
19+
up-prod:
20+
@docker compose -f docker-compose.prod.yml up
21+
22+
down-dev:
23+
@docker compose -f docker-compose.dev.yml down
24+
25+
down-prod:
26+
@docker compose -f docker-compose.prod.yml down
27+
28+
refresh-db:
29+
@if [ -n "$(shell docker volume ls -q)" ]; then docker volume rm $(shell docker volume ls -q); fi

Pipfile

-15
This file was deleted.

0 commit comments

Comments
 (0)