diff --git a/aidlc-docs/features/gitea-deployment-workflow/aidlc-state.md b/aidlc-docs/features/gitea-deployment-workflow/aidlc-state.md index 641e73a..9155e3b 100644 --- a/aidlc-docs/features/gitea-deployment-workflow/aidlc-state.md +++ b/aidlc-docs/features/gitea-deployment-workflow/aidlc-state.md @@ -38,6 +38,17 @@ - **Include Operations Phase**: Yes - **Decided At**: Requirements Analysis +### Deployment Setup +- **Included**: Yes +- **Method**: CI/CD (Gitea Actions — confirms what U5/U6 already built) +- **Completed**: 2026-07-28. Real domains: `test.slpsoftware.nl` / `slpsoftware.nl`. Ports 5100/5101 + chosen for the Pi's local Kestrel bindings. Database backup script drafted. Full FTPS future-switch + procedure drafted (Q5 = B), with an explicit caveat that shared hosting is very likely IIS-based, + so systemd-restart and atomic-symlink-switch do not carry over unchanged — treated as a starting + brief for a future Infrastructure Design pass, not a ready-to-execute procedure +- **Artifacts**: `operations/deployment/deployment-plan.md`, `deployment-instructions.md`, + `rollback-plan.md` + ## Scope Decisions (from feature-selection.md) - **Public website**: documentation/instructions only — where the website build lands in `wwwroot/`, how it coexists with `wwwroot/admin/`, and what a per-website workspace must deliver. The website's own build/deploy workflow stays out of scope (Q4 = A). - **Environments**: local, test, production only. diff --git a/aidlc-docs/features/gitea-deployment-workflow/audit.md b/aidlc-docs/features/gitea-deployment-workflow/audit.md index ad01ab0..15fb8f0 100644 --- a/aidlc-docs/features/gitea-deployment-workflow/audit.md +++ b/aidlc-docs/features/gitea-deployment-workflow/audit.md @@ -1212,3 +1212,48 @@ by Infrastructure Design. U5 carries REF-U3-01's Umami origin gate plus FR-21 an **Operations Phase Decision**: Yes (Decided At: Requirements Analysis) --- +## Build and Test — Local Debug Crash Reported and Fixed + +**Timestamp**: 2026-07-28T03:10:00Z + +**User Input**: "Als ik de Master api start krijg ik de volgende error: K:/Development/Projects/SlpModularCms/src/SlpModularCms.Api/bin/Debug/net10.0/SlpModularCms.Api.exe Unhandled exception. System.IO.DirectoryNotFoundException: K:\Development\Projects\SlpModularCms\src\SlpModularCms.Api\wwwroot\ [full stack trace through WebApplication.CreateBuilder] ... Bij het starten van de slave krijg ik dat niet. Dit is lokaal" + +**AI Response**: "Root cause identified: not a defect in the feature's code, but a leftover of my own Build and Test verification — dotnet publish had created wwwroot/ (triggering ASP.NET Core's static web assets manifest to reference it), then my cleanup deleted the directory without a clean rebuild, leaving a stale Debug-config static-web-assets manifest pointing at a now-missing path. Fixed by fully cleaning and rebuilding SlpModularCms.Api's Debug obj/bin (dotnet clean + manual rm -rf + dotnet build). Confirmed the Master host starts cleanly again (/health -> 200). Nothing was tracked in git (gitignored build output), so no commit was needed." + +**Context**: Build and Test — post-completion issue found by the user, diagnosed and fixed before proceeding + +--- + +## Build and Test (Confirmed) — Proceeding to Deployment Setup + +**Timestamp**: 2026-07-28T03:15:00Z + +**User Input**: "Werkt weer, ga verder" + +**AI Response**: "Confirmed fixed. Proceeding to the Operations phase, starting with Deployment Setup." + +**Context**: Build and Test stage fully closed; entering OPERATIONS phase + +--- +## Deployment Setup (Planning) + +**Timestamp**: 2026-07-28T03:20:00Z + +**User Input**: "(implicit — entering Operations phase)" + +**AI Response**: "Created deployment-setup-plan.md with 6 questions. Q1/Q2 (inclusion + method) are confirmations of what was already built in Construction, not new decisions. Q3-Q6 are the genuinely open host-specific facts that infrastructure-design.md deliberately deferred to this stage: real domains, whether to draft the DB backup script, FTPS documentation depth, and the one-time host-setup checklist scope." + +**Context**: OPERATIONS — Deployment Setup, Step 2-4 (question generation), awaiting user answers + +--- +## Deployment Setup (Complete) + +**Timestamp**: 2026-07-28T03:30:00Z + +**User Input**: "Ik heb de vragen ingevuld, ga verder" (Q1=A, Q2=A, Q3=A with production=slpsoftware.nl/test=test.slpsoftware.nl, Q4=A, Q5=B, Q6=A) + +**AI Response**: "Generated deployment-plan.md, deployment-instructions.md (one-time host setup: enable-linger, directory skeleton, shared/env runtime config file, systemd --user units for both environments on ports 5100/5101, nginx routing snippet, Gitea variables/secrets table, database backup script drafted and documented, full FTPS future-switch procedure with an explicit caveat that shared hosting is likely IIS-based and several mechanisms don't carry over unchanged), and rollback-plan.md (fast symlink-repoint rollback, rebuild-and-redeploy for older commits, database restore, explicit list of what a rollback never touches)." + +**Context**: OPERATIONS — Deployment Setup complete, presenting completion message + +--- diff --git a/aidlc-docs/features/gitea-deployment-workflow/operations/deployment/deployment-instructions.md b/aidlc-docs/features/gitea-deployment-workflow/operations/deployment/deployment-instructions.md new file mode 100644 index 0000000..df81933 --- /dev/null +++ b/aidlc-docs/features/gitea-deployment-workflow/operations/deployment/deployment-instructions.md @@ -0,0 +1,268 @@ +# Deployment Instructions + +**Target**: single Raspberry Pi (`linux-arm64`), test and production both on it, separated by +directory, `systemd --user` unit, and local port. Domains: `test.slpsoftware.nl` (test), +`slpsoftware.nl` (production). + +--- + +## 1. One-Time Host Setup + +Do this once, before the first automated deploy. Everything here is host configuration the workflow +assumes already exists — `deploy-scp.yaml` (U6) never creates any of it. + +### 1.1 Prerequisites +- .NET 10 runtime installed on the Pi (ASM-03) — the publish is framework-dependent + (`infrastructure-design.md` § 1), so the Pi needs the runtime, not the full SDK +- SSH access for the deploy user, password auth for now (matches the reference project; + SSH-key migration remains a documented future step, same as there) +- `sqlcmd` installed, for the backup script (§ 4) — e.g. `mssql-tools18` / `unixodbc` on Debian-based + Raspberry Pi OS + +### 1.2 Enable Lingering (INFRA-U6-01 — do this first, easy to forget) +```bash +sudo loginctl enable-linger +``` +Without this, the `systemd --user` service manager is torn down when the deploy SSH session ends, +killing the just-restarted app a few seconds after every successful deploy. This also fixes the +common "Failed to connect to bus" error `systemctl --user` can throw when invoked from a +non-interactive SSH command — lingering keeps the user's systemd instance (and `XDG_RUNTIME_DIR`) +running independent of any login session. + +### 1.3 Directory Skeleton +Run once per environment (`test`, `production`): +```bash +mkdir -p ~/apps/slpmodularcms-/releases +mkdir -p ~/apps/slpmodularcms-/shared/wwwroot-web +``` +`current` is created by the first deploy itself (`ln -sfn`) — don't pre-create it. + +### 1.4 Runtime Configuration File +One file per environment, **outside** the release directory so it survives every switch: +```bash +touch ~/apps/slpmodularcms-/shared/env +chmod 600 ~/apps/slpmodularcms-/shared/env +``` +Contents (fill in real values — this file is never read by the workflow, only by the systemd unit +below): +```ini +ASPNETCORE_ENVIRONMENT=Production +ASPNETCORE_URLS=http://localhost: +ConnectionStrings__DefaultConnection=Server=127.0.0.1,1433;User ID=;Password=;Database=SlpModularCms;TrustServerCertificate=True +JwtSettings__Secret= +JwtSettings__Issuer=SlpModularCms +JwtSettings__Audience=SlpModularCmsPortal +MasterModule__MasterUrl=https:// +Observability__SentryDsn= +SecurityHeaders__AllowedScriptOrigins__0= +SecurityHeaders__AllowedConnectOrigins__0= +``` +Use ports **5100** (test) and **5101** (production) unless something else on the Pi already +occupies them. `ASPNETCORE_ENVIRONMENT=Production` is used for **both** environments deliberately — +`Development` disables HSTS and exposes the Scalar API explorer (`Program.cs`), neither of which +should be true for anything reachable at a real domain, including test. + +**`SecurityHeaders__AllowedScriptOrigins__0` / `_AllowedConnectOrigins__0` must exactly match** the +Gitea variables `SECURITY_ALLOWED_SCRIPT_ORIGINS_TEST` / `_PRODUCTION` (see § 3) — REF-U5-01's CI +gate only catches drift between the frontend build and that Gitea variable; it cannot see this file, +so keeping the two in sync is a manual discipline, not something enforced automatically. + +### 1.5 systemd User Units +Create `~/.config/systemd/user/slpmodularcms-test.service`: +```ini +[Unit] +Description=SlpModularCms API (test) +After=network.target + +[Service] +WorkingDirectory=%h/apps/slpmodularcms-test/current +ExecStart=/usr/bin/dotnet %h/apps/slpmodularcms-test/current/SlpModularCms.Api.dll +EnvironmentFile=%h/apps/slpmodularcms-test/shared/env +Restart=on-failure +RestartSec=5 +KillSignal=SIGINT +TimeoutStopSec=20 + +[Install] +WantedBy=default.target +``` +And `~/.config/systemd/user/slpmodularcms-production.service` — identical, with `test` replaced by +`production` throughout (including the port inside `shared/env`). + +Enable both (does not start them yet — nothing is deployed there until the first CI run): +```bash +systemctl --user daemon-reload +systemctl --user enable slpmodularcms-test.service +systemctl --user enable slpmodularcms-production.service +``` + +### 1.6 nginx Routing +The existing reverse proxy (`infrastructure-design.md` § 1, § 4) needs a server block per domain, +routing to the matching local port: +```nginx +server { + listen 443 ssl; + server_name test.slpsoftware.nl; + location / { + proxy_pass http://127.0.0.1:5100; + proxy_set_header Host $host; + proxy_set_header X-Forwarded-Proto $scheme; + } +} + +server { + listen 443 ssl; + server_name slpsoftware.nl; + location / { + proxy_pass http://127.0.0.1:5101; + proxy_set_header Host $host; + proxy_set_header X-Forwarded-Proto $scheme; + } +} +``` +TLS certificate provisioning (e.g. certbot) is existing host operations, out of scope for this +feature — assumed already handled the same way the reference project's domains are. + +### 1.7 Database Backup Credentials (§ 4 depends on this) +```bash +touch ~/.config/slpmodularcms-db-backup.env +chmod 600 ~/.config/slpmodularcms-db-backup.env +``` +```ini +DB_SERVER=127.0.0.1,1433 +DB_NAME=SlpModularCmsProduction +DB_USER= +DB_PASSWORD= +``` +Kept **separate** from `shared/env` (§ 1.4) deliberately — the backup script needs its own +credential, ideally scoped to just `BACKUP DATABASE` permission rather than the application's own +data-access login. + +### 1.8 Gitea Actions Variables and Secrets +Set once, in this repository's Gitea Actions settings: + +| Name | Kind | Value | +|---|---|---| +| `PI_MAIN_ADDRESS` | secret | the Pi's address | +| `PI_MAIN_PORT` | secret | SSH port | +| `PI_MAIN_USERNAME` | secret | deploy user | +| `PI_MAIN_PASSWORD` | secret | deploy user's password | +| `DEPLOY_PATH_TEST` | variable | `/home//apps/slpmodularcms-test` | +| `DEPLOY_PATH_PRODUCTION` | variable | `/home//apps/slpmodularcms-production` | +| `SERVICE_NAME_TEST` | variable | `slpmodularcms-test.service` | +| `SERVICE_NAME_PRODUCTION` | variable | `slpmodularcms-production.service` | +| `HEALTH_CHECK_URL_TEST` | variable | `https://test.slpsoftware.nl/health` | +| `HEALTH_CHECK_URL_PRODUCTION` | variable | `https://slpsoftware.nl/health` | +| `VITE_SENTRY_DSN` | variable | Sentry DSN (shared, not sensitive — safe in the client bundle) | +| `VITE_UMAMI_SCRIPT_URL` | variable | Umami script host (shared) | +| `VITE_UMAMI_WEBSITE_ID_TEST` / `_PRODUCTION` | variable | per-environment Umami website ID | +| `SECURITY_ALLOWED_SCRIPT_ORIGINS_TEST` / `_PRODUCTION` | variable | **must match** § 1.4's `SecurityHeaders__AllowedScriptOrigins__0` for that environment | + +--- + +## 2. Deploy Sequence (What Actually Happens on a Run) + +Already built (U5/U6) — this is the read-only walkthrough for whoever operates it: + +1. Push to `master`, or a manual `workflow_dispatch` → the six gates run +2. `publish-test` runs (and `publish-production`, only if `workflow_dispatch` with the flag) +3. `deploy-test` (always, if gates pass) calls `deploy-scp.yaml`, which uploads into a new + `releases/{timestamp}/`, links `shared/wwwroot-web` in, switches `current`, restarts + `slpmodularcms-test.service`, verifies `https://test.slpsoftware.nl/health`, then prunes old + releases (only test — `run_db_backup: false`) +4. `deploy-production` (only with the flag) does the same, plus a database backup first + (`run_db_backup: true`) — see § 4 + +## 3. First-Ever Deploy Notes + +- `wwwroot/web/` will be empty until a website workspace deploys into it — `/` serves the built-in + placeholder until then (this is expected, not a failure) +- The very first run has no "previous release" to keep — pruning naturally has nothing to prune +- Verify manually after the first run: `curl https://test.slpsoftware.nl/health` and + `curl https://slpsoftware.nl/health` (after the first production run) both return `200` + +## 4. Database Backup Script + +Create `~/scripts/backup-slpmodularcms-db.sh` on the Pi (this script is host-side by design — never +part of this repository, so no DB credential ever reaches Gitea): + +```bash +#!/usr/bin/env bash +set -euo pipefail + +ENVIRONMENT="${1:?Usage: backup-slpmodularcms-db.sh }" +CREDENTIALS_FILE="$HOME/.config/slpmodularcms-db-backup.env" + +if [[ ! -f "$CREDENTIALS_FILE" ]]; then + echo "Missing $CREDENTIALS_FILE — see deployment-instructions.md § 1.7" >&2 + exit 1 +fi +# shellcheck source=/dev/null +source "$CREDENTIALS_FILE" +: "${DB_SERVER:?}" "${DB_NAME:?}" "${DB_USER:?}" "${DB_PASSWORD:?}" + +BACKUP_DIR="$HOME/backups/slpmodularcms-${ENVIRONMENT}" +mkdir -p "$BACKUP_DIR" +TIMESTAMP=$(date -u +%Y%m%d%H%M%S) +BACKUP_FILE="$BACKUP_DIR/${DB_NAME}-${TIMESTAMP}.bak" + +sqlcmd -S "$DB_SERVER" -U "$DB_USER" -P "$DB_PASSWORD" -C -Q \ + "BACKUP DATABASE [$DB_NAME] TO DISK = N'$BACKUP_FILE' WITH INIT, COMPRESSION" + +echo "Backup written to $BACKUP_FILE" + +# Retention: keep the 7 most recent backups for this environment +ls -1t "$BACKUP_DIR"/*.bak 2>/dev/null | tail -n +8 | xargs -r rm -f +``` + +```bash +chmod +x ~/scripts/backup-slpmodularcms-db.sh +``` + +**Verify once, manually**, before relying on it in a real deploy: +```bash +~/scripts/backup-slpmodularcms-db.sh production +``` +Confirm a `.bak` file appears under `~/backups/slpmodularcms-production/` and that `sqlcmd` didn't +silently fail (the script uses `set -euo pipefail`, so a real SQL error does propagate as a non-zero +exit — which fails the calling `deploy-scp.yaml` step, correctly blocking the deploy). + +## 5. Future: Switching Production to FTPS (Shared Hosting) + +D-02/NFR-09 required the workflow's *transport* to be swappable without restructuring — satisfied by +the `transport` input already on `deploy-scp.yaml` (currently only `scp` is implemented). This +section documents what actually changes when that day comes (OPEN-04 — not scheduled, drafted now +per Q5 = B of the deployment setup plan). + +### 5.1 What carries over unchanged +- The CI workflow's gates, the two-build split, the `config` job pattern +- The overall shape of the interface: `artifact_name`, `environment`, `deploy_path` + +### 5.2 What does not carry over — read this before assuming it's a drop-in swap +Shared .NET hosting is almost always **Windows/IIS-based**, not Linux/systemd. That changes more +than the transport: +- **No `systemctl --user` restart** — IIS picks up a new deployment via an app-pool recycle, usually + triggered by touching `web.config` or the app-pool's own recycle mechanism, not a service restart + command +- **The atomic release-switch pattern may not be available at all** — many shared hosts expose only + a single web root over FTPS, with no ability to create sibling directories and swap a symlink. + `wwwroot/web/` persistence (FR-08, ASM-01) would need a **different** mechanism on such a host — + e.g. never touching a specific subfolder during upload, rather than linking a persistent directory + outside a swapped release tree, since "outside the release tree" may not be an available concept +- **Database backup** — shared hosting frequently does not expose direct `sqlcmd`/SSH access at all; + the backup step in `deploy-scp.yaml` (§ 4 script) would need to become either a provider-specific + API call or a documented manual pre-production step (the FR-20 fallback U6 already designed for) + +### 5.3 What building `deploy-ftps.yaml` would actually require +1. A new reusable workflow implementing the **same five required inputs** + (`artifact_name`, `environment`, `deploy_path`, `service_name`, `health_check_url`) so + `continuous_integration.yaml` calls it identically via the `transport` input +2. An FTPS upload step (e.g. `lftp` mirror or `curl --ftp-ssl`) replacing the `scp` step +3. A **new decision, not yet made**: what "atomic switch" and "restart" even mean on the target + host — this cannot be answered generically; it depends on which shared host is actually chosen +4. Re-running this feature's Infrastructure Design step for U6, scoped to the new host, once a + specific shared-hosting provider is selected — not a checklist item that can be pre-answered today + +**Recommendation**: treat this section as a starting brief for that future Infrastructure Design +pass, not as a ready-to-execute procedure — the concrete host was unknown at the time this was +written (OPEN-04), so several of the decisions above are necessarily provider-specific. diff --git a/aidlc-docs/features/gitea-deployment-workflow/operations/deployment/deployment-plan.md b/aidlc-docs/features/gitea-deployment-workflow/operations/deployment/deployment-plan.md new file mode 100644 index 0000000..c819ade --- /dev/null +++ b/aidlc-docs/features/gitea-deployment-workflow/operations/deployment/deployment-plan.md @@ -0,0 +1,57 @@ +# Deployment Plan + +**Date**: 2026-07-28 +**Decided at**: Deployment Setup, per `deployment-setup-plan.md` (Q1 = A, Q2 = A) + +## Method + +Gitea Actions CI/CD, already built in Construction: +- `.gitea/workflows/continuous_integration.yaml` (U5) — six blocking gates, then a per-environment + `dotnet publish`, then invokes the deploy workflow +- `.gitea/workflows/deploy-scp.yaml` (U6) — reusable `workflow_call` workflow: backup (production + only) → upload → link persistent website → atomic switch → restart → health check → prune + +This stage does not change that mechanism — it documents the operational side Construction +deliberately left out: host setup, real domains, the database backup script, and the rollback +procedure. + +## Environments + +| Environment | Host | Domain | Trigger | +|---|---|---|---| +| Test | Raspberry Pi (`linux-arm64`) | `test.slpsoftware.nl` | Automatic on push to `master`, or any `workflow_dispatch` | +| Production | Same Pi, separate directory | `slpsoftware.nl` | Only `workflow_dispatch` with `deploy_production = true` | + +Same physical host for both (per `infrastructure-design.md` § 1) — separated by directory, +`systemd --user` unit, and local port, never by anything the workflow manages directly. + +## Rationale for What's Documented Here vs. Already Decided + +| Already decided (Construction) | Documented here (Operations) | +|---|---| +| Atomic switch mechanism, retention count, transport | Real domains, real ports | +| `deploy-scp.yaml`'s step sequence | The one-time host setup that sequence assumes already exists | +| That a DB backup step runs before production deploys | The actual backup script's content | +| That `transport` is reserved for FTPS (D-02, NFR-09) | What actually changes operationally when that day comes | + +## Automation Level + +Fully automated for test (push to `master` → live on `test.slpsoftware.nl` with no human step). +Production requires an explicit, manual `workflow_dispatch` run with the flag checked — this manual +trigger **is** the approval gate (D-09); no separate approval workflow step exists or is needed. + +## Rollback Strategy + +Summarized here; full procedure in `rollback-plan.md`. Two releases are always retained +(`infrastructure-design.md` § 3), so rolling back is a re-point-and-restart, never a rebuild, for the +most recent deploy. Migrations are required to be forward-compatible and non-destructive (D-26), so +redeploying an older commit remains valid further back than just one release. + +## Secrets and Configuration + +Per D-16, runtime configuration lives in **host** environment variables that the workflow never +writes — see `deployment-instructions.md` § "Runtime Configuration" for the full list and where it +lives on the host (a single `shared/env` file per environment, outside the swapped release +directory, `chmod 600`). Gitea Actions secrets are limited to what the workflow itself needs to +reach the host: SSH credentials (`PI_MAIN_*`). No application secret is ever a Gitea secret or +variable. diff --git a/aidlc-docs/features/gitea-deployment-workflow/operations/deployment/rollback-plan.md b/aidlc-docs/features/gitea-deployment-workflow/operations/deployment/rollback-plan.md new file mode 100644 index 0000000..79f6c97 --- /dev/null +++ b/aidlc-docs/features/gitea-deployment-workflow/operations/deployment/rollback-plan.md @@ -0,0 +1,65 @@ +# Rollback Plan + +**Mechanism** (D-26, `infrastructure-design.md` § 3–4): two releases are always retained — +`current` plus exactly one previous. Pruning only ever runs after a **passing** health check, so a +failed deploy never leaves fewer than two releases on disk. + +## When to Roll Back + +- The deploy workflow's health check failed (job already shows red; `current` was left pointing at + the new, unhealthy release — no automatic rollback, per `infrastructure-design.md` § 4) +- The deploy succeeded and passed its health check, but a defect surfaces afterward that only shows + up under real traffic + +## Fast Rollback (Previous Release Still on Disk) — the Common Case + +No rebuild needed. Over SSH, on the Pi: + +```bash +cd ~/apps/slpmodularcms-/releases +ls -1t # confirm which directory is the previous, working release +ln -sfn ~/apps/slpmodularcms-/releases/ ~/apps/slpmodularcms-/current +systemctl --user restart slpmodularcms-.service +curl -f https:///health # confirm the rollback itself is healthy +``` + +This is the same atomic-switch primitive the deploy workflow itself uses — pointing it backward +instead of forward. `wwwroot/web/` is untouched either way, since it was never part of the switched +directory to begin with (FR-08). + +## Rebuild-and-Redeploy Rollback (Older Than One Release Back) + +If the defect predates the retained previous release, redeploy an **earlier commit** through the +normal pipeline: + +1. `git revert` or check out the last-known-good commit on a branch +2. Push to `master` (test) or run `workflow_dispatch` with `deploy_production: true` (production) — + the same gates and deploy sequence run as any other deploy +3. This only works because migrations are required to be forward-compatible and non-destructive + (D-26) — redeploying an older commit's code against a database that has since had newer + migrations applied must not break. If a migration since the target commit **was** destructive, + this path is not safe and the database backup (§ below) is the actual recovery route instead + +## Database Rollback + +A backup is taken before every **production** deploy (`operations/deployment/deployment-instructions.md` +§ 4, invoked by `deploy-scp.yaml` when `run_db_backup: true`). To restore: + +```bash +sqlcmd -S -U -P -C -Q \ + "RESTORE DATABASE [SlpModularCmsProduction] FROM DISK = N'.bak' WITH REPLACE" +``` + +Restoring a database backup and rolling back the application release are **independent actions** — +decide based on the actual failure whether one, the other, or both are needed. Rolling back the app +without restoring the database is usually sufficient (migrations are non-destructive by design); +restoring the database without rolling back the app should be rare and deliberate. + +## What Is Never Part of a Rollback + +- `wwwroot/web/` (the customer's website) — structurally outside every release directory; no + rollback action should ever touch it +- The Data Protection key ring — lives in the database, not the release directory; rolling back the + app release does not affect it +- Gitea Actions variables/secrets — these describe the *target*, not a specific release; nothing + about them changes during a rollback diff --git a/aidlc-docs/features/gitea-deployment-workflow/operations/plans/deployment-setup-plan.md b/aidlc-docs/features/gitea-deployment-workflow/operations/plans/deployment-setup-plan.md new file mode 100644 index 0000000..f2c8be0 --- /dev/null +++ b/aidlc-docs/features/gitea-deployment-workflow/operations/plans/deployment-setup-plan.md @@ -0,0 +1,104 @@ +# Deployment Setup Plan + +## Context already established (not re-asked) + +The deployment *mechanism* was already decided and built during Construction — this stage is about +producing the operational documentation around it, not re-deciding the method: + +- **Method**: Gitea Actions CI/CD (`continuous_integration.yaml` → `deploy-scp.yaml`), built in U5/U6 +- **Target**: a single Raspberry Pi (`linux-arm64`), test and production on the same host, split by + directory only (`infrastructure-design.md` § 1) +- **Transport**: SSH/SCP, `sshpass` + plain shell steps (D-05) +- **Release strategy**: atomic release-directory switch, 2 releases retained, `systemd --user` + services, no sudo (`infrastructure-design.md` §§ 2–3) +- **Rollback mechanism**: re-point the `current` symlink to the previous release and restart the + service — already fully specified, this stage just needs to write it up as a runnable procedure +- **Approval gate for production**: the `workflow_dispatch` + `deploy_production` flag itself is the + approval gate (D-09) — no additional gate is needed + +## Question 1: Include Deployment Setup? + +Given all of the above was purpose-built in Construction specifically to be deployed, declining this +stage would leave the pipeline built but undocumented for actual first use. Recommended: A. + +A) Yes — produce the deployment documentation (Recommended) +B) No — deployment is handled elsewhere or not needed +C) Not sure — suggest an approach and I'll decide + +X) Other (please describe after [Answer]: tag below) + +[Answer]:A + +## Question 2: Deployment Method + +Confirms the method already built — asked per the mandatory format, not because it's genuinely open. + +A) CI/CD pipeline (already built: Gitea Actions, this is a confirmation, not a new choice) (Recommended) +B) Something else entirely — describe below + +X) Other (please describe after [Answer]: tag below) + +[Answer]: A + +--- + +The remaining questions are the genuinely open items — real host facts that only you know, which +`infrastructure-design.md` deliberately left for this stage. + +## Question 3: Real Domains + +`HEALTH_CHECK_URL_TEST` / `_PRODUCTION` and the nginx routing (`infrastructure-design.md` § 1) need +real hostnames to write concrete instructions. What should the deployment instructions use? + +A) I'll provide the real domains now — describe them after [Answer]: below (e.g. `test.example.nl`, + `example.nl`) +B) Use placeholders (``, ``) in the generated instructions; I'll fill + them in myself when configuring Gitea variables + +X) Other (please describe after [Answer]: tag below) + +[Answer]:A, production: slpsoftware.nl, test: test.slpsoftware.nl + +## Question 4: Database Backup Script + +`deploy-scp.yaml` (U6) invokes `~/scripts/backup-slpmodularcms-db.sh` on the host but does not create +it — it's host-side, deliberately kept out of the workflow so no DB credentials ever touch Gitea. +Should this stage draft that script's content for you to place on the Pi? + +A) Yes, draft a `sqlcmd`/`BACKUP DATABASE` script now, parameterized by environment (Recommended) +B) No, I'll write the backup script myself — just document what it must accept/do (arguments, + exit-code contract, where the workflow expects it) +C) Not applicable yet — document it as a TODO, I'll come back to this before the first production deploy + +X) Other (please describe after [Answer]: tag below) + +[Answer]:A + +## Question 5: FTPS Switch Documentation Depth + +D-02 requires the *workflow* to allow adding FTPS later without restructuring (already satisfied by +the `transport` input in `deploy-scp.yaml`) — but how much should the *documentation* say about +actually switching to it when production eventually moves to shared hosting? + +A) Brief note only: confirm the extension point exists in the workflow, don't draft the FTPS steps + themselves (Recommended — shared hosting isn't happening yet, OPEN-04) +B) Draft a full FTPS switch procedure now, even though it won't be used until shared hosting happens + +X) Other (please describe after [Answer]: tag below) + +[Answer]:B + +## Question 6: One-Time Host Setup Checklist + +Several one-time, host-side prerequisites were identified during Infrastructure Design and must be +documented somewhere before the first real deploy can succeed: +`loginctl enable-linger` (INFRA-U6-01), creating the `systemd --user` unit files, creating the +`releases`/`shared`/`current` directory skeleton, and the Gitea secrets/variables from +`infrastructure-design.md` § 6. Confirm this belongs in `deployment-instructions.md`? + +A) Yes, include a full one-time host-setup checklist in the deployment instructions (Recommended) +B) No, I'll assemble the host setup myself from the Infrastructure Design doc directly + +X) Other (please describe after [Answer]: tag below) + +[Answer]:A