From edb00c104dbf47bfb9961a5b850a11f0164ecefe Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Bedna=C5=99=C3=ADk?= <jan.bednarik@gmail.com>
Date: Mon, 24 May 2021 17:13:11 +0200
Subject: [PATCH] CI and Docker deployment

---
 .dockerignore    |  3 +++
 .gitlab-ci.yml   | 21 +++++++++++++++++++++
 Dockerfile       | 18 ++++++++++++++++++
 README.md        | 26 ++++++++++++++++++++++++++
 gunicorn.conf.py |  7 +++++++
 requirements.txt |  4 +++-
 run.sh           | 10 ++++++++++
 settings.py      | 20 +++++++++++++++++++-
 8 files changed, 107 insertions(+), 2 deletions(-)
 create mode 100644 .dockerignore
 create mode 100644 .gitlab-ci.yml
 create mode 100644 Dockerfile
 create mode 100644 gunicorn.conf.py
 create mode 100644 run.sh

diff --git a/.dockerignore b/.dockerignore
new file mode 100644
index 0000000..e7994c1
--- /dev/null
+++ b/.dockerignore
@@ -0,0 +1,3 @@
+.git
+.venv
+.env
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
new file mode 100644
index 0000000..8c91786
--- /dev/null
+++ b/.gitlab-ci.yml
@@ -0,0 +1,21 @@
+stages:
+  - build
+
+image: docker:20.10.8
+
+variables:
+  DOCKER_TLS_CERTDIR: "/certs"
+  IMAGE_TAG_APP: $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG
+
+services:
+  - docker:20.10.8-dind
+
+before_script:
+  - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
+
+build_app:
+  stage: build
+  script:
+    - docker pull $CI_REGISTRY_IMAGE:test || true
+    - docker build --cache-from $CI_REGISTRY_IMAGE:test -t $IMAGE_TAG_APP .
+    - docker push $IMAGE_TAG_APP
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..fdc82f9
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,18 @@
+FROM python:3.6
+
+RUN mkdir /app
+WORKDIR /app
+
+COPY requirements.txt .
+RUN pip install -r requirements.txt
+
+COPY . .
+
+RUN bash -c 'adduser --disabled-login --quiet --gecos app app &&  \
+             chmod -R o+r /app/ && \
+             chmod o+x /app/run.sh'
+USER app
+
+EXPOSE 8000
+
+CMD ["bash", "run.sh"]
diff --git a/README.md b/README.md
index c6d2bbc..626383f 100644
--- a/README.md
+++ b/README.md
@@ -3,3 +3,29 @@
 Fork used by the Czech Pirate Party. Includes PirateID integration and Czech translation.
 
 Helios is an end-to-end verifiable voting system.
+
+## Info
+
+větve v gitu:
+
+- `original` je kopie https://github.com/benadida/helios-server
+- `master` je z `original` odvozená Pirátská verze nasazená v produkci
+- `test` je testovací verze odvozená z `master` nasazená pro testování
+
+Obvykle děláme změny v `test` a když je to ok, releasujeme do `master`.
+
+### Jak na upgrade?
+
+1. aktualizuj větev `original`
+2. udělej rebase větve `master` z větve `original`
+3. udělej novou větev `test` odvozenou z `master`
+
+## Čas
+
+S časem se v kódu pracuje velmi primitivně. Všechny časy jsou naivně a musí být UTC.
+Což komplikuje všechno. Ale podařilo se to rozumně pořešit na vstupu a výstupu.
+
+V kódu je to ohnuté takto:
+
+* Vizuálně času v templatech prohnán přes filtr `|timezone:"Europe/Prague"`.
+* Ve form field `SplitDateTimeField` očekáváme Europe/Prague a převádíme na UTC.
diff --git a/gunicorn.conf.py b/gunicorn.conf.py
new file mode 100644
index 0000000..16484fd
--- /dev/null
+++ b/gunicorn.conf.py
@@ -0,0 +1,7 @@
+bind = "0.0.0.0:8000"
+accesslog = "-"
+workers = 1
+max_requests = 1000
+max_requests_jitter = 10
+timeout = 60
+graceful_timeout = 60
diff --git a/requirements.txt b/requirements.txt
index f5e6b05..7adbb62 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -6,7 +6,7 @@ gunicorn==20.0.4
 dj_database_url==0.5.0
 psycopg2==2.8.4
 
-celery==4.2.1
+celery[redis]==4.4.7
 django-picklefield==1.1.0
 
 django-anymail==8.4
@@ -25,3 +25,5 @@ rollbar==0.14.7
 
 requests-oauthlib==1.3.1
 unidecode==1.3.6
+
+sentry-sdk[django]==1.40.5
diff --git a/run.sh b/run.sh
new file mode 100644
index 0000000..d8aac6d
--- /dev/null
+++ b/run.sh
@@ -0,0 +1,10 @@
+#!/bin/bash
+
+# exit on error
+set -e
+
+# migrate database
+python manage.py migrate
+
+# start webserver
+exec gunicorn -c gunicorn.conf.py wsgi
diff --git a/settings.py b/settings.py
index a7f24a3..05fed0b 100644
--- a/settings.py
+++ b/settings.py
@@ -5,6 +5,11 @@ import sys
 import json
 import os
 
+import sentry_sdk
+from sentry_sdk.integrations.celery import CeleryIntegration
+from sentry_sdk.integrations.django import DjangoIntegration
+from sentry_sdk.integrations.logging import LoggingIntegration
+
 TESTING = 'test' in sys.argv
 
 # go through environment variables and override them
@@ -49,7 +54,7 @@ DATABASES = {
 # override if we have an env variable
 if get_from_env('DATABASE_URL', None):
     import dj_database_url
-    DATABASES['default'] = dj_database_url.config(conn_max_age=600, ssl_require=True)
+    DATABASES['default'] = dj_database_url.config(conn_max_age=600)
     DATABASES['default']['ENGINE'] = 'django.db.backends.postgresql_psycopg2'
 
 # Local time zone for this installation. Choices can be found here:
@@ -308,3 +313,16 @@ OCTOPUS_API_TOKEN = get_from_env('OCTOPUS_API_TOKEN', '')
 
 if DEBUG:
     EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
+
+# Sentry setup
+SENTRY_DSN = get_from_env('SENTRY_DSN', '')
+if SENTRY_DSN:
+    sentry_sdk.init(
+        dsn=SENTRY_DSN,
+        integrations=[
+            DjangoIntegration(),
+            CeleryIntegration(),
+            LoggingIntegration(level=logging.INFO, event_level=logging.WARNING),
+        ],
+        send_default_pii=True,
+    )
-- 
GitLab