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 <Ladislau.Felisbino@windriver.com>
This commit is contained in:
Ladislau
2025-12-10 15:58:40 -03:00
committed by Ladislau Felisbino
parent a750c423ea
commit f44ba20d1c

View File

@@ -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")
}