Maak DEPLOY_PATH configureerbaar via Gitea Actions variable i.p.v. hardcoded #7

Merged
Sluijsens merged 10 commits from feature/production_deploy_automation into master 2026-07-30 23:58:06 +02:00
4 changed files with 122 additions and 24 deletions
Showing only changes of commit a208aebae0 - Show all commits
+87 -3
View File
@@ -1,6 +1,11 @@
name: Continuous Integration
on:
workflow_dispatch: {}
workflow_dispatch:
inputs:
deploy_production:
description: 'Na een succesvolle build/test ook naar productie deployen (naast de automatische testdeploy)?'
type: boolean
default: false
pull_request:
types: [opened, synchronize, reopened]
push:
@@ -16,6 +21,7 @@ env:
NODE_VERSION: '20'
PNPM_VERSION: '9'
ARTIFACT_NAME: dist
ARTIFACT_NAME_PRODUCTION: dist-production
ARTIFACT_PATH: dist/
DEPLOY_ENVIRONMENT: test
# Niet hardcoded: DEPLOY_PATH komt uit een Gitea Actions repository variable
@@ -23,6 +29,8 @@ env:
# aangepast kan worden zonder de workflow zelf te wijzigen. Zie
# deployment-instructions.md voor de eenmalige setup van deze variable.
DEPLOY_PATH: ${{ vars.DEPLOY_PATH_TEST }}
DEPLOY_ENVIRONMENT_PRODUCTION: production
DEPLOY_PATH_PRODUCTION: ${{ vars.DEPLOY_PATH_PRODUCTION }}
jobs:
# Geeft de env-variabelen hierboven door als job-outputs, zodat ze ook
@@ -33,14 +41,20 @@ jobs:
runs-on: ubuntu-latest
outputs:
artifact_name: ${{ steps.set.outputs.artifact_name }}
artifact_name_production: ${{ steps.set.outputs.artifact_name_production }}
deploy_environment: ${{ steps.set.outputs.deploy_environment }}
deploy_path: ${{ steps.set.outputs.deploy_path }}
deploy_environment_production: ${{ steps.set.outputs.deploy_environment_production }}
deploy_path_production: ${{ steps.set.outputs.deploy_path_production }}
steps:
- id: set
run: |
echo "artifact_name=${{ env.ARTIFACT_NAME }}" >> "$GITHUB_OUTPUT"
echo "artifact_name_production=${{ env.ARTIFACT_NAME_PRODUCTION }}" >> "$GITHUB_OUTPUT"
echo "deploy_environment=${{ env.DEPLOY_ENVIRONMENT }}" >> "$GITHUB_OUTPUT"
echo "deploy_path=${{ env.DEPLOY_PATH }}" >> "$GITHUB_OUTPUT"
echo "deploy_environment_production=${{ env.DEPLOY_ENVIRONMENT_PRODUCTION }}" >> "$GITHUB_OUTPUT"
echo "deploy_path_production=${{ env.DEPLOY_PATH_PRODUCTION }}" >> "$GITHUB_OUTPUT"
prepare:
runs-on: ubuntu-latest
@@ -106,8 +120,9 @@ jobs:
VITE_SENTRY_DSN: ${{ vars.VITE_SENTRY_DSN }}
# Build-time tag die bepaalt of dev/test-only UI (zoals de tijdelijke
# SentryTestButton) zichtbaar is; zie src/components/SentryTestButton.tsx.
# Zolang er nog geen aparte productie-build/deploy bestaat, is dit altijd
# gelijk aan DEPLOY_ENVIRONMENT ('test').
# Dit is de testomgeving-build, dus altijd gelijk aan DEPLOY_ENVIRONMENT
# ('test'). Zie de build-production job hieronder voor de aparte
# productie-build met VITE_APP_ENV=production.
VITE_APP_ENV: ${{ env.DEPLOY_ENVIRONMENT }}
run: pnpm run build
@@ -118,6 +133,59 @@ jobs:
path: ${{ env.ARTIFACT_PATH }}
retention-days: 1
# Aparte build voor productie, alleen nodig/gedraaid als deploy_production
# is aangevinkt bij een handmatige workflow_dispatch-run. Dit bestaat naast
# de gewone `build`-job (in plaats van die job te hergebruiken) omdat
# VITE_APP_ENV een build-time Vite-variabele is: één en dezelfde dist/-bundel
# kan niet zowel als 'test' als 'production' getagd zijn. Zonder deze aparte
# build zou de test-bundel (met environment: test) naar productie
# gedeployed worden, wat Sentry-events/analytics verkeerd zou taggen.
build-production:
needs: prepare
runs-on: ubuntu-latest
if: github.event_name == 'workflow_dispatch' && github.event.inputs.deploy_production == 'true'
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
- uses: pnpm/action-setup@v4
with:
version: ${{ env.PNPM_VERSION }}
- name: Get pnpm store directory
id: pnpm-store
run: echo "path=$(pnpm store path)" >> "$GITHUB_OUTPUT"
- name: Restore pnpm store
uses: actions/cache@v4
with:
path: ${{ steps.pnpm-store.outputs.path }}
key: pnpm-${{ hashFiles('pnpm-lock.yaml') }}
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Build
env:
# Zelfde Umami/Sentry-variabelen als de testbuild hierboven, want er
# is (nog) geen apart productie-Umami-website-ID of -Sentry-project
# gekozen. Splits deze pas op zodra dat nodig blijkt.
VITE_UMAMI_SCRIPT_URL: ${{ vars.VITE_UMAMI_SCRIPT_URL }}
VITE_UMAMI_WEBSITE_ID: ${{ vars.VITE_UMAMI_WEBSITE_ID }}
VITE_SENTRY_DSN: ${{ vars.VITE_SENTRY_DSN }}
VITE_APP_ENV: ${{ env.DEPLOY_ENVIRONMENT_PRODUCTION }}
run: pnpm run build
- name: Upload build artifact
uses: actions/upload-artifact@v3
with:
name: ${{ env.ARTIFACT_NAME_PRODUCTION }}
path: ${{ env.ARTIFACT_PATH }}
retention-days: 1
test:
needs: build
runs-on: ubuntu-latest
@@ -163,3 +231,19 @@ jobs:
artifact_name: ${{ needs.config.outputs.artifact_name }}
environment: ${{ needs.config.outputs.deploy_environment }}
deploy_path: ${{ needs.config.outputs.deploy_path }}
# Productie-deploy is bewust NIET automatisch bij elke push naar master
# (in tegenstelling tot deploy-test hierboven): dit is pas een expliciete,
# bewuste actie via workflow_dispatch met het "deploy_production"-vinkje
# aangevinkt. Zo blijft de bestaande testdeploy-flow ongewijzigd en kan
# niemand per ongeluk productie deployen door simpelweg naar master te
# pushen of de workflow handmatig te starten zonder dat vinkje.
deploy-production:
needs: [build-production, test, config]
if: github.event_name == 'workflow_dispatch' && github.event.inputs.deploy_production == 'true'
uses: ./.gitea/workflows/deploy.yaml
secrets: inherit
with:
artifact_name: ${{ needs.config.outputs.artifact_name_production }}
environment: ${{ needs.config.outputs.deploy_environment_production }}
deploy_path: ${{ needs.config.outputs.deploy_path_production }}
@@ -2,18 +2,20 @@
## Overview
Deployment gebeurt via Gitea Actions, opgesplitst in twee bestanden:
- `.gitea/workflows/continuous_integration.yaml` — build/test/lint-gate, plus de `deploy-test` job.
- `.gitea/workflows/continuous_integration.yaml` — build/test/lint-gate, plus de `deploy-test`- en `deploy-production`-jobs.
- `.gitea/workflows/deploy.yaml` — herbruikbare workflow die `dist/` via SCP naar een omgeving uploadt.
Sinds deze stap wordt er automatisch gedeployed naar een **testomgeving**: een Raspberry Pi die de site serveert via nginx, bereikbaar achter een tweede Raspberry Pi met een nginx reverse proxy.
Sinds deze stap wordt er automatisch gedeployed naar een **testomgeving**: een Raspberry Pi die de site serveert via nginx, bereikbaar achter een tweede Raspberry Pi met een nginx reverse proxy. Een **productiedeploy** is ook mogelijk, maar bewust niet automatisch — zie "How to Deploy to Production" hieronder.
## Pipeline Files
- `continuous_integration.yaml` — getriggerd door `pull_request` (build/test/lint-gate, ongeacht branch), `push` naar `master` (build/test/lint-gate + `deploy-test`), en handmatig via `workflow_dispatch`.
- Heeft bovenaan een `env:`-blok met alle aanpasbare waarden op één plek: `NODE_VERSION`, `PNPM_VERSION`, `ARTIFACT_NAME` (`dist`), `ARTIFACT_PATH` (`dist/`), `DEPLOY_ENVIRONMENT` (`test`) en `DEPLOY_PATH` (gelezen uit de Gitea repository variable `DEPLOY_PATH_TEST`, zie hieronder — bewust geen hardcoded pad, zodat het aan te passen is zonder de workflow zelf te wijzigen).
- `prepare``build` (uploadt de artifact, naam/pad uit `env.ARTIFACT_NAME`/`env.ARTIFACT_PATH`) → `test` (lint + unit tests)
- Een losse `config`-job zet deze `env`-waarden om in job-outputs (zie hieronder waarom dat nodig is).
- `deploy-test` (alleen bij `workflow_dispatch` of een push naar `master`) roept `deploy.yaml` aan met `artifact_name`/`environment`/`deploy_path` afkomstig van `needs.config.outputs.*` (dus indirect uit het `env:`-blok).
- `deploy.yaml` — download de artifact (naam/lokaal pad = `inputs.artifact_name`) en upload de inhoud via een `scp`-commando (met `sshpass` voor het wachtwoord) in een gewone shell-stap naar de opgegeven `deploy_path` op de host uit de meegegeven secrets.
- `continuous_integration.yaml` — getriggerd door `pull_request` (build/test/lint-gate, ongeacht branch), `push` naar `master` (build/test/lint-gate + `deploy-test`), en handmatig via `workflow_dispatch` (met een optioneel `deploy_production`-vinkje).
- Heeft bovenaan een `env:`-blok met alle aanpasbare waarden op één plek: `NODE_VERSION`, `PNPM_VERSION`, `ARTIFACT_NAME` (`dist`), `ARTIFACT_NAME_PRODUCTION` (`dist-production`), `ARTIFACT_PATH` (`dist/`), `DEPLOY_ENVIRONMENT` (`test`), `DEPLOY_PATH` (gelezen uit de Gitea repository variable `DEPLOY_PATH_TEST`, zie hieronder — bewust geen hardcoded pad), `DEPLOY_ENVIRONMENT_PRODUCTION` (`production`) en `DEPLOY_PATH_PRODUCTION` (gelezen uit de variable `DEPLOY_PATH_PRODUCTION`).
- `prepare``build` (test-bundel, `VITE_APP_ENV=test`) → `test` (lint + unit tests)
- `build-production` draait ernaast, alléén als `deploy_production` is aangevinkt bij een handmatige `workflow_dispatch`-run. Dit is een aparte build (niet hetzelfde artifact als `build`) omdat `VITE_APP_ENV` een build-time Vite-variabele is: één bundel kan niet tegelijk als `test` én `production` getagd zijn in Sentry/analytics.
- Een losse `config`-job zet alle `env`-waarden (test én productie) om in job-outputs (zie hieronder waarom dat nodig is).
- `deploy-test` (bij `workflow_dispatch` of een push naar `master`) roept `deploy.yaml` aan met `artifact_name`/`environment`/`deploy_path` afkomstig van `needs.config.outputs.*` (dus indirect uit het `env:`-blok).
- `deploy-production` (alléén bij `workflow_dispatch` mét `deploy_production: true`) roept `deploy.yaml` op dezelfde manier aan, maar met de productie-artifact/omgeving/pad. Draait **nooit** automatisch bij een push naar `master`.
- `deploy.yaml` — download de artifact (naam/lokaal pad = `inputs.artifact_name`) en upload de inhoud via een `scp`-commando (met `sshpass` voor het wachtwoord) in een gewone shell-stap naar de opgegeven `deploy_path` op de host uit de meegegeven secrets. Deze workflow is omgeving-agnostisch (test/productie) en hoefde niet gewijzigd te worden.
### Waarom `sshpass`/`scp` in een shell-stap in plaats van de `appleboy/scp-action` Docker-action?
De oorspronkelijke aanpak gebruikte de `appleboy/scp-action` (een Docker-container-action). Dit werkte niet op deze zelf-gehoste Gitea-runner: de stap faalde met `failed to attach to container: unable to upgrade to tcp, received 409`, een bekende beperking van Podman's Docker-compatibele API, die het attach/log-streaming-mechanisme voor container-based actions niet volledig ondersteunt. De huidige aanpak (een normale `run:`-stap die `sshpass` installeert en zelf `scp` aanroept) heeft geen geneste container nodig en werkt daardoor wel.
@@ -39,12 +41,13 @@ Voeg deze variable toe in Gitea: **Repository → Settings → Actions → Varia
| Variable | Waarde |
|---|---|
| `DEPLOY_PATH_TEST` | `/html/test/slpsoftware` |
| `DEPLOY_PATH_PRODUCTION` | het uploadpad voor productie op Pi Main (bepaal dit zodra de productiemap op de Pi is aangemaakt, analoog aan stap 4 van "Eenmalige Setup — nginx & SSL"; bijv. `/html/slpsoftware` als de nginx `root` `/mnt/storage1/www/html/slpsoftware` wordt) |
Dit vervangt het eerder hardcoded `DEPLOY_PATH` in het `env:`-blok van `continuous_integration.yaml`, zodat het upload-pad aangepast kan worden zonder de workflow zelf te wijzigen. Zonder deze variable is `DEPLOY_PATH` leeg en faalt de `deploy-test`-job bij de SCP-upload — deze variable moet dus vóór de eerste deploy zijn ingesteld. Wanneer later een productie-job wordt toegevoegd, hoort daar een eigen variable bij (bijv. `DEPLOY_PATH_PRODUCTION`), analoog aan dit patroon.
Dit vervangt het eerder hardcoded `DEPLOY_PATH` in het `env:`-blok van `continuous_integration.yaml`, zodat het upload-pad aangepast kan worden zonder de workflow zelf te wijzigen. Zonder `DEPLOY_PATH_TEST` is `DEPLOY_PATH` leeg en faalt de `deploy-test`-job bij de SCP-upload — deze variable moet dus vóór de eerste deploy zijn ingesteld. Zonder `DEPLOY_PATH_PRODUCTION` faalt op dezelfde manier de `deploy-production`-job; die hoeft pas ingesteld te zijn vóór de eerste keer dat je `deploy_production` aanvinkt.
## Eenmalige Setup — Domeinnaam & DNS
- **Test**: `test.slpsoftware.nl` → moet als DNS A-record wijzen naar het publieke IP van de reverse-proxy-Pi.
- **Productie** (nog niet automatisch gedeployed, maar domein al bekend): `slpsoftware.nl` (en `www.slpsoftware.nl`) → zelfde reverse-proxy-Pi, zodra productie wordt opgezet.
- **Productie** (domein al bekend, nginx/SSL-configuratie op de Pi's moet nog opgezet worden): `slpsoftware.nl` (en `www.slpsoftware.nl`) → zelfde reverse-proxy-Pi. De deploy-pipeline zelf ondersteunt productie al (zie hieronder); wat nog ontbreekt is de nginx/SSL-configuratie en het aanmaken van de webroot-map op de Pi's, analoog aan de teststappen hieronder.
## Eenmalige Setup — nginx & SSL op de Raspberry Pi's
1. Kopieer `operations/deployment/nginx/webserver-nginx.conf.example` naar `/etc/nginx/sites-available/` op de webserver-Pi, maak een symlink in `sites-enabled/`, en herlaad nginx. Dit bestand is de daadwerkelijk in gebruik zijnde configuratie (`server_name test.slpsoftware.nl`, luistert op poort 80, serveert vanaf `/mnt/storage1/www/html/test/slpsoftware`).
@@ -63,10 +66,21 @@ Merge een pull request naar `master` — de `deploy-test` job draait dan automat
2. Selecteer de **Continuous Integration** workflow.
3. Klik **Run workflow**, kies de gewenste branch/ref, en start.
## How to Deploy to Production
Productie deployt **nooit** automatisch bij een push naar `master` — alleen via een expliciete, handmatige actie:
1. In Gitea, open de repository's **Actions** tab.
2. Selecteer de **Continuous Integration** workflow.
3. Klik **Run workflow**, kies de gewenste branch/ref (meestal `master`).
4. Vink **`deploy_production`** aan voordat je de run start.
5. Dit triggert naast de gebruikelijke `build`/`test`/`deploy-test` ook `build-production` en `deploy-production`.
Vereist eenmalig vooraf: de Gitea-variable `DEPLOY_PATH_PRODUCTION` (zie boven) en de productie-nginx/SSL-configuratie + webroot-map op de Pi's (zie "Eenmalige Setup — Domeinnaam & DNS").
## Verifying a Deployment
1. Bevestig dat de Gitea Actions run succesvol is (alle jobs groen, inclusief `deploy-test`).
2. Open de testomgeving in de browser (via het adres/IP dat je bij de reverse-proxy hebt ingesteld) en controleer dat de site correct laadt (check de browserconsole op fouten, zoals in de handmatige smoke test in `construction/build-and-test/integration-test-instructions.md`).
1. Bevestig dat de Gitea Actions run succesvol is (alle relevante jobs groen — `deploy-test` altijd, `build-production`/`deploy-production` alleen als je `deploy_production` had aangevinkt).
2. Open de omgeving in de browser (test: via het adres/IP dat je bij de reverse-proxy hebt ingesteld; productie: `slpsoftware.nl` zodra de nginx/SSL-setup daar staat) en controleer dat de site correct laadt (check de browserconsole op fouten, zoals in de handmatige smoke test in `construction/build-and-test/integration-test-instructions.md`).
## Future Work
- **Van wachtwoord naar SSH-key**: vervang `sshpass -p "${{ secrets.PI_MAIN_PASSWORD }}" scp ...` in `deploy.yaml` door een `scp`-commando met `-i <key-bestand>` (een nieuwe secret `PI_MAIN_SSH_KEY` die je eerst als bestand wegschrijft in de run-stap), en zet de bijbehorende public key in `~/.ssh/authorized_keys` van de `webadmin`-gebruiker op de webserver-Pi. Verwijder daarna het wachtwoord-secret.
- **Productie-omgeving**: voeg een `deploy-production`-job toe zodra de definitieve productiehosting bekend is (zie `deployment-plan.md`'s "Open Item"). De productie-nginx-voorbeeldconfiguratie is op verzoek van de gebruiker verwijderd totdat er een goed-werkende versie is; die kan later opnieuw opgebouwd worden naar analogie van `nginx/webserver-nginx.conf.example` en `nginx/reverse-proxy-nginx.conf.example`, met domein `slpsoftware.nl`.
- **Productie nginx/SSL-configuratie**: de deploy-pipeline ondersteunt productie al (`build-production`/`deploy-production`), maar er is nog geen productie-nginx-voorbeeldconfiguratie — deze is op verzoek van de gebruiker verwijderd totdat er een goed-werkende versie is, en kan later opnieuw opgebouwd worden naar analogie van `nginx/webserver-nginx.conf.example` en `nginx/reverse-proxy-nginx.conf.example`, met domein `slpsoftware.nl`.
- **Aparte productie-Umami-website/Sentry-project**: `build-production` gebruikt momenteel dezelfde `VITE_UMAMI_WEBSITE_ID`/`VITE_SENTRY_DSN` als de testbuild. Overweeg dit te splitsen zodra test- en productieverkeer niet meer door elkaar gemengd mogen worden in dezelfde analytics/Sentry-projecten.
@@ -16,11 +16,11 @@ Sinds deze stap is er een echte, geautomatiseerde upload naar een **testomgeving
6. De reverse-proxy-Pi is ook verantwoordelijk voor SSL: certificaten worden net als voorheen aangevraagd via certbot (Let's Encrypt) en HTTP-verkeer wordt doorverwezen naar HTTPS.
## Environments
- **Test** (nieuw, geautomatiseerd): zoals hierboven beschreven — de enige omgeving die op dit moment daadwerkelijk automatisch gedeployed wordt. Domeinnaam: `test.slpsoftware.nl` (SSL via certbot op de reverse-proxy-Pi).
- **Productie**: nog niet geautomatiseerd. Zodra de definitieve productiehosting bekend is, kan een vergelijkbare `deploy-production`-job worden toegevoegd die `deploy.yaml` aanroept met `environment: production` en de productie-secrets/pad. Domeinnaam ligt al vast: `slpsoftware.nl` (SSL eveneens via certbot). Er is (nog) geen productie-nginx-voorbeeldconfiguratie; deze is op verzoek verwijderd totdat er een goed-werkende, foutloze versie is, en kan later opnieuw opgebouwd worden naar analogie van de testomgeving-configuraties.
- **Test** (geautomatiseerd, altijd): zoals hierboven beschreven. Draait automatisch bij elke merge naar `master` en bij elke handmatige `workflow_dispatch`-run. Domeinnaam: `test.slpsoftware.nl` (SSL via certbot op de reverse-proxy-Pi).
- **Productie** (geautomatiseerd, opt-in): via een eigen `build-production`- en `deploy-production`-job in `continuous_integration.yaml`, die `deploy.yaml` aanroepen met `environment: production`. In tegenstelling tot de testdeploy draait dit **niet** automatisch bij een push naar `master` — alleen wanneer je de workflow handmatig start via `workflow_dispatch` mét het `deploy_production`-vinkje aangevinkt. Dit is bewust: zo kan niemand per ongeluk productie deployen door simpelweg naar `master` te pushen. Reden voor een aparte `build-production`-job (in plaats van hetzelfde artifact als de testbuild te hergebruiken): `VITE_APP_ENV` is een build-time Vite-variabele, dus één bundel kan niet tegelijk als `test` én `production` getagd zijn in Sentry/analytics. Domeinnaam: `slpsoftware.nl` (SSL eveneens via certbot). Er is (nog) geen productie-nginx-voorbeeldconfiguratie; deze is op verzoek verwijderd totdat er een goed-werkende, foutloze versie is, en kan later opnieuw opgebouwd worden naar analogie van de testomgeving-configuraties. Vereist eenmalig de Gitea-variable `DEPLOY_PATH_PRODUCTION` (zie `deployment-instructions.md`) — zonder deze faalt de upload.
## Automation Level
Volledig geautomatiseerd voor de testomgeving: build, test, lint én upload naar de test-Pi gebeuren zonder handmatige tussenstap, zodra er gemerged wordt naar `master` (of handmatig getriggerd wordt). Alleen productie is nog niet geautomatiseerd.
Volledig geautomatiseerd voor de testomgeving: build, test, lint én upload naar de test-Pi gebeuren zonder handmatige tussenstap, zodra er gemerged wordt naar `master` (of handmatig getriggerd wordt). Productie is ook geautomatiseerd, maar alleen als bewuste, expliciete actie (handmatige `workflow_dispatch` met het `deploy_production`-vinkje) — nooit automatisch bij een push.
## Rollback Strategy
Zie `rollback-plan.md` — voor de testomgeving kan een eerdere commit/branch opnieuw gebouwd en geüpload worden door de workflow opnieuw te triggeren.
@@ -36,8 +36,8 @@ Deze secrets heten bewust `PI_MAIN_*` in plaats van `PI_TEST_*`: alle webhosts g
Dit is bewust wachtwoord-authenticatie (voor nu, zoals gekozen), zodat de testomgeving snel werkend is. Zie "Future Work" in `deployment-instructions.md` voor de overstap naar SSH-key-authenticatie.
## Open Item — Productie-deploy Nog Niet Geautomatiseerd
Zodra de definitieve productiehosting bekend is (en of dit dezelfde soort Raspberry Pi-opstelling is, of een externe hostingpartij), voeg een `deploy-production`-job toe aan `continuous_integration.yaml` die `deploy.yaml` aanroept met `environment: production`. Zolang het dezelfde webhost (Pi Main) blijft, kunnen de bestaande `PI_MAIN_*` secrets hergebruikt worden; pas dit pas aan naar omgeving-specifieke secrets als productie daadwerkelijk op een andere host komt. Voor het uploadpad hoort een eigen Gitea variable (`DEPLOY_PATH_PRODUCTION`), analoog aan `DEPLOY_PATH_TEST` — zie `deployment-instructions.md`. Domeinnaam (`slpsoftware.nl`) en SSL-aanpak (certbot/Let's Encrypt op de reverse-proxy-Pi) liggen al vast; er is bewust (nog) geen productie-nginx-voorbeeldconfiguratie, deze wordt later opnieuw opgebouwd zodra er een goed-werkende, foutloze versie is.
## Resolved Item — Productie-deploy Geautomatiseerd
`continuous_integration.yaml` bevat nu een `build-production`- en `deploy-production`-job, alleen actief bij een handmatige `workflow_dispatch`-run met het `deploy_production`-vinkje aangevinkt (zie "Environments" hierboven). Aangezien de webhost (Pi Main) dezelfde blijft als de testomgeving, worden de bestaande `PI_MAIN_*` secrets hergebruikt pas dit pas aan naar omgeving-specifieke secrets als productie daadwerkelijk op een andere host komt. Domeinnaam (`slpsoftware.nl`) en SSL-aanpak (certbot/Let's Encrypt op de reverse-proxy-Pi) liggen al vast; er is bewust (nog) geen productie-nginx-voorbeeldconfiguratie, deze wordt later opnieuw opgebouwd zodra er een goed-werkende, foutloze versie is. **Voordat dit voor het eerst gebruikt wordt**, moet de Gitea-variable `DEPLOY_PATH_PRODUCTION` nog worden aangemaakt (zie `deployment-instructions.md`) — zonder deze faalt de upload.
## Verified Build Prerequisite
Dit plan bouwt voort op de Build and Test-stage (`construction/build-and-test/build-and-test-summary.md`): `pnpm run build` produceert een statische `dist/`-bundel zonder server-side vereisten, geschikt om direct door nginx geserveerd te worden.
@@ -2,7 +2,7 @@
## Deployment
- **Status**: Configured
- **Method**: Manually-triggered Gitea Actions pipeline (`workflow_dispatch`, also runs automatically as a build/test/lint gate on pull requests) that packages the `dist/` build as a downloadable artifact; actual upload to the host is currently a manual step (see `operations/deployment/deployment-plan.md`)
- **Method**: Gitea Actions pipeline (`workflow_dispatch`, also runs automatically as a build/test/lint gate on pull requests, and as a full build/test/deploy-test on every push to `master`) that builds and uploads `dist/` straight to the test host via SCP. Production deploy is also automated (`build-production`/`deploy-production` jobs), but intentionally opt-in only — triggered by a manual `workflow_dispatch` run with the `deploy_production` checkbox, never automatically on push (see `operations/deployment/deployment-plan.md`)
- **Rollback Plan**: Yes — `operations/deployment/rollback-plan.md` (manual re-upload of a previous `release/*` build; no persistent/database state to roll back)
## Monitoring
@@ -11,14 +11,14 @@
## Additional Readiness Items
- **Backups**: N/A — this unit is a static marketing site (`dist/` bundle) with no database and no server-side/persistent state; nothing to back up beyond the source repository itself, which is already under git version control.
- **Secrets Management**: N/A for now — no automated host upload exists yet, so no host credentials are configured in Gitea Actions at this stage. When the automatic deploy step is added later (tracked as an open item in `deployment-plan.md`), credentials must be stored as Gitea Actions Secrets, never committed to the repo.
- **Secrets Management**: Configured — host credentials (`PI_MAIN_HOST`/`PORT`/`USERNAME`/`PASSWORD`) are stored as Gitea Actions Secrets (never committed to the repo) and reused for both test and production deploys, since both target the same host for now; see `deployment-instructions.md`.
- **Runbook/Support Handover**: Partially covered — `operations/deployment/deployment-instructions.md` and `operations/deployment/rollback-plan.md` document how to trigger a release, upload the build, and roll back. No separate incident-response runbook exists beyond these documents, which is proportionate given this is a single-owner static site with no backend to page someone about.
- **Alert Ownership**: N/A — Alerting was explicitly declared out of scope in Monitoring Setup, so there is no alert-on-call/ownership rotation to define. If the chosen uptime dashboard tool (UptimeRobot/Better Uptime) is configured with its own opportunistic e-mail notification, the site owner is the sole recipient.
## Overall Readiness
- **Ready for Production**: Yes with caveats
- **Open Follow-ups**:
- Finalize the hosting/domain setup and extend `.gitea/workflows/deploy.yml`'s `deploy` job to actually upload `dist/` to the host, instead of only packaging it as a downloadable artifact (see `deployment-plan.md` "Open Item")
- ~~Finalize the hosting/domain setup and extend the deploy pipeline to actually upload `dist/` to the host~~ — **Resolved**: `.gitea/workflows/continuous_integration.yaml` now has automated `build`/`deploy-test` (always, on push to `master` or manual dispatch) and `build-production`/`deploy-production` (opt-in, manual `workflow_dispatch` with `deploy_production` checked). Remaining one-time manual setup: create the `DEPLOY_PATH_PRODUCTION` Gitea variable and the production nginx/SSL configuration on the Pi's before the first production run (see `deployment-instructions.md`)
- ~~Decide the client-side error logging destination~~ — **Resolved and verified**: console + Sentry free tier, implemented and confirmed working end-to-end (errors, logs, metrics) locally and on the test environment, including tracing, environment/release tags, and an ad-blocker-proof tunnel (see `monitoring-setup.md`); remaining manual step is creating the `VITE_SENTRY_DSN` Gitea Actions variable for the test/production build
- ~~Pick and configure the concrete analytics tool and uptime dashboard tool~~ — **Resolved**: self-hosted Umami (Podman on Pi Main) + UptimeRobot decided; see `monitoring-setup.md` and the new `umami-setup.md`. Remaining manual follow-ups: actually deploy the Umami containers, set up `analytics.slpsoftware.nl` DNS/SSL, create the UptimeRobot monitor, and register the final production URL once hosting is finalized
- Run a formal Lighthouse performance check before the first real production deployment (flagged as not yet run in `build-and-test-summary.md`)