pipeline { agent { label '构建机1' } options { timestamps() disableConcurrentBuilds() skipDefaultCheckout(true) } environment { REGISTRY_URL = 'reg.nxsir.cn' APP_IMAGE_NAME = 'duplicati_preview/app' IMAGE_TAG = "${env.BUILD_ID}" DOCKER_CREDS = 'previewrobot_key' TARGET_PLATFORMS = 'linux/amd64,linux/arm64' BUILDER_NAME = 'duplicati-preview-buildx' NODE_IMAGE = 'docker.m.daocloud.io/library/node:24-alpine' GIT_REPO_URL = 'https://gitea.nxsir.cn/nanxun/duplicati_preview.git' GIT_BRANCH = 'main' GIT_CREDS = '' APP_IMAGE_TAGGED = "${REGISTRY_URL}/${APP_IMAGE_NAME}:${IMAGE_TAG}" APP_IMAGE_LATEST = "${REGISTRY_URL}/${APP_IMAGE_NAME}:latest" } stages { stage('Checkout') { steps { script { def userRemoteConfig = [url: env.GIT_REPO_URL] if (env.GIT_CREDS?.trim()) { userRemoteConfig.credentialsId = env.GIT_CREDS.trim() } checkout([ $class: 'GitSCM', branches: [[name: "*/${env.GIT_BRANCH}"]], userRemoteConfigs: [userRemoteConfig] ]) } } } stage('Prepare Buildx') { steps { sh ''' set -e sudo docker buildx version sudo docker run --privileged --rm tonistiigi/binfmt --install arm64 ''' } } stage('Login Registry') { steps { withCredentials([ usernamePassword( credentialsId: "${DOCKER_CREDS}", usernameVariable: 'DOCKER_USERNAME', passwordVariable: 'DOCKER_PASSWORD' ) ]) { sh ''' set -e echo "$DOCKER_PASSWORD" | sudo docker login ${REGISTRY_URL} -u "$DOCKER_USERNAME" --password-stdin ''' } } } stage('Build And Push Image') { steps { sh ''' set -e run_with_heartbeat() { log_file="$1" shift rm -f "$log_file" : > "$log_file" "$@" >"$log_file" 2>&1 & cmd_pid=$! last_line=0 while kill -0 "$cmd_pid" >/dev/null 2>&1; do if [ -f "$log_file" ]; then total_lines=$(wc -l < "$log_file" || echo 0) else total_lines=0 fi if [ "$total_lines" -gt "$last_line" ]; then sed -n "$((last_line + 1)),${total_lines}p" "$log_file" last_line=$total_lines else echo "[heartbeat] Duplicati Preview multi-arch build still running at $(date -u +%Y-%m-%dT%H:%M:%SZ)" fi sleep 20 done set +e wait "$cmd_pid" cmd_status=$? set -e if [ -f "$log_file" ]; then total_lines=$(wc -l < "$log_file" || echo 0) if [ "$total_lines" -gt "$last_line" ]; then sed -n "$((last_line + 1)),${total_lines}p" "$log_file" fi if [ "$cmd_status" -ne 0 ] && [ -s "$log_file" ]; then echo "[captured-log] Full buildx log follows because the command failed:" cat "$log_file" fi fi return "$cmd_status" } if ! sudo docker buildx inspect --builder ${BUILDER_NAME} >/dev/null 2>&1; then sudo docker buildx create \ --name ${BUILDER_NAME} \ --driver docker-container \ --driver-opt network=host \ --driver-opt 'env.HTTP_PROXY=http://192.168.5.200:7890' \ --driver-opt 'env.HTTPS_PROXY=http://192.168.5.200:7890' \ --driver-opt 'env.NO_PROXY=reg.nxsir.cn' \ --driver-opt 'env.http_proxy=http://192.168.5.200:7890' \ --driver-opt 'env.https_proxy=http://192.168.5.200:7890' \ --driver-opt 'env.no_proxy=reg.nxsir.cn' \ --use fi sudo docker buildx inspect --builder ${BUILDER_NAME} --bootstrap >/dev/null echo "Building and pushing multi-arch image: ${APP_IMAGE_TAGGED}" run_with_heartbeat /tmp/duplicati-preview-buildx-${IMAGE_TAG}.log \ sudo docker buildx build \ --builder ${BUILDER_NAME} \ --platform ${TARGET_PLATFORMS} \ --network host \ --progress=plain \ --provenance=false \ --build-arg NODE_IMAGE=${NODE_IMAGE} \ --build-arg HTTP_PROXY=http://192.168.5.200:7890 \ --build-arg HTTPS_PROXY=http://192.168.5.200:7890 \ --build-arg NO_PROXY=127.0.0.1,localhost,reg.nxsir.cn,gitea.nxsir.cn,reg.nxsir.cn \ --build-arg http_proxy=http://192.168.5.200:7890 \ --build-arg https_proxy=http://192.168.5.200:7890 \ --build-arg no_proxy=127.0.0.1,localhost,reg.nxsir.cn,gitea.nxsir.cn,reg.nxsir.cn \ -f Dockerfile \ -t ${APP_IMAGE_TAGGED} \ -t ${APP_IMAGE_LATEST} \ --push \ . ''' } } } post { success { echo 'Pipeline completed successfully.' } failure { echo 'Pipeline failed. Please check the build log.' } always { sh ''' set +e sudo docker logout ${REGISTRY_URL} >/dev/null 2>&1 || true true ''' deleteDir() } } }