Commit Graph
12 Commits
Author SHA1 Message Date
Sluijsens 084b941204 Pins actions/setup-node to v3, skips DB backup when no database exists yet
Continuous Integration / config (pull_request) Successful in 10s
Continuous Integration / backend-build (pull_request) Successful in 4m57s
Continuous Integration / vulnerability-scan (pull_request) Successful in 4m47s
Continuous Integration / frontend-prepare (pull_request) Successful in 1m51s
Continuous Integration / backend-test (pull_request) Successful in 5m17s
Continuous Integration / frontend-build (pull_request) Successful in 2m15s
Continuous Integration / frontend-test (pull_request) Successful in 4m24s
Continuous Integration / frontend-lint (pull_request) Successful in 2m10s
Continuous Integration / publish-production (pull_request) Skipped
Continuous Integration / deploy-production (pull_request) Skipped
Continuous Integration / publish-test (pull_request) Successful in 6m19s
Continuous Integration / deploy-test (pull_request) Skipped
actions/setup-node@v4's Post step attempts a cache-save that fails hard on
this self-hosted Gitea Actions runner (no GHES cache API support), marking
frontend-prepare as failed and blocking its dependents even though the
actual install/build work succeeds. v3's older cache implementation doesn't
hit this. Also cleans up two stale comments left over from the /admin
MSBuild-target removal.

Also updates the documented backup script to skip cleanly (exit 0) when the
target database doesn't exist yet (MariaDB error 1049) rather than failing
the whole deploy - expected on a brand-new environment's first production
run, before migrations have ever had a chance to create it. Still fails hard
on any other error, so a real backup failure against an existing database
still blocks the deploy as intended.
2026-07-30 18:54:09 +02:00
Sluijsens f10d7b983e Reverts deploy-test to push/workflow_dispatch only, drops the pull_request trigger
Continuous Integration / config (pull_request) Successful in 9s
Continuous Integration / backend-build (pull_request) Successful in 5m22s
Continuous Integration / vulnerability-scan (pull_request) Successful in 4m44s
Continuous Integration / frontend-prepare (pull_request) Successful in 1m39s
Continuous Integration / backend-test (pull_request) Successful in 5m11s
Continuous Integration / frontend-build (pull_request) Successful in 2m10s
Continuous Integration / frontend-test (pull_request) Successful in 4m27s
Continuous Integration / frontend-lint (pull_request) Successful in 1m56s
Continuous Integration / publish-production (pull_request) Skipped
Continuous Integration / deploy-production (pull_request) Skipped
Continuous Integration / publish-test (pull_request) Successful in 6m24s
Continuous Integration / deploy-test (pull_request) Skipped
The pull_request trigger was a temporary convenience to test the deploy
pipeline itself against the real test environment on every PR push. Now
that the pipeline is verified working end to end, going back to only
deploying on a master push or manual dispatch avoids concurrent PRs
silently overwriting the single shared test environment.
2026-07-30 13:38:31 +02:00
Sluijsens 7c1d2aa520 Fixes /admin 404 by building the admin SPA before dotnet publish, not via an MSBuild target
Continuous Integration / config (pull_request) Successful in 10s
Continuous Integration / backend-build (pull_request) Successful in 4m52s
Continuous Integration / vulnerability-scan (pull_request) Successful in 4m50s
Continuous Integration / frontend-prepare (pull_request) Successful in 1m44s
Continuous Integration / backend-test (pull_request) Successful in 5m37s
Continuous Integration / frontend-build (pull_request) Successful in 2m11s
Continuous Integration / frontend-test (pull_request) Successful in 4m33s
Continuous Integration / frontend-lint (pull_request) Successful in 2m2s
Continuous Integration / publish-production (pull_request) Skipped
Continuous Integration / deploy-production (pull_request) Skipped
Continuous Integration / publish-test (pull_request) Successful in 6m14s
Deploy (SCP) / deploy (pull_request) Successful in 1m20s
Continuous Integration / deploy-test (pull_request) Successful in 1m21s
The BuildAndCopyAdminFrontend MSBuild target (BeforeTargets=Publish) never actually
worked: files it created after project evaluation were silently absent from the
publish output, and forcing them in via an explicit Content item collided with the
SDK's own static-web-asset resolution. Verified locally with a clean obj/bin and a
fresh node_modules - every deploy so far genuinely shipped without wwwroot/admin.
Moves the frontend build + copy into its own CI step ahead of dotnet publish, so the
SDK's ordinary wwwroot handling picks it up with no custom MSBuild involved.

