fix(ci): isolate Docker registry credentials

This commit is contained in:
2026-08-24 11:00:03 +08:00
parent a4a693837e
commit 9198ce02d9
2 changed files with 32 additions and 7 deletions
+12 -7
View File
@@ -139,16 +139,21 @@ pipeline {
String contextDir = ".docker-context/${env.RESOLVED_SERVICE}" String contextDir = ".docker-context/${env.RESOLVED_SERVICE}"
String jar = serviceJars[env.RESOLVED_SERVICE] String jar = serviceJars[env.RESOLVED_SERVICE]
String registryHost = env.RESOLVED_IMAGE_REPOSITORY.tokenize('/')[0] String registryHost = env.RESOLVED_IMAGE_REPOSITORY.tokenize('/')[0]
String dockerConfigDir = "${pwd(tmp: true)}/docker-auth"
sh "mkdir -p '${contextDir}' && cp -f '${jar}' '${contextDir}/app.jar'" sh "mkdir -p '${contextDir}' && cp -f '${jar}' '${contextDir}/app.jar'"
sh "docker build -f deploy/docker/Dockerfile --build-arg BASE_IMAGE=${params.DOCKER_BASE_IMAGE} --build-arg JAR_FILE=app.jar -t ${image} '${contextDir}'"
echo "推送共享镜像: ${image}" echo "推送共享镜像: ${image}"
withCredentials([usernamePassword(credentialsId: env.REGISTRY_CREDENTIALS, usernameVariable: 'REGISTRY_USER', passwordVariable: 'REGISTRY_PASSWORD')]) { withEnv(["DOCKER_CONFIG=${dockerConfigDir}"]) {
try { sh 'mkdir -p "$DOCKER_CONFIG"'
sh 'printf %s "$REGISTRY_PASSWORD" | docker login -u "$REGISTRY_USER" --password-stdin "' + registryHost + '"' withCredentials([usernamePassword(credentialsId: env.REGISTRY_CREDENTIALS, usernameVariable: 'REGISTRY_USER', passwordVariable: 'REGISTRY_PASSWORD')]) {
sh "docker push ${image}" try {
} finally { sh 'printf %s "$REGISTRY_PASSWORD" | docker login -u "$REGISTRY_USER" --password-stdin "' + registryHost + '"'
sh "docker logout ${registryHost} || true" sh "docker build -f deploy/docker/Dockerfile --build-arg BASE_IMAGE=${params.DOCKER_BASE_IMAGE} --build-arg JAR_FILE=app.jar -t ${image} '${contextDir}'"
sh "docker push ${image}"
} finally {
sh "docker logout ${registryHost} || true"
sh 'rm -rf "$DOCKER_CONFIG"'
}
} }
} }
if (params.DEPLOY) { if (params.DEPLOY) {
@@ -64,6 +64,26 @@ test('development and test deployment locks are acquired before target executors
assert.ok(lockIndex < nodeIndex, 'deployment lock must be acquired before target node allocation') assert.ok(lockIndex < nodeIndex, 'deployment lock must be acquired before target node allocation')
}) })
test('parallel image publications isolate registry credentials per Jenkins job', () => {
const imageStage = pipeline.slice(
pipeline.indexOf("stage('构建并发布单服务镜像')"),
pipeline.indexOf("stage('部署开发或测试环境')")
)
const loginIndex = imageStage.indexOf('docker login')
const buildIndex = imageStage.indexOf('docker build')
const pushIndex = imageStage.indexOf('docker push')
const logoutIndex = imageStage.indexOf('docker logout')
const cleanupIndex = imageStage.indexOf('rm -rf "$DOCKER_CONFIG"')
assert.match(imageStage, /String dockerConfigDir = "\$\{pwd\(tmp: true\)\}\/docker-auth"/)
assert.match(imageStage, /withEnv\(\["DOCKER_CONFIG=\$\{dockerConfigDir\}"\]\)/)
assert.ok(loginIndex >= 0 && loginIndex < buildIndex, 'registry login must happen before image build')
assert.ok(buildIndex < pushIndex, 'image build must happen before image push')
assert.ok(pushIndex < logoutIndex, 'registry logout must happen after image push')
assert.ok(logoutIndex < cleanupIndex, 'temporary Docker credentials must be removed last')
assert.doesNotMatch(imageStage, /\/home\/deploy\/\.docker\/config\.json/)
})
test('parallel remote deployments keep env and compose assets isolated by project', () => { test('parallel remote deployments keep env and compose assets isolated by project', () => {
assert.match(directDeployScript, /REMOTE_ENV_NAME="\.env\.\$PROJECT_NAME"/) assert.match(directDeployScript, /REMOTE_ENV_NAME="\.env\.\$PROJECT_NAME"/)
assert.match(directDeployScript, /REMOTE_COMPOSE_NAME="docker-compose\.\$PROJECT_NAME\.yml"/) assert.match(directDeployScript, /REMOTE_COMPOSE_NAME="docker-compose\.\$PROJECT_NAME\.yml"/)