I need to create a Jenkins pipeline to clear the build queue periodically. I have the script that i can run in the script console, but it does not work when i try to run it in the pipeline.
def q = Jenkins.instance.queue for (queued in Jenkins.instance.queue.items) { q.cancel(queued.task) }
I am pretty sure it has to do with importing classes, but i am having issues writing this script.
If you just want to clear the enitre build queue from a pipeline you can use the clear method from the Jenkins Queue object which will clear the entire build queue:
Jenkins.instance.queue.clear()
Notice: you will probably get a RejectedAccessException: Scripts not permitted to use method error when running this script, which means a Jenkins admin will have to approve it.
In addition, if you are running this code in a declarative pipeline make sure to warp it with a script directive.
If you are using Jenkins Shared Libraries you can create a more generic clearBuildQueue function inside your library and later on use it in your various pipelines.
It can look something like:
#NonCPS
def clearBuildQueue(String pattern = ''){
def queue = Jenkins.instance.queue
if(pattern) {
queue.items.findAll { it.task.name =~ pattern}.each {
println "Item '${it.task.name}' was cleared from the queue"
queue.cancel(it.task)
}
}
else {
println "Clearing ${queue.items.length} items from the queue"
queue.clear()
}
}
Related
I have a somewhat unique setup where I need to be able to dynamically load Jenkinsfiles that live outside of the src I'm building. The Jenkinsfiles themselves usually call node() and then some build steps. This causes multiple executors to be eaten up unnecessarily because I need to have already called node() in order to use the load step to run a Jenkinsfile, or to execute the groovy if I read the Jenkinsfile as a string and execute it.
What I have in the job UI today:
#Library(value='myGlobalLib#head', changelog=fase) _
node{
load "${JENKINSFILES_ROOT}/${PROJECT_NAME}/Jenkinsfile"
}
The Jenkinsfile that's loaded usually also calls node(). For example:
node('agent-type-foo'){
someBuildFlavor{
buildProperty = "some value unique to this build"
someConfig = ["VALUE1", "VALUE2", "VALUE3"]
runTestTarget = true
}
}
This causes 2 executors to be consumed during the pipeline run. Ideally, I load the Jenkinsfiles without first calling node(), but whenever I try, I get an error message stating:
"Required context class hudson.FilePath is missing
Perhaps you forgot to surround the code with a step that provides this, such as: node"
Is there any way to load a Jenkinsfile or execute groovy without first having hudson.FilePath context? I can't seem to find anything in the doc. I'm at the point where I'm going to preprocess the Jenkinsfiles to remove their initial call to node() and call node() with the value the Jenkinsfile was using, then load the rest of the file, but, that's somewhat too brittle for me to be happy with.
When using load step Jenkins evaluates the file. You can wrap your Jenkinsfile's logics into a function (named run() in my example) so that it will load but not run automatically.
def run() {
node('agent-type-foo'){
someBuildFlavor{
buildProperty = "some value unique to this build"
someConfig = ["VALUE1", "VALUE2", "VALUE3"]
runTestTarget = true
}
}
}
// This return statement is important in the end of Jenkinsfile
return this
Call it from your job script like this:
def jenkinsfile
node{
jenkinsfile = load "${JENKINSFILES_ROOT}/${PROJECT_NAME}/Jenkinsfile"
}
jenkinsfile.run()
This way there is no more nested node blocks because the first gets closed before run() function is called.
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 ;)
I'm hoping to add a conditional stage to my Jenkinsfile that runs depending on how the build was triggered. Currently we are set up such that builds are either triggered by:
changes to our git repo that are picked up on branch indexing
a user manually triggering the build using the 'build now' button in the UI.
Is there any way to run different pipeline steps depending on which of these actions triggered the build?
The following code should works to determine if a user has started the pipeline or a timer/other trigger:
def isStartedByUser = currentBuild.rawBuild.getCause(hudson.model.Cause$UserIdCause) != null
In Jenkins Pipeline without currentBuild.rawBuild access the build causes could be retrieved in the following way:
// started by commit
currentBuild.getBuildCauses('jenkins.branch.BranchEventCause')
// started by timer
currentBuild.getBuildCauses('hudson.triggers.TimerTrigger$TimerTriggerCause')
// started by user
currentBuild.getBuildCauses('hudson.model.Cause$UserIdCause')
You can get a boolean value with:
isTriggeredByTimer = !currentBuild.getBuildCauses('hudson.triggers.TimerTrigger$TimerTriggerCause').isEmpty()
Or, as getBuildCauses() returns an array, the array's size will work correctly with Groovy truthy semantics:
if (currentBuild.getBuildCauses('hudson.triggers.TimerTrigger$TimerTriggerCause')) {
The ability to get causes for a workflow run was released in version 2.22 (2018 Nov 02) to the Pipeline Supporting APIs Plugin. The feature was requested in JENKINS-41272.
A couple methods were added to the currentBuild global variable with that release:
getBuildCauses
Returns a JSON array of build causes for the current build
EXPERIMENTAL - MAY CHANGE getBuildCauses(String causeClass)
Takes a string representing the fully qualified Cause class and returns a JSON array of build causes filtered by that type for the current build, or an empty JSON array if no causes of the specified type apply to the current build
And an example from me submitting:
echo "${currentBuild.buildCauses}" // same as currentBuild.getBuildCauses()
echo "${currentBuild.getBuildCauses('hudson.model.Cause$UserCause')}"
echo "${currentBuild.getBuildCauses('hudson.triggers.TimerTrigger$TimerTriggerCause')}"
And the output:
[Pipeline] echo
[[_class:hudson.model.Cause$UserIdCause, shortDescription:Started by user anonymous, userId:null, userName:anonymous], [_class:org.jenkinsci.plugins.workflow.cps.replay.ReplayCause, shortDescription:Replayed #12]]
[Pipeline] echo
[]
[Pipeline] echo
[]
[Pipeline] End of Pipeline
Finished: SUCCESS
NOTE
There appears to be an issue with the currentBuild.getBuildCauses(type) when the type is a type of Cause contributed by a plugin. For example, currentBuild.getBuildCauses('org.jenkinsci.plugins.workflow.cps.replay.ReplayCause') fails with a java.lang.ClassNotFoundException. This was reported in JENKINS-54673 for the 2.22 version of the Pipeline: Supporting APIs (workflow-support) plugin. It is reportedly fixed in the 2.24 version.
I might be missing something, but you can achieve what you want easily by making use of the when directive:
pipeline {
agent any
stages {
stage('Always') {
steps {
echo "I am always executed"
}
}
stage('ManualTimed') {
steps {
echo "I am only executed when triggered manually or timed"
}
when {
beforeAgent true
anyOf {
triggeredBy 'TimerTrigger'
triggeredBy cause: 'UserIdCause'
}
}
}
stage('GitLabWebHookCause') {
steps {
echo "I am only executed when triggered by SCM push"
}
when {
beforeAgent true
triggeredBy 'GitLabWebHookCause'
}
}
}
}
You will find many similar useful examples for various use cases in the documentation of the when directive.
Edit:
thanks to Jean-Francois Larvoire's answer, I was able to figure out 'my trigger' GitLabWebHookCause I required for my use case.
#vitalii-blagodir:
Your answer works for detecting builds triggered by users and timers, but not by commits.
Instead, I found this to work in my case:
def isTriggeredByIndexing = currentBuild.getBuildCauses('jenkins.branch.BranchIndexingCause').size()
def isTriggeredByCommit = currentBuild.getBuildCauses('com.cloudbees.jenkins.GitHubPushCause').size()
def isTriggeredByUser = currentBuild.getBuildCauses('hudson.model.Cause$UserIdCause').size()
def isTriggeredByTimer = currentBuild.getBuildCauses('hudson.triggers.TimerTrigger$TimerTriggerCause').size()
The .size() suffix returns 0 if the object is missing, or 1 if it's present. This makes the result usable as a boolean.
For finding the object name to use, I found it convenient to display this in the log:
echo "# Build causes"
def buildCauses = currentBuild.buildCauses
def numCause = 0
for (cause in buildCauses) {
echo "${numCause++}: ${cause.shortDescription}" // Display a human-readable index and description
echo "${cause}" // Display the object class name. This allows knowing what names to use in getBuildCauses(name) calls below.
}
Finally, if the goal is to abort a pipeline build in specific cases, then the test must be done before the beginning of the pipeline.
For example, we had a problem with the branch indexing triggering extra useless builds. This was fixed by adding this before the pipeline:
// Avoid useless buils: The branch indexing should only trigger the initial build of a new branch.
def isTriggeredByBranchIndexing = currentBuild.getBuildCauses('jenkins.branch.BranchIndexingCause').size()
if (isTriggeredByBranchIndexing && currentBuild.previousBuild) { // Then it's not the initial build.
echo "# Reindexing a branch already built. It is useless to rebuild it now. Aborting."
currentBuild.result = 'SUCCESS' // Make sure the build is not displayed in red in the Jenkins UI.
return // Abort before the pipeline even starts. (Inside the pipeline, this would only abort one stage.)
}
I think that the answers here are incomplete and do not provide an actual ready to use answer. Here's my code to get it working:
import com.cloudbees.groovy.cps.NonCPS
#NonCPS
def isStartedByTimer() {
def buildCauses = currentBuild.rawBuild.getCauses()
echo buildCauses
boolean isStartedByTimer = false
for (buildCause in buildCauses) {
if ("${buildCause}".contains("hudson.triggers.TimerTrigger\$TimerTriggerCause")) {
isStartedByTimer = true
}
}
echo isStartedByTimer
return isStartedByTimer
}
// [...]
// Other pipeline stuff
script {
isStartedByTimer()
}
When started by user:
00:00:01.353 [hudson.model.Cause$UserIdCause#fa5cb22a]
[Pipeline] echo
00:00:01.358 false
When started by timer:
00:00:01.585 [hudson.triggers.TimerTrigger$TimerTriggerCause#5]
[Pipeline] echo
00:00:01.590 true
Note: the NonCPS decorator is needed because otherwise the next non-script step will throw.
Assuming the two different build causes are "timer" and "push" (to a git repo), you can add the following stage to your Jenkinsfile (in a declarative Jenkins pipeline) to make use of getBuildCauses():
pipeline {
stages {
stage('preparation') {
steps {
script {
// get build cause (time triggered vs. SCM change)
def buildCause = currentBuild.getBuildCauses()[0].shortDescription
echo "Current build was caused by: ${buildCause}\n"
// e.g. "Current build was caused by: Started by GitHub push by mirekphd"
// vs. "Started by timer"
}
}
}
}
}
Then I can decide whether to perform certain stages conditionally (depending on the build cause). For example, pulling a docker base image and inspecting for changes in system libraries (likely security updates) should be done periodically, regardless of whether there was a source code change or not.
We can use "BUILD_CAUSE" variable for getting the information about who initiated the run
for [jenkins-pipeline] you may use
currentBuild.rawBuild.getCauses()
(see github.com/jenkinsci/pipeline-examples/blob/master/… for more details)
There was a similar requirement, where user detail who triggered the build should be there in success / failure notification. The job was already had time based triggered, hence could not use wrap([$class: 'BuildUser']) directly.
I used below step, which print username if the job is triggered manually or timer triggered. So, I used this:
pipeline {
agent any
stages {
stage('Test') {
steps {
script{
env.buildCauses = currentBuild.rawBuild.getCauses()
if (buildCauses.contains("hudson.triggers.TimerTrigger")){
env.builduser = "TimerTrigger"
} else {
wrap([$class: 'BuildUser']) {
env.builduser = "${BUILD_USER}"
}
}
}
echo "Initiated by: ${env.builduser}"
}
}
}
}
I am looking at limiting the number of concurrent builds to a specific number in Jenkins, leveraging the multibranch pipeline workflow but haven't found any good way to do this in the docs or google.
Some docs say this can be accomplished using concurrency in the stage step of a Jenkinsfile but I've also read elsewhere that that is a deprecated way of doing it.
It looks like there was something released fairly recently for limiting concurrency via Job Properties but I couldn't find documentation for it and I'm having trouble following the code. The only thing I found a PR that shows the following:
properties([concurrentBuilds(false)])
But I am having trouble getting it working.
Does anybody know or have a good example of how to limit the number of concurrent builds for a given, multibranch project? Maybe a Jenkinsfile snippet that shows how to limit or cap the number of multibranch concurrent builds?
Found what I was looking for. You can limit the concurrent builds using the following block in your Jenkinsfile.
node {
// This limits build concurrency to 1 per branch
properties([disableConcurrentBuilds()])
//do stuff
...
}
The same can be achieved with a declarative syntax:
pipeline {
options {
disableConcurrentBuilds()
}
}
Limiting concurrent builds or stages are possible with the Lockable Resources Plugin (GitHub). I always use this mechanism to ensure that no publishing/release step is executed at the same time, while normal stages can be build concurrently.
echo 'Starting'
lock('my-resource-name') {
echo 'Do something here that requires unique access to the resource'
// any other build will wait until the one locking the resource leaves this block
}
echo 'Finish'
As #VadminKotov indicated it is possible to disable concurrentbuilds using jenkins declarative pipelines as well:
pipeline {
agent any
options { disableConcurrentBuilds() }
stages {
stage('Build') {
steps {
echo 'Hello Jenkins Declarative Pipeline'
}
}
}
}
disableConcurrentBuilds
Disallow concurrent executions of the Pipeline. Can be useful for
preventing simultaneous accesses to shared resources, etc. For
example: options { disableConcurrentBuilds() }
Thanks Jazzschmidt, I looking to lock all stages easily, this works for me (source)
pipeline {
agent any
options {
lock('shared_resource_lock')
}
...
...
}
I got the solution for multibranch locking too, with de lockable-resources plugin and the shared-libs here is :
Jenkinsfile :
#Library('my_pipeline_lib#master') _
myLockablePipeline()
myLockablePipeline.groovy :
call(Map config){
def jobIdentifier = env.JOB_NAME.tokenize('/') as String[];
def projectName = jobIdentifier[0];
def repoName = jobIdentifier[1];
def branchName = jobIdentifier[2];
//now you can use either part of the jobIdentifier to lock or
limit the concurrents build
//here I choose to lock any concurrent build for PR but you can choose all
if(branchName.startsWith("PR-")){
lock(projectName+"/"+repoName){
yourTruePipelineFromYourSharedLib(config);
}
}else{
// Others branches can freely concurrently build
yourTruePipelineFromYourSharedLib(config);
}
}
To lock for all branches just do in myLockablePipeline.groovy :
call(Map config){
def jobIdentifier = env.JOB_NAME.tokenize('/') as String[];
def projectName = jobIdentifier[0];
def repoName = jobIdentifier[1];
def branchName = jobIdentifier[2];
lock(projectName+"/"+repoName){
yourTruePipelineFromYourSharedLib(config);
}
}
I am trying to do a poc of jenkins pipeline as code. I am using the Github organization folder plugin to scan Github orgs and create jobs per branch. Is there a way to explicitly define the names for the pipeline jobs that get from Jenkinsfile? I also want to add some descriptions for the jobs.
You need to use currentBuild like below. The node part is important
node {
currentBuild.displayName = "$yournamevariable-$another"
currentBuild.description = "$yourdescriptionvariable-$another"
}
Edit: Above one renames build where as Original question is about renaming jobs.
Following script in pipeline will do that(this requires appropriate permissions)
item = Jenkins.instance.getItemByFullName("originalJobName")
item.setDescription("This description was changed by script")
item.save()
item.renameTo("newJobName")
I'm late to the party on this one, but this question forced me in the #jenkins chat where I spent most of my day today. I would like to thank #tang^ from that chat for helping solve this in a graceful way for my situation.
To set the JOB description and JOB display name for a child in a multi-branch DECLARATIVE pipeline use the following steps block in a stage:
steps {
script {
if(currentBuild.rawBuild.project.displayName != 'jobName') {
currentBuild.rawBuild.project.description = 'NEW JOB DESCRIPTION'
currentBuild.rawBuild.project.setDisplayName('NEW JOB DISPLAY NAME')
}
else {
echo 'Name change not required'
}
}
}
This will require that you approve the individual script calls through the Jenkins sandbox approval method, but it was far simpler than anything else I'd found across the web about renaming the actual children of the parent pipeline. The last thing to note is that this should work in a Jenkinsfile where you can use the environment variables to manipulate the job items being set.
I tried to used code snippet from accepted answer to describe my Jenkins pipeline in Jenkinsfile. I had to wrap code snippet into function with #NonCPS annotation and use def for item variable. I have placed code snippet in root of Jenkinsfile, not in node section.
#NonCPS
def setDescription() {
def item = Jenkins.instance.getItemByFullName(env.JOB_NAME)
item.setDescription("Some description.")
item.save()
}
setDescription()