Getting error while deleting Jenkins build history - jenkins

Hello All I am getting below error while deleting build history of jenkins jobs
No build logs found in calcmanager-pull-request and build history count is : 0
[Pipeline] End of Pipeline
java.lang.NullPointerException: Cannot invoke method getLastBuild() on null object
Below is the pipeline script I am using. calcmanager-pull-request job contains build history but it is displaying as zero
pipeline {
agent { label 'master' }
stages {
stage('delete all jobs build history'){
steps {
script {
def buildLogCount=10
Jenkins.instance.getAllItems(Job.class).each { jobitem ->
def jobName = jobitem.name
def jobInfo = Jenkins.instance.getItem(jobName)
if(jobInfo.getLastBuild()){
def lastBuildNum=jobInfo.getLastBuild().getNumber()
if(lastBuildNum>=buildLogCount){
def deletedBuildNum=lastBuildNum - buildLogCount
def deletedNumRange = Fingerprint.RangeSet.fromString("0-${deletedBuildNum}",false);
def buildCount=jobInfo.getBuilds(deletedNumRange).size()
if(buildCount==0){
println "No build logs found in ${jobName} and build history count is : ${buildCount}"
}
else{
jobInfo.getBuilds(deletedNumRange).each { item ->
item.delete()
}
}
}
else{
println "No build logs to delete in ${jobName}"
}
}
else{
println "No build logs found in ${jobName}"
}
}
}
}
}
}
}
}

You got that error due to some of jobs was never run. It doesn't have history.
So you can build a List with jobs with history:
Jenkins.instance.getAllItems(Job.class)
.findAll { it.getLastBuild()?.getNumber() } // if job doesn't not history it will return null == false
.each { job ->
def lastBuildNum=job.getLastBuild().getNumber()
/// you code is here
}

Above code works fine if you don't have any multi branch pipeline jobs. If you have multibranch pipeline jobs then use below code in jenkins file.
properties(
[
buildDiscarder(logRotator(daysToKeepStr: '60', numToKeepStr: '7')),
]
)

Related

How to get Jenkins build status without running build

How can I get the status of already completed builds?
The code below starts the build, but I just need to know the build status by name.
I need to find out the names of the failed builds in order to send the data in one message to the mail.
Map buildResults = [:]
Boolean failedJobs = false
void nofify_email(Map results) {
echo "TEST SIMULATE notify: ${results.toString()}"
}
Boolean buildJob(String jobName, Map results) {
def jobBuild = **build job: jobName**, propagate: false
def jobResult = jobBuild.getResult()
echo "Build of '${jobName}' returned result: ${jobResult}"
results[jobName] = jobResult
return jobResult == 'SUCCESS'
}
pipeline {
agent any
stages {
stage('Parallel Builds') {
steps {
parallel(
"testJob1": {
script {
if (!buildJob('testJob1', buildResults)) {
failedJobs = true
}
}
},
)
}
}
I couldn't find anything to replace build job

Jenkins Pipeline - build same job multiple times in parallel

I am trying to create a pipeline in Jenkins which triggers same job multiple times in different node(agents).
I have "Create_Invoice" job Jenkins, configured : (Execute Concurrent builds if necessary)
If I click on Build 10 times it will run 10 times in different (available) agents/nodes.
Instead of me clicking 10 times, I want to create a parallel pipeline.
I created something like below - it triggers the job but only once.
What Am I missing or is it even possible to trigger same test more than once at the same time from pipeline?
Thank you in advance
node {
def notifyBuild = { String buildStatus ->
// build status of null means successful
buildStatus = buildStatus ?: 'SUCCESSFUL'
// Default values
def tasks = [:]
try {
tasks["Test-1"] = {
stage ("Test-1") {
b = build(job: "Create_Invoice", propagate: false).result
}
}
tasks["Test-2"] = {
stage ("Test-2") {
b = build(job: "Create_Invoice", propagate: false).result
}
}
parallel tasks
} catch (e) {
// If there was an exception thrown, the build failed
currentBuild.result = "FAILED"
throw e
}
finally {
notifyBuild(currentBuild.result)
}
}
}
I had the same problem and solved it by passing different parameters to the same job. You should add parameters to your build steps, although you obviously don't need them. For example, I added a string parameter.
tasks["Test-1"] = {
stage ("Test-1") {
b = build(job: "Create_Invoice", parameters: [string(name: "PARAM", value: "1")], propagate: false).result
}
}
tasks["Test-2"] = {
stage ("Test-2") {
b = build(job: "Create_Invoice", parameters: [string(name: "PARAM", value: "2")], propagate: false).result
}
}
As long as the same parameters or no parameters are passed to the same job, the job is only tirggered once.
See also this Jenkins issue, it describes the same problem:
https://issues.jenkins.io/browse/JENKINS-55748
I think you have to switch to Declarative pipeline instead of Scripted pipeline.
Declarative pipeline has parallel stages support which is your goal:
https://www.jenkins.io/blog/2017/09/25/declarative-1/
This example will grab the available agent from the Jenkins and iterate and run the pipeline in all the active agents.
with this approach, you no need to invoke this job from an upstream job many time to build on a different agent. This Job itself will manage everything and run all the stages define in all the online node.
jenkins.model.Jenkins.instance.computers.each { c ->
if(c.node.toComputer().online) {
node(c.node.labelString) {
stage('steps-one') {
echo "Hello from Steps One"
}
stage('stage-two') {
echo "Hello from Steps Two"
}
}
} else {
println "SKIP ${c.node.labelString} Because the status is : ${c.node.toComputer().online} "
}
}

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 run trigger another job outside pipeline

I have three jobs which are in pipeline. Whenever anyone fails due to an internal account lock these have to trigger post-build action.In POst build action i mentioned Trigger when build is failed. I wrote a robot test to unlock the account and I wrote a shell script to call this test.
I am calling this template in both jobs in post-build action and building it on the same node.But what i found is this post build action is kept in pending state and jenkins is triggering downstream project. How to make Jenkins to run post build action when the current job fails?
How to achieve that?
You can play with the seed job's propagate property.
Simple example:
Map jobResults = [:]
pipeline {
agent any
stages {
stage('Build seedjob 1') {
steps {
script {
String seedJobName = 'testjob1'
def seedJob = build job: seedJobName, propagate: false
jobResults[seedJobName] = seedJob.result
echo "Result of ${seedJobName}: ${seedJob.result}"
}
}
}
stage('Build seedjob 2') {
steps {
script {
String seedJobName = 'testjob2'
def seedJob = build job: seedJobName, propagate: false
jobResults[seedJobName] = seedJob.result
echo "Result of ${seedJobName}: ${seedJob.result}"
}
}
}
}
post {
success {
script {
if(jobResults['testjob1'] == 'FAILURE') {
echo "Running another job"
build job: 'another-job1', propagate: true
}
if(jobResults['testjob2'] == 'FAILURE') {
echo "Running another job"
build job: 'another-job2', propagate: true
}
}
}
}
}

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