How to disable or remove multibranch pipeline trigger on existing jobs - jenkins

I have hundreds of jenkins jobs(multibranch pipeline) with trigger enabled to periodically scan respective repositories every 5 mins. I'm trying to disable "scan multibranch pipeline triggers" on all the existing jobs in a particular folder(development/microservice). I'm running below script from Jenkins script console and getting exception at removeTrigger
import hudson.model.*
import hudson.triggers.*
import jenkins.model.*
import com.cloudbees.hudson.plugins.folder.Folder
for (it in Jenkins.instance.getAllItems(jenkins.branch.MultiBranchProject.class)) {
if(it.fullName.length() > 25 && it.fullName.substring(0,25) ==
'development/microservice/' && it.fullName.split("/").length == 3) {
println it.fullName
it.triggers.each { descriptor, trigger ->
it.removeTrigger(descriptor)
it.save()
}
}
}
Can someone please help me how to disable triggers on multibranch pipeline jobs programmatically.

It seems one just needs to iterate over the triggers, and pass the right part to removeTrigger(); that means passing the trigger rather than the descriptor:
for (p in Jenkins.instance.getAllItems(jenkins.branch.MultiBranchProject.class)) {
p.triggers.each { descriptor, trigger ->
//println descriptor
//println trigger
p.removeTrigger(trigger)
}
}
Output sample for a single trigger, when println statements are not commented out:
com.cloudbees.hudson.plugins.folder.computed.PeriodicFolderTrigger$DescriptorImpl#30f35b28
com.cloudbees.hudson.plugins.folder.computed.PeriodicFolderTrigger#3745f994
Many thanks for your question, your almost working code helped me a lot. ;)

Related

Jenkins pipeline - parse logs of specific branch

I have jenkins pipeline that was build in parallel, and when I go to "<jenkins_pipeline>/<build_id>/consoleFull", I can see logs like that:
[branch-1] hi
[branch-2] log11
[branch-3] my logg
second line of logg
[branch-1] yooo
[branch-2] loggerr
hii
hiiiiiii
[branch-1] log line
How can I parse logs of specific branch (e.g. branch-2).
I prefer to have the logic in my code, and not to use third-party packages.
NOTE: scripted pipeline
node {
stage('CheckLog') {
steps {
def loglist = manager.build.logFile.readLines()
filteredLog = loglist.grep(~/^branch\-2.*/)
//< do your stuff >
}
}
}

Determine whether a Jenkinsfile runs from a multibranch pipeline

I have a multibranch pipeline that runs a Jenkinsfile of select branches. Now I need to run that same Jenkinsfile with parameters, so I figured I could use a regular pipeline.
Now all I have to do is to determine whether I run in a multibranch pipeline or not. I could check for any parameters in the build, and when there aren't any I could deduce that I'm in a multibranch pipeline:
def isMultibranchPipeline() {
!params.any()
}
I was searching for a more direct method to know whether the script is running in a multibranch pipeline or not, but couldn't find anything like it.
By getting the current "project" (which is a Jenkins job) you're able to know if it's a multibranch job or not thanks to its class:
import jenkins.model.Jenkins
import org.jenkinsci.plugins.workflow.multibranch.WorkflowMultiBranchProject
def isMultiBranchPipeline() {
return Jenkins.get().getItem(currentBuild.projectName) instanceof WorkflowMultiBranchProject
}
if job is PipelineMultiBranchDefaultsProject previous approach is not working. I'm
checking with scm object
boolean isMultiBranchPipeline() {
try {
if (script.scm)
return true
} catch (Exception e) {
return false
}
}

How to run Jenkins in test mode with all automatic triggers disables?

I am trying to find a way to disable automatic job execution on all jenkins jobs on a stage instance.
We are using jenkins-job-builder to create and update more than 1000 jobs and I want to populate a staging instance with the same jobs I have on production but I want to avoid triggering them on schedulers.
So far I identified two sources that need to be disabled:
timer schedule
gerrit trigger
I do know what disabling jobs does have this effect but this also prevents people from manually triggering these jobs, and for staging instance that's in fact the only kind of job triggering that I want to keep enabled.
Is there a way to achieve this?
I just wrote 2 groovy scripts for the exact use case.
Stop TimerTrigger and PeriodicTrigger jobs:
/**
*
* This script disables all jobs that have TimerTrigger or PeriodicFolderTrigger (WorkflowMultiBranchProject)
*
* */
import jenkins.model.Jenkins
import hudson.model.*
import hudson.triggers.*
import com.cloudbees.hudson.plugins.folder.computed.PeriodicFolderTrigger
def items = Jenkins.instance.getAllItems()
items.each { item ->
if(item.hasProperty('triggers')){
item.triggers.values().each { trigger ->
if(trigger instanceof TimerTrigger || trigger instanceof PeriodicFolderTrigger) {
println "Disabling project with timer trigger: ${item.name}"
item.setDisabled(true)
}
}
}
}
Stop SCMTrigger jobs (same idea, different Trigger class)
/**
*
* This script disables all jobs with SCM trigger
*
* */
import jenkins.model.Jenkins
import hudson.model.*
import hudson.triggers.*
def items = Jenkins.instance.getAllItems()
items.each { item ->
if(item.hasProperty('triggers')){
item.triggers.values().each { trigger ->
if(trigger instanceof SCMTrigger) {
println "Disabling project with SCM trigger: ${item.name}"
item.setDisabled(true)
}
}
}
}

How to differentiate build triggers in Jenkins Pipeline

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}"
}
}
}
}

How to fast fail Jenkins Build Flow Plugin job?

I'm trying to fail a Build Flow Plugin job if a parameter isn't set. When I try:
final parameter = params['PARAMETER']
if (parameter.isEmpty()) {
out.println('Error: PARAMETER must be defined')
System.exit(1)
}
the entire Jenkins master dies (double plus ungood).
How do I get the job to fail without killing Jenkins?
Set the Result on build.state then return so as not to execute anything else:
import hudson.model.Result
final parameter = params['PARAMETER']
if (parameter.isEmpty()) {
out.println('Error: PARAMETER must be defined')
build.state.setResult(Result.FAILURE)
return
}

Resources