From 0572bbdd384fc120ef824700394d01fd08e3e422 Mon Sep 17 00:00:00 2001 From: XuqmGroup Date: Thu, 11 Jun 2026 17:05:43 +0800 Subject: [PATCH] =?UTF-8?q?fix(ci):=20=E4=BF=AE=E5=A4=8D=20Jenkinsfile.ten?= =?UTF-8?q?ant-web/ops-web=20=E7=BC=93=E5=AD=98=E7=A9=BF=E9=80=8F=E5=92=8C?= =?UTF-8?q?=E9=94=99=E8=AF=AF=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 添加 --build-arg GIT_COMMIT 使 Docker 每次提交都重新构建源码层 - 添加 NO_CACHE 参数支持强制禁用缓存 - 修复 bat 错误处理(if %errorlevel% neq 0 exit /b 1) - Deploy 阶段添加 lock('prod-deploy') + retry(2) 防并发失败 - Dockerfile.ops: 将 COPY ops-platform 移到 yarn install 之后,添加 ARG GIT_COMMIT Co-Authored-By: Claude Sonnet 4.6 --- Dockerfile.ops | 5 ++++- Jenkinsfile.ops-web | 29 +++++++++++++++++++++-------- Jenkinsfile.tenant-web | 29 +++++++++++++++++++++-------- 3 files changed, 46 insertions(+), 17 deletions(-) diff --git a/Dockerfile.ops b/Dockerfile.ops index 698c911..a7b547b 100644 --- a/Dockerfile.ops +++ b/Dockerfile.ops @@ -4,13 +4,16 @@ WORKDIR /workspace COPY package.json ./package.json COPY yarn.lock ./yarn.lock -COPY ops-platform ./ops-platform ENV YARN_CACHE_FOLDER=/var/cache/yarn RUN --mount=type=cache,target=/var/cache/yarn,sharing=locked \ yarn install --frozen-lockfile +# GIT_COMMIT invalidates source-code layers on every commit, without re-running yarn install +ARG GIT_COMMIT=unknown +COPY ops-platform ./ops-platform + ARG OPS_APP_BASE=/ ARG OPS_API_BASE_URL=/api diff --git a/Jenkinsfile.ops-web b/Jenkinsfile.ops-web index 37178ba..80cf318 100644 --- a/Jenkinsfile.ops-web +++ b/Jenkinsfile.ops-web @@ -4,6 +4,7 @@ pipeline { parameters { string(name: 'IMAGE_TAG', defaultValue: 'latest', description: '镜像 Tag') booleanParam(name: 'DEPLOY', defaultValue: true, description: '构建后是否自动部署') + booleanParam(name: 'NO_CACHE', defaultValue: false, description: '禁用 Docker 构建缓存(缓存错误时使用)') } environment { @@ -43,12 +44,20 @@ pipeline { withCredentials([string(credentialsId: 'ACR_PASSWORD', variable: 'ACR_PASS')]) { script { def fullImage = "${env.ACR_REGISTRY}/${env.ACR_NAMESPACE}/${env.IMAGE_NAME}:${params.IMAGE_TAG}" + def cacheArgs = params.NO_CACHE + ? '--no-cache' + : "--build-arg BUILDKIT_INLINE_CACHE=1 --cache-from ${fullImage}" + def gitCommit = env.GIT_COMMIT ?: 'unknown' bat """ docker login ${env.ACR_REGISTRY} -u ${env.ACR_USERNAME} -p %ACR_PASS% + if %errorlevel% neq 0 exit /b 1 docker pull --platform=linux/amd64 ${fullImage} || echo Pull failed, will build fresh - docker build --platform=linux/amd64 -f ${env.DOCKERFILE} ${env.BUILD_ARGS} --build-arg BUILDKIT_INLINE_CACHE=1 --cache-from ${fullImage} -t ${fullImage} . + docker build --platform=linux/amd64 -f ${env.DOCKERFILE} ${env.BUILD_ARGS} --build-arg GIT_COMMIT=${gitCommit} ${cacheArgs} -t ${fullImage} . + if %errorlevel% neq 0 exit /b 1 docker push ${fullImage} - docker rmi ${fullImage} || exit 0 + if %errorlevel% neq 0 exit /b 1 + docker rmi ${fullImage} + exit /b 0 """ } } @@ -58,12 +67,16 @@ pipeline { stage('Deploy to Production') { when { expression { return params.DEPLOY } } steps { - withCredentials([sshUserPrivateKey(credentialsId: 'PROD_SSH_KEY', keyFileVariable: 'SSH_KEY')]) { - script { - def fullImage = "${env.ACR_REGISTRY}/${env.ACR_NAMESPACE}/${env.IMAGE_NAME}:${params.IMAGE_TAG}" - bat """ - ssh -i "%SSH_KEY%" -o StrictHostKeyChecking=no ${env.PROD_USER}@${env.PROD_HOST} "docker pull ${fullImage} && docker compose -f ${env.COMPOSE_FILE} up -d --no-deps --force-recreate ${env.DEPLOY_SERVICE} && docker image prune -f" - """ + lock('prod-deploy') { + withCredentials([sshUserPrivateKey(credentialsId: 'PROD_SSH_KEY', keyFileVariable: 'SSH_KEY')]) { + script { + def fullImage = "${env.ACR_REGISTRY}/${env.ACR_NAMESPACE}/${env.IMAGE_NAME}:${params.IMAGE_TAG}" + retry(2) { + bat """ + ssh -i "%SSH_KEY%" -o StrictHostKeyChecking=no ${env.PROD_USER}@${env.PROD_HOST} "docker image prune -f 2>/dev/null || true; docker pull ${fullImage} || exit 1; docker compose -f ${env.COMPOSE_FILE} up -d --no-deps --force-recreate ${env.DEPLOY_SERVICE} || exit 1; docker image prune -f" + """ + } + } } } } diff --git a/Jenkinsfile.tenant-web b/Jenkinsfile.tenant-web index 7ae830c..304dbcc 100644 --- a/Jenkinsfile.tenant-web +++ b/Jenkinsfile.tenant-web @@ -4,6 +4,7 @@ pipeline { parameters { string(name: 'IMAGE_TAG', defaultValue: 'latest', description: '镜像 Tag') booleanParam(name: 'DEPLOY', defaultValue: true, description: '构建后是否自动部署') + booleanParam(name: 'NO_CACHE', defaultValue: false, description: '禁用 Docker 构建缓存(缓存错误时使用)') } environment { @@ -43,12 +44,20 @@ pipeline { withCredentials([string(credentialsId: 'ACR_PASSWORD', variable: 'ACR_PASS')]) { script { def fullImage = "${env.ACR_REGISTRY}/${env.ACR_NAMESPACE}/${env.IMAGE_NAME}:${params.IMAGE_TAG}" + def cacheArgs = params.NO_CACHE + ? '--no-cache' + : "--build-arg BUILDKIT_INLINE_CACHE=1 --cache-from ${fullImage}" + def gitCommit = env.GIT_COMMIT ?: 'unknown' bat """ docker login ${env.ACR_REGISTRY} -u ${env.ACR_USERNAME} -p %ACR_PASS% + if %errorlevel% neq 0 exit /b 1 docker pull --platform=linux/amd64 ${fullImage} || echo Pull failed, will build fresh - docker build --platform=linux/amd64 -f ${env.DOCKERFILE} ${env.BUILD_ARGS} --build-arg BUILDKIT_INLINE_CACHE=1 --cache-from ${fullImage} -t ${fullImage} . + docker build --platform=linux/amd64 -f ${env.DOCKERFILE} ${env.BUILD_ARGS} --build-arg GIT_COMMIT=${gitCommit} ${cacheArgs} -t ${fullImage} . + if %errorlevel% neq 0 exit /b 1 docker push ${fullImage} - docker rmi ${fullImage} || exit 0 + if %errorlevel% neq 0 exit /b 1 + docker rmi ${fullImage} + exit /b 0 """ } } @@ -58,12 +67,16 @@ pipeline { stage('Deploy to Production') { when { expression { return params.DEPLOY } } steps { - withCredentials([sshUserPrivateKey(credentialsId: 'PROD_SSH_KEY', keyFileVariable: 'SSH_KEY')]) { - script { - def fullImage = "${env.ACR_REGISTRY}/${env.ACR_NAMESPACE}/${env.IMAGE_NAME}:${params.IMAGE_TAG}" - bat """ - ssh -i "%SSH_KEY%" -o StrictHostKeyChecking=no ${env.PROD_USER}@${env.PROD_HOST} "docker rm -f xuqm-${env.DEPLOY_SERVICE} 2>/dev/null || true; docker pull ${fullImage} && docker compose -f ${env.COMPOSE_FILE} up -d --no-deps --force-recreate ${env.DEPLOY_SERVICE} && docker image prune -f" - """ + lock('prod-deploy') { + withCredentials([sshUserPrivateKey(credentialsId: 'PROD_SSH_KEY', keyFileVariable: 'SSH_KEY')]) { + script { + def fullImage = "${env.ACR_REGISTRY}/${env.ACR_NAMESPACE}/${env.IMAGE_NAME}:${params.IMAGE_TAG}" + retry(2) { + bat """ + ssh -i "%SSH_KEY%" -o StrictHostKeyChecking=no ${env.PROD_USER}@${env.PROD_HOST} "docker image prune -f 2>/dev/null || true; docker pull ${fullImage} || exit 1; docker compose -f ${env.COMPOSE_FILE} up -d --no-deps --force-recreate ${env.DEPLOY_SERVICE} || exit 1; docker image prune -f" + """ + } + } } } }