|
|
|
|
@@ -23,9 +23,11 @@
|
|
|
|
|
<div class="toc">
|
|
|
|
|
<a href="#overview">Overview</a>
|
|
|
|
|
<a href="#structure">Structure</a>
|
|
|
|
|
<a href="#workflow">Workflow</a>
|
|
|
|
|
<a href="#local">Local Dev</a>
|
|
|
|
|
<a href="#docker">Docker</a>
|
|
|
|
|
<a href="#db">Database</a>
|
|
|
|
|
<a href="#guidelines">Guidelines</a>
|
|
|
|
|
<a href="#translations">Translations</a>
|
|
|
|
|
<a href="#pdf">PDF Pipeline</a>
|
|
|
|
|
<a href="#email">Email Pipeline</a>
|
|
|
|
|
@@ -47,6 +49,7 @@
|
|
|
|
|
<li>Repository: <code>workdock-platform</code> (current local path; legacy compose project name may still be retained for runtime continuity)</li>
|
|
|
|
|
<li>Main stack: Django + Celery + PostgreSQL + Redis + MailHog</li>
|
|
|
|
|
<li>Runtime mode: Docker Compose for local development and staging-style operation</li>
|
|
|
|
|
<li>Repository-level quick-start guide for future coders: <code>CONTRIBUTING.md</code></li>
|
|
|
|
|
</ul>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
@@ -54,6 +57,9 @@
|
|
|
|
|
<ul>
|
|
|
|
|
<li><code>/backend/config/</code>: Django settings, WSGI, URL config</li>
|
|
|
|
|
<li><code>/backend/workflows/</code>: application logic, views, models, tasks, templates, static assets</li>
|
|
|
|
|
<li><code>/backend/workflows/views.py</code>: thin route wrapper layer; most view logic now lives in split modules by domain</li>
|
|
|
|
|
<li><code>/backend/workflows/models.py</code>: stable model import surface over split model modules</li>
|
|
|
|
|
<li><code>/backend/workflows/tasks.py</code>: async task entrypoints; PDF and notification logic were moved into dedicated modules</li>
|
|
|
|
|
<li><code>/backend/workflows/templates/workflows/base_shell.html</code>: standard page shell for new staff-facing pages</li>
|
|
|
|
|
<li><code>/backend/workflows/roles.py</code>: centralized role names, capability matrix, and template permission helpers</li>
|
|
|
|
|
<li>Rule: all interactive app pages should extend <code>base_shell.html</code>; do not rebuild topbar/frame logic in page-local templates.</li>
|
|
|
|
|
@@ -65,7 +71,29 @@
|
|
|
|
|
<li><code>/.github/workflows/i18n.yml</code>: translation compile validation in CI</li>
|
|
|
|
|
</ul>
|
|
|
|
|
|
|
|
|
|
<h2 id="local">3) Local Development Workflow</h2>
|
|
|
|
|
<h2 id="workflow">3) Working Model</h2>
|
|
|
|
|
<div class="box">
|
|
|
|
|
<h3>Branch strategy</h3>
|
|
|
|
|
<ul>
|
|
|
|
|
<li><code>develop</code> is the active integration branch.</li>
|
|
|
|
|
<li><code>main</code> is the stable branch intended for production promotion.</li>
|
|
|
|
|
<li>Feature work should start from <code>develop</code> and merge back into <code>develop</code>.</li>
|
|
|
|
|
</ul>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="box">
|
|
|
|
|
<h3>Normal change flow</h3>
|
|
|
|
|
<ol>
|
|
|
|
|
<li>start from <code>develop</code></li>
|
|
|
|
|
<li>implement the change</li>
|
|
|
|
|
<li>run validation</li>
|
|
|
|
|
<li>update docs if architecture or workflow changed</li>
|
|
|
|
|
<li>push to GitHub and let CI run</li>
|
|
|
|
|
<li>deploy from the Mac if test-server verification is needed</li>
|
|
|
|
|
<li>promote <code>develop</code> into <code>main</code> when stable</li>
|
|
|
|
|
</ol>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<h2 id="local">4) Local Development Workflow</h2>
|
|
|
|
|
<h3>Start</h3>
|
|
|
|
|
<pre><code>cd /path/to/workdock-platform
|
|
|
|
|
docker compose up -d --build</code></pre>
|
|
|
|
|
@@ -81,7 +109,7 @@ docker compose up -d --build</code></pre>
|
|
|
|
|
<li>User: <code>user_test</code> / <code>user12345</code></li>
|
|
|
|
|
</ul>
|
|
|
|
|
|
|
|
|
|
<h2 id="docker">4) Docker Operations</h2>
|
|
|
|
|
<h2 id="docker">5) Docker Operations</h2>
|
|
|
|
|
<pre><code>docker compose up -d --build
|
|
|
|
|
docker compose restart web
|
|
|
|
|
docker compose restart worker
|
|
|
|
|
@@ -93,7 +121,7 @@ docker compose down -v</code></pre>
|
|
|
|
|
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 <code>docker compose up -d --build</code>.
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<h2 id="db">5) Database and Migrations</h2>
|
|
|
|
|
<h2 id="db">6) Database and Migrations</h2>
|
|
|
|
|
<pre><code>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</code></pre>
|
|
|
|
|
@@ -101,6 +129,7 @@ docker compose exec -T web python manage.py check</code></pre>
|
|
|
|
|
<li>Never edit or remove historical migrations casually.</li>
|
|
|
|
|
<li>When adding fields to builder-driven models, preserve fallback behavior for existing rows.</li>
|
|
|
|
|
<li>Fresh boot sequence runs migrations automatically in <code>entrypoint-web.sh</code>.</li>
|
|
|
|
|
<li>Do not assume database-backed admin config matches between local and deployed environments.</li>
|
|
|
|
|
</ul>
|
|
|
|
|
|
|
|
|
|
<h3>Role and Permission Model</h3>
|
|
|
|
|
@@ -117,7 +146,27 @@ docker compose exec -T web python manage.py check</code></pre>
|
|
|
|
|
<li>When adding a new operational page or action, define the capability in <code>roles.py</code>, gate the view, and hide the UI affordance when the capability is absent.</li>
|
|
|
|
|
</ul>
|
|
|
|
|
|
|
|
|
|
<h2 id="translations">6) Translation Workflow</h2>
|
|
|
|
|
<h2 id="guidelines">7) Engineering Guidelines</h2>
|
|
|
|
|
<div class="box">
|
|
|
|
|
<h3>Core rules</h3>
|
|
|
|
|
<ol>
|
|
|
|
|
<li>Preserve behavior while refactoring.</li>
|
|
|
|
|
<li>Prefer shared components over page-local special cases.</li>
|
|
|
|
|
<li>Do not overwrite environment-specific runtime config as a side effect of code deploys.</li>
|
|
|
|
|
<li>Keep code-driven behavior and data-driven behavior mentally separate.</li>
|
|
|
|
|
<li>Update documentation in the same branch when operational workflow changes.</li>
|
|
|
|
|
</ol>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="box">
|
|
|
|
|
<h3>Code vs data</h3>
|
|
|
|
|
<ul>
|
|
|
|
|
<li><strong>Code-driven:</strong> routing, permissions, deployment scripts, rendering, tasks, layout.</li>
|
|
|
|
|
<li><strong>Data-driven:</strong> app registry order, branding, company config, builder config, intro checklist items, notification templates.</li>
|
|
|
|
|
<li>If local UI and server UI differ, inspect runtime configuration before assuming code drift.</li>
|
|
|
|
|
</ul>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<h2 id="translations">8) Translation Workflow</h2>
|
|
|
|
|
<h3>Standard Django i18n path</h3>
|
|
|
|
|
<pre><code>make i18n-update-en
|
|
|
|
|
make i18n-compile</code></pre>
|
|
|
|
|
@@ -134,11 +183,12 @@ docker compose exec -T web django-admin compilemessages</code></pre>
|
|
|
|
|
<li><code>preferred_language</code> is normalized in model <code>save()</code> and also has a DB default of <code>de</code>, so alternate creation paths cannot insert null values.</li>
|
|
|
|
|
</ul>
|
|
|
|
|
|
|
|
|
|
<h2 id="pdf">7) PDF Pipeline</h2>
|
|
|
|
|
<h2 id="pdf">9) PDF Pipeline</h2>
|
|
|
|
|
<ul>
|
|
|
|
|
<li>PDF generation is HTML-to-PDF using <code>xhtml2pdf</code>.</li>
|
|
|
|
|
<li>Letterhead overlay defaults to <code>templates.pdf</code>, but can now be replaced from Admin Apps → <code>Branding</code>.</li>
|
|
|
|
|
<li>Main logic lives in <code>backend/workflows/tasks.py</code>.</li>
|
|
|
|
|
<li>Task entrypoints live in <code>backend/workflows/tasks.py</code>.</li>
|
|
|
|
|
<li>Rendering and section-building now live in <code>pdf_rendering.py</code> and <code>pdf_sections.py</code>.</li>
|
|
|
|
|
<li>Fixed PDF labels/headings are rendered from task-level DE/EN text dictionaries, not hard-coded directly in request processing logic.</li>
|
|
|
|
|
<li>PDF language follows the normalized request <code>preferred_language</code>, with German fallback.</li>
|
|
|
|
|
<li>Key templates:
|
|
|
|
|
@@ -154,18 +204,19 @@ docker compose exec -T web django-admin compilemessages</code></pre>
|
|
|
|
|
xhtml2pdf is sensitive to layout complexity. Keep print templates conservative and verify every structural change with a real generated PDF.
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<h2 id="email">8) Email Pipeline</h2>
|
|
|
|
|
<h2 id="email">10) Email Pipeline</h2>
|
|
|
|
|
<ul>
|
|
|
|
|
<li>Notification defaults are defined in <code>DEFAULT_NOTIFICATION_TEMPLATES</code> in <code>tasks.py</code>.</li>
|
|
|
|
|
<li>Notification defaults are defined in the workflow/email orchestration layer.</li>
|
|
|
|
|
<li>Admin-configured overrides live in <code>NotificationTemplate</code> and <code>NotificationRule</code>.</li>
|
|
|
|
|
<li>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.</li>
|
|
|
|
|
<li>Welcome-email configuration under Admin Apps has the same DE/EN split and follows the same runtime language selection.</li>
|
|
|
|
|
<li>The request objects passed into email tasks always carry a normalized <code>preferred_language</code> value.</li>
|
|
|
|
|
<li>Mail sending uses Celery tasks and supports test mode redirection.</li>
|
|
|
|
|
<li>Related helper modules include <code>email_workflows.py</code> and <code>notification_dispatch.py</code>.</li>
|
|
|
|
|
<li>MailHog is the local verification path when test mode is active.</li>
|
|
|
|
|
</ul>
|
|
|
|
|
|
|
|
|
|
<h2 id="nextcloud">9) Nextcloud Integration</h2>
|
|
|
|
|
<h2 id="nextcloud">11) Nextcloud Integration</h2>
|
|
|
|
|
<ul>
|
|
|
|
|
<li>Configured from Admin Apps → Integrations.</li>
|
|
|
|
|
<li>Upload logic lives in <code>backend/workflows/services.py</code>.</li>
|
|
|
|
|
@@ -175,7 +226,7 @@ docker compose exec -T web django-admin compilemessages</code></pre>
|
|
|
|
|
<li>Do not point remote backup at the same Nextcloud directory used for normal onboarding/offboarding document uploads.</li>
|
|
|
|
|
</ul>
|
|
|
|
|
|
|
|
|
|
<h2 id="branding">10) Branding</h2>
|
|
|
|
|
<h2 id="branding">12) Branding</h2>
|
|
|
|
|
<ul>
|
|
|
|
|
<li>Portal-level branding is stored in the singleton model <code>PortalBranding</code>.</li>
|
|
|
|
|
<li>Configured from Admin Apps → <code>Branding</code>.</li>
|
|
|
|
|
@@ -186,7 +237,7 @@ docker compose exec -T web django-admin compilemessages</code></pre>
|
|
|
|
|
<li>User invitation emails and welcome-template fallbacks also use the configured branding defaults.</li>
|
|
|
|
|
</ul>
|
|
|
|
|
|
|
|
|
|
<h2 id="app-registry">10b) App Registry</h2>
|
|
|
|
|
<h2 id="app-registry">12b) App Registry</h2>
|
|
|
|
|
<ul>
|
|
|
|
|
<li>Registry definitions live in <code>workflows/app_registry.py</code>.</li>
|
|
|
|
|
<li>DB overrides live in <code>PortalAppConfig</code>.</li>
|
|
|
|
|
@@ -195,7 +246,7 @@ docker compose exec -T web django-admin compilemessages</code></pre>
|
|
|
|
|
<li>Management UI: <code>/admin-tools/apps/</code> for <code>Platform Owner</code>.</li>
|
|
|
|
|
</ul>
|
|
|
|
|
|
|
|
|
|
<h2 id="trial">10c) Trial Lifecycle</h2>
|
|
|
|
|
<h2 id="trial">12c) Trial Lifecycle</h2>
|
|
|
|
|
<ul>
|
|
|
|
|
<li>Deployment-level trial settings are stored in the singleton model <code>PortalTrialConfig</code>.</li>
|
|
|
|
|
<li>Management UI: <code>/admin-tools/trial/</code> for <code>Platform Owner</code>.</li>
|
|
|
|
|
@@ -207,11 +258,14 @@ docker compose exec -T web django-admin compilemessages</code></pre>
|
|
|
|
|
</li>
|
|
|
|
|
</ul>
|
|
|
|
|
|
|
|
|
|
<h2 id="builders">11) Builder Architecture</h2>
|
|
|
|
|
<h2 id="builders">13) Builder Architecture</h2>
|
|
|
|
|
<h3>Form Builder</h3>
|
|
|
|
|
<ul>
|
|
|
|
|
<li>Model: <code>FormFieldConfig</code> + <code>FormOption</code></li>
|
|
|
|
|
<li>Controls field order, visibility, required flags, option sets, and bilingual label/help-text overrides</li>
|
|
|
|
|
<li>Static definitions live in <code>form_builder_config.py</code>.</li>
|
|
|
|
|
<li>Runtime behavior lives in <code>form_builder_runtime.py</code>.</li>
|
|
|
|
|
<li><code>form_builder.py</code> is the stable facade import path.</li>
|
|
|
|
|
</ul>
|
|
|
|
|
<h3>Intro Builder</h3>
|
|
|
|
|
<ul>
|
|
|
|
|
@@ -232,7 +286,7 @@ docker compose exec -T web django-admin compilemessages</code></pre>
|
|
|
|
|
<li>Backup UI page: <code>/admin-tools/backups/</code> for create, verify, and delete actions. Keep real restore CLI-only.</li>
|
|
|
|
|
</ul>
|
|
|
|
|
|
|
|
|
|
<h2 id="testing">11) Testing and Validation</h2>
|
|
|
|
|
<h2 id="testing">14) Testing and Validation</h2>
|
|
|
|
|
<pre><code>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</code></pre>
|
|
|
|
|
@@ -247,7 +301,7 @@ docker compose exec -T web python manage.py run_staging_e2e_check</code></pre>
|
|
|
|
|
<li>The Requests Dashboard includes a retry action for failed requests. Retries reset the error text, set the request back to <code>submitted</code>, and enqueue the appropriate Celery task again.</li>
|
|
|
|
|
</ul>
|
|
|
|
|
|
|
|
|
|
<h2 id="backup">12) Backup and Restore</h2>
|
|
|
|
|
<h2 id="backup">15) Backup and Restore</h2>
|
|
|
|
|
<pre><code>make backup-create
|
|
|
|
|
make backup-verify BACKUP_DIR=backups/backup_YYYYmmdd_HHMMSS</code></pre>
|
|
|
|
|
<ul>
|
|
|
|
|
@@ -268,7 +322,7 @@ make backup-verify BACKUP_DIR=backups/backup_YYYYmmdd_HHMMSS</code></pre>
|
|
|
|
|
<li>The staff UI uses the shared action-progress overlay for backup creation and verification so long-running actions present one standard app behavior.</li>
|
|
|
|
|
</ul>
|
|
|
|
|
|
|
|
|
|
<h2 id="hosts">13) Host and Domain Configuration</h2>
|
|
|
|
|
<h2 id="hosts">16) Host and Domain Configuration</h2>
|
|
|
|
|
<ul>
|
|
|
|
|
<li>Primary env variables:
|
|
|
|
|
<ul>
|
|
|
|
|
@@ -286,77 +340,102 @@ make backup-verify BACKUP_DIR=backups/backup_YYYYmmdd_HHMMSS</code></pre>
|
|
|
|
|
<li>An <code>Invalid HTTP_HOST header</code> 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.</li>
|
|
|
|
|
</ul>
|
|
|
|
|
|
|
|
|
|
<h2 id="cicd">14) CI/CD</h2>
|
|
|
|
|
<h2 id="cicd">17) CI/CD</h2>
|
|
|
|
|
<div class="box">
|
|
|
|
|
<h3>Current operating model</h3>
|
|
|
|
|
<ul>
|
|
|
|
|
<li>Repository model: one private GitHub repository, not separate dev/prod repositories.</li>
|
|
|
|
|
<li>Branch model: <code>develop</code> for the test deployment, <code>main</code> reserved for production.</li>
|
|
|
|
|
<li>Current test workflow file: <code>.github/workflows/deploy-test.yml</code>.</li>
|
|
|
|
|
<li>Current production workflow file: <code>.github/workflows/deploy-prod.yml</code>.</li>
|
|
|
|
|
<li>GitHub Actions uploads the working tree to the server over SSH. The server does not clone from GitHub.</li>
|
|
|
|
|
<li>This is intentional for a private repository: it removes the need to store GitHub deploy keys on the target server.</li>
|
|
|
|
|
<li>However, GitHub-hosted runners cannot reach a LAN-only private IP like <code>192.168.2.55</code>. 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.</li>
|
|
|
|
|
<li>Repository model: one private GitHub repository.</li>
|
|
|
|
|
<li>Working branch: <code>develop</code>.</li>
|
|
|
|
|
<li>Stable branch: <code>main</code>.</li>
|
|
|
|
|
<li>GitHub Actions is used for CI.</li>
|
|
|
|
|
<li>Current test deployment is done manually from a LAN-connected machine, not from GitHub-hosted runners.</li>
|
|
|
|
|
</ul>
|
|
|
|
|
<h3>GitHub Environments</h3>
|
|
|
|
|
<ul>
|
|
|
|
|
<li>Create GitHub environments named <code>development</code> and <code>production</code>.</li>
|
|
|
|
|
<li><strong>Development</strong> secrets:
|
|
|
|
|
<ul>
|
|
|
|
|
<li><code>TEST_DEPLOY_HOST</code></li>
|
|
|
|
|
<li><code>TEST_DEPLOY_USER</code></li>
|
|
|
|
|
<li><code>TEST_DEPLOY_PORT</code></li>
|
|
|
|
|
<li><code>TEST_DEPLOY_PATH</code></li>
|
|
|
|
|
<li><code>TEST_DEPLOY_SSH_KEY</code></li>
|
|
|
|
|
</ul>
|
|
|
|
|
</li>
|
|
|
|
|
<li><strong>Production</strong> secrets:
|
|
|
|
|
<ul>
|
|
|
|
|
<li><code>PROD_DEPLOY_HOST</code></li>
|
|
|
|
|
<li><code>PROD_DEPLOY_USER</code></li>
|
|
|
|
|
<li><code>PROD_DEPLOY_PORT</code></li>
|
|
|
|
|
<li><code>PROD_DEPLOY_PATH</code></li>
|
|
|
|
|
<li><code>PROD_DEPLOY_SSH_KEY</code></li>
|
|
|
|
|
</ul>
|
|
|
|
|
</li>
|
|
|
|
|
</ul>
|
|
|
|
|
<h3>Exact GitHub UI steps</h3>
|
|
|
|
|
<ol>
|
|
|
|
|
<li>Open the private repository on GitHub.</li>
|
|
|
|
|
<li>Open <code>Settings</code>.</li>
|
|
|
|
|
<li>Open <code>Environments</code> in the left sidebar.</li>
|
|
|
|
|
<li>Create the environment <code>development</code>.</li>
|
|
|
|
|
<li>Create the environment <code>production</code>.</li>
|
|
|
|
|
<li>Open <code>development</code>.</li>
|
|
|
|
|
<li>Under <code>Environment secrets</code>, add the deployment secrets one by one.</li>
|
|
|
|
|
<li>Repeat later for <code>production</code>.</li>
|
|
|
|
|
</ol>
|
|
|
|
|
<h3>Current test deployment values</h3>
|
|
|
|
|
<ul>
|
|
|
|
|
<li>Host: <code>192.168.2.55</code></li>
|
|
|
|
|
<li>User: <code>root</code></li>
|
|
|
|
|
<li>Path: <code>/opt/workdock</code></li>
|
|
|
|
|
<li>URL: <code>http://192.168.2.55:8088</code></li>
|
|
|
|
|
</ul>
|
|
|
|
|
<h3>Current development secrets</h3>
|
|
|
|
|
<ul>
|
|
|
|
|
<li><code>TEST_DEPLOY_HOST=192.168.2.55</code></li>
|
|
|
|
|
<li><code>TEST_DEPLOY_USER=root</code></li>
|
|
|
|
|
<li><code>TEST_DEPLOY_PORT=22</code></li>
|
|
|
|
|
<li><code>TEST_DEPLOY_PATH=/opt/workdock</code></li>
|
|
|
|
|
<li><code>TEST_DEPLOY_SSH_KEY=<full private key content></code></li>
|
|
|
|
|
</ul>
|
|
|
|
|
<h3>First GitHub Actions test</h3>
|
|
|
|
|
<ol>
|
|
|
|
|
<li>Open GitHub Actions.</li>
|
|
|
|
|
<li>Run the workflow <code>Deploy Test</code> on branch <code>develop</code>.</li>
|
|
|
|
|
<li>Wait for the SSH upload and deploy steps to finish successfully.</li>
|
|
|
|
|
<li>Verify <code>http://192.168.2.55:8088/healthz/</code> returns HTTP 200.</li>
|
|
|
|
|
<li>Then verify the app UI in the browser.</li>
|
|
|
|
|
</ol>
|
|
|
|
|
<div class="note">
|
|
|
|
|
The current LAN test deployment intentionally uses <code>DJANGO_DEBUG=1</code> in <code>.env.test</code> because the security checks correctly reject insecure cookie settings when <code>DEBUG=0</code> and the deployment is still plain HTTP. This is acceptable for the internal test box only. Production must run with HTTPS and <code>DEBUG=0</code>.
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<h2 id="deploy">15) Deployment</h2>
|
|
|
|
|
<div class="box">
|
|
|
|
|
<h3>Why deploy is manual right now</h3>
|
|
|
|
|
<p>The test server is inside the local network and uses a private IP address <code>192.168.2.55</code>. GitHub-hosted runners on the public internet cannot reliably reach that target. Because of that, the correct deployment path today is:</p>
|
|
|
|
|
<ol>
|
|
|
|
|
<li>push code to GitHub</li>
|
|
|
|
|
<li>let GitHub run CI</li>
|
|
|
|
|
<li>deploy from the Mac on the same LAN</li>
|
|
|
|
|
</ol>
|
|
|
|
|
<p>Automatic CD from GitHub becomes appropriate only after moving to a public server or using a self-hosted runner inside the LAN.</p>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="box">
|
|
|
|
|
<h3>What to do for normal work</h3>
|
|
|
|
|
<ol>
|
|
|
|
|
<li>Start from <code>develop</code>.</li>
|
|
|
|
|
<li>Do the implementation work.</li>
|
|
|
|
|
<li>Push to GitHub.</li>
|
|
|
|
|
<li>Let CI finish.</li>
|
|
|
|
|
<li>Run the local test deployment helper from the Mac.</li>
|
|
|
|
|
<li>Verify the updated version in the browser.</li>
|
|
|
|
|
<li>When the integration line is stable, merge <code>develop</code> into <code>main</code>.</li>
|
|
|
|
|
</ol>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="box">
|
|
|
|
|
<h3>One-command test deployment</h3>
|
|
|
|
|
<p>From the Mac on the same network:</p>
|
|
|
|
|
<pre><code>git checkout develop
|
|
|
|
|
./scripts/deploy_test_from_mac.sh</code></pre>
|
|
|
|
|
<p>This helper script does all of the following:</p>
|
|
|
|
|
<ol>
|
|
|
|
|
<li>checks that the current branch is <code>develop</code></li>
|
|
|
|
|
<li>fast-forwards from <code>origin/develop</code></li>
|
|
|
|
|
<li>checks that the server env file exists</li>
|
|
|
|
|
<li>syncs the repository to <code>/opt/workdock</code> with <code>rsync</code></li>
|
|
|
|
|
<li>preserves server-local env files like <code>.env.test</code> and <code>.env.prod</code></li>
|
|
|
|
|
<li>runs the remote deployment script</li>
|
|
|
|
|
<li>waits for the health endpoint</li>
|
|
|
|
|
<li>prints the deployed commit and branch</li>
|
|
|
|
|
</ol>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="box">
|
|
|
|
|
<h3>Current test server values</h3>
|
|
|
|
|
<ul>
|
|
|
|
|
<li>Host: <code>192.168.2.55</code></li>
|
|
|
|
|
<li>SSH user: <code>root</code></li>
|
|
|
|
|
<li>Deploy path: <code>/opt/workdock</code></li>
|
|
|
|
|
<li>Env file: <code>.env.test</code></li>
|
|
|
|
|
<li>Health URL: <code>http://192.168.2.55:8088/healthz/</code></li>
|
|
|
|
|
<li>Public app URL: <code>https://workdock.bostame.de/</code></li>
|
|
|
|
|
</ul>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="box">
|
|
|
|
|
<h3>GitHub Actions status</h3>
|
|
|
|
|
<ul>
|
|
|
|
|
<li><code>.github/workflows/deploy-test.yml</code> exists, but GitHub-hosted deploy to the LAN server is not the recommended path right now.</li>
|
|
|
|
|
<li><code>.github/workflows/deploy-prod.yml</code> exists for later production use.</li>
|
|
|
|
|
<li>The practical use of GitHub today is CI, code review, PRs, and branch history.</li>
|
|
|
|
|
</ul>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="box">
|
|
|
|
|
<h3>If the local deploy helper fails</h3>
|
|
|
|
|
<ol>
|
|
|
|
|
<li>Check whether <code>/opt/workdock/.env.test</code> still exists on the server.</li>
|
|
|
|
|
<li>Check SSH access from the Mac:
|
|
|
|
|
<pre><code>ssh -4 root@192.168.2.55</code></pre>
|
|
|
|
|
</li>
|
|
|
|
|
<li>Check server health directly:
|
|
|
|
|
<pre><code>curl -I http://192.168.2.55:8088/healthz/</code></pre>
|
|
|
|
|
</li>
|
|
|
|
|
<li>Check container status:
|
|
|
|
|
<pre><code>ssh root@192.168.2.55 "cd /opt/workdock && docker compose --env-file .env.test -f docker-compose.prod.yml ps"</code></pre>
|
|
|
|
|
</li>
|
|
|
|
|
</ol>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="note">
|
|
|
|
|
The current LAN test deployment intentionally uses <code>DJANGO_DEBUG=1</code> in <code>.env.test</code> because the security checks correctly reject insecure cookie settings when <code>DEBUG=0</code> 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 <code>DEBUG=0</code>.
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<h2 id="deploy">18) Deployment</h2>
|
|
|
|
|
<h3>Test server stack</h3>
|
|
|
|
|
<ul>
|
|
|
|
|
<li>Stack file: <code>docker-compose.prod.yml</code></li>
|
|
|
|
|
@@ -385,7 +464,8 @@ make backup-verify BACKUP_DIR=backups/backup_YYYYmmdd_HHMMSS</code></pre>
|
|
|
|
|
<h3>Manual deploy</h3>
|
|
|
|
|
<p>The preferred current test-deployment path is the local helper script from a Mac or another LAN-connected workstation:</p>
|
|
|
|
|
<pre><code>./scripts/deploy_test_from_mac.sh</code></pre>
|
|
|
|
|
<p>This script fast-forwards <code>develop</code>, syncs the repo to the server with <code>rsync</code>, runs the remote deployment, and verifies the health endpoint.</p>
|
|
|
|
|
<p>This script fast-forwards <code>develop</code>, checks that the remote env file exists, syncs the repo to the server with <code>rsync</code>, runs the remote deployment, verifies the health endpoint, and prints the deployed commit hash.</p>
|
|
|
|
|
<p>The script explicitly preserves server-local env files such as <code>.env.test</code> and <code>.env.prod</code> so deployment does not wipe machine-specific secrets.</p>
|
|
|
|
|
<p>Direct server-side deploy is still available if the code is already on the server:</p>
|
|
|
|
|
<pre><code>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</code></pre>
|
|
|
|
|
@@ -419,7 +499,7 @@ lxc.mount.entry: /dev/null sys/module/apparmor/parameters/enabled none bind 0 0<
|
|
|
|
|
<li>Take a snapshot commit before major next-phase work</li>
|
|
|
|
|
</ol>
|
|
|
|
|
|
|
|
|
|
<h2 id="troubleshooting">16) Troubleshooting</h2>
|
|
|
|
|
<h2 id="troubleshooting">19) Troubleshooting</h2>
|
|
|
|
|
<ul>
|
|
|
|
|
<li><strong>Page looks stale:</strong> restart <code>web</code> and hard-refresh browser</li>
|
|
|
|
|
<li><strong>Second request hangs:</strong> inspect web logs and verify health endpoint</li>
|
|
|
|
|
@@ -430,7 +510,7 @@ lxc.mount.entry: /dev/null sys/module/apparmor/parameters/enabled none bind 0 0<
|
|
|
|
|
<li><strong>Requests dependency warning appears:</strong> verify <code>chardet==5.2.0</code> is installed in the rebuilt image and restart <code>web</code>/<code>worker</code></li>
|
|
|
|
|
</ul>
|
|
|
|
|
|
|
|
|
|
<h2 id="security">17) Security and Maintenance Notes</h2>
|
|
|
|
|
<h2 id="security">20) Security and Maintenance Notes</h2>
|
|
|
|
|
<ul>
|
|
|
|
|
<li>Containers run as non-root <code>app</code> user.</li>
|
|
|
|
|
<li>Keep secrets in <code>.env</code>, not in tracked files.</li>
|
|
|
|
|
@@ -438,6 +518,15 @@ lxc.mount.entry: /dev/null sys/module/apparmor/parameters/enabled none bind 0 0<
|
|
|
|
|
<li>Prefer standard framework workflows over custom one-off maintenance scripts.</li>
|
|
|
|
|
<li>When adding new features, document them in both the Project Wiki and this handbook if they change engineering workflow.</li>
|
|
|
|
|
</ul>
|
|
|
|
|
<div class="box">
|
|
|
|
|
<h3>For the next coder</h3>
|
|
|
|
|
<ul>
|
|
|
|
|
<li>Read this page first, then the Project Wiki, then the release checklist.</li>
|
|
|
|
|
<li>Assume some visible behavior is runtime configuration, not only template or view code.</li>
|
|
|
|
|
<li>Keep stable import surfaces intact where possible when modularizing.</li>
|
|
|
|
|
<li>Prefer explicit, documented behavior over hidden automation.</li>
|
|
|
|
|
</ul>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{% endblock %}
|
|
|
|
|
|