Compare commits

..

20 Commits

Author SHA1 Message Date
Md Bayazid Bostame
53629d963d Merge branch 'develop' into main
Some checks failed
CI / python-validation (push) Has been cancelled
CI / docker-release-gate (push) Has been cancelled
i18n / compile-translations (push) Has been cancelled
2026-03-31 11:54:55 +02:00
Md Bayazid Bostame
9933185ad9 docs: define customer release branch policy 2026-03-31 11:54:47 +02:00
Md Bayazid Bostame
2e263c1fdc Merge branch 'develop' into main 2026-03-30 14:51:49 +02:00
Md Bayazid Bostame
2c57b04ed6 docs: clarify standard ci cd model 2026-03-30 14:51:43 +02:00
Md Bayazid Bostame
d3c35a46b4 Merge branch 'develop' into main 2026-03-30 13:08:23 +02:00
Md Bayazid Bostame
edd76316eb docs: add stale localhost ui troubleshooting 2026-03-30 13:08:20 +02:00
Md Bayazid Bostame
7e59b82f6f Merge branch 'develop' 2026-03-30 13:00:50 +02:00
Md Bayazid Bostame
74bd2c9b85 snapshot: preserve request dashboard header and delete fixes 2026-03-30 13:00:47 +02:00
Md Bayazid Bostame
f1d0506cb1 fix: scope request dashboard details styling 2026-03-30 12:58:30 +02:00
Md Bayazid Bostame
c3be89e1dd fix: raise header overlays above dashboard surfaces 2026-03-30 12:54:32 +02:00
Md Bayazid Bostame
a5422cbb27 Merge branch 'develop' 2026-03-29 01:55:32 +01:00
Md Bayazid Bostame
712cf9ff00 Merge branch 'develop' 2026-03-29 01:49:05 +01:00
Md Bayazid Bostame
1ac5e62ad4 Merge branch 'develop' 2026-03-29 01:43:36 +01:00
Md Bayazid Bostame
2a9ba8da93 Merge branch 'develop' 2026-03-29 01:38:15 +01:00
Md Bayazid Bostame
62125f7a90 Merge branch 'develop' into main 2026-03-29 01:27:25 +01:00
Md Bayazid Bostame
d69a5017af feat: add local test deployment helper 2026-03-29 00:23:44 +01:00
Md Bayazid Bostame
025d9b18f8 feat: add deployment host and domain configuration guide 2026-03-29 00:15:19 +01:00
Md Bayazid Bostame
b60db8c154 fix: build deployment archive outside repo root 2026-03-28 23:55:33 +01:00
Md Bayazid Bostame
88e3aaae29 fix: upload release archive in deploy workflows 2026-03-28 23:53:19 +01:00
Bostame Md Bayazid
79cbb956a0 Merge pull request #1 from Bostame/develop
Merge validated platform and deployment improvements into main
2026-03-28 23:32:59 +01:00
6 changed files with 167 additions and 3 deletions

View File

@@ -14,9 +14,26 @@ If you change architecture, deployment flow, or operational behavior, update the
- `develop`: active integration branch - `develop`: active integration branch
- `main`: stable branch for production promotion - `main`: stable branch for production promotion
- short-lived feature branches: branch from `develop`, merge back into `develop` - short-lived feature branches: branch from `develop`, merge back into `develop`
- customer release branches: branch from `main` for a frozen customer line when a customer must not receive future feature work automatically
Do not use `main` as the default development branch. Do not use `main` as the default development branch.
### Customer release policy
For customer-specific deliveries such as TUBCO, use a dedicated release branch from the current stable line.
Recommended pattern:
- `release/tubco-v1`: customer maintenance branch
- `tubco-baseline-2026-03`: baseline tag taken from the same stable point
Rule:
- new product features continue on `develop`
- customer release branches only receive approved:
- bug fixes
- security updates
- UI improvements
Do not let a customer deployment track `develop` directly.
## Current Delivery Model ## Current Delivery Model
- GitHub Actions is used for CI - GitHub Actions is used for CI
- the current test server is local/LAN-only - the current test server is local/LAN-only

View File

