From e8ef4550ebc195e7d75a96642cc23df10b663c41 Mon Sep 17 00:00:00 2001 From: Md Bayazid Bostame Date: Sat, 28 Mar 2026 23:53:19 +0100 Subject: [PATCH 1/8] fix: upload release archive in deploy workflows --- .github/workflows/deploy-prod.yml | 17 +++++++++++++++-- .github/workflows/deploy-test.yml | 17 +++++++++++++++-- 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/.github/workflows/deploy-prod.yml b/.github/workflows/deploy-prod.yml index a4ce23d..8a7c618 100644 --- a/.github/workflows/deploy-prod.yml +++ b/.github/workflows/deploy-prod.yml @@ -15,6 +15,18 @@ jobs: - name: Check out code uses: actions/checkout@v5 + - name: Build release archive + run: | + tar \ + --exclude=.git \ + --exclude=.github \ + --exclude=.venv \ + --exclude=__pycache__ \ + --exclude=node_modules \ + --exclude=backend/media \ + --exclude=backend/staticfiles \ + -czf release.tgz . + - name: Upload release bundle uses: appleboy/scp-action@v1.0.0 with: @@ -22,12 +34,11 @@ jobs: username: ${{ secrets.PROD_DEPLOY_USER }} key: ${{ secrets.PROD_DEPLOY_SSH_KEY }} port: ${{ secrets.PROD_DEPLOY_PORT || 22 }} - source: "." + source: "release.tgz" target: ${{ secrets.PROD_DEPLOY_PATH }} rm: false overwrite: true strip_components: 0 - exclude: ".git,.github,.venv,__pycache__,node_modules,backend/media,backend/staticfiles" - name: Deploy over SSH uses: appleboy/ssh-action@v1.2.0 @@ -40,4 +51,6 @@ jobs: set -e DEPLOY_DIR="${{ secrets.PROD_DEPLOY_PATH }}" cd "$DEPLOY_DIR" + tar -xzf release.tgz + rm -f release.tgz RUN_DJANGO_CHECK=1 ./scripts/deploy_stack.sh .env.prod docker-compose.prod.yml diff --git a/.github/workflows/deploy-test.yml b/.github/workflows/deploy-test.yml index 2435b2e..7fc4317 100644 --- a/.github/workflows/deploy-test.yml +++ b/.github/workflows/deploy-test.yml @@ -18,6 +18,18 @@ jobs: - name: Check out code uses: actions/checkout@v5 + - name: Build release archive + run: | + tar \ + --exclude=.git \ + --exclude=.github \ + --exclude=.venv \ + --exclude=__pycache__ \ + --exclude=node_modules \ + --exclude=backend/media \ + --exclude=backend/staticfiles \ + -czf release.tgz . + - name: Upload release bundle uses: appleboy/scp-action@v1.0.0 with: @@ -25,12 +37,11 @@ jobs: username: ${{ secrets.TEST_DEPLOY_USER }} key: ${{ secrets.TEST_DEPLOY_SSH_KEY }} port: ${{ secrets.TEST_DEPLOY_PORT || 22 }} - source: "." + source: "release.tgz" target: ${{ secrets.TEST_DEPLOY_PATH }} rm: false overwrite: true strip_components: 0 - exclude: ".git,.github,.venv,__pycache__,node_modules,backend/media,backend/staticfiles" - name: Deploy over SSH uses: appleboy/ssh-action@v1.2.0 @@ -43,4 +54,6 @@ jobs: set -e DEPLOY_DIR="${{ secrets.TEST_DEPLOY_PATH }}" cd "$DEPLOY_DIR" + tar -xzf release.tgz + rm -f release.tgz RUN_DJANGO_CHECK=0 DEPLOY_HEALTH_URL="http://127.0.0.1:8088/healthz/" ./scripts/deploy_stack.sh .env.test docker-compose.prod.yml From d3c928134628c1eb297c932c67e9a70e1bfaee35 Mon Sep 17 00:00:00 2001 From: Md Bayazid Bostame Date: Sat, 28 Mar 2026 23:55:33 +0100 Subject: [PATCH 2/8] fix: build deployment archive outside repo root --- .github/workflows/deploy-prod.yml | 6 ++++-- .github/workflows/deploy-test.yml | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/.github/workflows/deploy-prod.yml b/.github/workflows/deploy-prod.yml index 8a7c618..0c3f9c7 100644 --- a/.github/workflows/deploy-prod.yml +++ b/.github/workflows/deploy-prod.yml @@ -17,6 +17,7 @@ jobs: - name: Build release archive run: | + rm -f /tmp/release.tgz tar \ --exclude=.git \ --exclude=.github \ @@ -25,7 +26,8 @@ jobs: --exclude=node_modules \ --exclude=backend/media \ --exclude=backend/staticfiles \ - -czf release.tgz . + --exclude=release.tgz \ + -czf /tmp/release.tgz . - name: Upload release bundle uses: appleboy/scp-action@v1.0.0 @@ -34,7 +36,7 @@ jobs: username: ${{ secrets.PROD_DEPLOY_USER }} key: ${{ secrets.PROD_DEPLOY_SSH_KEY }} port: ${{ secrets.PROD_DEPLOY_PORT || 22 }} - source: "release.tgz" + source: "/tmp/release.tgz" target: ${{ secrets.PROD_DEPLOY_PATH }} rm: false overwrite: true diff --git a/.github/workflows/deploy-test.yml b/.github/workflows/deploy-test.yml index 7fc4317..c957077 100644 --- a/.github/workflows/deploy-test.yml +++ b/.github/workflows/deploy-test.yml @@ -20,6 +20,7 @@ jobs: - name: Build release archive run: | + rm -f /tmp/release.tgz tar \ --exclude=.git \ --exclude=.github \ @@ -28,7 +29,8 @@ jobs: --exclude=node_modules \ --exclude=backend/media \ --exclude=backend/staticfiles \ - -czf release.tgz . + --exclude=release.tgz \ + -czf /tmp/release.tgz . - name: Upload release bundle uses: appleboy/scp-action@v1.0.0 @@ -37,7 +39,7 @@ jobs: username: ${{ secrets.TEST_DEPLOY_USER }} key: ${{ secrets.TEST_DEPLOY_SSH_KEY }} port: ${{ secrets.TEST_DEPLOY_PORT || 22 }} - source: "release.tgz" + source: "/tmp/release.tgz" target: ${{ secrets.TEST_DEPLOY_PATH }} rm: false overwrite: true From 0736bebd63205b7484fc543f2f1cdf92fe7a39c7 Mon Sep 17 00:00:00 2001 From: Md Bayazid Bostame Date: Sun, 29 Mar 2026 00:15:19 +0100 Subject: [PATCH 3/8] feat: add deployment host and domain configuration guide --- .env.dev.example | 2 + .env.prod.example | 2 + .env.test.example | 2 + backend/config/settings.py | 23 +++++- backend/workflows/admin_config_views.py | 3 + .../templates/workflows/deployment_hosts.html | 78 +++++++++++++++++++ .../workflows/developer_handbook.html | 27 ++++++- .../templates/workflows/handbook.html | 15 ++++ backend/workflows/urls.py | 1 + backend/workflows/views.py | 4 + 10 files changed, 151 insertions(+), 6 deletions(-) create mode 100644 backend/workflows/templates/workflows/deployment_hosts.html diff --git a/.env.dev.example b/.env.dev.example index ab3ea14..c2eab77 100644 --- a/.env.dev.example +++ b/.env.dev.example @@ -1,3 +1,5 @@ +APP_DOMAIN= +APP_BASE_URL= DJANGO_SECRET_KEY=change-me DJANGO_DEBUG=1 DJANGO_ALLOWED_HOSTS=localhost,127.0.0.1 diff --git a/.env.prod.example b/.env.prod.example index 284485c..5604b8a 100644 --- a/.env.prod.example +++ b/.env.prod.example @@ -1,3 +1,5 @@ +APP_DOMAIN=workdock.example.com +APP_BASE_URL=https://workdock.example.com DJANGO_SECRET_KEY=change-me-long-random-value DJANGO_DEBUG=0 DJANGO_ALLOWED_HOSTS=workdock.example.com diff --git a/.env.test.example b/.env.test.example index e1c216c..b021ffb 100644 --- a/.env.test.example +++ b/.env.test.example @@ -1,3 +1,5 @@ +APP_DOMAIN= +APP_BASE_URL= DJANGO_SECRET_KEY=change-me-long-random-value DJANGO_DEBUG=1 DJANGO_ALLOWED_HOSTS=192.168.2.55,localhost,127.0.0.1 diff --git a/backend/config/settings.py b/backend/config/settings.py index 045551c..a3c70d9 100644 --- a/backend/config/settings.py +++ b/backend/config/settings.py @@ -1,13 +1,32 @@ import os import sys from pathlib import Path +from urllib.parse import urlsplit BASE_DIR = Path(__file__).resolve().parent.parent +def _split_csv_env(name: str, default: str = ''): + return [item.strip() for item in os.getenv(name, default).split(',') if item.strip()] + +def _append_unique(items, value): + if value and value not in items: + items.append(value) + +def _hostname_from_url(url: str) -> str: + try: + return (urlsplit(url).hostname or '').strip() + except ValueError: + return '' + SECRET_KEY = os.getenv('DJANGO_SECRET_KEY', 'unsafe-dev-key') DEBUG = os.getenv('DJANGO_DEBUG', '0') == '1' -ALLOWED_HOSTS = [h.strip() for h in os.getenv('DJANGO_ALLOWED_HOSTS', 'localhost,127.0.0.1').split(',') if h.strip()] -CSRF_TRUSTED_ORIGINS = [o.strip() for o in os.getenv('DJANGO_CSRF_TRUSTED_ORIGINS', '').split(',') if o.strip()] +APP_DOMAIN = os.getenv('APP_DOMAIN', '').strip() +APP_BASE_URL = os.getenv('APP_BASE_URL', '').strip().rstrip('/') +ALLOWED_HOSTS = _split_csv_env('DJANGO_ALLOWED_HOSTS', 'localhost,127.0.0.1') +CSRF_TRUSTED_ORIGINS = _split_csv_env('DJANGO_CSRF_TRUSTED_ORIGINS', '') +_append_unique(ALLOWED_HOSTS, APP_DOMAIN) +_append_unique(ALLOWED_HOSTS, _hostname_from_url(APP_BASE_URL)) +_append_unique(CSRF_TRUSTED_ORIGINS, APP_BASE_URL) # Security hardening SESSION_COOKIE_HTTPONLY = True diff --git a/backend/workflows/admin_config_views.py b/backend/workflows/admin_config_views.py index 91b4ef6..71284b0 100644 --- a/backend/workflows/admin_config_views.py +++ b/backend/workflows/admin_config_views.py @@ -406,5 +406,8 @@ def project_wiki_page_impl(request): def developer_handbook_page_impl(request): return render(request, 'workflows/developer_handbook.html') +def deployment_hosts_page_impl(request): + return render(request, 'workflows/deployment_hosts.html') + def release_checklist_page_impl(request): return render(request, 'workflows/release_checklist.html') diff --git a/backend/workflows/templates/workflows/deployment_hosts.html b/backend/workflows/templates/workflows/deployment_hosts.html new file mode 100644 index 0000000..0ed0232 --- /dev/null +++ b/backend/workflows/templates/workflows/deployment_hosts.html @@ -0,0 +1,78 @@ +{% extends 'workflows/base_shell.html' %} +{% load static i18n %} + +{% block title %}{% trans "Host & Domain Setup" %}{% endblock %} + +{% block extra_css %} + +{% endblock %} + +{% block shell_body %} + {% include 'workflows/includes/app_header.html' with header_show_home=1 header_inside_shell=1 %} +
+
+

