How to use correctly cps notation in Jenkins pipeline - jenkins

I am trying to write some code in Jenkins, but my knowledge is quite limited, I need to read some information (the commiter for that CL) from a xml file that Perforce generates(SCM) I then use that information I get, in another function to send an email in case the static analysis finds an error, the thing is I keep getting the expected to call WorkflowScript.sendEmailNewErrors but wound up catching readJSON Error, I have gone through the CPS documentation The output tells me but honestly it is still not clear to me what is wrong. My pipeline would be something like this:
import groovy.json.JsonSlurper
#NonCPS
def sendEmailNewErrors(){
def submitter = findItemInChangelog("changeUser")
def Emails = readJSON file: "D:/Emails.json"
def email = "test#email.com"
def msgList = []
def url = "http://localhost:8080/job/job1/1374/cppcheck/new/api/json"
def json = new JsonSlurper().parseText(new URL(url).text)
for (key in Emails.keySet()) {
if (submitter == key){
email = Emails.get(key)
}
}
json.issues.each{issue->
def msg = "New ERROR found in static analysis, TYPE OF ERROR ${issue.type}"+
", SEVERITY: ${issue.severity}, ERROR MESSAGE: ${issue.message}"+
", FILE ${issue.fileName} AT LINE: ${issue.lineStart}"
msgList.add(msg)
}
msgList.each{msg->
println msg
mail to: email,
subject: "New errors found in job1 build pipeline",
body: "$msg"
}
}
#NonCPS
def findItemInChangelog(item){
def result = "Build ran manually"
def found = false
def file = new XmlSlurper().parse("C:/Users/User/.jenkins/jobs/job1/builds/1374/changelog5966810591791724161.xml")
file.entry.each { entry ->
entry.changenumber.each { changenumber ->
changenumber.children().each { tag ->
if(tag.name() == item && found != true){
result = tag.text()
found = true
}
}
}
}
return result.toString()
}
pipeline {
agent any
stages {
stage("test"){
steps{
script{
sendEmailNewErrors()
}
}
}
}
}
I have tried without CPS notation but I understand if using .each method the notation has to be used. Anyone with more experience with this is able to help?

Related

Grab available artifact versions in jenkins parameters

I want to have an ability in Jenkins build fetch available artefacts versions (i use Artifactory to store code packed in .zip) and put them as a dropdown list, so i can select the version i want to use with this build.
Could you give an example how to do this best way?
To do so, i use jenkins plugin Active Choices and add reactive parameter into the job. This gives me an ability to select what Environment to use, and based on it fetch available artifacts from artifactory
Job configuration:
Groovy code used in "Active choice" parameters:
import groovy.json.JsonSlurper
import java.util.regex.Matcher;
import java.util.regex.Pattern;
Pattern pattern = Pattern.compile("((?:develop|master|function)_(?:latest|[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+).*)")
def repository_dev = "https://repo.nibr.novartis.net/artifactory/api/storage/nibr-generic/intuence_discovery/idaw_health_checker/develop/"
def repository_tst = "https://repo.nibr.novartis.net/artifactory/api/storage/nibr-generic/intuence_discovery/idaw_health_checker/release/"
def repository_prd = "https://repo.nibr.novartis.net/artifactory/api/storage/nibr-generic/intuence_discovery/idaw_health_checker/master/"
try {
if (DEPLOY_TO == "dev") {
versions = "curl -s $repository_dev"
}
else if (DEPLOY_TO == "tst") {
versions = "curl -s $repository_tst"
}
else if (DEPLOY_TO == "prd") {
versions = "curl -s $repository_prd"
}
def proc = versions.execute()
proc.waitFor()
def output = proc.in.text
def jsonSlurper = new JsonSlurper()
def artifactsJsonObject = jsonSlurper.parseText(output)
def dataArray = artifactsJsonObject.children
List<String> artifacts = new ArrayList<String>()
for(item in dataArray) {
Matcher m = pattern.matcher(item.uri)
while (m.find()) {
artifacts.add(m.group());
}
}
return artifacts
}
catch (Exception e) {
return ["There was a problem fetching the artifacts", e]
}

Bulk update parameter in jobs