@@ -7,6 +7,24 @@
- GitHub Actions uploads the repository contents to the server over SSH - GitHub Actions uploads the repository contents to the server over SSH
- the server does not need GitHub access to deploy - the server does not need GitHub access to deploy
## Standard target model for later
When this moves from an internal test setup to a more standard delivery model, the target should be:
1. feature branch
2. CI
3. merge into `develop`
4. staging deploy
5. staging validation
6. merge into `main`
7. production deploy
Recommended standards for that future state:
- staging and production use separate hosts or clearly separated environments
- staging and production use separate secrets
- production deploys only from `main`
- production runs with HTTPS and `DJANGO_DEBUG=0`
- production deploys require environment protection or approval in GitHub
- rollback is documented and tested
This is intentional. For a private repository, server-side `git clone` adds unnecessary credential management. This is intentional. For a private repository, server-side `git clone` adds unnecessary credential management.
## Branch strategy ## Branch strategy
@@ -491,6 +509,21 @@ docker compose --env-file .env.test -f docker-compose.prod.yml logs --tail=100 w
docker compose --env-file .env.test -f docker-compose.prod.yml logs --tail=100 caddy docker compose --env-file .env.test -f docker-compose.prod.yml logs --tail=100 caddy
``` ```
## If localhost still looks wrong after the server is fixed
Use this order before assuming the local checkout is missing code:
1. hard refresh the page with `Cmd + Shift + R`
2. clear site data for `127.0.0.1:8088` in browser devtools and sign in again
3. restart the local web container:
```bash
docker compose restart web
```
4. if it still survives, rebuild the local stack:
```bash
docker compose up -d --build
```
This is the correct recovery path for stale browser state, shared-header fixes, page-local CSS fixes, and versioned static asset updates.
## Rollback ## Rollback
This deployment path is source-upload based, not image-tag based. This deployment path is source-upload based, not image-tag based.

View File

@@ -14,6 +14,9 @@
box-sizing: border-box; box-sizing: border-box;
width: min(var(--app-shell-width), 100%); width: min(var(--app-shell-width), 100%);
margin: 0 auto 12px; margin: 0 auto 12px;
position: relative;
z-index: 30;
overflow: visible;
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: flex-start; align-items: flex-start;
@@ -137,6 +140,9 @@
box-sizing: border-box; box-sizing: border-box;
width: 100%; width: 100%;
margin: 0; margin: 0;
position: relative;
z-index: 30;
overflow: visible;
padding: 22px 24px 18px; padding: 22px 24px 18px;
border: 0; border: 0;
border-bottom: 1px solid rgba(217, 227, 238, 0.9); border-bottom: 1px solid rgba(217, 227, 238, 0.9);
@@ -174,6 +180,8 @@
display: flex; display: flex;
margin-left: auto; margin-left: auto;
flex: 0 0 auto; flex: 0 0 auto;
position: relative;
z-index: 31;
gap: 8px; gap: 8px;
flex-wrap: wrap; flex-wrap: wrap;
justify-content: flex-end; justify-content: flex-end;

View File

