# GitLab-CI dev→prod pipeline for VNCmail+ — GitOps via ArgoCD. # # Design: # - MR into `dev`: verify only (typecheck/lint/unit test/build check). No # push, no deploy — this is the multi-developer merge gate. # - Push to `dev`: build+push an immutable `sha-` tag with Docker + # docker-in-docker, then commit a one-line tag-bump into # overlays/dev/image-tag/kustomization.yaml (`[skip ci]`). ArgoCD's # `vncmail-dev` Application syncs it automatically. # - Push to `main`: NEVER rebuilds. `main` only advances via # `git merge --ff-only dev`, so main's HEAD commit already has a built # image. This job just bumps overlays/prod/image-tag/kustomization.yaml # to point at that same tag. The actual promotion gate is a HUMAN # clicking Sync on the `vncmail-prod` ArgoCD Application. # # Deliberately single-platform (linux/amd64) — this pipeline serves two # known amd64 microk8s clusters, not public multi-arch distribution (that's # what the GHCR release workflows are for, untouched by this file). # # Prerequisite this file assumes: # - A GitLab Runner with Docker-in-Docker service support (Kubernetes or # Docker executor). The `docker:28.4.0-dind` service requires privileged # mode on most Kubernetes executors. # - Either "allow this job token to push to this project" enabled # (Settings → CI/CD → Job token permissions), OR a project access token # with `write_repository` scope in $GITLAB_PUSH_TOKEN. The bump jobs # try CI_JOB_TOKEN first (see the script). # # deploy/k8s/ca/ (the EJBCA internal CA) is never referenced anywhere below, # and neither ArgoCD Application in deploy/argocd/ points at it — that stays # a fully manual, human-only runbook (see deploy/k8s/ca/README.md). stages: - verify - build - bump-dev - bump-prod variables: IMAGE: $CI_REGISTRY_IMAGE GIT_STRATEGY: clone DOCKER_DRIVER: overlay2 # DinD service is reached at the `docker` alias (set explicitly on the # service below), not localhost. TLS disabled so the daemon listens on # plaintext 2375 — same pattern as the working vnc-localidp pipeline. DOCKER_HOST: tcp://docker:2375 DOCKER_TLS_CERTDIR: "" # --------------------------------------------------------------------------- # verify — required check on every MR into dev. No registry, no cluster. # --------------------------------------------------------------------------- verify: stage: verify image: node:24-alpine rules: - if: '$CI_PIPELINE_SOURCE == "merge_request_event"' script: - npm ci - npm run typecheck - npm run lint - npm run test:translations - npm run build # test:integration is deliberately NOT here — it spins up a real Stalwart # fixture via docker-compose, which needs an actual Docker daemon this # runner's Kubernetes executor doesn't provide without privileged mode # (see the build job below). Candidate for a separate scheduled job on a # differently-configured runner, not a blocker on every MR. # --------------------------------------------------------------------------- # build — push to dev only. Builds once; main never rebuilds (see header). # --------------------------------------------------------------------------- build: stage: build image: docker:28.4.0 services: - name: docker:28.4.0-dind alias: docker rules: - if: '$CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH == "dev"' before_script: - until docker info; do sleep 1; done - docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" "$CI_REGISTRY" script: - > docker build --build-arg GIT_COMMIT=$CI_COMMIT_SHA -t "$IMAGE:sha-$CI_COMMIT_SHORT_SHA" -t "$IMAGE:dev-latest" . - docker push "$IMAGE:sha-$CI_COMMIT_SHORT_SHA" - docker push "$IMAGE:dev-latest" # --------------------------------------------------------------------------- # bump-dev — no cluster access. Commits the just-built tag into the overlay # ArgoCD watches; ArgoCD's automated sync does the actual apply. # --------------------------------------------------------------------------- bump-dev: stage: bump-dev # alpine/git:2.47.0 was never published on Docker Hub — the 2.47.x line # starts at 2.47.1. Using 2.47.2 (latest 2.47.x). image: alpine/git:2.47.2 rules: - if: '$CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH == "dev"' script: - TAG="sha-$CI_COMMIT_SHORT_SHA" - | cat > deploy/k8s/overlays/dev/image-tag/kustomization.yaml < deploy/k8s/overlays/prod/image-tag/kustomization.yaml <