I have a Jenkins Multijob project with a very simple structure:
I want to set the Multijob status as follows:
I know I can use a Groovy post build action with a script such as that below, but I don't know how to set the required threshold levels:
void log(msg) {
manager.listener.logger.println(msg)
}
threshold = Result.SUCCESS
void aggregate_results() {
failed = false
mainJob = manager.build.getProject().getName()
job = hudson.model.Hudson.instance.getItem(mainJob)
log '-------------------------------------------------------------------------------------'
log 'Aggregated status report'
log '-------------------------------------------------------------------------------------'
log('${mainJob} #${manager.build.getNumber()} - ${manager.build.getResult()}')
job.getLastBuild().getSubBuilds().each { subBuild->
subJob = subBuild.getJobName()
subJobNumber = subBuild.getBuildNumber()
job = hudson.model.Hudson.instance.getItem(subBuild.getJobName())
log '${subJob} #${subJobNumber} - ${job.getLastCompletedBuild().getResult()}'
log job.getLastCompletedBuild().getLog()
//println subBuild
dePhaseJob = hudson.model.Hudson.instance.getItem(subBuild.getJobName())
dePhaseJobBuild = dePhaseJob.getBuildByNumber(subBuild.getBuildNumber())
dePhaseJobBuild.getSubBuilds().each { childSubBuild ->
try {
log ' ${childSubBuild.jobName}'
job = hudson.model.Hudson.instance.getItem(childSubBuild.getJobName())
build = job.getBuildByNumber(childSubBuild.getBuildNumber())
indent = ' '
log '${indent} #${build.getNumber()} - ${build.getResult()}'
log build.getLog()
if(!failed && build.getResult().isWorseThan(threshold) ) {
failed = true
}
} catch (Exception e) {
log('ERROR: ${e.getMessage()}')
failed = true
}
}
}
if(failed) {manager.build.setResult(hudson.model.Result.FAILURE)}
}
try {
aggregate_results()
} catch(Exception e) {
log('ERROR: ${e.message}')
log('ERROR: Failed Status report aggregation')
manager.build.setResult(hudson.model.Result.FAILURE)
}
Can anyone help tweak the script to achieve what I need?
Not sure if this really qualifies as an answer. Might be more of a comment but comments do not really lend themselves to long code snippets so here goes.
To make your code a tad more readable and easier to grok I did the following:
build.getResult()
becomes build.result
log('ERROR: ${e.getMessage()}')
becomes log 'ERROR: ${e.getMessage()}'
log 'ERROR: ${e.message}'
becomes log "ERROR: ${e.message}"
subJob = ...
becomes def subJob = ...
. Declaring everything in the global scope leads to hard-to-find issues, especially if you are re-using variable names like job
.I also cleaned out some reduncancies, an example:
job.getLastBuild().getSubBuilds().each { subBuild->
subJob = subBuild.getJobName()
subJobNumber = subBuild.getBuildNumber()
job = hudson.model.Hudson.instance.getItem(subBuild.getJobName()) // <---
...
//println subBuild
dePhaseJob = hudson.model.Hudson.instance.getItem(subBuild.getJobName()) // <---
dePhaseJobBuild = dePhaseJob.getBuildByNumber(subBuild.getBuildNumber())
so here we set both job
and dePhaseJob
to the same value. Assigning the same value to two separate variables like this is redundant and only makes the code harder to read.
Furthermore (and I'm not intimately familiar with the jenkins internal apis so I might be wrong here) the following flow in the above code seems off:
subBuild
instancejob
and dePhaseJob
dePhaseJobBuild
using dePHaseJob.getBuildByNumber(subBuild.buildNumer)
but doesn't that leave us with subBuild == dePhaseJobBuild
? I.e. we spent all this code just to retrieve a value we already had. We go from build to job and back to build. Unless I'm missing something esoteric in the jenkins apis this seems redunant as well.
With all those changes and a few other minor ones we und up with the following code:
def job(name) {
hudson.model.Hudson.instance.getItem(name)
}
def aggregateResults() {
def mainJobName = manager.build.project.name
log '-------------------------------------------------------------------------------------'
log 'Aggregated status report'
log '-------------------------------------------------------------------------------------'
log "${mainJobName} #${manager.build.number} - ${manager.build.result}"
def failed = false
job(mainJobName).lastBuild.subBuilds.each { subBuild ->
log "${subBuild.jobName} #${subBuild.buildNumber} - ${subBuild.result}"
log subBuild.log
subBuild.subBuilds.each { subSubBuild ->
try {
log " ${subSubBuild.jobName} #${subSubBuild.buildNumber} - ${subSubBuild.result}"
log " " + subSubBuild.getLog(Integer.MAX_VALUE).join("\n ") //indent the log lines
if(!failed && subSubBuild.result.isWorseThan(threshold)) {
failed = true
}
} catch (Exception e) {
log "ERROR: ${e.message}"
failed = true
}
}
}
if(failed) {
manager.build.result = hudson.model.Result.FAILURE
}
}
and again, I don't have a jenkins instance to test this on so I'm flying in the dark here and apologize in advance for misspellings, syntax fumbles or other abuse of the code and the jenkins apis.
The issues in your code (like the string interpolation one which I can't see ever having worked) makes me think that the original code was not working but rather an example pattern.
This makes me further wonder if you really need to do two levels of nesting here, i.e. is the following:
job(mainJobName).lastBuild.subBuilds.each { subBuild ->
subBuild.subBuilds.each { subSubBuild ->
...
}
}
really necessary or would one level be enough? From the quick graph in your question it would seem that we only need to care about the main job and its sub jobs, not sub-sub jobs.
If this is the case, you could get away with logic along the lines of:
def aggregateResults() {
def mainJob = job(manager.build.project.name)
def subs = mainJob.lastBuild.subBuilds
def total = subs.size()
def failed = subs.findAll { sub -> sub.result.isWorseThan(threshold) }.size()
if(failed > 0) {
manager.build.result = hudson.model.Result.FAILURE
}
failed == 0 ? "green" : (failed/total < 0.25 ? "yellow" : "red")
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With