How to get number of failed builds by cause with Jenkins - jenkins

I have the Build Failure Analyzer plugin installed in my Jenkins instance, and I have a number of different failures entered into the plugin. Does anyone know if it is possible to get the total number of failures across all jobs that have the same cause?
For example, I occasionally get "ChannelClosedException" failures if the build node goes offline during a build or test unexpectedly and I would like to determine how often this is happening across all my jobs. Is there some way to aggregate this value? I imagine it could be done through groovy if you can iterate over each build for each job and collect the Build Failure cause if one is detected.
Has anyone else done something like this before?

Not the exact answer... but should be able to modify to get what you are looking for:
Jenkins.instance.getAllItems(Job).each{
def jobBuilds=it.getBuilds()
//for each job get the things (you can limit at your convenience)
jobBuilds.each { build ->
def runningSince = groovy.time.TimeCategory.minus( new Date(), build.getTime() )
def currentStatus = build.buildStatusSummary.message
def cause = build.getCauses()[0] //we keep the first cause
def user = cause instanceof Cause.UserIdCause? cause.getUserId():""
println "Build: ${build} | Since: ${runningSince} | Status: ${currentStatus} | Cause: ${cause} | User: ${user}"
def parameters = build.getAction(ParametersAction)?.parameters
parameters.each {
println "Type: ${it.class} Name: ${it.name}, Value: ${it.dump()}"
}
}
}

Using some google-fu and some of the info from VinDev, I came up with this solution:
// get all jobs in Jenkins
Jenkins.instance.getAllItems(Job).each {
// get all builds for each job
def jobBuilds=it.getBuilds()
//for each build, get the name and status + and failure messages
jobBuilds.each { build ->
// get the build status
def currentStatus = build.buildStatusSummary.message
// we only care about the broken builds because we want failure messages
if (currentStatus.contains("broken")) {
println "Build: ${build} | Status: ${currentStatus}"
def BFA = build.actions.find{ it instanceof com.sonyericsson.jenkins.plugins.bfa.model.FailureCauseBuildAction };
if (BFA != null) {
for (failureCause in BFA.getFoundFailureCauses()) {
println("name: " + failureCause.getName() + ", description: " + failureCause.getDescription())
}
}
}
}
}

Related

From deprecated cliOnline to OnlineNodeCommand

So I created this groovy script but read that the cliOnline() command is deprecated.
But I can't seem to figure out how to actually change my code to use hudson.model.Hudson.instance.OnlineNodeCommand()?
deprecated cliOnline
def nodes_checklist = ["PD100069", "PD100070", "PD100090", "PD10756"]; // List of nodes which should be turned online
def jenkinsNodes = jenkins.model.Jenkins.instance.getNodes() // Get all existing nodes on this Jenkins URL
for(def node_checker: nodes_checklist) {
for(def node: jenkinsNodes) {
if(node.getNodeName().contains(node_checker)) {
println "The node " + node.getNodeName() + "'s offline status: " + node.toComputer().isOffline()
if (node.toComputer().isOffline()){
println "Turning " + node.getNodeName() + " online"
node.toComputer().cliOnline() // If node is offline, turn it online
println node.getNodeName() + "'s online status: " node.toComputer().isOnline()
}
}
}
}
Does anyone know how to rewrite this to use the non-deprecated version?
If you look at this depricated method, it simply calls a non depricated method setTemporarilyOffline(boolean temporarilyOffline, OfflineCause cause). So not sure why this was depricated. Anyway instead of using cliOnline() you can use setTemporarilyOffline. Check the following.
node.getComputer().setTemporarilyOffline(false, null)
Some proper code with a proper cause. Cause is not really needed when setting the node online though.
import hudson.slaves.OfflineCause.UserCause
def jenkinsNodes = Jenkins.instance.getNodes()
for(def node: jenkinsNodes) {
if (node.getComputer().isTemporarilyOffline()){
node.getComputer().setTemporarilyOffline(false, null)
}
}
Setting to temporarily offline
UserCause cause = new UserCause(User.current(), "This is a automated process!!")
node.getComputer().setTemporarilyOffline(true, cause)

How can I find out if current Jenkins build is first run of the day from trigger

