Docker compose refactor
Added shell interpolation for environment variables
This commit is contained in:
parent
e96c6f3528
commit
10affeb32f
16 changed files with 298 additions and 287 deletions
79
Dockerfile
Normal file
79
Dockerfile
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
# stage 1
|
||||
FROM python:3.11-alpine AS backend
|
||||
|
||||
ARG USER_UID=1000
|
||||
ARG GROUP_UID=1000
|
||||
|
||||
RUN apk update \
|
||||
&& apk add --no-cache \
|
||||
vim \
|
||||
curl \
|
||||
gettext
|
||||
|
||||
RUN addgroup -g $USER_UID newsreader && adduser -Du $GROUP_UID -G newsreader newsreader
|
||||
|
||||
RUN mkdir --parents /app/src /app/logs /app/media /app/bin /app/static \
|
||||
&& chown -R newsreader:newsreader /app
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
USER newsreader
|
||||
|
||||
COPY --chown=newsreader:newsreader uv.lock pyproject.toml /app/
|
||||
|
||||
COPY --from=ghcr.io/astral-sh/uv:latest /uv /bin/uv
|
||||
|
||||
RUN --mount=type=cache,target=$HOME/.cache/uv \
|
||||
uv sync --frozen --no-default-groups --no-install-project
|
||||
|
||||
COPY --chown=newsreader:newsreader ./bin/docker-entrypoint.sh /app/bin/docker-entrypoint.sh
|
||||
|
||||
VOLUME ["/app/logs", "/app/media", "/app/static"]
|
||||
|
||||
|
||||
|
||||
# stage 2
|
||||
FROM node:lts-alpine AS frontend-build
|
||||
|
||||
ARG BUILD_ARG=prod
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
RUN chown node:node /app
|
||||
|
||||
USER node
|
||||
|
||||
COPY --chown=node:node ./package*.json ./webpack.*.js ./babel.config.js /app/
|
||||
|
||||
RUN --mount=type=cache,target=$HOME/.npm \
|
||||
npm ci
|
||||
|
||||
COPY --chown=node:node ./src /app/src
|
||||
|
||||
RUN npm run build:$BUILD_ARG
|
||||
|
||||
|
||||
|
||||
# stage 3
|
||||
FROM backend AS production
|
||||
|
||||
COPY --from=frontend-build --chown=newsreader:newsreader \
|
||||
/app/src/newsreader/static /app/src/newsreader/static
|
||||
|
||||
RUN --mount=type=cache,target=$HOME/.cache/uv \
|
||||
uv sync --frozen --only-group production --extra sentry
|
||||
|
||||
COPY --chown=newsreader:newsreader ./src /app/src
|
||||
|
||||
# Note that the static volume will have to be recreated to be pre-populated
|
||||
# correctly with the latest static files. See
|
||||
# https://docs.docker.com/storage/volumes/#populate-a-volume-using-a-container
|
||||
RUN uv run --no-sync -- src/manage.py collectstatic --noinput
|
||||
|
||||
|
||||
|
||||
# (optional) stage 4
|
||||
FROM backend AS development
|
||||
|
||||
RUN --mount=type=cache,target=$HOME/.cache/uv \
|
||||
uv sync --frozen --group development
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
#!/bin/bash
|
||||
|
||||
/app/.venv/bin/python /app/src/manage.py migrate
|
||||
uv run --no-sync -- /app/src/manage.py migrate
|
||||
|
||||
exec "$@"
|
||||
|
|
|
|||
|
|
@ -1,18 +1,11 @@
|
|||
volumes:
|
||||
static-files:
|
||||
node-modules:
|
||||
|
||||
services:
|
||||
celery:
|
||||
build:
|
||||
target: development
|
||||
volumes:
|
||||
- ./src/:/app/src
|
||||
|
||||
django:
|
||||
build:
|
||||
build: &app-development-build
|
||||
target: development
|
||||
command: /app/.venv/bin/python /app/src/manage.py runserver 0.0.0.0:8000
|
||||
command: uv run --no-sync -- /app/src/manage.py runserver 0.0.0.0:8000
|
||||
ports:
|
||||
- "${DJANGO_PORT:-8000}:8000"
|
||||
volumes:
|
||||
|
|
@ -21,12 +14,19 @@ services:
|
|||
stdin_open: true
|
||||
tty: true
|
||||
|
||||
celery:
|
||||
build:
|
||||
<<: *app-development-build
|
||||
volumes:
|
||||
- ./src/:/app/src
|
||||
|
||||
webpack:
|
||||
build:
|
||||
target: frontend-build
|
||||
context: .
|
||||
dockerfile: ./docker/webpack
|
||||
args:
|
||||
BUILD_ARG: "dev"
|
||||
command: npm run build:watch
|
||||
volumes:
|
||||
- ./src/:/app/src
|
||||
- static-files:/app/src/newsreader/static
|
||||
- node-modules:/app/node_modules
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ services:
|
|||
django:
|
||||
condition: service_healthy
|
||||
ports:
|
||||
# Note that --env-file should be used to set these correctly
|
||||
- "${NGINX_HTTP_PORT:-80}:80"
|
||||
volumes:
|
||||
- ./config/nginx/conf.d:/etc/nginx/conf.d
|
||||
|
|
|
|||
|
|
@ -4,33 +4,43 @@ volumes:
|
|||
postgres-data:
|
||||
static-files:
|
||||
|
||||
x-db-env: &db-env
|
||||
POSTGRES_HOST:
|
||||
POSTGRES_PORT:
|
||||
POSTGRES_DB:
|
||||
POSTGRES_USER:
|
||||
POSTGRES_PASSWORD:
|
||||
x-db-connection-env: &db-connection-env
|
||||
POSTGRES_HOST: ${POSTGRES_HOST:-db}
|
||||
POSTGRES_PORT: ${POSTGRES_PORT:-5432}
|
||||
POSTGRES_DB: &pg-database ${POSTGRES_DB:-newsreader}
|
||||
POSTGRES_USER: &pg-user ${POSTGRES_USER:-newsreader}
|
||||
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-newsreader}
|
||||
|
||||
x-django-build-env: &django-build-env
|
||||
<<: *db-env
|
||||
DJANGO_SECRET_KEY:
|
||||
DJANGO_SETTINGS_MODULE:
|
||||
x-db-env: &db-env
|
||||
<<: *db-connection-env
|
||||
PGUSER: *pg-user
|
||||
PGDATABASE: *pg-database
|
||||
|
||||
x-django-env: &django-env
|
||||
<<: *django-build-env
|
||||
VERSION:
|
||||
<<: *db-connection-env
|
||||
|
||||
ALLOWED_HOSTS: ${ALLOWED_HOSTS:-localhost,127.0.0.1,django}
|
||||
INTERNAL_IPS: ${INTERNAL_IPS:-localhost,127.0.0.1,django}
|
||||
|
||||
# see token_urlsafe from python's secret module to generate one
|
||||
DJANGO_SECRET_KEY: ${DJANGO_SECRET_KEY:-Ojg68lYsP3kq2r5JgozUzKVSRFywm17BTMS5iwpLM44}
|
||||
DJANGO_SETTINGS_MODULE: ${DJANGO_SETTINGS_MODULE:-newsreader.conf.production}
|
||||
|
||||
ADMINS: ${ADMINS:-""}
|
||||
|
||||
VERSION: ${VERSION:-""}
|
||||
|
||||
# Email
|
||||
EMAIL_HOST:
|
||||
EMAIL_PORT:
|
||||
EMAIL_HOST_USER:
|
||||
EMAIL_HOST_PASSWORD:
|
||||
EMAIL_USE_TLS:
|
||||
EMAIL_USE_SSL:
|
||||
EMAIL_DEFAULT_FROM:
|
||||
EMAIL_HOST: ${EMAIL_HOST:-localhost}
|
||||
EMAIL_PORT: ${EMAIL_PORT:-25}
|
||||
EMAIL_HOST_USER: ${EMAIL_HOST_USER:-""}
|
||||
EMAIL_HOST_PASSWORD: ${EMAIL_HOST_PASSWORD:-""}
|
||||
EMAIL_USE_TLS: ${EMAIL_USE_TLS:-no}
|
||||
EMAIL_USE_SSL: ${EMAIL_USE_SSL:-no}
|
||||
EMAIL_DEFAULT_FROM: ${EMAIL_DEFAULT_FROM:-webmaster@localhost}
|
||||
|
||||
# Sentry
|
||||
SENTRY_DSN:
|
||||
SENTRY_DSN: ${SENTRY_DSN:-""}
|
||||
|
||||
services:
|
||||
db:
|
||||
|
|
@ -38,8 +48,8 @@ services:
|
|||
<<: *db-env
|
||||
image: postgres:15
|
||||
healthcheck:
|
||||
# Note that --env-file should be used to set these correctly
|
||||
test: /usr/bin/pg_isready --username="${POSTGRES_USER}" --dbname="${POSTGRES_DB}"
|
||||
test: /usr/bin/pg_isready
|
||||
start_period: 10s
|
||||
interval: 5s
|
||||
timeout: 10s
|
||||
retries: 10
|
||||
|
|
@ -55,58 +65,23 @@ services:
|
|||
- memcached
|
||||
- -m 64
|
||||
|
||||
celery:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: ./docker/django
|
||||
target: production
|
||||
args:
|
||||
<<: *django-build-env
|
||||
environment:
|
||||
<<: *django-env
|
||||
command: |
|
||||
/app/.venv/bin/celery --app newsreader
|
||||
--workdir /app/src/
|
||||
worker --loglevel INFO
|
||||
--concurrency 2
|
||||
--beat
|
||||
--scheduler django
|
||||
-n worker1@%h
|
||||
-n worker2@%h
|
||||
healthcheck:
|
||||
test: celery --app newsreader status || exit 1
|
||||
interval: 10s
|
||||
timeout: 10s
|
||||
retries: 5
|
||||
depends_on:
|
||||
rabbitmq:
|
||||
condition: service_started
|
||||
memcached:
|
||||
condition: service_started
|
||||
db:
|
||||
condition: service_healthy
|
||||
django:
|
||||
condition: service_healthy
|
||||
volumes:
|
||||
- logs:/app/logs
|
||||
|
||||
django:
|
||||
build:
|
||||
build: &app-build
|
||||
context: .
|
||||
dockerfile: ./docker/django
|
||||
target: production
|
||||
args:
|
||||
<<: *django-build-env
|
||||
environment:
|
||||
<<: *django-env
|
||||
entrypoint: /app/bin/docker-entrypoint.sh
|
||||
entrypoint: ["/bin/sh", "/app/bin/docker-entrypoint.sh"]
|
||||
command: |
|
||||
/app/.venv/bin/gunicorn --bind 0.0.0.0:8000
|
||||
uv run --no-sync --
|
||||
gunicorn
|
||||
--bind 0.0.0.0:8000
|
||||
--workers 3
|
||||
--chdir /app/src/
|
||||
newsreader.wsgi:application
|
||||
healthcheck:
|
||||
test: /usr/bin/curl --fail http://django:8000 || exit 1
|
||||
start_period: 10s
|
||||
interval: 10s
|
||||
timeout: 10s
|
||||
retries: 5
|
||||
|
|
@ -119,3 +94,33 @@ services:
|
|||
- logs:/app/logs
|
||||
- media:/app/media
|
||||
- static-files:/app/static
|
||||
|
||||
celery:
|
||||
build:
|
||||
<<: *app-build
|
||||
environment:
|
||||
<<: *django-env
|
||||
command: |
|
||||
uv run --no-sync --
|
||||
celery
|
||||
--app newsreader
|
||||
--workdir /app/src/
|
||||
worker --loglevel INFO
|
||||
--concurrency 2
|
||||
--beat
|
||||
--scheduler django
|
||||
-n worker1@%h
|
||||
-n worker2@%h
|
||||
healthcheck:
|
||||
test: uv run --no-sync -- celery --app newsreader status || exit 1
|
||||
start_period: 10s
|
||||
interval: 10s
|
||||
timeout: 10s
|
||||
retries: 5
|
||||
depends_on:
|
||||
rabbitmq:
|
||||
condition: service_started
|
||||
django:
|
||||
condition: service_healthy
|
||||
volumes:
|
||||
- logs:/app/logs
|
||||
|
|
|
|||
102
docker/django
102
docker/django
|
|
@ -1,102 +0,0 @@
|
|||
# stage 1
|
||||
FROM python:3.11-bookworm AS backend
|
||||
|
||||
RUN apt-get update && apt-get install --yes --no-install-recommends \
|
||||
vim \
|
||||
curl \
|
||||
gettext \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
WORKDIR /app
|
||||
RUN mkdir /app/src
|
||||
RUN mkdir /app/logs
|
||||
RUN mkdir /app/media
|
||||
|
||||
COPY uv.lock pyproject.toml /app/
|
||||
|
||||
COPY --from=ghcr.io/astral-sh/uv:latest /uv /bin/uv
|
||||
RUN uv sync --frozen --no-default-groups --no-install-project
|
||||
|
||||
|
||||
# stage 2
|
||||
FROM node:lts AS frontend-build
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY ./*.json ./*.js ./babel.config.js /app/
|
||||
|
||||
RUN npm ci
|
||||
|
||||
COPY ./src /app/src
|
||||
|
||||
RUN npm run build:prod
|
||||
|
||||
|
||||
# stage 3
|
||||
FROM python:3.11-bookworm AS production
|
||||
|
||||
RUN apt-get update && apt-get install --yes --no-install-recommends \
|
||||
postgresql-client \
|
||||
vim \
|
||||
curl \
|
||||
gettext \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
RUN mkdir /app/logs
|
||||
RUN mkdir /app/media
|
||||
RUN mkdir /app/bin
|
||||
|
||||
COPY --from=backend /app/.venv /app/.venv
|
||||
COPY --from=backend /app/uv.lock /app/pyproject.toml /app/
|
||||
COPY --from=backend /bin/uv /bin/uv
|
||||
|
||||
COPY ./bin/docker-entrypoint.sh /app/bin/docker-entrypoint.sh
|
||||
|
||||
COPY --from=frontend-build /app/src/newsreader/static /app/src/newsreader/static
|
||||
|
||||
COPY ./src /app/src
|
||||
|
||||
RUN uv sync --frozen --only-group production --extra sentry
|
||||
|
||||
RUN useradd --no-create-home --uid 1000 newsreader
|
||||
RUN chown --recursive newsreader:newsreader /app
|
||||
|
||||
USER newsreader
|
||||
|
||||
ARG POSTGRES_HOST
|
||||
ARG POSTGRES_PORT
|
||||
ARG POSTGRES_DB
|
||||
ARG POSTGRES_USER
|
||||
ARG POSTGRES_PASSWORD
|
||||
ARG DJANGO_SECRET_KEY
|
||||
ARG DJANGO_SETTINGS_MODULE
|
||||
|
||||
# Note that the static volume will have to be recreated to be pre-populated
|
||||
# correctly with the latest static files. See
|
||||
# https://docs.docker.com/storage/volumes/#populate-a-volume-using-a-container
|
||||
RUN /app/.venv/bin/python src/manage.py collectstatic --noinput
|
||||
|
||||
|
||||
# (optional) stage 4
|
||||
FROM python:3.11-bookworm AS development
|
||||
|
||||
RUN apt-get update && apt-get install --yes --no-install-recommends \
|
||||
vim \
|
||||
curl \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
RUN mkdir /app/logs
|
||||
RUN mkdir /app/media
|
||||
RUN mkdir /app/bin
|
||||
|
||||
COPY --from=backend /app/.venv /app/.venv
|
||||
COPY --from=backend /app/uv.lock /app/pyproject.toml /app/
|
||||
COPY ./bin/docker-entrypoint.sh /app/bin/docker-entrypoint.sh
|
||||
COPY --from=backend /app/src/ /app/src/
|
||||
|
||||
COPY --from=backend /bin/uv /bin/uv
|
||||
RUN uv sync --frozen --group development
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
FROM node:lts
|
||||
|
||||
WORKDIR /app
|
||||
RUN mkdir /app/src
|
||||
|
||||
COPY package*.json webpack.*.js babel.config.js /app/
|
||||
|
||||
RUN npm install
|
||||
|
||||
COPY ./src /app/src
|
||||
|
|
@ -5,8 +5,8 @@
|
|||
"scripts": {
|
||||
"lint": "npx prettier \"src/newsreader/js/**/*.js\" --check",
|
||||
"format": "npx prettier \"src/newsreader/js/**/*.js\" --write",
|
||||
"build": "npx webpack --config webpack.dev.babel.js",
|
||||
"build:watch": "npx webpack --config webpack.dev.babel.js --watch",
|
||||
"build:dev": "npx webpack --config webpack.dev.babel.js",
|
||||
"build:prod": "npx webpack --config webpack.prod.babel.js",
|
||||
"test": "npx jest",
|
||||
"test:watch": "npm test -- --watch"
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ requires-python = ">=3.11"
|
|||
dependencies = [
|
||||
"django~=4.2",
|
||||
"celery~=5.4",
|
||||
"psycopg",
|
||||
"psycopg[binary]",
|
||||
"django-axes",
|
||||
"django-celery-beat~=2.7.0",
|
||||
"django-rest-framework",
|
||||
|
|
@ -36,7 +36,7 @@ production = ["gunicorn~=23.0"]
|
|||
sentry = ["sentry-sdk~=2.0"]
|
||||
|
||||
[tool.uv]
|
||||
environments = ["sys_platform == "linux""]
|
||||
environments = ["sys_platform == 'linux'"]
|
||||
default-groups = ["test-tools"]
|
||||
|
||||
[tool.ruff]
|
||||
|
|
@ -68,7 +68,7 @@ django = ["django"]
|
|||
[tool.coverage.run]
|
||||
source = ["./src/newsreader/"]
|
||||
omit = [
|
||||
"**/tests/**"
|
||||
"**/tests/**",
|
||||
"**/migrations/**",
|
||||
"**/conf/**",
|
||||
"**/apps.py",
|
||||
|
|
@ -77,5 +77,5 @@ omit = [
|
|||
"**/urls.py",
|
||||
"**/wsgi.py",
|
||||
"**/celery.py",
|
||||
"**/__init__.py
|
||||
"**/__init__.py"
|
||||
]
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@ from pathlib import Path
|
|||
|
||||
from dotenv import load_dotenv
|
||||
|
||||
from newsreader.conf.utils import get_env, get_root_dir
|
||||
|
||||
|
||||
load_dotenv()
|
||||
|
||||
|
|
@ -15,16 +17,13 @@ except ImportError:
|
|||
DjangoIntegration = None
|
||||
|
||||
|
||||
BASE_DIR = Path(__file__).resolve().parent.parent.parent.parent
|
||||
BASE_DIR = get_root_dir()
|
||||
DJANGO_PROJECT_DIR = BASE_DIR / "src" / "newsreader"
|
||||
|
||||
# Quick-start development settings - unsuitable for production
|
||||
# See https://docs.djangoproject.com/en/2.2/howto/deployment/checklist/
|
||||
# SECURITY WARNING: don"t run with debug turned on in production!
|
||||
DEBUG = False
|
||||
|
||||
ALLOWED_HOSTS = ["127.0.0.1", "localhost"]
|
||||
INTERNAL_IPS = ["127.0.0.1", "localhost"]
|
||||
ALLOWED_HOSTS = get_env("ALLOWED_HOSTS", split=",", default=["127.0.0.1", "localhost"])
|
||||
INTERNAL_IPS = get_env("INTERNAL_IPS", split=",", default=["127.0.0.1", "localhost"])
|
||||
|
||||
# Application definition
|
||||
INSTALLED_APPS = [
|
||||
|
|
@ -48,7 +47,7 @@ INSTALLED_APPS = [
|
|||
"newsreader.news.collection",
|
||||
]
|
||||
|
||||
SECRET_KEY = os.environ["DJANGO_SECRET_KEY"]
|
||||
SECRET_KEY = get_env("DJANGO_SECRET_KEY", default="")
|
||||
|
||||
AUTHENTICATION_BACKENDS = [
|
||||
"axes.backends.AxesBackend",
|
||||
|
|
@ -73,11 +72,10 @@ FORM_RENDERER = "django.forms.renderers.TemplatesSetting"
|
|||
TEMPLATES = [
|
||||
{
|
||||
"BACKEND": "django.template.backends.django.DjangoTemplates",
|
||||
"DIRS": [os.path.join(DJANGO_PROJECT_DIR, "templates")],
|
||||
"DIRS": [DJANGO_PROJECT_DIR / "templates"],
|
||||
"APP_DIRS": True,
|
||||
"OPTIONS": {
|
||||
"context_processors": [
|
||||
"django.template.context_processors.debug",
|
||||
"django.template.context_processors.request",
|
||||
"django.contrib.auth.context_processors.auth",
|
||||
"django.contrib.messages.context_processors.messages",
|
||||
|
|
@ -88,16 +86,14 @@ TEMPLATES = [
|
|||
|
||||
WSGI_APPLICATION = "newsreader.wsgi.application"
|
||||
|
||||
# Database
|
||||
# https://docs.djangoproject.com/en/2.2/ref/settings/#databases
|
||||
DATABASES = {
|
||||
"default": {
|
||||
"ENGINE": "django.db.backends.postgresql",
|
||||
"HOST": os.environ["POSTGRES_HOST"],
|
||||
"PORT": os.environ["POSTGRES_PORT"],
|
||||
"NAME": os.environ["POSTGRES_DB"],
|
||||
"USER": os.environ["POSTGRES_USER"],
|
||||
"PASSWORD": os.environ["POSTGRES_PASSWORD"],
|
||||
"HOST": get_env("POSTGRES_HOST", default=""),
|
||||
"PORT": get_env("POSTGRES_PORT", default=""),
|
||||
"NAME": get_env("POSTGRES_DB", default=""),
|
||||
"USER": get_env("POSTGRES_USER", default=""),
|
||||
"PASSWORD": get_env("POSTGRES_PASSWORD", default=""),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -114,8 +110,6 @@ CACHES = {
|
|||
},
|
||||
}
|
||||
|
||||
# Logging
|
||||
# https://docs.djangoproject.com/en/2.2/topics/logging/#configuring-logging
|
||||
LOGGING = {
|
||||
"version": 1,
|
||||
"disable_existing_loggers": False,
|
||||
|
|
@ -169,8 +163,6 @@ LOGGING = {
|
|||
},
|
||||
}
|
||||
|
||||
# Password validation
|
||||
# https://docs.djangoproject.com/en/2.2/ref/settings/#auth-password-validators
|
||||
AUTH_PASSWORD_VALIDATORS = [
|
||||
{
|
||||
"NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator"
|
||||
|
|
@ -185,8 +177,6 @@ AUTH_USER_MODEL = "accounts.User"
|
|||
|
||||
LOGIN_REDIRECT_URL = "/"
|
||||
|
||||
# Internationalization
|
||||
# https://docs.djangoproject.com/en/2.2/topics/i18n/
|
||||
LANGUAGE_CODE = "en-us"
|
||||
|
||||
TIME_ZONE = "Europe/Amsterdam"
|
||||
|
|
@ -194,20 +184,33 @@ USE_I18N = True
|
|||
USE_L10N = True
|
||||
USE_TZ = True
|
||||
|
||||
# Static files (CSS, JavaScript, Images)
|
||||
# https://docs.djangoproject.com/en/2.2/howto/static-files/
|
||||
STATIC_URL = "/static/"
|
||||
STATIC_ROOT = os.path.join(BASE_DIR, "static")
|
||||
STATICFILES_DIRS = [os.path.join(DJANGO_PROJECT_DIR, "static")]
|
||||
STATIC_ROOT = BASE_DIR / "static"
|
||||
STATICFILES_DIRS = (
|
||||
DJANGO_PROJECT_DIR / "static",
|
||||
)
|
||||
|
||||
# https://docs.djangoproject.com/en/2.2/ref/settings/#std:setting-STATICFILES_FINDERS
|
||||
STATICFILES_FINDERS = [
|
||||
"django.contrib.staticfiles.finders.FileSystemFinder",
|
||||
"django.contrib.staticfiles.finders.AppDirectoriesFinder",
|
||||
]
|
||||
|
||||
# Email
|
||||
EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend"
|
||||
EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend"
|
||||
|
||||
DEFAULT_FROM_EMAIL = get_env(
|
||||
"EMAIL_DEFAULT_FROM", required=False, default="webmaster@localhost"
|
||||
)
|
||||
|
||||
EMAIL_HOST = get_env("EMAIL_HOST", required=False, default="localhost")
|
||||
EMAIL_PORT = get_env("EMAIL_PORT", cast=int, required=False, default=25)
|
||||
|
||||
EMAIL_HOST_USER = get_env("EMAIL_HOST_USER", required=False, default="")
|
||||
EMAIL_HOST_PASSWORD = get_env("EMAIL_HOST_PASSWORD", required=False, default="")
|
||||
|
||||
EMAIL_USE_TLS = get_env("EMAIL_USE_TLS", required=False, default=False)
|
||||
EMAIL_USE_SSL = get_env("EMAIL_USE_SSL", required=False, default=False)
|
||||
|
||||
|
||||
# Third party settings
|
||||
AXES_HANDLER = "axes.handlers.cache.AxesCacheHandler"
|
||||
|
|
@ -216,7 +219,6 @@ AXES_FAILURE_LIMIT = 5
|
|||
AXES_COOLOFF_TIME = 3 # in hours
|
||||
AXES_RESET_ON_SUCCESS = True
|
||||
|
||||
# TODO: verify parser works correctly
|
||||
REST_FRAMEWORK = {
|
||||
"DEFAULT_AUTHENTICATION_CLASSES": (
|
||||
"rest_framework.authentication.SessionAuthentication",
|
||||
|
|
@ -247,7 +249,7 @@ CELERY_BROKER_URL = "amqp://guest@rabbitmq:5672"
|
|||
|
||||
# Sentry
|
||||
SENTRY_CONFIG = {
|
||||
"dsn": os.environ.get("SENTRY_DSN"),
|
||||
"dsn": get_env("SENTRY_DSN", default="", required=False),
|
||||
"send_default_pii": False,
|
||||
"integrations": [DjangoIntegration(), CeleryIntegration()]
|
||||
if DjangoIntegration and CeleryIntegration
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
from .base import * # noqa: F403
|
||||
from .version import get_current_version
|
||||
from .utils import get_current_version
|
||||
|
||||
|
||||
DEBUG = True
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
from .base import * # noqa: F403
|
||||
from .version import get_current_version
|
||||
from .utils import get_current_version
|
||||
|
||||
|
||||
SECRET_KEY = "mv4&5#+)-=abz3^&1r^nk_ca6y54--p(4n4cg%z*g&rb64j%wl"
|
||||
|
|
@ -10,6 +10,11 @@ EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend"
|
|||
|
||||
INSTALLED_APPS += ["debug_toolbar", "django_extensions"] # noqa: F405
|
||||
|
||||
|
||||
TEMPLATES[0]["OPTIONS"]["context_processors"].append(
|
||||
"django.template.context_processors.debug",
|
||||
)
|
||||
|
||||
# Project settings
|
||||
VERSION = get_current_version()
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
from .base import * # noqa: F403
|
||||
from .version import get_current_version
|
||||
from .utils import get_current_version
|
||||
|
||||
|
||||
ALLOWED_HOSTS = ["django", "127.0.0.1"]
|
||||
DEBUG = True
|
||||
|
||||
INSTALLED_APPS += ["debug_toolbar", "django_extensions"] # noqa: F405
|
||||
|
||||
|
|
@ -16,7 +16,10 @@ LOGGING["loggers"].update( # noqa: F405
|
|||
|
||||
EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend"
|
||||
|
||||
DEBUG = True
|
||||
|
||||
TEMPLATES[0]["OPTIONS"]["context_processors"].append(
|
||||
"django.template.context_processors.debug",
|
||||
)
|
||||
|
||||
# Project settings
|
||||
VERSION = get_current_version()
|
||||
|
|
|
|||
|
|
@ -1,49 +1,20 @@
|
|||
import os
|
||||
|
||||
from newsreader.conf.utils import get_env
|
||||
|
||||
from .base import * # noqa: F403
|
||||
from .version import get_current_version
|
||||
from .utils import get_current_version
|
||||
|
||||
|
||||
DEBUG = False
|
||||
|
||||
SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https")
|
||||
|
||||
ALLOWED_HOSTS = ["127.0.0.1", "localhost", "rss.fudiggity.nl", "django"]
|
||||
|
||||
ADMINS = [
|
||||
("", email)
|
||||
for email in os.getenv("ADMINS", "").split(",")
|
||||
if os.environ.get("ADMINS")
|
||||
for email in get_env("ADMINS", split=",", required=False, default=[])
|
||||
]
|
||||
|
||||
TEMPLATES = [
|
||||
{
|
||||
"BACKEND": "django.template.backends.django.DjangoTemplates",
|
||||
"DIRS": [os.path.join(DJANGO_PROJECT_DIR, "templates")], # noqa: F405
|
||||
"APP_DIRS": True,
|
||||
"OPTIONS": {
|
||||
"context_processors": [
|
||||
"django.template.context_processors.request",
|
||||
"django.contrib.auth.context_processors.auth",
|
||||
"django.contrib.messages.context_processors.messages",
|
||||
]
|
||||
},
|
||||
}
|
||||
]
|
||||
|
||||
# Email
|
||||
EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend"
|
||||
DEFAULT_FROM_EMAIL = os.environ.get("EMAIL_DEFAULT_FROM", "webmaster@localhost")
|
||||
|
||||
EMAIL_HOST = os.environ.get("EMAIL_HOST", "localhost")
|
||||
EMAIL_PORT = os.environ.get("EMAIL_PORT", 25)
|
||||
|
||||
EMAIL_HOST_USER = os.environ.get("EMAIL_HOST_USER", "")
|
||||
EMAIL_HOST_PASSWORD = os.environ.get("EMAIL_HOST_PASSWORD", "")
|
||||
|
||||
EMAIL_USE_TLS = bool(os.environ.get("EMAIL_USE_TLS"))
|
||||
EMAIL_USE_SSL = bool(os.environ.get("EMAIL_USE_SSL"))
|
||||
|
||||
# Project settings
|
||||
VERSION = get_current_version(debug=False)
|
||||
ENVIRONMENT = "production"
|
||||
|
|
|
|||
85
src/newsreader/conf/utils.py
Normal file
85
src/newsreader/conf/utils.py
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
import logging
|
||||
import os
|
||||
import subprocess
|
||||
|
||||
from pathlib import Path
|
||||
from typing import Any, Iterable, Type
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def get_env(
|
||||
name: str,
|
||||
cast: Type = str,
|
||||
required: bool = True,
|
||||
default: Any = None,
|
||||
split: str = ""
|
||||
) -> Any:
|
||||
if cast is not str and split:
|
||||
raise TypeError(f"Split is not possible with {cast}")
|
||||
|
||||
value = os.getenv(name)
|
||||
|
||||
if not value:
|
||||
if required:
|
||||
logger.warning(f"Missing environment variable: {name}")
|
||||
|
||||
return default
|
||||
|
||||
bool_mapping = {"yes": True, "true": True, "false": False, "no": False}
|
||||
|
||||
if cast is bool:
|
||||
_value = bool_mapping.get(value.lower())
|
||||
|
||||
if not value:
|
||||
raise ValueError(f"Unknown boolean value: {_value}")
|
||||
|
||||
return _value
|
||||
|
||||
value = value if not cast else cast(value)
|
||||
return value if not split else value.split(split)
|
||||
|
||||
|
||||
def get_current_version(debug: bool = True) -> str:
|
||||
version = get_env("VERSION", required=False)
|
||||
|
||||
if version:
|
||||
return version
|
||||
|
||||
if debug:
|
||||
try:
|
||||
output = subprocess.check_output(
|
||||
["git", "show", "--no-patch", "--format=%H"], universal_newlines=True
|
||||
)
|
||||
return output.strip()
|
||||
except (subprocess.CalledProcessError, OSError):
|
||||
return ""
|
||||
|
||||
try:
|
||||
output = subprocess.check_output(
|
||||
["git", "describe", "--tags"], universal_newlines=True
|
||||
)
|
||||
return output.strip()
|
||||
except (subprocess.CalledProcessError, OSError):
|
||||
return ""
|
||||
|
||||
|
||||
ROOT_MARKERS = ("pyproject.toml", "package.json", "README.md", "CHANGELOG.md")
|
||||
|
||||
|
||||
def get_root_dir() -> Path:
|
||||
file = Path(__file__)
|
||||
return _traverse_dirs(file.parent, ROOT_MARKERS)
|
||||
|
||||
|
||||
def _traverse_dirs(path: Path, root_markers: Iterable[str]) -> Path:
|
||||
if path.parent == path:
|
||||
raise OSError("Root directory detected")
|
||||
|
||||
files = (file.name for file in path.iterdir())
|
||||
|
||||
if not any((marker for marker in root_markers if marker in files)):
|
||||
return _traverse_dirs(path.parent, root_markers)
|
||||
|
||||
return path
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
import os
|
||||
import subprocess
|
||||
|
||||
|
||||
def get_current_version(debug=True):
|
||||
version = os.environ.get("VERSION")
|
||||
|
||||
if version:
|
||||
return version
|
||||
|
||||
if debug:
|
||||
try:
|
||||
output = subprocess.check_output(
|
||||
["git", "show", "--no-patch", "--format=%H"], universal_newlines=True
|
||||
)
|
||||
return output.strip()
|
||||
except (subprocess.CalledProcessError, OSError):
|
||||
return ""
|
||||
|
||||
try:
|
||||
output = subprocess.check_output(
|
||||
["git", "describe", "--tags"], universal_newlines=True
|
||||
)
|
||||
return output.strip()
|
||||
except (subprocess.CalledProcessError, OSError):
|
||||
return ""
|
||||
Loading…
Add table
Add a link
Reference in a new issue