From 6d93ded20e127e9d14d7d6b21ae87b9a4c6abbe1 Mon Sep 17 00:00:00 2001 From: smerle33 Date: Wed, 2 Aug 2023 14:07:26 +0200 Subject: [PATCH 01/30] multiplatforms builds --- vars/buildDockerAndPublishImage.groovy | 440 +++++++++++++------------ 1 file changed, 225 insertions(+), 215 deletions(-) diff --git a/vars/buildDockerAndPublishImage.groovy b/vars/buildDockerAndPublishImage.groovy index 26f6012d3..4d9fdc598 100644 --- a/vars/buildDockerAndPublishImage.groovy +++ b/vars/buildDockerAndPublishImage.groovy @@ -10,6 +10,7 @@ def call(String imageShortName, Map userConfig=[:]) { includeImageNameInTag: false, // Set to true for multiple semversioned images built in parallel, will include the image name in tag to avoid conflict dockerfile: 'Dockerfile', // Obvious default platform: 'linux/amd64', // Intel/AMD 64 Bits, following Docker platform identifiers + platforms: [:], // Docker platform identifiers nextVersionCommand: 'jx-release-version', // Commmand line used to retrieve the next version gitCredentials: 'github-app-infra', // Credential ID for tagging and creating release imageDir: '.', // Relative path to the context directory for the Docker build @@ -33,248 +34,257 @@ def call(String imageShortName, Map userConfig=[:]) { DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX") final String buildDate = dateFormat.format(now) - // Warn about potential Linux/Windows contradictions between platform & agentLabels, and set the Windows config suffix for CST files - String cstConfigSuffix = '' - if (finalConfig.agentLabels.contains('windows') || finalConfig.platform.contains('windows')) { - if (finalConfig.agentLabels.contains('windows') && !finalConfig.platform.contains('windows')) { - echo "WARNING: A 'windows' agent is requested, but the 'platform' is set to '${finalConfig.platform}'." - } - if (!finalConfig.agentLabels.contains('windows') && finalConfig.platform.contains('windows')) { - echo "WARNING: The 'platform' is set to '${finalConfig.platform}', but there isn't any 'windows' agent requested." - } - cstConfigSuffix = '-windows' + if (finalConfig.platforms.size() > 0) { + echo "INFO: Using platforms from the pipeline configuration" + } else { + echo "INFO: Using platform from the pipeline configuration" + finalConfig.platforms[finalConfig.platform]=finalConfig.platform } - String operatingSystem = finalConfig.platform.split('/')[0] - final InfraConfig infraConfig = new InfraConfig(env) - final String defaultRegistryNamespace = infraConfig.getDockerRegistryNamespace() - final String registryNamespace = finalConfig.registryNamespace ?: defaultRegistryNamespace - final String imageName = registryNamespace + '/' + imageShortName - echo "INFO: Resolved Container Image Name: ${imageName}" - - node(finalConfig.agentLabels) { - withEnv([ - "BUILD_DATE=${buildDate}", - "IMAGE_NAME=${imageName}", - "IMAGE_DIR=${finalConfig.imageDir}", - "IMAGE_DOCKERFILE=${finalConfig.dockerfile}", - "IMAGE_PLATFORM=${finalConfig.platform}", - ]) { - infra.withDockerPullCredentials{ - String nextVersion = '' - stage("Prepare ${imageName}") { - checkout scm - if (finalConfig.unstash != '') { - unstash finalConfig.unstash - } + platforms.each {platform -> + // Warn about potential Linux/Windows contradictions between platform & agentLabels, and set the Windows config suffix for CST files + String cstConfigSuffix = '' + if (finalConfig.agentLabels.contains('windows') || platform.contains('windows')) { + if (finalConfig.agentLabels.contains('windows') && !platform.contains('windows')) { + echo "WARNING: A 'windows' agent is requested, but the 'platform' is set to '${platform}'." + } + if (!finalConfig.agentLabels.contains('windows') && platform.contains('windows')) { + echo "WARNING: The 'platform' is set to '${platform}', but there isn't any 'windows' agent requested." + } + cstConfigSuffix = '-windows' + } + String operatingSystem = platform.split('/')[0] - // The makefile to use must come from the pipeline to avoid a nasty user trying to exfiltrate data from the build - // Even though we have mitigation through the multibranch job config allowing to build PRs only from the repository contributors - writeFile file: 'Makefile', text: makefileContent - } // stage + final InfraConfig infraConfig = new InfraConfig(env) + final String defaultRegistryNamespace = infraConfig.getDockerRegistryNamespace() + final String registryNamespace = finalConfig.registryNamespace ?: defaultRegistryNamespace + final String imageName = registryNamespace + '/' + imageShortName + echo "INFO: Resolved Container Image Name: ${imageName}" - // Automatic tagging on principal branch is not enabled by default, show potential next version in PR anyway - if (finalConfig.automaticSemanticVersioning) { - stage("Get Next Version of ${imageName}") { - String imageInTag = '-' + imageName.replace('-','').replace(':','').toLowerCase() - if (isUnix()) { - sh 'git fetch --all --tags' // Ensure that all the tags are retrieved (uncoupling from job configuration, wether tags are fetched or not) - if (!finalConfig.includeImageNameInTag) { - nextVersion = sh(script: finalConfig.nextVersionCommand, returnStdout: true).trim() - } else { - echo "Including the image name '${imageName}' in the next version" - // Retrieving the semver part from the last tag including the image name - String currentTagScript = 'git tag --list \"*' + imageInTag + '\" --sort=-v:refname | head -1' - String currentSemVerVersion = sh(script: currentTagScript, returnStdout: true).trim() - echo "Current semver version is '${currentSemVerVersion}'" - // Set a default value if there isn't any tag for the current image yet (https://groovy-lang.org/operators.html#_elvis_operator) - currentSemVerVersion = currentSemVerVersion ?: '0.0.0-' + imageInTag - String nextVersionScript = finalConfig.nextVersionCommand + ' -debug --previous-version=' + currentSemVerVersion - String nextVersionSemVerPart = sh(script: nextVersionScript, returnStdout: true).trim() - echo "Next semver version part is '${nextVersionSemVerPart}'" - nextVersion = nextVersionSemVerPart + imageInTag - } - } else { - powershell 'git fetch --all --tags' // Ensure that all the tags are retrieved (uncoupling from job configuration, wether tags are fetched or not) - if (!finalConfig.includeImageNameInTag) { - nextVersion = powershell(script: finalConfig.nextVersionCommand, returnStdout: true).trim() - } else { - echo "Including the image name '${imageName}' in the next version" - // Retrieving the semver part from the last tag including the image name - String currentTagScript = 'git tag --list \"*' + imageInTag + '\" --sort=-v:refname | head -1' - String currentSemVerVersion = powershell(script: currentTagScript, returnStdout: true).trim() - echo "Current semver version is '${currentSemVerVersion}'" - // Set a default value if there isn't any tag for the current image yet (https://groovy-lang.org/operators.html#_elvis_operator) - currentSemVerVersion = currentSemVerVersion ?: '0.0.0-' + imageInTag - String nextVersionScript = finalConfig.nextVersionCommand + ' -debug --previous-version=' + currentSemVerVersion - String nextVersionSemVerPart = powershell(script: nextVersionScript, returnStdout: true).trim() - echo "Next semver version part is '${nextVersionSemVerPart}'" - nextVersion = nextVersionSemVerPart + imageInTag - } + node(finalConfig.agentLabels) { + withEnv([ + "BUILD_DATE=${buildDate}", + "IMAGE_NAME=${imageName}", + "IMAGE_DIR=${finalConfig.imageDir}", + "IMAGE_DOCKERFILE=${finalConfig.dockerfile}", + "IMAGE_PLATFORM=${platform}", + ]) { + infra.withDockerPullCredentials{ + String nextVersion = '' + stage("Prepare ${imageName}") { + checkout scm + if (finalConfig.unstash != '') { + unstash finalConfig.unstash } - echo "Next Release Version = ${nextVersion}" + + // The makefile to use must come from the pipeline to avoid a nasty user trying to exfiltrate data from the build + // Even though we have mitigation through the multibranch job config allowing to build PRs only from the repository contributors + writeFile file: 'Makefile', text: makefileContent } // stage - } // if - stage("Lint ${imageName}") { - // Define the image name as prefix to support multi images per pipeline - String hadolintReportId = "${imageName.replaceAll(':','-').replaceAll('/','-')}-hadolint-${now.getTime()}" - String hadoLintReportFile = "${hadolintReportId}.json" - withEnv(["HADOLINT_REPORT=${env.WORKSPACE}/${hadoLintReportFile}"]) { - try { + // Automatic tagging on principal branch is not enabled by default, show potential next version in PR anyway + if (finalConfig.automaticSemanticVersioning) { + stage("Get Next Version of ${imageName}") { + String imageInTag = '-' + imageName.replace('-','').replace(':','').toLowerCase() if (isUnix()) { - sh 'make lint' + sh 'git fetch --all --tags' // Ensure that all the tags are retrieved (uncoupling from job configuration, wether tags are fetched or not) + if (!finalConfig.includeImageNameInTag) { + nextVersion = sh(script: finalConfig.nextVersionCommand, returnStdout: true).trim() + } else { + echo "Including the image name '${imageName}' in the next version" + // Retrieving the semver part from the last tag including the image name + String currentTagScript = 'git tag --list \"*' + imageInTag + '\" --sort=-v:refname | head -1' + String currentSemVerVersion = sh(script: currentTagScript, returnStdout: true).trim() + echo "Current semver version is '${currentSemVerVersion}'" + // Set a default value if there isn't any tag for the current image yet (https://groovy-lang.org/operators.html#_elvis_operator) + currentSemVerVersion = currentSemVerVersion ?: '0.0.0-' + imageInTag + String nextVersionScript = finalConfig.nextVersionCommand + ' -debug --previous-version=' + currentSemVerVersion + String nextVersionSemVerPart = sh(script: nextVersionScript, returnStdout: true).trim() + echo "Next semver version part is '${nextVersionSemVerPart}'" + nextVersion = nextVersionSemVerPart + imageInTag + } } else { - powershell 'make lint' + powershell 'git fetch --all --tags' // Ensure that all the tags are retrieved (uncoupling from job configuration, wether tags are fetched or not) + if (!finalConfig.includeImageNameInTag) { + nextVersion = powershell(script: finalConfig.nextVersionCommand, returnStdout: true).trim() + } else { + echo "Including the image name '${imageName}' in the next version" + // Retrieving the semver part from the last tag including the image name + String currentTagScript = 'git tag --list \"*' + imageInTag + '\" --sort=-v:refname | head -1' + String currentSemVerVersion = powershell(script: currentTagScript, returnStdout: true).trim() + echo "Current semver version is '${currentSemVerVersion}'" + // Set a default value if there isn't any tag for the current image yet (https://groovy-lang.org/operators.html#_elvis_operator) + currentSemVerVersion = currentSemVerVersion ?: '0.0.0-' + imageInTag + String nextVersionScript = finalConfig.nextVersionCommand + ' -debug --previous-version=' + currentSemVerVersion + String nextVersionSemVerPart = powershell(script: nextVersionScript, returnStdout: true).trim() + echo "Next semver version part is '${nextVersionSemVerPart}'" + nextVersion = nextVersionSemVerPart + imageInTag + } } - } finally { - recordIssues( - enabledForFailure: true, - aggregatingResults: false, - tool: hadoLint(id: hadolintReportId, pattern: hadoLintReportFile) - ) - } - } - } // stage + echo "Next Release Version = ${nextVersion}" + } // stage + } // if - stage("Build ${imageName}") { - if (isUnix()) { - sh 'make build' - } else { - powershell 'make build' - } - } //stage - - // There can be 2 kind of tests: per image and per repository - // Assuming Windows versions of cst configuration files finishing by "-windows" (e.g. "common-cst-windows.yml") - [ - 'Image Test Harness': "${finalConfig.imageDir}/cst${cstConfigSuffix}.yml", - 'Common Test Harness': "${env.WORKSPACE}/common-cst${cstConfigSuffix}.yml" - ].each { testName, testHarness -> - if (fileExists(testHarness)) { - stage("Test ${testName} for ${imageName}") { - withEnv(["TEST_HARNESS=${testHarness}"]) { + stage("Lint ${imageName}") { + // Define the image name as prefix to support multi images per pipeline + String hadolintReportId = "${imageName.replaceAll(':','-').replaceAll('/','-')}-hadolint-${now.getTime()}" + String hadoLintReportFile = "${hadolintReportId}.json" + withEnv(["HADOLINT_REPORT=${env.WORKSPACE}/${hadoLintReportFile}"]) { + try { if (isUnix()) { - sh 'make test' + sh 'make lint' } else { - powershell 'make test' + powershell 'make lint' } - } // withEnv - } //stage - } else { - echo "Skipping test ${testName} for ${imageName} as ${testHarness} does not exist" - } // if else - } // each + } finally { + recordIssues( + enabledForFailure: true, + aggregatingResults: false, + tool: hadoLint(id: hadolintReportId, pattern: hadoLintReportFile) + ) + } + } + } // stage - // Automatic tagging on principal branch is not enabled by default - if (semVerEnabledOnPrimaryBranch) { - stage("Semantic Release of ${imageName}") { - echo "Configuring credential.helper" - // The credential.helper will execute everything after the '!', here echoing the username, the password and an empty line to be passed to git as credentials when git needs it. + stage("Build ${imageName}") { if (isUnix()) { - sh 'git config --local credential.helper "!set -u; echo username=\\$GIT_USERNAME && echo password=\\$GIT_PASSWORD && echo"' + sh 'make build' } else { - // Using 'bat' here instead of 'powershell' to avoid variable interpolation problem with $ - bat 'git config --local credential.helper "!sh.exe -c \'set -u; echo username=$GIT_USERNAME && echo password=$GIT_PASSWORD && echo"\'' + powershell 'make build' } + } //stage - withCredentials([ - usernamePassword(credentialsId: "${finalConfig.gitCredentials}", passwordVariable: 'GIT_PASSWORD', usernameVariable: 'GIT_USERNAME') - ]) { - withEnv(["NEXT_VERSION=${nextVersion}"]) { - echo "Tagging and pushing the new version: ${nextVersion}" - if (isUnix()) { - sh ''' - git config user.name "${GIT_USERNAME}" - git config user.email "jenkins-infra@googlegroups.com" + // There can be 2 kind of tests: per image and per repository + // Assuming Windows versions of cst configuration files finishing by "-windows" (e.g. "common-cst-windows.yml") + [ + 'Image Test Harness': "${finalConfig.imageDir}/cst${cstConfigSuffix}.yml", + 'Common Test Harness': "${env.WORKSPACE}/common-cst${cstConfigSuffix}.yml" + ].each { testName, testHarness -> + if (fileExists(testHarness)) { + stage("Test ${testName} for ${imageName}") { + withEnv(["TEST_HARNESS=${testHarness}"]) { + if (isUnix()) { + sh 'make test' + } else { + powershell 'make test' + } + } // withEnv + } //stage + } else { + echo "Skipping test ${testName} for ${imageName} as ${testHarness} does not exist" + } // if else + } // each - git tag -a "${NEXT_VERSION}" -m "${IMAGE_NAME}" - git push origin --tags - ''' + // Automatic tagging on principal branch is not enabled by default + if (semVerEnabledOnPrimaryBranch) { + stage("Semantic Release of ${imageName}") { + echo "Configuring credential.helper" + // The credential.helper will execute everything after the '!', here echoing the username, the password and an empty line to be passed to git as credentials when git needs it. + if (isUnix()) { + sh 'git config --local credential.helper "!set -u; echo username=\\$GIT_USERNAME && echo password=\\$GIT_PASSWORD && echo"' + } else { + // Using 'bat' here instead of 'powershell' to avoid variable interpolation problem with $ + bat 'git config --local credential.helper "!sh.exe -c \'set -u; echo username=$GIT_USERNAME && echo password=$GIT_PASSWORD && echo"\'' + } + + withCredentials([ + usernamePassword(credentialsId: "${finalConfig.gitCredentials}", passwordVariable: 'GIT_PASSWORD', usernameVariable: 'GIT_USERNAME') + ]) { + withEnv(["NEXT_VERSION=${nextVersion}"]) { + echo "Tagging and pushing the new version: ${nextVersion}" + if (isUnix()) { + sh ''' + git config user.name "${GIT_USERNAME}" + git config user.email "jenkins-infra@googlegroups.com" + + git tag -a "${NEXT_VERSION}" -m "${IMAGE_NAME}" + git push origin --tags + ''' + } else { + powershell ''' + git config user.email "jenkins-infra@googlegroups.com" + git config user.password $env:GIT_PASSWORD + + git tag -a "$env:NEXT_VERSION" -m "$env:IMAGE_NAME" + git push origin --tags + ''' + } + } // withEnv + } // withCredentials + } // stage + } // if + }// withDockerPullCredentials + infra.withDockerPushCredentials{ + if (env.TAG_NAME || env.BRANCH_IS_PRIMARY) { + stage("Deploy ${imageName}") { + String imageDeployName = imageName + if (env.TAG_NAME) { + // User could specify a tag in the image name. In that case the git tag is appended. Otherwise the docker tag is set to the git tag. + if (imageDeployName.contains(':')) { + imageDeployName += "-${env.TAG_NAME}" } else { - powershell ''' - git config user.email "jenkins-infra@googlegroups.com" - git config user.password $env:GIT_PASSWORD + imageDeployName += ":${env.TAG_NAME}" + } + } - git tag -a "$env:NEXT_VERSION" -m "$env:IMAGE_NAME" - git push origin --tags - ''' + withEnv(["IMAGE_DEPLOY_NAME=${imageDeployName}"]) { + // Please note that "make deploy" uses the environment variable "IMAGE_DEPLOY_NAME" + if (isUnix()) { + sh 'make deploy' + } else { + powershell 'make deploy' } } // withEnv - } // withCredentials - } // stage - } // if - }// withDockerPullCredentials - infra.withDockerPushCredentials{ - if (env.TAG_NAME || env.BRANCH_IS_PRIMARY) { - stage("Deploy ${imageName}") { - String imageDeployName = imageName - if (env.TAG_NAME) { - // User could specify a tag in the image name. In that case the git tag is appended. Otherwise the docker tag is set to the git tag. - if (imageDeployName.contains(':')) { - imageDeployName += "-${env.TAG_NAME}" - } else { - imageDeployName += ":${env.TAG_NAME}" - } - } + } //stage + } // if + } // withDockerPushCredentials - withEnv(["IMAGE_DEPLOY_NAME=${imageDeployName}"]) { - // Please note that "make deploy" uses the environment variable "IMAGE_DEPLOY_NAME" + + if (env.TAG_NAME && finalConfig.automaticSemanticVersioning) { + stage('GitHub Release') { + withCredentials([ + usernamePassword(credentialsId: "${finalConfig.gitCredentials}", passwordVariable: 'GITHUB_TOKEN', usernameVariable: 'GITHUB_USERNAME') + ]) { + String release = '' if (isUnix()) { - sh 'make deploy' + final String releaseScript = ''' + originUrlWithGit="$(git remote get-url origin)" + originUrl="${originUrlWithGit%.git}" + org="$(echo "${originUrl}" | cut -d'/' -f4)" + repository="$(echo "${originUrl}" | cut -d'/' -f5)" + releasesUrl="/repos/${org}/${repository}/releases" + releaseId="$(gh api "${releasesUrl}" | jq -e -r '[ .[] | select(.draft == true and .name == "next").id] | max | select(. != null)')" + if test "${releaseId}" -gt 0 + then + gh api -X PATCH -F draft=false -F name="${TAG_NAME}" -F tag_name="${TAG_NAME}" "${releasesUrl}/${releaseId}" > /dev/null + fi + echo "${releaseId}" + ''' + release = sh(script: releaseScript, returnStdout: true) } else { - powershell 'make deploy' + final String releaseScript = ''' + $originUrl = (git remote get-url origin) -replace '\\.git', '' + $org = $originUrl.split('/')[3] + $repository = $originUrl.split('/')[4] + $releasesUrl = "/repos/$org/$repository/releases" + $releaseId = (gh api $releasesUrl | jq -e -r '[ .[] | select(.draft == true and .name == \"next\").id] | max | select(. != null)') + $output = '' + if ($releaseId -gt 0) + { + Invoke-Expression -Command "gh api -X PATCH -F draft=false -F name=$env:TAG_NAME -F tag_name=$env:TAG_NAME $releasesUrl/$releaseId" > $null + $output = $releaseId + } + Write-Output $output + ''' + release = powershell(script: releaseScript, returnStdout: true) } - } // withEnv - } //stage + if (release == '') { + echo "No next release draft found." + } // if + } // withCredentials + } // stage } // if - } // withDockerPushCredentials - - - if (env.TAG_NAME && finalConfig.automaticSemanticVersioning) { - stage('GitHub Release') { - withCredentials([ - usernamePassword(credentialsId: "${finalConfig.gitCredentials}", passwordVariable: 'GITHUB_TOKEN', usernameVariable: 'GITHUB_USERNAME') - ]) { - String release = '' - if (isUnix()) { - final String releaseScript = ''' - originUrlWithGit="$(git remote get-url origin)" - originUrl="${originUrlWithGit%.git}" - org="$(echo "${originUrl}" | cut -d'/' -f4)" - repository="$(echo "${originUrl}" | cut -d'/' -f5)" - releasesUrl="/repos/${org}/${repository}/releases" - releaseId="$(gh api "${releasesUrl}" | jq -e -r '[ .[] | select(.draft == true and .name == "next").id] | max | select(. != null)')" - if test "${releaseId}" -gt 0 - then - gh api -X PATCH -F draft=false -F name="${TAG_NAME}" -F tag_name="${TAG_NAME}" "${releasesUrl}/${releaseId}" > /dev/null - fi - echo "${releaseId}" - ''' - release = sh(script: releaseScript, returnStdout: true) - } else { - final String releaseScript = ''' - $originUrl = (git remote get-url origin) -replace '\\.git', '' - $org = $originUrl.split('/')[3] - $repository = $originUrl.split('/')[4] - $releasesUrl = "/repos/$org/$repository/releases" - $releaseId = (gh api $releasesUrl | jq -e -r '[ .[] | select(.draft == true and .name == \"next\").id] | max | select(. != null)') - $output = '' - if ($releaseId -gt 0) - { - Invoke-Expression -Command "gh api -X PATCH -F draft=false -F name=$env:TAG_NAME -F tag_name=$env:TAG_NAME $releasesUrl/$releaseId" > $null - $output = $releaseId - } - Write-Output $output - ''' - release = powershell(script: releaseScript, returnStdout: true) - } - if (release == '') { - echo "No next release draft found." - } // if - } // withCredentials - } // stage - } // if - } // withEnv - } // node + } // withEnv + } // node + } // each platform } // call From ef9b15f5eb16cb444e583e980ad55a36cf20dddd Mon Sep 17 00:00:00 2001 From: smerle33 Date: Wed, 2 Aug 2023 15:05:06 +0200 Subject: [PATCH 02/30] getime bug --- ...BuildDockerAndPublishImageStepTests.groovy | 34 +++++++++++++++++++ vars/buildDockerAndPublishImage.groovy | 32 +++++++++-------- 2 files changed, 52 insertions(+), 14 deletions(-) diff --git a/test/groovy/BuildDockerAndPublishImageStepTests.groovy b/test/groovy/BuildDockerAndPublishImageStepTests.groovy index 0c8d8367f..5c7dbf466 100644 --- a/test/groovy/BuildDockerAndPublishImageStepTests.groovy +++ b/test/groovy/BuildDockerAndPublishImageStepTests.groovy @@ -562,4 +562,38 @@ class BuildDockerAndPublishImageStepTests extends BaseTest { // And all mocked/stubbed methods have to be called verifyMocks() } + + @Test + void itBuildsAndDeploysWithPlatformsSpecificOnPrincipalBranch() throws Exception { + def script = loadScript(scriptName) + mockPrincipalBranch() + withMocks{ + script.call(testImageName, [ + dockerfile: 'build.Dockerfile', + imageDir: 'docker/', + platforms: ['linux/amd64','linux/arm64'], + automaticSemanticVersioning: true, + gitCredentials: 'git-creds', + registryNamespace: 'jenkins', + ]) + } + printCallStack() + // Then we expect a successful build with the code cloned + assertJobStatusSuccess() + // With the common workflow run as expected + assertTrue(assertBaseWorkflow()) + assertTrue(assertMethodCallContainsPattern('node', 'docker')) + // And the expected environment variables set to their default values + assertTrue(assertMethodCallContainsPattern('withEnv', 'IMAGE_DIR=.')) + assertTrue(assertMethodCallContainsPattern('withEnv', 'IMAGE_DOCKERFILE=Dockerfile')) + assertTrue(assertMethodCallContainsPattern('withEnv', 'IMAGE_PLATFORM=linux/amd64')) + // And generated reports recorded + assertTrue(assertRecordIssues()) + // And the deploy step called + assertTrue(assertMakeDeploy()) + // But no release created automatically + assertFalse(assertTagPushed(defaultGitTag)) + // And all mocked/stubbed methods been called + verifyMocks() + } } diff --git a/vars/buildDockerAndPublishImage.groovy b/vars/buildDockerAndPublishImage.groovy index 4d9fdc598..ab25ce578 100644 --- a/vars/buildDockerAndPublishImage.groovy +++ b/vars/buildDockerAndPublishImage.groovy @@ -10,7 +10,7 @@ def call(String imageShortName, Map userConfig=[:]) { includeImageNameInTag: false, // Set to true for multiple semversioned images built in parallel, will include the image name in tag to avoid conflict dockerfile: 'Dockerfile', // Obvious default platform: 'linux/amd64', // Intel/AMD 64 Bits, following Docker platform identifiers - platforms: [:], // Docker platform identifiers + platforms: [], // Docker platform identifiers nextVersionCommand: 'jx-release-version', // Commmand line used to retrieve the next version gitCredentials: 'github-app-infra', // Credential ID for tagging and creating release imageDir: '.', // Relative path to the context directory for the Docker build @@ -38,27 +38,31 @@ def call(String imageShortName, Map userConfig=[:]) { echo "INFO: Using platforms from the pipeline configuration" } else { echo "INFO: Using platform from the pipeline configuration" - finalConfig.platforms[finalConfig.platform]=finalConfig.platform + finalConfig.platforms = [finalConfig.platform] } - platforms.each {platform -> + + final InfraConfig infraConfig = new InfraConfig(env) + final String defaultRegistryNamespace = infraConfig.getDockerRegistryNamespace() + final String registryNamespace = finalConfig.registryNamespace ?: defaultRegistryNamespace + final String imageName = registryNamespace + '/' + imageShortName + finalConfig.platforms.each {oneplatform -> + + echo "DEBUG platform in build '${oneplatform}'." + // Warn about potential Linux/Windows contradictions between platform & agentLabels, and set the Windows config suffix for CST files String cstConfigSuffix = '' - if (finalConfig.agentLabels.contains('windows') || platform.contains('windows')) { - if (finalConfig.agentLabels.contains('windows') && !platform.contains('windows')) { - echo "WARNING: A 'windows' agent is requested, but the 'platform' is set to '${platform}'." + if (finalConfig.agentLabels.contains('windows') || oneplatform.contains('windows')) { + if (finalConfig.agentLabels.contains('windows') && !oneplatform.contains('windows')) { + echo "WARNING: A 'windows' agent is requested, but the 'platform' is set to '${oneplatform}'." } - if (!finalConfig.agentLabels.contains('windows') && platform.contains('windows')) { - echo "WARNING: The 'platform' is set to '${platform}', but there isn't any 'windows' agent requested." + if (!finalConfig.agentLabels.contains('windows') && oneplatform.contains('windows')) { + echo "WARNING: The 'platform' is set to '${oneplatform}', but there isn't any 'windows' agent requested." } cstConfigSuffix = '-windows' } - String operatingSystem = platform.split('/')[0] + String operatingSystem = oneplatform.split('/')[0] - final InfraConfig infraConfig = new InfraConfig(env) - final String defaultRegistryNamespace = infraConfig.getDockerRegistryNamespace() - final String registryNamespace = finalConfig.registryNamespace ?: defaultRegistryNamespace - final String imageName = registryNamespace + '/' + imageShortName echo "INFO: Resolved Container Image Name: ${imageName}" node(finalConfig.agentLabels) { @@ -67,7 +71,7 @@ def call(String imageShortName, Map userConfig=[:]) { "IMAGE_NAME=${imageName}", "IMAGE_DIR=${finalConfig.imageDir}", "IMAGE_DOCKERFILE=${finalConfig.dockerfile}", - "IMAGE_PLATFORM=${platform}", + "IMAGE_PLATFORM=${oneplatform}", ]) { infra.withDockerPullCredentials{ String nextVersion = '' From b4c4ac6b2a92da9e0fa5e0fbfaf6230d1f245d27 Mon Sep 17 00:00:00 2001 From: smerle33 Date: Thu, 3 Aug 2023 11:26:48 +0200 Subject: [PATCH 03/30] correct unit test --- ...BuildDockerAndPublishImageStepTests.groovy | 20 +++++++++---------- vars/buildDockerAndPublishImage.groovy | 3 ++- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/test/groovy/BuildDockerAndPublishImageStepTests.groovy b/test/groovy/BuildDockerAndPublishImageStepTests.groovy index 5c7dbf466..7cf04dcb7 100644 --- a/test/groovy/BuildDockerAndPublishImageStepTests.groovy +++ b/test/groovy/BuildDockerAndPublishImageStepTests.groovy @@ -577,23 +577,23 @@ class BuildDockerAndPublishImageStepTests extends BaseTest { registryNamespace: 'jenkins', ]) } + final String expectedImageName = 'jenkins/' + testImageName printCallStack() // Then we expect a successful build with the code cloned assertJobStatusSuccess() // With the common workflow run as expected assertTrue(assertBaseWorkflow()) assertTrue(assertMethodCallContainsPattern('node', 'docker')) - // And the expected environment variables set to their default values - assertTrue(assertMethodCallContainsPattern('withEnv', 'IMAGE_DIR=.')) - assertTrue(assertMethodCallContainsPattern('withEnv', 'IMAGE_DOCKERFILE=Dockerfile')) + // And the environement variables set with the custom configuration values + assertTrue(assertMethodCallContainsPattern('withEnv', 'IMAGE_DIR=docker/')) + assertTrue(assertMethodCallContainsPattern('withEnv', 'IMAGE_DOCKERFILE=build.Dockerfile')) assertTrue(assertMethodCallContainsPattern('withEnv', 'IMAGE_PLATFORM=linux/amd64')) - // And generated reports recorded - assertTrue(assertRecordIssues()) - // And the deploy step called - assertTrue(assertMakeDeploy()) - // But no release created automatically - assertFalse(assertTagPushed(defaultGitTag)) - // And all mocked/stubbed methods been called + assertTrue(assertMethodCallContainsPattern('withEnv', 'IMAGE_PLATFORM=linux/arm64')) + assertTrue(assertMethodCallContainsPattern('withEnv', 'IMAGE_NAME=' + expectedImageName)) + // But no tag and no deploy called (branch or PR) + assertTrue(assertMakeDeploy(expectedImageName)) + assertTrue(assertTagPushed(defaultGitTag)) + // And all mocked/stubbed methods have to be called verifyMocks() } } diff --git a/vars/buildDockerAndPublishImage.groovy b/vars/buildDockerAndPublishImage.groovy index ab25ce578..b38ed2452 100644 --- a/vars/buildDockerAndPublishImage.groovy +++ b/vars/buildDockerAndPublishImage.groovy @@ -46,6 +46,7 @@ def call(String imageShortName, Map userConfig=[:]) { final String defaultRegistryNamespace = infraConfig.getDockerRegistryNamespace() final String registryNamespace = finalConfig.registryNamespace ?: defaultRegistryNamespace final String imageName = registryNamespace + '/' + imageShortName + final String mygetTime = now.getTime().toString() finalConfig.platforms.each {oneplatform -> echo "DEBUG platform in build '${oneplatform}'." @@ -131,7 +132,7 @@ def call(String imageShortName, Map userConfig=[:]) { stage("Lint ${imageName}") { // Define the image name as prefix to support multi images per pipeline - String hadolintReportId = "${imageName.replaceAll(':','-').replaceAll('/','-')}-hadolint-${now.getTime()}" + String hadolintReportId = "${imageName.replaceAll(':','-').replaceAll('/','-')}-hadolint-${mygetTime}" String hadoLintReportFile = "${hadolintReportId}.json" withEnv(["HADOLINT_REPORT=${env.WORKSPACE}/${hadoLintReportFile}"]) { try { From b676dd3315a23f85240219e65a548e62678f707c Mon Sep 17 00:00:00 2001 From: smerle33 Date: Thu, 3 Aug 2023 11:37:58 +0200 Subject: [PATCH 04/30] spotless clean --- test/groovy/BuildDockerAndPublishImageStepTests.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/groovy/BuildDockerAndPublishImageStepTests.groovy b/test/groovy/BuildDockerAndPublishImageStepTests.groovy index 7cf04dcb7..4aaa885af 100644 --- a/test/groovy/BuildDockerAndPublishImageStepTests.groovy +++ b/test/groovy/BuildDockerAndPublishImageStepTests.groovy @@ -571,7 +571,7 @@ class BuildDockerAndPublishImageStepTests extends BaseTest { script.call(testImageName, [ dockerfile: 'build.Dockerfile', imageDir: 'docker/', - platforms: ['linux/amd64','linux/arm64'], + platforms: ['linux/amd64', 'linux/arm64'], automaticSemanticVersioning: true, gitCredentials: 'git-creds', registryNamespace: 'jenkins', From 7d8b02f9c5be2152565dc1ae8eff1aa8a82d69d8 Mon Sep 17 00:00:00 2001 From: smerle33 Date: Thu, 3 Aug 2023 14:48:45 +0200 Subject: [PATCH 05/30] add platform to hadolint id to avoid concurrence --- vars/buildDockerAndPublishImage.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vars/buildDockerAndPublishImage.groovy b/vars/buildDockerAndPublishImage.groovy index b38ed2452..029269973 100644 --- a/vars/buildDockerAndPublishImage.groovy +++ b/vars/buildDockerAndPublishImage.groovy @@ -132,7 +132,7 @@ def call(String imageShortName, Map userConfig=[:]) { stage("Lint ${imageName}") { // Define the image name as prefix to support multi images per pipeline - String hadolintReportId = "${imageName.replaceAll(':','-').replaceAll('/','-')}-hadolint-${mygetTime}" + String hadolintReportId = "${imageName.replaceAll(':','-').replaceAll('/','-')}-hadolint-${oneplatform}-${mygetTime}" String hadoLintReportFile = "${hadolintReportId}.json" withEnv(["HADOLINT_REPORT=${env.WORKSPACE}/${hadoLintReportFile}"]) { try { From 0a569e111d04b8841c3ed3ed1d9173a5c6218e10 Mon Sep 17 00:00:00 2001 From: smerle33 Date: Thu, 3 Aug 2023 15:09:22 +0200 Subject: [PATCH 06/30] correct unit test with platform and replace slash from hadolintReportId --- test/groovy/BuildDockerAndPublishImageStepTests.groovy | 4 ++-- vars/buildDockerAndPublishImage.groovy | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/test/groovy/BuildDockerAndPublishImageStepTests.groovy b/test/groovy/BuildDockerAndPublishImageStepTests.groovy index 4aaa885af..dc6c6672f 100644 --- a/test/groovy/BuildDockerAndPublishImageStepTests.groovy +++ b/test/groovy/BuildDockerAndPublishImageStepTests.groovy @@ -122,8 +122,8 @@ class BuildDockerAndPublishImageStepTests extends BaseTest { } // Return if the usual static checks had been recorded with the usual pattern - Boolean assertRecordIssues(String imageName = fullTestImageName) { - final String reportId = "${imageName}-hadolint-${mockedTimestamp}".replaceAll('/','-').replaceAll(':', '-') + Boolean assertRecordIssues(String imageName = fullTestImageName, String platform = 'linux/amd64') { + final String reportId = "${imageName}-hadolint-${platform.replaceAll('/','-')}-${mockedTimestamp}".replaceAll('/','-').replaceAll(':', '-') return assertMethodCallContainsPattern( 'recordIssues', "{enabledForFailure=true, aggregatingResults=false, tool={id=${reportId}, pattern=${reportId}.json}}", diff --git a/vars/buildDockerAndPublishImage.groovy b/vars/buildDockerAndPublishImage.groovy index 029269973..5b4c53d7a 100644 --- a/vars/buildDockerAndPublishImage.groovy +++ b/vars/buildDockerAndPublishImage.groovy @@ -132,7 +132,7 @@ def call(String imageShortName, Map userConfig=[:]) { stage("Lint ${imageName}") { // Define the image name as prefix to support multi images per pipeline - String hadolintReportId = "${imageName.replaceAll(':','-').replaceAll('/','-')}-hadolint-${oneplatform}-${mygetTime}" + String hadolintReportId = "${imageName.replaceAll(':','-').replaceAll('/','-')}-hadolint-${oneplatform.replaceAll('/','-')}-${mygetTime}" String hadoLintReportFile = "${hadolintReportId}.json" withEnv(["HADOLINT_REPORT=${env.WORKSPACE}/${hadoLintReportFile}"]) { try { From 16244f574803607e63160da21931c5b83004f314 Mon Sep 17 00:00:00 2001 From: smerle33 Date: Thu, 3 Aug 2023 17:26:07 +0200 Subject: [PATCH 07/30] add amend part for multiplatform builds --- vars/buildDockerAndPublishImage.groovy | 33 ++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/vars/buildDockerAndPublishImage.groovy b/vars/buildDockerAndPublishImage.groovy index 5b4c53d7a..cfe4a11a1 100644 --- a/vars/buildDockerAndPublishImage.groovy +++ b/vars/buildDockerAndPublishImage.groovy @@ -33,9 +33,10 @@ def call(String imageShortName, Map userConfig=[:]) { final Date now = new Date() DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX") final String buildDate = dateFormat.format(now) - + boolean flagmultiplatforms = false if (finalConfig.platforms.size() > 0) { echo "INFO: Using platforms from the pipeline configuration" + flagmultiplatforms = true } else { echo "INFO: Using platform from the pipeline configuration" finalConfig.platforms = [finalConfig.platform] @@ -45,8 +46,9 @@ def call(String imageShortName, Map userConfig=[:]) { final InfraConfig infraConfig = new InfraConfig(env) final String defaultRegistryNamespace = infraConfig.getDockerRegistryNamespace() final String registryNamespace = finalConfig.registryNamespace ?: defaultRegistryNamespace - final String imageName = registryNamespace + '/' + imageShortName + String imageName = registryNamespace + '/' + imageShortName final String mygetTime = now.getTime().toString() + finalConfig.platforms.each {oneplatform -> echo "DEBUG platform in build '${oneplatform}'." @@ -64,6 +66,11 @@ def call(String imageShortName, Map userConfig=[:]) { } String operatingSystem = oneplatform.split('/')[0] + // in case of multi plafforms, we need to add the platform to the image name to be able to amend the image build + if (flagmultiplatforms) { + imageName = imageName + '-' + oneplatform.split('/')[1].replace('/','-') + } + echo "INFO: Resolved Container Image Name: ${imageName}" node(finalConfig.agentLabels) { @@ -292,4 +299,26 @@ def call(String imageShortName, Map userConfig=[:]) { } // withEnv } // node } // each platform + if (flagmultiplatforms) { + stage('Multiplatforms Amend') { + infra.withDockerPushCredentials { + if (env.TAG_NAME || env.BRANCH_IS_PRIMARY) { + if (env.TAG_NAME) { + dockertag = env.TAG_NAME + } else { + dockertag = 'latest' + } + imageName = registryNamespace + '/' + imageShortName + String shcommand = 'docker manifest create \\' + shcommand += '"${imageName}":"${dockertag}" \\' + finalConfig.platforms.each {eachplatform -> + specificImageName = imageName + ':' + eachplatform.split('/')[1].replace('/','-') + shcommand += '--amend "${specificImageName}" \\' + } + sh shcommand + sh 'docker manifest push "${imageName}":"${dockertag}"' + } // amend manifest only for primary branch or tags + } // need docker credential to push + } // stage + } // if } // call From d012c8667879dddd61745244aa75790cdffcfe07 Mon Sep 17 00:00:00 2001 From: smerle33 Date: Fri, 4 Aug 2023 11:48:32 +0200 Subject: [PATCH 08/30] chore --- vars/buildDockerAndPublishImage.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vars/buildDockerAndPublishImage.groovy b/vars/buildDockerAndPublishImage.groovy index cfe4a11a1..84e71f081 100644 --- a/vars/buildDockerAndPublishImage.groovy +++ b/vars/buildDockerAndPublishImage.groovy @@ -68,7 +68,7 @@ def call(String imageShortName, Map userConfig=[:]) { // in case of multi plafforms, we need to add the platform to the image name to be able to amend the image build if (flagmultiplatforms) { - imageName = imageName + '-' + oneplatform.split('/')[1].replace('/','-') + imageName = imageName + ':' + oneplatform.split('/')[1].replace('/','-') } echo "INFO: Resolved Container Image Name: ${imageName}" From 853b9db544db98ed273fe14a4dadc28093a24789 Mon Sep 17 00:00:00 2001 From: smerle33 Date: Fri, 4 Aug 2023 14:20:55 +0200 Subject: [PATCH 09/30] distinguish defaultImageName and ImageName for tags and image naming --- vars/buildDockerAndPublishImage.groovy | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/vars/buildDockerAndPublishImage.groovy b/vars/buildDockerAndPublishImage.groovy index 84e71f081..57a05e992 100644 --- a/vars/buildDockerAndPublishImage.groovy +++ b/vars/buildDockerAndPublishImage.groovy @@ -46,7 +46,7 @@ def call(String imageShortName, Map userConfig=[:]) { final InfraConfig infraConfig = new InfraConfig(env) final String defaultRegistryNamespace = infraConfig.getDockerRegistryNamespace() final String registryNamespace = finalConfig.registryNamespace ?: defaultRegistryNamespace - String imageName = registryNamespace + '/' + imageShortName + final String defaultImageName = registryNamespace + '/' + imageShortName final String mygetTime = now.getTime().toString() finalConfig.platforms.each {oneplatform -> @@ -68,7 +68,9 @@ def call(String imageShortName, Map userConfig=[:]) { // in case of multi plafforms, we need to add the platform to the image name to be able to amend the image build if (flagmultiplatforms) { - imageName = imageName + ':' + oneplatform.split('/')[1].replace('/','-') + imageName = defaultImageName + ':' + oneplatform.split('/')[1].replace('/','-') + } else { + imageName = defaultImageName } echo "INFO: Resolved Container Image Name: ${imageName}" @@ -96,14 +98,14 @@ def call(String imageShortName, Map userConfig=[:]) { // Automatic tagging on principal branch is not enabled by default, show potential next version in PR anyway if (finalConfig.automaticSemanticVersioning) { - stage("Get Next Version of ${imageName}") { - String imageInTag = '-' + imageName.replace('-','').replace(':','').toLowerCase() + stage("Get Next Version of ${defaultImageName}") { + String imageInTag = '-' + defaultImageName.replace('-','').replace(':','').toLowerCase() if (isUnix()) { sh 'git fetch --all --tags' // Ensure that all the tags are retrieved (uncoupling from job configuration, wether tags are fetched or not) if (!finalConfig.includeImageNameInTag) { nextVersion = sh(script: finalConfig.nextVersionCommand, returnStdout: true).trim() } else { - echo "Including the image name '${imageName}' in the next version" + echo "Including the image name '${defaultImageName}' in the next version" // Retrieving the semver part from the last tag including the image name String currentTagScript = 'git tag --list \"*' + imageInTag + '\" --sort=-v:refname | head -1' String currentSemVerVersion = sh(script: currentTagScript, returnStdout: true).trim() @@ -120,7 +122,7 @@ def call(String imageShortName, Map userConfig=[:]) { if (!finalConfig.includeImageNameInTag) { nextVersion = powershell(script: finalConfig.nextVersionCommand, returnStdout: true).trim() } else { - echo "Including the image name '${imageName}' in the next version" + echo "Including the image name '${defaultImageName}' in the next version" // Retrieving the semver part from the last tag including the image name String currentTagScript = 'git tag --list \"*' + imageInTag + '\" --sort=-v:refname | head -1' String currentSemVerVersion = powershell(script: currentTagScript, returnStdout: true).trim() @@ -139,7 +141,7 @@ def call(String imageShortName, Map userConfig=[:]) { stage("Lint ${imageName}") { // Define the image name as prefix to support multi images per pipeline - String hadolintReportId = "${imageName.replaceAll(':','-').replaceAll('/','-')}-hadolint-${oneplatform.replaceAll('/','-')}-${mygetTime}" + String hadolintReportId = "${imageName.replaceAll(':','-').replaceAll('/','-')}-hadolint-${mygetTime}" String hadoLintReportFile = "${hadolintReportId}.json" withEnv(["HADOLINT_REPORT=${env.WORKSPACE}/${hadoLintReportFile}"]) { try { @@ -189,7 +191,7 @@ def call(String imageShortName, Map userConfig=[:]) { // Automatic tagging on principal branch is not enabled by default if (semVerEnabledOnPrimaryBranch) { - stage("Semantic Release of ${imageName}") { + stage("Semantic Release of ${defaultImageName}") { echo "Configuring credential.helper" // The credential.helper will execute everything after the '!', here echoing the username, the password and an empty line to be passed to git as credentials when git needs it. if (isUnix()) { @@ -308,15 +310,14 @@ def call(String imageShortName, Map userConfig=[:]) { } else { dockertag = 'latest' } - imageName = registryNamespace + '/' + imageShortName String shcommand = 'docker manifest create \\' - shcommand += '"${imageName}":"${dockertag}" \\' + shcommand += '"${defaultImageName}":"${dockertag}" \\' finalConfig.platforms.each {eachplatform -> - specificImageName = imageName + ':' + eachplatform.split('/')[1].replace('/','-') + specificImageName = defaultImageName + ':' + eachplatform.split('/')[1].replace('/','-') shcommand += '--amend "${specificImageName}" \\' } sh shcommand - sh 'docker manifest push "${imageName}":"${dockertag}"' + sh 'docker manifest push "${defaultImageName}":"${dockertag}"' } // amend manifest only for primary branch or tags } // need docker credential to push } // stage From 0c84660f6e63c43abd41fc080afe61050d0d0b31 Mon Sep 17 00:00:00 2001 From: smerle33 Date: Fri, 4 Aug 2023 14:24:43 +0200 Subject: [PATCH 10/30] remove test name specific --- test/groovy/BuildDockerAndPublishImageStepTests.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/groovy/BuildDockerAndPublishImageStepTests.groovy b/test/groovy/BuildDockerAndPublishImageStepTests.groovy index dc6c6672f..8b4572d39 100644 --- a/test/groovy/BuildDockerAndPublishImageStepTests.groovy +++ b/test/groovy/BuildDockerAndPublishImageStepTests.groovy @@ -123,7 +123,7 @@ class BuildDockerAndPublishImageStepTests extends BaseTest { // Return if the usual static checks had been recorded with the usual pattern Boolean assertRecordIssues(String imageName = fullTestImageName, String platform = 'linux/amd64') { - final String reportId = "${imageName}-hadolint-${platform.replaceAll('/','-')}-${mockedTimestamp}".replaceAll('/','-').replaceAll(':', '-') + final String reportId = "${imageName}-hadolint-${mockedTimestamp}".replaceAll('/','-').replaceAll(':', '-') return assertMethodCallContainsPattern( 'recordIssues', "{enabledForFailure=true, aggregatingResults=false, tool={id=${reportId}, pattern=${reportId}.json}}", From c7a33a5930fb85c83bcb31909c0b55b16fd5a4bd Mon Sep 17 00:00:00 2001 From: smerle33 Date: Fri, 4 Aug 2023 15:16:45 +0200 Subject: [PATCH 11/30] add a node for last stage, and remove git tag creation on multiplatforms to avoid multiple tags --- vars/buildDockerAndPublishImage.groovy | 43 ++++++++++++++------------ 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/vars/buildDockerAndPublishImage.groovy b/vars/buildDockerAndPublishImage.groovy index 57a05e992..4d85e5ade 100644 --- a/vars/buildDockerAndPublishImage.groovy +++ b/vars/buildDockerAndPublishImage.groovy @@ -190,7 +190,8 @@ def call(String imageShortName, Map userConfig=[:]) { } // each // Automatic tagging on principal branch is not enabled by default - if (semVerEnabledOnPrimaryBranch) { + // not on multiplatforms builds + if (semVerEnabledOnPrimaryBranch and ! flagmultiplatforms) { stage("Semantic Release of ${defaultImageName}") { echo "Configuring credential.helper" // The credential.helper will execute everything after the '!', here echoing the username, the password and an empty line to be passed to git as credentials when git needs it. @@ -302,24 +303,26 @@ def call(String imageShortName, Map userConfig=[:]) { } // node } // each platform if (flagmultiplatforms) { - stage('Multiplatforms Amend') { - infra.withDockerPushCredentials { - if (env.TAG_NAME || env.BRANCH_IS_PRIMARY) { - if (env.TAG_NAME) { - dockertag = env.TAG_NAME - } else { - dockertag = 'latest' - } - String shcommand = 'docker manifest create \\' - shcommand += '"${defaultImageName}":"${dockertag}" \\' - finalConfig.platforms.each {eachplatform -> - specificImageName = defaultImageName + ':' + eachplatform.split('/')[1].replace('/','-') - shcommand += '--amend "${specificImageName}" \\' - } - sh shcommand - sh 'docker manifest push "${defaultImageName}":"${dockertag}"' - } // amend manifest only for primary branch or tags - } // need docker credential to push - } // stage + node(finalConfig.agentLabels) { + stage('Multiplatforms Amend') { + infra.withDockerPushCredentials { + if (env.TAG_NAME || env.BRANCH_IS_PRIMARY) { + if (env.TAG_NAME) { + dockertag = env.TAG_NAME + } else { + dockertag = 'latest' + } + String shcommand = 'docker manifest create \\' + shcommand += '"${defaultImageName}":"${dockertag}" \\' + finalConfig.platforms.each {eachplatform -> + specificImageName = defaultImageName + ':' + eachplatform.split('/')[1].replace('/','-') + shcommand += '--amend "${specificImageName}" \\' + } + sh shcommand + sh 'docker manifest push "${defaultImageName}":"${dockertag}"' + } // amend manifest only for primary branch or tags + } // need docker credential to push + } // stage + } // node } // if } // call From c7459d164a10c76579019bf0515af8a0b8da7a43 Mon Sep 17 00:00:00 2001 From: smerle33 Date: Fri, 4 Aug 2023 15:19:42 +0200 Subject: [PATCH 12/30] temporary disable tag test for multiplatforms --- test/groovy/BuildDockerAndPublishImageStepTests.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/groovy/BuildDockerAndPublishImageStepTests.groovy b/test/groovy/BuildDockerAndPublishImageStepTests.groovy index 8b4572d39..00ad74aa3 100644 --- a/test/groovy/BuildDockerAndPublishImageStepTests.groovy +++ b/test/groovy/BuildDockerAndPublishImageStepTests.groovy @@ -592,7 +592,7 @@ class BuildDockerAndPublishImageStepTests extends BaseTest { assertTrue(assertMethodCallContainsPattern('withEnv', 'IMAGE_NAME=' + expectedImageName)) // But no tag and no deploy called (branch or PR) assertTrue(assertMakeDeploy(expectedImageName)) - assertTrue(assertTagPushed(defaultGitTag)) + //assertTrue(assertTagPushed(defaultGitTag)) // And all mocked/stubbed methods have to be called verifyMocks() } From 459b9a7cd14fa6957fd5c9cf6d56bc436c821426 Mon Sep 17 00:00:00 2001 From: smerle33 Date: Fri, 4 Aug 2023 15:20:02 +0200 Subject: [PATCH 13/30] chore --- vars/buildDockerAndPublishImage.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vars/buildDockerAndPublishImage.groovy b/vars/buildDockerAndPublishImage.groovy index 4d85e5ade..e94c080f0 100644 --- a/vars/buildDockerAndPublishImage.groovy +++ b/vars/buildDockerAndPublishImage.groovy @@ -191,7 +191,7 @@ def call(String imageShortName, Map userConfig=[:]) { // Automatic tagging on principal branch is not enabled by default // not on multiplatforms builds - if (semVerEnabledOnPrimaryBranch and ! flagmultiplatforms) { + if (semVerEnabledOnPrimaryBranch && !flagmultiplatforms) { stage("Semantic Release of ${defaultImageName}") { echo "Configuring credential.helper" // The credential.helper will execute everything after the '!', here echoing the username, the password and an empty line to be passed to git as credentials when git needs it. From 34a5c4a95f848d6b5b49b1072ad02e73b88a85f7 Mon Sep 17 00:00:00 2001 From: smerle33 Date: Fri, 4 Aug 2023 15:25:43 +0200 Subject: [PATCH 14/30] remove old remnants from test --- test/groovy/BuildDockerAndPublishImageStepTests.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/groovy/BuildDockerAndPublishImageStepTests.groovy b/test/groovy/BuildDockerAndPublishImageStepTests.groovy index 00ad74aa3..ccb982307 100644 --- a/test/groovy/BuildDockerAndPublishImageStepTests.groovy +++ b/test/groovy/BuildDockerAndPublishImageStepTests.groovy @@ -122,7 +122,7 @@ class BuildDockerAndPublishImageStepTests extends BaseTest { } // Return if the usual static checks had been recorded with the usual pattern - Boolean assertRecordIssues(String imageName = fullTestImageName, String platform = 'linux/amd64') { + Boolean assertRecordIssues(String imageName = fullTestImageName) { final String reportId = "${imageName}-hadolint-${mockedTimestamp}".replaceAll('/','-').replaceAll(':', '-') return assertMethodCallContainsPattern( 'recordIssues', From cc85becde1068df4f9a0a97ca14151be1df3864d Mon Sep 17 00:00:00 2001 From: smerle33 Date: Fri, 4 Aug 2023 15:37:48 +0200 Subject: [PATCH 15/30] chore script string --- vars/buildDockerAndPublishImage.groovy | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/vars/buildDockerAndPublishImage.groovy b/vars/buildDockerAndPublishImage.groovy index e94c080f0..3a3c79577 100644 --- a/vars/buildDockerAndPublishImage.groovy +++ b/vars/buildDockerAndPublishImage.groovy @@ -312,11 +312,11 @@ def call(String imageShortName, Map userConfig=[:]) { } else { dockertag = 'latest' } - String shcommand = 'docker manifest create \\' - shcommand += '"${defaultImageName}":"${dockertag}" \\' + String shcommand = 'docker manifest create \ ' + "\n" + shcommand += '"${defaultImageName}":"${dockertag}" \ ' + "\n" finalConfig.platforms.each {eachplatform -> specificImageName = defaultImageName + ':' + eachplatform.split('/')[1].replace('/','-') - shcommand += '--amend "${specificImageName}" \\' + shcommand += '--amend "${specificImageName}" \ ' + "\n" } sh shcommand sh 'docker manifest push "${defaultImageName}":"${dockertag}"' From ad17701949f888c8d8566a821734acc9bbaabd73 Mon Sep 17 00:00:00 2001 From: smerle33 Date: Fri, 4 Aug 2023 15:38:57 +0200 Subject: [PATCH 16/30] chore script string --- vars/buildDockerAndPublishImage.groovy | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/vars/buildDockerAndPublishImage.groovy b/vars/buildDockerAndPublishImage.groovy index 3a3c79577..315a7d88e 100644 --- a/vars/buildDockerAndPublishImage.groovy +++ b/vars/buildDockerAndPublishImage.groovy @@ -312,11 +312,11 @@ def call(String imageShortName, Map userConfig=[:]) { } else { dockertag = 'latest' } - String shcommand = 'docker manifest create \ ' + "\n" - shcommand += '"${defaultImageName}":"${dockertag}" \ ' + "\n" + String shcommand = 'docker manifest create \\ ' + "\n" + shcommand += '"${defaultImageName}":"${dockertag}" \\ ' + "\n" finalConfig.platforms.each {eachplatform -> specificImageName = defaultImageName + ':' + eachplatform.split('/')[1].replace('/','-') - shcommand += '--amend "${specificImageName}" \ ' + "\n" + shcommand += '--amend "${specificImageName}" \\ ' + "\n" } sh shcommand sh 'docker manifest push "${defaultImageName}":"${dockertag}"' From 63b413ad651577c9793261246dd206b3000bc948 Mon Sep 17 00:00:00 2001 From: smerle33 Date: Fri, 4 Aug 2023 15:51:29 +0200 Subject: [PATCH 17/30] uncomment tag check --- test/groovy/BuildDockerAndPublishImageStepTests.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/groovy/BuildDockerAndPublishImageStepTests.groovy b/test/groovy/BuildDockerAndPublishImageStepTests.groovy index ccb982307..4aaa885af 100644 --- a/test/groovy/BuildDockerAndPublishImageStepTests.groovy +++ b/test/groovy/BuildDockerAndPublishImageStepTests.groovy @@ -592,7 +592,7 @@ class BuildDockerAndPublishImageStepTests extends BaseTest { assertTrue(assertMethodCallContainsPattern('withEnv', 'IMAGE_NAME=' + expectedImageName)) // But no tag and no deploy called (branch or PR) assertTrue(assertMakeDeploy(expectedImageName)) - //assertTrue(assertTagPushed(defaultGitTag)) + assertTrue(assertTagPushed(defaultGitTag)) // And all mocked/stubbed methods have to be called verifyMocks() } From 0d8e70ee033365caec9e146047ff0124c8ed684b Mon Sep 17 00:00:00 2001 From: smerle33 Date: Fri, 4 Aug 2023 15:52:07 +0200 Subject: [PATCH 18/30] add nextVersion tag in git for multiplatform --- vars/buildDockerAndPublishImage.groovy | 38 +++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/vars/buildDockerAndPublishImage.groovy b/vars/buildDockerAndPublishImage.groovy index 315a7d88e..85bc167df 100644 --- a/vars/buildDockerAndPublishImage.groovy +++ b/vars/buildDockerAndPublishImage.groovy @@ -33,6 +33,7 @@ def call(String imageShortName, Map userConfig=[:]) { final Date now = new Date() DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX") final String buildDate = dateFormat.format(now) + String nextVersion = '' //global boolean flagmultiplatforms = false if (finalConfig.platforms.size() > 0) { echo "INFO: Using platforms from the pipeline configuration" @@ -84,7 +85,7 @@ def call(String imageShortName, Map userConfig=[:]) { "IMAGE_PLATFORM=${oneplatform}", ]) { infra.withDockerPullCredentials{ - String nextVersion = '' + nextVersion = '' // reset for each turn stage("Prepare ${imageName}") { checkout scm if (finalConfig.unstash != '') { @@ -304,6 +305,41 @@ def call(String imageShortName, Map userConfig=[:]) { } // each platform if (flagmultiplatforms) { node(finalConfig.agentLabels) { + stage("Multiplatform Semantic Release of ${defaultImageName}") { + echo "Configuring credential.helper" + // The credential.helper will execute everything after the '!', here echoing the username, the password and an empty line to be passed to git as credentials when git needs it. + if (isUnix()) { + sh 'git config --local credential.helper "!set -u; echo username=\\$GIT_USERNAME && echo password=\\$GIT_PASSWORD && echo"' + } else { + // Using 'bat' here instead of 'powershell' to avoid variable interpolation problem with $ + bat 'git config --local credential.helper "!sh.exe -c \'set -u; echo username=$GIT_USERNAME && echo password=$GIT_PASSWORD && echo"\'' + } + + withCredentials([ + usernamePassword(credentialsId: "${finalConfig.gitCredentials}", passwordVariable: 'GIT_PASSWORD', usernameVariable: 'GIT_USERNAME') + ]) { + withEnv(["NEXT_VERSION=${nextVersion}"]) { + echo "Tagging and pushing the new version: ${nextVersion}" + if (isUnix()) { + sh ''' + git config user.name "${GIT_USERNAME}" + git config user.email "jenkins-infra@googlegroups.com" + + git tag -a "${NEXT_VERSION}" -m "${IMAGE_NAME}" + git push origin --tags + ''' + } else { + powershell ''' + git config user.email "jenkins-infra@googlegroups.com" + git config user.password $env:GIT_PASSWORD + + git tag -a "$env:NEXT_VERSION" -m "$env:IMAGE_NAME" + git push origin --tags + ''' + } + } // withEnv + } // withCredentials + } // stage stage('Multiplatforms Amend') { infra.withDockerPushCredentials { if (env.TAG_NAME || env.BRANCH_IS_PRIMARY) { From a4f7460d8822ee58edab029777b44f683213169a Mon Sep 17 00:00:00 2001 From: smerle33 Date: Mon, 7 Aug 2023 11:33:21 +0200 Subject: [PATCH 19/30] add checkout for new nodes for final stage --- vars/buildDockerAndPublishImage.groovy | 1 + 1 file changed, 1 insertion(+) diff --git a/vars/buildDockerAndPublishImage.groovy b/vars/buildDockerAndPublishImage.groovy index 85bc167df..ec438ca4f 100644 --- a/vars/buildDockerAndPublishImage.groovy +++ b/vars/buildDockerAndPublishImage.groovy @@ -306,6 +306,7 @@ def call(String imageShortName, Map userConfig=[:]) { if (flagmultiplatforms) { node(finalConfig.agentLabels) { stage("Multiplatform Semantic Release of ${defaultImageName}") { + checkout scm echo "Configuring credential.helper" // The credential.helper will execute everything after the '!', here echoing the username, the password and an empty line to be passed to git as credentials when git needs it. if (isUnix()) { From 555040ba09f96ce1399ebea489a809ee4176a366 Mon Sep 17 00:00:00 2001 From: smerle33 Date: Mon, 7 Aug 2023 12:00:46 +0200 Subject: [PATCH 20/30] =?UTF-8?q?env=20variables=C3=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- vars/buildDockerAndPublishImage.groovy | 45 +++++++++++++++----------- 1 file changed, 27 insertions(+), 18 deletions(-) diff --git a/vars/buildDockerAndPublishImage.groovy b/vars/buildDockerAndPublishImage.groovy index ec438ca4f..0010c37e8 100644 --- a/vars/buildDockerAndPublishImage.groovy +++ b/vars/buildDockerAndPublishImage.groovy @@ -319,7 +319,10 @@ def call(String imageShortName, Map userConfig=[:]) { withCredentials([ usernamePassword(credentialsId: "${finalConfig.gitCredentials}", passwordVariable: 'GIT_PASSWORD', usernameVariable: 'GIT_USERNAME') ]) { - withEnv(["NEXT_VERSION=${nextVersion}"]) { + withEnv([ + "NEXT_VERSION=${nextVersion}", + "IMAGE_NAME=${defaultImageName}" + ]) { echo "Tagging and pushing the new version: ${nextVersion}" if (isUnix()) { sh ''' @@ -342,23 +345,29 @@ def call(String imageShortName, Map userConfig=[:]) { } // withCredentials } // stage stage('Multiplatforms Amend') { - infra.withDockerPushCredentials { - if (env.TAG_NAME || env.BRANCH_IS_PRIMARY) { - if (env.TAG_NAME) { - dockertag = env.TAG_NAME - } else { - dockertag = 'latest' - } - String shcommand = 'docker manifest create \\ ' + "\n" - shcommand += '"${defaultImageName}":"${dockertag}" \\ ' + "\n" - finalConfig.platforms.each {eachplatform -> - specificImageName = defaultImageName + ':' + eachplatform.split('/')[1].replace('/','-') - shcommand += '--amend "${specificImageName}" \\ ' + "\n" - } - sh shcommand - sh 'docker manifest push "${defaultImageName}":"${dockertag}"' - } // amend manifest only for primary branch or tags - } // need docker credential to push + withEnv([ + "NEXT_VERSION=${nextVersion}", + "IMAGE_NAME=${defaultImageName}" + ]) { + infra.withDockerPushCredentials { + if (env.TAG_NAME || env.BRANCH_IS_PRIMARY) { + if (env.TAG_NAME) { + dockertag = env.TAG_NAME + } else { + dockertag = 'latest' + } + String shcommand = 'docker manifest create \\ ' + "\n" + shcommand += '"${IMAGE_NAME}":"${dockertag}" \\ ' + "\n" + finalConfig.platforms.each {eachplatform -> + specificImageName = defaultImageName + ':' + eachplatform.split('/')[1].replace('/','-') + shcommand += '--amend "${specificImageName}" \\ ' + "\n" + } + echo shcommand + sh shcommand + sh 'docker manifest push "${IMAGE_NAME}":"${dockertag}"' + } // amend manifest only for primary branch or tags + } // need docker credential to push + } // withEnv } // stage } // node } // if From 00f3ad3463aeaeffdd2d655f154536a900b9ca20 Mon Sep 17 00:00:00 2001 From: smerle33 Date: Mon, 7 Aug 2023 12:07:34 +0200 Subject: [PATCH 21/30] spotless lint mvn spotless:apply --- vars/buildDockerAndPublishImage.groovy | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/vars/buildDockerAndPublishImage.groovy b/vars/buildDockerAndPublishImage.groovy index 0010c37e8..d126da06e 100644 --- a/vars/buildDockerAndPublishImage.groovy +++ b/vars/buildDockerAndPublishImage.groovy @@ -319,10 +319,7 @@ def call(String imageShortName, Map userConfig=[:]) { withCredentials([ usernamePassword(credentialsId: "${finalConfig.gitCredentials}", passwordVariable: 'GIT_PASSWORD', usernameVariable: 'GIT_USERNAME') ]) { - withEnv([ - "NEXT_VERSION=${nextVersion}", - "IMAGE_NAME=${defaultImageName}" - ]) { + withEnv(["NEXT_VERSION=${nextVersion}", "IMAGE_NAME=${defaultImageName}"]) { echo "Tagging and pushing the new version: ${nextVersion}" if (isUnix()) { sh ''' @@ -345,10 +342,7 @@ def call(String imageShortName, Map userConfig=[:]) { } // withCredentials } // stage stage('Multiplatforms Amend') { - withEnv([ - "NEXT_VERSION=${nextVersion}", - "IMAGE_NAME=${defaultImageName}" - ]) { + withEnv(["NEXT_VERSION=${nextVersion}", "IMAGE_NAME=${defaultImageName}"]) { infra.withDockerPushCredentials { if (env.TAG_NAME || env.BRANCH_IS_PRIMARY) { if (env.TAG_NAME) { From 22363392104e5778ec3ec22df3301be8d1c196e0 Mon Sep 17 00:00:00 2001 From: smerle33 Date: Mon, 7 Aug 2023 15:14:26 +0200 Subject: [PATCH 22/30] encapsulation --- vars/buildDockerAndPublishImage.groovy | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/vars/buildDockerAndPublishImage.groovy b/vars/buildDockerAndPublishImage.groovy index d126da06e..158fa8902 100644 --- a/vars/buildDockerAndPublishImage.groovy +++ b/vars/buildDockerAndPublishImage.groovy @@ -342,7 +342,12 @@ def call(String imageShortName, Map userConfig=[:]) { } // withCredentials } // stage stage('Multiplatforms Amend') { - withEnv(["NEXT_VERSION=${nextVersion}", "IMAGE_NAME=${defaultImageName}"]) { + String manifestList + finalConfig.platforms.each {eachplatform -> + specificImageName = defaultImageName + ':' + eachplatform.split('/')[1].replace('/','-') + manifestList += '--amend "${specificImageName}" ' + } + withEnv(["NEXT_VERSION=${nextVersion}", "IMAGE_NAME=${defaultImageName}", "MANIFESTLIST=${manifestList}"]) { infra.withDockerPushCredentials { if (env.TAG_NAME || env.BRANCH_IS_PRIMARY) { if (env.TAG_NAME) { @@ -350,14 +355,11 @@ def call(String imageShortName, Map userConfig=[:]) { } else { dockertag = 'latest' } - String shcommand = 'docker manifest create \\ ' + "\n" - shcommand += '"${IMAGE_NAME}":"${dockertag}" \\ ' + "\n" - finalConfig.platforms.each {eachplatform -> - specificImageName = defaultImageName + ':' + eachplatform.split('/')[1].replace('/','-') - shcommand += '--amend "${specificImageName}" \\ ' + "\n" - } - echo shcommand - sh shcommand + sh ''' + docker manifest create \ + "${IMAGE_NAME}":"${dockertag}" \ + ${MANIFESTLIST} + ''' sh 'docker manifest push "${IMAGE_NAME}":"${dockertag}"' } // amend manifest only for primary branch or tags } // need docker credential to push From 1c25f3a42858a46500c26a629981bf9e9cbe1432 Mon Sep 17 00:00:00 2001 From: smerle33 Date: Mon, 7 Aug 2023 15:35:51 +0200 Subject: [PATCH 23/30] test sh command --- vars/buildDockerAndPublishImage.groovy | 318 ++++++++++++------------- 1 file changed, 158 insertions(+), 160 deletions(-) diff --git a/vars/buildDockerAndPublishImage.groovy b/vars/buildDockerAndPublishImage.groovy index 158fa8902..83433ec6a 100644 --- a/vars/buildDockerAndPublishImage.groovy +++ b/vars/buildDockerAndPublishImage.groovy @@ -140,55 +140,55 @@ def call(String imageShortName, Map userConfig=[:]) { } // stage } // if - stage("Lint ${imageName}") { - // Define the image name as prefix to support multi images per pipeline - String hadolintReportId = "${imageName.replaceAll(':','-').replaceAll('/','-')}-hadolint-${mygetTime}" - String hadoLintReportFile = "${hadolintReportId}.json" - withEnv(["HADOLINT_REPORT=${env.WORKSPACE}/${hadoLintReportFile}"]) { - try { - if (isUnix()) { - sh 'make lint' - } else { - powershell 'make lint' - } - } finally { - recordIssues( - enabledForFailure: true, - aggregatingResults: false, - tool: hadoLint(id: hadolintReportId, pattern: hadoLintReportFile) - ) - } - } - } // stage + // stage("Lint ${imageName}") { + // // Define the image name as prefix to support multi images per pipeline + // String hadolintReportId = "${imageName.replaceAll(':','-').replaceAll('/','-')}-hadolint-${mygetTime}" + // String hadoLintReportFile = "${hadolintReportId}.json" + // withEnv(["HADOLINT_REPORT=${env.WORKSPACE}/${hadoLintReportFile}"]) { + // try { + // if (isUnix()) { + // sh 'make lint' + // } else { + // powershell 'make lint' + // } + // } finally { + // recordIssues( + // enabledForFailure: true, + // aggregatingResults: false, + // tool: hadoLint(id: hadolintReportId, pattern: hadoLintReportFile) + // ) + // } + // } + // } // stage - stage("Build ${imageName}") { - if (isUnix()) { - sh 'make build' - } else { - powershell 'make build' - } - } //stage + // stage("Build ${imageName}") { + // if (isUnix()) { + // sh 'make build' + // } else { + // powershell 'make build' + // } + // } //stage // There can be 2 kind of tests: per image and per repository // Assuming Windows versions of cst configuration files finishing by "-windows" (e.g. "common-cst-windows.yml") - [ - 'Image Test Harness': "${finalConfig.imageDir}/cst${cstConfigSuffix}.yml", - 'Common Test Harness': "${env.WORKSPACE}/common-cst${cstConfigSuffix}.yml" - ].each { testName, testHarness -> - if (fileExists(testHarness)) { - stage("Test ${testName} for ${imageName}") { - withEnv(["TEST_HARNESS=${testHarness}"]) { - if (isUnix()) { - sh 'make test' - } else { - powershell 'make test' - } - } // withEnv - } //stage - } else { - echo "Skipping test ${testName} for ${imageName} as ${testHarness} does not exist" - } // if else - } // each + // [ + // 'Image Test Harness': "${finalConfig.imageDir}/cst${cstConfigSuffix}.yml", + // 'Common Test Harness': "${env.WORKSPACE}/common-cst${cstConfigSuffix}.yml" + // ].each { testName, testHarness -> + // if (fileExists(testHarness)) { + // stage("Test ${testName} for ${imageName}") { + // withEnv(["TEST_HARNESS=${testHarness}"]) { + // if (isUnix()) { + // sh 'make test' + // } else { + // powershell 'make test' + // } + // } // withEnv + // } //stage + // } else { + // echo "Skipping test ${testName} for ${imageName} as ${testHarness} does not exist" + // } // if else + // } // each // Automatic tagging on principal branch is not enabled by default // not on multiplatforms builds @@ -230,140 +230,138 @@ def call(String imageShortName, Map userConfig=[:]) { } // stage } // if }// withDockerPullCredentials - infra.withDockerPushCredentials{ - if (env.TAG_NAME || env.BRANCH_IS_PRIMARY) { - stage("Deploy ${imageName}") { - String imageDeployName = imageName - if (env.TAG_NAME) { - // User could specify a tag in the image name. In that case the git tag is appended. Otherwise the docker tag is set to the git tag. - if (imageDeployName.contains(':')) { - imageDeployName += "-${env.TAG_NAME}" - } else { - imageDeployName += ":${env.TAG_NAME}" - } - } + // infra.withDockerPushCredentials{ + // if (env.TAG_NAME || env.BRANCH_IS_PRIMARY) { + // stage("Deploy ${imageName}") { + // String imageDeployName = imageName + // if (env.TAG_NAME) { + // // User could specify a tag in the image name. In that case the git tag is appended. Otherwise the docker tag is set to the git tag. + // if (imageDeployName.contains(':')) { + // imageDeployName += "-${env.TAG_NAME}" + // } else { + // imageDeployName += ":${env.TAG_NAME}" + // } + // } - withEnv(["IMAGE_DEPLOY_NAME=${imageDeployName}"]) { - // Please note that "make deploy" uses the environment variable "IMAGE_DEPLOY_NAME" - if (isUnix()) { - sh 'make deploy' - } else { - powershell 'make deploy' - } - } // withEnv - } //stage - } // if - } // withDockerPushCredentials + // withEnv(["IMAGE_DEPLOY_NAME=${imageDeployName}"]) { + // // Please note that "make deploy" uses the environment variable "IMAGE_DEPLOY_NAME" + // if (isUnix()) { + // sh 'make deploy' + // } else { + // powershell 'make deploy' + // } + // } // withEnv + // } //stage + // } // if + // } // withDockerPushCredentials - if (env.TAG_NAME && finalConfig.automaticSemanticVersioning) { - stage('GitHub Release') { - withCredentials([ - usernamePassword(credentialsId: "${finalConfig.gitCredentials}", passwordVariable: 'GITHUB_TOKEN', usernameVariable: 'GITHUB_USERNAME') - ]) { - String release = '' - if (isUnix()) { - final String releaseScript = ''' - originUrlWithGit="$(git remote get-url origin)" - originUrl="${originUrlWithGit%.git}" - org="$(echo "${originUrl}" | cut -d'/' -f4)" - repository="$(echo "${originUrl}" | cut -d'/' -f5)" - releasesUrl="/repos/${org}/${repository}/releases" - releaseId="$(gh api "${releasesUrl}" | jq -e -r '[ .[] | select(.draft == true and .name == "next").id] | max | select(. != null)')" - if test "${releaseId}" -gt 0 - then - gh api -X PATCH -F draft=false -F name="${TAG_NAME}" -F tag_name="${TAG_NAME}" "${releasesUrl}/${releaseId}" > /dev/null - fi - echo "${releaseId}" - ''' - release = sh(script: releaseScript, returnStdout: true) - } else { - final String releaseScript = ''' - $originUrl = (git remote get-url origin) -replace '\\.git', '' - $org = $originUrl.split('/')[3] - $repository = $originUrl.split('/')[4] - $releasesUrl = "/repos/$org/$repository/releases" - $releaseId = (gh api $releasesUrl | jq -e -r '[ .[] | select(.draft == true and .name == \"next\").id] | max | select(. != null)') - $output = '' - if ($releaseId -gt 0) - { - Invoke-Expression -Command "gh api -X PATCH -F draft=false -F name=$env:TAG_NAME -F tag_name=$env:TAG_NAME $releasesUrl/$releaseId" > $null - $output = $releaseId - } - Write-Output $output - ''' - release = powershell(script: releaseScript, returnStdout: true) - } - if (release == '') { - echo "No next release draft found." - } // if - } // withCredentials - } // stage - } // if + // if (env.TAG_NAME && finalConfig.automaticSemanticVersioning) { + // stage('GitHub Release') { + // withCredentials([ + // usernamePassword(credentialsId: "${finalConfig.gitCredentials}", passwordVariable: 'GITHUB_TOKEN', usernameVariable: 'GITHUB_USERNAME') + // ]) { + // String release = '' + // if (isUnix()) { + // final String releaseScript = ''' + // originUrlWithGit="$(git remote get-url origin)" + // originUrl="${originUrlWithGit%.git}" + // org="$(echo "${originUrl}" | cut -d'/' -f4)" + // repository="$(echo "${originUrl}" | cut -d'/' -f5)" + // releasesUrl="/repos/${org}/${repository}/releases" + // releaseId="$(gh api "${releasesUrl}" | jq -e -r '[ .[] | select(.draft == true and .name == "next").id] | max | select(. != null)')" + // if test "${releaseId}" -gt 0 + // then + // gh api -X PATCH -F draft=false -F name="${TAG_NAME}" -F tag_name="${TAG_NAME}" "${releasesUrl}/${releaseId}" > /dev/null + // fi + // echo "${releaseId}" + // ''' + // release = sh(script: releaseScript, returnStdout: true) + // } else { + // final String releaseScript = ''' + // $originUrl = (git remote get-url origin) -replace '\\.git', '' + // $org = $originUrl.split('/')[3] + // $repository = $originUrl.split('/')[4] + // $releasesUrl = "/repos/$org/$repository/releases" + // $releaseId = (gh api $releasesUrl | jq -e -r '[ .[] | select(.draft == true and .name == \"next\").id] | max | select(. != null)') + // $output = '' + // if ($releaseId -gt 0) + // { + // Invoke-Expression -Command "gh api -X PATCH -F draft=false -F name=$env:TAG_NAME -F tag_name=$env:TAG_NAME $releasesUrl/$releaseId" > $null + // $output = $releaseId + // } + // Write-Output $output + // ''' + // release = powershell(script: releaseScript, returnStdout: true) + // } + // if (release == '') { + // echo "No next release draft found." + // } // if + // } // withCredentials + // } // stage + // } // if } // withEnv } // node } // each platform if (flagmultiplatforms) { node(finalConfig.agentLabels) { - stage("Multiplatform Semantic Release of ${defaultImageName}") { - checkout scm - echo "Configuring credential.helper" - // The credential.helper will execute everything after the '!', here echoing the username, the password and an empty line to be passed to git as credentials when git needs it. - if (isUnix()) { - sh 'git config --local credential.helper "!set -u; echo username=\\$GIT_USERNAME && echo password=\\$GIT_PASSWORD && echo"' - } else { - // Using 'bat' here instead of 'powershell' to avoid variable interpolation problem with $ - bat 'git config --local credential.helper "!sh.exe -c \'set -u; echo username=$GIT_USERNAME && echo password=$GIT_PASSWORD && echo"\'' - } + // stage("Multiplatform Semantic Release of ${defaultImageName}") { + // checkout scm + // echo "Configuring credential.helper" + // // The credential.helper will execute everything after the '!', here echoing the username, the password and an empty line to be passed to git as credentials when git needs it. + // if (isUnix()) { + // sh 'git config --local credential.helper "!set -u; echo username=\\$GIT_USERNAME && echo password=\\$GIT_PASSWORD && echo"' + // } else { + // // Using 'bat' here instead of 'powershell' to avoid variable interpolation problem with $ + // bat 'git config --local credential.helper "!sh.exe -c \'set -u; echo username=$GIT_USERNAME && echo password=$GIT_PASSWORD && echo"\'' + // } - withCredentials([ - usernamePassword(credentialsId: "${finalConfig.gitCredentials}", passwordVariable: 'GIT_PASSWORD', usernameVariable: 'GIT_USERNAME') - ]) { - withEnv(["NEXT_VERSION=${nextVersion}", "IMAGE_NAME=${defaultImageName}"]) { - echo "Tagging and pushing the new version: ${nextVersion}" - if (isUnix()) { - sh ''' - git config user.name "${GIT_USERNAME}" - git config user.email "jenkins-infra@googlegroups.com" + // withCredentials([ + // usernamePassword(credentialsId: "${finalConfig.gitCredentials}", passwordVariable: 'GIT_PASSWORD', usernameVariable: 'GIT_USERNAME') + // ]) { + // withEnv(["NEXT_VERSION=${nextVersion}", "IMAGE_NAME=${defaultImageName}"]) { + // echo "Tagging and pushing the new version: ${nextVersion}" + // if (isUnix()) { + // sh ''' + // git config user.name "${GIT_USERNAME}" + // git config user.email "jenkins-infra@googlegroups.com" - git tag -a "${NEXT_VERSION}" -m "${IMAGE_NAME}" - git push origin --tags - ''' - } else { - powershell ''' - git config user.email "jenkins-infra@googlegroups.com" - git config user.password $env:GIT_PASSWORD + // git tag -a "${NEXT_VERSION}" -m "${IMAGE_NAME}" + // git push origin --tags + // ''' + // } else { + // powershell ''' + // git config user.email "jenkins-infra@googlegroups.com" + // git config user.password $env:GIT_PASSWORD - git tag -a "$env:NEXT_VERSION" -m "$env:IMAGE_NAME" - git push origin --tags - ''' - } - } // withEnv - } // withCredentials - } // stage + // git tag -a "$env:NEXT_VERSION" -m "$env:IMAGE_NAME" + // git push origin --tags + // ''' + // } + // } // withEnv + // } // withCredentials + // } // stage stage('Multiplatforms Amend') { String manifestList finalConfig.platforms.each {eachplatform -> specificImageName = defaultImageName + ':' + eachplatform.split('/')[1].replace('/','-') manifestList += '--amend "${specificImageName}" ' } - withEnv(["NEXT_VERSION=${nextVersion}", "IMAGE_NAME=${defaultImageName}", "MANIFESTLIST=${manifestList}"]) { - infra.withDockerPushCredentials { - if (env.TAG_NAME || env.BRANCH_IS_PRIMARY) { - if (env.TAG_NAME) { - dockertag = env.TAG_NAME - } else { - dockertag = 'latest' - } + infra.withDockerPushCredentials { + if (env.TAG_NAME || env.BRANCH_IS_PRIMARY) { + if (env.TAG_NAME) { + dockertag = env.TAG_NAME + } else { + dockertag = 'latest' + } + withEnv(["NEXT_VERSION=${nextVersion}", "FULL_IMAGE_NAME=${defaultImageName}:${dockertag}", "MANIFESTLIST=${manifestList}"]) { sh ''' - docker manifest create \ - "${IMAGE_NAME}":"${dockertag}" \ - ${MANIFESTLIST} + docker manifest create "${FULL_IMAGE_NAME}" ${MANIFESTLIST} ''' - sh 'docker manifest push "${IMAGE_NAME}":"${dockertag}"' - } // amend manifest only for primary branch or tags - } // need docker credential to push - } // withEnv + sh 'docker manifest push "${FULL_IMAGE_NAME}"' + } // withEnv + } // amend manifest only for primary branch or tags + } // need docker credential to push } // stage } // node } // if From 6906eaf3134cab80a69731adec7ebdbab704afb6 Mon Sep 17 00:00:00 2001 From: smerle33 Date: Mon, 7 Aug 2023 16:08:26 +0200 Subject: [PATCH 24/30] test --- vars/buildDockerAndPublishImage.groovy | 434 ++++++++++++------------- 1 file changed, 217 insertions(+), 217 deletions(-) diff --git a/vars/buildDockerAndPublishImage.groovy b/vars/buildDockerAndPublishImage.groovy index 83433ec6a..f61d1d79f 100644 --- a/vars/buildDockerAndPublishImage.groovy +++ b/vars/buildDockerAndPublishImage.groovy @@ -76,232 +76,232 @@ def call(String imageShortName, Map userConfig=[:]) { echo "INFO: Resolved Container Image Name: ${imageName}" - node(finalConfig.agentLabels) { - withEnv([ - "BUILD_DATE=${buildDate}", - "IMAGE_NAME=${imageName}", - "IMAGE_DIR=${finalConfig.imageDir}", - "IMAGE_DOCKERFILE=${finalConfig.dockerfile}", - "IMAGE_PLATFORM=${oneplatform}", - ]) { - infra.withDockerPullCredentials{ - nextVersion = '' // reset for each turn - stage("Prepare ${imageName}") { - checkout scm - if (finalConfig.unstash != '') { - unstash finalConfig.unstash - } + // node(finalConfig.agentLabels) { + // withEnv([ + // "BUILD_DATE=${buildDate}", + // "IMAGE_NAME=${imageName}", + // "IMAGE_DIR=${finalConfig.imageDir}", + // "IMAGE_DOCKERFILE=${finalConfig.dockerfile}", + // "IMAGE_PLATFORM=${oneplatform}", + // ]) { + // infra.withDockerPullCredentials{ + // nextVersion = '' // reset for each turn + // stage("Prepare ${imageName}") { + // checkout scm + // if (finalConfig.unstash != '') { + // unstash finalConfig.unstash + // } - // The makefile to use must come from the pipeline to avoid a nasty user trying to exfiltrate data from the build - // Even though we have mitigation through the multibranch job config allowing to build PRs only from the repository contributors - writeFile file: 'Makefile', text: makefileContent - } // stage + // // The makefile to use must come from the pipeline to avoid a nasty user trying to exfiltrate data from the build + // // Even though we have mitigation through the multibranch job config allowing to build PRs only from the repository contributors + // writeFile file: 'Makefile', text: makefileContent + // } // stage - // Automatic tagging on principal branch is not enabled by default, show potential next version in PR anyway - if (finalConfig.automaticSemanticVersioning) { - stage("Get Next Version of ${defaultImageName}") { - String imageInTag = '-' + defaultImageName.replace('-','').replace(':','').toLowerCase() - if (isUnix()) { - sh 'git fetch --all --tags' // Ensure that all the tags are retrieved (uncoupling from job configuration, wether tags are fetched or not) - if (!finalConfig.includeImageNameInTag) { - nextVersion = sh(script: finalConfig.nextVersionCommand, returnStdout: true).trim() - } else { - echo "Including the image name '${defaultImageName}' in the next version" - // Retrieving the semver part from the last tag including the image name - String currentTagScript = 'git tag --list \"*' + imageInTag + '\" --sort=-v:refname | head -1' - String currentSemVerVersion = sh(script: currentTagScript, returnStdout: true).trim() - echo "Current semver version is '${currentSemVerVersion}'" - // Set a default value if there isn't any tag for the current image yet (https://groovy-lang.org/operators.html#_elvis_operator) - currentSemVerVersion = currentSemVerVersion ?: '0.0.0-' + imageInTag - String nextVersionScript = finalConfig.nextVersionCommand + ' -debug --previous-version=' + currentSemVerVersion - String nextVersionSemVerPart = sh(script: nextVersionScript, returnStdout: true).trim() - echo "Next semver version part is '${nextVersionSemVerPart}'" - nextVersion = nextVersionSemVerPart + imageInTag - } - } else { - powershell 'git fetch --all --tags' // Ensure that all the tags are retrieved (uncoupling from job configuration, wether tags are fetched or not) - if (!finalConfig.includeImageNameInTag) { - nextVersion = powershell(script: finalConfig.nextVersionCommand, returnStdout: true).trim() - } else { - echo "Including the image name '${defaultImageName}' in the next version" - // Retrieving the semver part from the last tag including the image name - String currentTagScript = 'git tag --list \"*' + imageInTag + '\" --sort=-v:refname | head -1' - String currentSemVerVersion = powershell(script: currentTagScript, returnStdout: true).trim() - echo "Current semver version is '${currentSemVerVersion}'" - // Set a default value if there isn't any tag for the current image yet (https://groovy-lang.org/operators.html#_elvis_operator) - currentSemVerVersion = currentSemVerVersion ?: '0.0.0-' + imageInTag - String nextVersionScript = finalConfig.nextVersionCommand + ' -debug --previous-version=' + currentSemVerVersion - String nextVersionSemVerPart = powershell(script: nextVersionScript, returnStdout: true).trim() - echo "Next semver version part is '${nextVersionSemVerPart}'" - nextVersion = nextVersionSemVerPart + imageInTag - } - } - echo "Next Release Version = ${nextVersion}" - } // stage - } // if + // // Automatic tagging on principal branch is not enabled by default, show potential next version in PR anyway + // if (finalConfig.automaticSemanticVersioning) { + // stage("Get Next Version of ${defaultImageName}") { + // String imageInTag = '-' + defaultImageName.replace('-','').replace(':','').toLowerCase() + // if (isUnix()) { + // sh 'git fetch --all --tags' // Ensure that all the tags are retrieved (uncoupling from job configuration, wether tags are fetched or not) + // if (!finalConfig.includeImageNameInTag) { + // nextVersion = sh(script: finalConfig.nextVersionCommand, returnStdout: true).trim() + // } else { + // echo "Including the image name '${defaultImageName}' in the next version" + // // Retrieving the semver part from the last tag including the image name + // String currentTagScript = 'git tag --list \"*' + imageInTag + '\" --sort=-v:refname | head -1' + // String currentSemVerVersion = sh(script: currentTagScript, returnStdout: true).trim() + // echo "Current semver version is '${currentSemVerVersion}'" + // // Set a default value if there isn't any tag for the current image yet (https://groovy-lang.org/operators.html#_elvis_operator) + // currentSemVerVersion = currentSemVerVersion ?: '0.0.0-' + imageInTag + // String nextVersionScript = finalConfig.nextVersionCommand + ' -debug --previous-version=' + currentSemVerVersion + // String nextVersionSemVerPart = sh(script: nextVersionScript, returnStdout: true).trim() + // echo "Next semver version part is '${nextVersionSemVerPart}'" + // nextVersion = nextVersionSemVerPart + imageInTag + // } + // } else { + // powershell 'git fetch --all --tags' // Ensure that all the tags are retrieved (uncoupling from job configuration, wether tags are fetched or not) + // if (!finalConfig.includeImageNameInTag) { + // nextVersion = powershell(script: finalConfig.nextVersionCommand, returnStdout: true).trim() + // } else { + // echo "Including the image name '${defaultImageName}' in the next version" + // // Retrieving the semver part from the last tag including the image name + // String currentTagScript = 'git tag --list \"*' + imageInTag + '\" --sort=-v:refname | head -1' + // String currentSemVerVersion = powershell(script: currentTagScript, returnStdout: true).trim() + // echo "Current semver version is '${currentSemVerVersion}'" + // // Set a default value if there isn't any tag for the current image yet (https://groovy-lang.org/operators.html#_elvis_operator) + // currentSemVerVersion = currentSemVerVersion ?: '0.0.0-' + imageInTag + // String nextVersionScript = finalConfig.nextVersionCommand + ' -debug --previous-version=' + currentSemVerVersion + // String nextVersionSemVerPart = powershell(script: nextVersionScript, returnStdout: true).trim() + // echo "Next semver version part is '${nextVersionSemVerPart}'" + // nextVersion = nextVersionSemVerPart + imageInTag + // } + // } + // echo "Next Release Version = ${nextVersion}" + // } // stage + // } // if - // stage("Lint ${imageName}") { - // // Define the image name as prefix to support multi images per pipeline - // String hadolintReportId = "${imageName.replaceAll(':','-').replaceAll('/','-')}-hadolint-${mygetTime}" - // String hadoLintReportFile = "${hadolintReportId}.json" - // withEnv(["HADOLINT_REPORT=${env.WORKSPACE}/${hadoLintReportFile}"]) { - // try { - // if (isUnix()) { - // sh 'make lint' - // } else { - // powershell 'make lint' - // } - // } finally { - // recordIssues( - // enabledForFailure: true, - // aggregatingResults: false, - // tool: hadoLint(id: hadolintReportId, pattern: hadoLintReportFile) - // ) - // } - // } - // } // stage + // // stage("Lint ${imageName}") { + // // // Define the image name as prefix to support multi images per pipeline + // // String hadolintReportId = "${imageName.replaceAll(':','-').replaceAll('/','-')}-hadolint-${mygetTime}" + // // String hadoLintReportFile = "${hadolintReportId}.json" + // // withEnv(["HADOLINT_REPORT=${env.WORKSPACE}/${hadoLintReportFile}"]) { + // // try { + // // if (isUnix()) { + // // sh 'make lint' + // // } else { + // // powershell 'make lint' + // // } + // // } finally { + // // recordIssues( + // // enabledForFailure: true, + // // aggregatingResults: false, + // // tool: hadoLint(id: hadolintReportId, pattern: hadoLintReportFile) + // // ) + // // } + // // } + // // } // stage - // stage("Build ${imageName}") { - // if (isUnix()) { - // sh 'make build' - // } else { - // powershell 'make build' - // } - // } //stage + // // stage("Build ${imageName}") { + // // if (isUnix()) { + // // sh 'make build' + // // } else { + // // powershell 'make build' + // // } + // // } //stage - // There can be 2 kind of tests: per image and per repository - // Assuming Windows versions of cst configuration files finishing by "-windows" (e.g. "common-cst-windows.yml") - // [ - // 'Image Test Harness': "${finalConfig.imageDir}/cst${cstConfigSuffix}.yml", - // 'Common Test Harness': "${env.WORKSPACE}/common-cst${cstConfigSuffix}.yml" - // ].each { testName, testHarness -> - // if (fileExists(testHarness)) { - // stage("Test ${testName} for ${imageName}") { - // withEnv(["TEST_HARNESS=${testHarness}"]) { - // if (isUnix()) { - // sh 'make test' - // } else { - // powershell 'make test' - // } - // } // withEnv - // } //stage - // } else { - // echo "Skipping test ${testName} for ${imageName} as ${testHarness} does not exist" - // } // if else - // } // each + // // There can be 2 kind of tests: per image and per repository + // // Assuming Windows versions of cst configuration files finishing by "-windows" (e.g. "common-cst-windows.yml") + // // [ + // // 'Image Test Harness': "${finalConfig.imageDir}/cst${cstConfigSuffix}.yml", + // // 'Common Test Harness': "${env.WORKSPACE}/common-cst${cstConfigSuffix}.yml" + // // ].each { testName, testHarness -> + // // if (fileExists(testHarness)) { + // // stage("Test ${testName} for ${imageName}") { + // // withEnv(["TEST_HARNESS=${testHarness}"]) { + // // if (isUnix()) { + // // sh 'make test' + // // } else { + // // powershell 'make test' + // // } + // // } // withEnv + // // } //stage + // // } else { + // // echo "Skipping test ${testName} for ${imageName} as ${testHarness} does not exist" + // // } // if else + // // } // each - // Automatic tagging on principal branch is not enabled by default - // not on multiplatforms builds - if (semVerEnabledOnPrimaryBranch && !flagmultiplatforms) { - stage("Semantic Release of ${defaultImageName}") { - echo "Configuring credential.helper" - // The credential.helper will execute everything after the '!', here echoing the username, the password and an empty line to be passed to git as credentials when git needs it. - if (isUnix()) { - sh 'git config --local credential.helper "!set -u; echo username=\\$GIT_USERNAME && echo password=\\$GIT_PASSWORD && echo"' - } else { - // Using 'bat' here instead of 'powershell' to avoid variable interpolation problem with $ - bat 'git config --local credential.helper "!sh.exe -c \'set -u; echo username=$GIT_USERNAME && echo password=$GIT_PASSWORD && echo"\'' - } + // // Automatic tagging on principal branch is not enabled by default + // // not on multiplatforms builds + // if (semVerEnabledOnPrimaryBranch && !flagmultiplatforms) { + // stage("Semantic Release of ${defaultImageName}") { + // echo "Configuring credential.helper" + // // The credential.helper will execute everything after the '!', here echoing the username, the password and an empty line to be passed to git as credentials when git needs it. + // if (isUnix()) { + // sh 'git config --local credential.helper "!set -u; echo username=\\$GIT_USERNAME && echo password=\\$GIT_PASSWORD && echo"' + // } else { + // // Using 'bat' here instead of 'powershell' to avoid variable interpolation problem with $ + // bat 'git config --local credential.helper "!sh.exe -c \'set -u; echo username=$GIT_USERNAME && echo password=$GIT_PASSWORD && echo"\'' + // } - withCredentials([ - usernamePassword(credentialsId: "${finalConfig.gitCredentials}", passwordVariable: 'GIT_PASSWORD', usernameVariable: 'GIT_USERNAME') - ]) { - withEnv(["NEXT_VERSION=${nextVersion}"]) { - echo "Tagging and pushing the new version: ${nextVersion}" - if (isUnix()) { - sh ''' - git config user.name "${GIT_USERNAME}" - git config user.email "jenkins-infra@googlegroups.com" + // withCredentials([ + // usernamePassword(credentialsId: "${finalConfig.gitCredentials}", passwordVariable: 'GIT_PASSWORD', usernameVariable: 'GIT_USERNAME') + // ]) { + // withEnv(["NEXT_VERSION=${nextVersion}"]) { + // echo "Tagging and pushing the new version: ${nextVersion}" + // if (isUnix()) { + // sh ''' + // git config user.name "${GIT_USERNAME}" + // git config user.email "jenkins-infra@googlegroups.com" - git tag -a "${NEXT_VERSION}" -m "${IMAGE_NAME}" - git push origin --tags - ''' - } else { - powershell ''' - git config user.email "jenkins-infra@googlegroups.com" - git config user.password $env:GIT_PASSWORD + // git tag -a "${NEXT_VERSION}" -m "${IMAGE_NAME}" + // git push origin --tags + // ''' + // } else { + // powershell ''' + // git config user.email "jenkins-infra@googlegroups.com" + // git config user.password $env:GIT_PASSWORD - git tag -a "$env:NEXT_VERSION" -m "$env:IMAGE_NAME" - git push origin --tags - ''' - } - } // withEnv - } // withCredentials - } // stage - } // if - }// withDockerPullCredentials - // infra.withDockerPushCredentials{ - // if (env.TAG_NAME || env.BRANCH_IS_PRIMARY) { - // stage("Deploy ${imageName}") { - // String imageDeployName = imageName - // if (env.TAG_NAME) { - // // User could specify a tag in the image name. In that case the git tag is appended. Otherwise the docker tag is set to the git tag. - // if (imageDeployName.contains(':')) { - // imageDeployName += "-${env.TAG_NAME}" - // } else { - // imageDeployName += ":${env.TAG_NAME}" - // } - // } + // git tag -a "$env:NEXT_VERSION" -m "$env:IMAGE_NAME" + // git push origin --tags + // ''' + // } + // } // withEnv + // } // withCredentials + // } // stage + // } // if + // }// withDockerPullCredentials + // // infra.withDockerPushCredentials{ + // // if (env.TAG_NAME || env.BRANCH_IS_PRIMARY) { + // // stage("Deploy ${imageName}") { + // // String imageDeployName = imageName + // // if (env.TAG_NAME) { + // // // User could specify a tag in the image name. In that case the git tag is appended. Otherwise the docker tag is set to the git tag. + // // if (imageDeployName.contains(':')) { + // // imageDeployName += "-${env.TAG_NAME}" + // // } else { + // // imageDeployName += ":${env.TAG_NAME}" + // // } + // // } - // withEnv(["IMAGE_DEPLOY_NAME=${imageDeployName}"]) { - // // Please note that "make deploy" uses the environment variable "IMAGE_DEPLOY_NAME" - // if (isUnix()) { - // sh 'make deploy' - // } else { - // powershell 'make deploy' - // } - // } // withEnv - // } //stage - // } // if - // } // withDockerPushCredentials + // // withEnv(["IMAGE_DEPLOY_NAME=${imageDeployName}"]) { + // // // Please note that "make deploy" uses the environment variable "IMAGE_DEPLOY_NAME" + // // if (isUnix()) { + // // sh 'make deploy' + // // } else { + // // powershell 'make deploy' + // // } + // // } // withEnv + // // } //stage + // // } // if + // // } // withDockerPushCredentials - // if (env.TAG_NAME && finalConfig.automaticSemanticVersioning) { - // stage('GitHub Release') { - // withCredentials([ - // usernamePassword(credentialsId: "${finalConfig.gitCredentials}", passwordVariable: 'GITHUB_TOKEN', usernameVariable: 'GITHUB_USERNAME') - // ]) { - // String release = '' - // if (isUnix()) { - // final String releaseScript = ''' - // originUrlWithGit="$(git remote get-url origin)" - // originUrl="${originUrlWithGit%.git}" - // org="$(echo "${originUrl}" | cut -d'/' -f4)" - // repository="$(echo "${originUrl}" | cut -d'/' -f5)" - // releasesUrl="/repos/${org}/${repository}/releases" - // releaseId="$(gh api "${releasesUrl}" | jq -e -r '[ .[] | select(.draft == true and .name == "next").id] | max | select(. != null)')" - // if test "${releaseId}" -gt 0 - // then - // gh api -X PATCH -F draft=false -F name="${TAG_NAME}" -F tag_name="${TAG_NAME}" "${releasesUrl}/${releaseId}" > /dev/null - // fi - // echo "${releaseId}" - // ''' - // release = sh(script: releaseScript, returnStdout: true) - // } else { - // final String releaseScript = ''' - // $originUrl = (git remote get-url origin) -replace '\\.git', '' - // $org = $originUrl.split('/')[3] - // $repository = $originUrl.split('/')[4] - // $releasesUrl = "/repos/$org/$repository/releases" - // $releaseId = (gh api $releasesUrl | jq -e -r '[ .[] | select(.draft == true and .name == \"next\").id] | max | select(. != null)') - // $output = '' - // if ($releaseId -gt 0) - // { - // Invoke-Expression -Command "gh api -X PATCH -F draft=false -F name=$env:TAG_NAME -F tag_name=$env:TAG_NAME $releasesUrl/$releaseId" > $null - // $output = $releaseId - // } - // Write-Output $output - // ''' - // release = powershell(script: releaseScript, returnStdout: true) - // } - // if (release == '') { - // echo "No next release draft found." - // } // if - // } // withCredentials - // } // stage - // } // if - } // withEnv - } // node + // // if (env.TAG_NAME && finalConfig.automaticSemanticVersioning) { + // // stage('GitHub Release') { + // // withCredentials([ + // // usernamePassword(credentialsId: "${finalConfig.gitCredentials}", passwordVariable: 'GITHUB_TOKEN', usernameVariable: 'GITHUB_USERNAME') + // // ]) { + // // String release = '' + // // if (isUnix()) { + // // final String releaseScript = ''' + // // originUrlWithGit="$(git remote get-url origin)" + // // originUrl="${originUrlWithGit%.git}" + // // org="$(echo "${originUrl}" | cut -d'/' -f4)" + // // repository="$(echo "${originUrl}" | cut -d'/' -f5)" + // // releasesUrl="/repos/${org}/${repository}/releases" + // // releaseId="$(gh api "${releasesUrl}" | jq -e -r '[ .[] | select(.draft == true and .name == "next").id] | max | select(. != null)')" + // // if test "${releaseId}" -gt 0 + // // then + // // gh api -X PATCH -F draft=false -F name="${TAG_NAME}" -F tag_name="${TAG_NAME}" "${releasesUrl}/${releaseId}" > /dev/null + // // fi + // // echo "${releaseId}" + // // ''' + // // release = sh(script: releaseScript, returnStdout: true) + // // } else { + // // final String releaseScript = ''' + // // $originUrl = (git remote get-url origin) -replace '\\.git', '' + // // $org = $originUrl.split('/')[3] + // // $repository = $originUrl.split('/')[4] + // // $releasesUrl = "/repos/$org/$repository/releases" + // // $releaseId = (gh api $releasesUrl | jq -e -r '[ .[] | select(.draft == true and .name == \"next\").id] | max | select(. != null)') + // // $output = '' + // // if ($releaseId -gt 0) + // // { + // // Invoke-Expression -Command "gh api -X PATCH -F draft=false -F name=$env:TAG_NAME -F tag_name=$env:TAG_NAME $releasesUrl/$releaseId" > $null + // // $output = $releaseId + // // } + // // Write-Output $output + // // ''' + // // release = powershell(script: releaseScript, returnStdout: true) + // // } + // // if (release == '') { + // // echo "No next release draft found." + // // } // if + // // } // withCredentials + // // } // stage + // // } // if + // } // withEnv + // } // node } // each platform if (flagmultiplatforms) { node(finalConfig.agentLabels) { @@ -342,10 +342,10 @@ def call(String imageShortName, Map userConfig=[:]) { // } // withCredentials // } // stage stage('Multiplatforms Amend') { - String manifestList + String manifestList = '' finalConfig.platforms.each {eachplatform -> specificImageName = defaultImageName + ':' + eachplatform.split('/')[1].replace('/','-') - manifestList += '--amend "${specificImageName}" ' + manifestList += '--amend "' + specificImageName + '" ' } infra.withDockerPushCredentials { if (env.TAG_NAME || env.BRANCH_IS_PRIMARY) { @@ -354,7 +354,7 @@ def call(String imageShortName, Map userConfig=[:]) { } else { dockertag = 'latest' } - withEnv(["NEXT_VERSION=${nextVersion}", "FULL_IMAGE_NAME=${defaultImageName}:${dockertag}", "MANIFESTLIST=${manifestList}"]) { + withEnv(["FULL_IMAGE_NAME=${defaultImageName}:${dockertag}", "MANIFESTLIST=${manifestList}"]) { sh ''' docker manifest create "${FULL_IMAGE_NAME}" ${MANIFESTLIST} ''' From ef5ed8183d4ac8215406ace56fc0a11604eac6a9 Mon Sep 17 00:00:00 2001 From: smerle33 Date: Mon, 7 Aug 2023 17:04:50 +0200 Subject: [PATCH 25/30] test --- vars/buildDockerAndPublishImage.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vars/buildDockerAndPublishImage.groovy b/vars/buildDockerAndPublishImage.groovy index f61d1d79f..313b27abb 100644 --- a/vars/buildDockerAndPublishImage.groovy +++ b/vars/buildDockerAndPublishImage.groovy @@ -345,7 +345,7 @@ def call(String imageShortName, Map userConfig=[:]) { String manifestList = '' finalConfig.platforms.each {eachplatform -> specificImageName = defaultImageName + ':' + eachplatform.split('/')[1].replace('/','-') - manifestList += '--amend "' + specificImageName + '" ' + manifestList += "--amend $specificImageName " } infra.withDockerPushCredentials { if (env.TAG_NAME || env.BRANCH_IS_PRIMARY) { From 0ed12d7d5b2d11537012f61a1f6c4b270aecc78c Mon Sep 17 00:00:00 2001 From: smerle33 Date: Mon, 7 Aug 2023 17:48:02 +0200 Subject: [PATCH 26/30] parallel stages --- vars/buildDockerAndPublishImage.groovy | 548 +++++++++++++------------ 1 file changed, 276 insertions(+), 272 deletions(-) diff --git a/vars/buildDockerAndPublishImage.groovy b/vars/buildDockerAndPublishImage.groovy index 313b27abb..29e1b1290 100644 --- a/vars/buildDockerAndPublishImage.groovy +++ b/vars/buildDockerAndPublishImage.groovy @@ -49,6 +49,7 @@ def call(String imageShortName, Map userConfig=[:]) { final String registryNamespace = finalConfig.registryNamespace ?: defaultRegistryNamespace final String defaultImageName = registryNamespace + '/' + imageShortName final String mygetTime = now.getTime().toString() + def ParrallelStages = [failFast:false] finalConfig.platforms.each {oneplatform -> @@ -75,294 +76,297 @@ def call(String imageShortName, Map userConfig=[:]) { } echo "INFO: Resolved Container Image Name: ${imageName}" + ParallelStages["${imageName}"] = { + node(finalConfig.agentLabels) { + withEnv([ + "BUILD_DATE=${buildDate}", + "IMAGE_NAME=${imageName}", + "IMAGE_DIR=${finalConfig.imageDir}", + "IMAGE_DOCKERFILE=${finalConfig.dockerfile}", + "IMAGE_PLATFORM=${oneplatform}", + ]) { + infra.withDockerPullCredentials{ + nextVersion = '' // reset for each turn + stage("Prepare ${imageName}") { + checkout scm + if (finalConfig.unstash != '') { + unstash finalConfig.unstash + } - // node(finalConfig.agentLabels) { - // withEnv([ - // "BUILD_DATE=${buildDate}", - // "IMAGE_NAME=${imageName}", - // "IMAGE_DIR=${finalConfig.imageDir}", - // "IMAGE_DOCKERFILE=${finalConfig.dockerfile}", - // "IMAGE_PLATFORM=${oneplatform}", - // ]) { - // infra.withDockerPullCredentials{ - // nextVersion = '' // reset for each turn - // stage("Prepare ${imageName}") { - // checkout scm - // if (finalConfig.unstash != '') { - // unstash finalConfig.unstash - // } + // The makefile to use must come from the pipeline to avoid a nasty user trying to exfiltrate data from the build + // Even though we have mitigation through the multibranch job config allowing to build PRs only from the repository contributors + writeFile file: 'Makefile', text: makefileContent + } // stage - // // The makefile to use must come from the pipeline to avoid a nasty user trying to exfiltrate data from the build - // // Even though we have mitigation through the multibranch job config allowing to build PRs only from the repository contributors - // writeFile file: 'Makefile', text: makefileContent - // } // stage + // Automatic tagging on principal branch is not enabled by default, show potential next version in PR anyway + if (finalConfig.automaticSemanticVersioning) { + stage("Get Next Version of ${defaultImageName}") { + String imageInTag = '-' + defaultImageName.replace('-','').replace(':','').toLowerCase() + if (isUnix()) { + sh 'git fetch --all --tags' // Ensure that all the tags are retrieved (uncoupling from job configuration, wether tags are fetched or not) + if (!finalConfig.includeImageNameInTag) { + nextVersion = sh(script: finalConfig.nextVersionCommand, returnStdout: true).trim() + } else { + echo "Including the image name '${defaultImageName}' in the next version" + // Retrieving the semver part from the last tag including the image name + String currentTagScript = 'git tag --list \"*' + imageInTag + '\" --sort=-v:refname | head -1' + String currentSemVerVersion = sh(script: currentTagScript, returnStdout: true).trim() + echo "Current semver version is '${currentSemVerVersion}'" + // Set a default value if there isn't any tag for the current image yet (https://groovy-lang.org/operators.html#_elvis_operator) + currentSemVerVersion = currentSemVerVersion ?: '0.0.0-' + imageInTag + String nextVersionScript = finalConfig.nextVersionCommand + ' -debug --previous-version=' + currentSemVerVersion + String nextVersionSemVerPart = sh(script: nextVersionScript, returnStdout: true).trim() + echo "Next semver version part is '${nextVersionSemVerPart}'" + nextVersion = nextVersionSemVerPart + imageInTag + } + } else { + powershell 'git fetch --all --tags' // Ensure that all the tags are retrieved (uncoupling from job configuration, wether tags are fetched or not) + if (!finalConfig.includeImageNameInTag) { + nextVersion = powershell(script: finalConfig.nextVersionCommand, returnStdout: true).trim() + } else { + echo "Including the image name '${defaultImageName}' in the next version" + // Retrieving the semver part from the last tag including the image name + String currentTagScript = 'git tag --list \"*' + imageInTag + '\" --sort=-v:refname | head -1' + String currentSemVerVersion = powershell(script: currentTagScript, returnStdout: true).trim() + echo "Current semver version is '${currentSemVerVersion}'" + // Set a default value if there isn't any tag for the current image yet (https://groovy-lang.org/operators.html#_elvis_operator) + currentSemVerVersion = currentSemVerVersion ?: '0.0.0-' + imageInTag + String nextVersionScript = finalConfig.nextVersionCommand + ' -debug --previous-version=' + currentSemVerVersion + String nextVersionSemVerPart = powershell(script: nextVersionScript, returnStdout: true).trim() + echo "Next semver version part is '${nextVersionSemVerPart}'" + nextVersion = nextVersionSemVerPart + imageInTag + } + } + echo "Next Release Version = ${nextVersion}" + } // stage + } // if - // // Automatic tagging on principal branch is not enabled by default, show potential next version in PR anyway - // if (finalConfig.automaticSemanticVersioning) { - // stage("Get Next Version of ${defaultImageName}") { - // String imageInTag = '-' + defaultImageName.replace('-','').replace(':','').toLowerCase() - // if (isUnix()) { - // sh 'git fetch --all --tags' // Ensure that all the tags are retrieved (uncoupling from job configuration, wether tags are fetched or not) - // if (!finalConfig.includeImageNameInTag) { - // nextVersion = sh(script: finalConfig.nextVersionCommand, returnStdout: true).trim() - // } else { - // echo "Including the image name '${defaultImageName}' in the next version" - // // Retrieving the semver part from the last tag including the image name - // String currentTagScript = 'git tag --list \"*' + imageInTag + '\" --sort=-v:refname | head -1' - // String currentSemVerVersion = sh(script: currentTagScript, returnStdout: true).trim() - // echo "Current semver version is '${currentSemVerVersion}'" - // // Set a default value if there isn't any tag for the current image yet (https://groovy-lang.org/operators.html#_elvis_operator) - // currentSemVerVersion = currentSemVerVersion ?: '0.0.0-' + imageInTag - // String nextVersionScript = finalConfig.nextVersionCommand + ' -debug --previous-version=' + currentSemVerVersion - // String nextVersionSemVerPart = sh(script: nextVersionScript, returnStdout: true).trim() - // echo "Next semver version part is '${nextVersionSemVerPart}'" - // nextVersion = nextVersionSemVerPart + imageInTag - // } - // } else { - // powershell 'git fetch --all --tags' // Ensure that all the tags are retrieved (uncoupling from job configuration, wether tags are fetched or not) - // if (!finalConfig.includeImageNameInTag) { - // nextVersion = powershell(script: finalConfig.nextVersionCommand, returnStdout: true).trim() - // } else { - // echo "Including the image name '${defaultImageName}' in the next version" - // // Retrieving the semver part from the last tag including the image name - // String currentTagScript = 'git tag --list \"*' + imageInTag + '\" --sort=-v:refname | head -1' - // String currentSemVerVersion = powershell(script: currentTagScript, returnStdout: true).trim() - // echo "Current semver version is '${currentSemVerVersion}'" - // // Set a default value if there isn't any tag for the current image yet (https://groovy-lang.org/operators.html#_elvis_operator) - // currentSemVerVersion = currentSemVerVersion ?: '0.0.0-' + imageInTag - // String nextVersionScript = finalConfig.nextVersionCommand + ' -debug --previous-version=' + currentSemVerVersion - // String nextVersionSemVerPart = powershell(script: nextVersionScript, returnStdout: true).trim() - // echo "Next semver version part is '${nextVersionSemVerPart}'" - // nextVersion = nextVersionSemVerPart + imageInTag - // } - // } - // echo "Next Release Version = ${nextVersion}" - // } // stage - // } // if + stage("Lint ${imageName}") { + // Define the image name as prefix to support multi images per pipeline + String hadolintReportId = "${imageName.replaceAll(':','-').replaceAll('/','-')}-hadolint-${mygetTime}" + String hadoLintReportFile = "${hadolintReportId}.json" + withEnv(["HADOLINT_REPORT=${env.WORKSPACE}/${hadoLintReportFile}"]) { + try { + if (isUnix()) { + sh 'make lint' + } else { + powershell 'make lint' + } + } finally { + recordIssues( + enabledForFailure: true, + aggregatingResults: false, + tool: hadoLint(id: hadolintReportId, pattern: hadoLintReportFile) + ) + } + } + } // stage - // // stage("Lint ${imageName}") { - // // // Define the image name as prefix to support multi images per pipeline - // // String hadolintReportId = "${imageName.replaceAll(':','-').replaceAll('/','-')}-hadolint-${mygetTime}" - // // String hadoLintReportFile = "${hadolintReportId}.json" - // // withEnv(["HADOLINT_REPORT=${env.WORKSPACE}/${hadoLintReportFile}"]) { - // // try { - // // if (isUnix()) { - // // sh 'make lint' - // // } else { - // // powershell 'make lint' - // // } - // // } finally { - // // recordIssues( - // // enabledForFailure: true, - // // aggregatingResults: false, - // // tool: hadoLint(id: hadolintReportId, pattern: hadoLintReportFile) - // // ) - // // } - // // } - // // } // stage + stage("Build ${imageName}") { + if (isUnix()) { + sh 'make build' + } else { + powershell 'make build' + } + } //stage - // // stage("Build ${imageName}") { - // // if (isUnix()) { - // // sh 'make build' - // // } else { - // // powershell 'make build' - // // } - // // } //stage + // There can be 2 kind of tests: per image and per repository + // Assuming Windows versions of cst configuration files finishing by "-windows" (e.g. "common-cst-windows.yml") + [ + 'Image Test Harness': "${finalConfig.imageDir}/cst${cstConfigSuffix}.yml", + 'Common Test Harness': "${env.WORKSPACE}/common-cst${cstConfigSuffix}.yml" + ].each { testName, testHarness -> + if (fileExists(testHarness)) { + stage("Test ${testName} for ${imageName}") { + withEnv(["TEST_HARNESS=${testHarness}"]) { + if (isUnix()) { + sh 'make test' + } else { + powershell 'make test' + } + } // withEnv + } //stage + } else { + echo "Skipping test ${testName} for ${imageName} as ${testHarness} does not exist" + } // if else + } // each - // // There can be 2 kind of tests: per image and per repository - // // Assuming Windows versions of cst configuration files finishing by "-windows" (e.g. "common-cst-windows.yml") - // // [ - // // 'Image Test Harness': "${finalConfig.imageDir}/cst${cstConfigSuffix}.yml", - // // 'Common Test Harness': "${env.WORKSPACE}/common-cst${cstConfigSuffix}.yml" - // // ].each { testName, testHarness -> - // // if (fileExists(testHarness)) { - // // stage("Test ${testName} for ${imageName}") { - // // withEnv(["TEST_HARNESS=${testHarness}"]) { - // // if (isUnix()) { - // // sh 'make test' - // // } else { - // // powershell 'make test' - // // } - // // } // withEnv - // // } //stage - // // } else { - // // echo "Skipping test ${testName} for ${imageName} as ${testHarness} does not exist" - // // } // if else - // // } // each + // Automatic tagging on principal branch is not enabled by default + // not on multiplatforms builds + if (semVerEnabledOnPrimaryBranch && !flagmultiplatforms) { + stage("Semantic Release of ${defaultImageName}") { + echo "Configuring credential.helper" + // The credential.helper will execute everything after the '!', here echoing the username, the password and an empty line to be passed to git as credentials when git needs it. + if (isUnix()) { + sh 'git config --local credential.helper "!set -u; echo username=\\$GIT_USERNAME && echo password=\\$GIT_PASSWORD && echo"' + } else { + // Using 'bat' here instead of 'powershell' to avoid variable interpolation problem with $ + bat 'git config --local credential.helper "!sh.exe -c \'set -u; echo username=$GIT_USERNAME && echo password=$GIT_PASSWORD && echo"\'' + } - // // Automatic tagging on principal branch is not enabled by default - // // not on multiplatforms builds - // if (semVerEnabledOnPrimaryBranch && !flagmultiplatforms) { - // stage("Semantic Release of ${defaultImageName}") { - // echo "Configuring credential.helper" - // // The credential.helper will execute everything after the '!', here echoing the username, the password and an empty line to be passed to git as credentials when git needs it. - // if (isUnix()) { - // sh 'git config --local credential.helper "!set -u; echo username=\\$GIT_USERNAME && echo password=\\$GIT_PASSWORD && echo"' - // } else { - // // Using 'bat' here instead of 'powershell' to avoid variable interpolation problem with $ - // bat 'git config --local credential.helper "!sh.exe -c \'set -u; echo username=$GIT_USERNAME && echo password=$GIT_PASSWORD && echo"\'' - // } + withCredentials([ + usernamePassword(credentialsId: "${finalConfig.gitCredentials}", passwordVariable: 'GIT_PASSWORD', usernameVariable: 'GIT_USERNAME') + ]) { + withEnv(["NEXT_VERSION=${nextVersion}"]) { + echo "Tagging and pushing the new version: ${nextVersion}" + if (isUnix()) { + sh ''' + git config user.name "${GIT_USERNAME}" + git config user.email "jenkins-infra@googlegroups.com" - // withCredentials([ - // usernamePassword(credentialsId: "${finalConfig.gitCredentials}", passwordVariable: 'GIT_PASSWORD', usernameVariable: 'GIT_USERNAME') - // ]) { - // withEnv(["NEXT_VERSION=${nextVersion}"]) { - // echo "Tagging and pushing the new version: ${nextVersion}" - // if (isUnix()) { - // sh ''' - // git config user.name "${GIT_USERNAME}" - // git config user.email "jenkins-infra@googlegroups.com" + git tag -a "${NEXT_VERSION}" -m "${IMAGE_NAME}" + git push origin --tags + ''' + } else { + powershell ''' + git config user.email "jenkins-infra@googlegroups.com" + git config user.password $env:GIT_PASSWORD - // git tag -a "${NEXT_VERSION}" -m "${IMAGE_NAME}" - // git push origin --tags - // ''' - // } else { - // powershell ''' - // git config user.email "jenkins-infra@googlegroups.com" - // git config user.password $env:GIT_PASSWORD + git tag -a "$env:NEXT_VERSION" -m "$env:IMAGE_NAME" + git push origin --tags + ''' + } + } // withEnv + } // withCredentials + } // stage + } // if + }// withDockerPullCredentials + infra.withDockerPushCredentials{ + if (env.TAG_NAME || env.BRANCH_IS_PRIMARY) { + stage("Deploy ${imageName}") { + String imageDeployName = imageName + if (env.TAG_NAME) { + // User could specify a tag in the image name. In that case the git tag is appended. Otherwise the docker tag is set to the git tag. + if (imageDeployName.contains(':')) { + imageDeployName += "-${env.TAG_NAME}" + } else { + imageDeployName += ":${env.TAG_NAME}" + } + } - // git tag -a "$env:NEXT_VERSION" -m "$env:IMAGE_NAME" - // git push origin --tags - // ''' - // } - // } // withEnv - // } // withCredentials - // } // stage - // } // if - // }// withDockerPullCredentials - // // infra.withDockerPushCredentials{ - // // if (env.TAG_NAME || env.BRANCH_IS_PRIMARY) { - // // stage("Deploy ${imageName}") { - // // String imageDeployName = imageName - // // if (env.TAG_NAME) { - // // // User could specify a tag in the image name. In that case the git tag is appended. Otherwise the docker tag is set to the git tag. - // // if (imageDeployName.contains(':')) { - // // imageDeployName += "-${env.TAG_NAME}" - // // } else { - // // imageDeployName += ":${env.TAG_NAME}" - // // } - // // } + withEnv(["IMAGE_DEPLOY_NAME=${imageDeployName}"]) { + // Please note that "make deploy" uses the environment variable "IMAGE_DEPLOY_NAME" + if (isUnix()) { + sh 'make deploy' + } else { + powershell 'make deploy' + } + } // withEnv + } //stage + } // if + } // withDockerPushCredentials - // // withEnv(["IMAGE_DEPLOY_NAME=${imageDeployName}"]) { - // // // Please note that "make deploy" uses the environment variable "IMAGE_DEPLOY_NAME" - // // if (isUnix()) { - // // sh 'make deploy' - // // } else { - // // powershell 'make deploy' - // // } - // // } // withEnv - // // } //stage - // // } // if - // // } // withDockerPushCredentials - - // // if (env.TAG_NAME && finalConfig.automaticSemanticVersioning) { - // // stage('GitHub Release') { - // // withCredentials([ - // // usernamePassword(credentialsId: "${finalConfig.gitCredentials}", passwordVariable: 'GITHUB_TOKEN', usernameVariable: 'GITHUB_USERNAME') - // // ]) { - // // String release = '' - // // if (isUnix()) { - // // final String releaseScript = ''' - // // originUrlWithGit="$(git remote get-url origin)" - // // originUrl="${originUrlWithGit%.git}" - // // org="$(echo "${originUrl}" | cut -d'/' -f4)" - // // repository="$(echo "${originUrl}" | cut -d'/' -f5)" - // // releasesUrl="/repos/${org}/${repository}/releases" - // // releaseId="$(gh api "${releasesUrl}" | jq -e -r '[ .[] | select(.draft == true and .name == "next").id] | max | select(. != null)')" - // // if test "${releaseId}" -gt 0 - // // then - // // gh api -X PATCH -F draft=false -F name="${TAG_NAME}" -F tag_name="${TAG_NAME}" "${releasesUrl}/${releaseId}" > /dev/null - // // fi - // // echo "${releaseId}" - // // ''' - // // release = sh(script: releaseScript, returnStdout: true) - // // } else { - // // final String releaseScript = ''' - // // $originUrl = (git remote get-url origin) -replace '\\.git', '' - // // $org = $originUrl.split('/')[3] - // // $repository = $originUrl.split('/')[4] - // // $releasesUrl = "/repos/$org/$repository/releases" - // // $releaseId = (gh api $releasesUrl | jq -e -r '[ .[] | select(.draft == true and .name == \"next\").id] | max | select(. != null)') - // // $output = '' - // // if ($releaseId -gt 0) - // // { - // // Invoke-Expression -Command "gh api -X PATCH -F draft=false -F name=$env:TAG_NAME -F tag_name=$env:TAG_NAME $releasesUrl/$releaseId" > $null - // // $output = $releaseId - // // } - // // Write-Output $output - // // ''' - // // release = powershell(script: releaseScript, returnStdout: true) - // // } - // // if (release == '') { - // // echo "No next release draft found." - // // } // if - // // } // withCredentials - // // } // stage - // // } // if - // } // withEnv - // } // node + if (env.TAG_NAME && finalConfig.automaticSemanticVersioning) { + stage('GitHub Release') { + withCredentials([ + usernamePassword(credentialsId: "${finalConfig.gitCredentials}", passwordVariable: 'GITHUB_TOKEN', usernameVariable: 'GITHUB_USERNAME') + ]) { + String release = '' + if (isUnix()) { + final String releaseScript = ''' + originUrlWithGit="$(git remote get-url origin)" + originUrl="${originUrlWithGit%.git}" + org="$(echo "${originUrl}" | cut -d'/' -f4)" + repository="$(echo "${originUrl}" | cut -d'/' -f5)" + releasesUrl="/repos/${org}/${repository}/releases" + releaseId="$(gh api "${releasesUrl}" | jq -e -r '[ .[] | select(.draft == true and .name == "next").id] | max | select(. != null)')" + if test "${releaseId}" -gt 0 + then + gh api -X PATCH -F draft=false -F name="${TAG_NAME}" -F tag_name="${TAG_NAME}" "${releasesUrl}/${releaseId}" > /dev/null + fi + echo "${releaseId}" + ''' + release = sh(script: releaseScript, returnStdout: true) + } else { + final String releaseScript = ''' + $originUrl = (git remote get-url origin) -replace '\\.git', '' + $org = $originUrl.split('/')[3] + $repository = $originUrl.split('/')[4] + $releasesUrl = "/repos/$org/$repository/releases" + $releaseId = (gh api $releasesUrl | jq -e -r '[ .[] | select(.draft == true and .name == \"next\").id] | max | select(. != null)') + $output = '' + if ($releaseId -gt 0) + { + Invoke-Expression -Command "gh api -X PATCH -F draft=false -F name=$env:TAG_NAME -F tag_name=$env:TAG_NAME $releasesUrl/$releaseId" > $null + $output = $releaseId + } + Write-Output $output + ''' + release = powershell(script: releaseScript, returnStdout: true) + } + if (release == '') { + echo "No next release draft found." + } // if + } // withCredentials + } // stage + } // if + } // withEnv + } // node + } //ParallelStages } // each platform + + parallel(ParallelStages) // parallel + + //After ParallelStages if (flagmultiplatforms) { - node(finalConfig.agentLabels) { - // stage("Multiplatform Semantic Release of ${defaultImageName}") { - // checkout scm - // echo "Configuring credential.helper" - // // The credential.helper will execute everything after the '!', here echoing the username, the password and an empty line to be passed to git as credentials when git needs it. - // if (isUnix()) { - // sh 'git config --local credential.helper "!set -u; echo username=\\$GIT_USERNAME && echo password=\\$GIT_PASSWORD && echo"' - // } else { - // // Using 'bat' here instead of 'powershell' to avoid variable interpolation problem with $ - // bat 'git config --local credential.helper "!sh.exe -c \'set -u; echo username=$GIT_USERNAME && echo password=$GIT_PASSWORD && echo"\'' - // } + stage("Multiplatform Semantic Release of ${defaultImageName}") { + //checkout scm // should not be necessary on main node + echo "Configuring credential.helper" + // The credential.helper will execute everything after the '!', here echoing the username, the password and an empty line to be passed to git as credentials when git needs it. + if (isUnix()) { + sh 'git config --local credential.helper "!set -u; echo username=\\$GIT_USERNAME && echo password=\\$GIT_PASSWORD && echo"' + } else { + // Using 'bat' here instead of 'powershell' to avoid variable interpolation problem with $ + bat 'git config --local credential.helper "!sh.exe -c \'set -u; echo username=$GIT_USERNAME && echo password=$GIT_PASSWORD && echo"\'' + } - // withCredentials([ - // usernamePassword(credentialsId: "${finalConfig.gitCredentials}", passwordVariable: 'GIT_PASSWORD', usernameVariable: 'GIT_USERNAME') - // ]) { - // withEnv(["NEXT_VERSION=${nextVersion}", "IMAGE_NAME=${defaultImageName}"]) { - // echo "Tagging and pushing the new version: ${nextVersion}" - // if (isUnix()) { - // sh ''' - // git config user.name "${GIT_USERNAME}" - // git config user.email "jenkins-infra@googlegroups.com" + withCredentials([ + usernamePassword(credentialsId: "${finalConfig.gitCredentials}", passwordVariable: 'GIT_PASSWORD', usernameVariable: 'GIT_USERNAME') + ]) { + withEnv(["NEXT_VERSION=${nextVersion}", "IMAGE_NAME=${defaultImageName}"]) { + echo "Tagging and pushing the new version: ${nextVersion}" + if (isUnix()) { + sh ''' + git config user.name "${GIT_USERNAME}" + git config user.email "jenkins-infra@googlegroups.com" - // git tag -a "${NEXT_VERSION}" -m "${IMAGE_NAME}" - // git push origin --tags - // ''' - // } else { - // powershell ''' - // git config user.email "jenkins-infra@googlegroups.com" - // git config user.password $env:GIT_PASSWORD + git tag -a "${NEXT_VERSION}" -m "${IMAGE_NAME}" + git push origin --tags + ''' + } else { + powershell ''' + git config user.email "jenkins-infra@googlegroups.com" + git config user.password $env:GIT_PASSWORD - // git tag -a "$env:NEXT_VERSION" -m "$env:IMAGE_NAME" - // git push origin --tags - // ''' - // } - // } // withEnv - // } // withCredentials - // } // stage - stage('Multiplatforms Amend') { - String manifestList = '' - finalConfig.platforms.each {eachplatform -> - specificImageName = defaultImageName + ':' + eachplatform.split('/')[1].replace('/','-') - manifestList += "--amend $specificImageName " - } - infra.withDockerPushCredentials { - if (env.TAG_NAME || env.BRANCH_IS_PRIMARY) { - if (env.TAG_NAME) { - dockertag = env.TAG_NAME - } else { - dockertag = 'latest' - } - withEnv(["FULL_IMAGE_NAME=${defaultImageName}:${dockertag}", "MANIFESTLIST=${manifestList}"]) { - sh ''' - docker manifest create "${FULL_IMAGE_NAME}" ${MANIFESTLIST} - ''' - sh 'docker manifest push "${FULL_IMAGE_NAME}"' - } // withEnv - } // amend manifest only for primary branch or tags - } // need docker credential to push - } // stage - } // node + git tag -a "$env:NEXT_VERSION" -m "$env:IMAGE_NAME" + git push origin --tags + ''' + } + } // withEnv + } // withCredentials + } // stage + stage('Multiplatforms Amend') { + String manifestList = '' + finalConfig.platforms.each {eachplatform -> + specificImageName = defaultImageName + ':' + eachplatform.split('/')[1].replace('/','-') + manifestList += "--amend $specificImageName " + } + infra.withDockerPushCredentials { + if (env.TAG_NAME || env.BRANCH_IS_PRIMARY) { + if (env.TAG_NAME) { + dockertag = env.TAG_NAME + } else { + dockertag = 'latest' + } + withEnv(["FULL_IMAGE_NAME=${defaultImageName}:${dockertag}", "MANIFESTLIST=${manifestList}"]) { + sh ''' + docker manifest create "${FULL_IMAGE_NAME}" ${MANIFESTLIST} + ''' + sh 'docker manifest push "${FULL_IMAGE_NAME}"' + } // withEnv + } // amend manifest only for primary branch or tags + } // need docker credential to push + } // stage } // if } // call From 3f204dfe1c4609888333e1ae0fdff537de2cd7f6 Mon Sep 17 00:00:00 2001 From: smerle33 Date: Mon, 7 Aug 2023 17:56:38 +0200 Subject: [PATCH 27/30] chore --- vars/buildDockerAndPublishImage.groovy | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/vars/buildDockerAndPublishImage.groovy b/vars/buildDockerAndPublishImage.groovy index 29e1b1290..cd67da9e7 100644 --- a/vars/buildDockerAndPublishImage.groovy +++ b/vars/buildDockerAndPublishImage.groovy @@ -4,6 +4,7 @@ import java.util.Date import java.text.DateFormat def call(String imageShortName, Map userConfig=[:]) { + def parallelStages = [failFast:false] def defaultConfig = [ agentLabels: 'docker || linux-amd64-docker', // String expression for the labels the agent must match automaticSemanticVersioning: false, // Do not automagically increase semantic version by default @@ -49,7 +50,7 @@ def call(String imageShortName, Map userConfig=[:]) { final String registryNamespace = finalConfig.registryNamespace ?: defaultRegistryNamespace final String defaultImageName = registryNamespace + '/' + imageShortName final String mygetTime = now.getTime().toString() - def ParrallelStages = [failFast:false] + finalConfig.platforms.each {oneplatform -> @@ -76,7 +77,7 @@ def call(String imageShortName, Map userConfig=[:]) { } echo "INFO: Resolved Container Image Name: ${imageName}" - ParallelStages["${imageName}"] = { + parallelStages["${imageName}"] = { node(finalConfig.agentLabels) { withEnv([ "BUILD_DATE=${buildDate}", @@ -303,12 +304,12 @@ def call(String imageShortName, Map userConfig=[:]) { } // if } // withEnv } // node - } //ParallelStages + } //parallelStages } // each platform - parallel(ParallelStages) // parallel + parallel(parallelStages) // parallel - //After ParallelStages + //After parallelStages if (flagmultiplatforms) { stage("Multiplatform Semantic Release of ${defaultImageName}") { //checkout scm // should not be necessary on main node From 8843696094214ecdf0c3132cf8bb9c2b75ce0b00 Mon Sep 17 00:00:00 2001 From: smerle33 Date: Mon, 7 Aug 2023 18:24:47 +0200 Subject: [PATCH 28/30] imagename use env variable for loop --- vars/buildDockerAndPublishImage.groovy | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/vars/buildDockerAndPublishImage.groovy b/vars/buildDockerAndPublishImage.groovy index cd67da9e7..4b8db2e84 100644 --- a/vars/buildDockerAndPublishImage.groovy +++ b/vars/buildDockerAndPublishImage.groovy @@ -88,7 +88,7 @@ def call(String imageShortName, Map userConfig=[:]) { ]) { infra.withDockerPullCredentials{ nextVersion = '' // reset for each turn - stage("Prepare ${imageName}") { + stage("Prepare ${IMAGE_NAME}") { checkout scm if (finalConfig.unstash != '') { unstash finalConfig.unstash @@ -142,9 +142,9 @@ def call(String imageShortName, Map userConfig=[:]) { } // stage } // if - stage("Lint ${imageName}") { + stage("Lint ${IMAGE_NAME}") { // Define the image name as prefix to support multi images per pipeline - String hadolintReportId = "${imageName.replaceAll(':','-').replaceAll('/','-')}-hadolint-${mygetTime}" + String hadolintReportId = "${IMAGE_NAME.replaceAll(':','-').replaceAll('/','-')}-hadolint-${mygetTime}" String hadoLintReportFile = "${hadolintReportId}.json" withEnv(["HADOLINT_REPORT=${env.WORKSPACE}/${hadoLintReportFile}"]) { try { @@ -163,7 +163,7 @@ def call(String imageShortName, Map userConfig=[:]) { } } // stage - stage("Build ${imageName}") { + stage("Build ${IMAGE_NAME}") { if (isUnix()) { sh 'make build' } else { @@ -178,7 +178,7 @@ def call(String imageShortName, Map userConfig=[:]) { 'Common Test Harness': "${env.WORKSPACE}/common-cst${cstConfigSuffix}.yml" ].each { testName, testHarness -> if (fileExists(testHarness)) { - stage("Test ${testName} for ${imageName}") { + stage("Test ${testName} for ${IMAGE_NAME}") { withEnv(["TEST_HARNESS=${testHarness}"]) { if (isUnix()) { sh 'make test' @@ -188,7 +188,7 @@ def call(String imageShortName, Map userConfig=[:]) { } // withEnv } //stage } else { - echo "Skipping test ${testName} for ${imageName} as ${testHarness} does not exist" + echo "Skipping test ${testName} for ${IMAGE_NAME} as ${testHarness} does not exist" } // if else } // each @@ -234,8 +234,8 @@ def call(String imageShortName, Map userConfig=[:]) { }// withDockerPullCredentials infra.withDockerPushCredentials{ if (env.TAG_NAME || env.BRANCH_IS_PRIMARY) { - stage("Deploy ${imageName}") { - String imageDeployName = imageName + stage("Deploy ${IMAGE_NAME}") { + String imageDeployName = IMAGE_NAME if (env.TAG_NAME) { // User could specify a tag in the image name. In that case the git tag is appended. Otherwise the docker tag is set to the git tag. if (imageDeployName.contains(':')) { From 8fcd9d73689e6708d6999d27469ec590342677f9 Mon Sep 17 00:00:00 2001 From: smerle33 Date: Thu, 10 Aug 2023 11:48:38 +0200 Subject: [PATCH 29/30] groovy ImageName vs environement shell IMAGE_NAME --- vars/buildDockerAndPublishImage.groovy | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/vars/buildDockerAndPublishImage.groovy b/vars/buildDockerAndPublishImage.groovy index 4b8db2e84..5b96cb95e 100644 --- a/vars/buildDockerAndPublishImage.groovy +++ b/vars/buildDockerAndPublishImage.groovy @@ -51,7 +51,6 @@ def call(String imageShortName, Map userConfig=[:]) { final String defaultImageName = registryNamespace + '/' + imageShortName final String mygetTime = now.getTime().toString() - finalConfig.platforms.each {oneplatform -> echo "DEBUG platform in build '${oneplatform}'." @@ -70,10 +69,9 @@ def call(String imageShortName, Map userConfig=[:]) { String operatingSystem = oneplatform.split('/')[0] // in case of multi plafforms, we need to add the platform to the image name to be able to amend the image build + String imageName = defaultImageName if (flagmultiplatforms) { imageName = defaultImageName + ':' + oneplatform.split('/')[1].replace('/','-') - } else { - imageName = defaultImageName } echo "INFO: Resolved Container Image Name: ${imageName}" @@ -88,7 +86,7 @@ def call(String imageShortName, Map userConfig=[:]) { ]) { infra.withDockerPullCredentials{ nextVersion = '' // reset for each turn - stage("Prepare ${IMAGE_NAME}") { + stage("Prepare ${imageName}") { checkout scm if (finalConfig.unstash != '') { unstash finalConfig.unstash @@ -142,9 +140,9 @@ def call(String imageShortName, Map userConfig=[:]) { } // stage } // if - stage("Lint ${IMAGE_NAME}") { + stage("Lint ${imageName}") { // Define the image name as prefix to support multi images per pipeline - String hadolintReportId = "${IMAGE_NAME.replaceAll(':','-').replaceAll('/','-')}-hadolint-${mygetTime}" + String hadolintReportId = "${imageName.replaceAll(':','-').replaceAll('/','-')}-hadolint-${mygetTime}" String hadoLintReportFile = "${hadolintReportId}.json" withEnv(["HADOLINT_REPORT=${env.WORKSPACE}/${hadoLintReportFile}"]) { try { @@ -163,7 +161,7 @@ def call(String imageShortName, Map userConfig=[:]) { } } // stage - stage("Build ${IMAGE_NAME}") { + stage("Build ${imageName}") { if (isUnix()) { sh 'make build' } else { @@ -178,7 +176,7 @@ def call(String imageShortName, Map userConfig=[:]) { 'Common Test Harness': "${env.WORKSPACE}/common-cst${cstConfigSuffix}.yml" ].each { testName, testHarness -> if (fileExists(testHarness)) { - stage("Test ${testName} for ${IMAGE_NAME}") { + stage("Test ${testName} for ${imageName}") { withEnv(["TEST_HARNESS=${testHarness}"]) { if (isUnix()) { sh 'make test' @@ -188,7 +186,7 @@ def call(String imageShortName, Map userConfig=[:]) { } // withEnv } //stage } else { - echo "Skipping test ${testName} for ${IMAGE_NAME} as ${testHarness} does not exist" + echo "Skipping test ${testName} for ${imageName} as ${testHarness} does not exist" } // if else } // each @@ -234,8 +232,8 @@ def call(String imageShortName, Map userConfig=[:]) { }// withDockerPullCredentials infra.withDockerPushCredentials{ if (env.TAG_NAME || env.BRANCH_IS_PRIMARY) { - stage("Deploy ${IMAGE_NAME}") { - String imageDeployName = IMAGE_NAME + stage("Deploy ${imageName}") { + String imageDeployName = imageName if (env.TAG_NAME) { // User could specify a tag in the image name. In that case the git tag is appended. Otherwise the docker tag is set to the git tag. if (imageDeployName.contains(':')) { From 873f2a0423c327fb9bf24aadd96f8c954e694b09 Mon Sep 17 00:00:00 2001 From: smerle33 Date: Thu, 10 Aug 2023 12:10:13 +0200 Subject: [PATCH 30/30] add a node for multiplatform task --- vars/buildDockerAndPublishImage.groovy | 110 +++++++++++++------------ 1 file changed, 56 insertions(+), 54 deletions(-) diff --git a/vars/buildDockerAndPublishImage.groovy b/vars/buildDockerAndPublishImage.groovy index 5b96cb95e..c7d2154ec 100644 --- a/vars/buildDockerAndPublishImage.groovy +++ b/vars/buildDockerAndPublishImage.groovy @@ -309,63 +309,65 @@ def call(String imageShortName, Map userConfig=[:]) { //After parallelStages if (flagmultiplatforms) { - stage("Multiplatform Semantic Release of ${defaultImageName}") { - //checkout scm // should not be necessary on main node - echo "Configuring credential.helper" - // The credential.helper will execute everything after the '!', here echoing the username, the password and an empty line to be passed to git as credentials when git needs it. - if (isUnix()) { - sh 'git config --local credential.helper "!set -u; echo username=\\$GIT_USERNAME && echo password=\\$GIT_PASSWORD && echo"' - } else { - // Using 'bat' here instead of 'powershell' to avoid variable interpolation problem with $ - bat 'git config --local credential.helper "!sh.exe -c \'set -u; echo username=$GIT_USERNAME && echo password=$GIT_PASSWORD && echo"\'' - } + node(finalConfig.agentLabels) { + stage("Multiplatform Semantic Release of ${defaultImageName}") { + checkout scm // should not be necessary on main node + echo "Configuring credential.helper" + // The credential.helper will execute everything after the '!', here echoing the username, the password and an empty line to be passed to git as credentials when git needs it. + if (isUnix()) { + sh 'git config --local credential.helper "!set -u; echo username=\\$GIT_USERNAME && echo password=\\$GIT_PASSWORD && echo"' + } else { + // Using 'bat' here instead of 'powershell' to avoid variable interpolation problem with $ + bat 'git config --local credential.helper "!sh.exe -c \'set -u; echo username=$GIT_USERNAME && echo password=$GIT_PASSWORD && echo"\'' + } - withCredentials([ - usernamePassword(credentialsId: "${finalConfig.gitCredentials}", passwordVariable: 'GIT_PASSWORD', usernameVariable: 'GIT_USERNAME') - ]) { - withEnv(["NEXT_VERSION=${nextVersion}", "IMAGE_NAME=${defaultImageName}"]) { - echo "Tagging and pushing the new version: ${nextVersion}" - if (isUnix()) { - sh ''' - git config user.name "${GIT_USERNAME}" - git config user.email "jenkins-infra@googlegroups.com" + withCredentials([ + usernamePassword(credentialsId: "${finalConfig.gitCredentials}", passwordVariable: 'GIT_PASSWORD', usernameVariable: 'GIT_USERNAME') + ]) { + withEnv(["NEXT_VERSION=${nextVersion}", "IMAGE_NAME=${defaultImageName}"]) { + echo "Tagging and pushing the new version: ${nextVersion}" + if (isUnix()) { + sh ''' + git config user.name "${GIT_USERNAME}" + git config user.email "jenkins-infra@googlegroups.com" - git tag -a "${NEXT_VERSION}" -m "${IMAGE_NAME}" - git push origin --tags - ''' - } else { - powershell ''' - git config user.email "jenkins-infra@googlegroups.com" - git config user.password $env:GIT_PASSWORD + git tag -a "${NEXT_VERSION}" -m "${IMAGE_NAME}" + git push origin --tags + ''' + } else { + powershell ''' + git config user.email "jenkins-infra@googlegroups.com" + git config user.password $env:GIT_PASSWORD - git tag -a "$env:NEXT_VERSION" -m "$env:IMAGE_NAME" - git push origin --tags - ''' - } - } // withEnv - } // withCredentials - } // stage - stage('Multiplatforms Amend') { - String manifestList = '' - finalConfig.platforms.each {eachplatform -> - specificImageName = defaultImageName + ':' + eachplatform.split('/')[1].replace('/','-') - manifestList += "--amend $specificImageName " - } - infra.withDockerPushCredentials { - if (env.TAG_NAME || env.BRANCH_IS_PRIMARY) { - if (env.TAG_NAME) { - dockertag = env.TAG_NAME - } else { - dockertag = 'latest' - } - withEnv(["FULL_IMAGE_NAME=${defaultImageName}:${dockertag}", "MANIFESTLIST=${manifestList}"]) { - sh ''' - docker manifest create "${FULL_IMAGE_NAME}" ${MANIFESTLIST} - ''' - sh 'docker manifest push "${FULL_IMAGE_NAME}"' + git tag -a "$env:NEXT_VERSION" -m "$env:IMAGE_NAME" + git push origin --tags + ''' + } } // withEnv - } // amend manifest only for primary branch or tags - } // need docker credential to push - } // stage + } // withCredentials + } // stage + stage('Multiplatforms Amend') { + String manifestList = '' + finalConfig.platforms.each {eachplatform -> + specificImageName = defaultImageName + ':' + eachplatform.split('/')[1].replace('/','-') + manifestList += "--amend $specificImageName " + } + infra.withDockerPushCredentials { + if (env.TAG_NAME || env.BRANCH_IS_PRIMARY) { + if (env.TAG_NAME) { + dockertag = env.TAG_NAME + } else { + dockertag = 'latest' + } + withEnv(["FULL_IMAGE_NAME=${defaultImageName}:${dockertag}", "MANIFESTLIST=${manifestList}"]) { + sh ''' + docker manifest create "${FULL_IMAGE_NAME}" ${MANIFESTLIST} + ''' + sh 'docker manifest push "${FULL_IMAGE_NAME}"' + } // withEnv + } // amend manifest only for primary branch or tags + } // need docker credential to push + } // stage + } // node } // if } // call