Files
SRCmail/.gitlab-ci.yml
Stefan-Sanger 30d6c23908 fix(ci): use alpine/git:2.47.2 — 2.47.0 was never published
The bump-dev (and identically-configured bump-prod) job failed during
prepare_script with:

  ERROR: Job failed: prepare environment: waiting for pod running:
  pulling image "alpine/git:2.47.0": image pull failed: ... not found

Root cause: alpine/git:2.47.0 does not exist on Docker Hub. The alpine/git
2.47.x line starts at 2.47.1 — there is no 2.47.0 build. The runner's
image pull correctly fails with 'not found', and GitLab's Kubernetes
executor treats an image-pull failure during prepare_script as fatal, so
the job never reaches its script block.

Fix: pin both bump-dev and bump-prod to alpine/git:2.47.2 (latest 2.47.x).
Pinned rather than 'latest' so the job stays reproducible. bump-prod had
the same nonexistent tag and would have hit the identical failure on its
next run (whenever main advances), so both are fixed together.
2026-08-06 11:44:05 +02:00

166 lines
7.0 KiB
YAML

# 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-<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 <<EOF
# Owned by CI (bump-dev job in .gitlab-ci.yml) - regenerated every
# push to dev. Do not hand-edit; edits here get overwritten.
apiVersion: kustomize.config.k8s.io/v1alpha1
kind: Component
images:
- name: vncmail-plus
newName: $IMAGE
newTag: $TAG
EOF
- git config user.name "vncmail-ci"
- git config user.email "ci@vnc.biz"
- git add deploy/k8s/overlays/dev/image-tag/kustomization.yaml
- |
if git diff --cached --quiet; then
echo "No change (tag already pinned) - nothing to commit"
else
git commit -m "chore(deploy): pin dev to $TAG [skip ci]"
git push "https://gitlab-ci-token:${GITLAB_PUSH_TOKEN:-$CI_JOB_TOKEN}@${CI_SERVER_HOST}/${CI_PROJECT_PATH}.git" HEAD:dev
fi
# ---------------------------------------------------------------------------
# bump-prod — no cluster access, no rebuild. Points overlays/prod at the
# exact tag already running on dev. Does NOT deploy anything: vncmail-prod's
# ArgoCD Application has manual sync, so this only prepares what a human
# would be syncing, it doesn't sync it.
# ---------------------------------------------------------------------------
bump-prod:
stage: bump-prod
image: alpine/git:2.47.2
rules:
- if: '$CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH == "main"'
script:
- TAG="sha-$CI_COMMIT_SHORT_SHA"
- echo "main advanced to $CI_COMMIT_SHA (must be a dev commit, ff-only) - that image already exists as $IMAGE:$TAG"
- |
cat > deploy/k8s/overlays/prod/image-tag/kustomization.yaml <<EOF
# Owned by CI (bump-prod job in .gitlab-ci.yml) - regenerated every
# push to main. Do not hand-edit; edits here get overwritten. Bumping
# this is NOT the same as deploying it - vncmail-prod's ArgoCD
# Application has manual sync, see the note in the parent
# kustomization.yaml.
apiVersion: kustomize.config.k8s.io/v1alpha1
kind: Component
images:
- name: vncmail-plus
newName: $IMAGE
newTag: $TAG
EOF
- git config user.name "vncmail-ci"
- git config user.email "ci@vnc.biz"
- git add deploy/k8s/overlays/prod/image-tag/kustomization.yaml
- |
if git diff --cached --quiet; then
echo "No change (tag already pinned) - nothing to commit"
else
git commit -m "chore(deploy): point prod overlay at $TAG (not synced - manual gate in ArgoCD) [skip ci]"
git push "https://gitlab-ci-token:${GITLAB_PUSH_TOKEN:-$CI_JOB_TOKEN}@${CI_SERVER_HOST}/${CI_PROJECT_PATH}.git" HEAD:main
fi