java.io.NotSerializableException: groovy.util.slurpersupport.Attributes - jenkins

I'm trying to run this pipeline script
#!groovy
#NonCPS
def findCommand(filePath) {
def file = new File(filePath)
def text = file.getText()
def components = new XmlSlurper().parseText( text )
def map = new HashMap<>()
components.component.each { def component->
map << ["${component.#application}" : [:]]
def componentMap = map.get("${component.#application}")
component.environments.environment.each { def environment->
componentMap << ["${environment.#name}" : [:]]
def actionMap = componentMap.get("${environment.#name}")
environment.actions.action.each { def action->
actionMap << ["${action.#toDo}" : action]
}
}
}
components = null
text = null
file = null
return map
}
pipeline {
agent any
stages {
stage('Build') {
steps {
echo 'Building..'
echo "Running ${env.BUILD_ID} on ${env.JENKINS_URL}"
echo "Parameters: ${params.environment} ${params.actions} ${params.applications}"
script{
try {
map = findCommand("XXX.xml")
} catch (Exception e) {
echo "Catching the NonCPS Exception";
}
echo "${map}"
}
build job: "sample echo", parameters: [
[$class: 'StringParameterValue', name: 'application', value: "${params.application}"],
[$class: 'StringParameterValue', name: 'environment', value: "${params.environment}"]
]
}
}
}
post {
failure {
echo "Build failed..."
}
}
}
I know I'm using XmlSlurper, but I've also specified the code-block to be #NonCPS. However, this is still throwing a java.IO.NonSerializableException.
I also tried using a try-catch block to catch the exception, but that didn't seem to work either.
I'd gladly appreciate some help in how I could make the build completely cleanly. Thanks :(

The answer was in the exception. It's quite weird, but changing
components.component.each { def component->
map << ["${component.#application}" : [:]]
def componentMap = map.get("${component.#application}")
component.environments.environment.each { def environment->
componentMap << ["${environment.#name}" : [:]]
def actionMap = componentMap.get("${environment.#name}")
environment.actions.action.each { def action->
actionMap << ["${action.#toDo}" : action]
}
}
}
to
components.component.each { def component->
def application = "${component.#application}".toString()
map << [ (application) : [:]]
def componentMap = map.get(application)
component.environments.environment.each { def environment->
def name = "${environment.#name}".toString()
componentMap << [ (name) : [:]]
def actionMap = componentMap.get(name)
environment.actions.action.each { def action->
def toDo = "${action.#toDo}".toString()
def actionString = action.toString()
actionMap << [ (toDo) : (actionString)]
}
}
}
solved the issue. It had to do with the (non serializable) XMLSlurper data structure type still being carried into my HashMap.

Related

Jenkinsfile: how to concatenate a variable name for the if statement?

def flag_prod1 = true
def flag_prod2 = false
def prods = [
'prod1',
'prod2'
]
pipeline
{
agent { label "j" }
stages
{
stage('Trigger')
{
steps
{
script
{
prods.each
{ prd ->
stage("stage $prd")
{
if (flag_${prd})
{
echo "Do something with $prd..."
}
}
}
}
}
}
}
}
The if statement does not work: if (flag_${prd}).
What should be the syntax of the if statement where part of the variable name needs to be taken from the other variable name?
why not to declare known variable with all possible flags like this:
def prods = [
'prod1',
'prod2'
]
def flags = [
prod1: true,
prod1: false,
]
prods.each{prd->
if( flags[prd] ){
//do something
}
}
or you could try to define all in one:
def prods = [
prod1: [
flag: true
],
prod2: [
flag: false
]
]
for(prd in prods){
if( prd.flag ){
//do smth
}
}
This works (using iterator key/value, and toBoolean() conversion):
def jobs = [
'prod1': true,
'prod2': false
]
pipeline
{
agent { label "j" }
stages
{
stage('Trigger')
{
steps
{
script
{
jobs.each
{
if ("$it.value".toBoolean())
{
stage("Stage $it.key")
{
echo "Do something..."
}
}
}
}
}
}
}
}

How can I return file values in Jenkins choice parameter dropdown list?