We have a lot of jobs that all perform SCM checkout based on a Build Parameter value: say REPO_URL=ssh://url. Over time there accumulated small differences in names and values of these parameters: REPOURL, repo_url, =ssh://url/, =ssh://url:port, etc.
We need to reduce them to a common denominator with a single parameter name and a single value. How do we bulk update parameters in 50+ jobs?
Using Jenkins Script Console.
NOTE: these are essentially destructive operations, so make sure you tested your code on some spare jobs before running it in production!!!
Change default value of a parameter
Jenkins.instance.getAllItems(Job)
// filter jobs by name if needed
.findAll { it.fullName.startsWith('sandbox/tmp-magic') }
.each {
it
.getProperty(ParametersDefinitionProperty)
.getParameterDefinition('MAGIC_PARAMETER')
// `each` ensures nothing happens if `get` returns null; also see paragraph below
.each {
it.defaultValue = 'shmagic'
}
// the job has changed, but next config reload (f.x. at restart) will overwrite our changes
// so we need to save job config to its config.xml file
it.save()
}
Instead of .getParameterDefinition('MAGIC_PARAMETER') you can use
.parameterDefinitions
.findAll { it.name == 'MAGIC_PARAMETER' }
, changing predicate in findAll if you need f.x. to change value of multiple parameters with different names - then you iterate over found definitions via each{}.
Change parameter name (and value)
This is slightly more tricky, since apparently you cannot edit name of ParameterDefinition, only replace one in a list.
Jenkins.instance.getAllItems(Job)
.findAll { it.fullName.startsWith('sandbox/tmp-magic') }
.each {
def parameters = it.getProperty(ParametersDefinitionProperty).parameterDefinitions
def oldParameter = parameters.find { it.name == 'FOO' }
// avoid changing jobs without this parameter
if (!oldParameter)
return
def idx = parameters.indexOf(oldParameter)
// preserve original value if necessary
def oldValue = oldParameter.defaultValue
parameters[idx] = new StringParameterDefinition('GOOD_FOO', oldValue)
it.save()
}
Bonus points: replace value for SCM step in Freestyle and Pipeline From SCM job
Some of our jobs use MercurialSCM plugin, and some use MultiSCM plugin to checkout multiple repos, so this is what I tested it with.
import hudson.plugins.mercurial.MercurialSCM
import org.jenkinsci.plugins.multiplescms.MultiSCM
import org.jenkinsci.plugins.workflow.cps.CpsScmFlowDefinition
import org.jenkinsci.plugins.workflow.job.WorkflowJob
Jenkins.instance.getAllItems(Job)
.findAll { it.fullName.startsWith('sandbox/tmp-magic') }
.each {
print "Checking $it ... "
if (it.class == FreeStyleProject && it.scm) {
println "Freestyle"
it.scm = replaceWhateverScm(it.scm)
it.save()
} else if (it.class == WorkflowJob) {
print "Pipeline ... "
def flow = it.definition
if (flow.class == CpsScmFlowDefinition) {
println "ScmFlow"
def scm = replaceWhateverScm(flow.scm)
def newFlow = new CpsScmFlowDefinition(scm, flow.scriptPath)
newFlow.lightweight = flow.lightweight
it.definition = newFlow
it.save()
} else
println "unsupported definition"
} else
println "unsupported job"
}
def replaceWhateverScm(scm) {
if (scm.class == MercurialSCM) {
println "replacing MercurialSCM"
return replaceMercurialSource(scm)
}
if (scm.class == MultiSCM) {
println "replacing MultiSCM"
// cannot replace part of MultiSCM, replace whole scm instead
return new MultiSCM(
scm.configuredSCMs
.collect { (it.class == MercurialSCM) ? replaceMercurialSource(it) : it }
)
}
throw new Exception("unknown class ${scm.class}")
}
def replaceMercurialSource(MercurialSCM original) {
if (!original.source.toLowerCase().contains('repo_url'))
return original
def s = new MercurialSCM('<new_url>')
for (v in ["browser","clean","credentialsId","disableChangeLog","installation","modules","revision","revisionType","subdir",]) {
s."$v" = original."$v"
}
return s
}```

fetch source values from jenkins extended choice parameter

I have added an extended choice paramter. Now the source values are lin1, lin2, lin3 as listed in screenshot
now when I run,
If I select lin1 then I get param3 = lin1,
If I select lin1 and lin2 then I get param2 - lin1,lin2 ( delimiter is comma )
The question here is, inside jenkins pipeline how can get what all source values were set when the param was created. In short, without selecting any of the checkboxes, want to get the list of the possible values probably in a list
Eg:
list1 = some_method(param3)
// expected output >> list1 = [lin,lin2,lin3]
Let me know if this description is not clear.
The user who runs this does not have configure access ( we dont want to give configure access to anonynmous user ) Hence the job/config.xml idea will not work here
As requested you can also get the values dynamically:
import hudson.model.*
import org.jenkinsci.plugins.workflow.job.*
import com.cwctravel.hudson.plugins.extended_choice_parameter.ExtendedChoiceParameterDefinition
def getJob(name) {
def hi = Hudson.instance
return hi.getItemByFullName(name, Job)
}
def getParam(WorkflowJob job, String paramName) {
def prop = job.getProperty(ParametersDefinitionProperty.class)
for (param in prop.getParameterDefinitions()) {
if (param.name == paramName) {
return param
}
}
return null
}
pipeline {
agent any
parameters {
choice(name: 'FOO', choices: ['1','2','3','4'])
}
stages {
stage('test') {
steps {
script {
def job = getJob(JOB_NAME)
def param = getParam(job, "FOO")
if (param instanceof ChoiceParameterDefinition) {
// for the standard choice parameter
print param.getChoices()
} else if (param instanceof ExtendedChoiceParameterDefinition) {
// for the extended choice parameter plugin
print param.getValue()
}
}
}
}
}
}
As you can see it requires a lot of scripting, so just must either disable the Groovy sandbox or approve most of the calls on the script approval page.
I couldn't find any variable or method to get the parameter list. I guess it's somehow possible through a undocumented method on the param or currentBuild maps.
A possible solution to your problem could be defining the map outside of the pipeline and then just use that variables like this:
def param3Choices = ['lin1', 'lin2', 'lin3']
pipeline {
parameters {
choice(name: 'PARAM3', choices: param3Choices, description: '')
}
stage('Debug') {
steps {
echo param.PARAM3
print param3Choices
}
}
}

Caused: java.io.NotSerializableException: org.jenkinsci.plugins.scriptsecurity.scripts.ScriptApproval$PendingScript

In my declarative Jenkins pipeline I have added code which should approve scripts as suggested here:
...
} catch (Exception jobFailed) {
if (jobFailed.getMessage() == "script not yet approved for use") {
echo("[WARNING] Changes in delivery job were automatically approved")
approveDeliveryJob()
return false
}
...
#NonCPS
def approveDeliveryJob() {
toApprove = ScriptApproval.get().getPendingScripts().collect()
toApprove.each { pending -> ScriptApproval.get().approveScript(pending.getHash())} }
}
...
As suggested here
to solve this put all the code that works with non serializable variables into #NonCPS annotated function
What I am missing?
toApprove = ScriptApproval.get().getPendingScripts().collect()
Here you are storing the result in the script binding, which is affected by serialization.
You need a local variable instead:
def toApprove = ScriptApproval.get().getPendingScripts().collect()

How to trigger multiple down stream jobs in jenkins dynamically based on some input parameter

Scenario: I want to trigger few down stream jobs(Job A and Job B ....) dynamically based on the input parameter received by the current job.
import hudson.model.*
def values = ${configname}.split(',')
def currentBuild = Thread.currentThread().executable
println ${configname}
println ${sourceBranch}
values.eachWithIndex { item, index ->
println item
println index
def job = hudson.model.Hudson.instance.getJob(item)
def params = new StringParameterValue('upstream_job', ${sourceBranch})
def paramsAction = new ParametersAction(params)
def cause = new hudson.model.Cause.UpstreamCause(currentBuild)
def causeAction = new hudson.model.CauseAction(cause)
hudson.model.Hudson.instance.queue.schedule(job, 0, causeAction, paramsAction)
}
How about something like this? I was getting a comma separated list from the upstream system and I splitted them as individaul string which is internally jobs. Making a call by passing each individual strings.
this Jenkinsfile would do that:
#!/usr/bin/env groovy
pipeline {
agent { label 'docker' }
parameters {
string(name: 'myHotParam', defaultValue: '', description: 'What is your param, sir?')
}
stages {
stage('build') {
steps {
script {
if (params.myHotParam == 'buildEverything') {
build 'mydir/jobA'
build 'mydir/jobB'
}
}
}
}
}
}

Resources