Also fixes unreadable <code> badges on the website placeholder page (no explicit
text color, relying on inherited body color pairing unreliably with the badge
background).
2026-07-30 11:10:17 +02:00
Sluijsens 9b44a5e85e Adds a shared/modules symlink mechanism for optional plugin modules
Continuous Integration / config (pull_request) Successful in 9s
Continuous Integration / backend-build (pull_request) Successful in 5m4s
Continuous Integration / vulnerability-scan (pull_request) Successful in 4m23s
Continuous Integration / frontend-prepare (pull_request) Successful in 1m40s
Continuous Integration / backend-test (pull_request) Successful in 5m46s
Continuous Integration / frontend-build (pull_request) Successful in 2m18s
Continuous Integration / frontend-test (pull_request) Successful in 4m33s
Continuous Integration / frontend-lint (pull_request) Successful in 2m0s
Continuous Integration / publish-production (pull_request) Skipped
Continuous Integration / deploy-production (pull_request) Skipped
Continuous Integration / publish-test (pull_request) Successful in 6m11s
Deploy (SCP) / deploy (pull_request) Successful in 1m25s
Continuous Integration / deploy-test (pull_request) Successful in 1m25s
ModuleOrchestrator already discovers SlpModularCms.Modules.*.dll from disk at
startup via reflection, so this needed no code change — only a persistent
location (mirroring shared/wwwroot-web) that survives release swaps, and a
deploy-scp.yaml step to symlink its contents into each new release before
restart.
2026-07-30 10:09:53 +02:00
SluijsensandClaude Sonnet 5 d51285d9dd Removes the secret-diagnosis debug step, fixes wwwroot symlink parent dir
Continuous Integration / config (pull_request) Successful in 10s
Continuous Integration / backend-build (pull_request) Successful in 4m34s
Continuous Integration / vulnerability-scan (pull_request) Successful in 4m39s
Continuous Integration / frontend-prepare (pull_request) Successful in 1m45s
Continuous Integration / backend-test (pull_request) Successful in 5m15s
Continuous Integration / frontend-build (pull_request) Successful in 2m14s
Continuous Integration / frontend-test (pull_request) Successful in 4m27s
Continuous Integration / frontend-lint (pull_request) Successful in 1m57s
Continuous Integration / publish-production (pull_request) Skipped
Continuous Integration / deploy-production (pull_request) Skipped
Continuous Integration / publish-test (pull_request) Successful in 5m51s
Deploy (SCP) / deploy (pull_request) Failing after 11m46s
Continuous Integration / deploy-test (pull_request) Failing after 11m47s
The PI_MAIN_USERNAME/PASSWORD debug step served its purpose (found the
shell-metacharacter password bug fixed in 51488d6) and is removed now
that deploy-test gets past authentication.

