Skip to content

Commit

Permalink
Merge pull request #1136 from gafderks/action/docker
Browse files Browse the repository at this point in the history
Added docker workflow file
  • Loading branch information
gafderks committed Dec 21, 2023
2 parents 138939a + 00510f2 commit 0cda642
Show file tree
Hide file tree
Showing 10 changed files with 116 additions and 32 deletions.
1 change: 0 additions & 1 deletion .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ build/
static/
media/
functional_tests/
tests/
**/*.mo
**/*.pyc
.git/
Expand Down
73 changes: 73 additions & 0 deletions .github/workflows/docker.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
name: Docker build and export

on:
push:
branches:
- "master"
pull_request:

env:
WEB_TEST_IMAGE: dbase-web:test
WEB_IMAGE: gafderkspersonal/dbase-web
NGINX_TEST_IMAGE: dbase-nginx:test
NGINX_IMAGE: gafderkspersonal/dbase-nginx

jobs:
build:
environment: CI
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Set short git commit SHA
id: vars
run: echo "sha_short=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
- name: Output git commit SHA
run: echo ${{ steps.vars.outputs.sha_short }}
- name: Build web test image
uses: docker/build-push-action@v5
with:
target: web-test
context: .
load: true
tags: ${{ env.WEB_TEST_IMAGE }}
- name: Test web test image
run: |
docker run \
-e SECRET_KEY="${{ secrets.SECRET_KEY }}" \
-e DATABASE_URL="sqlite:////app/db.sqlite3" \
-e DEBUG=on \
--rm ${{ env.WEB_TEST_IMAGE }} \
python manage.py test --exclude-tag=functional -v 2
- name: Build nginx image
uses: docker/build-push-action@v5
with:
target: nginx
context: .
load: true
tags: ${{ env.NGINX_TEST_IMAGE }}
- name: Publish web image
uses: docker/build-push-action@v5
with:
context: .
push: true
target: runtime
tags: ${{ env.WEB_IMAGE }}:latest,${{ env.WEB_IMAGE }}:${{ steps.vars.outputs.sha_short }}
if: github.ref == 'refs/heads/master' && github.event_name != 'pull_request'
- name: Publish nginx image
uses: docker/build-push-action@v5
with:
context: .
push: true
target: nginx
tags: ${{ env.NGINX_IMAGE }}:latest,${{ env.NGINX_IMAGE }}:${{ steps.vars.outputs.sha_short }}
if: github.ref == 'refs/heads/master' && github.event_name != 'pull_request'
38 changes: 27 additions & 11 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,25 @@ ENV APP_HOME=/app
RUN mkdir -p ${APP_HOME}
WORKDIR ${APP_HOME}

RUN apt-get update && \
apt-get install -y --no-install-recommends \
curl ca-certificates gnupg git
RUN apt-get update && apt-get install -y --no-install-recommends curl ca-certificates gnupg git \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*

# Install Node.js
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
RUN mkdir -p /etc/apt/keyrings && \
curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg && \
NODE_MAJOR=20; echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_$NODE_MAJOR.x nodistro main" | tee /etc/apt/sources.list.d/nodesource.list && \
apt-get update && apt-get install -y --no-install-recommends nodejs
apt-get update && apt-get install -y --no-install-recommends nodejs \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*

RUN npm install -g [email protected]

ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1
ENV DOCKER_BUILD 1
ENV NODE_ENV production

RUN pip install --upgrade --no-cache-dir pipenv==2023.10.24 wheel==0.41.2
COPY ./Pipfile .
Expand All @@ -33,12 +37,12 @@ RUN PIPENV_VENV_IN_PROJECT=1 pipenv install --deploy
COPY ./package-lock.json .
COPY ./package.json .

RUN npm ci && \
RUN npm ci --omit=dev && \
npx update-browserslist-db@latest

COPY . .

RUN SECRET_KEY=dummy pipenv run ./manage.py collectstatic --noinput
RUN SECRET_KEY=dummy pipenv run python ./manage.py collectstatic --noinput

###########
## NGINX ##
Expand All @@ -59,7 +63,9 @@ COPY --from=base /app/static /opt/services/dbase/static
FROM python:3.12-slim@sha256:41487afa4d11d89b3ec37fdfb652ceb2f2db0c19b2259a24b052e5805bc22197 as runtime
LABEL maintainer="Geert Derks <[email protected]>"

RUN apt-get update && apt-get install -y --no-install-recommends gettext curl
RUN apt-get update && apt-get install -y --no-install-recommends gettext curl \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*

# Prevent writing pyc files
ENV PYTHONDONTWRITEBYTECODE 1
Expand All @@ -73,7 +79,8 @@ ENV APP_HOME=/app
RUN groupadd -g 999 appuser && \
useradd -r -u 999 -g appuser appuser

