Adds Monitoring Setup docs and deploy-scp troubleshooting/debug fixes #2

Merged
Sluijsens merged 11 commits from feature/gitea-deployment-workflow into master 2026-07-30 23:57:52 +02:00
2 changed files with 53 additions and 3 deletions
Showing only changes of commit 9b44a5e85e - Show all commits
+28
View File
@@ -132,6 +132,34 @@ jobs:
rm -rf $RELEASE_DIR/wwwroot/web && \ rm -rf $RELEASE_DIR/wwwroot/web && \
ln -s ../../../shared/wwwroot-web $RELEASE_DIR/wwwroot/web" ln -s ../../../shared/wwwroot-web $RELEASE_DIR/wwwroot/web"
# Optional plugin modules (future modular support): a compiled SlpModularCms.Modules.*.dll
# dropped into shared/modules survives every release, the same persistence pattern as
# shared/wwwroot-web above — except there's no cross-account permission dance here, since
# shared/modules lives entirely under gitea-workflow's own tree (unlike wwwroot-web, which
# reaches into webadmin's). ModuleOrchestrator.DiscoverModules() already scans its own base
# directory on disk for matching assemblies at startup (SlpModularCms.Core/Hosting/
# ModuleOrchestrator.cs) and loads whatever it finds via reflection — no code change needed,
# only somewhere for the DLL to still be after the next deploy replaces releases/{timestamp}.
# `ls ... 2>/dev/null` piped to a `while read` (rather than a bare glob loop) keeps this
# POSIX-sh safe and correct when shared/modules is empty, which is the common case today.
- name: Link persistent modules
env:
SSH_USER: ${{ secrets.PI_MAIN_USERNAME }}
SSH_PASS: ${{ secrets.PI_MAIN_PASSWORD }}
SSH_PORT: ${{ secrets.PI_MAIN_PORT }}
SSH_HOST: ${{ secrets.PI_MAIN_ADDRESS }}
run: |
RELEASE_DIR="${{ inputs.deploy_path }}/releases/${{ steps.release.outputs.timestamp }}"
sshpass -p "$SSH_PASS" ssh \
-p "$SSH_PORT" \
-o StrictHostKeyChecking=no \
"$SSH_USER@$SSH_HOST" \
"mkdir -p ${{ inputs.deploy_path }}/shared/modules && \
ls ${{ inputs.deploy_path }}/shared/modules/*.dll 2>/dev/null | while read -r f; do \
name=\$(basename \"\$f\"); \
ln -sf \"../../shared/modules/\$name\" \"$RELEASE_DIR/\$name\"; \
done"
# Atomic release switch (FR-06, D-27): `ln -sfn` replaces the `current` symlink target in a # Atomic release switch (FR-06, D-27): `ln -sfn` replaces the `current` symlink target in a
# single filesystem operation, so there is no moment where `current` points at a half-written # single filesystem operation, so there is no moment where `current` points at a half-written
# directory. The process is then restarted so it picks up the new assemblies — a running .NET # directory. The process is then restarted so it picks up the new assemblies — a running .NET
@@ -148,6 +148,28 @@ sudo chgrp -R webshared /mnt/storage1/www/html/slpsoftware/<env>
and make sure webadmin's FTP server creates new uploads group-readable (`g+rx`, not just and make sure webadmin's FTP server creates new uploads group-readable (`g+rx`, not just
owner-readable) — a one-time permission setup, not something either pipeline touches per deploy. owner-readable) — a one-time permission setup, not something either pipeline touches per deploy.
**`shared/modules` is the equivalent mechanism for optional plugin modules** (future modular
support) — but simpler, since it lives entirely under `gitea-workflow`'s own tree, with no
cross-account permission dance:
👤 **pi-main / `gitea-workflow`:**
```bash
mkdir -p ~/apps/slpsoftware/<env>/shared/modules
```
`deploy-scp.yaml`'s "Link persistent modules" step already runs this `mkdir -p` on every deploy, so
this line is only useful if you want the directory to exist before the very first deploy — it isn't
required. To add a module: build a `SlpModularCms.Modules.<Name>.dll` against the same version of
`SlpModularCms.Core` the running app was built against (mismatched contracts are caught per-module
by `ModuleOrchestrator`'s existing try/catch — logged and skipped, not a crash), `scp`/`sftp` it as
`gitea-workflow` into `~/apps/slpsoftware/<env>/shared/modules/`, then either wait for the next
deploy or restart the service by hand:
```bash
systemctl --user restart slpsoftware-<env>.service
```
Either way, `ModuleOrchestrator.DiscoverModules()` picks it up from the release directory the next
time the process starts — no CI run required to add a module this way.
### 1.5 Runtime Configuration File ### 1.5 Runtime Configuration File
One file per environment, **outside** the release directory so it survives every switch: One file per environment, **outside** the release directory so it survives every switch:
@@ -498,9 +520,9 @@ Already built (U5/U6) — this is the read-only walkthrough for whoever operates
1. Push to `master`, or a manual `workflow_dispatch` → the six gates run 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) 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 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 `releases/{timestamp}/`, links `shared/wwwroot-web` and any `shared/modules/*.dll` in, switches
`slpsoftware-test.service`, verifies `https://test.slpsoftware.nl/health`, then prunes old `current`, restarts `slpsoftware-test.service`, verifies `https://test.slpsoftware.nl/health`,
releases (only test — `run_db_backup: false`) 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 4. `deploy-production` (only with the flag) does the same, plus a database backup first
(`run_db_backup: true`) — see § 4 (`run_db_backup: true`) — see § 4