"Link persistent website content" then failed with `ln: failed to
create symbolic link '.../wwwroot/web': No such file or directory` -
dotnet publish only emits a wwwroot/ folder when the source project's
own wwwroot has content, so a release built before any frontend/website
content exists can land with no wwwroot/ directory at all, not merely
an empty wwwroot/web/. `ln -s` can't create the missing parent
directory itself. Added `mkdir -p $RELEASE_DIR/wwwroot` before the
existing rm/ln pair to guarantee the parent exists either way.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015FffvxxJp5wG34Ru48GBig
2026-07-30 00:28:29 +02:00
SluijsensandClaude Sonnet 5 51488d6d94 Routes PI_MAIN_* secrets through env vars instead of inline interpolation
Continuous Integration / config (pull_request) Successful in 9s
Continuous Integration / backend-build (pull_request) Successful in 4m57s
Continuous Integration / vulnerability-scan (pull_request) Successful in 4m21s
Continuous Integration / frontend-prepare (pull_request) Successful in 1m28s
Continuous Integration / backend-test (pull_request) Successful in 5m3s
Continuous Integration / frontend-build (pull_request) Successful in 2m10s
Continuous Integration / frontend-test (pull_request) Successful in 4m42s
Continuous Integration / frontend-lint (pull_request) Successful in 1m56s
Continuous Integration / publish-production (pull_request) Skipped
Continuous Integration / deploy-production (pull_request) Skipped
Continuous Integration / publish-test (pull_request) Successful in 6m30s
Deploy (SCP) / deploy (pull_request) Failing after 57s
Continuous Integration / deploy-test (pull_request) Failing after 58s
Every ssh/scp step interpolated ${{ secrets.PI_MAIN_PASSWORD }} and
${{ secrets.PI_MAIN_USERNAME }} directly into the run: shell text.
Gitea/GitHub Actions substitutes that as literal text before bash ever
sees it, so any shell-metacharacter in the password ($, `, ", \) gets
re-interpreted by bash instead of passed through - silently changing
what sshpass actually receives. This plausibly explains a persistent
"Permission denied" even after the username and password length were
both confirmed correct via the debug step (which used env: and so
never hit this).

All five steps now receive SSH_USER/SSH_PASS/SSH_PORT/SSH_HOST via
env:, which bash treats as opaque values with no re-parsing.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015FffvxxJp5wG34Ru48GBig
2026-07-29 20:42:57 +02:00
SluijsensandClaude Sonnet 5 3776c0f27b Adds a trimmed-length check to the PI_MAIN_PASSWORD debug step
Continuous Integration / config (pull_request) Successful in 9s
Continuous Integration / backend-build (pull_request) Successful in 4m39s
Continuous Integration / vulnerability-scan (pull_request) Successful in 4m15s
Continuous Integration / frontend-prepare (pull_request) Successful in 1m47s
Continuous Integration / backend-test (pull_request) Successful in 5m15s
Continuous Integration / frontend-build (pull_request) Successful in 2m11s
Continuous Integration / frontend-test (pull_request) Successful in 4m40s
Continuous Integration / frontend-lint (pull_request) Successful in 1m58s
Continuous Integration / publish-production (pull_request) Skipped
Continuous Integration / deploy-production (pull_request) Skipped
Continuous Integration / publish-test (pull_request) Successful in 6m25s
Deploy (SCP) / deploy (pull_request) Failing after 56s
Continuous Integration / deploy-test (pull_request) Failing after 57s
Username is now confirmed correct (14 chars, exact match). This checks
whether the password secret carries leading/trailing whitespace or a
stray newline picked up when it was pasted into Gitea - the length
differs from the trimmed length if so, without ever logging the
password's actual characters.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015FffvxxJp5wG34Ru48GBig
2026-07-29 20:39:36 +02:00
SluijsensandClaude Sonnet 5 92547efd47 Allows deploy-test to run on pull_request events too
Continuous Integration / config (pull_request) Canceled after 0s
Continuous Integration / backend-build (pull_request) Canceled after 0s
Continuous Integration / backend-test (pull_request) Canceled after 0s
Continuous Integration / vulnerability-scan (pull_request) Canceled after 0s
Continuous Integration / frontend-prepare (pull_request) Canceled after 0s
Continuous Integration / frontend-build (pull_request) Canceled after 0s
Continuous Integration / frontend-test (pull_request) Canceled after 0s
Continuous Integration / frontend-lint (pull_request) Canceled after 0s
Continuous Integration / publish-test (pull_request) Canceled after 0s
Continuous Integration / publish-production (pull_request) Canceled after 0s
Continuous Integration / deploy-test (pull_request) Canceled after 0s
Continuous Integration / deploy-production (pull_request) Canceled after 0s
So PR pushes deploy straight to the test environment, without a manual
workflow_dispatch each time. Only one test environment exists, so this
means concurrent PRs would overwrite each other's deploy - acceptable
with a single active PR, worth revisiting once that's no longer true.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015FffvxxJp5wG34Ru48GBig
2026-07-29 19:41:17 +02:00
SluijsensandClaude Sonnet 5 56f4f6fe0f Adds Monitoring Setup docs and deploy-scp troubleshooting/debug fixes
Continuous Integration / config (pull_request) Successful in 9s
Continuous Integration / backend-build (pull_request) Successful in 4m27s
Continuous Integration / vulnerability-scan (pull_request) Successful in 4m10s
Continuous Integration / backend-test (pull_request) Canceled after 0s
Continuous Integration / frontend-build (pull_request) Canceled after 0s
Continuous Integration / frontend-test (pull_request) Canceled after 0s
Continuous Integration / frontend-lint (pull_request) Canceled after 0s
Continuous Integration / publish-test (pull_request) Canceled after 0s
Continuous Integration / publish-production (pull_request) Canceled after 0s
Continuous Integration / deploy-test (pull_request) Canceled after 0s
Continuous Integration / deploy-production (pull_request) Canceled after 0s
Continuous Integration / frontend-prepare (pull_request) Canceled after 50s
Monitoring Setup: operations/plans/monitoring-setup-plan.md and
operations/monitoring/monitoring-instructions.md, covering Sentry alert
rules on the security_event tag, UptimeRobot's 6 liveness monitors, and
the two new Umami website entries for the admin SPA.

deployment-instructions.md gains a missing Observability__Environment
host var (without it, both environments would tag Sentry events as
"Production"), the nginx client_max_body_size fix for the 413 seen on
publish-test/production artifact uploads, and two troubleshooting notes
on Gitea Actions re-run behaviour: re-running deploy-test/production
alone loses the run's uploaded artifact, and re-running all jobs on an
existing (rather than a brand new) run can replay stale secrets.

deploy-scp.yaml: step names no longer show literal unresolved
${{ inputs.* }} text (Gitea doesn't interpolate that context in step
names), and a temporary debug step logs PI_MAIN_USERNAME/PASSWORD
length plus a username equality check to diagnose a persistent
Permission denied during the SSH steps, without ever logging the
secret values themselves.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015FffvxxJp5wG34Ru48GBig
2026-07-29 19:39:44 +02:00
Sluijsens 2cd8c4aa88 Renames the backup script to match the instance, not the software
Continuous Integration / config (pull_request) Successful in 10s
Continuous Integration / backend-build (pull_request) Successful in 4m6s
Continuous Integration / vulnerability-scan (pull_request) Successful in 3m39s
Continuous Integration / frontend-prepare (pull_request) Failing after 58s
Continuous Integration / frontend-build (pull_request) Skipped
Continuous Integration / frontend-test (pull_request) Skipped
Continuous Integration / frontend-lint (pull_request) Skipped
Continuous Integration / backend-test (pull_request) Successful in 4m26s
Continuous Integration / publish-test (pull_request) Skipped
Continuous Integration / publish-production (pull_request) Skipped
Continuous Integration / deploy-test (pull_request) Skipped
Continuous Integration / deploy-production (pull_request) Skipped
backup-slpmodularcms-db.sh named the CMS; this backs up slpsoftware
specifically, same as every other instance-scoped path already does.
Hardcoded in deploy-scp.yaml itself, so this is the actual pipeline
code, not just docs describing it.
2026-07-29 14:17:41 +02:00
Sluijsens 9f4ae475e7 U5 — the gate everything else has to pass
continuous_integration.yaml: six blocking checks, then a separate
publish per environment so a Vite build never gets tagged for the
wrong one, then a call into last commit's deploy workflow. Along the
way: the lint list had drifted (two problems not in the requirement,
one already fixed), and the Umami-origin gate needed a variable pair
of its own since the backend's side of that comparison lives on the
host, not in CI. Pinned the two vulnerable packages while at it.
2026-07-28 16:07:03 +02:00
Sluijsens bd2a963498 U6 — the deploy workflow itself
deploy-scp.yaml: download, back up the production database before
touching anything, upload into a timestamped release, symlink the
persistent website in, switch current and restart, verify /health
with retries, prune old releases only once that check passes. No
sudo, no container actions, one shared interface U5 will call next.
2026-07-28 15:33:03 +02:00