build-production hergebruikte vars.VITE_UMAMI_WEBSITE_ID van de testbuild, maar Umami-website-ID's horen bij één specifieke domeinentry in het dashboard. Zonder deze fix zou productieverkeer in de teststatistieken terechtkomen. Introduceert VITE_UMAMI_WEBSITE_ID_PRODUCTION als losse Gitea variable, en documenteert dat er twee aparte Umami-website-entries nodig zijn (test.slpsoftware.nl / slpsoftware.nl). VITE_UMAMI_SCRIPT_URL en VITE_SENTRY_DSN blijven bewust gedeeld tussen omgevingen. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
11 KiB
Deployment Instructions
Overview
Deployment gebeurt via Gitea Actions, opgesplitst in twee bestanden:
.gitea/workflows/continuous_integration.yaml— build/test/lint-gate, plus dedeploy-test- endeploy-production-jobs..gitea/workflows/deploy.yaml— herbruikbare workflow diedist/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. Een productiedeploy is ook mogelijk, maar bewust niet automatisch — zie "How to Deploy to Production" hieronder.
Pipeline Files
continuous_integration.yaml— getriggerd doorpull_request(build/test/lint-gate, ongeacht branch),pushnaarmaster(build/test/lint-gate +deploy-test), en handmatig viaworkflow_dispatch(met een optioneeldeploy_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 variableDEPLOY_PATH_TEST, zie hieronder — bewust geen hardcoded pad),DEPLOY_ENVIRONMENT_PRODUCTION(production) enDEPLOY_PATH_PRODUCTION(gelezen uit de variableDEPLOY_PATH_PRODUCTION). prepare→build(test-bundel,VITE_APP_ENV=test) →test(lint + unit tests)build-productiondraait ernaast, alléén alsdeploy_productionis aangevinkt bij een handmatigeworkflow_dispatch-run. Dit is een aparte build (niet hetzelfde artifact alsbuild) omdatVITE_APP_ENVeen build-time Vite-variabele is: één bundel kan niet tegelijk alstesténproductiongetagd zijn in Sentry/analytics.- Een losse
config-job zet alleenv-waarden (test én productie) om in job-outputs (zie hieronder waarom dat nodig is). deploy-test(bijworkflow_dispatchof een push naarmaster) roeptdeploy.yamlaan metartifact_name/environment/deploy_pathafkomstig vanneeds.config.outputs.*(dus indirect uit hetenv:-blok).deploy-production(alléén bijworkflow_dispatchmétdeploy_production: true) roeptdeploy.yamlop dezelfde manier aan, maar met de productie-artifact/omgeving/pad. Draait nooit automatisch bij een push naarmaster.
- Heeft bovenaan een
deploy.yaml— download de artifact (naam/lokaal pad =inputs.artifact_name) en upload de inhoud via eenscp-commando (metsshpassvoor het wachtwoord) in een gewone shell-stap naar de opgegevendeploy_pathop 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.
Waarom een aparte config-job in plaats van rechtstreeks het env:-blok?
Gitea/GitHub Actions ondersteunt geen env-context in de with:-sectie waarmee een reusable workflow wordt aangeroepen (jobs.<job_id>.with) — dat werkt alléén binnen jobs.<job_id>.steps. De oplossing is een klein voorloop-job (config) dat de gewenste env-waarden via $GITHUB_OUTPUT naar job-outputs schrijft; die outputs (needs.config.outputs.*) zijn wél bruikbaar in jobs.<job_id>.with. Zo hoef je, om de artifact-naam/pad of de testdeploy-bestemming te wijzigen, alléén het env:-blok bovenaan continuous_integration.yaml aan te passen — niet de deploy-test-job zelf.
Eenmalige Setup — Gitea Secrets
Voeg deze secrets toe in Gitea: Repository → Settings → Actions → Secrets:
| Secret | Waarde |
|---|---|
PI_MAIN_HOST |
Intern IP-adres van de webserver-Pi (192.168.1.103) |
PI_MAIN_PORT |
SSH-poort (2224) |
PI_MAIN_USERNAME |
SSH-gebruikersnaam (webadmin) |
PI_MAIN_PASSWORD |
Het SSH-wachtwoord van deze gebruiker |
Deze secrets heten PI_MAIN_* (niet PI_TEST_*), omdat dezelfde Pi (Pi Main) en dezelfde inloggegevens naar verwachting ook voor toekomstige omgevingen/webhosts gebruikt worden. Mocht dat later veranderen, dan worden hiervoor alsnog omgeving-specifieke secrets geïntroduceerd.
Eenmalige Setup — Gitea Variables
Voeg deze variable toe in Gitea: Repository → Settings → Actions → Variables (geen secret — het is geen gevoelige waarde, net als VITE_UMAMI_SCRIPT_URL/VITE_SENTRY_DSN):
| 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 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 (domein al bekend, nginx/SSL-configuratie op de Pi's moet nog opgezet worden):
slpsoftware.nl(enwww.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
- Kopieer
operations/deployment/nginx/webserver-nginx.conf.examplenaar/etc/nginx/sites-available/op de webserver-Pi, maak een symlink insites-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). - Kopieer
operations/deployment/nginx/reverse-proxy-nginx.conf.examplenaar/etc/nginx/sites-available/slpsoftware-test.confop de reverse-proxy-Pi, maak een symlink insites-enabled/, en herlaad nginx. Dit is de versie van vóór certbot (alleen poort 80, geen SSL), metserver_name test.slpsoftware.nl. - Vraag op de reverse-proxy-Pi een SSL-certificaat aan met certbot (Let's Encrypt), nadat het DNS-record klopt:
sudo certbot --nginx -d test.slpsoftware.nl. Certbot herschrijft dit bestand automatisch met de HTTPS-configuratie en de HTTP→HTTPS-redirect — zieoperations/deployment/nginx/reverse-proxy-nginx.conf.post-certbot.examplevoor hoe het er dan uitziet (referentie, niet zelf kopiëren). - Zorg dat de map
/mnt/storage1/www/html/test/slpsoftwarebestaat op de webserver-Pi en schrijfbaar is voor de gebruikerwebadmin(bijv.sudo mkdir -p /mnt/storage1/www/html/test/slpsoftware && sudo chown webadmin:webadmin /mnt/storage1/www/html/test/slpsoftware).
Waarom
deploy_pathen de nginxrootniet hetzelfde pad zijn: de pipeline uploadt via SCP naardeploy_path=/html/test/slpsoftware(de waarde van de Gitea variableDEPLOY_PATH_TEST, ingelezen viaenv.DEPLOY_PATHincontinuous_integration.yaml), terwijl de nginxrootinwebserver-nginx.conf.examplehet volledige pad/mnt/storage1/www/html/test/slpsoftwareis. Dit is geen fout of inconsistentie: de SSH/SCP-gebruiker (webadmin) heeft/mnt/storage1/wwwals root (vergelijkbaar met een FTP-chroot), dus vanuit het perspectief van deze gebruiker is/html/test/slpsoftwarehet juiste (relatieve) pad, terwijl dat op het bestandssysteem van de Pi zelf overeenkomt met het volledige pad/mnt/storage1/www/html/test/slpsoftwaredat nginx alsrootgebruikt. Kortom:deploy_path(/html/test/slpsoftware) + de root van dewebadmin-gebruiker (/mnt/storage1/www) = de nginxroot(/mnt/storage1/www/html/test/slpsoftware).
How to Deploy to Test
Automatisch
Merge een pull request naar master — de deploy-test job draait dan automatisch na een groene build/test-run.
Handmatig
- In Gitea, open de repository's Actions tab.
- Selecteer de Continuous Integration workflow.
- 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:
- In Gitea, open de repository's Actions tab.
- Selecteer de Continuous Integration workflow.
- Klik Run workflow, kies de gewenste branch/ref (meestal
master). - Vink
deploy_productionaan voordat je de run start. - Dit triggert naast de gebruikelijke
build/test/deploy-testookbuild-productionendeploy-production.
Vereist eenmalig vooraf:
- De Gitea-variable
DEPLOY_PATH_PRODUCTION(zie boven). - De Gitea-variable
VITE_UMAMI_WEBSITE_ID_PRODUCTION(zieumami-setup.mdstap 4/5) — zónder deze wordt productieverkeer per ongeluk meegeteld bij de teststatistieken. - De productie-nginx/SSL-configuratie + webroot-map op de Pi's (zie "Eenmalige Setup — Domeinnaam & DNS").
Verifying a Deployment
- Bevestig dat de Gitea Actions run succesvol is (alle relevante jobs groen —
deploy-testaltijd,build-production/deploy-productionalleen als jedeploy_productionhad aangevinkt). - Open de omgeving in de browser (test: via het adres/IP dat je bij de reverse-proxy hebt ingesteld; productie:
slpsoftware.nlzodra de nginx/SSL-setup daar staat) en controleer dat de site correct laadt (check de browserconsole op fouten, zoals in de handmatige smoke test inconstruction/build-and-test/integration-test-instructions.md).
Future Work
- Van wachtwoord naar SSH-key: vervang
sshpass -p "${{ secrets.PI_MAIN_PASSWORD }}" scp ...indeploy.yamldoor eenscp-commando met-i <key-bestand>(een nieuwe secretPI_MAIN_SSH_KEYdie je eerst als bestand wegschrijft in de run-stap), en zet de bijbehorende public key in~/.ssh/authorized_keysvan dewebadmin-gebruiker op de webserver-Pi. Verwijder daarna het wachtwoord-secret. - 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 vannginx/webserver-nginx.conf.exampleennginx/reverse-proxy-nginx.conf.example, met domeinslpsoftware.nl. - Aparte productie-Umami-website/Sentry-project:
build-productiongebruikt momenteel dezelfdeVITE_UMAMI_WEBSITE_ID/VITE_SENTRY_DSNals de testbuild. Overweeg dit te splitsen zodra test- en productieverkeer niet meer door elkaar gemengd mogen worden in dezelfde analytics/Sentry-projecten.