How to handle exceptions in a Jenkins Job DSL seed job? - jenkins

If I had a Git repository full of Job DSL groovy scripts and a typical seed job e.g.:
job('seed') {
//... scm, triggers etc.
steps {
dsl {
external 'jobs/**/*.groovy'
}
}
//... more config etc.
}
what happens if just one of the job dsl scripts throws an exception for some reason, for example:
job('deliberate-fail') {
throw new Exception("Arrrgggghhh")
}
Is it possible to handle this exception in the seed job or will the whole seed job fail?
If all but one would work - is it possible for the seed job to record an UNSTABLE result rather than FAILURE?
I don't really want one bad apple to spoil the bunch.

Based on Opal's suggestion to use a try-catch, I modifed the job to capture the exception and print an error to the console.
job('deliberate-fail') {
try {
throw new Exception("Arrrgggghhh")
} catch (Exception ex){
println("deliberate-fail job is [UNSTABLE]")
}
}
As I am currently using the Job DSL plugin (and not a Jenkins Pipeline script), I don't think Opal's suggestion to use "currentBuild.result = 'UNSTABLE'" was available to me. After a little digging I found I could use the Text-Finder plugin to search the console for the "[UNSTABLE]" error and change the seed job state accordingly.
job('seed-job') {
steps {
dsl {
external '**/*_jobdsl.groovy'
}
}
publishers {
textFinder(/[UNSTABLE]/, '', true, false, true)
}
}
A bit convoluted but it seems to work!

Related

How do I use `when` condition on a file's last modified time in Jenkins pipeline syntax

I am creating a Jenkins pipeline, I want certain stage to be triggered only when a particular log file's(log file is located in the server node where all the stages are going to run) last modified date is updated after the initiation of pipeline job, I understand we need to use "When" condition but not really sure how to implement it.
Tried referring some of the pipeline related portals but could not able to find an answer
Can some please help me through this?
Thanks in advance!
To get data about file is quite tricky in a Jenkins pipeline when using the Groovy sandbox since you're not allowed to do new File(...).lastModified. However there is the findFiles step, which basically returns a list of wrapped File objects with a getter for last modified time in millis, so we can use findFiles(glob: "...")[0].lastModified.
The returned array may be empty, so we should rather check on that (see full example below).
The current build start time in millis is accessible via currentBuild.currentBuild.startTimeInMillis.
Now that we git both, we can use them in an expression:
pipeline {
agent any
stages {
stage("create file") {
steps {
touch "testfile.log"
}
}
stage("when file") {
when {
expression {
def files = findFiles(glob: "testfile.log")
files && files[0].lastModified < currentBuild.startTimeInMillis
}
}
steps {
echo "i ran"
}
}
}
}

Determine Failed Stage in Jenkins Scripted Pipeline

I am looking for a generic way to determine the name of the failed stage at the end of a Jenkins scripted Pipeline.
Please note that this is different than Determine Failed Stage in Jenkins Declaritive Pipeline which is about declarative pipeline.
Also note that use of try/catch inside each stage is out of question because it would make the pipeline script impossible to read. This is because we have like 10-15 stages which are stored in multiple files and they are compiled using JJB to create the final pipeline script. They are already complex so I need a clean approach on finding which stage failed.
U could also create a custom step in a shared library, a super_stage
Quick example:
// vars/super_stage.groovy
def call(name, body) {
try {
stage(name) {
body()
}
} catch(e) {
register_failed_stage(name, e)
throw e
}
}
In that way you can 'reuse' the same exception handler.
In your scripted pipeline you would then use it like:
super_stage("mystage01") {
//do stuff
}
Source
Use a GraphListener:
def listener = new GraphListener.Synchronous(){
#NonCPS
void onNewHead(FlowNode node){
if (node instanceof StepStartNode){
// before steps execution
}else if (node instanceof StepEndNode){
// after steps execution
}
}
def execution = (FlowExecution) currentBuild.getRawBuild().getExecution()
execution.addListener(listener)
You are going to need a few helper functions in order to make it work, for example StepStartNode and StepEndNode gets called twice so you have to filter the one with the label. Also variables like env are available inside the listener so you can store anything in there to be picked up later.
This answer is pretty generic but I've found that is useful in many of the stackoverflow questions regarding doing something before/after some stage (or in all).
You cannot try/catch exceptions inside the pipeline as this approach is not a wrapper for the stage but just a listener that gets executed once per each line instruction but you can just record the stage at the begining and at the end check currentBuild.result to see if the stage failed. You can do pretty much anything at this point.
At some point with the FlowExecution you have access to the pipeline script, I don't know if it's writtable at that point but it would be awesome to rewrite the pipeline to actually try/catch the stages. If you do something in this line please let me know ;)