I have this output.txt file:
cg-123456
cg-456789
cg-987654
cg-087431
Is it possible to get these values into a jenkins dropdown, like by using choice-parameter or active-choice-reactive parameter?
You can do something like the below.
pipeline {
agent any
stages {
stage ("Get Inputs") {
steps {
script{
script {
def input = input message: 'Please select the choice', ok: 'Ok',
parameters: [
choice(name: 'CHOICE', choices: getChoices(), description: 'Please select')]
}
}
}
}
}
}
def getChoices() {
filePath = "/path/to/file/output.txt"
def choices = []
def content = readFile(file: filePath)
for(def line : content.split('\n')) {
if(!line.allWhitespace){
choices.add(line.trim())
}
}
return choices
}

Jenkins parameters using groovy script

UPDATE
I have a simple pipeline where I want to receive in parameters multiple choices from a file.
In my file I have
#Test1,Accounts
#Test2,Services
#Test3,Accesses
and I want to have all of "#Test1", "#Test2" and "#Test3" in checkboxes as parameters so I would run only the tests selected.
But I'm not understanding what I'm doing wrong.
Pipeline
def code = """tests = getChoices()
return tests
def getChoices() {
def filecontent = readFile "/var/jenkins_home/test.txt"
def stringList = []
for (line in filecontent.readLines()) {stringList.add(line.split(",")[0].toString())}
List modifiedList = stringList.collect{'"' + it + '"'}
return modifiedList
}""".stripIndent()
properties([
parameters([
[$class : 'CascadeChoiceParameter',
choiceType : 'PT_CHECKBOX',
description : 'Select a choice',
filterLength : 1,
filterable : false,
name : 'choice1',
referencedParameters: 'role',
script : [$class : 'GroovyScript',
fallbackScript: [
classpath: [],
sandbox : true,
script : 'return ["ERROR"]'
],
script : [
classpath: [],
sandbox : true,
script : code
]
]
]
])
])
pipeline {
agent {
docker { image 'node:latest' }
}
stages {
stage('Tags') {
steps {
getChoices()
}
}
}
}
def getChoices() {
def filecontent = readFile "/var/jenkins_home/test.txt"
def stringList = []
for (line in filecontent.readLines()) {
stringList.add(line.split(',')[0].toString())
}
List modifiedList = stringList.collect { '"' + it + '"' }
echo "$modifiedList"
return modifiedList
}
With this approach I know I can use multi-select checkboxes because I tried to substitute
def code = """ tests = ["Test1", "Test2", "Test3"]
return tests""".stripIndent()
and I get the output that I wanted.
But when I run my pipeline I get build SUCCESS but always get fallbackScript in my Build parameters checkbox. Can anyone help me out understand what is causing fallbackScript to run always? Thanks :)
If you want to auto-populate build parameters you have to return a list of parameters from your function. When you execute the pipeline the build with parameters will be populated. Note in this was only from the second execution of the pipeline the new parameters will be available. Refer following.
pipeline {
agent any
parameters{
choice(name: 'TESTES', choices: tests() , description: 'example')
}
stages {
stage('Hello') {
steps {
echo 'Hello World'
}
}
}
}
def tests() {
return ["Test01", "Test2", "Test4"]
}
If you want to get user input each time you execute a build you should move your choice parameter into a stage. Please refer to the following.
pipeline {
agent any
stages {
stage('Get Parameters') {
steps {
script{
def choice = input message: 'Please select', ok: 'Next',
parameters: [choice(name: 'PRODUCT', choices: tests(), description: 'Please select the test')]
echo '$choice'
}
}
}
}
}
def tests() {
return ["Test01", "Test2", "Test4"]
}
Update 02
Following is how to read from a file and dynamically create the choice list.
pipeline {
agent any
stages {
stage('Get Parameters') {
steps {
script{
sh'''
echo "#Test1,Accounts" >> test.txt
echo "#Test2,Services" >> test.txt
'''
def choice = input message: 'Please select', ok: 'Next',
parameters: [choice(name: 'PRODUCT', choices: getChoices(), description: 'Please select the test')]
}
}
}
}
}
def getChoices() {
def filecontent = readFile "test.txt"
def choices = []
for(line in filecontent.readLines()) {
echo "$line"
choices.add(line.split(',')[0].split('#')[1])
}
return choices
}

create dsl job inside a job

