Automatiseer productie-deploy als opt-in stap naast de bestaande testdeploy
Voegt build-production en deploy-production jobs toe aan continuous_integration.yaml, alleen actief bij een handmatige workflow_dispatch-run met het deploy_production-vinkje aangevinkt (nooit automatisch bij een push naar master). Een aparte build-production job is nodig omdat VITE_APP_ENV een build-time Vite-variabele is: dezelfde bundel kan niet zowel als test als production getagd zijn in Sentry/analytics. Uploadpad komt uit de nieuwe Gitea-variable DEPLOY_PATH_PRODUCTION, analoog aan DEPLOY_PATH_TEST. Documentatie en production-readiness-checklist bijgewerkt om dit open item als opgelost te markeren. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
+27
-13
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user