{% trans "Host & Domain Setup" %}

+ +
+

{% trans "Reference for configuring hostnames and origins correctly in Django deployments, including how to fix Invalid HTTP_HOST errors." %}

+ +
+

{% trans "Why this error happens" %}

+

{% trans "Django rejects requests whose Host header is not present in ALLOWED_HOSTS. This validation runs before normal page routing, so a broken hostname cannot show a friendly in-app error page on that same host." %}

+

{% trans "Use this guide from a working hostname or IP address to correct the environment configuration." %}

+
+ +
+

{% trans "Recommended environment variables" %}

+
    +
  • APP_DOMAIN: {% trans "canonical hostname without scheme, for example" %} workdock.bostame.de
  • +
  • APP_BASE_URL: {% trans "canonical external URL with scheme, for example" %} https://workdock.bostame.de
  • +
  • DJANGO_ALLOWED_HOSTS: {% trans "comma-separated hostnames and IPs allowed to reach the app" %}
  • +
  • DJANGO_CSRF_TRUSTED_ORIGINS: {% trans "comma-separated origins with scheme for POST and CSRF-safe requests" %}
  • +
+

{% trans "The application automatically adds APP_DOMAIN and the hostname from APP_BASE_URL to the effective host allow-list. APP_BASE_URL is also added to trusted CSRF origins." %}

+
+ +
+

{% trans "Current test deployment example" %}

+
APP_DOMAIN=workdock.bostame.de
+APP_BASE_URL=https://workdock.bostame.de
+DJANGO_ALLOWED_HOSTS=192.168.2.55,localhost,127.0.0.1
+DJANGO_CSRF_TRUSTED_ORIGINS=http://192.168.2.55:8088,https://workdock.bostame.de
+
+ +
+

{% trans "Production example" %}

+
APP_DOMAIN=workdock.example.com
+APP_BASE_URL=https://workdock.example.com
+DJANGO_ALLOWED_HOSTS=workdock.example.com
+DJANGO_CSRF_TRUSTED_ORIGINS=https://workdock.example.com
+

{% trans "Production should run with HTTPS, DEBUG disabled, secure cookies enabled, and SSL redirect enabled." %}

+
+ +
+

{% trans "How to fix a live server" %}

+
    +
  1. {% trans "Log into the server and edit the active env file, for example" %} /opt/workdock/.env.test
  2. +
  3. {% trans "Set APP_DOMAIN and APP_BASE_URL to the real external hostname." %}
  4. +
  5. {% trans "Keep the local IP in DJANGO_ALLOWED_HOSTS if you still use direct IP access." %}
  6. +
  7. {% trans "Restart the stack or rerun the deployment script." %}
  8. +
+
cd /opt/workdock
+nano .env.test
+RUN_DJANGO_CHECK=0 DEPLOY_HEALTH_URL="http://127.0.0.1:8088/healthz/" ./scripts/deploy_stack.sh .env.test docker-compose.prod.yml
+
+ +
+

