Patch release - no major changes

Testing fixes and stabilization
Test improvement / speed up
PostgreSQL side container for building
This commit is contained in:
Oliver Falk
2025-02-07 10:43:37 +00:00
parent cf5c058bfb
commit dcbd2c5df5
7 changed files with 65 additions and 7 deletions

1
.gitignore vendored
View File

@@ -21,3 +21,4 @@ falko_gravatar.jpg
dump_all*.sql dump_all*.sql
dist/ dist/
.env.local .env.local
tmp/

View File

@@ -4,9 +4,25 @@ image:
- "/bin/sh" - "/bin/sh"
- "-c" - "-c"
# Cache pip deps to speed up builds
cache:
paths:
- .pipcache
variables:
PIP_CACHE_DIR: .pipcache
test_and_coverage: test_and_coverage:
stage: build stage: build
coverage: "/^TOTAL.*\\s+(\\d+\\%)$/" coverage: "/^TOTAL.*\\s+(\\d+\\%)$/"
services:
- postgres:latest
variables:
POSTGRES_DB: django_db
POSTGRES_USER: django_user
POSTGRES_PASSWORD: django_password
POSTGRES_HOST: postgres
DATABASE_URL: "postgres://django_user:django_password@postgres/django_db"
PYTHONUNBUFFERED: 1
before_script: before_script:
- virtualenv -p python3 /tmp/.virtualenv - virtualenv -p python3 /tmp/.virtualenv
- source /tmp/.virtualenv/bin/activate - source /tmp/.virtualenv/bin/activate
@@ -24,8 +40,9 @@ test_and_coverage:
- echo "DEBUG = True" >> config_local.py - echo "DEBUG = True" >> config_local.py
- echo "from config import CACHES" >> config_local.py - echo "from config import CACHES" >> config_local.py
- echo "CACHES['default'] = CACHES['filesystem']" >> config_local.py - echo "CACHES['default'] = CACHES['filesystem']" >> config_local.py
- python manage.py sqldsn
- python manage.py collectstatic --noinput - python manage.py collectstatic --noinput
- coverage run --source . manage.py test -v3 - coverage run --source . manage.py test -v3 --noinput
- coverage report --fail-under=70 - coverage report --fail-under=70
- coverage html - coverage html
artifacts: artifacts:

View File

@@ -153,6 +153,19 @@ if "POSTGRESQL_DATABASE" in os.environ:
"HOST": "postgresql", "HOST": "postgresql",
} }
# CI/CD config has different naming
if "POSTGRES_DB" in os.environ:
DATABASES["default"] = { # pragma: no cover
"ENGINE": "django.db.backends.postgresql",
"NAME": os.environ["POSTGRES_DB"],
"USER": os.environ["POSTGRES_USER"],
"PASSWORD": os.environ["POSTGRES_PASSWORD"],
"HOST": os.environ["POSTGRES_HOST"],
"TEST": {
"NAME": os.environ["POSTGRES_DB"],
},
}
SESSION_SERIALIZER = "django.contrib.sessions.serializers.JSONSerializer" SESSION_SERIALIZER = "django.contrib.sessions.serializers.JSONSerializer"
USE_X_FORWARDED_HOST = True USE_X_FORWARDED_HOST = True

View File

@@ -395,8 +395,8 @@ class Tester(TestCase): # pylint: disable=too-many-public-methods
self.assertEqual(response["Content-Type"], "image/jpg", "Content type wrong!?") self.assertEqual(response["Content-Type"], "image/jpg", "Content type wrong!?")
self.assertEqual( self.assertEqual(
response.content, bytes(response.content),
self.user.photo_set.first().data, bytes(self.user.photo_set.first().data),
"raw_image should return the same content as if we\ "raw_image should return the same content as if we\
read it directly from the DB", read it directly from the DB",
) )

View File

