How to run groovy script to read build cause? - jenkins

I am writing a first groovy script in the Jenkins, have an upstream job A which calls job B.
Being in job B I need to read GERRIT_CHANGE_NUMBER which triggered the job A.
In below e.g, how to get 28331 in the downstream job B?, its printed in the job B's console as below:
Started by upstream project some_up_project build number 100
originally caused by:
Triggered by Gerrit: https://gerrit-server.com/28331
I looked at this SO answer, but not sure how to do this in jenkins.
In job B, I did Add build step to add Execute system Groovy script section, then chose Groovy command in its dropdown, and in the Groovy Script area, added below for testing purpose, it gives error as unable to resolve class Run.cause ..., tried many other ways too and nothing worked.
import hudson.model.Run
for (cause in Run.getCauses()) {
if (cause instanceof Run.Cause.UserIdCause) {
println cause.getUserName()
}
}

there is no such class Run.Cause
start from something that works: hudson.model.Run
search for the documentation: hudson.model.Run.getCauses()
the method returns: List<Cause>
so, import this class into your code and use it:
import hudson.model.Cause
import hudson.model.Run
for (cause in Run.getCauses()) {
if (cause instanceof Cause.UserIdCause) {
println cause.getUserName()
}
}
Note: I have not tested the code. I just gave you an idea how to resolve an error.

Related

Failures in init.groovy.d scripts: null values returned

I'm trying to get Jenkins set up, with configuration, within a Docker environment. Per a variety of sources, it appears the suggested method is to insert scripts into JENKINS_HOME/init.groovy.d. I've taken scripts from places like the Jenkins wiki and made slight changes. They're only partially working. Here is one of them:
import java.util.logging.ConsoleHandler
import java.util.logging.FileHandler
import java.util.logging.SimpleFormatter
import java.util.logging.LogManager
import jenkins.model.Jenkins
// Log into a file
println("extralogging.groovy")
def RunLogger = LogManager.getLogManager().getLogger("hudson.model.Run")
def logsDir = new File("/var/log/jenkins")
if (!logsDir.exists()) { logsDir.mkdirs() }
FileHandler handler = new FileHandler(logsDir.absolutePath+"/jenkins-%g.log", 1024 * 1024, 10, true);
handler.setFormatter(new SimpleFormatter());
RunLogger.addHandler(handler)
This script fails on the last line, RunLogger.addHandler(handler).
2019-12-20 19:25:18.231+0000 [id=30] WARNING j.util.groovy.GroovyHookScript#execute: Failed to run script file:/var/lib/jenkins/init.groovy.d/02-extralogging.groovy
java.lang.NullPointerException: Cannot invoke method addHandler() on null object
I've had a number of other scripts return NULL objects from various gets similar to this one:
def RunLogger = LogManager.getLogManager().getLogger("hudson.model.Run")
My goal is to be able to develop (locally) a Jenkins implementation and then hand it to our sysops guys. Later, as I add pipelines and what not, I'd like to be able to also work on them in a local Jenkins configuration and then hand something for import into production Jenkins.
I'm not sure how to produce API documentation so I can chase this myself. Maybe I need to stop doing it this way and just grab the files that get modified when I do this via the GUI and just stuff the files into the right place.
Suggestions?

Jenkins console script different results