When I run the job I should create a freestyle job with a parameter name and repo.
I already try this but it doesn't work.
freeStyleJob('seed') {
parameters {
stringParam("GITHUB_REPO_NAME", "", "repo_name")
stringParam("JOB_NAME", "", "name for the job")
}
steps {
dsl {
job('\$DISPLAY_NAME') {
}
}
}
}
You can create a job inside a job by using 'text': https://jenkinsci.github.io/job-dsl-plugin/#path/freeStyleJob-steps-dsl-text
steps {
dsl {
text('job ("name") {}')
}
}
Here is the script that will help you out. It will create a Freestyle Job named example with Build Parameters JOB_NAME and GITLAB_REPOSITORY_URL. This job will have Job DSL Scripts.
freeStyleJob('example') {
logRotator(-1, 10)
parameters{
stringParam('JOB_NAME', '', 'Set the Gitlab repository URL')
stringParam('GITLAB_REPOSITORY_URL', '', 'Set the Gitlab repository URL')
}
steps {
dsl {
external('**/project.groovy')
}
}
}
In project.groovy, I have written my DSL scripts.
project.groovy
import hudson.model.*
def thr = Thread.currentThread()
def build = thr?.executable
def jobname_param = "JOB_NAME"
def resolver = build.buildVariableResolver
def jobname = 'TEST/'+ resolver.resolve(jobname_param)
println "found jobname: '${jobname}'"
def project_url_param = "GITLAB_REPOSITORY_URL"
def resolver2 = build.buildVariableResolver
def project_url = resolver2.resolve(project_url_param)
println "found project_url: '${project_url}'"
def repoUrl = "https://example.com/gitlab/repoA/jenkinsfile-repo.git"
pipelineJob(jobname) {
parameters {
stringParam('GITLAB_REPOSITORY_URL', '', 'Set the Gitlab repository URL')
}
logRotator {
numToKeep(5)
daysToKeep(5)
}
definition {
cpsScm {
scm {
git {
remote {
url(repoUrl)
credentials('gitlab-test')
}
branches('master')
extensions {
cleanBeforeCheckout()
}
}
}
scriptPath("Jenkinsfile")
}
}
}
For more reference:
https://jenkinsci.github.io/job-dsl-plugin/#method/javaposse.jobdsl.dsl.DslFactory.freeStyleJob
https://jenkinsci.github.io/job-dsl-plugin/#method/javaposse.jobdsl.dsl.helpers.step.StepContext.dsl

No such DLS method "style" (MarkupBuilder)

I try to build a string in html-format to send it as an E-Mail in a jenkins-pipeline. I have following bit of code:
import groovy.xml.MarkupBuilder
def writer = new StringWriter()
def html = new MarkupBuilder( writer )
html.table( class: "squishSummary" ) {
style( type: "text/css" ) {
mkp.yield( getTableStyle() )
}
...
}
def getTableStyle() {
....
}
Unfortunately this runs into an error while executing the pipeline: java.lang.NoSuchMethodError: No such DSL method 'style' found among steps [...] If I'm understanding it correctly the jenkins-server tries to execute the style-command as a jenkins build-step, what reasonably fails. How can I fix this?
the following code works fine:
import groovy.xml.MarkupBuilder
def writer = new StringWriter()
def html = new MarkupBuilder( writer )
html.table( class: "squishSummary" ) {
style( type: "text/css" ) {
mkp.yield("123")
}
}
println writer
however if you declare variable named style then you get the error similar to yours
so this code will fail with groovy.lang.MissingMethodException: No signature of method ...
import groovy.xml.MarkupBuilder
def writer = new StringWriter()
def html = new MarkupBuilder( writer )
def style = "my style1" //just add this line and code fails
html.table( class: "squishSummary" ) {
style( type: "text/css" ) {
mkp.yield("123")
}
}
println writer
to solve problem use delegate or GString instead of plain method name:
import groovy.xml.MarkupBuilder
def writer = new StringWriter()
def html = new MarkupBuilder( writer )
def style = "style1"
html.table( class: "squishSummary" ) {
delegate.style( type: "text/css" ) {
mkp.yield("123")
}
//or like this:
"${'style'}"( type: "text/css" ) {
mkp.yield("123")
}
}
println writer

Resources