RUN mkdir ${APP_HOME}
RUN mkdir ${APP_HOME} && \
chown appuser:appuser ${APP_HOME}
RUN mkdir ${APP_HOME}/media && \
chown appuser:appuser ${APP_HOME}/media
WORKDIR ${APP_HOME}
Expand All @@ -88,12 +95,21 @@ COPY --chown=appuser:appuser . .

USER 999

ENV PATH="/${APP_HOME}/.venv/bin:$PATH"
ENV PATH="${APP_HOME}/.venv/bin:$PATH"

RUN django-admin compilemessages

ENTRYPOINT [ "./entrypoint.sh" ]

# Migrate (separate command, do not want to run this simultaneously if started multiple times.)
#############
## TESTING ##
#############

FROM base as base-test

RUN PIPENV_VENV_IN_PROJECT=1 pipenv install --deploy --dev

FROM runtime as web-test

COPY --from=base-test --chown=appuser:appuser ${APP_HOME}/.venv ./.venv

# TODO: https://snyk.io/blog/best-practices-containerizing-python-docker/#:~:text=5.%20Handle%20unhealthy%20states%20of%20your%20containerized%20Python%20application
5 changes: 2 additions & 3 deletions booking/api/event.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
from datetime import datetime

from django.http import HttpResponse
from django.utils.translation import gettext as _
from django.utils.timezone import now
from openpyxl import Workbook
from openpyxl.styles import Alignment
from openpyxl.utils import get_column_letter
Expand All @@ -27,7 +26,7 @@ def get(self, request, *args, **kwargs):
if context["current_group"]
else _("All groups"),
downloaded=_("downloaded on"),
date=datetime.now().strftime("%Y-%m-%d"),
date=now().strftime("%Y-%m-%d"),
)

workbook = Workbook()
Expand Down
24 changes: 10 additions & 14 deletions booking/models/event.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
from datetime import timedelta, datetime, timezone
from datetime import timedelta

from django.db import models
from django.urls import reverse
from django.utils.translation import gettext_lazy as _
from django.utils.timezone import now
from rules.contrib.models import RulesModel

from booking import rules
Expand All @@ -20,11 +21,9 @@ def viewable(user):
def editable(user):
query = Event.objects.viewable(user)
if not user.has_perm("booking.can_book_on_locked_events"):
query = query.filter(
locked=False, privileged_booking_end__gt=datetime.now(timezone.utc)
)
query = query.filter(locked=False, privileged_booking_end__gt=now())
if not user.has_perm("booking.can_book_on_privileged_events"):
query = query.filter(booking_end__gt=datetime.now(timezone.utc))
query = query.filter(booking_end__gt=now())
return query


Expand Down Expand Up @@ -110,16 +109,16 @@ class BookingStatus(models.TextChoices):

@property
def booking_status(self):
now = datetime.now(timezone.utc)
_now = now()
if not self.visible:
return self.BookingStatus.HIDDEN
if self.locked:
return self.BookingStatus.LOCKED
if now < self.booking_start:
if _now < self.booking_start:
return self.BookingStatus.NOT_STARTED
if now < self.booking_end:
if _now < self.booking_end:
return self.BookingStatus.OPENED
if now < self.privileged_booking_end:
if _now < self.privileged_booking_end:
return self.BookingStatus.PRIVILEGED
return self.BookingStatus.LOCKED

Expand All @@ -132,10 +131,7 @@ def is_privileged(self):
:return: bool
"""
return (
not self.locked
and self.booking_end
< datetime.now(timezone.utc)
< self.privileged_booking_end
not self.locked and self.booking_end < now() < self.privileged_booking_end
)

@property
Expand All @@ -145,4 +141,4 @@ def is_locked(self):
True or the privileged booking period has ended.
:return: bool
"""
return self.locked or datetime.now(timezone.utc) > self.privileged_booking_end
return self.locked or now() > self.privileged_booking_end
Empty file removed dbase/__init__.py
Empty file.
2 changes: 1 addition & 1 deletion dbase/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@
"default": {
**env.db(),
"TEST": {
"NAME": BASE_DIR / "db.sqlite3.test",
"NAME": BASE_DIR / "db.test.sqlite3",
},
},
}
Expand Down
Empty file modified entrypoint.sh
100644 → 100755
Empty file.
5 changes: 3 additions & 2 deletions functional_tests/test_authentication.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from datetime import timedelta, datetime
from datetime import timedelta
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from django.utils.timezone import now

from booking.models import Event
from tests.utils import english
Expand All @@ -10,7 +11,7 @@

def create_events():
def now_plus(days_delta):
return datetime.now() + timedelta(days=days_delta)
return now() + timedelta(days=days_delta)

Event(
name="Active event",
Expand Down
Empty file removed tests/__init__.py
Empty file.

0 comments on commit 0cda642

Please sign in to comment.