pipeline { agent { kubernetes { label 'metis' defaultContainer 'builder' yaml """ apiVersion: v1 kind: Pod metadata: labels: app: metis spec: nodeSelector: kubernetes.io/arch: arm64 node-role.kubernetes.io/worker: "true" imagePullSecrets: - name: harbor-robot-pipeline 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: "" - name: DOCKER_CONFIG value: /root/.docker volumeMounts: - name: workspace-volume mountPath: /home/jenkins/agent - name: docker-config-writable mountPath: /root/.docker - name: harbor-config mountPath: /docker-config - name: tester image: golang:1.22-bookworm command: ["cat"] tty: true volumeMounts: - name: workspace-volume mountPath: /home/jenkins/agent - name: publisher image: python:3.12-slim command: ["cat"] tty: true volumeMounts: - name: workspace-volume mountPath: /home/jenkins/agent volumes: - name: workspace-volume emptyDir: {} - name: docker-config-writable emptyDir: {} - name: dind-storage persistentVolumeClaim: claimName: jenkins-dind-cache - name: harbor-config secret: secretName: harbor-robot-pipeline items: - key: .dockerconfigjson path: config.json """ } } environment { REGISTRY = 'registry.bstein.dev/bstein' IMAGE = "${REGISTRY}/metis" SENTINEL_IMAGE = "${REGISTRY}/metis-sentinel" VERSION_TAG = 'dev' SEMVER = 'dev' COVERAGE_JSON = 'build/coverage.json' JUNIT_XML = 'build/junit.xml' METRICS_PREFIX = 'ariadne_ci' VM_IMPORT_URL = 'http://victoria-metrics-single-server.monitoring.svc.cluster.local:8428/api/v1/import/prometheus' REPO_NAME = 'metis' } options { disableConcurrentBuilds() } triggers { pollSCM('H/5 * * * *') } stages { stage('Checkout') { steps { checkout scm } } stage('Unit tests') { steps { container('tester') { sh ''' bash -lc ' set -euo pipefail apt-get update >/dev/null apt-get install -y --no-install-recommends xz-utils >/dev/null mkdir -p build go install github.com/jstemmer/go-junit-report/v2@latest set +e go test -coverprofile=build/coverage.out ./... 2>&1 | tee build/test.out test_rc=${PIPESTATUS[0]} set -e /root/go/bin/go-junit-report < build/test.out > "${JUNIT_XML}" coverage="0" if [ -f build/coverage.out ]; then coverage="$(go tool cover -func=build/coverage.out | awk '/^total:/ {gsub("%","",$3); print $3}')" fi export GO_COVERAGE="${coverage}" python3 - <<'"'"'PY'"'"' import json, os coverage = float(os.environ.get("GO_COVERAGE", "0") or "0") with open("build/coverage.json", "w", encoding="utf-8") as handle: json.dump({"summary": {"percent_covered": coverage}}, handle) PY exit ${test_rc} ' ''' } } } stage('Publish test metrics') { steps { container('publisher') { sh ''' set -euo pipefail python scripts/publish_test_metrics.py ''' } } } stage('Prep toolchain') { steps { container('builder') { sh ''' set -euo pipefail mkdir -p /root/.docker cp /docker-config/config.json /root/.docker/config.json ''' } } } stage('Compute version') { steps { container('builder') { script { sh ''' set -euo pipefail SEMVER="0.1.0-${BUILD_NUMBER}" 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 use default || docker buildx create --name default --driver docker --use ''' } } } stage('Build & push image') { steps { container('builder') { sh ''' set -euo pipefail VERSION_TAG="$(cut -d= -f2 build.env)" docker buildx build \ --platform linux/amd64,linux/arm64 \ --tag "${IMAGE}:${VERSION_TAG}" \ --tag "${IMAGE}:latest" \ --target runtime \ --push \ . docker buildx build \ --platform linux/amd64,linux/arm64 \ --tag "${SENTINEL_IMAGE}:${VERSION_TAG}" \ --tag "${SENTINEL_IMAGE}:latest" \ --target sentinel \ --push \ . ''' } } } } post { always { script { if (fileExists('build/junit.xml')) { try { junit allowEmptyResults: true, testResults: 'build/junit.xml' } catch (Throwable err) { echo "junit step unavailable: ${err.class.simpleName}" } } } archiveArtifacts artifacts: 'build/junit.xml,build/coverage.json,build/coverage.out', allowEmptyArchive: true, fingerprint: true } } }