{% trans "Important rules" %}

+
    +
  • DJANGO_ALLOWED_HOSTS: {% trans "hostnames only, no scheme" %}
  • +
  • DJANGO_CSRF_TRUSTED_ORIGINS: {% trans "must include scheme, for example" %} https://workdock.bostame.de
  • +
  • {% trans "If you use both IP and domain access, keep both in the configuration." %}
  • +
  • {% trans "A broken hostname setup cannot self-heal via the same broken host. Use a working host or IP to access this guide." %}
  • +
+
+
+{% endblock %} diff --git a/backend/workflows/templates/workflows/developer_handbook.html b/backend/workflows/templates/workflows/developer_handbook.html index c0b0a39..b91aae4 100644 --- a/backend/workflows/templates/workflows/developer_handbook.html +++ b/backend/workflows/templates/workflows/developer_handbook.html @@ -33,6 +33,7 @@ Builders Testing Backup + Hosts & Domains CI/CD Deployment Troubleshooting @@ -267,7 +268,25 @@ make backup-verify BACKUP_DIR=backups/backup_YYYYmmdd_HHMMSS
  • The staff UI uses the shared action-progress overlay for backup creation and verification so long-running actions present one standard app behavior.
  • -

    13) CI/CD

    +

    13) Host and Domain Configuration

    +
      +
    • Primary env variables: +
        +
      • APP_DOMAIN: canonical hostname without scheme
      • +
      • APP_BASE_URL: canonical external URL including scheme
      • +
      • DJANGO_ALLOWED_HOSTS: explicit host/IP allow-list
      • +
      • DJANGO_CSRF_TRUSTED_ORIGINS: explicit origin allow-list with scheme
      • +
      +
    • +
    • The settings layer now folds APP_DOMAIN and the hostname from APP_BASE_URL into the effective allowed-host configuration automatically.
    • +
    • APP_BASE_URL is also appended to trusted CSRF origins automatically.
    • +
    • Use APP_DOMAIN and APP_BASE_URL as the primary deployment-facing values instead of repeatedly editing long host/origin strings.
    • +
    • If you also reach the app by IP address, keep the IP in DJANGO_ALLOWED_HOSTS and, if needed, in DJANGO_CSRF_TRUSTED_ORIGINS.
    • +
    • The dedicated runbook page is available at /admin-tools/deployment-hosts/.
    • +
    • An Invalid HTTP_HOST header failure happens before normal page routing, so a broken hostname cannot render a custom error page on that same broken host. Use a working host or IP to access the runbook and fix the env file.
    • +
    + +

    14) CI/CD

    • Repository model: one private GitHub repository, not separate dev/prod repositories.
    • Branch model: develop for the test deployment, main reserved for production.
    • @@ -336,7 +355,7 @@ make backup-verify BACKUP_DIR=backups/backup_YYYYmmdd_HHMMSS The current LAN test deployment intentionally uses DJANGO_DEBUG=1 in .env.test because the security checks correctly reject insecure cookie settings when DEBUG=0 and the deployment is still plain HTTP. This is acceptable for the internal test box only. Production must run with HTTPS and DEBUG=0. -

      14) Deployment

      +

      15) Deployment

      Test server stack

      • Stack file: docker-compose.prod.yml
      • @@ -395,7 +414,7 @@ lxc.mount.entry: /dev/null sys/module/apparmor/parameters/enabled none bind 0 0<
      • Take a snapshot commit before major next-phase work
      • -

        15) Troubleshooting

        +

        16) Troubleshooting

        • Page looks stale: restart web and hard-refresh browser
        • Second request hangs: inspect web logs and verify health endpoint
        • @@ -406,7 +425,7 @@ lxc.mount.entry: /dev/null sys/module/apparmor/parameters/enabled none bind 0 0<
        • Requests dependency warning appears: verify chardet==5.2.0 is installed in the rebuilt image and restart web/worker
        -

        16) Security and Maintenance Notes

        +

        17) Security and Maintenance Notes

        • Containers run as non-root app user.
        • Keep secrets in .env, not in tracked files.
        • diff --git a/backend/workflows/templates/workflows/handbook.html b/backend/workflows/templates/workflows/handbook.html index 3e8f56f..3bf0ab6 100644 --- a/backend/workflows/templates/workflows/handbook.html +++ b/backend/workflows/templates/workflows/handbook.html @@ -48,6 +48,21 @@ +
          +
          {% trans "Deployment" %}
          +

          {% trans "Host & Domain Setup" %}

          +

          {% trans "Runbook for ALLOWED_HOSTS, CSRF trusted origins, canonical domain variables, and how to resolve Invalid HTTP_HOST errors safely." %}

          +
            +
          • {% trans "APP_DOMAIN and APP_BASE_URL" %}
          • +
          • {% trans "ALLOWED_HOSTS and CSRF origin rules" %}
          • +
          • {% trans "local test versus production examples" %}
          • +
          • {% trans "error recovery steps for wrong hostname setup" %}
          • +
          + +
          +
          {% trans "Release" %}

          {% trans "Release Checklist" %}

          diff --git a/backend/workflows/urls.py b/backend/workflows/urls.py index 41421fe..ad0aa73 100644 --- a/backend/workflows/urls.py +++ b/backend/workflows/urls.py @@ -49,6 +49,7 @@ urlpatterns = [ path('admin-tools/users//delete/', views.delete_user_from_admin, name='delete_user_from_admin'), path('admin-tools/wiki/', views.project_wiki_page, name='project_wiki_page'), path('admin-tools/developer-handbook/', views.developer_handbook_page, name='developer_handbook_page'), + path('admin-tools/deployment-hosts/', views.deployment_hosts_page, name='deployment_hosts_page'), path('admin-tools/release-checklist/', views.release_checklist_page, name='release_checklist_page'), path('admin-tools/audit-log/', views.audit_log_page, name='audit_log_page'), path('admin-tools/backups/', views.backup_recovery_page, name='backup_recovery_page'), diff --git a/backend/workflows/views.py b/backend/workflows/views.py index ef340b6..3b4efaa 100644 --- a/backend/workflows/views.py +++ b/backend/workflows/views.py @@ -333,6 +333,10 @@ def project_wiki_page(request): def developer_handbook_page(request): return admin_config_views.developer_handbook_page_impl(request) +@_require_capability('view_docs') +def deployment_hosts_page(request): + return admin_config_views.deployment_hosts_page_impl(request) + @_require_capability('view_docs') def release_checklist_page(request): From 1864218d16e0a9f0d2ad60955715f834fff60833 Mon Sep 17 00:00:00 2001 From: Md Bayazid Bostame Date: Sun, 29 Mar 2026 00:23:44 +0100 Subject: [PATCH 4/8] feat: add local test deployment helper --- DEPLOYMENT.md | 37 ++++++++++++++- .../workflows/developer_handbook.html | 5 +++ scripts/deploy_test_from_mac.sh | 45 +++++++++++++++++++ 3 files changed, 86 insertions(+), 1 deletion(-) create mode 100755 scripts/deploy_test_from_mac.sh diff --git a/DEPLOYMENT.md b/DEPLOYMENT.md index 55b88a4..2af6f36 100644 --- a/DEPLOYMENT.md +++ b/DEPLOYMENT.md @@ -161,7 +161,37 @@ And a real HTTPS hostname in: - `SITE_ADDRESS` ## Manual test deployment -If you need to deploy manually on the test server: +For a LAN-only test server, this is the recommended CD path. + +### One-command local deployment from your Mac +Use: +```bash +./scripts/deploy_test_from_mac.sh +``` + +What it does: +1. requires the current branch to be `develop` +2. fast-forwards from `origin/develop` +3. syncs the repo to `/opt/workdock` via `rsync` +4. runs the remote deployment script +5. verifies the health endpoint + +Default assumptions: +- target host: `root@192.168.2.55` +- target path: `/opt/workdock` +- env file: `.env.test` +- health URL: `http://192.168.2.55:8088/healthz/` + +Optional overrides: +```bash +DEPLOY_HOST=root@192.168.2.55 \ +DEPLOY_PATH=/opt/workdock \ +HEALTH_URL=http://192.168.2.55:8088/healthz/ \ +./scripts/deploy_test_from_mac.sh +``` + +### Manual server-side deploy only +If the latest code is already on the server: ```bash cd /opt/workdock RUN_DJANGO_CHECK=0 DEPLOY_HEALTH_URL="http://127.0.0.1:8088/healthz/" ./scripts/deploy_stack.sh .env.test docker-compose.prod.yml @@ -185,6 +215,11 @@ Behavior: - uploads the working tree to the server over SSH - runs the server deployment script +Important: +- this workflow only works if the GitHub runner can reach the server +- it is not suitable for a pure LAN-only target using a private IP like `192.168.2.55` +- for the current environment, prefer the local Mac deploy script or a self-hosted runner on the LAN + ### Production deployment workflow File: - [deploy-prod.yml](/Users/bostame/Documents/workdock-platform/.github/workflows/deploy-prod.yml) diff --git a/backend/workflows/templates/workflows/developer_handbook.html b/backend/workflows/templates/workflows/developer_handbook.html index b91aae4..1ee31f2 100644 --- a/backend/workflows/templates/workflows/developer_handbook.html +++ b/backend/workflows/templates/workflows/developer_handbook.html @@ -294,6 +294,7 @@ make backup-verify BACKUP_DIR=backups/backup_YYYYmmdd_HHMMSS
        • Current production workflow file: .github/workflows/deploy-prod.yml.
        • GitHub Actions uploads the working tree to the server over SSH. The server does not clone from GitHub.
        • This is intentional for a private repository: it removes the need to store GitHub deploy keys on the target server.
        • +
        • However, GitHub-hosted runners cannot reach a LAN-only private IP like 192.168.2.55. For the current local test server, the correct CD path is manual deployment from a LAN machine or a self-hosted runner inside the same network.

        GitHub Environments

          @@ -382,6 +383,10 @@ make backup-verify BACKUP_DIR=backups/backup_YYYYmmdd_HHMMSS
        • Wait until /healthz/ becomes healthy
        • Manual deploy

          +

          The preferred current test-deployment path is the local helper script from a Mac or another LAN-connected workstation:

          +
          ./scripts/deploy_test_from_mac.sh
          +

          This script fast-forwards develop, syncs the repo to the server with rsync, runs the remote deployment, and verifies the health endpoint.

          +

          Direct server-side deploy is still available if the code is already on the server:

          cd /opt/workdock
           RUN_DJANGO_CHECK=0 DEPLOY_HEALTH_URL="http://127.0.0.1:8088/healthz/" ./scripts/deploy_stack.sh .env.test docker-compose.prod.yml

          Validation after deploy

          diff --git a/scripts/deploy_test_from_mac.sh b/scripts/deploy_test_from_mac.sh new file mode 100755 index 0000000..0ad18b6 --- /dev/null +++ b/scripts/deploy_test_from_mac.sh @@ -0,0 +1,45 @@ +#!/usr/bin/env bash +set -euo pipefail + +REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +DEPLOY_HOST="${DEPLOY_HOST:-root@192.168.2.55}" +DEPLOY_PATH="${DEPLOY_PATH:-/opt/workdock}" +HEALTH_URL="${HEALTH_URL:-http://192.168.2.55:8088/healthz/}" +REMOTE_ENV_FILE="${REMOTE_ENV_FILE:-.env.test}" +COMPOSE_FILE="${COMPOSE_FILE:-docker-compose.prod.yml}" +RUN_DJANGO_CHECK="${RUN_DJANGO_CHECK:-0}" +SSH_CMD="${SSH_CMD:-ssh -4}" +RSYNC_SSH="${RSYNC_SSH:-ssh -4}" + +cd "$REPO_ROOT" + +current_branch="$(git branch --show-current)" +if [[ "$current_branch" != "develop" ]]; then + echo "Expected branch 'develop' for test deployment, got '$current_branch'." >&2 + echo "Switch to develop or override intentionally before deploying." >&2 + exit 1 +fi + +echo "Updating local branch from origin/develop..." +git pull --ff-only origin develop + +echo "Syncing repository to ${DEPLOY_HOST}:${DEPLOY_PATH} ..." +rsync -az --delete \ + --exclude '.git' \ + --exclude '.github' \ + --exclude '.venv' \ + --exclude '__pycache__' \ + --exclude 'node_modules' \ + --exclude 'backend/media' \ + --exclude 'backend/staticfiles' \ + -e "$RSYNC_SSH" \ + "$REPO_ROOT"/ \ + "${DEPLOY_HOST}:${DEPLOY_PATH}/" + +echo "Running remote deployment..." +$SSH_CMD "$DEPLOY_HOST" \ + "cd '$DEPLOY_PATH' && RUN_DJANGO_CHECK='$RUN_DJANGO_CHECK' DEPLOY_HEALTH_URL='$HEALTH_URL' ./scripts/deploy_stack.sh '$REMOTE_ENV_FILE' '$COMPOSE_FILE'" + +echo "Verifying health endpoint..." +curl --fail --silent --show-error --max-time 10 "$HEALTH_URL" >/dev/null +echo "Test deployment healthy: $HEALTH_URL" From b7a2d84b4510c00b161c8311a26b2cfa3171859c Mon Sep 17 00:00:00 2001 From: Md Bayazid Bostame Date: Sun, 29 Mar 2026 00:31:03 +0100 Subject: [PATCH 5/8] fix: preserve server env files during local deploy --- DEPLOYMENT.md | 6 ++++++ .../workflows/templates/workflows/developer_handbook.html | 1 + scripts/deploy_test_from_mac.sh | 2 ++ 3 files changed, 9 insertions(+) diff --git a/DEPLOYMENT.md b/DEPLOYMENT.md index 2af6f36..1d39405 100644 --- a/DEPLOYMENT.md +++ b/DEPLOYMENT.md @@ -176,6 +176,12 @@ What it does: 4. runs the remote deployment script 5. verifies the health endpoint +Important: +- the helper preserves server-local env files: + - `.env.test` + - `.env.prod` +- those files are not supposed to be replaced from your Mac checkout + Default assumptions: - target host: `root@192.168.2.55` - target path: `/opt/workdock` diff --git a/backend/workflows/templates/workflows/developer_handbook.html b/backend/workflows/templates/workflows/developer_handbook.html index 1ee31f2..f78f29c 100644 --- a/backend/workflows/templates/workflows/developer_handbook.html +++ b/backend/workflows/templates/workflows/developer_handbook.html @@ -386,6 +386,7 @@ make backup-verify BACKUP_DIR=backups/backup_YYYYmmdd_HHMMSS

          The preferred current test-deployment path is the local helper script from a Mac or another LAN-connected workstation:

          ./scripts/deploy_test_from_mac.sh

          This script fast-forwards develop, syncs the repo to the server with rsync, runs the remote deployment, and verifies the health endpoint.

          +

          The script explicitly preserves server-local env files such as .env.test and .env.prod so deployment does not wipe machine-specific secrets.

          Direct server-side deploy is still available if the code is already on the server:

          cd /opt/workdock
           RUN_DJANGO_CHECK=0 DEPLOY_HEALTH_URL="http://127.0.0.1:8088/healthz/" ./scripts/deploy_stack.sh .env.test docker-compose.prod.yml
          diff --git a/scripts/deploy_test_from_mac.sh b/scripts/deploy_test_from_mac.sh index 0ad18b6..0cf80aa 100755 --- a/scripts/deploy_test_from_mac.sh +++ b/scripts/deploy_test_from_mac.sh @@ -25,6 +25,8 @@ git pull --ff-only origin develop echo "Syncing repository to ${DEPLOY_HOST}:${DEPLOY_PATH} ..." rsync -az --delete \ + --filter 'P .env.test' \ + --filter 'P .env.prod' \ --exclude '.git' \ --exclude '.github' \ --exclude '.venv' \ From cd0c0ad65ac71c69c7831f8d3896257582f793d6 Mon Sep 17 00:00:00 2001 From: Md Bayazid Bostame Date: Sun, 29 Mar 2026 00:34:19 +0100 Subject: [PATCH 6/8] chore: improve local deploy helper feedback --- DEPLOYMENT.md | 8 +++++--- .../templates/workflows/developer_handbook.html | 2 +- scripts/deploy_test_from_mac.sh | 10 ++++++++++ 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/DEPLOYMENT.md b/DEPLOYMENT.md index 1d39405..4d45bcd 100644 --- a/DEPLOYMENT.md +++ b/DEPLOYMENT.md @@ -172,9 +172,11 @@ Use: What it does: 1. requires the current branch to be `develop` 2. fast-forwards from `origin/develop` -3. syncs the repo to `/opt/workdock` via `rsync` -4. runs the remote deployment script -5. verifies the health endpoint +3. verifies that the server env file exists before syncing +4. syncs the repo to `/opt/workdock` via `rsync` +5. runs the remote deployment script +6. verifies the health endpoint +7. prints the deployed commit and branch Important: - the helper preserves server-local env files: diff --git a/backend/workflows/templates/workflows/developer_handbook.html b/backend/workflows/templates/workflows/developer_handbook.html index f78f29c..d72a959 100644 --- a/backend/workflows/templates/workflows/developer_handbook.html +++ b/backend/workflows/templates/workflows/developer_handbook.html @@ -385,7 +385,7 @@ make backup-verify BACKUP_DIR=backups/backup_YYYYmmdd_HHMMSS

          Manual deploy

          The preferred current test-deployment path is the local helper script from a Mac or another LAN-connected workstation:

          ./scripts/deploy_test_from_mac.sh
          -

          This script fast-forwards develop, syncs the repo to the server with rsync, runs the remote deployment, and verifies the health endpoint.

          +

          This script fast-forwards develop, checks that the remote env file exists, syncs the repo to the server with rsync, runs the remote deployment, verifies the health endpoint, and prints the deployed commit hash.

          The script explicitly preserves server-local env files such as .env.test and .env.prod so deployment does not wipe machine-specific secrets.

          Direct server-side deploy is still available if the code is already on the server:

          cd /opt/workdock
          diff --git a/scripts/deploy_test_from_mac.sh b/scripts/deploy_test_from_mac.sh
          index 0cf80aa..817a112 100755
          --- a/scripts/deploy_test_from_mac.sh
          +++ b/scripts/deploy_test_from_mac.sh
          @@ -23,6 +23,13 @@ fi
           echo "Updating local branch from origin/develop..."
           git pull --ff-only origin develop
           
          +echo "Checking remote env file..."
          +$SSH_CMD "$DEPLOY_HOST" "test -f '$DEPLOY_PATH/$REMOTE_ENV_FILE'" || {
          +  echo "Missing remote env file: $DEPLOY_PATH/$REMOTE_ENV_FILE" >&2
          +  echo "Create or restore the server env file before deploying." >&2
          +  exit 1
          +}
          +
           echo "Syncing repository to ${DEPLOY_HOST}:${DEPLOY_PATH} ..."
           rsync -az --delete \
             --filter 'P .env.test' \
          @@ -44,4 +51,7 @@ $SSH_CMD "$DEPLOY_HOST" \
           
           echo "Verifying health endpoint..."
           curl --fail --silent --show-error --max-time 10 "$HEALTH_URL" >/dev/null
          +commit_sha="$(git rev-parse --short HEAD)"
           echo "Test deployment healthy: $HEALTH_URL"
          +echo "Deployed commit: $commit_sha"
          +echo "Branch: $current_branch"
          
          From da1d099918aef902340c761ea47a55df02754332 Mon Sep 17 00:00:00 2001
          From: Md Bayazid  Bostame 
          Date: Sun, 29 Mar 2026 00:38:03 +0100
          Subject: [PATCH 7/8] docs: simplify handbook ci cd instructions
          
          ---
           .../workflows/developer_handbook.html         | 157 ++++++++++--------
           1 file changed, 91 insertions(+), 66 deletions(-)
          
          diff --git a/backend/workflows/templates/workflows/developer_handbook.html b/backend/workflows/templates/workflows/developer_handbook.html
          index d72a959..04d5474 100644
          --- a/backend/workflows/templates/workflows/developer_handbook.html
          +++ b/backend/workflows/templates/workflows/developer_handbook.html
          @@ -287,73 +287,98 @@ make backup-verify BACKUP_DIR=backups/backup_YYYYmmdd_HHMMSS

        14) CI/CD

        -
          -
        • Repository model: one private GitHub repository, not separate dev/prod repositories.
        • -
        • Branch model: develop for the test deployment, main reserved for production.
        • -
        • Current test workflow file: .github/workflows/deploy-test.yml.
        • -
        • Current production workflow file: .github/workflows/deploy-prod.yml.
        • -
        • GitHub Actions uploads the working tree to the server over SSH. The server does not clone from GitHub.
        • -
        • This is intentional for a private repository: it removes the need to store GitHub deploy keys on the target server.
        • -
        • However, GitHub-hosted runners cannot reach a LAN-only private IP like 192.168.2.55. For the current local test server, the correct CD path is manual deployment from a LAN machine or a self-hosted runner inside the same network.
        • -
        -

        GitHub Environments

        -
          -
        • Create GitHub environments named development and production.
        • -
        • Development secrets: -
            -
          • TEST_DEPLOY_HOST
          • -
          • TEST_DEPLOY_USER
          • -
          • TEST_DEPLOY_PORT
          • -
          • TEST_DEPLOY_PATH
          • -
          • TEST_DEPLOY_SSH_KEY
          • -
          -
        • -
        • Production secrets: -
            -
          • PROD_DEPLOY_HOST
          • -
          • PROD_DEPLOY_USER
          • -
          • PROD_DEPLOY_PORT
          • -
          • PROD_DEPLOY_PATH
          • -
          • PROD_DEPLOY_SSH_KEY
          • -
          -
        • -
        -

        Exact GitHub UI steps

        -
          -
        1. Open the private repository on GitHub.
        2. -
        3. Open Settings.
        4. -
        5. Open Environments in the left sidebar.
        6. -
        7. Create the environment development.
        8. -
        9. Create the environment production.
        10. -
        11. Open development.
        12. -
        13. Under Environment secrets, add the deployment secrets one by one.
        14. -
        15. Repeat later for production.
        16. -
        -

        Current test deployment values

        -
          -
        • Host: 192.168.2.55
        • -
        • User: root
        • -
        • Path: /opt/workdock
        • -
        • URL: http://192.168.2.55:8088
        • -
        -

        Current development secrets

        -
          -
        • TEST_DEPLOY_HOST=192.168.2.55
        • -
        • TEST_DEPLOY_USER=root
        • -
        • TEST_DEPLOY_PORT=22
        • -
        • TEST_DEPLOY_PATH=/opt/workdock
        • -
        • TEST_DEPLOY_SSH_KEY=<full private key content>
        • -
        -

        First GitHub Actions test

        -
          -
        1. Open GitHub Actions.
        2. -
        3. Run the workflow Deploy Test on branch develop.
        4. -
        5. Wait for the SSH upload and deploy steps to finish successfully.
        6. -
        7. Verify http://192.168.2.55:8088/healthz/ returns HTTP 200.
        8. -
        9. Then verify the app UI in the browser.
        10. -
        +
        +

        Current operating model

        +
          +
        • Repository model: one private GitHub repository.
        • +
        • Working branch: develop.
        • +
        • Stable branch: main.
        • +
        • GitHub Actions is used for CI.
        • +
        • Current test deployment is done manually from a LAN-connected machine, not from GitHub-hosted runners.
        • +
        +
        + +
        +

        Why deploy is manual right now

        +

        The test server is inside the local network and uses a private IP address 192.168.2.55. GitHub-hosted runners on the public internet cannot reliably reach that target. Because of that, the correct deployment path today is:

        +
          +
        1. push code to GitHub
        2. +
        3. let GitHub run CI
        4. +
        5. deploy from the Mac on the same LAN
        6. +
        +

        Automatic CD from GitHub becomes appropriate only after moving to a public server or using a self-hosted runner inside the LAN.

        +
        + +
        +

        What to do for normal work

        +
          +
        1. Start from develop.
        2. +
        3. Do the implementation work.
        4. +
        5. Push to GitHub.
        6. +
        7. Let CI finish.
        8. +
        9. Run the local test deployment helper from the Mac.
        10. +
        11. Verify the updated version in the browser.
        12. +
        13. When the integration line is stable, merge develop into main.
        14. +
        +
        + +
        +

        One-command test deployment

        +

        From the Mac on the same network:

        +
        git checkout develop
        +./scripts/deploy_test_from_mac.sh
        +

        This helper script does all of the following:

        +
          +
        1. checks that the current branch is develop
        2. +
        3. fast-forwards from origin/develop
        4. +
        5. checks that the server env file exists
        6. +
        7. syncs the repository to /opt/workdock with rsync
        8. +
        9. preserves server-local env files like .env.test and .env.prod
        10. +
        11. runs the remote deployment script
        12. +
        13. waits for the health endpoint
        14. +
        15. prints the deployed commit and branch
        16. +
        +
        + +
        +

        Current test server values

        +
          +
        • Host: 192.168.2.55
        • +
        • SSH user: root
        • +
        • Deploy path: /opt/workdock
        • +
        • Env file: .env.test
        • +
        • Health URL: http://192.168.2.55:8088/healthz/
        • +
        • Public app URL: https://workdock.bostame.de/
        • +
        +
        + +
        +

        GitHub Actions status

        +
          +
        • .github/workflows/deploy-test.yml exists, but GitHub-hosted deploy to the LAN server is not the recommended path right now.
        • +
        • .github/workflows/deploy-prod.yml exists for later production use.
        • +
        • The practical use of GitHub today is CI, code review, PRs, and branch history.
        • +
        +
        + +
        +

        If the local deploy helper fails

        +
          +
        1. Check whether /opt/workdock/.env.test still exists on the server.
        2. +
        3. Check SSH access from the Mac: +
          ssh -4 root@192.168.2.55
          +
        4. +
        5. Check server health directly: +
          curl -I http://192.168.2.55:8088/healthz/
          +
        6. +
        7. Check container status: +
          ssh root@192.168.2.55 "cd /opt/workdock && docker compose --env-file .env.test -f docker-compose.prod.yml ps"
          +
        8. +
        +
        +
        - The current LAN test deployment intentionally uses DJANGO_DEBUG=1 in .env.test because the security checks correctly reject insecure cookie settings when DEBUG=0 and the deployment is still plain HTTP. This is acceptable for the internal test box only. Production must run with HTTPS and DEBUG=0. + The current LAN test deployment intentionally uses DJANGO_DEBUG=1 in .env.test because the security checks correctly reject insecure cookie settings when DEBUG=0 and the deployment is still plain HTTP behind a local test topology. This is acceptable for the test box only. Production must run with HTTPS and DEBUG=0.

        15) Deployment

        From 48afccbca3fa573b26c9c184dd7bf9152d2f9526 Mon Sep 17 00:00:00 2001 From: Md Bayazid Bostame Date: Sun, 29 Mar 2026 01:26:20 +0100 Subject: [PATCH 8/8] docs: expand engineering handbook and contributor guide --- CONTRIBUTING.md | 119 ++++++++++++++++++ .../workflows/developer_handbook.html | 103 ++++++++++++--- .../templates/workflows/handbook.html | 3 +- 3 files changed, 204 insertions(+), 21 deletions(-) create mode 100644 CONTRIBUTING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..6f6fce1 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,119 @@ +# Contributing Guide + +## Purpose +This repository is the standalone productized Workdock platform. + +Use this file as the quick-start guide for future coders. It complements: +- `DEPLOYMENT.md` +- `backend/workflows/templates/workflows/project_wiki.html` +- `backend/workflows/templates/workflows/developer_handbook.html` + +If you change architecture, deployment flow, or operational behavior, update the relevant documentation in the same branch. + +## Branch Policy +- `develop`: active integration branch +- `main`: stable branch for production promotion +- short-lived feature branches: branch from `develop`, merge back into `develop` + +Do not use `main` as the default development branch. + +## Current Delivery Model +- GitHub Actions is used for CI +- the current test server is local/LAN-only +- deployment to the test server is manual from a LAN-connected machine + +Standard test deployment command: + +```bash +./scripts/deploy_test_from_mac.sh +``` + +Why: +- GitHub-hosted runners cannot reliably reach the private LAN target at `192.168.2.55` + +## Architecture Summary +Main stack: +- Django +- Celery +- PostgreSQL +- Redis +- Caddy + +Important repository areas: +- `backend/config/`: Django configuration +- `backend/workflows/`: application code +- `backend/workflows/templates/workflows/`: templates and in-app documentation +- `backend/workflows/static/workflows/`: CSS and JS +- `backend/media/templates/`: PDF HTML templates + +Current backend modularization: +- `views.py`: thin route wrapper layer +- split view modules: + - `request_views.py` + - `account_views.py` + - `admin_config_views.py` + - `form_builder_views.py` + - `observability_views.py` + - `integration_admin_views.py` + - `welcome_email_views.py` + - `intro_builder_views.py` +- `tasks.py`: async task entrypoints/orchestration +- `pdf_rendering.py` and `pdf_sections.py`: PDF rendering +- `email_workflows.py` and `notification_dispatch.py`: mail/notification orchestration +- `models.py`: stable import facade over split model modules + +## Engineering Rules +1. Preserve runtime behavior while refactoring. +2. Prefer shared UI/system primitives over page-local one-off patterns. +3. Do not silently overwrite environment-specific configuration data. +4. Treat database-backed admin configuration as runtime state, not seed data. +5. Update docs when workflow or architecture changes. + +## Code vs Data +Not all visible behavior comes from code. + +Code-driven: +- page layout +- permissions +- routing +- task behavior +- deploy scripts + +Database-driven: +- app registry order and visibility +- branding and company config +- builder settings +- intro checklist items +- notification templates and rules + +If local UI and server UI differ, check the database-backed config before assuming a code drift. + +## Testing Rules +Minimum validation after non-trivial changes: + +```bash +docker compose exec -T web python manage.py check +docker compose exec -T web python manage.py test +``` + +Also verify specifically when changing: +- PDFs +- deployment/config +- templates/UI + +## Documentation Rules +Keep these current: +- `DEPLOYMENT.md` +- `PRODUCTIZATION_ROADMAP.md` +- `backend/workflows/templates/workflows/project_wiki.html` +- `backend/workflows/templates/workflows/developer_handbook.html` + +## Recommended Change Flow +1. Start from `develop` +2. Implement the change +3. Validate locally +4. Update docs if needed +5. Push to GitHub +6. Let CI run +7. Deploy from the Mac if runtime verification is needed +8. Promote `develop` to `main` when stable diff --git a/backend/workflows/templates/workflows/developer_handbook.html b/backend/workflows/templates/workflows/developer_handbook.html index 04d5474..01b9dea 100644 --- a/backend/workflows/templates/workflows/developer_handbook.html +++ b/backend/workflows/templates/workflows/developer_handbook.html @@ -23,9 +23,11 @@
        Overview Structure + Workflow Local Dev Docker Database + Guidelines Translations PDF Pipeline Email Pipeline @@ -47,6 +49,7 @@
      • Repository: workdock-platform (current local path; legacy compose project name may still be retained for runtime continuity)
      • Main stack: Django + Celery + PostgreSQL + Redis + MailHog
      • Runtime mode: Docker Compose for local development and staging-style operation
      • +
      • Repository-level quick-start guide for future coders: CONTRIBUTING.md
      @@ -54,6 +57,9 @@
      • /backend/config/: Django settings, WSGI, URL config
      • /backend/workflows/: application logic, views, models, tasks, templates, static assets
      • +
      • /backend/workflows/views.py: thin route wrapper layer; most view logic now lives in split modules by domain
      • +
      • /backend/workflows/models.py: stable model import surface over split model modules
      • +
      • /backend/workflows/tasks.py: async task entrypoints; PDF and notification logic were moved into dedicated modules
      • /backend/workflows/templates/workflows/base_shell.html: standard page shell for new staff-facing pages
      • /backend/workflows/roles.py: centralized role names, capability matrix, and template permission helpers
      • Rule: all interactive app pages should extend base_shell.html; do not rebuild topbar/frame logic in page-local templates.
      • @@ -65,7 +71,29 @@
      • /.github/workflows/i18n.yml: translation compile validation in CI
      -

      3) Local Development Workflow

      +

      3) Working Model

      +
      +

      Branch strategy

      +
        +
      • develop is the active integration branch.
      • +
      • main is the stable branch intended for production promotion.
      • +
      • Feature work should start from develop and merge back into develop.
      • +
      +
      +
      +

      Normal change flow

      +
        +
      1. start from develop
      2. +
      3. implement the change
      4. +
      5. run validation
      6. +
      7. update docs if architecture or workflow changed
      8. +
      9. push to GitHub and let CI run
      10. +
      11. deploy from the Mac if test-server verification is needed
      12. +
      13. promote develop into main when stable
      14. +
      +
      + +

      4) Local Development Workflow

      Start

      cd /path/to/workdock-platform
       docker compose up -d --build
      @@ -81,7 +109,7 @@ docker compose up -d --build
    • User: user_test / user12345
    -

    4) Docker Operations

    +

    5) Docker Operations

    docker compose up -d --build
     docker compose restart web
     docker compose restart worker
    @@ -93,7 +121,7 @@ docker compose down -v
    The source code is bind-mounted into the container. Most template/view/static changes only require a web restart, not a full rebuild. Image changes such as system packages require docker compose up -d --build. -

    5) Database and Migrations

    +

    6) Database and Migrations

    docker compose exec -T web python manage.py makemigrations
     docker compose exec -T web python manage.py migrate
     docker compose exec -T web python manage.py check
    @@ -101,6 +129,7 @@ docker compose exec -T web python manage.py check
  • Never edit or remove historical migrations casually.
  • When adding fields to builder-driven models, preserve fallback behavior for existing rows.
  • Fresh boot sequence runs migrations automatically in entrypoint-web.sh.
  • +
  • Do not assume database-backed admin config matches between local and deployed environments.
  • Role and Permission Model

    @@ -117,7 +146,27 @@ docker compose exec -T web python manage.py check
  • When adding a new operational page or action, define the capability in roles.py, gate the view, and hide the UI affordance when the capability is absent.
  • -

    6) Translation Workflow

    +

    7) Engineering Guidelines

    +
    +

    Core rules

    +
      +
    1. Preserve behavior while refactoring.
    2. +
    3. Prefer shared components over page-local special cases.
    4. +
    5. Do not overwrite environment-specific runtime config as a side effect of code deploys.
    6. +
    7. Keep code-driven behavior and data-driven behavior mentally separate.
    8. +
    9. Update documentation in the same branch when operational workflow changes.
    10. +
    +
    +
    +

    Code vs data

    +
      +
    • Code-driven: routing, permissions, deployment scripts, rendering, tasks, layout.
    • +
    • Data-driven: app registry order, branding, company config, builder config, intro checklist items, notification templates.
    • +
    • If local UI and server UI differ, inspect runtime configuration before assuming code drift.
    • +
    +
    + +

    8) Translation Workflow

    Standard Django i18n path

    make i18n-update-en
     make i18n-compile
    @@ -134,11 +183,12 @@ docker compose exec -T web django-admin compilemessages
  • preferred_language is normalized in model save() and also has a DB default of de, so alternate creation paths cannot insert null values.
  • -

    7) PDF Pipeline

    +

    9) PDF Pipeline

    • PDF generation is HTML-to-PDF using xhtml2pdf.
    • Letterhead overlay defaults to templates.pdf, but can now be replaced from Admin Apps → Branding.
    • -
    • Main logic lives in backend/workflows/tasks.py.
    • +
    • Task entrypoints live in backend/workflows/tasks.py.
    • +
    • Rendering and section-building now live in pdf_rendering.py and pdf_sections.py.
    • Fixed PDF labels/headings are rendered from task-level DE/EN text dictionaries, not hard-coded directly in request processing logic.
    • PDF language follows the normalized request preferred_language, with German fallback.
    • Key templates: @@ -154,18 +204,19 @@ docker compose exec -T web django-admin compilemessages xhtml2pdf is sensitive to layout complexity. Keep print templates conservative and verify every structural change with a real generated PDF. -

      8) Email Pipeline

      +

      10) Email Pipeline

        -
      • Notification defaults are defined in DEFAULT_NOTIFICATION_TEMPLATES in tasks.py.
      • +
      • Notification defaults are defined in the workflow/email orchestration layer.
      • Admin-configured overrides live in NotificationTemplate and NotificationRule.
      • Both models now support explicit DE/EN content fields. Prefer filling both languages in the frontend integrations UI instead of embedding mixed-language copy into a single template.
      • Welcome-email configuration under Admin Apps has the same DE/EN split and follows the same runtime language selection.
      • The request objects passed into email tasks always carry a normalized preferred_language value.
      • Mail sending uses Celery tasks and supports test mode redirection.
      • +
      • Related helper modules include email_workflows.py and notification_dispatch.py.
      • MailHog is the local verification path when test mode is active.
      -

      9) Nextcloud Integration

      +

      11) Nextcloud Integration

      • Configured from Admin Apps → Integrations.
      • Upload logic lives in backend/workflows/services.py.
      • @@ -175,7 +226,7 @@ docker compose exec -T web django-admin compilemessages
      • Do not point remote backup at the same Nextcloud directory used for normal onboarding/offboarding document uploads.
      -

      10) Branding

      +

      12) Branding

      • Portal-level branding is stored in the singleton model PortalBranding.
      • Configured from Admin Apps → Branding.
      • @@ -186,7 +237,7 @@ docker compose exec -T web django-admin compilemessages
      • User invitation emails and welcome-template fallbacks also use the configured branding defaults.
      -

      10b) App Registry

      +

      12b) App Registry

      • Registry definitions live in workflows/app_registry.py.
      • DB overrides live in PortalAppConfig.
      • @@ -195,7 +246,7 @@ docker compose exec -T web django-admin compilemessages
      • Management UI: /admin-tools/apps/ for Platform Owner.
      -

      10c) Trial Lifecycle

      +

      12c) Trial Lifecycle

      • Deployment-level trial settings are stored in the singleton model PortalTrialConfig.
      • Management UI: /admin-tools/trial/ for Platform Owner.
      • @@ -207,11 +258,14 @@ docker compose exec -T web django-admin compilemessages
      -

      11) Builder Architecture

      +

      13) Builder Architecture

      Form Builder

      • Model: FormFieldConfig + FormOption
      • Controls field order, visibility, required flags, option sets, and bilingual label/help-text overrides
      • +
      • Static definitions live in form_builder_config.py.
      • +
      • Runtime behavior lives in form_builder_runtime.py.
      • +
      • form_builder.py is the stable facade import path.

      Intro Builder

        @@ -232,7 +286,7 @@ docker compose exec -T web django-admin compilemessages
      • Backup UI page: /admin-tools/backups/ for create, verify, and delete actions. Keep real restore CLI-only.
      -

      11) Testing and Validation

      +

      14) Testing and Validation

      docker compose exec -T web python manage.py check
       docker compose exec -T web python manage.py test
       docker compose exec -T web python manage.py run_staging_e2e_check
      @@ -247,7 +301,7 @@ docker compose exec -T web python manage.py run_staging_e2e_check
    • The Requests Dashboard includes a retry action for failed requests. Retries reset the error text, set the request back to submitted, and enqueue the appropriate Celery task again.
    -

    12) Backup and Restore

    +

    15) Backup and Restore

    make backup-create
     make backup-verify BACKUP_DIR=backups/backup_YYYYmmdd_HHMMSS
      @@ -268,7 +322,7 @@ make backup-verify BACKUP_DIR=backups/backup_YYYYmmdd_HHMMSS
    • The staff UI uses the shared action-progress overlay for backup creation and verification so long-running actions present one standard app behavior.
    -

    13) Host and Domain Configuration

    +

    16) Host and Domain Configuration

    • Primary env variables:
        @@ -286,7 +340,7 @@ make backup-verify BACKUP_DIR=backups/backup_YYYYmmdd_HHMMSS
      • An Invalid HTTP_HOST header failure happens before normal page routing, so a broken hostname cannot render a custom error page on that same broken host. Use a working host or IP to access the runbook and fix the env file.
      -

      14) CI/CD

      +

      17) CI/CD

      Current operating model

        @@ -381,7 +435,7 @@ make backup-verify BACKUP_DIR=backups/backup_YYYYmmdd_HHMMSS The current LAN test deployment intentionally uses DJANGO_DEBUG=1 in .env.test because the security checks correctly reject insecure cookie settings when DEBUG=0 and the deployment is still plain HTTP behind a local test topology. This is acceptable for the test box only. Production must run with HTTPS and DEBUG=0.
      -

      15) Deployment

      +

      18) Deployment

      Test server stack

      • Stack file: docker-compose.prod.yml
      • @@ -445,7 +499,7 @@ lxc.mount.entry: /dev/null sys/module/apparmor/parameters/enabled none bind 0 0<
      • Take a snapshot commit before major next-phase work
      • -

        16) Troubleshooting

        +

        19) Troubleshooting

        • Page looks stale: restart web and hard-refresh browser
        • Second request hangs: inspect web logs and verify health endpoint
        • @@ -456,7 +510,7 @@ lxc.mount.entry: /dev/null sys/module/apparmor/parameters/enabled none bind 0 0<
        • Requests dependency warning appears: verify chardet==5.2.0 is installed in the rebuilt image and restart web/worker
        -

        17) Security and Maintenance Notes

        +

        20) Security and Maintenance Notes

        • Containers run as non-root app user.
        • Keep secrets in .env, not in tracked files.
        • @@ -464,6 +518,15 @@ lxc.mount.entry: /dev/null sys/module/apparmor/parameters/enabled none bind 0 0<
        • Prefer standard framework workflows over custom one-off maintenance scripts.
        • When adding new features, document them in both the Project Wiki and this handbook if they change engineering workflow.
        +
        +

        For the next coder

        +
          +
        • Read this page first, then the Project Wiki, then the release checklist.
        • +
        • Assume some visible behavior is runtime configuration, not only template or view code.
        • +
        • Keep stable import surfaces intact where possible when modularizing.
        • +
        • Prefer explicit, documented behavior over hidden automation.
        • +
        +
        {% endblock %} diff --git a/backend/workflows/templates/workflows/handbook.html b/backend/workflows/templates/workflows/handbook.html index 3bf0ab6..e1fa3ca 100644 --- a/backend/workflows/templates/workflows/handbook.html +++ b/backend/workflows/templates/workflows/handbook.html @@ -36,9 +36,10 @@
        {% trans "Engineering" %}

        {% trans "Developer Handbook" %}

        -

        {% trans "Engineering documentation for architecture, local setup, Docker, migrations, translations, deployment, testing, and long-term maintenance." %}

        +

        {% trans "Engineering runbook for architecture, branch workflow, deployment, CI/CD, code guidelines, and long-term maintenance." %}

        • {% trans "repository and service structure" %}
        • +
        • {% trans "branch workflow and coding guidelines" %}
        • {% trans "Docker and migration workflow" %}
        • {% trans "translation and builder architecture" %}
        • {% trans "CI/CD, deployment, security, and maintenance notes" %}