From f44ba20d1c554b92cc5e20683abc8e8ccb1b89f7 Mon Sep 17 00:00:00 2001 From: Ladislau Date: Wed, 10 Dec 2025 15:58:40 -0300 Subject: [PATCH] Fix Jenkins CPS warnings for implicit field creation in monolithic.Jenkinsfile Jenkins v2.516.1 with Pipeline Groovy plugin v4045+ displays memory leak warnings when variables are declared without the 'def' keyword. These warnings appear because the CPS transformer interprets unscoped variables as implicit script-level fields. This change eliminates all CPS warnings by applying proper Groovy syntax for variable declarations and Map operations. pipelines/monolithic.Jenkinsfile - MODIFIED Lines 11-19: Pipeline-level variables - Added @groovy.transform.Field annotation to PROPS, IMG_PARAMS, IMAGES_FAILED - These variables persist across pipeline stages and require explicit field declaration - Reference: https://www.jenkins.io/doc/book/pipeline/shared-libraries/ Lines 22-24: parseProps() function - Empty Map declaration - Changed 'def x = {}' to 'def x = [:]' - Using {} creates a Closure, not a Map - Reference: https://groovy-lang.org/groovy-dev-kit.html Lines 29-32: parseProps() function - Loop variables - Added 'def' keyword to parts, key, value variables - Prevents implicit field creation for loop-scoped variables Lines 39-58: loadEnv() function - Map declaration and property access - Changed 'def data = {}' to 'def data = [:]' for proper Map instantiation - Changed dot notation (data.KEY) to bracket notation (data['KEY']) - Bracket notation avoids false positive CPS warnings. It explicitly calls Map.putAt() method instead of ambiguous property access - Reference: https://www.jenkins.io/doc/book/pipeline/cps-method-mismatches/ Lines 72, 100-106, 281, 376: PROPS property reads - Changed PROPS.KEY to PROPS['KEY'] for consistency with bracket notation TEST PLAN Scenario 1 - Jenkins v2.492.1 backward compatibility: PASS: Pipeline executed without errors PASS: No sandbox permission issues PASS: All stages completed successfully Scenario 2 - Jenkins v2.516.1 warning elimination: PASS: Without fix - 17 CPS warnings displayed PASS: With fix - 0 CPS warnings displayed PASS: Pipeline executed without errors PASS: All stages completed successfully Closes-Bug: 2134568 Change-Id: Ic963c15cb0fe5cde96654e8d4542534cbcf07c30 Signed-off-by: Ladislau --- pipelines/monolithic.Jenkinsfile | 58 ++++++++++++++++++++------------ 1 file changed, 36 insertions(+), 22 deletions(-) diff --git a/pipelines/monolithic.Jenkinsfile b/pipelines/monolithic.Jenkinsfile index af89512..3048f4b 100644 --- a/pipelines/monolithic.Jenkinsfile +++ b/pipelines/monolithic.Jenkinsfile @@ -8,40 +8,54 @@ library "common@${params.JENKINS_SCRIPTS_BRANCH}" -PROPS = null -IMG_PARAMS = null -IMAGES_FAILED = false +// Pipeline-level variables require @Field annotation to avoid Jenkins CPS warnings +// about implicit field creation. These variables persist across pipeline stages. +// Reference: https://www.jenkins.io/doc/book/pipeline/shared-libraries/ +@groovy.transform.Field +def PROPS = null +@groovy.transform.Field +def IMG_PARAMS = null +@groovy.transform.Field +def IMAGES_FAILED = false def parseProps(text) { - def x = {} + // Use [:] for empty Map. Using {} creates a Closure, not a Map. + // Reference: https://groovy-lang.org/groovy-dev-kit.html + def x = [:] for (line in text.split (/\n+/)) { if (line.matches (/\s*(?:#.*)?#/)) { continue } - parts = line.split ("=", 2) - key = parts[0] - value = parts[1] + // Use def keyword for loop variables to avoid CPS field creation warnings + def parts = line.split ("=", 2) + def key = parts[0] + def value = parts[1] x."${key}" = value } return x } def loadEnv() { - def data = {} - data.NEED_BUILD = false - data.SUPPRESS_DOCKER_IMAGE_BUILD_ERRORS = params.SUPPRESS_DOCKER_IMAGE_BUILD_ERRORS + // Use [:] for empty Map. Using {} creates a Closure, not a Map. + // Use bracket notation for Map property access to avoid false positive CPS warnings. + // Dot notation (data.KEY) triggers warnings because CPS cannot distinguish between + // dynamic Map property access and implicit field creation. + // Reference: https://www.jenkins.io/doc/book/pipeline/cps-method-mismatches/ + def data = [:] + data["NEED_BUILD"] = false + data["SUPPRESS_DOCKER_IMAGE_BUILD_ERRORS"] = params.SUPPRESS_DOCKER_IMAGE_BUILD_ERRORS ws(params.BUILD_HOME) { if (fileExists ("NEED_BUILD")) { - data.NEED_BUILD = true + data["NEED_BUILD"] = true } } final String configText = sh (script: "${Constants.SCRIPTS_DIR}/print-config.sh", returnStdout: true) final props = parseProps (configText) - data.BUILD_OUTPUT_HOME_URL = props.BUILD_OUTPUT_HOME_URL - data.PUBLISH_URL = props.PUBLISH_URL - data.BUILD_REMOTE_CLI = props.BUILD_REMOTE_CLI == "true" + data["BUILD_OUTPUT_HOME_URL"] = props.BUILD_OUTPUT_HOME_URL + data["PUBLISH_URL"] = props.PUBLISH_URL + data["BUILD_REMOTE_CLI"] = props.BUILD_REMOTE_CLI == "true" PROPS = data - return data.NEED_BUILD + return data["NEED_BUILD"] } def partJobName (name) { @@ -69,7 +83,7 @@ def runImagesPart (name, params = []) { // current build. Instead we note when an image-related // job has failed, and skip all subsequent image-related // jobs. - if (PROPS.SUPPRESS_DOCKER_IMAGE_BUILD_ERRORS) { + if (PROPS["SUPPRESS_DOCKER_IMAGE_BUILD_ERRORS"]) { if (!IMAGES_FAILED) { final jobName = partJobName (name) final res = runPart (name, IMG_PARAMS + params, false).result @@ -97,10 +111,10 @@ def printBuildFooter() { msg += "\n" msg += "========================================\n" msg += "\n" - if (PROPS.NEED_BUILD) { - msg += "Build output: ${PROPS.BUILD_OUTPUT_HOME_URL}\n" - if (PROPS.PUBLISH_URL) { - msg += "Publish output: ${PROPS.PUBLISH_URL}\n" + if (PROPS["NEED_BUILD"]) { + msg += "Build output: ${PROPS["BUILD_OUTPUT_HOME_URL"]}\n" + if (PROPS["PUBLISH_URL"]) { + msg += "Publish output: ${PROPS["PUBLISH_URL"]}\n" } if (IMAGES_FAILED) { msg += "\n" @@ -278,7 +292,7 @@ or with paths relative to repo root: } // This stage runs only if build is required stage('X0') { - when { expression { PROPS.NEED_BUILD } } + when { expression { PROPS["NEED_BUILD"] } } stages { stage('PREPARE') { steps { @@ -373,7 +387,7 @@ or with paths relative to repo root: } // stages } // stage('IMAGES') stage('remote-cli') { - when { expression { PROPS.BUILD_REMOTE_CLI } } + when { expression { PROPS["BUILD_REMOTE_CLI"] } } steps { runPart ("build-remote-cli") }