@@ -42,6 +42,7 @@ MIDDLEWARE = [
"django.contrib.auth.middleware.AuthenticationMiddleware", "django.contrib.auth.middleware.AuthenticationMiddleware",
"django.contrib.messages.middleware.MessageMiddleware", "django.contrib.messages.middleware.MessageMiddleware",
"django.middleware.clickjacking.XFrameOptionsMiddleware", "django.middleware.clickjacking.XFrameOptionsMiddleware",
"django.middleware.locale.LocaleMiddleware",
] ]
ROOT_URLCONF = "ivatar.urls" ROOT_URLCONF = "ivatar.urls"
@@ -49,7 +50,7 @@ ROOT_URLCONF = "ivatar.urls"
TEMPLATES = [ TEMPLATES = [
{ {
"BACKEND": "django.template.backends.django.DjangoTemplates", "BACKEND": "django.template.backends.django.DjangoTemplates",
"DIRS": [], "DIRS": [os.path.join(BASE_DIR, "templates")],
"APP_DIRS": True, "APP_DIRS": True,
"OPTIONS": { "OPTIONS": {
"context_processors": [ "context_processors": [
@@ -57,7 +58,9 @@ TEMPLATES = [
"django.template.context_processors.request", "django.template.context_processors.request",
"django.contrib.auth.context_processors.auth", "django.contrib.auth.context_processors.auth",
"django.contrib.messages.context_processors.messages", "django.contrib.messages.context_processors.messages",
"django.template.context_processors.i18n",
], ],
"debug": DEBUG,
}, },
}, },
] ]
@@ -72,6 +75,7 @@ DATABASES = {
"default": { "default": {
"ENGINE": "django.db.backends.sqlite3", "ENGINE": "django.db.backends.sqlite3",
"NAME": os.path.join(BASE_DIR, "db.sqlite3"), "NAME": os.path.join(BASE_DIR, "db.sqlite3"),
"ATOMIC_REQUESTS": True,
} }
} }
@@ -85,6 +89,9 @@ AUTH_PASSWORD_VALIDATORS = [
}, },
{ {
"NAME": "django.contrib.auth.password_validation.MinimumLengthValidator", # noqa "NAME": "django.contrib.auth.password_validation.MinimumLengthValidator", # noqa
"OPTIONS": {
"min_length": 6,
},
}, },
{ {
"NAME": "django.contrib.auth.password_validation.CommonPasswordValidator", # noqa "NAME": "django.contrib.auth.password_validation.CommonPasswordValidator", # noqa
@@ -94,6 +101,26 @@ AUTH_PASSWORD_VALIDATORS = [
}, },
] ]
# Password Hashing (more secure)
PASSWORD_HASHERS = [
# This isn't working in older Python environments
# "django.contrib.auth.hashers.Argon2PasswordHasher",
"django.contrib.auth.hashers.PBKDF2PasswordHasher",
"django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher",
]
# Security Settings
SECURE_BROWSER_XSS_FILTER = True
SECURE_CONTENT_TYPE_NOSNIFF = True
X_FRAME_OPTIONS = "DENY"
CSRF_COOKIE_SECURE = not DEBUG
SESSION_COOKIE_SECURE = not DEBUG
if not DEBUG:
SECURE_SSL_REDIRECT = True
SECURE_HSTS_SECONDS = 31536000 # 1 year
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
SECURE_HSTS_PRELOAD = True
# Internationalization # Internationalization
# https://docs.djangoproject.com/en/2.0/topics/i18n/ # https://docs.djangoproject.com/en/2.0/topics/i18n/
@@ -116,4 +143,4 @@ STATIC_ROOT = os.path.join(BASE_DIR, "static")
DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField" DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
from config import * # pylint: disable=wildcard-import,wrong-import-position,unused-wildcard-import from config import * # pylint: disable=wildcard-import,wrong-import-position,unused-wildcard-import # noqa

View File

@@ -1,7 +1,7 @@
autopep8 autopep8
bcrypt bcrypt
defusedxml defusedxml
Django Django>=5.1
django-anymail[mailgun] django-anymail[mailgun]
django-auth-ldap django-auth-ldap
django-bootstrap4 django-bootstrap4

View File

@@ -1,6 +1,6 @@
{% load i18n %} {% load i18n %}
{% block topbar_base %} {% block topbar_base %}
<nav class="navbar navbarlibravatar"> <nav class="navbar navbarlibravatar" {% if site_name == 'Libravatar DEVELOPMENT' %}style="background: linear-gradient(135deg, #aa0f1d 0%, #fd002e 100%);"{% endif %}>
<div class="container-fluid"> <div class="container-fluid">
<div class="navbar-header"> <div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false"> <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false">