Gå til indholdet

Deployment og rollback

Produktionsmiljøet app.groopswork.dk drives lokalt på build-serveren og er autoritativt. Stakken køres manuelt:

docker compose -f docker-compose.yml -f compose.ci.yml -p groopworks-main up -d

Secrets (DB_PASSWORD, BETTER_AUTH_SECRET, ONLYOFFICE_JWT_SECRET m.fl.) læses fra en root-only env-fil i /srv/groopworks (ikke fra repoet). Et git push til main deployer derfor ikke automatisk til produktion — pipelinen er kun en test+build-gate, og deploy_production er manuelt (se nedenfor). Dermed kan en push aldrig overskrive den lokale stak.

Hver review-branch får sin egen isolerede stak på app-<branch-slug>.groopswork.dk. Coolify-opsætningen for eksterne miljøer er beskrevet i DEPLOY.md i repo-roden.

Advarsel — projektnavn ved lokal dev: stop_review kører docker compose -p groopworks-<branch-slug> down -v (fjerner volumener). En lokal dev-stak må derfor aldrig hedde groopworks-<branch-slug> — brug projektnavnet gwdev (docker compose -p gwdev ...), så en pipeline aldrig river dine data ned. Produktionen bruger navnet groopworks-main.

Pipeline

test → build → docs → deployingen test-jobs har allow_failure. Pipelinen er en gate: den tester og bygger images, men rører ikke den lokale produktions-stak. deploy_production er manuelt (when: manual, allow_failure: true) og bevaret til lejlighedsvis manuel brug via GitLab-UI'et — det kører aldrig automatisk på main.

Stage Job Indhold
test backend-test tsc --noEmit (blokerende), prisma migrate deploy, 124 tests (E2E-flows, edge cases, sikkerhed/multi-tenancy/DS-JWT)
test frontend-build ADR-002 token-gate, build-markør-assertion, lint, vite-build
build build-images Bygger og pusher images tagget <branch-slug>/{frontend,backend}:<commit-sha>
docs docs-build mkdocs build --strict — fejler på døde links og sider uden nav
deploy deploy_review (kun review-branches) docker compose pull && up -d med commit-taggede images
deploy deploy_production (kun main, manuel) Rører ikke den lokale stak automatisk; kun til manuel brug
deploy deploy_docs (kun main) Publicerer MkDocs-sitet på https://docs.groopswork.dk

Migrationer

  • Migrationer kører automatisk ved boot (entrypoint.shprisma migrate deploy) og er forward-only. Skriv aldrig destruktive migrationer uden en ledsagende plan i MR'en.
  • Verificeret mod produktionskopi 2026-06-12: dump af groopworks-main-db-1 gendannet i isoleret postgres, prisma migrate deploy → 8 migrationer fundet, "No pending migrations", migrate status → "Database schema is up to date".
  • Test selv en migration mod prod-kopi:
sudo docker exec groopworks-main-db-1 pg_dump -U postgres -d groopswork_db -F c -f /tmp/prodcopy.dump
sudo docker cp groopworks-main-db-1:/tmp/prodcopy.dump /tmp/
sudo docker run -d --rm --name prodcopy --network gw-test \
  -e POSTGRES_USER=postgres -e POSTGRES_PASSWORD=test -e POSTGRES_DB=groopswork_db postgres:14-alpine
sudo docker cp /tmp/prodcopy.dump prodcopy:/tmp/ \
  && sudo docker exec prodcopy pg_restore -U postgres -d groopswork_db --no-owner /tmp/prodcopy.dump
sudo docker run --rm --network gw-test -v "$PWD/backend":/app -w /app \
  -e DATABASE_URL=postgresql://postgres:test@prodcopy:5432/groopswork_db \
  oven/bun:1 sh -c "bunx prisma migrate deploy && bunx prisma migrate status"

Rollback-plan

Images er taggede med commit-SHA (<registry>/<branch-slug>/backend:<sha>), så en rollback er en redeploy af den forrige SHA — ingen rebuild.

1. Rollback af applikationen (minutter)

# Find den forrige grønne main-pipeline og dens SHA i GitLab (Pipelines → main)
PREV_SHA=<kort-sha>

cd /sti/til/groopworks-new   # repo-checkout på build-serveren
export CI_REGISTRY_IMAGE=<registry>/root/groopworks-new \
       CI_COMMIT_REF_SLUG=main CI_COMMIT_SHORT_SHA=$PREV_SHA \
       DEPLOY_HOST=app.groopswork.dk ROUTER_NAME=groopworks-prod \
       ROUTER_RULE='Host(`app.groopswork.dk`)'
docker compose -f docker-compose.yml -f compose.ci.yml -p groopworks-main \
  pull frontend backend
docker compose -f docker-compose.yml -f compose.ci.yml -p groopworks-main \
  up -d frontend backend

Alternativt via GitLab-UI'et: åbn den forrige grønne pipeline på main og kør deploy_production-jobbet igen (Retry) — det puller dén pipelines SHA-taggede images.

2. Rollback af databasen (kun hvis en migration skal fortrydes)

Migrationer er forward-only, så database-rollback = gendan fra dump:

# FØR enhver risikabel deploy: tag en dump
sudo docker exec groopworks-main-db-1 pg_dump -U postgres -d groopswork_db -F c -f /tmp/pre-deploy.dump

# Rollback: stop backend, gendan, start forrige image
docker compose -p groopworks-main stop backend
sudo docker exec groopworks-main-db-1 dropdb -U postgres groopswork_db
sudo docker exec groopworks-main-db-1 createdb -U postgres groopswork_db
sudo docker exec groopworks-main-db-1 pg_restore -U postgres -d groopswork_db --no-owner /tmp/pre-deploy.dump
# ...og deploy derefter den forrige app-SHA (trin 1)

Vigtigt: data oprettet mellem dump og rollback går tabt — tag altid en frisk dump umiddelbart før deploys med migrationer, og hold vinduet kort.

3. Dokumentfiler (uploads-volumen)

backend-servicens uploads ligger i en navngiven docker-volume og rulles IKKE tilbage sammen med databasen. Dokument-records uden fil falder tilbage til en blank skabelon (serveDocument), så uoverensstemmelser er ufarlige, men tag en kopi af volumen sammen med dumpen ved større operationer:

sudo docker run --rm --volumes-from groopworks-main-backend-1 -v /tmp:/backup \
  alpine tar czf /backup/uploads-$(date +%F).tgz /app/src/uploads

Alle upload-kategorier (afleveringer, opgave-vedhæftninger, gruppe-vedhæftninger og arbejdsrums-dokumenter) ligger under den samme volume-monterede rod /app/src/uploads, defineret ét sted i backend/src/config/uploads.ts. Skriv aldrig uploads til <cwd>/uploads (uden src/) — den sti ligger uden for volumen og tabes ved redeploy.

Sundhedstjek efter deploy/rollback

curl -s https://app.groopswork.dk/api/health        # {"status":"ok",...}
curl -s https://app.groopswork.dk/api/health/deep   # db + onlyoffice