Jenkins shared library and CDK constructs for AWS infrastructure. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
220 lines
6.7 KiB
Groovy
220 lines
6.7 KiB
Groovy
/**
|
|
* Docker utilities for Spicy CDK pipelines
|
|
* Pushes images to Nexus repository (Sonatype)
|
|
*/
|
|
|
|
// Default Nexus configuration
|
|
def getDefaultConfig() {
|
|
return [
|
|
registry: 'nexus.kodeniks.com',
|
|
repository: 'docker-hosted',
|
|
credentialsId: 'kodeniks-nexus-repository'
|
|
]
|
|
}
|
|
|
|
def getImageRef(Map args, String tag) {
|
|
def config = getDefaultConfig() + args
|
|
return "${config.registry}/${config.repository}/${args.imageName}:${tag}"
|
|
}
|
|
|
|
def getImageTag(Map args) {
|
|
return args.imageTag ?: gitUtils.getShortSHA()
|
|
}
|
|
|
|
def getImageRefSHA(Map args) {
|
|
return getImageRef(args, getImageTag(args))
|
|
}
|
|
|
|
def getImageRefLatest(Map args) {
|
|
return getImageRef(args, 'latest')
|
|
}
|
|
|
|
def copyFromImage(Map args) {
|
|
sh(
|
|
script: """#!/bin/bash +x
|
|
set -e
|
|
IMG_ID=\$(dd if=/dev/urandom bs=1k count=1 2> /dev/null | LC_CTYPE=C tr -cd "a-z0-9" | cut -c 1-22)
|
|
docker create --name \${IMG_ID} ${args.imageID}
|
|
docker cp \${IMG_ID}:${args.dockerPath} ${args.jenkinsPath}
|
|
docker rm \${IMG_ID}
|
|
"""
|
|
)
|
|
}
|
|
|
|
def getContainerName(Map args) {
|
|
return "${args.imageName}-${gitUtils.getShortSHA()}"
|
|
}
|
|
|
|
/**
|
|
* Build a Docker image
|
|
* @param args.imageName - Name of the image
|
|
* @param args.dockerfile - Path to Dockerfile (default: 'Dockerfile')
|
|
* @param args.context - Build context (default: '.')
|
|
* @param args.buildArgs - Map of build arguments (optional)
|
|
*/
|
|
def buildImage(Map args) {
|
|
def containerName = getContainerName(args)
|
|
def dockerfile = args.dockerfile ?: 'Dockerfile'
|
|
def context = args.context ?: '.'
|
|
|
|
def buildArgsString = ''
|
|
if (args.buildArgs) {
|
|
buildArgsString = args.buildArgs.collect { k, v -> "--build-arg ${k}=${v}" }.join(' ')
|
|
}
|
|
|
|
sh("docker build -t ${containerName} -f ${dockerfile} ${buildArgsString} ${context}")
|
|
return containerName
|
|
}
|
|
|
|
/**
|
|
* Tag image for Nexus registry
|
|
* @param args.imageName - Name of the image
|
|
* @param args.tagLatest - Whether to also tag as 'latest' (default: true on main branch)
|
|
*/
|
|
def tagImage(Map args) {
|
|
def containerName = getContainerName(args)
|
|
def tagLatest = args.tagLatest != null ? args.tagLatest : gitUtils.isMain()
|
|
|
|
def shaImageRef = getImageRefSHA(args)
|
|
sh("docker tag ${containerName} ${shaImageRef}")
|
|
if (tagLatest) {
|
|
// Tag latest from the SHA-tagged image to ensure they point to the same image
|
|
sh("docker tag ${shaImageRef} ${getImageRefLatest(args)}")
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Push image to Nexus registry
|
|
* Optionally compares with remote 'latest' to skip push if unchanged
|
|
* @param args.imageName - Name of the image
|
|
* @param args.credentialsId - Jenkins credentials ID for Nexus (default: 'kodeniks-nexus-repository')
|
|
* @param args.registry - Nexus registry URL (default: 'nexus.kodeniks.com')
|
|
* @param args.repository - Nexus repository name (default: 'docker-hosted')
|
|
* @param args.tagLatest - Whether to also push 'latest' tag (default: true on main branch)
|
|
* @param args.compareWithLatest - Skip push if image matches remote 'latest' (default: true)
|
|
*/
|
|
def pushImage(Map args) {
|
|
def config = getDefaultConfig() + args
|
|
def tagLatest = args.tagLatest != null ? args.tagLatest : gitUtils.isMain()
|
|
def compareWithLatest = args.compareWithLatest != null ? args.compareWithLatest : true
|
|
|
|
def localImageId = sh(
|
|
script: "docker inspect -f '{{.Id}}' ${getImageRefSHA(args)}",
|
|
returnStdout: true
|
|
).trim()
|
|
echo "Local image ID: ${localImageId}"
|
|
|
|
def remoteImageId = ""
|
|
if (compareWithLatest && tagLatest) {
|
|
withCredentials([usernamePassword(
|
|
credentialsId: config.credentialsId,
|
|
usernameVariable: 'NEXUS_USER',
|
|
passwordVariable: 'NEXUS_PASS'
|
|
)]) {
|
|
try {
|
|
sh "echo \$NEXUS_PASS | docker login ${config.registry} -u \$NEXUS_USER --password-stdin"
|
|
sh "docker pull ${getImageRefLatest(args)}"
|
|
remoteImageId = sh(
|
|
script: "docker inspect -f '{{.Id}}' ${getImageRefLatest(args)}",
|
|
returnStdout: true
|
|
).trim()
|
|
echo "Remote (latest) image ID: ${remoteImageId}"
|
|
} catch (Exception e) {
|
|
echo "Could not pull remote 'latest' image (might be the first build): ${e.getMessage()}"
|
|
}
|
|
}
|
|
}
|
|
|
|
if (remoteImageId && localImageId == remoteImageId) {
|
|
echo "No changes detected (new image is identical to remote 'latest'). Skipping push."
|
|
return false
|
|
}
|
|
|
|
// Re-tag latest from SHA image right before pushing
|
|
// This ensures latest points to the new image even if we pulled the old remote latest for comparison
|
|
if (tagLatest) {
|
|
sh("docker tag ${getImageRefSHA(args)} ${getImageRefLatest(args)}")
|
|
}
|
|
|
|
withCredentials([usernamePassword(
|
|
credentialsId: config.credentialsId,
|
|
usernameVariable: 'NEXUS_USER',
|
|
passwordVariable: 'NEXUS_PASS'
|
|
)]) {
|
|
sh "echo \$NEXUS_PASS | docker login ${config.registry} -u \$NEXUS_USER --password-stdin"
|
|
sh "docker push ${getImageRefSHA(args)}"
|
|
if (tagLatest) {
|
|
sh "docker push ${getImageRefLatest(args)}"
|
|
}
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
/**
|
|
* Build and push a Docker image to Nexus
|
|
* @param args.imageName - Name of the image (required)
|
|
* @param args.dockerfile - Path to Dockerfile (default: 'Dockerfile')
|
|
* @param args.context - Build context (default: '.')
|
|
* @param args.buildArgs - Map of build arguments (optional)
|
|
* @param args.credentialsId - Jenkins credentials ID for Nexus (default: 'kodeniks-nexus-repository')
|
|
* @param args.registry - Nexus registry URL (default: 'nexus.kodeniks.com')
|
|
* @param args.repository - Nexus repository name (default: 'docker-hosted')
|
|
* @param args.tagLatest - Whether to also tag/push 'latest' (default: true on main branch)
|
|
* @param args.compareWithLatest - Skip push if image matches remote 'latest' (default: true)
|
|
* @return The full image reference (registry/repo/name:tag)
|
|
*/
|
|
def buildAndPush(Map args) {
|
|
buildImage(args)
|
|
tagImage(args)
|
|
pushImage(args)
|
|
|
|
return getImageRefSHA(args)
|
|
}
|
|
|
|
/**
|
|
* Prune dangling Docker images
|
|
* @param args.pruneCache - Whether to also prune build cache (default: false)
|
|
*/
|
|
def prune(Map args = [:]) {
|
|
def pruneCache = args.pruneCache ?: false
|
|
echo "Cleaning up dangling Docker images..."
|
|
if (pruneCache) {
|
|
sh 'docker system prune -f'
|
|
} else {
|
|
// Only prune dangling images, not build cache
|
|
sh 'docker image prune -f'
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Login to Nexus registry
|
|
*/
|
|
def login(Map args = [:]) {
|
|
def config = getDefaultConfig() + args
|
|
|
|
withCredentials([usernamePassword(
|
|
credentialsId: config.credentialsId,
|
|
usernameVariable: 'NEXUS_USER',
|
|
passwordVariable: 'NEXUS_PASS'
|
|
)]) {
|
|
sh "echo \$NEXUS_PASS | docker login ${config.registry} -u \$NEXUS_USER --password-stdin"
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Pull an image from Nexus
|
|
*/
|
|
def pullImage(Map args) {
|
|
def config = getDefaultConfig() + args
|
|
def tag = args.tag ?: 'latest'
|
|
def imageRef = getImageRef(args, tag)
|
|
|
|
login(config)
|
|
sh "docker pull ${imageRef}"
|
|
|
|
return imageRef
|
|
}
|
|
|
|
return this
|