pipeline { agent { kubernetes { label 'bstein-dev-home' defaultContainer 'builder' yaml """ apiVersion: v1 kind: Pod metadata: labels: app: bstein-dev-home spec: nodeSelector: kubernetes.io/arch: arm64 node-role.kubernetes.io/worker: "true" containers: - name: dind image: docker:27-dind securityContext: privileged: true env: - name: DOCKER_TLS_CERTDIR value: "" args: - --mtu=1400 - --host=unix:///var/run/docker.sock - --host=tcp://0.0.0.0:2375 volumeMounts: - name: dind-storage mountPath: /var/lib/docker - name: builder image: docker:27 command: ["cat"] tty: true env: - name: DOCKER_HOST value: tcp://localhost:2375 - name: DOCKER_TLS_CERTDIR value: "" volumeMounts: - name: workspace-volume mountPath: /home/jenkins/agent - name: docker-config-writable mountPath: /root/.docker - name: harbor-config mountPath: /docker-config volumes: - name: workspace-volume emptyDir: {} - name: docker-config-writable emptyDir: {} - name: dind-storage emptyDir: {} - name: harbor-config secret: secretName: harbor-bstein-robot items: - key: .dockerconfigjson path: config.json """ } } environment { REGISTRY = 'registry.bstein.dev/bstein' FRONT_IMAGE = "${REGISTRY}/bstein-dev-home-frontend" BACK_IMAGE = "${REGISTRY}/bstein-dev-home-backend" VERSION_TAG = 'dev' SEMVER = 'dev' } options { disableConcurrentBuilds() } triggers { // Poll every 2 minutes; notifyCommit can also trigger, but polling keeps it moving without webhook tokens. pollSCM('H/2 * * * *') } stages { stage('Checkout') { steps { checkout scm } } stage('Prep toolchain') { steps { container('builder') { sh ''' set -euo pipefail apk add --no-cache bash git jq mkdir -p /root/.docker cp /docker-config/config.json /root/.docker/config.json ''' } } } stage('Compute version') { steps { container('builder') { script { sh ''' set -euo pipefail if git describe --tags --exact-match >/dev/null 2>&1; then SEMVER="$(git describe --tags --exact-match)" else BASE="$(jq -r '.version' frontend/package.json || echo '0.1.0')" SEMVER="${BASE}-${BUILD_NUMBER}" fi # Accept bare semver or leading v (with optional prerelease). if ! echo "$SEMVER" | grep -Eq '^v?[0-9]+\\.[0-9]+\\.[0-9]+([-.][0-9A-Za-z]+)?$'; then SEMVER="0.1.0-${BUILD_NUMBER}" fi echo "SEMVER=${SEMVER}" > build.env ''' def props = readProperties file: 'build.env' env.SEMVER = props['SEMVER'] ?: "0.1.0-${env.BUILD_NUMBER}" env.VERSION_TAG = env.SEMVER } } } } stage('Buildx setup') { steps { container('builder') { sh ''' set -euo pipefail for i in $(seq 1 10); do if docker info >/dev/null 2>&1; then break fi sleep 2 done docker buildx create --name bstein-builder --driver docker-container --bootstrap --use || docker buildx use bstein-builder ''' } } } stage('Build & push frontend') { steps { container('builder') { sh ''' set -euo pipefail docker buildx build \ --platform linux/arm64 \ --tag "${FRONT_IMAGE}:${VERSION_TAG}" \ --tag "${FRONT_IMAGE}:latest" \ --file Dockerfile.frontend \ --push \ . ''' } } } stage('Build & push backend') { steps { container('builder') { sh ''' set -euo pipefail docker buildx build \ --platform linux/arm64 \ --tag "${BACK_IMAGE}:${VERSION_TAG}" \ --tag "${BACK_IMAGE}:latest" \ --file Dockerfile.backend \ --push \ . ''' } } } } post { always { echo "Build complete for ${env.VERSION_TAG}" } } }