From 75fe108bbc1fb118cbf633bba7c9000f94ed41f0 Mon Sep 17 00:00:00 2001 From: Daniel Mursa Date: Fri, 20 Feb 2026 11:59:42 +0100 Subject: [PATCH 1/9] :zap: [#28] Improve CI tests --- .github/workflows/ci.yml | 53 +++++++++++++++++++++++++++++--------- bin/check_changed_files.sh | 20 ++++++++++++++ 2 files changed, 61 insertions(+), 12 deletions(-) create mode 100644 bin/check_changed_files.sh diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 95d8b21e..163c5a0f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,6 +18,28 @@ env: jobs: + # determine changed files to decide if certain jobs can be skipped or not + changed-files: + runs-on: ubuntu-latest # windows-latest | macos-latest + name: Determine changed files + steps: + + - uses: actions/checkout@v6 + with: + fetch-depth: 0 + + - name: Get changed PY files + id: changed-py-files + run: bin/check_changed_files.sh ^src/.*\.py$ + + - name: Get changed requirements files + id: changed-requirements + run: bin/check_changed_files.sh ^requirements/.*\.txt$ + + outputs: + changed-py-files: ${{ steps.changed-py-files.outputs.any_changed }} + changed-requirements: ${{ steps.changed-requirements.outputs.any_changed }} + setup: name: Set up the build variables runs-on: ubuntu-latest @@ -45,27 +67,36 @@ jobs: echo "git_hash=${GITHUB_SHA}" >> $GITHUB_OUTPUT tests: - name: Run the Django test suite + name: Tests (PG ${{ matrix.postgres }}) runs-on: ubuntu-latest + needs: + - changed-files + + # only run tests if source files have changed (e.g. skip for PRs that only update docs) + if: ${{ needs.changed-files.outputs.changed-py-files == 'true'|| needs.changed-files.outputs.changed-requirements == 'true'|| github.event_name == 'push' }} + + strategy: + matrix: + postgres: ["14", "15", "16", "17"] + use_pooling: [false] + include: + - postgres: "17" + use_pooling: true services: postgres: - image: postgres:17 + image: postgres:${{ matrix.postgres }} env: POSTGRES_HOST_AUTH_METHOD: trust ports: - 5432:5432 - # Needed because the postgres container does not provide a healthcheck - options: >- - --name postgres - --health-cmd pg_isready - --health-interval 10s - --health-timeout 5s - --health-retries 5 + # needed because the postgres container does not provide a healthcheck + options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5 redis: - image: redis:6 + image: redis:8 ports: - 6379:6379 + steps: - uses: actions/checkout@v4 - name: Set up backend environment @@ -83,8 +114,6 @@ jobs: coverage run src/manage.py test src env: SECRET_KEY: dummy - DB_USER: postgres - DB_PASSWORD: '' - name: Publish coverage report uses: codecov/codecov-action@v4 diff --git a/bin/check_changed_files.sh b/bin/check_changed_files.sh new file mode 100644 index 00000000..c7eb5213 --- /dev/null +++ b/bin/check_changed_files.sh @@ -0,0 +1,20 @@ +#!/bin/sh + +# Determine the base ref or fallback to the last commit +if [ -n "$GITHUB_BASE_REF" ]; then + BASE_REF="origin/$GITHUB_BASE_REF" +else + BASE_REF="HEAD^" # Compare with the previous commit on main branch +fi + +git fetch --all + +# Get the list of files that were changed between the base branch and the current commit +CHANGED_FILES=$(git diff --name-only $BASE_REF...HEAD) +if echo "$CHANGED_FILES" | grep -q "$1"; then + echo "Files were changed!" + echo "any_changed=true" >> $GITHUB_OUTPUT +else + echo "No changes detected" + echo "any_changed=false" >> $GITHUB_OUTPUT +fi From 060a70169c0ee1dca53866d84cca22848f88b260 Mon Sep 17 00:00:00 2001 From: Daniel Mursa Date: Fri, 20 Feb 2026 12:11:40 +0100 Subject: [PATCH 2/9] :sparkles: [#28] Add check-envvar-docs action and open-api-ci actions --- .github/workflows/ci.yml | 80 +++++++++++++++++++--------------------- src/opendms/conf/ci.py | 46 ++++++++++++++--------- 2 files changed, 66 insertions(+), 60 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 163c5a0f..87e75e22 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -144,55 +144,51 @@ jobs: npx playwright install npm run test - # docs: - # name: Build and check documentation - # runs-on: ubuntu-latest - - # steps: - # - uses: actions/checkout@v4 - # - uses: maykinmedia/setup-django-backend@v1.3 - # with: - # python-version: '3.14' - # setup-node: 'no' - # # apt-packages: 'gettext postgresql-client' # the default - - # - name: Build and test docs - # run: | - # export OPENSSL_CONF=$(pwd)/openssl.conf - # pytest check_sphinx.py -v --tb=auto - # working-directory: docs - - docker_build: - name: Build Docker image + check-envvar-docs: runs-on: ubuntu-latest - outputs: - image_tag: ${{ steps.image_build.outputs.image_tag }} - - needs: - - setup + name: Documentation build steps: - uses: actions/checkout@v4 + - name: Set up backend environment + uses: maykinmedia/setup-django-backend@v1.3 + with: + python-version: '3.12' + apt-packages: "libgdal-dev gdal-bin" + setup-node: false - - name: Build the production Docker image - id: image_build + - name: Generate environment variable documentation using OAf and check if it was updated run: | - image_tag="$IMAGE_NAME:$RELEASE_VERSION" - echo "image_tag=${image_tag}" >> $GITHUB_OUTPUT - docker build . \ - --tag $image_tag \ - --build-arg COMMIT_HASH=${{ needs.setup.outputs.git_hash }} \ - --build-arg RELEASE=${{ needs.setup.outputs.tag }} \ - env: - RELEASE_VERSION: ${{ needs.setup.outputs.tag }} + bin/generate_envvar_docs.sh + changes=$(git diff docs/installation/config/env_configuration.rst) + if [ ! -z "$changes" ]; then + echo $changes + echo "Please update the environment documentation by running \`bin/generate_envvar_docs.sh\`" + exit 1 + fi + + store-reusable-workflow-vars: + name: create values which can be passed through a reusable workflow + runs-on: ubuntu-latest + outputs: + image-name: ${{ steps.image-name.outputs.image-name }} - - run: docker image save -o image.tar $IMAGE_NAME:${{ needs.setup.outputs.tag }} - - name: Store image artifact - uses: actions/upload-artifact@v4 - with: - name: docker-image - path: image.tar - retention-days: 1 + steps: + - run: echo "image-name=$IMAGE_NAME" >> $GITHUB_OUTPUT + name: 'Store the docker image name' + id: image-name + + open-api-ci: + uses: maykinmedia/open-api-workflows/.github/workflows/ci.yml@v6 + needs: + - store-reusable-workflow-vars + with: + apt-packages: 'graphviz graphviz-dev' + main-branch: 'master' + run-docs: true + python-version: '3.14' + docker-image-name: ${{ needs.store-reusable-workflow-vars.outputs.image-name }} + django-settings-module: opendms.conf.ci # docker_push: # needs: diff --git a/src/opendms/conf/ci.py b/src/opendms/conf/ci.py index daccf13a..fab8ec1c 100644 --- a/src/opendms/conf/ci.py +++ b/src/opendms/conf/ci.py @@ -1,36 +1,44 @@ +""" +Continuous integration settings module. +""" + import os import warnings -os.environ.setdefault("DEBUG", "yes") -os.environ.setdefault("ALLOWED_HOSTS", "*") -os.environ.setdefault( - "SECRET_KEY", - "django-insecure-kex=ipoau_q(o_w=6bd8q6_sb2#xgkdbknq4m&hl2jtx(7#$n_", -) -os.environ.setdefault("IS_HTTPS", "no") -os.environ.setdefault("VERSION_TAG", "dev") +# Importing the idna module has an IO side-effect to load the data, which is a rather +# big file. Pre-loading this in the settings file populates the python module cache, +# preventing flakiness in hypothesis tests that hit this code path. +import idna # noqa: F401 +from open_api_framework.conf.utils import mute_logging -os.environ.setdefault("DB_NAME", "opendms") -os.environ.setdefault("DB_USER", "opendms") -os.environ.setdefault("DB_PASSWORD", "opendms") - -os.environ.setdefault("ENVIRONMENT", "development") +os.environ.setdefault("IS_HTTPS", "no") +os.environ.setdefault("SECRET_KEY", "dummy") +# Do not log requests in CI/tests: +# +# * overhead making tests slower +# * it conflicts with SimpleTestCase in some cases when the run-time configuration is +# looked up from the django-solo model +os.environ.setdefault("LOG_REQUESTS", "no") os.environ.setdefault("OTEL_SDK_DISABLED", "true") +os.environ.setdefault("OTEL_SERVICE_NAME", "opendms-ci") from .base import * # noqa isort:skip -from .base import CACHES CACHES.update( { - "default": { - "BACKEND": "django.core.cache.backends.locmem.LocMemCache", - "LOCATION": "default", - }, + "default": {"BACKEND": "django.core.cache.backends.locmem.LocMemCache"}, # See: https://github.com/jazzband/django-axes/blob/master/docs/configuration.rst#cache-problems "axes": {"BACKEND": "django.core.cache.backends.dummy.DummyCache"}, + "oidc": {"BACKEND": "django.core.cache.backends.locmem.LocMemCache"}, } ) +# shut up logging +mute_logging(LOGGING) + +# don't spend time on password hashing in tests/user factories +PASSWORD_HASHERS = ["django.contrib.auth.hashers.PBKDF2PasswordHasher"] + ENVIRONMENT = "CI" @@ -47,3 +55,5 @@ RuntimeWarning, r"django\.db\.models\.fields", ) + +NOTIFICATIONS_DISABLED = True From 79f4a013bf01d09080b9109216179ea5061210f3 Mon Sep 17 00:00:00 2001 From: Daniel Mursa Date: Fri, 20 Feb 2026 12:38:57 +0100 Subject: [PATCH 3/9] :arrow_up: [#28] Add open-api-framework[cors,markup,csp,commonground,structlog] --- requirements/base.in | 3 +- requirements/base.txt | 148 +++++++++++++++++++++++++++-- requirements/ci.txt | 215 +++++++++++++++++++++++++++++++++++++++++- requirements/dev.txt | 204 ++++++++++++++++++++++++++++++++++++++- 4 files changed, 552 insertions(+), 18 deletions(-) diff --git a/requirements/base.in b/requirements/base.in index 3bf694b0..f6b39a3e 100644 --- a/requirements/base.in +++ b/requirements/base.in @@ -16,8 +16,7 @@ mozilla-django-oidc-db josepy # API libraries -djangorestframework-camel-case -open-api-framework +open-api-framework[cors,markup,csp,commonground,structlog] # WSGI servers & monitoring - production oriented uwsgi diff --git a/requirements/base.txt b/requirements/base.txt index 514152e9..8ad84404 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -1,24 +1,37 @@ # This file was autogenerated by uv via the following command: # ./bin/compile_dependencies.sh +amqp==5.3.1 + # via kombu annotated-doc==0.0.4 # via typer annotated-types==0.7.0 # via pydantic +ape-pie==0.2.0 + # via + # commonground-api-common + # notifications-api-common + # zgw-consumers asgiref==3.11.1 # via # django # django-axes + # django-cors-headers + # django-structlog attrs==25.4.0 # via # glom # jsonschema # referencing +billiard==4.2.4 + # via celery boltons==25.0.0 # via # face # glom cbor2==5.8.0 # via webauthn +celery==5.6.2 + # via notifications-api-common certifi==2026.1.4 # via # elastic-apm @@ -29,9 +42,23 @@ cffi==2.0.0 charset-normalizer==3.4.4 # via requests click==8.3.1 - # via typer + # via + # celery + # click-didyoumean + # click-plugins + # click-repl + # typer +click-didyoumean==0.3.1 + # via celery +click-plugins==1.1.1.2 + # via celery +click-repl==0.3.0 + # via celery +commonground-api-common==2.11.0 + # via open-api-framework cryptography==46.0.5 # via + # django-simple-certmanager # josepy # mozilla-django-oidc # pyopenssl @@ -39,32 +66,45 @@ cryptography==46.0.5 django==5.2.11 # via # -r requirements/base.in + # commonground-api-common # django-admin-index # django-appconf # django-axes + # django-cors-headers + # django-csp # django-filter # django-formtools # django-health-check # django-hijack # django-jsonform # django-log-outgoing-requests + # django-markup # django-otp # django-phonenumber-field + # django-privates # django-redis + # django-relativedelta + # django-rest-framework-condition # django-rosetta + # django-sendfile2 # django-sessionprofile # django-setup-configuration + # django-simple-certmanager # django-solo + # django-structlog # django-two-factor-auth # django-upgrade-check # djangorestframework + # drf-nested-routers # drf-spectacular # drf-spectacular-sidecar # maykin-2fa # maykin-common # mozilla-django-oidc # mozilla-django-oidc-db + # notifications-api-common # open-api-framework + # zgw-consumers django-admin-index==4.0.0 # via # -r requirements/base.in @@ -77,8 +117,14 @@ django-axes==8.3.1 # -r requirements/base.in # maykin-common # open-api-framework -django-filter==25.1 +django-cors-headers==4.9.0 # via open-api-framework +django-csp==4.0 + # via open-api-framework +django-filter==25.1 + # via + # commonground-api-common + # open-api-framework django-formtools==2.5.1 # via django-two-factor-auth django-health-check==4.0.0 @@ -86,13 +132,17 @@ django-health-check==4.0.0 django-hijack==3.7.6 # via -r requirements/base.in django-ipware==7.0.1 - # via django-axes + # via + # django-axes + # django-structlog django-jsonform==2.23.2 # via # mozilla-django-oidc-db # open-api-framework django-log-outgoing-requests==0.7.1 # via open-api-framework +django-markup==1.10 + # via open-api-framework django-ordered-model==3.7.4 # via django-admin-index django-otp==1.7.0 @@ -101,32 +151,57 @@ django-otp==1.7.0 # maykin-2fa django-phonenumber-field==8.4.0 # via django-two-factor-auth +django-privates==3.1.1 + # via django-simple-certmanager django-redis==6.0.0 # via -r requirements/base.in +django-relativedelta==2.0.0 + # via zgw-consumers +django-rest-framework-condition==0.1.1 + # via commonground-api-common django-rosetta==0.10.3 # via -r requirements/base.in +django-sendfile2==0.7.2 + # via django-privates django-sessionprofile==3.0.0 # via open-api-framework django-setup-configuration==0.11.0 # via open-api-framework +django-simple-certmanager==2.5.0 + # via zgw-consumers django-solo==2.5.1 - # via django-log-outgoing-requests + # via + # commonground-api-common + # django-log-outgoing-requests + # notifications-api-common + # zgw-consumers +django-structlog==10.0.0 + # via open-api-framework django-two-factor-auth==1.18.1 # via maykin-2fa django-upgrade-check==1.1.0 # via open-api-framework djangorestframework==3.16.1 # via + # commonground-api-common + # drf-nested-routers # drf-spectacular + # notifications-api-common # open-api-framework djangorestframework-camel-case==1.4.2 - # via -r requirements/base.in + # via + # commonground-api-common + # notifications-api-common dnspython==2.8.0 # via django-health-check docutils==0.22.4 # via django-setup-configuration +drf-nested-routers==0.95.0 + # via commonground-api-common drf-spectacular==0.29.0 - # via open-api-framework + # via + # commonground-api-common + # open-api-framework drf-spectacular-sidecar==2026.1.1 # via drf-spectacular ecs-logging==2.3.0 @@ -137,6 +212,8 @@ elastic-apm==6.25.0 # open-api-framework face==26.0.0 # via glom +furl==2.1.4 + # via ape-pie glom==25.12.0 # via mozilla-django-oidc-db googleapis-common-protos==1.72.0 @@ -151,12 +228,16 @@ importlib-metadata==8.7.1 # via opentelemetry-api inflection==0.5.1 # via drf-spectacular +iso639-lang==2.6.3 + # via commonground-api-common josepy==2.2.0 # via -r requirements/base.in jsonschema==4.26.0 # via drf-spectacular jsonschema-specifications==2025.9.1 # via jsonschema +kombu==5.6.2 + # via celery markdown-it-py==4.0.0 # via rich maykin-2fa==2.0.1 @@ -174,6 +255,8 @@ mozilla-django-oidc-db==1.1.1 # via # -r requirements/base.in # open-api-framework +notifications-api-common==0.10.1 + # via commonground-api-common open-api-framework==0.13.4 # via -r requirements/base.in opentelemetry-api==1.39.1 @@ -252,12 +335,21 @@ opentelemetry-util-http==0.60b1 # opentelemetry-instrumentation-django # opentelemetry-instrumentation-requests # opentelemetry-instrumentation-wsgi +orderedmultidict==1.0.2 + # via furl +oyaml==1.0 + # via commonground-api-common packaging==26.0 - # via opentelemetry-instrumentation + # via + # django-csp + # kombu + # opentelemetry-instrumentation pillow==12.1.1 # via -r requirements/base.in polib==1.2.0 # via django-rosetta +prompt-toolkit==3.0.52 + # via click-repl protobuf==6.33.5 # via # googleapis-common-protos @@ -285,9 +377,16 @@ pydantic-settings==2.13.0 pygments==2.19.2 # via rich pyjwt==2.11.0 - # via mozilla-django-oidc + # via + # commonground-api-common + # mozilla-django-oidc + # zgw-consumers pyopenssl==25.3.0 # via webauthn +python-dateutil==2.9.0.post0 + # via + # celery + # django-relativedelta python-decouple==3.8 # via # maykin-common @@ -302,6 +401,7 @@ python-ipware==3.0.0 pyyaml==6.0.3 # via # drf-spectacular + # oyaml # pydantic-settings qrcode==8.2 # via django-two-factor-auth @@ -313,12 +413,15 @@ referencing==0.37.0 # jsonschema-specifications requests==2.32.5 # via + # ape-pie + # commonground-api-common # django-log-outgoing-requests # django-rosetta # maykin-common # mozilla-django-oidc # open-api-framework # opentelemetry-exporter-otlp-proto-http + # zgw-consumers rich==14.3.2 # via typer rpds-py==0.30.0 @@ -332,11 +435,21 @@ semantic-version==2.10.0 sentry-sdk==2.52.0 # via # -r requirements/base.in + # commonground-api-common # open-api-framework shellingham==1.5.4 # via typer +six==1.17.0 + # via + # furl + # orderedmultidict + # python-dateutil sqlparse==0.5.5 # via django +structlog==25.5.0 + # via + # django-structlog + # open-api-framework typer==0.23.1 # via maykin-common typing-extensions==4.15.0 @@ -348,13 +461,21 @@ typing-extensions==4.15.0 # opentelemetry-exporter-otlp-proto-http # opentelemetry-sdk # opentelemetry-semantic-conventions + # psycopg # pydantic # pydantic-core + # pyopenssl + # referencing # typing-inspection + # zgw-consumers typing-inspection==0.4.2 # via # pydantic # pydantic-settings +tzdata==2025.3 + # via kombu +tzlocal==5.3.1 + # via celery uritemplate==4.2.0 # via drf-spectacular urllib3==2.6.3 @@ -364,6 +485,13 @@ urllib3==2.6.3 # sentry-sdk uwsgi==2.0.31 # via -r requirements/base.in +vine==5.1.0 + # via + # amqp + # celery + # kombu +wcwidth==0.6.0 + # via prompt-toolkit webauthn==2.7.1 # via django-two-factor-auth wrapt==1.17.3 @@ -372,5 +500,9 @@ wrapt==1.17.3 # opentelemetry-instrumentation # opentelemetry-instrumentation-dbapi # opentelemetry-instrumentation-redis +zgw-consumers==1.2.0 + # via + # commonground-api-common + # notifications-api-common zipp==3.23.0 # via importlib-metadata diff --git a/requirements/ci.txt b/requirements/ci.txt index aebb2537..31ad0cd8 100644 --- a/requirements/ci.txt +++ b/requirements/ci.txt @@ -1,5 +1,10 @@ # This file was autogenerated by uv via the following command: # ./bin/compile_dependencies.sh +amqp==5.3.1 + # via + # -c requirements/base.txt + # -r requirements/base.txt + # kombu annotated-doc==0.0.4 # via # -c requirements/base.txt @@ -10,12 +15,21 @@ annotated-types==0.7.0 # -c requirements/base.txt # -r requirements/base.txt # pydantic +ape-pie==0.2.0 + # via + # -c requirements/base.txt + # -r requirements/base.txt + # commonground-api-common + # notifications-api-common + # zgw-consumers asgiref==3.11.1 # via # -c requirements/base.txt # -r requirements/base.txt # django # django-axes + # django-cors-headers + # django-structlog attrs==25.4.0 # via # -c requirements/base.txt @@ -25,6 +39,11 @@ attrs==25.4.0 # referencing beautifulsoup4==4.14.3 # via webtest +billiard==4.2.4 + # via + # -c requirements/base.txt + # -r requirements/base.txt + # celery boltons==25.0.0 # via # -c requirements/base.txt @@ -36,6 +55,11 @@ cbor2==5.8.0 # -c requirements/base.txt # -r requirements/base.txt # webauthn +celery==5.6.2 + # via + # -c requirements/base.txt + # -r requirements/base.txt + # notifications-api-common certifi==2026.1.4 # via # -c requirements/base.txt @@ -57,13 +81,37 @@ click==8.3.1 # via # -c requirements/base.txt # -r requirements/base.txt + # celery + # click-didyoumean + # click-plugins + # click-repl # typer +click-didyoumean==0.3.1 + # via + # -c requirements/base.txt + # -r requirements/base.txt + # celery +click-plugins==1.1.1.2 + # via + # -c requirements/base.txt + # -r requirements/base.txt + # celery +click-repl==0.3.0 + # via + # -c requirements/base.txt + # -r requirements/base.txt + # celery +commonground-api-common==2.11.0 + # via + # -c requirements/base.txt + # -r requirements/base.txt coverage==7.13.4 # via -r requirements/test-tools.in cryptography==46.0.5 # via # -c requirements/base.txt # -r requirements/base.txt + # django-simple-certmanager # josepy # mozilla-django-oidc # pyopenssl @@ -74,32 +122,45 @@ django==5.2.11 # via # -c requirements/base.txt # -r requirements/base.txt + # commonground-api-common # django-admin-index # django-appconf # django-axes + # django-cors-headers + # django-csp # django-filter # django-formtools # django-health-check # django-hijack # django-jsonform # django-log-outgoing-requests + # django-markup # django-otp # django-phonenumber-field + # django-privates # django-redis + # django-relativedelta + # django-rest-framework-condition # django-rosetta + # django-sendfile2 # django-sessionprofile # django-setup-configuration + # django-simple-certmanager # django-solo + # django-structlog # django-two-factor-auth # django-upgrade-check # djangorestframework + # drf-nested-routers # drf-spectacular # drf-spectacular-sidecar # maykin-2fa # maykin-common # mozilla-django-oidc # mozilla-django-oidc-db + # notifications-api-common # open-api-framework + # zgw-consumers django-admin-index==4.0.0 # via # -c requirements/base.txt @@ -115,10 +176,19 @@ django-axes==8.3.1 # -c requirements/base.txt # -r requirements/base.txt # open-api-framework +django-cors-headers==4.9.0 + # via + # -c requirements/base.txt + # -r requirements/base.txt +django-csp==4.0 + # via + # -c requirements/base.txt + # -r requirements/base.txt django-filter==25.1 # via # -c requirements/base.txt # -r requirements/base.txt + # commonground-api-common # open-api-framework django-formtools==2.5.1 # via @@ -137,6 +207,7 @@ django-ipware==7.0.1 # via # -c requirements/base.txt # -r requirements/base.txt + # django-structlog django-jsonform==2.23.2 # via # -c requirements/base.txt @@ -148,6 +219,10 @@ django-log-outgoing-requests==0.7.1 # -c requirements/base.txt # -r requirements/base.txt # open-api-framework +django-markup==1.10 + # via + # -c requirements/base.txt + # -r requirements/base.txt django-ordered-model==3.7.4 # via # -c requirements/base.txt @@ -164,14 +239,34 @@ django-phonenumber-field==8.4.0 # -c requirements/base.txt # -r requirements/base.txt # django-two-factor-auth +django-privates==3.1.1 + # via + # -c requirements/base.txt + # -r requirements/base.txt + # django-simple-certmanager django-redis==6.0.0 # via # -c requirements/base.txt # -r requirements/base.txt +django-relativedelta==2.0.0 + # via + # -c requirements/base.txt + # -r requirements/base.txt + # zgw-consumers +django-rest-framework-condition==0.1.1 + # via + # -c requirements/base.txt + # -r requirements/base.txt + # commonground-api-common django-rosetta==0.10.3 # via # -c requirements/base.txt # -r requirements/base.txt +django-sendfile2==0.7.2 + # via + # -c requirements/base.txt + # -r requirements/base.txt + # django-privates django-sessionprofile==3.0.0 # via # -c requirements/base.txt @@ -182,11 +277,23 @@ django-setup-configuration==0.11.0 # -c requirements/base.txt # -r requirements/base.txt # open-api-framework +django-simple-certmanager==2.5.0 + # via + # -c requirements/base.txt + # -r requirements/base.txt + # zgw-consumers django-solo==2.5.1 # via # -c requirements/base.txt # -r requirements/base.txt + # commonground-api-common # django-log-outgoing-requests + # notifications-api-common + # zgw-consumers +django-structlog==10.0.0 + # via + # -c requirements/base.txt + # -r requirements/base.txt django-two-factor-auth==1.18.1 # via # -c requirements/base.txt @@ -203,12 +310,17 @@ djangorestframework==3.16.1 # via # -c requirements/base.txt # -r requirements/base.txt + # commonground-api-common + # drf-nested-routers # drf-spectacular + # notifications-api-common # open-api-framework djangorestframework-camel-case==1.4.2 # via # -c requirements/base.txt # -r requirements/base.txt + # commonground-api-common + # notifications-api-common dnspython==2.8.0 # via # -c requirements/base.txt @@ -219,10 +331,16 @@ docutils==0.22.4 # -c requirements/base.txt # -r requirements/base.txt # django-setup-configuration +drf-nested-routers==0.95.0 + # via + # -c requirements/base.txt + # -r requirements/base.txt + # commonground-api-common drf-spectacular==0.29.0 # via # -c requirements/base.txt # -r requirements/base.txt + # commonground-api-common # open-api-framework drf-spectacular-sidecar==2026.1.1 # via @@ -250,6 +368,11 @@ faker==40.4.0 # via factory-boy freezegun==1.5.5 # via -r requirements/test-tools.in +furl==2.1.4 + # via + # -c requirements/base.txt + # -r requirements/base.txt + # ape-pie glom==25.12.0 # via # -c requirements/base.txt @@ -281,6 +404,11 @@ inflection==0.5.1 # -c requirements/base.txt # -r requirements/base.txt # drf-spectacular +iso639-lang==2.6.3 + # via + # -c requirements/base.txt + # -r requirements/base.txt + # commonground-api-common josepy==2.2.0 # via # -c requirements/base.txt @@ -295,8 +423,11 @@ jsonschema-specifications==2025.9.1 # -c requirements/base.txt # -r requirements/base.txt # jsonschema -legacy-cgi==2.6.4 - # via webob +kombu==5.6.2 + # via + # -c requirements/base.txt + # -r requirements/base.txt + # celery lxml==6.0.2 # via pyquery markdown-it-py==4.0.0 @@ -328,6 +459,11 @@ mozilla-django-oidc-db==1.1.1 # -c requirements/base.txt # -r requirements/base.txt # open-api-framework +notifications-api-common==0.10.1 + # via + # -c requirements/base.txt + # -r requirements/base.txt + # commonground-api-common open-api-framework==0.13.4 # via # -c requirements/base.txt @@ -446,10 +582,22 @@ opentelemetry-util-http==0.60b1 # opentelemetry-instrumentation-django # opentelemetry-instrumentation-requests # opentelemetry-instrumentation-wsgi +orderedmultidict==1.0.2 + # via + # -c requirements/base.txt + # -r requirements/base.txt + # furl +oyaml==1.0 + # via + # -c requirements/base.txt + # -r requirements/base.txt + # commonground-api-common packaging==26.0 # via # -c requirements/base.txt # -r requirements/base.txt + # django-csp + # kombu # opentelemetry-instrumentation pillow==12.1.1 # via @@ -460,6 +608,11 @@ polib==1.2.0 # -c requirements/base.txt # -r requirements/base.txt # django-rosetta +prompt-toolkit==3.0.52 + # via + # -c requirements/base.txt + # -r requirements/base.txt + # click-repl protobuf==6.33.5 # via # -c requirements/base.txt @@ -515,7 +668,9 @@ pyjwt==2.11.0 # via # -c requirements/base.txt # -r requirements/base.txt + # commonground-api-common # mozilla-django-oidc + # zgw-consumers pyopenssl==25.3.0 # via # -c requirements/base.txt @@ -524,7 +679,12 @@ pyopenssl==25.3.0 pyquery==2.0.1 # via -r requirements/test-tools.in python-dateutil==2.9.0.post0 - # via freezegun + # via + # -c requirements/base.txt + # -r requirements/base.txt + # celery + # django-relativedelta + # freezegun python-decouple==3.8 # via # -c requirements/base.txt @@ -547,6 +707,7 @@ pyyaml==6.0.3 # -c requirements/base.txt # -r requirements/base.txt # drf-spectacular + # oyaml # pydantic-settings qrcode==8.2 # via @@ -568,11 +729,14 @@ requests==2.32.5 # via # -c requirements/base.txt # -r requirements/base.txt + # ape-pie + # commonground-api-common # django-log-outgoing-requests # django-rosetta # mozilla-django-oidc # open-api-framework # opentelemetry-exporter-otlp-proto-http + # zgw-consumers rich==14.3.2 # via # -c requirements/base.txt @@ -600,6 +764,7 @@ sentry-sdk==2.52.0 # via # -c requirements/base.txt # -r requirements/base.txt + # commonground-api-common # open-api-framework shellingham==1.5.4 # via @@ -607,7 +772,12 @@ shellingham==1.5.4 # -r requirements/base.txt # typer six==1.17.0 - # via python-dateutil + # via + # -c requirements/base.txt + # -r requirements/base.txt + # furl + # orderedmultidict + # python-dateutil soupsieve==2.8.3 # via beautifulsoup4 sqlparse==0.5.5 @@ -615,6 +785,11 @@ sqlparse==0.5.5 # -c requirements/base.txt # -r requirements/base.txt # django +structlog==25.5.0 + # via + # -c requirements/base.txt + # -r requirements/base.txt + # django-structlog tblib==3.2.2 # via -r requirements/test-tools.in typer==0.23.1 @@ -633,15 +808,29 @@ typing-extensions==4.15.0 # opentelemetry-exporter-otlp-proto-http # opentelemetry-sdk # opentelemetry-semantic-conventions + # psycopg # pydantic # pydantic-core + # pyopenssl + # referencing # typing-inspection + # zgw-consumers typing-inspection==0.4.2 # via # -c requirements/base.txt # -r requirements/base.txt # pydantic # pydantic-settings +tzdata==2025.3 + # via + # -c requirements/base.txt + # -r requirements/base.txt + # kombu +tzlocal==5.3.1 + # via + # -c requirements/base.txt + # -r requirements/base.txt + # celery uritemplate==4.2.0 # via # -c requirements/base.txt @@ -658,8 +847,20 @@ uwsgi==2.0.31 # via # -c requirements/base.txt # -r requirements/base.txt +vine==5.1.0 + # via + # -c requirements/base.txt + # -r requirements/base.txt + # amqp + # celery + # kombu waitress==3.0.2 # via webtest +wcwidth==0.6.0 + # via + # -c requirements/base.txt + # -r requirements/base.txt + # prompt-toolkit webauthn==2.7.1 # via # -c requirements/base.txt @@ -677,6 +878,12 @@ wrapt==1.17.3 # opentelemetry-instrumentation # opentelemetry-instrumentation-dbapi # opentelemetry-instrumentation-redis +zgw-consumers==1.2.0 + # via + # -c requirements/base.txt + # -r requirements/base.txt + # commonground-api-common + # notifications-api-common zipp==3.23.0 # via # -c requirements/base.txt diff --git a/requirements/dev.txt b/requirements/dev.txt index ebefe585..f6e8b1eb 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -2,6 +2,11 @@ # ./bin/compile_dependencies.sh alabaster==1.0.0 # via sphinx +amqp==5.3.1 + # via + # -c requirements/ci.txt + # -r requirements/ci.txt + # kombu annotated-doc==0.0.4 # via # -c requirements/ci.txt @@ -14,12 +19,21 @@ annotated-types==0.7.0 # pydantic anyio==4.12.1 # via httpx +ape-pie==0.2.0 + # via + # -c requirements/ci.txt + # -r requirements/ci.txt + # commonground-api-common + # notifications-api-common + # zgw-consumers asgiref==3.11.1 # via # -c requirements/ci.txt # -r requirements/ci.txt # django # django-axes + # django-cors-headers + # django-structlog attrs==25.4.0 # via # -c requirements/ci.txt @@ -34,6 +48,11 @@ beautifulsoup4==4.14.3 # -c requirements/ci.txt # -r requirements/ci.txt # webtest +billiard==4.2.4 + # via + # -c requirements/ci.txt + # -r requirements/ci.txt + # celery boltons==25.0.0 # via # -c requirements/ci.txt @@ -49,6 +68,11 @@ cbor2==5.8.0 # -c requirements/ci.txt # -r requirements/ci.txt # webauthn +celery==5.6.2 + # via + # -c requirements/ci.txt + # -r requirements/ci.txt + # notifications-api-common certifi==2026.1.4 # via # -c requirements/ci.txt @@ -73,8 +97,31 @@ click==8.3.1 # -c requirements/ci.txt # -r requirements/ci.txt # bump-my-version + # celery + # click-didyoumean + # click-plugins + # click-repl # rich-click # typer +click-didyoumean==0.3.1 + # via + # -c requirements/ci.txt + # -r requirements/ci.txt + # celery +click-plugins==1.1.1.2 + # via + # -c requirements/ci.txt + # -r requirements/ci.txt + # celery +click-repl==0.3.0 + # via + # -c requirements/ci.txt + # -r requirements/ci.txt + # celery +commonground-api-common==2.11.0 + # via + # -c requirements/ci.txt + # -r requirements/ci.txt coverage==7.13.4 # via # -c requirements/ci.txt @@ -83,6 +130,7 @@ cryptography==46.0.5 # via # -c requirements/ci.txt # -r requirements/ci.txt + # django-simple-certmanager # josepy # mozilla-django-oidc # pyopenssl @@ -96,9 +144,12 @@ django==5.2.11 # via # -c requirements/ci.txt # -r requirements/ci.txt + # commonground-api-common # django-admin-index # django-appconf # django-axes + # django-cors-headers + # django-csp # django-debug-toolbar # django-extensions # django-filter @@ -107,23 +158,33 @@ django==5.2.11 # django-hijack # django-jsonform # django-log-outgoing-requests + # django-markup # django-otp # django-phonenumber-field + # django-privates # django-redis + # django-relativedelta + # django-rest-framework-condition # django-rosetta + # django-sendfile2 # django-sessionprofile # django-setup-configuration + # django-simple-certmanager # django-solo + # django-structlog # django-two-factor-auth # django-upgrade-check # djangorestframework + # drf-nested-routers # drf-spectacular # drf-spectacular-sidecar # maykin-2fa # maykin-common # mozilla-django-oidc # mozilla-django-oidc-db + # notifications-api-common # open-api-framework + # zgw-consumers django-admin-index==4.0.0 # via # -c requirements/ci.txt @@ -139,6 +200,14 @@ django-axes==8.3.1 # -c requirements/ci.txt # -r requirements/ci.txt # open-api-framework +django-cors-headers==4.9.0 + # via + # -c requirements/ci.txt + # -r requirements/ci.txt +django-csp==4.0 + # via + # -c requirements/ci.txt + # -r requirements/ci.txt django-debug-toolbar==6.2.0 # via -r requirements/dev.in django-extensions==4.1 @@ -147,6 +216,7 @@ django-filter==25.1 # via # -c requirements/ci.txt # -r requirements/ci.txt + # commonground-api-common # open-api-framework django-formtools==2.5.1 # via @@ -165,6 +235,7 @@ django-ipware==7.0.1 # via # -c requirements/ci.txt # -r requirements/ci.txt + # django-structlog django-jsonform==2.23.2 # via # -c requirements/ci.txt @@ -176,6 +247,10 @@ django-log-outgoing-requests==0.7.1 # -c requirements/ci.txt # -r requirements/ci.txt # open-api-framework +django-markup==1.10 + # via + # -c requirements/ci.txt + # -r requirements/ci.txt django-ordered-model==3.7.4 # via # -c requirements/ci.txt @@ -192,14 +267,34 @@ django-phonenumber-field==8.4.0 # -c requirements/ci.txt # -r requirements/ci.txt # django-two-factor-auth +django-privates==3.1.1 + # via + # -c requirements/ci.txt + # -r requirements/ci.txt + # django-simple-certmanager django-redis==6.0.0 # via # -c requirements/ci.txt # -r requirements/ci.txt +django-relativedelta==2.0.0 + # via + # -c requirements/ci.txt + # -r requirements/ci.txt + # zgw-consumers +django-rest-framework-condition==0.1.1 + # via + # -c requirements/ci.txt + # -r requirements/ci.txt + # commonground-api-common django-rosetta==0.10.3 # via # -c requirements/ci.txt # -r requirements/ci.txt +django-sendfile2==0.7.2 + # via + # -c requirements/ci.txt + # -r requirements/ci.txt + # django-privates django-sessionprofile==3.0.0 # via # -c requirements/ci.txt @@ -210,11 +305,23 @@ django-setup-configuration==0.11.0 # -c requirements/ci.txt # -r requirements/ci.txt # open-api-framework +django-simple-certmanager==2.5.0 + # via + # -c requirements/ci.txt + # -r requirements/ci.txt + # zgw-consumers django-solo==2.5.1 # via # -c requirements/ci.txt # -r requirements/ci.txt + # commonground-api-common # django-log-outgoing-requests + # notifications-api-common + # zgw-consumers +django-structlog==10.0.0 + # via + # -c requirements/ci.txt + # -r requirements/ci.txt django-two-factor-auth==1.18.1 # via # -c requirements/ci.txt @@ -233,12 +340,17 @@ djangorestframework==3.16.1 # via # -c requirements/ci.txt # -r requirements/ci.txt + # commonground-api-common + # drf-nested-routers # drf-spectacular + # notifications-api-common # open-api-framework djangorestframework-camel-case==1.4.2 # via # -c requirements/ci.txt # -r requirements/ci.txt + # commonground-api-common + # notifications-api-common dnspython==2.8.0 # via # -c requirements/ci.txt @@ -251,10 +363,16 @@ docutils==0.22.4 # django-setup-configuration # sphinx # sphinx-rtd-theme +drf-nested-routers==0.95.0 + # via + # -c requirements/ci.txt + # -r requirements/ci.txt + # commonground-api-common drf-spectacular==0.29.0 # via # -c requirements/ci.txt # -r requirements/ci.txt + # commonground-api-common # open-api-framework drf-spectacular-sidecar==2026.1.1 # via @@ -289,6 +407,11 @@ freezegun==1.5.5 # via # -c requirements/ci.txt # -r requirements/ci.txt +furl==2.1.4 + # via + # -c requirements/ci.txt + # -r requirements/ci.txt + # ape-pie gitdb==4.0.12 # via gitpython gitpython==3.1.46 @@ -334,6 +457,11 @@ inflection==0.5.1 # -c requirements/ci.txt # -r requirements/ci.txt # drf-spectacular +iso639-lang==2.6.3 + # via + # -c requirements/ci.txt + # -r requirements/ci.txt + # commonground-api-common jinja2==3.1.6 # via sphinx josepy==2.2.0 @@ -350,11 +478,11 @@ jsonschema-specifications==2025.9.1 # -c requirements/ci.txt # -r requirements/ci.txt # jsonschema -legacy-cgi==2.6.4 +kombu==5.6.2 # via # -c requirements/ci.txt # -r requirements/ci.txt - # webob + # celery lxml==6.0.2 # via # -c requirements/ci.txt @@ -391,6 +519,11 @@ mozilla-django-oidc-db==1.1.1 # -c requirements/ci.txt # -r requirements/ci.txt # open-api-framework +notifications-api-common==0.10.1 + # via + # -c requirements/ci.txt + # -r requirements/ci.txt + # commonground-api-common open-api-framework==0.13.4 # via # -c requirements/ci.txt @@ -509,10 +642,22 @@ opentelemetry-util-http==0.60b1 # opentelemetry-instrumentation-django # opentelemetry-instrumentation-requests # opentelemetry-instrumentation-wsgi +orderedmultidict==1.0.2 + # via + # -c requirements/ci.txt + # -r requirements/ci.txt + # furl +oyaml==1.0 + # via + # -c requirements/ci.txt + # -r requirements/ci.txt + # commonground-api-common packaging==26.0 # via # -c requirements/ci.txt # -r requirements/ci.txt + # django-csp + # kombu # opentelemetry-instrumentation # sphinx pillow==12.1.1 @@ -525,7 +670,11 @@ polib==1.2.0 # -r requirements/ci.txt # django-rosetta prompt-toolkit==3.0.52 - # via questionary + # via + # -c requirements/ci.txt + # -r requirements/ci.txt + # click-repl + # questionary protobuf==6.33.5 # via # -c requirements/ci.txt @@ -584,7 +733,9 @@ pyjwt==2.11.0 # via # -c requirements/ci.txt # -r requirements/ci.txt + # commonground-api-common # mozilla-django-oidc + # zgw-consumers pyopenssl==25.3.0 # via # -c requirements/ci.txt @@ -598,6 +749,8 @@ python-dateutil==2.9.0.post0 # via # -c requirements/ci.txt # -r requirements/ci.txt + # celery + # django-relativedelta # freezegun python-decouple==3.8 # via @@ -621,6 +774,7 @@ pyyaml==6.0.3 # -c requirements/ci.txt # -r requirements/ci.txt # drf-spectacular + # oyaml # pydantic-settings qrcode==8.2 # via @@ -644,12 +798,15 @@ requests==2.32.5 # via # -c requirements/ci.txt # -r requirements/ci.txt + # ape-pie + # commonground-api-common # django-log-outgoing-requests # django-rosetta # mozilla-django-oidc # open-api-framework # opentelemetry-exporter-otlp-proto-http # sphinx + # zgw-consumers rich==14.3.2 # via # -c requirements/ci.txt @@ -685,6 +842,7 @@ sentry-sdk==2.52.0 # via # -c requirements/ci.txt # -r requirements/ci.txt + # commonground-api-common # open-api-framework shellingham==1.5.4 # via @@ -695,6 +853,8 @@ six==1.17.0 # via # -c requirements/ci.txt # -r requirements/ci.txt + # furl + # orderedmultidict # python-dateutil smmap==5.0.2 # via gitdb @@ -732,6 +892,11 @@ sqlparse==0.5.5 # -r requirements/ci.txt # django # django-debug-toolbar +structlog==25.5.0 + # via + # -c requirements/ci.txt + # -r requirements/ci.txt + # django-structlog tblib==3.2.2 # via # -c requirements/ci.txt @@ -746,6 +911,7 @@ typing-extensions==4.15.0 # via # -c requirements/ci.txt # -r requirements/ci.txt + # anyio # beautifulsoup4 # grpcio # mozilla-django-oidc-db @@ -754,15 +920,29 @@ typing-extensions==4.15.0 # opentelemetry-exporter-otlp-proto-http # opentelemetry-sdk # opentelemetry-semantic-conventions + # psycopg # pydantic # pydantic-core + # pyopenssl + # referencing # typing-inspection + # zgw-consumers typing-inspection==0.4.2 # via # -c requirements/ci.txt # -r requirements/ci.txt # pydantic # pydantic-settings +tzdata==2025.3 + # via + # -c requirements/ci.txt + # -r requirements/ci.txt + # kombu +tzlocal==5.3.1 + # via + # -c requirements/ci.txt + # -r requirements/ci.txt + # celery uritemplate==4.2.0 # via # -c requirements/ci.txt @@ -779,6 +959,13 @@ uwsgi==2.0.31 # via # -c requirements/ci.txt # -r requirements/ci.txt +vine==5.1.0 + # via + # -c requirements/ci.txt + # -r requirements/ci.txt + # amqp + # celery + # kombu waitress==3.0.2 # via # -c requirements/ci.txt @@ -787,7 +974,10 @@ waitress==3.0.2 wcmatch==10.1 # via bump-my-version wcwidth==0.6.0 - # via prompt-toolkit + # via + # -c requirements/ci.txt + # -r requirements/ci.txt + # prompt-toolkit webauthn==2.7.1 # via # -c requirements/ci.txt @@ -811,6 +1001,12 @@ wrapt==1.17.3 # opentelemetry-instrumentation # opentelemetry-instrumentation-dbapi # opentelemetry-instrumentation-redis +zgw-consumers==1.2.0 + # via + # -c requirements/ci.txt + # -r requirements/ci.txt + # commonground-api-common + # notifications-api-common zipp==3.23.0 # via # -c requirements/ci.txt From 77af38cc8ae18f93a0904f4600285b2285f7e521 Mon Sep 17 00:00:00 2001 From: Daniel Mursa Date: Fri, 20 Feb 2026 12:45:21 +0100 Subject: [PATCH 4/9] :zap: [#28] Improve base.py and dev.py --- .gitignore | 1 + pyproject.toml | 49 ++++++++++++++++++++++++++++++ src/opendms/conf/base.py | 65 +++++++--------------------------------- src/opendms/conf/dev.py | 54 +++++++++++++++++++++++---------- 4 files changed, 99 insertions(+), 70 deletions(-) diff --git a/.gitignore b/.gitignore index 8f04c3c0..8b85cccb 100644 --- a/.gitignore +++ b/.gitignore @@ -38,6 +38,7 @@ local.py /static/ /mail/ /log/*.log* +/log/*.jsonl* /log/nginx/*.log* /.env diff --git a/pyproject.toml b/pyproject.toml index 570775b9..aa14f2a7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -23,6 +23,55 @@ extend-select = [ "UP", # pyupgrade ] +ignore = [ + # Checks for assertRaises and pytest.raises context managers that catch Exception or BaseException. + "B017", + # Checks for useless expressions. + "B018", + # Checks for raise statements in exception handlers that lack a from clause. + "B904", + # Whitespace before ':' (conflicts with Black) + "E203", + # Checks for lines that exceed the specified maximum character length. + "E501", + # Do not assign a lambda expression + "E731", + # Name may be undefined from '*' import + "F405", + # Checks for CamelCase imports that are aliased to lowercase names + "N813", + # Checks for CamelCase imports that are aliased to constant-style names. + "N814", + # Checks for class variable names that follow the mixedCase convention + "N815", + # Exception name should be named with Error suffix + "N818", + # Checks for instance methods that use a name other than self for their first argument. + "N805", + # Checks for the use of non-lowercase variable names in functions + "N806", + # Checks for functions names that do not follow the snake_case naming convention. + "N802", + # Checks for module names that do not follow the snake_case naming convention or are otherwise invalid. + "N999", + # Checks for for loops that can be replaced by a list comprehension. + "PERF401", + # Checks for `if` statements that can be replaced with bool + "SIM103", + # Check for if-else-blocks that can be replaced with a ternary operator + "SIM108", + #Check for environment variables that are not capitalized. + "SIM112", + # Checks for nested if statements that can be collapsed into a single if statement + "SIM102", + # Use a single `with` statement with multiple contexts instead of nested `with` statements + "SIM117", + # Checks for str.format calls that can be replaced with f-strings. + "UP032", + # An undefined name is likely to raise NameError at runtime. + "F821", +] + [tool.ruff.lint.isort] combine-as-imports = true section-order = [ diff --git a/src/opendms/conf/base.py b/src/opendms/conf/base.py index 4d460a07..7d43a540 100644 --- a/src/opendms/conf/base.py +++ b/src/opendms/conf/base.py @@ -1,9 +1,12 @@ -# ruff: noqa: F403,F405 -from maykin_common.config import config -from maykin_common.health_checks import ( - default_health_check_apps, -) +import os + +os.environ["_USE_STRUCTLOG"] = "True" + +from pathlib import Path + +from maykin_common.health_checks import default_health_check_apps from open_api_framework.conf.base import * # noqa +from open_api_framework.conf.utils import config # noqa # APPLICATIONS enabled for this project # @@ -20,64 +23,18 @@ "opendms.frontend", ] -MIDDLEWARE = [ - "django.middleware.security.SecurityMiddleware", - "django.contrib.sessions.middleware.SessionMiddleware", - # 'django.middleware.locale.LocaleMiddleware', - "django.middleware.common.CommonMiddleware", - "django.middleware.csrf.CsrfViewMiddleware", - "django.contrib.auth.middleware.AuthenticationMiddleware", - "maykin_2fa.middleware.OTPMiddleware", - "django.contrib.messages.middleware.MessageMiddleware", - "django.middleware.clickjacking.XFrameOptionsMiddleware", - "hijack.middleware.HijackUserMiddleware", - "djangorestframework_camel_case.middleware.CamelCaseMiddleWare", - # should be last according to docs - "axes.middleware.AxesMiddleware", -] - -ROOT_URLCONF = "opendms.urls" - -TEMPLATES = [ - { - "BACKEND": "django.template.backends.django.DjangoTemplates", - "DIRS": [ - DJANGO_PROJECT_DIR / "templates", - DJANGO_PROJECT_DIR / "frontend/dist", - ], - "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", - "opendms.utils.context_processors.settings", - ], - }, - }, -] - - # Additional locations of static files -STATICFILES_DIRS = [ - DJANGO_PROJECT_DIR / "static", - DJANGO_PROJECT_DIR / "frontend/dist/static", -] - -LOGIN_URL = reverse_lazy("admin:login") +STATICFILES_DIRS += [Path(DJANGO_PROJECT_DIR) / "frontend/dist/static"] # # SECURITY settings # - -CSRF_FAILURE_VIEW = "opendms.accounts.views.csrf_failure" +CSRF_FAILURE_VIEW = "maykin_common.views.csrf_failure" # # FIXTURES # - -FIXTURE_DIRS = (DJANGO_PROJECT_DIR / "fixtures",) +FIXTURE_DIRS = (Path(DJANGO_PROJECT_DIR) / "fixtures",) # # Custom settings diff --git a/src/opendms/conf/dev.py b/src/opendms/conf/dev.py index 517c265c..15f6c62c 100644 --- a/src/opendms/conf/dev.py +++ b/src/opendms/conf/dev.py @@ -1,9 +1,6 @@ -# ruff: noqa: F403,F405 import os import warnings -from maykin_common.config import config - os.environ.setdefault("DEBUG", "yes") os.environ.setdefault("ALLOWED_HOSTS", "*") os.environ.setdefault( @@ -18,7 +15,15 @@ os.environ.setdefault("DB_PASSWORD", "opendms") os.environ.setdefault("ENVIRONMENT", "development") +os.environ.setdefault("DISABLE_2FA", "yes") +os.environ.setdefault("LOG_FORMAT_CONSOLE", "plain_console") + +os.environ.setdefault("RELEASE", "dev") +os.environ.setdefault("LOG_REQUESTS", "no") + os.environ.setdefault("OTEL_SDK_DISABLED", "true") +os.environ.setdefault("OTEL_EXPORTER_OTLP_METRICS_INSECURE", "true") + from .base import * # noqa isort:skip @@ -35,58 +40,75 @@ "opendms": { "handlers": ["console"], "level": "DEBUG", - "propagate": True, + "propagate": False, }, "django": { "handlers": ["console"], "level": "DEBUG", - "propagate": True, + "propagate": False, }, "django.db.backends": { - "handlers": ["django"], + "handlers": ["json_file"], "level": "DEBUG", "propagate": False, }, "performance": { "handlers": ["console"], "level": "INFO", - "propagate": True, + "propagate": False, }, # # See: https://code.djangoproject.com/ticket/30554 # Autoreload logs excessively, turn it down a bit. # "django.utils.autoreload": { - "handlers": ["django"], + "handlers": ["console"], "level": "INFO", "propagate": False, }, } ) -SESSION_ENGINE = "django.contrib.sessions.backends.db" - # in memory cache and django-axes don't get along. # https://django-axes.readthedocs.io/en/latest/configuration.html#known-configuration-problems CACHES = { "default": {"BACKEND": "django.core.cache.backends.locmem.LocMemCache"}, "axes": {"BACKEND": "django.core.cache.backends.dummy.DummyCache"}, + "oidc": {"BACKEND": "django.core.cache.backends.locmem.LocMemCache"}, } # # Library settings # +# Django extensions +INSTALLED_APPS += ["django_extensions"] + ELASTIC_APM["DEBUG"] = True # Django debug toolbar INSTALLED_APPS += ["debug_toolbar"] -MIDDLEWARE = ["debug_toolbar.middleware.DebugToolbarMiddleware"] + MIDDLEWARE -INTERNAL_IPS = ("127.0.0.1",) +MIDDLEWARE += [ + "debug_toolbar.middleware.DebugToolbarMiddleware", +] -# None of the authentication backends require two-factor authentication. -if config("DISABLE_2FA", default=True): - MAYKIN_2FA_ALLOW_MFA_BYPASS_BACKENDS = AUTHENTICATION_BACKENDS +INTERNAL_IPS = ("127.0.0.1",) +DEBUG_TOOLBAR_CONFIG = {"INTERCEPT_REDIRECTS": False} +DEBUG_TOOLBAR_PANELS = [ + "debug_toolbar.panels.versions.VersionsPanel", + "debug_toolbar.panels.timer.TimerPanel", + "debug_toolbar.panels.settings.SettingsPanel", + "debug_toolbar.panels.headers.HeadersPanel", + "debug_toolbar.panels.request.RequestPanel", + "debug_toolbar.panels.sql.SQLPanel", + "debug_toolbar.panels.staticfiles.StaticFilesPanel", + "debug_toolbar.panels.templates.TemplatesPanel", + "debug_toolbar.panels.cache.CachePanel", + "debug_toolbar.panels.signals.SignalsPanel", + "debug_toolbar.panels.logging.LoggingPanel", + "debug_toolbar.panels.redirects.RedirectsPanel", + "debug_toolbar.panels.profiling.ProfilingPanel", +] # THOU SHALT NOT USE NAIVE DATETIMES warnings.filterwarnings( @@ -97,7 +119,7 @@ ) # Override settings with local settings. -try: +try: # noqa: SIM105 from .local import * # noqa except ImportError: pass From 3a7cb6509fac27829672087010cc2883bc2a2de1 Mon Sep 17 00:00:00 2001 From: Daniel Mursa Date: Fri, 20 Feb 2026 12:58:45 +0100 Subject: [PATCH 5/9] :zap: [#28] Reuse open-api-workflows --- .github/workflows/ci.yml | 4 +- .github/workflows/code-quality.yml | 85 ++++----------------------- .github/workflows/codeql-analysis.yml | 33 ++--------- .github/workflows/oaf-check.yml | 19 ++++++ .github/workflows/quick-start.yml | 12 ++++ Dockerfile | 3 +- bin/generate_envvar_docs.sh | 4 ++ src/opendms/urls.py | 3 +- 8 files changed, 54 insertions(+), 109 deletions(-) create mode 100644 .github/workflows/oaf-check.yml create mode 100644 .github/workflows/quick-start.yml create mode 100755 bin/generate_envvar_docs.sh diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 87e75e22..7fe8688b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,7 +24,7 @@ jobs: name: Determine changed files steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@v4 with: fetch-depth: 0 @@ -153,7 +153,7 @@ jobs: - name: Set up backend environment uses: maykinmedia/setup-django-backend@v1.3 with: - python-version: '3.12' + python-version: '3.14' apt-packages: "libgdal-dev gdal-bin" setup-node: false diff --git a/.github/workflows/code-quality.yml b/.github/workflows/code-quality.yml index ec020cfd..643f2986 100644 --- a/.github/workflows/code-quality.yml +++ b/.github/workflows/code-quality.yml @@ -12,78 +12,13 @@ on: workflow_dispatch: jobs: - ruff: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: astral-sh/ruff-action@v3 - - name: Linting - run: ruff check - - name: Check formatting - run: ruff format --check - - migrations: - name: Check for model changes not present in migrations - runs-on: ubuntu-latest - - services: - postgres: - image: postgres:17 - env: - POSTGRES_HOST_AUTH_METHOD: trust - ports: - - 5432:5432 - # Needed because the postgres container does not provide a healthcheck - options: - --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5 - - steps: - - uses: actions/checkout@v4 - - name: Set up backend environment - uses: maykinmedia/setup-django-backend@v1.3 - with: - python-version: '3.14' - setup-node: 'no' - - - name: Run makemigrations to check for missing migrations - run: | - src/manage.py makemigrations \ - --check \ - --dry-run - env: - DJANGO_SETTINGS_MODULE: "opendms.conf.ci" - DEBUG: 'true' - SECRET_KEY: dummy - DB_USER: postgres - DB_NAME: postgres - DB_PASSWORD: '' - - translations: - name: Check for missing translations - runs-on: ubuntu-latest - - services: - postgres: - image: postgres:17 - env: - POSTGRES_HOST_AUTH_METHOD: trust - ports: - - 5432:5432 - # Needed because the postgres container does not provide a healthcheck - options: - --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5 - - steps: - - uses: actions/checkout@v4 - - name: Set up backend environment - uses: maykinmedia/setup-django-backend@v1.3 - with: - python-version: '3.14' - setup-node: 'no' - - - name: Run makemessages to check for missing translations - run: | - python src/manage.py makemessages --all - git diff --exit-code - env: - DJANGO_SETTINGS_MODULE: "opendms.conf.ci" + open-api-workflow-code-quality: + uses: maykinmedia/open-api-workflows/.github/workflows/code-quality.yml@v6 + with: + python-version: "3.14" + apt-packages: "libgdal-dev gdal-bin" + node-version: "24" + postgres-image: "postgis/postgis:16-3.5" + + django-settings-module: "openvtb.conf.ci" + django-secret-key: dummy diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index e8bc55eb..a5be6095 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -1,39 +1,14 @@ -name: 'CodeQL' +name: "CodeQL" on: push: branches: - main - stable/* + pull_request: schedule: - cron: '32 20 * * 3' jobs: - analyze: - name: Analyze - runs-on: ubuntu-latest - permissions: - actions: read - contents: read - security-events: write - - strategy: - fail-fast: false - matrix: - language: ['javascript', 'python'] - - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - # Initializes the CodeQL tools for scanning. - - name: Initialize CodeQL - uses: github/codeql-action/init@v3 - with: - languages: ${{ matrix.language }} - - name: Autobuild - uses: github/codeql-action/autobuild@v3 - - - name: Perform CodeQL Analysis - if: ${{ github.event.repository.visibility == 'public' }} # Seems to fail on private repo's - uses: github/codeql-action/analyze@v3 + open-api-workflow-code-analysis: + uses: maykinmedia/open-api-workflows/.github/workflows/code-analysis.yml@v6 diff --git a/.github/workflows/oaf-check.yml b/.github/workflows/oaf-check.yml new file mode 100644 index 00000000..1ce0e1a4 --- /dev/null +++ b/.github/workflows/oaf-check.yml @@ -0,0 +1,19 @@ +name: Check Open API Framework Version + +on: + push: + branches: + - main + tags: + - '*' + workflow_dispatch: + schedule: + - cron: '0 7 * * 1' + + +jobs: + open-api-workflow-check-oas: + uses: maykinmedia/open-api-workflows/.github/workflows/oaf-check.yml@v6 + + with: + python-version: '3.14' diff --git a/.github/workflows/quick-start.yml b/.github/workflows/quick-start.yml new file mode 100644 index 00000000..d915aaf3 --- /dev/null +++ b/.github/workflows/quick-start.yml @@ -0,0 +1,12 @@ +name: Quick Start + +on: + push: + branches: + - main + pull_request: + workflow_dispatch: + +jobs: + open-api-workflow-quick-start: + uses: maykinmedia/open-api-workflows/.github/workflows/quick-start.yml@v6 diff --git a/Dockerfile b/Dockerfile index 1dcd4cba..7c9836bf 100644 --- a/Dockerfile +++ b/Dockerfile @@ -88,7 +88,8 @@ COPY --from=frontend-build /app/dist /app/src/opendms/frontend # copy source code COPY ./src /app/src -RUN useradd -M -u 1000 maykin \ +RUN groupadd -g 1000 maykin \ + && useradd -M -u 1000 -g 1000 maykin \ && chown -R maykin:maykin /app # drop privileges diff --git a/bin/generate_envvar_docs.sh b/bin/generate_envvar_docs.sh new file mode 100755 index 00000000..bada72e7 --- /dev/null +++ b/bin/generate_envvar_docs.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +# Generates the documentation for environment variables +src/manage.py generate_envvar_docs --file docs/installation/config/env_configuration.rst --exclude-group Celery diff --git a/src/opendms/urls.py b/src/opendms/urls.py index 481eee04..22d020a9 100644 --- a/src/opendms/urls.py +++ b/src/opendms/urls.py @@ -9,10 +9,9 @@ from maykin_2fa import monkeypatch_admin from maykin_2fa.urls import urlpatterns, webauthn_urlpatterns +from maykin_common.accounts.views import PasswordResetView from mozilla_django_oidc_db.views import AdminLoginFailure -from opendms.accounts.views.password_reset import PasswordResetView - # Configure admin monkeypatch_admin() From 481449dc39ca047c27422fe85657d4fd3696711b Mon Sep 17 00:00:00 2001 From: Daniel Mursa Date: Fri, 20 Feb 2026 14:18:27 +0100 Subject: [PATCH 6/9] :fire: [#28] Remove obsolete files from maykin-common --- .../accounts/tests/test_password_reset_view.py | 13 ------------- src/opendms/accounts/views/__init__.py | 1 - src/opendms/accounts/views/csrf.py | 16 ---------------- src/opendms/accounts/views/password_reset.py | 10 ---------- 4 files changed, 40 deletions(-) delete mode 100644 src/opendms/accounts/tests/test_password_reset_view.py delete mode 100644 src/opendms/accounts/views/__init__.py delete mode 100644 src/opendms/accounts/views/csrf.py delete mode 100644 src/opendms/accounts/views/password_reset.py diff --git a/src/opendms/accounts/tests/test_password_reset_view.py b/src/opendms/accounts/tests/test_password_reset_view.py deleted file mode 100644 index 29ff3b4d..00000000 --- a/src/opendms/accounts/tests/test_password_reset_view.py +++ /dev/null @@ -1,13 +0,0 @@ -from django.test import TestCase -from django.urls import reverse - - -class PasswordResetViewTests(TestCase): - def test_user_cant_access_the_password_reset_view_more_than_5_times(self): - url = reverse("admin_password_reset") - - for _ in range(5): - response = self.client.get(url) - self.assertEqual(response.status_code, 200) - response = self.client.get(url) - self.assertEqual(response.status_code, 429) diff --git a/src/opendms/accounts/views/__init__.py b/src/opendms/accounts/views/__init__.py deleted file mode 100644 index 5b0ea015..00000000 --- a/src/opendms/accounts/views/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from .csrf import csrf_failure # noqa diff --git a/src/opendms/accounts/views/csrf.py b/src/opendms/accounts/views/csrf.py deleted file mode 100644 index 5cfa8bd4..00000000 --- a/src/opendms/accounts/views/csrf.py +++ /dev/null @@ -1,16 +0,0 @@ -from django.conf import settings -from django.http import HttpResponseRedirect -from django.views.csrf import ( - CSRF_FAILURE_TEMPLATE_NAME, - csrf_failure as original_csrf_failure, -) - - -def csrf_failure(request, reason="", template_name=CSRF_FAILURE_TEMPLATE_NAME): - """ - Catch CSRF failure when tryin to login a second time, when already logged - in, by redirecting to the LOGIN_REDIRECT_URL. - """ - if request.path in settings.LOGIN_URLS and request.user.is_authenticated: - return HttpResponseRedirect(settings.LOGIN_REDIRECT_URL) - return original_csrf_failure(request, reason=reason, template_name=template_name) diff --git a/src/opendms/accounts/views/password_reset.py b/src/opendms/accounts/views/password_reset.py deleted file mode 100644 index d4ab2fb9..00000000 --- a/src/opendms/accounts/views/password_reset.py +++ /dev/null @@ -1,10 +0,0 @@ -from django.contrib.auth import views as auth_views - -from maykin_common.throttling import IPThrottleMixin - - -class PasswordResetView(IPThrottleMixin, auth_views.PasswordResetView): - throttle_name = "password-reset" - throttle_visits = 5 - throttle_period = 60 - throttle_methods = ("get",) From cf67d5091aee0a1cf1d12dc3655a1a012a027084 Mon Sep 17 00:00:00 2001 From: Daniel Mursa Date: Fri, 20 Feb 2026 14:32:09 +0100 Subject: [PATCH 7/9] :recycle: [#28] Clean project requirements --- bin/check_changed_files.sh | 0 requirements/base.in | 19 ++---------------- requirements/base.txt | 41 +++++++++++++++++++------------------- requirements/ci.txt | 24 +++++++++++++++++++++- requirements/dev.txt | 24 +++++++++++++++++++++- 5 files changed, 68 insertions(+), 40 deletions(-) mode change 100644 => 100755 bin/check_changed_files.sh diff --git a/bin/check_changed_files.sh b/bin/check_changed_files.sh old mode 100644 new mode 100755 diff --git a/requirements/base.in b/requirements/base.in index f6b39a3e..a8a55c61 100644 --- a/requirements/base.in +++ b/requirements/base.in @@ -1,27 +1,12 @@ # Core python libraries Pillow # handle images -psycopg # database driver -python-dotenv # environment variables for secrets -# Framework libraries -django ~= 5.2 -django-admin-index -django-axes[ipware] django-hijack -django-redis -django-rosetta -maykin-2fa maykin-common[axes,cli,health-checks,mfa,otel] # other extras: pdf, vcr -mozilla-django-oidc-db josepy - +django-rosetta # API libraries -open-api-framework[cors,markup,csp,commonground,structlog] - -# WSGI servers & monitoring - production oriented -uwsgi -sentry-sdk # error monitoring -elastic-apm # Elastic APM integration +open-api-framework[cors,markup,geo,csp,commonground,inclusions,sanitization,server,redis,structlog] #postgres package psycopg2-binary diff --git a/requirements/base.txt b/requirements/base.txt index 8ad84404..2cee64c8 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -24,6 +24,8 @@ attrs==25.4.0 # referencing billiard==4.2.4 # via celery +bleach==6.3.0 + # via open-api-framework boltons==25.0.0 # via # face @@ -65,7 +67,6 @@ cryptography==46.0.5 # webauthn django==5.2.11 # via - # -r requirements/base.in # commonground-api-common # django-admin-index # django-appconf @@ -95,6 +96,8 @@ django==5.2.11 # django-two-factor-auth # django-upgrade-check # djangorestframework + # djangorestframework-gis + # djangorestframework-inclusions # drf-nested-routers # drf-spectacular # drf-spectacular-sidecar @@ -107,14 +110,12 @@ django==5.2.11 # zgw-consumers django-admin-index==4.0.0 # via - # -r requirements/base.in # maykin-common # open-api-framework django-appconf==1.2.0 # via django-log-outgoing-requests django-axes==8.3.1 # via - # -r requirements/base.in # maykin-common # open-api-framework django-cors-headers==4.9.0 @@ -124,6 +125,7 @@ django-csp==4.0 django-filter==25.1 # via # commonground-api-common + # djangorestframework-gis # open-api-framework django-formtools==2.5.1 # via django-two-factor-auth @@ -132,9 +134,7 @@ django-health-check==4.0.0 django-hijack==3.7.6 # via -r requirements/base.in django-ipware==7.0.1 - # via - # django-axes - # django-structlog + # via django-structlog django-jsonform==2.23.2 # via # mozilla-django-oidc-db @@ -154,7 +154,7 @@ django-phonenumber-field==8.4.0 django-privates==3.1.1 # via django-simple-certmanager django-redis==6.0.0 - # via -r requirements/base.in + # via open-api-framework django-relativedelta==2.0.0 # via zgw-consumers django-rest-framework-condition==0.1.1 @@ -184,6 +184,8 @@ django-upgrade-check==1.1.0 djangorestframework==3.16.1 # via # commonground-api-common + # djangorestframework-gis + # djangorestframework-inclusions # drf-nested-routers # drf-spectacular # notifications-api-common @@ -192,6 +194,10 @@ djangorestframework-camel-case==1.4.2 # via # commonground-api-common # notifications-api-common +djangorestframework-gis==1.2.0 + # via open-api-framework +djangorestframework-inclusions==1.2.0 + # via open-api-framework dnspython==2.8.0 # via django-health-check docutils==0.22.4 @@ -207,9 +213,7 @@ drf-spectacular-sidecar==2026.1.1 ecs-logging==2.3.0 # via elastic-apm elastic-apm==6.25.0 - # via - # -r requirements/base.in - # open-api-framework + # via open-api-framework face==26.0.0 # via glom furl==2.1.4 @@ -242,7 +246,6 @@ markdown-it-py==4.0.0 # via rich maykin-2fa==2.0.1 # via - # -r requirements/base.in # maykin-common # open-api-framework maykin-common==0.18.0 @@ -252,9 +255,7 @@ mdurl==0.1.2 mozilla-django-oidc==5.0.2 # via mozilla-django-oidc-db mozilla-django-oidc-db==1.1.1 - # via - # -r requirements/base.in - # open-api-framework + # via open-api-framework notifications-api-common==0.10.1 # via commonground-api-common open-api-framework==0.13.4 @@ -355,9 +356,7 @@ protobuf==6.33.5 # googleapis-common-protos # opentelemetry-proto psycopg==3.3.2 - # via - # -r requirements/base.in - # open-api-framework + # via open-api-framework psycopg-binary==3.3.2 # via psycopg psycopg2-binary==2.9.11 @@ -393,7 +392,6 @@ python-decouple==3.8 # open-api-framework python-dotenv==1.2.1 # via - # -r requirements/base.in # open-api-framework # pydantic-settings python-ipware==3.0.0 @@ -405,7 +403,7 @@ pyyaml==6.0.3 # pydantic-settings qrcode==8.2 # via django-two-factor-auth -redis==7.1.1 +redis==7.2.0 # via django-redis referencing==0.37.0 # via @@ -434,7 +432,6 @@ semantic-version==2.10.0 # via django-upgrade-check sentry-sdk==2.52.0 # via - # -r requirements/base.in # commonground-api-common # open-api-framework shellingham==1.5.4 @@ -484,7 +481,7 @@ urllib3==2.6.3 # requests # sentry-sdk uwsgi==2.0.31 - # via -r requirements/base.in + # via open-api-framework vine==5.1.0 # via # amqp @@ -494,6 +491,8 @@ wcwidth==0.6.0 # via prompt-toolkit webauthn==2.7.1 # via django-two-factor-auth +webencodings==0.5.1 + # via bleach wrapt==1.17.3 # via # elastic-apm diff --git a/requirements/ci.txt b/requirements/ci.txt index 31ad0cd8..9fd44258 100644 --- a/requirements/ci.txt +++ b/requirements/ci.txt @@ -44,6 +44,10 @@ billiard==4.2.4 # -c requirements/base.txt # -r requirements/base.txt # celery +bleach==6.3.0 + # via + # -c requirements/base.txt + # -r requirements/base.txt boltons==25.0.0 # via # -c requirements/base.txt @@ -151,6 +155,8 @@ django==5.2.11 # django-two-factor-auth # django-upgrade-check # djangorestframework + # djangorestframework-gis + # djangorestframework-inclusions # drf-nested-routers # drf-spectacular # drf-spectacular-sidecar @@ -189,6 +195,7 @@ django-filter==25.1 # -c requirements/base.txt # -r requirements/base.txt # commonground-api-common + # djangorestframework-gis # open-api-framework django-formtools==2.5.1 # via @@ -311,6 +318,8 @@ djangorestframework==3.16.1 # -c requirements/base.txt # -r requirements/base.txt # commonground-api-common + # djangorestframework-gis + # djangorestframework-inclusions # drf-nested-routers # drf-spectacular # notifications-api-common @@ -321,6 +330,14 @@ djangorestframework-camel-case==1.4.2 # -r requirements/base.txt # commonground-api-common # notifications-api-common +djangorestframework-gis==1.2.0 + # via + # -c requirements/base.txt + # -r requirements/base.txt +djangorestframework-inclusions==1.2.0 + # via + # -c requirements/base.txt + # -r requirements/base.txt dnspython==2.8.0 # via # -c requirements/base.txt @@ -714,7 +731,7 @@ qrcode==8.2 # -c requirements/base.txt # -r requirements/base.txt # django-two-factor-auth -redis==7.1.1 +redis==7.2.0 # via # -c requirements/base.txt # -r requirements/base.txt @@ -866,6 +883,11 @@ webauthn==2.7.1 # -c requirements/base.txt # -r requirements/base.txt # django-two-factor-auth +webencodings==0.5.1 + # via + # -c requirements/base.txt + # -r requirements/base.txt + # bleach webob==1.8.9 # via webtest webtest==3.0.7 diff --git a/requirements/dev.txt b/requirements/dev.txt index f6e8b1eb..9e9b346a 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -53,6 +53,10 @@ billiard==4.2.4 # -c requirements/ci.txt # -r requirements/ci.txt # celery +bleach==6.3.0 + # via + # -c requirements/ci.txt + # -r requirements/ci.txt boltons==25.0.0 # via # -c requirements/ci.txt @@ -175,6 +179,8 @@ django==5.2.11 # django-two-factor-auth # django-upgrade-check # djangorestframework + # djangorestframework-gis + # djangorestframework-inclusions # drf-nested-routers # drf-spectacular # drf-spectacular-sidecar @@ -217,6 +223,7 @@ django-filter==25.1 # -c requirements/ci.txt # -r requirements/ci.txt # commonground-api-common + # djangorestframework-gis # open-api-framework django-formtools==2.5.1 # via @@ -341,6 +348,8 @@ djangorestframework==3.16.1 # -c requirements/ci.txt # -r requirements/ci.txt # commonground-api-common + # djangorestframework-gis + # djangorestframework-inclusions # drf-nested-routers # drf-spectacular # notifications-api-common @@ -351,6 +360,14 @@ djangorestframework-camel-case==1.4.2 # -r requirements/ci.txt # commonground-api-common # notifications-api-common +djangorestframework-gis==1.2.0 + # via + # -c requirements/ci.txt + # -r requirements/ci.txt +djangorestframework-inclusions==1.2.0 + # via + # -c requirements/ci.txt + # -r requirements/ci.txt dnspython==2.8.0 # via # -c requirements/ci.txt @@ -783,7 +800,7 @@ qrcode==8.2 # django-two-factor-auth questionary==2.1.1 # via bump-my-version -redis==7.1.1 +redis==7.2.0 # via # -c requirements/ci.txt # -r requirements/ci.txt @@ -983,6 +1000,11 @@ webauthn==2.7.1 # -c requirements/ci.txt # -r requirements/ci.txt # django-two-factor-auth +webencodings==0.5.1 + # via + # -c requirements/ci.txt + # -r requirements/ci.txt + # bleach webob==1.8.9 # via # -c requirements/ci.txt From b7e1fbefde2535cbd4192d79bd6953b39069e908 Mon Sep 17 00:00:00 2001 From: Daniel Mursa Date: Tue, 24 Feb 2026 11:30:10 +0100 Subject: [PATCH 8/9] :memo: [#28] Fix documentation CI --- .github/workflows/ci.yml | 73 ++------- .github/workflows/code-quality.yml | 13 +- bin/compile_dependencies.sh | 20 +-- docs/Makefile | 20 +++ docs/_static/theme_overrides.css | 35 +++++ docs/changelog.rst | 3 + docs/check_sphinx.py | 17 ++ docs/conf.py | 125 +++++++++++++++ docs/index.rst | 19 +-- docs/install/dev.rst | 38 ----- docs/install/index.rst | 15 -- docs/install/production.rst | 5 - docs/install/staging.rst | 5 - .../installation/config/env_configuration.rst | 145 ++++++++++++++++++ docs/installation/config/index.rst | 12 ++ docs/installation/index.rst | 18 +++ docs/logo.png | Bin 0 -> 22967 bytes docs/testing.rst | 105 +++++++++++++ requirements/ci.txt | 55 +++++++ requirements/dev.txt | 104 +++++++++++-- requirements/docs.in | 9 ++ src/opendms/conf/base.py | 54 +++++-- src/opendms/conf/jenkins.py | 3 - src/opendms/conf/production.py | 7 +- 24 files changed, 714 insertions(+), 186 deletions(-) create mode 100644 docs/Makefile create mode 100644 docs/_static/theme_overrides.css create mode 100644 docs/changelog.rst create mode 100644 docs/check_sphinx.py create mode 100644 docs/conf.py delete mode 100644 docs/install/dev.rst delete mode 100644 docs/install/index.rst delete mode 100644 docs/install/production.rst delete mode 100644 docs/install/staging.rst create mode 100644 docs/installation/config/env_configuration.rst create mode 100644 docs/installation/config/index.rst create mode 100644 docs/installation/index.rst create mode 100644 docs/logo.png create mode 100644 docs/testing.rst create mode 100644 requirements/docs.in diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7fe8688b..a2619a78 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,6 +15,8 @@ env: IMAGE_NAME: maykinmedia/opendms DJANGO_SETTINGS_MODULE: opendms.conf.ci DOCKER_BUILDKIT: '1' + DB_PASSWORD: '' + DB_USER: postgres jobs: @@ -40,32 +42,6 @@ jobs: changed-py-files: ${{ steps.changed-py-files.outputs.any_changed }} changed-requirements: ${{ steps.changed-requirements.outputs.any_changed }} - setup: - name: Set up the build variables - runs-on: ubuntu-latest - outputs: - tag: ${{ steps.vars.outputs.tag }} - git_hash: ${{ steps.vars.outputs.git_hash }} - - steps: - - name: Extract version information - id: vars - run: | - # Strip git ref prefix from version - VERSION=$(echo "${{ github.ref }}" | sed -e 's,.*/\(.*\),\1,') - - # Strip "v" prefix from tag name (if present at all) - [[ "${{ github.ref }}" == "refs/tags/"* ]] && VERSION=$(echo $VERSION | sed -e 's/^v//') - - # Use Docker `latest` tag convention - [ "$VERSION" == "main" ] && VERSION=latest - - # PRs result in version 'merge' -> transform that into 'latest' - [ "$VERSION" == "merge" ] && VERSION=latest - - echo "tag=${VERSION}" >> $GITHUB_OUTPUT - echo "git_hash=${GITHUB_SHA}" >> $GITHUB_OUTPUT - tests: name: Tests (PG ${{ matrix.postgres }}) runs-on: ubuntu-latest @@ -78,10 +54,6 @@ jobs: strategy: matrix: postgres: ["14", "15", "16", "17"] - use_pooling: [false] - include: - - postgres: "17" - use_pooling: true services: postgres: @@ -103,13 +75,10 @@ jobs: uses: maykinmedia/setup-django-backend@v1.3 with: python-version: '3.14' - optimize-postgres: 'yes' - pg-service: 'postgres' - setup-node: 'no' + setup-node: true - name: Run tests run: | - python src/manage.py compilemessages python src/manage.py collectstatic --noinput --link coverage run src/manage.py test src env: @@ -184,35 +153,21 @@ jobs: - store-reusable-workflow-vars with: apt-packages: 'graphviz graphviz-dev' - main-branch: 'master' + main-branch: 'main' run-docs: true python-version: '3.14' docker-image-name: ${{ needs.store-reusable-workflow-vars.outputs.image-name }} django-settings-module: opendms.conf.ci -# docker_push: +# open-api-publish: +# uses: maykinmedia/open-api-workflows/.github/workflows/publish.yml@v6 # needs: +# - store-reusable-workflow-vars +# - open-api-ci # - tests -# - docker_build -# -# name: Push Docker image -# runs-on: ubuntu-latest -# if: github.event_name == 'push' # Exclude PRs -# -# steps: -# - name: Download built image -# uses: actions/download-artifact@v4 -# with: -# name: docker-image -# -# - name: Load image -# run: | -# docker image load -i image.tar -# -# - name: Log into registry -# run: -# echo "${{ secrets.DOCKER_TOKEN }}" | docker login -u ${{ secrets.DOCKER_USERNAME }} -# --password-stdin -# -# - name: Push the Docker image (production) -# run: docker push ${{ needs.docker_build.outputs.image_tag }} +# with: +# docker-image-name: ${{ needs.store-reusable-workflow-vars.outputs.image-name }} +# repository-owner: 'maykinmedia' +# secrets: +# docker-username: ${{ secrets.DOCKER_USERNAME }} +# docker-token: ${{ secrets.DOCKER_TOKEN }} diff --git a/.github/workflows/code-quality.yml b/.github/workflows/code-quality.yml index 643f2986..0e8cbf57 100644 --- a/.github/workflows/code-quality.yml +++ b/.github/workflows/code-quality.yml @@ -1,13 +1,14 @@ +# For most projects, this workflow file will not need changing; you simply need +# to commit it to your repository. +# +# You may wish to alter this file to override the set of languages analyzed, +# or to provide custom queries or build logic. name: Code quality checks -# Run this workflow every time a new commit pushed to your repository on: push: branches: - main - - stable/* - tags: - - '*' pull_request: workflow_dispatch: @@ -16,9 +17,7 @@ jobs: uses: maykinmedia/open-api-workflows/.github/workflows/code-quality.yml@v6 with: python-version: "3.14" - apt-packages: "libgdal-dev gdal-bin" node-version: "24" - postgres-image: "postgis/postgis:16-3.5" - django-settings-module: "openvtb.conf.ci" + django-settings-module: "opendms.conf.ci" django-secret-key: dummy diff --git a/bin/compile_dependencies.sh b/bin/compile_dependencies.sh index a26834c9..6e3dda11 100755 --- a/bin/compile_dependencies.sh +++ b/bin/compile_dependencies.sh @@ -14,29 +14,25 @@ set -ex command -v uv || (echo "uv not found on PATH. Install it https://astral.sh/uv" >&2 && exit 1) -cwd="${PWD}" -toplevel=$(git rev-parse --show-toplevel) - -cd "${toplevel}" +root_dir=$(git rev-parse --show-toplevel) export UV_CUSTOM_COMPILE_COMMAND="./bin/compile_dependencies.sh" # Base (& prod) deps uv pip compile \ - --output-file requirements/base.txt \ + --output-file "$root_dir/requirements/base.txt" \ "$@" \ - requirements/base.in + "$root_dir/requirements/base.in" # Dependencies for testing uv pip compile \ - --output-file requirements/ci.txt \ + --output-file "$root_dir/requirements/ci.txt" \ "$@" \ - requirements/test-tools.in + "$root_dir/requirements/test-tools.in" \ + "$root_dir/requirements/docs.in" # Dev depedencies - exact same set as CI + some extra tooling uv pip compile \ - --output-file requirements/dev.txt \ + --output-file "$root_dir/requirements/dev.txt" \ "$@" \ - requirements/dev.in - -cd "${cwd}" + "$root_dir/requirements/dev.in" diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 00000000..d4bb2cbb --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,20 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line, and also +# from the environment for the first two. +SPHINXOPTS ?= +SPHINXBUILD ?= sphinx-build +SOURCEDIR = . +BUILDDIR = _build + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: help Makefile + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/docs/_static/theme_overrides.css b/docs/_static/theme_overrides.css new file mode 100644 index 00000000..60b010a0 --- /dev/null +++ b/docs/_static/theme_overrides.css @@ -0,0 +1,35 @@ +/* override table width restrictions */ +@media screen and (min-width: 767px) { + + .wy-table-responsive table td { + /* !important prevents the common CSS stylesheets from overriding + this as on RTD they are loaded after this stylesheet */ + white-space: normal !important; + } + + .wy-table-responsive table td:first-child { + white-space: nowrap !important; + } + + .wy-table-responsive table { + background-color: white; + } + + .wy-table-responsive { + overflow: visible !important; + } +} + +.wy-side-nav-search { + background-color: #04A5BB; +} + +.rst-content a:link, +.rst-content a:visited { + color: #017092; +} + +.rst-content a:focus, +.rst-content a:hover { + color: #051F31; +} diff --git a/docs/changelog.rst b/docs/changelog.rst new file mode 100644 index 00000000..76888fe1 --- /dev/null +++ b/docs/changelog.rst @@ -0,0 +1,3 @@ +.. _changelog: + +.. include:: ../CHANGELOG.rst diff --git a/docs/check_sphinx.py b/docs/check_sphinx.py new file mode 100644 index 00000000..cc1003a1 --- /dev/null +++ b/docs/check_sphinx.py @@ -0,0 +1,17 @@ +import subprocess + + +def test_linkcheck(tmpdir): + doctrees = tmpdir.join("doctrees") + htmldir = tmpdir.join("html") + subprocess.check_call( + ["sphinx-build", "-W", "-blinkcheck", "-d", str(doctrees), ".", str(htmldir)], + ) + + +def test_build_docs(tmpdir): + doctrees = tmpdir.join("doctrees") + htmldir = tmpdir.join("html") + subprocess.check_call( + ["sphinx-build", "-W", "-bhtml", "-d", str(doctrees), ".", str(htmldir)], + ) diff --git a/docs/conf.py b/docs/conf.py new file mode 100644 index 00000000..294cdf87 --- /dev/null +++ b/docs/conf.py @@ -0,0 +1,125 @@ +# Configuration file for the Sphinx documentation builder. +# +# This file only contains a selection of the most common options. For a full +# list see the documentation: +# https://www.sphinx-doc.org/en/master/usage/configuration.html + +# -- Path setup -------------------------------------------------------------- +import os +import sys + +import django +from django.utils.translation import activate + +sys.path.insert(0, os.path.abspath("../src")) +os.environ["LOG_REQUESTS"] = "false" + +import opendms # noqa isort:skip + +# Import as private variable to avoid errors on build +from importlib.metadata import version as _version + +from opendms.setup import setup_env # noqa isort:skip + +setup_env() +django.setup() + +# -- Project information ----------------------------------------------------- + +project = "opendms" +copyright = "Maykin B.V. 2025" # noqa +author = opendms.__author__ + +# The full version, including alpha/beta/rc tags +release = opendms.__version__ + + +# -- General configuration --------------------------------------------------- + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = [ + "sphinx.ext.autodoc", + "sphinx.ext.todo", + "sphinx.ext.extlinks", + "sphinx.ext.intersphinx", + "sphinx.ext.autodoc", + "sphinx.ext.graphviz", + "django_setup_configuration.documentation.setup_config_example", + "django_setup_configuration.documentation.setup_config_usage", + "vng_api_common.diagrams.uml_images", + "sphinx_tabs.tabs", + "recommonmark", + # "sphinx_markdown_tables", +] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ["_templates"] + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +# +# This is also used if you do content translation via gettext catalogs. +# Usually you set "language" from the command line for these cases. +language = "en" + +# Also set the language to English for Django, to make sure that any translatable text +# is also shown in English (for instance the help texts for setup configuration examples) +activate("en") + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +# This pattern also affects html_static_path and html_extra_path. +exclude_patterns = ["_build", "Thumbs.db", ".DS_Store", ".pytest_cache", "_archive"] + +source_suffix = [".rst", ".md"] + +# Datamodel image settings +graphviz_output_format = "png" + +# -- Options for HTML output ------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +# +# html_logo = "logo.svg" +html_theme = "sphinx_rtd_theme" + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ["_static"] +html_css_files = [ + "theme_overrides.css", # override wide tables with word wrap +] + +todo_include_todos = True + +linkcheck_retries = 3 + +linkcheck_ignore = [ + r"urn:*", + r"https?://.*\.gemeente.nl", + r"http://localhost:\d+/", + r"https://.*sentry.*", + r"https://example.com*", + r"https://portal.azure.com*", + r"https://.*kvk\.nl*", + r"https://gdpr.eu*", +] + +extlinks = {} + +django_structlog_version = _version("django-structlog") +oaf_version = _version("open-api-framework") +intersphinx_mapping = { + "django-structlog": ( + f"https://django-structlog.readthedocs.io/en/{django_structlog_version}", + None, + ), + "oaf": ( + f"https://open-api-framework.readthedocs.io/en/{oaf_version}/", + None, + ), +} diff --git a/docs/index.rst b/docs/index.rst index 5091aa33..85fde18c 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -1,15 +1,16 @@ .. _index: -=================== +====================== +Open DMS Documentation +====================== -=================== - -Welcome to the documentation for the project. - - -Documentation -============= +Getting Started +--------------- .. toctree:: - :maxdepth: 3 + :maxdepth: 3 + :hidden: + installation/index + testing + changelog diff --git a/docs/install/dev.rst b/docs/install/dev.rst deleted file mode 100644 index 8a84ecc3..00000000 --- a/docs/install/dev.rst +++ /dev/null @@ -1,38 +0,0 @@ -.. _install_development: - -======================= -Development environment -======================= - -Quick start -=========== - -#. Navigate to the location where you want to place your project. - -#. Get the code:: - - git clone git@bitbucket.org:maykinmedia/opendms.git - cd opendms - -#. Bootstrap the virtual environment and install all required libraries. The - ``bootstrap.py`` script basically sets the proper Django settings file to be - used:: - - python bootstrap.py - -#. Activate your virtual environment and create the statics and database:: - - source env/bin/activate # or, workon if you use virtualenvwrapper - npm install - npm run build - python src/manage.py collectstatic --link - python src/manage.py migrate - - -Next steps ----------- - -You can now run your installation and point your browser to the address given -by this command:: - - python src/manage.py runserver diff --git a/docs/install/index.rst b/docs/install/index.rst deleted file mode 100644 index 1709cb6d..00000000 --- a/docs/install/index.rst +++ /dev/null @@ -1,15 +0,0 @@ -.. _install_index: - -============ -Installation -============ - -This section briefly describes how the project is intended to be installed in -development, staging and production environments. - -.. toctree:: - :maxdepth: 2 - - development - staging - production diff --git a/docs/install/production.rst b/docs/install/production.rst deleted file mode 100644 index 4bb2e350..00000000 --- a/docs/install/production.rst +++ /dev/null @@ -1,5 +0,0 @@ -.. _install_production: - -====================== -Production environment -====================== diff --git a/docs/install/staging.rst b/docs/install/staging.rst deleted file mode 100644 index 4e31e736..00000000 --- a/docs/install/staging.rst +++ /dev/null @@ -1,5 +0,0 @@ -.. _install_staging: - -=================== -Staging environment -=================== diff --git a/docs/installation/config/env_configuration.rst b/docs/installation/config/env_configuration.rst new file mode 100644 index 00000000..c0159a6b --- /dev/null +++ b/docs/installation/config/env_configuration.rst @@ -0,0 +1,145 @@ +.. _installation_env_config: + +=================================== +Environment configuration reference +=================================== + + + +Available environment variables +=============================== + + +Required +-------- + +* ``SECRET_KEY``: Secret key that's used for certain cryptographic utilities. . +* ``ALLOWED_HOSTS``: a comma separated (without spaces!) list of domains that serve the installation. Used to protect against Host header attacks. Defaults to: ``(empty string)``. +* ``CACHE_DEFAULT``: redis cache address for the default cache (this **MUST** be set when using Docker). Defaults to: ``localhost:6379/0``. +* ``CACHE_AXES``: redis cache address for the brute force login protection cache (this **MUST** be set when using Docker). Defaults to: ``localhost:6379/0``. +* ``EMAIL_HOST``: hostname for the outgoing e-mail server (this **MUST** be set when using Docker). Defaults to: ``localhost``. + + +Database +-------- + +* ``DB_NAME``: name of the PostgreSQL database. Defaults to: ``opendms``. +* ``DB_USER``: username of the database user. Defaults to: ``opendms``. +* ``DB_PASSWORD``: password of the database user. Defaults to: ``opendms``. +* ``DB_HOST``: hostname of the PostgreSQL database. Defaults to ``db`` for the docker environment, otherwise defaults to ``localhost``. +* ``DB_PORT``: port number of the database. Defaults to: ``5432``. +* ``DB_CONN_MAX_AGE``: The lifetime of a database connection, as an integer of seconds. Use 0 to close database connections at the end of each request — Django’s historical behavior. This setting is ignored if connection pooling is used. Defaults to: ``60``. +* ``DB_POOL_ENABLED``: **Experimental:** Whether to use connection pooling. This feature is not yet recommended for production use. See the documentation for details: https://open-api-framework.readthedocs.io/en/latest/connection_pooling.html. Defaults to: ``False``. +* ``DB_POOL_MIN_SIZE``: The minimum number of connection the pool will hold. The pool will actively try to create new connections if some are lost (closed, broken) and will try to never go below min_size. Defaults to: ``4``. +* ``DB_POOL_MAX_SIZE``: The maximum number of connections the pool will hold. If None, or equal to min_size, the pool will not grow or shrink. If larger than min_size, the pool can grow if more than min_size connections are requested at the same time and will shrink back after the extra connections have been unused for more than max_idle seconds. Defaults to: ``None``. +* ``DB_POOL_TIMEOUT``: The default maximum time in seconds that a client can wait to receive a connection from the pool (using connection() or getconn()). Note that these methods allow to override the timeout default. Defaults to: ``30``. +* ``DB_POOL_MAX_WAITING``: Maximum number of requests that can be queued to the pool, after which new requests will fail, raising TooManyRequests. 0 means no queue limit. Defaults to: ``0``. +* ``DB_POOL_MAX_LIFETIME``: The maximum lifetime of a connection in the pool, in seconds. Connections used for longer get closed and replaced by a new one. The amount is reduced by a random 10% to avoid mass eviction. Defaults to: ``3600``. +* ``DB_POOL_MAX_IDLE``: Maximum time, in seconds, that a connection can stay unused in the pool before being closed, and the pool shrunk. This only happens to connections more than min_size, if max_size allowed the pool to grow. Defaults to: ``600``. +* ``DB_POOL_RECONNECT_TIMEOUT``: Maximum time, in seconds, the pool will try to create a connection. If a connection attempt fails, the pool will try to reconnect a few times, using an exponential backoff and some random factor to avoid mass attempts. If repeated attempts fail, after reconnect_timeout second the connection attempt is aborted and the reconnect_failed() callback invoked. Defaults to: ``300``. +* ``DB_POOL_NUM_WORKERS``: Number of background worker threads used to maintain the pool state. Background workers are used for example to create new connections and to clean up connections when they are returned to the pool. Defaults to: ``3``. + + +Logging +------- + +* ``LOG_STDOUT``: whether to log to stdout or not. Defaults to: ``True``. +* ``LOG_LEVEL``: control the verbosity of logging output. Available values are ``CRITICAL``, ``ERROR``, ``WARNING``, ``INFO`` and ``DEBUG``. Defaults to: ``INFO``. +* ``LOG_QUERIES``: enable (query) logging at the database backend level. Note that you must also set ``DEBUG=1``, which should be done very sparingly!. Defaults to: ``False``. +* ``LOG_REQUESTS``: enable logging of the outgoing requests. This must be enabled along with `LOG_OUTGOING_REQUESTS_DB_SAVE` to save outgoing request logs in the database. Defaults to: ``False``. +* ``LOG_FORMAT_CONSOLE``: The format for the console logging handler, possible options: ``json``, ``plain_console``. Defaults to: ``json``. +* ``ENABLE_STRUCTLOG_REQUESTS``: enable structured logging of requests. Defaults to: ``True``. +* ``LOG_OUTGOING_REQUESTS_EMIT_BODY``: Whether or not outgoing request bodies should be logged. Defaults to: ``True``. +* ``LOG_OUTGOING_REQUESTS_DB_SAVE``: Whether or not outgoing request logs should be saved to the database. Defaults to: ``False``. +* ``LOG_OUTGOING_REQUESTS_DB_SAVE_BODY``: Whether or not outgoing request bodies should be saved to the database. Defaults to: ``True``. +* ``LOG_OUTGOING_REQUESTS_MAX_AGE``: The amount of time after which request logs should be deleted from the database. Defaults to: ``7``. + + +Cross-Origin-Resource-Sharing +----------------------------- + +* ``CORS_ALLOW_ALL_ORIGINS``: allow cross-domain access from any client. Defaults to: ``False``. +* ``CORS_ALLOWED_ORIGINS``: explicitly list the allowed origins for cross-domain requests. Example: http://localhost:3000,https://some-app.gemeente.nl. Defaults to: ``[]``. +* ``CORS_ALLOWED_ORIGIN_REGEXES``: same as ``CORS_ALLOWED_ORIGINS``, but supports regular expressions. Defaults to: ``[]``. +* ``CORS_EXTRA_ALLOW_HEADERS``: headers that are allowed to be sent as part of the cross-domain request. By default, Authorization, Accept-Crs and Content-Crs are already included. The value of this variable is added to these already included headers. Defaults to: ``[]``. + + +Elastic APM +----------- + +* ``ELASTIC_APM_SERVER_URL``: URL where Elastic APM is hosted. Defaults to: ``None``. +* ``ELASTIC_APM_SERVICE_NAME``: Name of the service for this application in Elastic APM. Defaults to ``opendms - ``. +* ``ELASTIC_APM_SECRET_TOKEN``: Token used to communicate with Elastic APM. Defaults to: ``default``. +* ``ELASTIC_APM_TRANSACTION_SAMPLE_RATE``: By default, the agent will sample every transaction (e.g. request to your service). To reduce overhead and storage requirements, set the sample rate to a value between 0.0 and 1.0. Defaults to: ``0.1``. + + +Content Security Policy +----------------------- + +* ``CSP_EXTRA_DEFAULT_SRC``: Extra default source URLs for CSP other than ``self``. Used for ``img-src``, ``style-src`` and ``script-src``. Defaults to: ``[]``. +* ``CSP_EXTRA_FORM_ACTION``: Additional `form-action` sources. Defaults to: ``[]``. +* ``CSP_FORM_ACTION``: Override the default `form-action` sources. Defaults to: ``['"\'self\'"']``. +* ``CSP_EXTRA_IMG_SRC``: Extra `img-src` sources. Defaults to: ``[]``. +* ``CSP_OBJECT_SRC``: `object-src` sources. Defaults to: ``['"\'none\'"']``. +* ``CSP_REPORT_URI``: URI for CSP report-uri directive. Defaults to: ``None``. +* ``CSP_REPORT_PERCENTAGE``: Fraction (between 0 and 1) of requests to include report-uri directive. Defaults to: ``0.0``. + + +Optional +-------- + +* ``SITE_ID``: The database ID of the site object. You usually won't have to touch this. Defaults to: ``1``. +* ``DEBUG``: Only set this to ``True`` on a local development environment. Various other security settings are derived from this setting!. Defaults to: ``False``. +* ``USE_X_FORWARDED_HOST``: whether to grab the domain/host from the X-Forwarded-Host header or not. This header is typically set by reverse proxies (such as nginx, traefik, Apache...). Note: this is a header that can be spoofed and you need to ensure you control it before enabling this. Defaults to: ``False``. +* ``IS_HTTPS``: Used to construct absolute URLs and controls a variety of security settings. Defaults to the inverse of ``DEBUG``. +* ``EMAIL_PORT``: port number of the outgoing e-mail server. Note that if you're on Google Cloud, sending e-mail via port 25 is completely blocked and you should use 487 for TLS. Defaults to: ``25``. +* ``EMAIL_HOST_USER``: username to connect to the mail server. Defaults to: ``(empty string)``. +* ``EMAIL_HOST_PASSWORD``: password to connect to the mail server. Defaults to: ``(empty string)``. +* ``EMAIL_USE_TLS``: whether to use TLS or not to connect to the mail server. Should be True if you're changing the ``EMAIL_PORT`` to 487. Defaults to: ``False``. +* ``DEFAULT_FROM_EMAIL``: The default email address from which emails are sent. Defaults to: ``opendms@example.com``. +* ``SESSION_COOKIE_AGE``: For how long, in seconds, the session cookie will be valid. Defaults to: ``1209600``. +* ``SESSION_COOKIE_SAMESITE``: The value of the SameSite flag on the session cookie. This flag prevents the cookie from being sent in cross-site requests thus preventing CSRF attacks and making some methods of stealing session cookie impossible.Currently interferes with OIDC. Keep the value set at Lax if used. Defaults to: ``Lax``. +* ``CSRF_COOKIE_SAMESITE``: The value of the SameSite flag on the CSRF cookie. This flag prevents the cookie from being sent in cross-site requests. Defaults to: ``Strict``. +* ``ENVIRONMENT``: An identifier for the environment, displayed in the admin depending on the settings module used and included in the error monitoring (see ``SENTRY_DSN``). The default is set according to ``DJANGO_SETTINGS_MODULE``. +* ``SUBPATH``: If hosted on a subpath, provide the value here. If you provide ``/gateway``, the component assumes its running at the base URL: ``https://somedomain/gateway/``. Defaults to an empty string. Defaults to: ``None``. +* ``RELEASE``: The version number or commit hash of the application (this is also sent to Sentry). +* ``NUM_PROXIES``: the number of reverse proxies in front of the application, as an integer. This is used to determine the actual client IP adres. On Kubernetes with an ingress you typically want to set this to 2. Defaults to: ``1``. +* ``CSRF_TRUSTED_ORIGINS``: A list of trusted origins for unsafe requests (e.g. POST). Defaults to: ``[]``. +* ``NOTIFICATIONS_DISABLED``: indicates whether or not notifications should be sent to the Notificaties API for operations on the API endpoints. Defaults to ``True`` for the ``dev`` environment, otherwise defaults to ``False``. +* ``SITE_DOMAIN``: Defines the primary domain where the application is hosted. Defaults to: ``(empty string)``. +* ``SENTRY_DSN``: URL of the sentry project to send error reports to. Default empty, i.e. -> no monitoring set up. Highly recommended to configure this. +* ``DISABLE_2FA``: Whether or not two factor authentication should be disabled. Defaults to: ``False``. + + + + + +Specifying the environment variables +===================================== + +There are two strategies to specify the environment variables: + +* provide them in a ``.env`` file +* start the component processes (with uwsgi/gunicorn/celery) in a process + manager that defines the environment variables + +Providing a .env file +--------------------- + +This is the most simple setup and easiest to debug. The ``.env`` file must be +at the root of the project - i.e. on the same level as the ``src`` directory ( +NOT *in* the ``src`` directory). + +The syntax is key-value: + +.. code:: + + SOME_VAR=some_value + OTHER_VAR="quoted_value" + + +Provide the envvars via the process manager +------------------------------------------- + +If you use a process manager (such as supervisor/systemd), use their techniques +to define the envvars. The component will pick them up out of the box. diff --git a/docs/installation/config/index.rst b/docs/installation/config/index.rst new file mode 100644 index 00000000..1140b1b8 --- /dev/null +++ b/docs/installation/config/index.rst @@ -0,0 +1,12 @@ +.. _configuration_index: + + +============= +Configuration +============= + +.. toctree:: + :maxdepth: 1 + :caption: Further reading + + env_configuration diff --git a/docs/installation/index.rst b/docs/installation/index.rst new file mode 100644 index 00000000..7689221a --- /dev/null +++ b/docs/installation/index.rst @@ -0,0 +1,18 @@ +.. _installation_index: + +============ +Installation +============ + +In addition to making its source code available publicly, the reference +implementation for Open DMS are also maintained and work out-of-the-box for most use cases. + +For the sake of simplicity, we have chosen to use Docker and Docker Compose for this. + +.. toctree:: + :maxdepth: 1 + :caption: Further reading + + config/index + + diff --git a/docs/logo.png b/docs/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..c5fcf3b461bf673cf5e141d0ab3d0e68de1dea48 GIT binary patch literal 22967 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 Date: Tue, 24 Feb 2026 09:23:16 +0100 Subject: [PATCH 9/9] :rotating_light: [#28] Temporary disable quick-start CI --- .github/workflows/quick-start.yml | 12 ------------ 1 file changed, 12 deletions(-) delete mode 100644 .github/workflows/quick-start.yml diff --git a/.github/workflows/quick-start.yml b/.github/workflows/quick-start.yml deleted file mode 100644 index d915aaf3..00000000 --- a/.github/workflows/quick-start.yml +++ /dev/null @@ -1,12 +0,0 @@ -name: Quick Start - -on: - push: - branches: - - main - pull_request: - workflow_dispatch: - -jobs: - open-api-workflow-quick-start: - uses: maykinmedia/open-api-workflows/.github/workflows/quick-start.yml@v6