Migratie naar 2-container opzet (app + database)
De Express-app serveert nu zelf de gebouwde Vite-frontend op / met een SPA-fallback naar index.html; de aparte nginx-container is vervallen. Bedoeld voor self-hosting op een Raspberry Pi: één image, één origin, geen interne proxy en geen CORS tussen frontend en API. Docker - Nieuwe multi-stage Dockerfile in de root met targets test, development en production; frontend/Dockerfile, frontend/nginx-spa.conf en backend/Dockerfile verwijderd - docker-compose.yml: alleen app + postgres, app publiceert APP_PORT (standaard 3400) naar containerpoort 3001 - docker-compose.dev.yml: dev-container draait API (node --watch) en de Vite dev-server samen via docker/dev-entrypoint.sh, hot reload intact - .dockerignore, .env.example, .gitignore en .gitattributes toegevoegd Backend - express.static + SPA-fallback op STATIC_DIR (default /app/public), met immutable caching voor gehashte assets en no-store voor index.html - Rate limiting alleen nog op /api — statische bestanden liepen anders na 200 requests tegen de limiet aan - Helmet CSP toegespitst op wat de frontend laadt (Google Fonts, inline styles, base64-avatars); upgrade-insecure-requests uit voor intern http - Schema-init verplaatst naar initDb(), niet meer bij import van db.js Tests en CI/CD - backend/test/app.test.js: 11 tests (node --test) voor API-routes, SPA-fallback, cache- en security-headers, zonder database - test.ps1 / test.sh: backend-tests, productiebuild en smoketest op de echte app + postgres stack - azure-pipelines.yml: Test -> Build (incl. smoketest) -> Publish (multi-arch amd64 + arm64, alleen op main/master of een tag) Scripts en documentatie - dev.ps1/dev.sh met targets app en postgres, acties logs en test; dev.ps1 up wist de dev-database niet langer (alleen dev.ps1 down) - release.ps1/release.sh bouwen één image voor amd64 + arm64 en draaien de testsuite vóór het publiceren - README.md en BEHEER.md bijgewerkt voor de nieuwe structuur Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
3c67381de9
commit
bfec3a25f3
@@ -0,0 +1,195 @@
|
||||
# ╔══════════════════════════════════════════════════════════════════════════╗
|
||||
# ║ VibeFinance CI/CD — 2-container opzet ║
|
||||
# ║ ║
|
||||
# ║ Eén app-image (Express API + gebouwde Vite-frontend) + postgres. ║
|
||||
# ║ ║
|
||||
# ║ Stages: ║
|
||||
# ║ Test backend-tests (Dockerfile target 'test') ║
|
||||
# ║ Build productie-image bouwen + smoketest op de echte stack ║
|
||||
# ║ Publish multi-arch image (amd64 + arm64) naar de registry pushen ║
|
||||
# ║ ║
|
||||
# ║ Vereist in Azure DevOps: ║
|
||||
# ║ - Service connection (type: Docker Registry) naar de registry; vul ║
|
||||
# ║ de naam in bij variabele dockerRegistryServiceConnection ║
|
||||
# ║ - Agent met Docker en buildx (ubuntu-latest heeft dit) ║
|
||||
# ╚══════════════════════════════════════════════════════════════════════════╝
|
||||
|
||||
trigger:
|
||||
branches:
|
||||
include:
|
||||
- main
|
||||
- master
|
||||
- feature/*
|
||||
tags:
|
||||
include:
|
||||
- "*"
|
||||
|
||||
pr:
|
||||
branches:
|
||||
include:
|
||||
- main
|
||||
- master
|
||||
|
||||
variables:
|
||||
# ── Aanpassen per omgeving ──────────────────────────────────────────────
|
||||
dockerRegistryServiceConnection: "vibefinance-registry"
|
||||
registryHost: "vibehoogie.duckdns.org/vibe"
|
||||
imageName: "vibefinance-app"
|
||||
# ── Afgeleid ────────────────────────────────────────────────────────────
|
||||
imageRepo: "$(registryHost)/$(imageName)"
|
||||
|
||||
pool:
|
||||
vmImage: ubuntu-latest
|
||||
|
||||
stages:
|
||||
|
||||
# ── Stage 1: tests ──────────────────────────────────────────────────────
|
||||
- stage: Test
|
||||
displayName: Tests
|
||||
jobs:
|
||||
- job: BackendTests
|
||||
displayName: Backend-tests (node --test)
|
||||
steps:
|
||||
- checkout: self
|
||||
fetchDepth: 1
|
||||
|
||||
- script: |
|
||||
set -euo pipefail
|
||||
docker build --target test -t vibefinance/app:test .
|
||||
docker run --rm vibefinance/app:test
|
||||
displayName: npm test (in Docker, zonder database)
|
||||
|
||||
# ── Stage 2: image bouwen en smoketesten ────────────────────────────────
|
||||
- stage: Build
|
||||
displayName: Build en smoketest
|
||||
dependsOn: Test
|
||||
jobs:
|
||||
- job: BuildImage
|
||||
displayName: Productie-image + smoketest
|
||||
steps:
|
||||
- checkout: self
|
||||
fetchDepth: 1
|
||||
|
||||
- script: |
|
||||
set -euo pipefail
|
||||
APP_VERSION=$(node -p "require('./package.json').version")
|
||||
echo "##vso[task.setvariable variable=appVersion]$APP_VERSION"
|
||||
echo "Versie: $APP_VERSION"
|
||||
displayName: Versienummer uit package.json
|
||||
|
||||
- script: |
|
||||
set -euo pipefail
|
||||
docker build --target production \
|
||||
--build-arg APP_VERSION=$(appVersion) \
|
||||
--build-arg VITE_APP_VERSION=$(appVersion) \
|
||||
--build-arg VITE_API_URL=/api \
|
||||
-t vibefinance/app:ci .
|
||||
displayName: Productie-image bouwen (incl. Vite-build)
|
||||
|
||||
- script: |
|
||||
set -euo pipefail
|
||||
docker network create vibefinance-ci
|
||||
|
||||
docker run -d --name ci-postgres --network vibefinance-ci \
|
||||
-e POSTGRES_DB=vibefinance \
|
||||
-e POSTGRES_USER=vibefinance \
|
||||
-e POSTGRES_PASSWORD=cipassword \
|
||||
postgres:18-alpine
|
||||
|
||||
for i in $(seq 1 30); do
|
||||
if docker exec ci-postgres pg_isready -U vibefinance >/dev/null 2>&1; then break; fi
|
||||
sleep 2
|
||||
done
|
||||
|
||||
docker run -d --name ci-app --network vibefinance-ci \
|
||||
-e NODE_ENV=production \
|
||||
-e JWT_SECRET=ci-secret \
|
||||
-e UPDATE_CHECK_URL= \
|
||||
-e DATABASE_URL=postgresql://vibefinance:cipassword@ci-postgres:5432/vibefinance \
|
||||
-p 3001:3001 \
|
||||
vibefinance/app:ci
|
||||
|
||||
gezond=0
|
||||
for i in $(seq 1 30); do
|
||||
if curl -fsS http://localhost:3001/api/health | grep -q ok; then gezond=1; break; fi
|
||||
sleep 2
|
||||
done
|
||||
if [ "$gezond" -ne 1 ]; then
|
||||
echo "App werd niet gezond. Logs:"
|
||||
docker logs ci-app
|
||||
exit 1
|
||||
fi
|
||||
echo "/api/health : ok"
|
||||
|
||||
curl -fsS http://localhost:3001/ | grep -q root
|
||||
echo "/ : index.html"
|
||||
|
||||
curl -fsS http://localhost:3001/voortgang | grep -q root
|
||||
echo "/voortgang : index.html (SPA-fallback)"
|
||||
|
||||
code=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:3001/api/bestaat-niet)
|
||||
test "$code" = "404"
|
||||
echo "/api/... : 404 JSON"
|
||||
displayName: Smoketest op app + postgres
|
||||
|
||||
- script: |
|
||||
docker logs ci-app || true
|
||||
docker rm -f ci-app ci-postgres || true
|
||||
docker network rm vibefinance-ci || true
|
||||
displayName: Opruimen
|
||||
condition: always()
|
||||
|
||||
# ── Stage 3: publiceren ─────────────────────────────────────────────────
|
||||
# Alleen vanaf main/master of een tag; feature-branches en PRs stoppen na Build.
|
||||
- stage: Publish
|
||||
displayName: Publiceren naar registry
|
||||
dependsOn: Build
|
||||
condition: |
|
||||
and(succeeded(),
|
||||
or(eq(variables['Build.SourceBranch'], 'refs/heads/main'),
|
||||
eq(variables['Build.SourceBranch'], 'refs/heads/master'),
|
||||
startsWith(variables['Build.SourceBranch'], 'refs/tags/')))
|
||||
jobs:
|
||||
- job: PushImage
|
||||
displayName: Multi-arch image pushen
|
||||
steps:
|
||||
- checkout: self
|
||||
fetchDepth: 1
|
||||
|
||||
- script: |
|
||||
set -euo pipefail
|
||||
APP_VERSION=$(node -p "require('./package.json').version")
|
||||
echo "##vso[task.setvariable variable=appVersion]$APP_VERSION"
|
||||
displayName: Versienummer bepalen
|
||||
|
||||
- task: Docker@2
|
||||
displayName: Inloggen op de registry
|
||||
inputs:
|
||||
command: login
|
||||
containerRegistry: $(dockerRegistryServiceConnection)
|
||||
|
||||
- script: |
|
||||
set -euo pipefail
|
||||
docker run --privileged --rm tonistiigi/binfmt --install arm64
|
||||
docker buildx create --name vibefinance --use
|
||||
docker buildx inspect --bootstrap
|
||||
displayName: buildx voor arm64 (Raspberry Pi) klaarzetten
|
||||
|
||||
- script: |
|
||||
set -euo pipefail
|
||||
docker buildx build \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--target production \
|
||||
--build-arg APP_VERSION=$(appVersion) \
|
||||
--build-arg VITE_APP_VERSION=$(appVersion) \
|
||||
--build-arg VITE_API_URL=/api \
|
||||
-t $(imageRepo):$(appVersion) \
|
||||
-t $(imageRepo):latest \
|
||||
--push .
|
||||
displayName: Bouwen en pushen (amd64 + arm64)
|
||||
|
||||
- script: |
|
||||
echo "Gepubliceerd:"
|
||||
echo " $(imageRepo):$(appVersion)"
|
||||
echo " $(imageRepo):latest"
|
||||
displayName: Samenvatting
|
||||
Reference in New Issue
Block a user