@@ -730,14 +730,14 @@
.inline-delete .btn { min-height: 38px; display: inline-flex; align-items: center; justify-content: center; } .inline-delete .btn { min-height: 38px; display: inline-flex; align-items: center; justify-content: center; }
.intro-panel { min-width: 260px; } .intro-panel { min-width: 260px; }
details { td.intro-panel details {
border: 1px solid var(--line); border: 1px solid var(--line);
border-radius: 16px; border-radius: 16px;
background: linear-gradient(180deg, #ffffff, #f8fbff); background: linear-gradient(180deg, #ffffff, #f8fbff);
overflow: hidden; overflow: hidden;
} }
details[open] { td.intro-panel details[open] {
border-color: #cad7e8; border-color: #cad7e8;
box-shadow: inset 0 1px 0 rgba(255,255,255,0.95); box-shadow: inset 0 1px 0 rgba(255,255,255,0.95);
} }
@@ -772,7 +772,7 @@
color: var(--brand-blue); color: var(--brand-blue);
} }
details[open] .intro-toggle::after { content: ""; } td.intro-panel details[open] .intro-toggle::after { content: ""; }
.intro-toggle::-webkit-details-marker { display: none; } .intro-toggle::-webkit-details-marker { display: none; }
.intro-menu { .intro-menu {

View File

@@ -79,6 +79,7 @@
<li><code>develop</code> is the active integration branch.</li> <li><code>develop</code> is the active integration branch.</li>
<li><code>main</code> is the stable branch intended for production promotion.</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> <li>Feature work should start from <code>develop</code> and merge back into <code>develop</code>.</li>
<li>Customer release branches should start from <code>main</code> when a customer must not receive future feature work automatically.</li>
</ul> </ul>
</div> </div>
<div class="box"> <div class="box">
@@ -93,6 +94,24 @@
<li>promote <code>develop</code> into <code>main</code> when stable</li> <li>promote <code>develop</code> into <code>main</code> when stable</li>
</ol> </ol>
</div> </div>
<div class="box">
<h3>Customer release branches</h3>
<p>Use a dedicated release branch when a customer should receive the current stable product line but not future features by default.</p>
<ul>
<li>Example branch: <code>release/tubco-v1</code></li>
<li>Example baseline tag: <code>tubco-baseline-2026-03</code></li>
<li>Base the branch on the current <code>main</code>.</li>
<li>Continue product work on <code>develop</code>.</li>
<li>Only backport approved:
<ul>
<li>bug fixes</li>
<li>security updates</li>
<li>UI improvements</li>
</ul>
</li>
</ul>
<p>This keeps the customer line stable while the main product keeps evolving.</p>
</div>
<h2 id="local">4) Local Development Workflow</h2> <h2 id="local">4) Local Development Workflow</h2>
<h3>Start</h3> <h3>Start</h3>
@@ -366,6 +385,16 @@ make backup-verify BACKUP_DIR=backups/backup_YYYYmmdd_HHMMSS</code></pre>
</ul> </ul>
</div> </div>
<div class="box">
<h3>What is good enough today vs what is standard later</h3>
<ul>
<li><strong>Today:</strong> GitHub runs CI, then a LAN-connected Mac deploys to the private test server with the helper scripts.</li>
<li><strong>Standard later:</strong> GitHub runs CI, then an approved deploy job promotes tested code to staging and production automatically.</li>
<li><strong>Why they differ:</strong> the current test server is inside the LAN, so GitHub-hosted runners cannot reach it directly.</li>
<li><strong>When to upgrade:</strong> do it once staging or production lives on a public host, or once you add a self-hosted runner inside the LAN.</li>
</ul>
</div>
<div class="box"> <div class="box">
<h3>Why deploy is manual right now</h3> <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> <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>
@@ -377,6 +406,34 @@ make backup-verify BACKUP_DIR=backups/backup_YYYYmmdd_HHMMSS</code></pre>
<p>Automatic CD from GitHub becomes appropriate only after moving to a public server or using a self-hosted runner inside the LAN.</p> <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>
<div class="box">
<h3>Recommended standard CI/CD model</h3>
<ol>
<li>Keep one private repository.</li>
<li>Use short-lived feature branches from <code>develop</code>.</li>
<li>Require CI to pass before merge into <code>develop</code>.</li>
<li>Deploy <code>develop</code> to a staging environment.</li>
<li>Promote <code>develop</code> into <code>main</code> only after staging validation.</li>
<li>Deploy <code>main</code> to production behind HTTPS with <code>DEBUG=0</code>.</li>
<li>Protect production with GitHub environment approvals, production secrets, and rollback steps.</li>
</ol>
<p>If you want the most standard shape, the long-term target is:</p>
<pre><code>feature branch -> CI -> develop -> staging deploy -> validate -> main -> production deploy</code></pre>
</div>
<div class="box">
<h3>What to change when this becomes a standard deployment</h3>
<ol>
<li>Move staging and production onto hosts that GitHub or a self-hosted runner can reach reliably.</li>
<li>Keep separate env files and secrets for staging and production.</li>
<li>Run production with <code>DJANGO_DEBUG=0</code>, secure cookies, and HTTPS only.</li>
<li>Add GitHub environment protection rules for <code>development</code> and <code>production</code>.</li>
<li>Use the production deploy path only from <code>main</code>.</li>
<li>Add backup verification and health verification as standard post-deploy checks.</li>
<li>Later, consider image-based deploys if you want cleaner rollbacks than source-upload deploys.</li>
</ol>
</div>
<div class="box"> <div class="box">
<h3>What to do for normal work</h3> <h3>What to do for normal work</h3>
<ol> <ol>
@@ -391,6 +448,15 @@ make backup-verify BACKUP_DIR=backups/backup_YYYYmmdd_HHMMSS</code></pre>
</ol> </ol>
</div> </div>
<div class="box">
<h3>Decision guide</h3>
<ul>
<li><strong>Keep the current manual Mac deploy flow</strong> if the server stays private inside the LAN.</li>
<li><strong>Use a self-hosted GitHub runner</strong> if you want automated deploys but the server must stay private.</li>
<li><strong>Use GitHub-hosted deploy jobs</strong> once staging or production runs on a publicly reachable host or VPN-reachable target.</li>
</ul>
</div>
<div class="box"> <div class="box">
<h3>One-command test deployment</h3> <h3>One-command test deployment</h3>
<p>From the Mac on the same network:</p> <p>From the Mac on the same network:</p>
@@ -448,6 +514,17 @@ make backup-verify BACKUP_DIR=backups/backup_YYYYmmdd_HHMMSS</code></pre>
</ul> </ul>
</div> </div>
<div class="box">
<h3>Minimal standard checklist for later</h3>
<ul>
<li>Staging and production must not share secrets.</li>
<li>Production deploy must run from <code>main</code> only.</li>
<li>Production must have HTTPS, health checks, backups, and a documented rollback path.</li>
<li>Runtime config sync must stay explicit. Code deploys should not silently overwrite DB-backed config.</li>
<li>Every deploy path should end with a health check and a quick browser verification of critical flows.</li>
</ul>
</div>
<div class="box"> <div class="box">
<h3>If the local deploy helper fails</h3> <h3>If the local deploy helper fails</h3>
<ol> <ol>
@@ -624,6 +701,20 @@ make backup-verify BACKUP_DIR=backups/backup_YYYYmmdd_HHMMSS</code></pre>
</div> </div>
<h2 id="troubleshooting">20) Troubleshooting</h2> <h2 id="troubleshooting">20) Troubleshooting</h2>
<div class="box">
<h3>Localhost still looks stale after the server is already fixed</h3>
<ol>
<li>Hard refresh the page with <code>Cmd + Shift + R</code>.</li>
<li>If it still looks wrong, clear site data for <code>127.0.0.1:8088</code> in the browser devtools and sign in again.</li>
<li>Restart the local web container:
<pre><code>docker compose restart web</code></pre>
</li>
<li>If the issue still survives, rebuild the local stack:
<pre><code>docker compose up -d --build</code></pre>
</li>
</ol>
<p>This is the right order when shared header fixes, page-local CSS fixes, or versioned static assets look correct on the server but localhost still shows the old UI.</p>
</div>
<ul> <ul>
<li><strong>Page looks stale:</strong> restart <code>web</code> and hard-refresh browser</li> <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> <li><strong>Second request hangs:</strong> inspect web logs and verify health endpoint</li>

View File

@@ -1,5 +1,6 @@
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
from django.test import TestCase from django.test import TestCase
from django.urls import reverse
from workflows.app_registry import build_portal_app_sections, ensure_portal_app_configs from workflows.app_registry import build_portal_app_sections, ensure_portal_app_configs
from workflows.models import PortalAppConfig from workflows.models import PortalAppConfig
@@ -48,3 +49,17 @@ class AppRegistryPermissionTests(TestCase):
self.assertNotIn('requests_dashboard', self._visible_keys(self.staff)) self.assertNotIn('requests_dashboard', self._visible_keys(self.staff))
self.assertIn('requests_dashboard', self._visible_keys(self.it_staff)) self.assertIn('requests_dashboard', self._visible_keys(self.it_staff))
def test_super_admin_cannot_open_platform_owner_app_registry_page(self):
self.client.force_login(self.super_admin)
response = self.client.get(reverse('portal_app_registry_page'))
self.assertEqual(response.status_code, 302)
self.assertEqual(response.url, reverse('home'))
def test_platform_owner_can_open_app_registry_page(self):
self.client.force_login(self.platform_owner)
response = self.client.get(reverse('portal_app_registry_page'))
self.assertEqual(response.status_code, 200)