I am trying to get last successful build on multibranch pipeline job, and only what I came to is this
def str = Jenkins.instance.getAllItems() .findAll{ it instanceof Job }.collect{ it.getLastSuccessfulBuild() }.findAll{ it } .sort{ it.timestamp }.grep(~/iOS.*/).last().toString()
println str.substring(str.lastIndexOf('/') + 1).replace('%2F', '_').replace(' #', '_')
but unfortunately, this only works in script console of Jenkins, and throwing different results in pipeline, any guesses what could be wrong?
Jenkins version 2.176.1
I expect the output of:
feature_INC-777_77
But getting:
java.util.GregorianCalendar[time=1566913952011,areFieldsSet=true,areAllFieldsSet=true,lenient=true,zone=sun.util.calendar.ZoneInfo[id="Europe/Moscow",offset=10800000,dstSavings=0,useDaylight=false,transitions=79,lastRule=null],firstDayOfWeek=1,minimalDaysInFirstWeek=1,ERA=1,YEAR=2019,MONTH=7,WEEK_OF_YEAR=35,WEEK_OF_MONTH=5,DAY_OF_MONTH=27,DAY_OF_YEAR=239,DAY_OF_WEEK=3,DAY_OF_WEEK_IN_MONTH=4,AM_PM=1,HOUR=4,HOUR_OF_DAY=16,MINUTE=52,SECOND=32,MILLISECOND=11,ZONE_OFFSET=10800000,DST_OFFSET=0]
For pipeline like:
import jenkins.model.*
import hudson.model.*
import jenkins.*
import hudson.*
def str = Jenkins.instance.getAllItems().findAll{ it instanceof Job }.collect{ it.getLastSuccessfulBuild() }.findAll{ it }.sort{it.timestamp}
println str
Can someone help with this, please?
ah this beautiful groovy + Jenkins combo...
put your statement into a method and add annotation on top #NonCPS
While I will be in trouble to get a clear full explanation, in short - jenkins push all the code in your scripted pipeline via super-complicated (as for me) set of CPS transforms - https://github.com/cloudbees/groovy-cps/ , which may or may not convert groovy API properly. Rule of thumb - when using closures - use #NonCPS annotation, otherwise you will have a ton of weird errors that are hard to debug.
more info:
https://wiki.jenkins.io/display/JENKINS/Pipeline+CPS+method+mismatches
https://github.com/jenkinsci/workflow-cps-plugin
What is the effect of #NonCPS in a Jenkins pipeline script

How to get last successful build from jenkins job using hudson classes and groovy

I am trying to get the last successful build from a job in jenkins so that when another condition is meet details from that build will then be passed to another job. I am trying to use Hudson's Run class since it has methods to get the last successful build, but I can't get it to compile. Since all my builds have failed, for now I am using getPreviousFailedBuild() instead, but it will be swapped for successful build later. Currently I have this:
import hudson.model.Run.*
def lastFailedBuild = getPreviousFailedBuild()
currently I get this error message when I try and run my script
Caught: groovy.lang.MissingMethodException: No signature of method: hudson7682146427918660061.getPreviousFailedBuild() is applicable for argument types: () values: []
at hudson7682146427918660061.run(hudson7682146427918660061.groovy:5)
I was getting errors with the import statement till I added the .* at the end, and I also tried calling the method like this:
Run.getPreviousFailedBuild() However that would result in a no such property Run exception instead.
What do I need to have in order to access my previous builds Hudson classes so I can send on the info I need to.
use execute system groovy script
def lastSuccess = build.getProject().getLastSuccessfulBuild()
import jenkins.model.Jenkins
def test_job = Jenkins.instance.getItemByFullName("My-Test-Dev")
last_sucessful_build_number=test_job.getLastSuccessfulBuild().getNumber()
println last_sucessful_build_number

Using the ez-template plugin for Jenkins through the Jenkins Job DSL doesn't apply the template after creation