I have Jenkins jobs that trigger twice a day and I would like to know if the current build is the first cron trigger of the day or not and do some action.
My cron job is as below
triggers {
// regression --> 3:00GMT, 14:00GMT
cron("00 3 * * 1-5 \n 00 14 * * 1-5")
}
Can I set some boolean param in my Jenkins file to check if it's the first trigger of the day?
The simplest option would be to check the build history. If the previous build was executed on the previous day, then the current build is the first build of the day. The logic must be defined in the executed job configurations.
The currentBuild object is an instance of the org.jenkinsci.plugins.workflow.support.steps.build.RunWrapper class which provides all necessary information.
steps {
echo "The first build of the day started by trigger: ${isFirstBuildOfDayStartedByTrigger(currentBuild)}"
}
// ...
boolean isFirstBuildOfDayStartedByTrigger(currentBuild) {
if (isStartedByTrigger(currentBuild)) {
return false
}
def today = toLocalDate(currentBuild.startTimeInMillis)
def build = currentBuild.previousBuild
while(build != null) {
if (toLocalDate(build.startTimeInMillis).isBefore(today)) {
return true
}
if (isStartedByTrigger(build)) {
return false
}
build = build.previousBuild
}
return true
}
LocalDate toLocalDate(long millis) {
return Instant.ofEpochMilli(millis).atZone(ZoneId.systemDefault()).toLocalDate()
}
boolean isStartedByTrigger(build) {
// TODO: use build.buildCauses or build.getBuildCauses('cause.class.Name')
// to analyze if the job was started by trigger
return true // or false
}
You have to figure out which build cause is added when the job is started by trigger.
If you just want to find the first build of the day executed by anything or anyone, then the code is much simpler:
steps {
echo "The first build of the day: ${isFirstBuildOfDay(currentBuild)}"
}
boolean isFirstBuildOfDay(currentBuild) {
def today = toLocalDate(currentBuild.startTimeInMillis)
def previousBuild = currentBuild.previousBuild
return previousBuild == null || toLocalDate(previousBuild.startTimeInMillis).isBefore(today)
}
LocalDate toLocalDate(long millis) {
return Instant.ofEpochMilli(millis).atZone(ZoneId.systemDefault()).toLocalDate()
}
I used the new date API which I think is not whitelisted, so you have to put that code to the Jenkins library or approve the used method signatures.
Found the answer, it's simple but working fine for me.
First I am checking if this is a scheduled job or not and the current hour is less than 5 (scheduled job runs before 5)
def isItFirstScheduledJob = (params.JOB_IS_SCHEDULED && new Date().getHours() < 5) ? true : false

How to list culprit users when Jenkins build fails (sending by Slack)

How I can list all culprits users related to the broken builds since the last successful build and the current one?
Besides that, how to compile all this information and then send by Slack?
The script below describes how to configure the post processing step to send culprits users from history of broken builds.
#!groovy​
pipeline {
agent { label 'pipeline-maven'}
post {
failure {
script {
def userDetailsService = load("get-users-details.groovy")
env.slack_msg = userDetailsService.getFailedBuildHistory()
}
slackSend baseUrl: 'https://xxx.slack.com/services/hooks/jenkins-ci/',
channel: '#xxxx',
color: 'bad',
token: 'aIPJis6V4P9VOpTFhUtCQRRL',
message: "Broken build ${currentBuild.fullDisplayName}\n${slack_msg}"
}
}
}
The script below (get-users-details.groovy) is responsible to enumerate all culprits based on the previous broken build history.
import jenkins.model.Jenkins
def String getFailedBuildHistory() {
def message = ""
// Iterate over previous broken builds to find culprits
def fullName = "pipeline-test"
def jobData = Jenkins.instance.getItemByFullName(fullName)
def lastStableBuild = jobData.getLastStableBuild()
def lastBuildNumber = jobData.getLastBuild().getNumber() - 1 // We subtract the current executing build from the list
if (lastStableBuild != null && lastStableBuild.getNumber() != lastBuildNumber) {
def culpritsSet = new HashSet();
message += "Responsibles:\n"
// From oldest to newest broken build, since the last sucessful build, find the culprits to notify them
// The list order represents who is more responsible to fix the build
for (int buildId = lastStableBuild.getNumber() + 1; buildId <= lastBuildNumber; buildId++) {
def lastBuildDetails = Jenkins.getInstance().getItemByFullName(fullName).getBuildByNumber(buildId)
if (lastBuildDetails != null) {
lastBuildDetails.getCulpritIds().each({ culprit ->
if (!culpritsSet.contains(culprit)) {
message += " ${culprit} (build ${lastBuildDetails.getNumber()})\n"
culpritsSet.add(culprit)
}
})
}
}
}
// Complement the message with information from the current executing build
if (currentBuild.getCurrentResult() != 'SUCCESS') {
def culprits = currentBuild.changeSets.collectMany({ it.toList().collect({ it.author }) }).unique()
if (culprits.isEmpty()) {
// If there is no change log, use the build executor user
def name = currentBuild.getBuildCauses('hudson.model.Cause$UserIdCause').userName
message += " ${name} (current build ${currentBuild.getId()})"
} else {
// If there is change log, use the committer user
culprits.each({ culprit ->
message += " ${culprit} (current build ${currentBuild.getId()})"
})
}
}
return message
}
return [
getFailedBuildHistory: this.&getFailedBuildHistory
]

