Update build parameter in declarative pipeline - jenkins

I need to update my build param in declarative pipeline script, I tried:
pipeline {
stages {
stage {
steps {
script {
def reporter = build.buildVariableResolver.resolve("reporter")
if (reporter != null) {
reporter = reporter.tokenize(',').find {item -> item.contains('displayName')}.tokenize('=')[1]
} else {
reporter = ""
}
def reporterParameter = new StringParameterValue("reporter", "\${reporter}")
build.addOrReplaceAction(new ParametersAction(reporterParameter))
}
}
}
}
}
but I get error hudson.remoting.ProxyException: groovy.lang.MissingPropertyException: No such property: build for class: WorkflowScript
How can I run this groovy script in declarative pipeline or upadate my build params in another way (but declaratively)

Related

Jenkins file groovy issues

Hi My jenkins file code is as follows : I am basically trying to make call to a python script and execute it, I have defined some variables in my code : And when i am trying to run it, It gives no such property error in the beginning and I cant find out the reason behind it.
I would really appreciate any suggestions on this .
import groovy.json.*
pipeline {
agent {
label 'test'
}
parameters {
choice(choices: '''\
env1
env2'''
, description: 'Environment to deploy', name: 'vpc-stack')
choice(choices: '''\
node1
node2'''
, description: '(choose )', name: 'stack')
}
stages {
stage('Tooling') {
steps {
script {
//set up terraform
def tfHome = tool name: 'Terraform 0.12.24'
env.PATH = "${tfHome}:${env.PATH}"
env.TFHOME = "${tfHome}"
}
}
}
stage('Build all modules') {
steps {
wrap([$class: 'BuildUser']) {
// build all modules
script {
if (params.refresh) {
echo "Jenkins refresh!"
currentBuild.result = 'ABORTED'
error('Jenkinsfile refresh! Aborting any real runs!')
}
sh(script: """pwd""")
def status_code = sh(script: """PYTHONUNBUFFERED=1 python3 scripts/test/test_script.py /$vpc-stack""", returnStatus: true)
if (status_code == 0) {
currentBuild.result = 'SUCCESS'
}
if (status_code == 1) {
currentBuild.result = 'FAILURE'
}
}
}
}
}
}
post {
always {
echo 'cleaning workspace'
step([$class: 'WsCleanup'])
}
}
}
And this code is giving me the following error :
hudson.remoting.ProxyException: groovy.lang.MissingPropertyException: No such property: vpc for class
Any suggestions what can be done to resolve this.
Use another name for the choice variable without the dash sign -, e.g. vpc_stack or vpcstack and replace the variable name in python call.

Value returned from a script does not assigned to a variable declared in jenkins declarative pipeline stage

