Maak DEPLOY_PATH configureerbaar via Gitea Actions variable i.p.v. hardcoded #7
@@ -1,6 +1,11 @@
|
|||||||
name: Continuous Integration
|
name: Continuous Integration
|
||||||
on:
|
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:
|
pull_request:
|
||||||
types: [opened, synchronize, reopened]
|
types: [opened, synchronize, reopened]
|
||||||
push:
|
push:
|
||||||
@@ -16,6 +21,7 @@ env:
|
|||||||
NODE_VERSION: '20'
|
NODE_VERSION: '20'
|
||||||
PNPM_VERSION: '9'
|
PNPM_VERSION: '9'
|
||||||
ARTIFACT_NAME: dist
|
ARTIFACT_NAME: dist
|
||||||
|
ARTIFACT_NAME_PRODUCTION: dist-production
|
||||||
ARTIFACT_PATH: dist/
|
ARTIFACT_PATH: dist/
|
||||||
DEPLOY_ENVIRONMENT: test
|
DEPLOY_ENVIRONMENT: test
|
||||||
# Niet hardcoded: DEPLOY_PATH komt uit een Gitea Actions repository variable
|
# 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
|
# aangepast kan worden zonder de workflow zelf te wijzigen. Zie
|
||||||
# deployment-instructions.md voor de eenmalige setup van deze variable.
|
# deployment-instructions.md voor de eenmalige setup van deze variable.
|
||||||
DEPLOY_PATH: ${{ vars.DEPLOY_PATH_TEST }}
|
DEPLOY_PATH: ${{ vars.DEPLOY_PATH_TEST }}
|
||||||
|
DEPLOY_ENVIRONMENT_PRODUCTION: production
|
||||||
|
DEPLOY_PATH_PRODUCTION: ${{ vars.DEPLOY_PATH_PRODUCTION }}
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
# Geeft de env-variabelen hierboven door als job-outputs, zodat ze ook
|
# Geeft de env-variabelen hierboven door als job-outputs, zodat ze ook
|
||||||
@@ -33,14 +41,20 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
outputs:
|
outputs:
|
||||||
artifact_name: ${{ steps.set.outputs.artifact_name }}
|
artifact_name: ${{ steps.set.outputs.artifact_name }}
|
||||||
|
artifact_name_production: ${{ steps.set.outputs.artifact_name_production }}
|
||||||
deploy_environment: ${{ steps.set.outputs.deploy_environment }}
|
deploy_environment: ${{ steps.set.outputs.deploy_environment }}
|
||||||
deploy_path: ${{ steps.set.outputs.deploy_path }}
|
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:
|
steps:
|
||||||
- id: set
|
- id: set
|
||||||
run: |
|
run: |
|
||||||
echo "artifact_name=${{ env.ARTIFACT_NAME }}" >> "$GITHUB_OUTPUT"
|
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_environment=${{ env.DEPLOY_ENVIRONMENT }}" >> "$GITHUB_OUTPUT"
|
||||||
echo "deploy_path=${{ env.DEPLOY_PATH }}" >> "$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:
|
prepare:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@@ -106,8 +120,9 @@ jobs:
|
|||||||
VITE_SENTRY_DSN: ${{ vars.VITE_SENTRY_DSN }}
|
VITE_SENTRY_DSN: ${{ vars.VITE_SENTRY_DSN }}
|
||||||
# Build-time tag die bepaalt of dev/test-only UI (zoals de tijdelijke
|
# Build-time tag die bepaalt of dev/test-only UI (zoals de tijdelijke
|
||||||
# SentryTestButton) zichtbaar is; zie src/components/SentryTestButton.tsx.
|
# SentryTestButton) zichtbaar is; zie src/components/SentryTestButton.tsx.
|
||||||
# Zolang er nog geen aparte productie-build/deploy bestaat, is dit altijd
|
# Dit is de testomgeving-build, dus altijd gelijk aan DEPLOY_ENVIRONMENT
|
||||||
# gelijk aan DEPLOY_ENVIRONMENT ('test').
|
# ('test'). Zie de build-production job hieronder voor de aparte
|
||||||
|
# productie-build met VITE_APP_ENV=production.
|
||||||
VITE_APP_ENV: ${{ env.DEPLOY_ENVIRONMENT }}
|
VITE_APP_ENV: ${{ env.DEPLOY_ENVIRONMENT }}
|
||||||
run: pnpm run build
|
run: pnpm run build
|
||||||
|
|
||||||
@@ -118,6 +133,59 @@ jobs:
|
|||||||
path: ${{ env.ARTIFACT_PATH }}
|
path: ${{ env.ARTIFACT_PATH }}
|
||||||
retention-days: 1
|
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:
|
test:
|
||||||
needs: build
|
needs: build
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@@ -163,3 +231,19 @@ jobs:
|
|||||||
artifact_name: ${{ needs.config.outputs.artifact_name }}
|
artifact_name: ${{ needs.config.outputs.artifact_name }}
|
||||||
environment: ${{ needs.config.outputs.deploy_environment }}
|
environment: ${{ needs.config.outputs.deploy_environment }}
|
||||||
deploy_path: ${{ needs.config.outputs.deploy_path }}
|
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 }}
|
||||||
|
|||||||
+27
-13
@@ -2,18 +2,20 @@
|
|||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
Deployment gebeurt via Gitea Actions, opgesplitst in twee bestanden:
|
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.
|
- `.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
|
## 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`.
|
- `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_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).
|
- 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` (uploadt de artifact, naam/pad uit `env.ARTIFACT_NAME`/`env.ARTIFACT_PATH`) → `test` (lint + unit tests)
|
- `prepare` → `build` (test-bundel, `VITE_APP_ENV=test`) → `test` (lint + unit tests)
|
||||||
- Een losse `config`-job zet deze `env`-waarden om in job-outputs (zie hieronder waarom dat nodig is).
|
- `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.
|
||||||
- `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).
|
- Een losse `config`-job zet alle `env`-waarden (test én productie) om in job-outputs (zie hieronder waarom dat nodig is).
|
||||||
- `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.
|
- `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?
|
### 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.
|
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 |
|
| Variable | Waarde |
|
||||||
|---|---|
|
|---|---|
|
||||||
| `DEPLOY_PATH_TEST` | `/html/test/slpsoftware` |
|
| `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
|
## Eenmalige Setup — Domeinnaam & DNS
|
||||||
- **Test**: `test.slpsoftware.nl` → moet als DNS A-record wijzen naar het publieke IP van de reverse-proxy-Pi.
|
- **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
|
## 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`).
|
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.
|
2. Selecteer de **Continuous Integration** workflow.
|
||||||
3. Klik **Run workflow**, kies de gewenste branch/ref, en start.
|
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
|
## Verifying a Deployment
|
||||||
1. Bevestig dat de Gitea Actions run succesvol is (alle jobs groen, inclusief `deploy-test`).
|
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 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`).
|
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
|
## 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.
|
- **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.
|
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
|
## 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).
|
- **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**: 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.
|
- **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
|
## 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
|
## 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.
|
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.
|
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
|
## Resolved Item — Productie-deploy 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.
|
`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
|
## 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.
|
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
|
## Deployment
|
||||||
- **Status**: Configured
|
- **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)
|
- **Rollback Plan**: Yes — `operations/deployment/rollback-plan.md` (manual re-upload of a previous `release/*` build; no persistent/database state to roll back)
|
||||||
|
|
||||||
## Monitoring
|
## Monitoring
|
||||||
@@ -11,14 +11,14 @@
|
|||||||
|
|
||||||
## Additional Readiness Items
|
## 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.
|
- **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.
|
- **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.
|
- **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
|
## Overall Readiness
|
||||||
- **Ready for Production**: Yes with caveats
|
- **Ready for Production**: Yes with caveats
|
||||||
- **Open Follow-ups**:
|
- **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
|
- ~~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
|
- ~~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`)
|
- Run a formal Lighthouse performance check before the first real production deployment (flagged as not yet run in `build-and-test-summary.md`)
|
||||||
|
|||||||
Reference in New Issue
Block a user