How to get list of individuals who broke the build in Jenkins Pipeline

Is there a way to retrieve a list of individuals who broke the build in Jenkins Pipeline, just like the mailer plugin apparently does to send out a mail to those involved?
I've found one possible way, though it is somewhat limited to the amount of builds that are being kept. By iterating over the previousBuild hierarchy and their change logs, this may be a solution:
def getAuthors(def build) {
def userIds = []
build.changeSets.each { hudson.scm.SubversionChangeLogSet changeLogSet ->
userIds += changeLogSet.collect { it.author.id }
}
userIds.unique()
}
def getIndividualsWhoBrokeTheBuild() {
def userIds = []
for(def build = currentBuild; build.result != 'SUCCESS'; build = build.previousBuild) {
userIds += getAuthors(build)
}
userIds.unique()
}
Suppose a job keeps only five builds, this won't return the original felon, if it was broken more than five builds before.

extract parameters from a jenkins previous build

I am working on Jenkins version 2.32.1 pipeline. I want to extract the parameters that were chosen in the previous build of my job.
In our previous Jenkins instance ( 1.627 ) we were using jenkins.model.Jenkins.instance.getItem(job).lastBuild.getBuildVariables().get(param);
For some reason this is not working in this version (I also tried disabling the sandbox).
Any pointers on how to accomplish it?
Simplified version of the previous script:
def build = Jenkins.get().getItems(org.jenkinsci.plugins.workflow.job.WorkflowJob).find {it.displayName == 'YOUR_JOB_NAME_HERE'}?.getLastBuild()
build.actions.find{ it instanceof ParametersAction }?.parameters.each {echo "${it.name}=${it.value}"}
Actually a little bit shorter version for those who want to get the params for the current build from the previous run and is working on new 2+ Jenkins versions.
To get 1 particular parameter:
def cls = currentBuild.getPreviousBuild().getRawBuild().actions.find{ it instanceof ParametersAction }?.parameters.find{it.name == 'cls'}?.value
Get all params respectfully:
def cls = currentBuild.getPreviousBuild().getRawBuild().actions.find{ it instanceof ParametersAction }?.parameters
Something like this might work, based on https://stackoverflow.com/a/19564602/3920342:
def h = hudson.model.Hudson.instance
def r = null
h.getItems(org.jenkinsci.plugins.workflow.job.WorkflowJob).each {project ->
if (project.displayName.equals('YOUR_JOB_NAME')) {
r = project
}
}
r.getBuilds().findAll { b -> // here we loop over all past builds, apply some filter if necessary
def p = b?.actions.find{ it instanceof ParametersAction }?.parameters
p.each {
echo "parameter ${it.name}: ${it.value}"
}
}
For those who are not able to access getActions() due to admin permissions i.e. facing the following error:
Scripts not permitted to use method hudson.model.Actionable getActions
They can copy the parameter variables to the env and get them using build.previousBuild.buildVariables
stage('Prepare environment') {
steps {
script {
env.MY_PARAM_COPY = "${MY_PARAM}"
}
}
}
println("MY_PARAM in previous build: ${currentBuild.previousBuild.buildVariables["MY_PARAM_COPY"]}")
That's how I made it works, answer from #dan.goriaynov and #jherb caused some CPS closure issues for me.
(the gist of the code is to allow only greater TAG number than the previous one to be deployed)
stage('Validate build number') {
def previous_build = currentBuild.getPreviousBuild().getRawBuild();
def PREVIOUS_TAG = '';
for (int i = 0; i < previous_build.allActions.size(); i++) {
if (previous_build.allActions[i] in hudson.model.ParametersAction) {
PREVIOUS_TAG = previous_build.allActions[i].getParameter("TAG").value
}
}
if (PREVIOUS_TAG.toInteger() > TAG.toInteger()) {
echo PREVIOUS_TAG
error('TAG number needs to be greater than the previous one')
}
}

Resources