pipeline { agent { kubernetes { defaultContainer 'tester' yaml """ apiVersion: v1 kind: Pod 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: workspace-volume mountPath: /home/jenkins/agent - 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 - name: tester image: python:3.12-slim command: - cat tty: true volumeMounts: - name: workspace-volume mountPath: /home/jenkins/agent volumes: - name: docker-config-writable emptyDir: {} - name: dind-storage emptyDir: {} - name: harbor-config secret: secretName: harbor-bstein-robot items: - key: .dockerconfigjson path: config.json - name: workspace-volume emptyDir: {} """ } } environment { PIP_DISABLE_PIP_VERSION_CHECK = '1' PYTHONUNBUFFERED = '1' } stages { stage('Checkout') { steps { checkout scm } } stage('Unit tests') { steps { container('tester') { sh ''' set -euo pipefail python -m pip install --no-cache-dir -r requirements.txt -r requirements-dev.txt python -m ruff check atlasbot --select C90,PLR mkdir -p build python -m slipcover --json --out build/coverage.json --source atlasbot --fail-under 90 -m pytest -q --junitxml build/junit.xml ''' } } } stage('Publish test metrics') { steps { container('tester') { sh ''' set -euo pipefail python scripts/publish_test_metrics.py ''' } } } 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 'git config --global --add safe.directory /home/jenkins/agent/workspace/atlasbot' def semver = sh(returnStdout: true, script: 'git describe --tags --exact-match || true').trim() if (!semver) { def offset = sh(returnStdout: true, script: 'cat VERSION_OFFSET 2>/dev/null || true').trim() if (!offset) { offset = "0" } def count = sh(returnStdout: true, script: 'git rev-list --count HEAD').trim() def versionNum = count.toInteger() + offset.toInteger() semver = "0.1.0-${versionNum}" } sh "echo SEMVER=${semver} > build.env" } } } } stage('Buildx setup') { steps { container('builder') { sh ''' set -euo pipefail seq 1 10 | while read _; do docker info && break || sleep 2 done docker buildx create --name bstein-builder --driver docker-container --bootstrap --use ''' } } } stage('Build & push image') { steps { container('builder') { sh ''' set -euo pipefail VERSION_TAG=$(cut -d= -f2 build.env) docker buildx build --platform linux/arm64 \ --tag registry.bstein.dev/bstein/atlasbot:${VERSION_TAG} \ --tag registry.bstein.dev/bstein/atlasbot:latest \ --push . ''' } } } } post { always { script { if (fileExists('build.env')) { def env = readProperties file: 'build.env' echo "Build complete for ${env.SEMVER}" } } archiveArtifacts artifacts: 'build/*', allowEmptyArchive: true } } }