I am working on adding a jenkins Declarative pipeline for automation testing. In the test run stage i want to extract the failed tests from the log. i am using a groovy function for extracting the test result. this function is not a part of the jenkins pipeline. It is another script file. The function works fine and it build a string containing the failure details. Inside a pipeline stage i am calling this function and assinging the returned string to another variable. But when i echo the variable value it prints empty string.
pipeline {
agent {
kubernetes {
yamlFile 'kubernetesPod.yml'
}
}
environment{
failure_msg = ""
}
stages {
stage('Run Test') {
steps {
container('ansible') {
script {
def notify = load('src/TestResult.groovy')
def result = notify.extractTestResult("${WORKSPACE}/testreport.xml")
sh "${result}"
if (result != "") {
failure_msg = failure_msg + result
}
}
}
}
}
post {
always {
script {
sh 'echo Failure message.............${failure_msg}'
}
}
}
}
here 'sh 'echo ${result}'' print empty string. But 'extractTestResult()' returns a non-empty string.
Also i am not able to use the environment variable 'failure_msg' in post section it return an error 'groovy.lang.MissingPropertyException: No such property: failure_msg for class: groovy.lang.Binding'
can anyone please help me with this ?
EDIT:
Even after i fixed the string interpolation, i was getting the same
error. That was because jenkins does not allow using 'sh' inside
docker container. there is an open bug ticket in jenkins issue
board
I would suggest to use a global variable for holding the error message. My guess is that the variable is not existing in your scope.
def FAILURE_MSG // Global Variable
pipeline {
...
stages {
stage(...
steps {
container('ansible') {
script {
...
if (result != "") {
FAILURE_MSG = FAILURE_MSG + result
}
}
}
}
}
post {
always {
script {
sh "${FAILURE_MSG}" // Hint: Use correct String Interpolation
}
}
}
}
(Similar SO question can be found here)

Why does this declarative pipeline fail in my shared library?

I have a shared library containing a declarative pipeline used in a repo Jenkinsfile (it is in fact called through an intermediate): Jenkinsfile -> bupJavadocApiPipeline.groovy -> bupMavenPipeline.groovy
Jenkinsfile (shared library is implicit):
bupJavadocApiPipeline {}
bupJavadocApiPipeline.groovy:
def call(body, Map defaults = [:]) {
if (defaults.mavenGoals == null) defaults.mavenGoals = 'javadoc:javadoc package'
bupMavenPipeline(body,defaults)
}
bupMavenPipeline.groovy (bupParameters does the DELEGATE_FIRST trick):
def call(body, defaults = [:]) {
if (defaults.maven == null) defaults.maven='MVN3'
if (defaults.mavenGoals == null) defaults.mavenGoals='package'
if (defaults.jdk == null) defaults.jdk='JDK8'
if (defaults.buildsToKeep == null) defaults.buildsToKeep='10'
def parameters = bupParameters(body,defaults)
pipeline {
options {
timestamps()
buildDiscarder(logRotator(numToKeepStr: "${parameters.buildsToKeep}"))
}
agent ('docker') {
tools {
maven "${parameters.maven}"
jdk "${parameters.maven}"
}
stages {
stage ('Build') {
steps {
sh "mvn -Dmaven.test.failure.ignore=true clean ${parameters.mavenGoals}"
}
post {
success {
junit '**/target/surefire-report/**/*.xml'
}
}
}
}
}
}
}
This fails with:
[Pipeline] Start of Pipeline
[Pipeline] End of Pipeline
java.lang.NoSuchMethodError: No such DSL method 'options' found among steps [...
Versions are Jenkins 2.162 and Pipeline 2.6, but so many web resources say this is supported since September 2017!
I can make it all work if I only do scripted pipeline in bupMavenPipeline.groovy, but I like the "safety" of declarative (and there seem to be many more resources on that then on scripted).
Can you see what is tripping me up?
By inlining into the actual Jenkinsfile Jenkins kindly helped me to identify the problem:
agent ('docker') { ... }
should be
agent { label 'docker' }

Last successful build's revision for an upstream MultiBranch Job in Jenkins Declarative Pipeline

I'd like to get the build revisions of the last successful builds of Upstream jobs. The upstream jobs are multibranch jobs.
So far I'm generating a list of upstream jobs' names as triggers. But I can't seem to find the right method to call.
import jenkins.model.Jenkins
def upstreamPackages = ['foo', 'bar']
def upstreamJobs = upstreamPackages.collect { "${it}-multibranch/master" }.join(',')
pipeline {
agent none
triggers {
upstream(upstreamProjects: upstreamJobs,
threshold: hudson.model.Result.SUCCESS)
}
stages {
stage('test'){
steps{
script {
upstreamJobs.each {
println it
job = Jenkins.instance.getItem(it)
job.getLastSuccessfulBuild()
revision = job.getLastSuccessfulBuild().changeset[0].revision
println revision
}
}
}
}
}
}
This results in a null object for item. What's the correct way to do this?
UPDATE 1
After discovering the Jenkins Script Console and this comment, I managed to come up with the folllowing:
import jenkins.model.Jenkins
import hudson.plugins.git.util.BuildData
def upstreamPackages = ['foo', 'bar']
def upstreamJobsList = upstreamPackages.collect { "${it}-multibranch/master" }.join(',')
#NonCPS
def resolveRequirementsIn(packages){
BASE_URL = 'git#github.com:myorg'
requirementsIn = ''
packages.each { pkg ->
revision = getLastSuccessfulBuildRevision("${pkg}-multibranch")
requirementsIn <<= "-e git+${BASE_URL}/${pkg}.git#${revision}#egg=${pkg}\n"
}
println requirementsIn
return requirementsIn
}
#NonCPS
def getLastSuccessfulBuildRevision(jobName){
project = Jenkins.instance.getItem(jobName)
masterJob = project.getAllItems().find { job -> job.getName() == 'master' }
build = masterJob.getLastSuccessfulBuild()
return build.getAction(BuildData.class).getLastBuiltRevision().sha1String
}
pipeline {
agent { label 'ci_agent' }
triggers {
upstream(upstreamProjects: upstreamJobsList,
threshold: hudson.model.Result.SUCCESS)
}
stages {
stage('Get artifacts'){
steps{
script{
requirementsIn = resolveRequirementsIn upstreamPackages
writeFile file: 'requirements.in', text: requirementsIn
}
}
}
}
}
It's throwing an error:
an exception which occurred:
in field org.jenkinsci.plugins.pipeline.modeldefinition.withscript.WithScriptScript.script
in object org.jenkinsci.plugins.pipeline.modeldefinition.agent.impl.LabelScript#56d1724
in field groovy.lang.Closure.delegate
in object org.jenkinsci.plugins.workflow.cps.CpsClosure2#27378d57
in field groovy.lang.Closure.delegate
in object org.jenkinsci.plugins.workflow.cps.CpsClosure2#6e6c3c4e
in field org.jenkinsci.plugins.workflow.cps.CpsThreadGroup.closures
in object org.jenkinsci.plugins.workflow.cps.CpsThreadGroup#5d0ffef3
in object org.jenkinsci.plugins.workflow.cps.CpsThreadGroup#5d0ffef3
Caused: java.io.NotSerializableException:
org.jenkinsci.plugins.workflow.multibranch.WorkflowMultiBranchProject
The problem was that Jenkins' Pipeline DSL requires all assigned objects to be Serializable.
Jenkins.instance.getItem(jobName) returns a WorkflowMultiBranchProject which is not Serializable. Neither is Jenkins.instance.getItem(jobName).getItem('master') which is a WorkflowJob object.
So I invariably went down the call chain to what I needed replacing variable assignments with chained method calls and came up with the following solution.
def upstreamPackages = ['foo', 'bar']
def upstreamJobsList = upstreamPackages.collect { "${it}-multibranch/master" }.join(',')
def String requirementsInFrom(packages){
final BASE_URL = 'git#github.com:myorg'
requirementsIn = ''
packages.each{ pkg ->
revision = Jenkins.instance.getItem("${pkg}-multibranch")
.getItem('master')
.getLastSuccessfulBuild()
.getAction(BuildData.class)
.getLastBuiltRevision()
.sha1String
requirementsIn <<= "-e git+${BASE_URL}/${pkg}.git#${revision}#egg=${pkg}\n"
}
return requirementsIn.toString()
}

How to handle nightly build in Jenkins declarative pipeline

I have a multibranch pipeline with a Jenkinsfile in my repo and I am able to have my CI workflow (build & unit tests -> deploy-dev -> approval -> deploy-QA -> approval -> deploy-prod) on every commit.
What I would like to do is add SonarQube Analysis on nightly builds in the first phase build & unit tests.
Since my build is triggerd by Gitlab I have defined my pipeline triggers as follow :
pipeline {
...
triggers {
gitlab(triggerOnPush: true, triggerOnMergeRequest: true, branchFilterType: 'All')
}
...
}
To setup my nightly build I have added
triggers {
...
cron('H H * * *')
}
But now, how to execute analysis step if we are only building the job triggered by the cron expression at night ?
My simplified build stage looks as follow :
stage('Build & Tests & Analysis') {
// HERE THE BEGIN SONAR ANALYSIS (to be executed on nightly builds)
bat 'msbuild.exe ...'
bat 'mstest.exe ...'
// HERE THE END SONAR ANALYSIS (to be executed on nightly builds)
}
There is the way how to get build trigger information. It is described here:
https://jenkins.io/doc/pipeline/examples/#get-build-cause
It is good for you to check this as well:
how to get $CAUSE in workflow
Very good reference for your case is https://hopstorawpointers.blogspot.com/2016/10/performing-nightly-build-steps-with.html. Here is the function from that source that exactly matches your need:
// check if the job was started by a timer
#NonCPS
def isJobStartedByTimer() {
def startedByTimer = false
try {
def buildCauses = currentBuild.rawBuild.getCauses()
for ( buildCause in buildCauses ) {
if (buildCause != null) {
def causeDescription = buildCause.getShortDescription()
echo "shortDescription: ${causeDescription}"
if (causeDescription.contains("Started by timer")) {
startedByTimer = true
}
}
}
} catch(theError) {
echo "Error getting build cause"
}
return startedByTimer
}
This works in declarative pipeline
when {
triggeredBy 'TimerTrigger'
}
For me the easiest way is to define a cron in build trigger and verify the hour on the nightly stage using a when expression:
pipeline {
agent any
triggers {
pollSCM('* * * * *') //runs this pipeline on every commit
cron('30 23 * * *') //run at 23:30:00
}
stages {
stage('nightly') {
when {//runs only when the expression evaluates to true
expression {//will return true when the build runs via cron trigger (also when there is a commit at night between 23:00 and 23:59)
return Calendar.instance.get(Calendar.HOUR_OF_DAY) in 23
}
}
steps {
echo "Running the nightly stage only at night..."
}
}
}
}
You could check the build cause like so:
stage('Build & Tests & Analysis') {
when {
expression {
for (Object currentBuildCause : script.currentBuild.rawBuild.getCauses()) {
return currentBuildCause.class.getName().contains('TimerTriggerCause')
}
}
steps {
bat 'msbuild.exe ...'
bat 'mstest.exe ...'
}
}
}
However, this requires the following entries in script-approval.xml:
<approvedSignatures>
<string>method hudson.model.Run getCauses</string>
<string>method org.jenkinsci.plugins.workflow.support.steps.build.RunWrapper getRawBuild</string>
</approvedSignatures>
This can also be approved via https://YOURJENKINS/scriptApproval/.
Hopefully, this won't be necessary after JENKINS-41272 is fixed.
Until then, a workaround could be to check the hour of day in the when expression (keep in mind that these times refer to to the timezone of Jenkins)
when { expression { return Calendar.instance.get(Calendar.HOUR_OF_DAY) in 0..3 } }
I've found a way, which does not use "currentBuild.rawBuild" which is restricted. Begin your pipeline with:
startedByTimer = false
def buildCauses = "${currentBuild.buildCauses}"
if (buildCauses != null) {
if (buildCauses.contains("Started by timer")) {
startedByTimer = true
}
}
Test the boolean where you need it, for example:
stage('Clean') {
when {
anyOf {
environment name: 'clean_build', value: 'Yes'
expression { (startedByTimer == true) }
}
}
steps {
echo "Cleaning..."
...
Thanks to this you can now do this without needing to the the use the non-whitelisted currentBuild.getRawBuild().getCauses() function which can give you Scripts not permitted to use method org.jenkinsci.plugins.workflow.support.steps.build.RunWrapper getRawBuild depending on your setup:
#NonCPS
def isJobStartedByTimer() {
def startedByTimer = false
try {
def buildCauses = currentBuild.getBuildCauses()
for ( buildCause in buildCauses ) {
if (buildCause != null) {
def causeDescription = buildCause.shortDescription
echo "shortDescription: ${causeDescription}"
if (causeDescription.contains("Started by timer")) {
startedByTimer = true
}
}
}
} catch(theError) {
echo "Error getting build cause"
}
return startedByTimer
}

Resources