How to get the summay text after the jenkins pipeline job complete - jenkins

In my one jenkins pipeline job, I use createSummary of post build plugin to add some log url on the jenkins job UI. For the files moved to somewhere else, so I want to change the url. Is there a way to reset the information for this build?
manager.createSummary("red/yellow/green.gif")
.appendText("""log""", false, false, false, "black")
Any tips/advice are appriciated, thanks a lot!

I finally solved it using BadgeSummaryAction.
Jenkins.instance.getItemByFullName(jobName).builds.findAll { it.number > startnumber &&
it.number < endnumber}.each {
List<Action> actions = it.getActions();
List<BadgeSummaryAction> summaryActions = it.getActions(BadgeSummaryAction.class);
String iconText = "green.gif"
for(int i =0; i<summaryActions.size();i++) {
BadgeSummaryAction currentAction = summaryActions.get(i);
String sActionText = currentAction.getText()
if(sActionText.contains("FAILED")){
iconText = "red.gif"
}else if(sActionText.contains("UNSTABLE")){
iconText = "yellow.gif"
}
actions.remove(currentAction);
BadgeSummaryAction summary = new BadgeSummaryAction(iconText)
summary.appendText(replaceText, false);
it.addAction(summary);
}//end of for
}

Related

Schedule Jenkins job using Jenkins Rest API

I have a Jenkins job which is scheduled for a specific time. I want to modify that timing programmatically.
I tried to modify the build by installing Schedule Build plugin and modify it using http://jenkins_url/job/jobname/build?delay=3344sec. But this will put the job in quiet period which holds the java thread. I'm looking to modify the Schedule entry without putting it to quiet period.
You can use the Build Triggers -> Build periodically job configuration option. Use that to specify the exact time for starting a new build.
If you need to change that time, use the Jenkins REST API to...
programmatically retrieve the job configuration in XML format, then
modify the scheduling time in that configuration (see below)
re-post the new job configuration
In bash, this can be done with a one-liner (using curl and sed) to modify the XML section below (the example schedules a run for noon, Feb 29):
[...]
<triggers>
<hudson.triggers.TimerTrigger>
<spec>00 12 29 02 * </spec>
</hudson.triggers.TimerTrigger>
</triggers>
[...]
Note:
as a plus you wouldn't depend on any supplementary plugins
caveat: you cannot specify a year in the schedule -- so if you need to schedule builds more than one year in advance then you need some magic on top.
I can't get it to work, but the source code for the plugin references a "schedule" url action and a "date" param.
I tried something like:
http://localhost:8080/job/jobname/job/develop/schedule?date=2020-02-20
Which it didn't reject but I can't see a build.
below is the source code of the action performed when the button is pressed to schedule:
var newRequest = function() {
if (window.XMLHttpRequest) {
return new XMLHttpRequest();
} else {
return new ActiveXObject("Microsoft.XMLHTTP");
}
}
var sumbitScheduleRequest = function(absoluteUrl, quietPeriodInSeconds, isJobParameterized){
if(isJobParameterized){
// if job has parameters, redirect to build page, so user can set parameters
window.location = absoluteUrl + "build?delay=" + quietPeriodInSeconds + "sec";
}else{
// if job has NO parameters, submit build directly
var csrfCrumb;
var csrfRequest = newRequest();
csrfRequest.onreadystatechange = function() {
if (csrfRequest.readyState === 4) {
if (csrfRequest.status === 200 || csrfRequest.status === 201) {
csrfCrumb = JSON.parse(csrfRequest.responseText);
} else {
// csrf might be deactivated
}
// do the actual submit
var xmlhttp = newRequest();
xmlhttp.onreadystatechange = function() {
if (xmlhttp.readyState === 4) {
if (xmlhttp.status === 200 || xmlhttp.status === 201) {
window.location = absoluteUrl;
return false;
} else {
window.location = absoluteUrl;
return false;
}
}
};
xmlhttp.open("POST", absoluteUrl + "build?delay=" + quietPeriodInSeconds + "sec", true);
if (csrfCrumb) {
xmlhttp.setRequestHeader(csrfCrumb.crumbRequestField, csrfCrumb.crumb)
}
xmlhttp.send();
}
};
csrfRequest.open('GET', rootURL + '/crumbIssuer/api/json', false);
csrfRequest.send();
}
}

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
]

Groovy syntax for grabbing build periodically property

I need to grab the "Build Triggers -> Build Periodically" values using a groovy script. I need to check if it is enabled and what the Schedule values are but I have no luck while searching through Jenkins' github and the javadoc.jenkins api documentation.
List keys = new ArrayList(job.getTriggers().keySet());
for (int i = 0; i < keys.size(); i++) {
Object obj = keys.get(i);
if(obj.getDisplayName().contains("Build periodically")) {
println job.fullName + "," + job.getTriggers().get(keys.get(i)).spec
}
}

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')
}
}

Jenkins Workflow Parallel step and closures

I'm trying to run a few suits with tests in parallel as a part of workflow. So I create a map, put there a couple of closures and pass it to parallel step. The issue that I'm facing related to name of the HTML report. After execution, I see a few identical "HTML Report" links, so I can't open specific report - all of them have the same name. I had tried to make this name unique, but these attempts were not successful. Does anyone faced similar thing?
def testExecutions = [:]
def testExecution = {
node {
//code to run tests
publishHTML(target: [allowMissing: false,
alwaysLinkToLastBuild: false,
keepAll: true, reportDir: 'target/reports',
reportFiles: 'index.html',
reportName: "HTML Report " + it)
}
}
for (int i = 0; i < 2; i++) {
final k = i
testExecutions.put("tests $k", {testExecution(k)})
}
parallel(testExecutions)
Maybe the testExecution() function lacks the it parameter?
def testExecution(it) = {
node {
....
}
}
It seems that your node is on the wrong level. Now this rises a really interesting question.
My guess would be that when your workers would actually get the node to execute k or it would have be already evaluated by the master, meaning that from the point of view of parallel call the closure implicit argument would always be equal to 1. (although I consider myself not educated enough with Jenkins workflow and groovy to say for sure)
This version should work for you
def testExecutions = [:]
def testExecution = {
println "HTML Report " + it
}
for (int i = 0; i < 2; i++) {
final k = i
testExecutions.put("tests $k", {node{testExecution(k)}})
}
parallel(testExecutions)

Resources