How to add "single conditional steps" under build section using dsl script

I'm currently trying to develop a DSL script that can create a jenkins job with all required plugins and options.
I think I've almost completed all the section. But, I stuck up under build section where I've to include "conditional steps (single)" under Build.
Actually what I wanted is this
But, what I get is this
Here's the code that I used,
job('Sample_dev') {
steps {
conditionalSteps {
condition {
alwaysRun()
}
}
maven {
goals('install')
}
}
}
You have done few mistakes there:
Using multi-step DSL for achieving single step.
Pushed maven outside context like individual step.
Wrong DSL for Maven Step declaration.
Try following
job('Sample_dev')
{
steps{
singleConditionalBuilder{
condition{
alwaysRun()
}
buildStep {
maven{
targets('install')
name('')
pom('')
properties('')
jvmOptions('')
usePrivateRepository(false)
settings {
standard()
}
globalSettings {
standard()
}
injectBuildVariables(false)
}
}
runner {
fail()
}
}
}
}
The creator has deployed most on this url https://jenkinsci.github.io/job-dsl-plugin. But I would suggest you install in you local instance and access it via http://<your-jenkins-host>:<port> /plugin/job-dsl/api-viewer/index.html as Job DSL support auto generation so there is bright chance that plugin not listed above still has DSL support.

Jenkins project generated by Job DSL is not triggered.

I have a project, named Demo, which doesn't do anything in particular.
I have a DSL script, like the following:
def gitUrl = 'GIT_URL'
job('unit-tests') {
scm {
git(gitUrl)
}
triggers {
buildResult('H/* * * * *') {
combinedJobs()
triggerInfo('Demo', BuildResult.SUCCESS, BuildResult.UNSTABLE)
}
}
}
Now what I'm wanting to do, is that when the Demo project runs successfully (it checks out a PHP application from Github), I want the unit-tests job to run.
Currently, when the Demo project is built, the unit-tests job never gets run.
I'm guessing my DSL script is incorrect, but I'm not sure why
I can reproduce your problem. The check box is not set when running the seed job for the first time. But it's set after running the seed job a second time. Must be a problem in the BuildResultTrigger plugin. Please file a bug report in the Jenkins JIRA: https://issues.jenkins-ci.org/projects/JENKINS
But you do not necessarily need to use the BuildResultTrigger plugin. You can use the built-in "Build after other projects are built" option, see https://jenkinsci.github.io/job-dsl-plugin/#path/job-triggers-upstream.
job('unit-tests') {
triggers {
upstream('Demo', 'UNSTABLE')
}
}
Use upstream which adds the "Build after other projects are built" trigger. see https://jenkinsci.github.io/job-dsl-plugin/#path/job-triggers-upstream
def gitUrl = 'GIT_URL'
job('unit-tests') {
scm {
git(gitUrl)
}
triggers {
buildResult('H/* * * * *') {
upstream('Demo', 'UNSTABLE')
}
}
}

How can I delete a job using Job DSL plugin(script) in Jenkins?

I am very new to Jenkins and Job DSL plugin. After a little research, I found how to create a job using DSL and now I am trying to delete a job using DSL.
I know to disable a job using this following code:
//create new job
//freeStyleJob("MyJob1", closure = null);
job("MyJob1"){
disabled(true);
}
It is working perfectly fine. But, I couldn't find any method to delete another job in jenkins.
Please help!
Thanks!
To delete a job, you have to set the "Action for removed jobs" option to "Delete" in the "Process Job DSLs" build step configuration. Then remove the job from your script and run the seed job.
Each instance of the Job Dsl plugin tracks what jobs (and views) it creates. When it is run again, you can configure what it does to jobs (and views) that were present the previous time this instance was run, but are not present this time.
Let's a assume you have two files you use to create jobs.
seed_jobdsl.groovy:
job('seed_all') {
steps {
dsl {
external('*_jobdsl.groovy')
// default behavior
// removeAction('IGNORE')
}
}
}
test_jobdsl.groovy:
job('test_stuff') {
steps {
shell('echo "I live!")
}
}
This will leave jobs created by seed_all unchanged even if they are not present in the list of job created the next time seed is run.
To get jobs to be deleted, change your seed job code:
seed_jobdsl.groovy:
job('seed_all') {
steps {
dsl {
external('*_jobdsl.groovy')
removeAction('DELETE')
}
}
}
Now, run seed_all job to apply your change (seed_all overwrites its own configuration when run). Then make the following change:
test_jobdsl.groovy:
job('test_other') {
steps {
shell('echo "The job is dead, long live the new job!"')
}
}
Run seed_all again. You notice test_stuff will be deleted and test_other will be created. If you remove test_jobdsl.groovy and then run seed_all, test_other will be deleted.

Resources