Upgrades
- Frontend: React 19.2, Vite 8.2, @vitejs/plugin-react 6.1, ESLint 9.39
- Backend: Express 5.2, Helmet 8.3, express-rate-limit 8.7, pino 10.3,
pino-http 11, pg 8.23, dotenv 17.4, bcryptjs 3.0, ESLint 10.9
- Lockfiles opnieuw gegenereerd (backend had er geen)
Breaking changes opgevangen
- Express 5 laat kale wildcards in routes niet toe: app.use("/api/*") is een
prefix-mount geworden en app.get("*") pad-loze middleware
- Vite 8 bundelt met Rolldown, dat de object-vorm van manualChunks niet kent;
omgezet naar build.rollupOptions.output.advancedChunks
- Rate-limit headers verschillen per RFC-draft; de test kijkt nu naar de hele
RateLimit-headerfamilie in plaats van naar RateLimit-Limit
- ESLint blijft in de frontend op 9: eslint-plugin-react ondersteunt nog geen
ESLint 10 (peer <= ^9.7)
Update-check bij het testen
- docker/check-updates.mjs vergelijkt beide package.json's met het
npm-register; werkt zonder node_modules en zonder npm install
- test.ps1 draait dit als stap 1 van 4, met -Strict (falen) en -SkipDeps
(offline). Ook informatief in de Test-stage van de pipeline
- Bewuste uitzonderingen met reden in docker/update-exceptions.json
- dev.ps1 up gebruikt --renew-anon-volumes, anders houdt de dev-container de
oude node_modules na een upgrade
Overig
- ESLint flat configs voor beide packages; npm run lint werkte niet omdat er
geen configuratie was. Beide draaien nu groen (alleen waarschuwingen over
bestaande ongebruikte variabelen)
- README.md, BEHEER.md en Changelog.md bijgewerkt
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
204 lines
8.4 KiB
YAML
204 lines
8.4 KiB
YAML
# ╔══════════════════════════════════════════════════════════════════════════╗
|
|
# ║ 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
|
|
|
|
# Informatief: meldt dependencies die niet op de laatste versie staan.
|
|
# Bewuste uitzonderingen staan in docker/update-exceptions.json.
|
|
- script: |
|
|
set -euo pipefail
|
|
node docker/check-updates.mjs
|
|
displayName: Dependency-check (informatief)
|
|
continueOnError: true
|
|
|
|
- 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
|