gitops_multiarch_builds.groovy (16257B)
1 // Seed job: GitOps multi-arch build pipeline 2 // Auto-creates the image build pipeline job from this definition 3 4 pipelineJob('gitops-multiarch-builds') { 5 description('Multi-arch container image builds for GitOps infrastructure') 6 7 triggers { 8 githubPush() 9 cron('H 3 * * *') 10 } 11 12 parameters { 13 choiceParam('BUILD_IMAGE', ['all', 'matrix-backup', 'continuwuity', 'blog', 'bookwyrm'], 'Which image(s) to build') 14 } 15 16 definition { 17 cps { 18 script(''' 19 pipeline { 20 agent { 21 kubernetes { 22 defaultContainer 'docker' 23 yaml """ 24 apiVersion: v1 25 kind: Pod 26 metadata: 27 labels: 28 jenkins: agent 29 job: gitops-multiarch-builds 30 spec: 31 serviceAccountName: jenkins-operator-jenkins 32 containers: 33 - name: jnlp 34 image: jenkins/inbound-agent:3248.v65ecb_254c298-6 35 env: 36 - name: JENKINS_TUNNEL 37 value: jenkins-operator-slave-jenkins.jenkins.svc.cluster.local:50000 38 - name: JENKINS_URL 39 value: http://jenkins-operator-http-jenkins.jenkins.svc.cluster.local:8080/ 40 resources: 41 requests: 42 cpu: 100m 43 memory: 256Mi 44 limits: 45 memory: 512Mi 46 volumeMounts: 47 - name: workspace 48 mountPath: /home/jenkins/agent 49 - name: docker 50 image: docker:dind 51 securityContext: 52 privileged: true 53 env: 54 - name: DOCKER_TLS_CERTDIR 55 value: "" 56 resources: 57 requests: 58 cpu: 2000m 59 memory: 6Gi 60 limits: 61 cpu: 4000m 62 memory: 12Gi 63 volumeMounts: 64 - name: workspace 65 mountPath: /home/jenkins/agent 66 - name: registry-secret 67 mountPath: /var/run/secrets/docker.io/config.json 68 subPath: dockerconfig.json 69 readOnly: true 70 - name: cosign-secret 71 mountPath: /var/run/secrets/cosign/key 72 subPath: cosign.key 73 readOnly: true 74 - name: cosign-secret 75 mountPath: /var/run/secrets/cosign/password 76 subPath: cosign.password 77 readOnly: true 78 volumes: 79 - name: workspace 80 emptyDir: {} 81 - name: registry-secret 82 secret: 83 secretName: registry-credentials 84 defaultMode: 256 85 - name: cosign-secret 86 secret: 87 secretName: cosign-signing-key 88 defaultMode: 256 89 restartPolicy: Never 90 """ 91 } 92 } 93 94 options { 95 timestamps() 96 timeout(time: 4, unit: 'HOURS') 97 buildDiscarder(logRotator(numToKeepStr: '10', artifactNumToKeepStr: '5')) 98 } 99 100 triggers { 101 githubPush() 102 // pollSCM as fallback in case the GitHub webhook is not reachable 103 pollSCM('H/15 * * * *') 104 cron('H 3 * * *') 105 } 106 107 environment { 108 REGISTRY = 'registry.midnightthoughts.space' 109 // docker CLI reads credentials from $DOCKER_CONFIG/config.json 110 DOCKER_CONFIG = '/var/run/secrets/docker.io' 111 } 112 113 stages { 114 stage('Prepare Build Environment') { 115 steps { 116 sh """ 117 apk add --no-cache git jq wget 118 119 # Allow git to operate on the workspace regardless of owner uid 120 git config --global --add safe.directory '*' 121 122 # Download cosign (same version as GitHub Actions) 123 wget -qO /usr/local/bin/cosign \\ 124 https://github.com/sigstore/cosign/releases/download/v3.0.6/cosign-linux-amd64 125 chmod +x /usr/local/bin/cosign 126 127 # dockerd inside docker:dind starts asynchronously; wait for the socket 128 for i in \\$(seq 1 30); do 129 if [ -S /var/run/docker.sock ]; then 130 break 131 fi 132 echo "Waiting for docker daemon... (\\$i/30)" 133 sleep 2 134 done 135 if [ ! -S /var/run/docker.sock ]; then 136 echo "ERROR: docker daemon did not start within 60 seconds" 137 exit 1 138 fi 139 140 # Register QEMU binfmt handlers so the kernel can run arm64 binaries 141 # on this amd64 node. The F-flag keeps the interpreter fd open kernel-side 142 # so it persists even after the container exits. 143 docker run --rm --privileged --platform linux/amd64 \\ 144 tonistiigi/binfmt:qemu-v8.1.5 --install all 145 146 # Create docker-container buildx builder. 147 # moby/buildkit:buildx-stable-1 already ships buildkit-qemu-aarch64 so 148 # no custom image is needed — the per-arch digest bug in Dockerfiles was 149 # the root cause of the earlier "Invalid ELF image" failures, not QEMU. 150 docker buildx create --name multiarch-builder --driver docker-container \\ 151 --platform linux/amd64,linux/arm64 2>/dev/null || true 152 docker buildx use multiarch-builder 153 docker buildx inspect --bootstrap 154 """ 155 } 156 } 157 158 stage('Clone Repository') { 159 steps { 160 // checkout scm only works in Multibranch/Pipeline-from-SCM jobs; 161 // this is an inline CPS pipeline so we clone explicitly. 162 git url: 'https://github.com/MTRNord/cluster.git', branch: 'main' 163 } 164 } 165 166 stage('Build Images') { 167 parallel { 168 stage('matrix-backup') { 169 when { 170 expression { params.BUILD_IMAGE == 'matrix-backup' || params.BUILD_IMAGE == 'all' } 171 } 172 steps { 173 script { build_matrix_backup() } 174 } 175 } 176 stage('continuwuity') { 177 when { 178 expression { params.BUILD_IMAGE == 'continuwuity' || params.BUILD_IMAGE == 'all' } 179 } 180 steps { 181 script { build_continuwuity() } 182 } 183 } 184 stage('blog') { 185 when { 186 expression { params.BUILD_IMAGE == 'blog' || params.BUILD_IMAGE == 'all' } 187 } 188 steps { 189 script { build_blog() } 190 } 191 } 192 stage('bookwyrm') { 193 when { 194 expression { params.BUILD_IMAGE == 'bookwyrm' || params.BUILD_IMAGE == 'all' } 195 } 196 steps { 197 script { build_bookwyrm() } 198 } 199 } 200 } 201 } 202 } 203 204 post { 205 success { echo "Build pipeline completed successfully!" } 206 failure { echo "Build pipeline failed!" } 207 } 208 } 209 210 // --------------------------------------------------------------------------- 211 // matrix-backup — Dockerfile: apps/talos_cluster/matrix-backup/backup-tool/ 212 // --------------------------------------------------------------------------- 213 def build_matrix_backup() { 214 sh \'\'\' 215 set -eu 216 echo "=== Building matrix-backup ===" 217 218 TAG_TS="$(date -u +%Y%m%d-%H%M%S)" 219 TAG_SHA="sha-$(git rev-parse --short HEAD)" 220 IMAGE="registry.midnightthoughts.space/mtrnord/cluster/matrix-backup" 221 222 docker buildx build \\ 223 --platform linux/amd64,linux/arm64 \\ 224 --tag "${IMAGE}:${TAG_TS}" \\ 225 --tag "${IMAGE}:main" \\ 226 --tag "${IMAGE}:${TAG_SHA}" \\ 227 --metadata-file /tmp/matrix-backup-meta.json \\ 228 --push \\ 229 -f apps/talos_cluster/matrix-backup/backup-tool/Dockerfile \\ 230 apps/talos_cluster/matrix-backup/backup-tool/ 231 232 DIGEST="$(jq -r \'."containerimage.digest"\' /tmp/matrix-backup-meta.json)" 233 echo "Signing ${IMAGE}@${DIGEST}" 234 { set +x; } 2>/dev/null 235 COSIGN_PRIVATE_KEY="$(cat /var/run/secrets/cosign/key)" 236 COSIGN_PASSWORD="$(cat /var/run/secrets/cosign/password)" 237 COSIGN_EXPERIMENTAL=1 COSIGN_OCI_EXPERIMENTAL=1 \\ 238 COSIGN_PRIVATE_KEY="$COSIGN_PRIVATE_KEY" COSIGN_PASSWORD="$COSIGN_PASSWORD" \\ 239 cosign sign --yes --key env://COSIGN_PRIVATE_KEY \\ 240 --new-bundle-format=false \\ 241 --use-signing-config=false \\ 242 --registry-referrers-mode=oci-1-1 \\ 243 "${IMAGE}@${DIGEST}" 244 { set -x; } 2>/dev/null 245 \'\'\' 246 } 247 248 // --------------------------------------------------------------------------- 249 // continuwuity — Dockerfile: apps/talos_cluster/continuwuity/ 250 // --------------------------------------------------------------------------- 251 def build_continuwuity() { 252 sh \'\'\' 253 set -eu 254 echo "=== Building continuwuity ===" 255 256 TAG_TS="$(date -u +%Y%m%d-%H%M%S)" 257 TAG_SHA="sha-$(git rev-parse --short HEAD)" 258 IMAGE="registry.midnightthoughts.space/mtrnord/cluster/continuwuity" 259 260 docker buildx build \\ 261 --platform linux/amd64,linux/arm64 \\ 262 --tag "${IMAGE}:main" \\ 263 --tag "${IMAGE}:${TAG_TS}" \\ 264 --tag "${IMAGE}:${TAG_SHA}" \\ 265 --metadata-file /tmp/continuwuity-meta.json \\ 266 --push \\ 267 -f apps/talos_cluster/continuwuity/Dockerfile \\ 268 apps/talos_cluster/continuwuity/ 269 270 DIGEST="$(jq -r \'."containerimage.digest"\' /tmp/continuwuity-meta.json)" 271 echo "Signing ${IMAGE}@${DIGEST}" 272 { set +x; } 2>/dev/null 273 COSIGN_PRIVATE_KEY="$(cat /var/run/secrets/cosign/key)" 274 COSIGN_PASSWORD="$(cat /var/run/secrets/cosign/password)" 275 COSIGN_EXPERIMENTAL=1 COSIGN_OCI_EXPERIMENTAL=1 \\ 276 COSIGN_PRIVATE_KEY="$COSIGN_PRIVATE_KEY" COSIGN_PASSWORD="$COSIGN_PASSWORD" \\ 277 cosign sign --yes --key env://COSIGN_PRIVATE_KEY \\ 278 --new-bundle-format=false \\ 279 --use-signing-config=false \\ 280 --registry-referrers-mode=oci-1-1 \\ 281 "${IMAGE}@${DIGEST}" 282 { set -x; } 2>/dev/null 283 \'\'\' 284 } 285 286 // --------------------------------------------------------------------------- 287 // blog — Dockerfile: apps/talos_cluster/blog/docker/ 288 // --------------------------------------------------------------------------- 289 def build_blog() { 290 sh \'\'\' 291 set -eu 292 echo "=== Building blog ===" 293 294 TAG_TS="$(date -u +%Y%m%d-%H%M%S)" 295 TAG_SHA="sha-$(git rev-parse --short HEAD)" 296 IMAGE="registry.midnightthoughts.space/mtrnord/blog" 297 298 docker buildx build \\ 299 --platform linux/amd64,linux/arm64 \\ 300 --tag "${IMAGE}:latest" \\ 301 --tag "${IMAGE}:${TAG_TS}" \\ 302 --tag "${IMAGE}:${TAG_SHA}" \\ 303 --metadata-file /tmp/blog-meta.json \\ 304 --push \\ 305 -f apps/talos_cluster/blog/docker/Dockerfile \\ 306 apps/talos_cluster/blog/docker/ 307 308 DIGEST="$(jq -r \'."containerimage.digest"\' /tmp/blog-meta.json)" 309 echo "Signing ${IMAGE}@${DIGEST}" 310 { set +x; } 2>/dev/null 311 COSIGN_PRIVATE_KEY="$(cat /var/run/secrets/cosign/key)" 312 COSIGN_PASSWORD="$(cat /var/run/secrets/cosign/password)" 313 COSIGN_EXPERIMENTAL=1 COSIGN_OCI_EXPERIMENTAL=1 \\ 314 COSIGN_PRIVATE_KEY="$COSIGN_PRIVATE_KEY" COSIGN_PASSWORD="$COSIGN_PASSWORD" \\ 315 cosign sign --yes --key env://COSIGN_PRIVATE_KEY \\ 316 --new-bundle-format=false \\ 317 --use-signing-config=false \\ 318 --registry-referrers-mode=oci-1-1 \\ 319 "${IMAGE}@${DIGEST}" 320 { set -x; } 2>/dev/null 321 \'\'\' 322 } 323 324 // --------------------------------------------------------------------------- 325 // bookwyrm — clones upstream source, applies our patch, skips if tag exists 326 // Mirrors the logic in .github/workflows/build-bookwyrm.yaml 327 // --------------------------------------------------------------------------- 328 def build_bookwyrm() { 329 sh \'\'\' 330 set -eu 331 echo "=== Building bookwyrm ===" 332 333 # Determine latest upstream release 334 VERSION="$(wget -qO - \'https://api.github.com/repos/bookwyrm-social/bookwyrm/releases/latest\' \\ 335 | jq -r \'.tag_name\')" 336 if [ -z "$VERSION" ]; then 337 echo "ERROR: could not determine upstream bookwyrm version" 338 exit 1 339 fi 340 echo "Upstream version: ${VERSION}" 341 342 IMAGE="registry.midnightthoughts.space/mtrnord/bookwyrm" 343 344 # Skip if this tag already exists in the registry (same logic as GH Actions) 345 TAGS_JSON="$(wget -qO - \'https://registry.midnightthoughts.space/v2/mtrnord/bookwyrm/tags/list\' 2>/dev/null || echo \'{}\')" 346 if echo "$TAGS_JSON" | jq -e --arg v "$VERSION" \'(.tags // []) | contains([$v])\' > /dev/null 2>&1; then 347 echo "Tag ${VERSION} already exists in registry — skipping build" 348 exit 0 349 fi 350 echo "Tag ${VERSION} not found in registry — building" 351 352 # Clone bookwyrm source at the release tag 353 rm -rf /tmp/bookwyrm 354 git clone --depth=1 --branch "${VERSION}" \\ 355 https://github.com/bookwyrm-social/bookwyrm.git /tmp/bookwyrm 356 357 # Apply our Dockerfile patch (tolerates if it doesn\'t apply cleanly) 358 PATCH="$(pwd)/apps/talos_cluster/bookwyrm/dockerfile.patch" 359 cd /tmp/bookwyrm 360 if git apply --check "$PATCH" 2>/dev/null; then 361 git apply "$PATCH" 362 echo "Dockerfile patch applied" 363 else 364 echo "Patch does not apply cleanly — proceeding without it" 365 fi 366 367 docker buildx build \\ 368 --platform linux/amd64,linux/arm64 \\ 369 --tag "${IMAGE}:${VERSION}" \\ 370 --tag "${IMAGE}:latest" \\ 371 --metadata-file /tmp/bookwyrm-meta.json \\ 372 --push \\ 373 . 374 375 DIGEST="$(jq -r \'."containerimage.digest"\' /tmp/bookwyrm-meta.json)" 376 echo "Signing ${IMAGE}@${DIGEST}" 377 { set +x; } 2>/dev/null 378 COSIGN_PRIVATE_KEY="$(cat /var/run/secrets/cosign/key)" 379 COSIGN_PASSWORD="$(cat /var/run/secrets/cosign/password)" 380 COSIGN_EXPERIMENTAL=1 COSIGN_OCI_EXPERIMENTAL=1 \\ 381 COSIGN_PRIVATE_KEY="$COSIGN_PRIVATE_KEY" COSIGN_PASSWORD="$COSIGN_PASSWORD" \\ 382 cosign sign --yes --key env://COSIGN_PRIVATE_KEY \\ 383 --new-bundle-format=false \\ 384 --use-signing-config=false \\ 385 --registry-referrers-mode=oci-1-1 \\ 386 "${IMAGE}@${DIGEST}" 387 { set -x; } 2>/dev/null 388 \'\'\' 389 } 390 '''.stripIndent()) 391 sandbox(true) 392 } 393 } 394 }