I am working on automating the creation of Jenkins jobs by using the Jenkins Job DSL (Groovy). Right now, I am trying to automate the creation of a job that uses the ez-template plugin to use an already existing template and apply that to my newly created job. However, after I am done writing the necessary configuration:
job('foo') {
properties {
templateImplementationProperty {
exclusions(['ez-templates', 'job-params', 'disabled', 'description'])
syncAssignedLabel(true)
syncBuildTriggers(true)
syncDescription(false)
syncDisabled(false)
syncMatrixAxis(true)
syncOwnership(true)
syncScm(true)
syncSecurity(true)
templateJobName('template')
}
}
}
the job gets created alright... except the template is never applied until AFTER I manually hit the save button on the UI in the newly created job. Checking the config.xml of the created job I can see that the xml contains the configuration I specified, but it was never applied.
Looking at the ez-template code, I can see that this is due to the silentSave feature that was implemented in that plugin - it writes configuration to disk without triggering any save events.
I've tried methods available to the Jenkins API but I've had no success there. Any ideas on how I can apply my configuration?
Full disclosure: I'm a co-worker, and was able to help shredmasteryjm solve this. I figured it'd be best to put this out on the net for others.
The Groovy code needed to trigger template implementation contents to be updated is:
import hudson.model.*;
import jenkins.model.*;
import com.joelj.jenkins.eztemplates.utils.TemplateUtils;
import com.joelj.jenkins.eztemplates.TemplateImplementationProperty;
Jenkins j = Jenkins.getInstance()
Item job = j.getItemByFullName('foo')
TemplateImplementationProperty template = TemplateUtils.getTemplateImplementationProperty(job)
TemplateUtils.handleTemplateImplementationSaved(job, template)
This utilizes the EZ-Templates TemplateUtils class to trigger the actual save event, using the template that the job uses. Of note, if job 'foo' doesn't implement a template, then the 'template' variable will be null, causing this code to error. YMMV
In our case, we needed to also add in some useful information from another question: Access to build environment variables from a groovy script in a Jenkins build step ( Windows)
in order to utilize a parameterized job name. As such our completed script looks like this:
import hudson.model.*;
import jenkins.model.*;
import com.joelj.jenkins.eztemplates.utils.TemplateUtils;
import com.joelj.jenkins.eztemplates.TemplateImplementationProperty;
// get current thread / Executor
def thr = Thread.currentThread()
// get current build
def build = thr?.executable
def hardcoded_param = "parameter_job_name"
def resolver = build.buildVariableResolver
def hardcoded_param_value = resolver.resolve(hardcoded_param)
Jenkins j = Jenkins.getInstance()
Item job = j.getItemByFullName(hardcoded_param_value)
TemplateImplementationProperty template = TemplateUtils.getTemplateImplementationProperty(job)
TemplateUtils.handleTemplateImplementationSaved(job, template)
FYI ez-templates 1.3.0 now triggers off additional save events such that you do not need the above trick.

How can I set the job timeout for all jobs using the Jenkins DSL

I read How can I set the job timeout using the Jenkins DSL. That sets the timeout for one job. I want to set it for all jobs, and with slightly different settings: 150%, averaged over 10 jobs, with a max of 30 minutes.
According to the relevant job-dsl-plugin documentation I should use this syntax:
job('example-3') {
wrappers {
timeout {
elastic(150, 10, 30)
failBuild()
writeDescription('Build failed due to timeout after {0} minutes')
}
}
}
I tested in http://job-dsl.herokuapp.com/ and this is the relevant XML part:
<buildWrappers>
<hudson.plugins.build__timeout.BuildTimeoutWrapper>
<strategy class='hudson.plugins.build_timeout.impl.ElasticTimeOutStrategy'>
<timeoutPercentage>150</timeoutPercentage>
<numberOfBuilds>10</numberOfBuilds>
<timeoutMinutesElasticDefault>30</timeoutMinutesElasticDefault>
</strategy>
<operationList>
<hudson.plugins.build__timeout.operations.FailOperation></hudson.plugins.build__timeout.operations.FailOperation>
<hudson.plugins.build__timeout.operations.WriteDescriptionOperation>
<description>Build failed due to timeout after {0} minutes</description>
</hudson.plugins.build__timeout.operations.WriteDescriptionOperation>
</operationList>
</hudson.plugins.build__timeout.BuildTimeoutWrapper>
</buildWrappers>
I verified with a job I edited manually before, and the XML is correct. So I know that the Jenkins DSL syntax up to here is correct.
Now I want to apply this to all jobs. First I tried to list all the job names:
import jenkins.model.*
jenkins.model.Jenkins.instance.items.findAll().each {
println("Job: " + it.name)
}
This works too, all job names are printed to console.
Now I want to plug it all together. This is the full code I use:
import jenkins.model.*
jenkins.model.Jenkins.instance.items.findAll().each {
job(it.name) {
wrappers {
timeout {
elastic(150, 10, 30)
failBuild()
writeDescription('Build failed due to timeout after {0} minutes')
}
}
}
}
When I push this code and Jenkins runs the DSL seed job, I get this error:
ERROR: Type of item "jobname" does not match existing type, item type can not be changed
What am I doing wrong here?
The Job-DSL plugin can only be used to maintain jobs that have been created by that plugin before. You're trying to modify the configuration of jobs that have been created in some other way -- this will not work.
For mass-modification of existing jobs (like, in your case, adding the timeout) the most straightforward way is to change the job's XML specification directly,
either by changing the config.xml file on disk, or
using the REST or CLI API
xmlstarlet is a powerful tool for performing such tasks directly on shell level.
Alternatively, it is possible to perform the change via a Groovy script from the "Script Console" -- but for that you need some understanding of Jenkins' internal workings and data structures.

Resources