Jenkins Pipeline - wait for non-job items (ComputedFolder, Multibranch Pipeline Project) - jenkins

I'm using multibranch pipeline projects in Jenkins.
I let Jenkins index new branches.
I need Jenkins to wait until the indexing of a multibranch pipeline project is complete.
This is my current code:
def triggerScanMultibranchPipeline(projectDir, repo) {
def multibranchProject = Jenkins.instance.getItemByFullName "$projectDir/$repo"
multibranchProject.scheduleBuild()
while (multibranchProject == null || multibranchProject.isDisabled()) {
sleep 1000 //1000 milliseconds = 1 second
}
sleep time: 1, unit: 'SECONDS'
}
Already tried:
change (second) sleep to 30 seconds
This increases the duration of the build each time triggerScanMultibranchPipeline is called.
I cannot use "wait: true" for the indexing to complete, because waiting for non-job items is not supported.
See https://github.com/jenkinsci/pipeline-build-step-plugin/blob/pipeline-build-step-2.13/src/main/java/org/jenkinsci/plugins/workflow/support/steps/build/BuildTriggerStepExecution.java#L80
How can I let Jenkins wait until the branch indexing of the Multibranch Pipeline Project is complete?

Related

How to block upstream/downstream build in Jenkins declarative pipeline?

I have 3 downstream build jobs which are triggered when 1 upstream job 'Project U' has been built successfully. Example:
triggers {
pollSCM('H/5 * * * *')
upstream(upstreamProjects: 'Project U', threshold: hudson.model.Result.SUCCESS)
}
This works as expected, however, if code changes are committed to all parts at the same time, the upstream and downstream builds start building simultaneously.
I want to avoid this, because the downstream builds will run twice, and the first run is quite useless as the upstream commit has not been built yet. So I would like to configure the downstream jobs to block their build while the upstream job is building.
I know how to do this in Jenkins Freestyle job in the user interface (also see this answer):
But I cannot find how to do this in a Jenkins declarative pipeline?
This approach works:
waitUntil {
def job = Jenkins.instance.getItemByFullName("Project U")
!job.isBuilding() && !job.isInQueue()
}
When this downstream is started, it will check if the upstream job is either active or queued, and if so, will wait until its build has finished.
I haven't been able to find out how to programmatically access the current job's upstream job(s), so the job names need to be copied. (There is a method getBuildingUpstream(), which would be much more convenient, but I haven't found a way to obtain the current Project object from the Job instance.)
I finally ended up creating this function in the Jenkins shared library:
/*
vars/waitForJobs.groovy
Wait until none of the upstream jobs is building or being queued any more
Parameters:
upstreamProjects String with a comma separated list of Jenkins jobs to check
(use same format as in upstream trigger)
*/
def call( Map params) {
projects = params['upstreamProjects'].split(', *')
echo 'Checking the following upstream projects:'
if ( projects.size() == 0) {
echo 'none'
} else {
projects.each {project ->
echo "${project}"
}
}
waitUntil {
def running = false
projects.each {project ->
def job = Jenkins.instance.getItemByFullName(project)
if (job == null) {
error "Project '${project} not found"
}
if (job.isBuilding()) {
echo "Waiting for ${project} (executing)"
running = true
}
if (job.isInQueue()) {
echo "Waiting for ${project} (queued for execution)"
running = true
}
}
return !running
}
}
The nice thing is that I can just copy the parameter from the upstream trigger, because it uses the exact same format. Here's an example of how it looks like:
pipeline {
[...]
triggers {
pollSCM('H/5 * * * *')
upstream(upstreamProjects: 'Project U1, Project U2, Project U3', threshold: hudson.model.Result.SUCCESS)
}
[...]
stages {
stage('Wait') {
steps {
script{
// Wait for upstream jobs to finish
waitForJobs(upstreamProjects: 'Project U1, Project U2, Project U3')
}
}
[...]
}
}
}

Jenkins - How to run a stage/function before pipeline starts?

We are using a Jenkins multibranch pipeline with BitBucket to build pull request branches as part of our code review process.
We wanted to abort any queued or in-progress builds so that we only run and keep the latest build - I created a function for this:
def call(){
def jobName = env.JOB_NAME
def buildNumber = env.BUILD_NUMBER.toInteger()
def currentJob = Jenkins.instance.getItemByFullName(jobName)
for (def build : currentJob.builds){
def exec = build.getExecutor()
if(build.isBuilding() && build.number.toInteger() != buildNumber && exec != null){
exec.interrupt(
Result.ABORTED,
new CauseOfInterruption.UserInterruption("Job aborted by #${currentBuild.number}")
)
println("Job aborted previously running build #${build.number}")
}
}
}
Now in my pipeline, I want to run this function when the build is triggered by the creation or push to a PR branch.
It seems the only way I can do this is to set the agent to none and then set it to the correct node for each of the subsequent stages. This results in missing environment variables etc. since the 'environment' section runs on the master.
If I use agent { label 'mybuildnode' } then it won't run the stage until the agent is free from the currently in-progress/running build.
Is there a way I can get the 'cancelpreviousbuilds()' function to run before the agent is allocated as part of my pipeline?
This is roughly what I have currently but doesn't work because of environment variable issues - I had to do the skipDefaultCheckout so I could do it manually as part of my build:
pipeline {
agent none
environment {
VER = '1.2'
FULLVER = "${VER}.${BUILD_NUMBER}.0"
PROJECTPATH = "<project path>"
TOOLVER = "2017"
}
options {
skipDefaultCheckout true
}
stages {
stage('Check Builds') {
when {
branch 'PR-*'
}
steps {
// abort any queued/in-progress builds for PR branches
cancelpreviousbuilds()
}
}
stage('Checkout') {
agent {
label 'buildnode'
}
steps {
checkout scm
}
}
}
}
It works and aborts the build successfully but I get errors related to missing environment variables because I had to start the pipeline with agent none and then set each stage to agent label 'buildnode' to.
I would prefer that my entire pipeline ran on the correct agent so that workspaces / environment variables were set correctly but I need a way to trigger the cancelpreviousbuilds() function without requiring the buildnode agent to be allocated first.
You can try combining the declarative pipeline and the scripted pipeline, which is possible.
Example (note I haven't tested it):
// this is scripted pipeline
node('master') { // use whatever node name or label you want
stage('Cancel older builds') {
cancel_old_builds()
}
}
// this is declarative pipeline
pipeline {
agent { label 'buildnode' }
...
}
As a small side comment, you seem to use: build.number.toInteger() != buildNumber which would abort not only older builds but also newer ones. In our CI setup, we've decided that it's best to abort the current build if a newer build has already been scheduled.

Run the Jenkins pipeline nightly and after every commit

We would like to run the Jenkins pipeline:
nightly, but only if there was any commit in the chosen branches (dev or master)
and after every commit
With the caveat that nightly build should also tigger additional step (long-running integration tests).
Is it possible? How can we configure this in Jenkinsfile?
So, your question boils down to several:
How can I run a nightly build?
How can I stop a build if no commits were made in the last 24 hours?
How can I run a "long-running" stage, but not always?
Let's address these one by one.
You can have a nightly build by using cron or parameterizedCron, if you install the ParameterizedCron plugin. As you seem to want to run your job in all the branches, this may look like e.g.
pipeline {
agent any
triggers {
// schedule a nightly build at random time after 0am on dev branch, 1am on master
cron(env.BRANCH_NAME == 'dev' ? '0 * * * *' : '')
cron(env.BRANCH_NAME == 'master' ? '1 * * * *' : '')
To address the other points, you need to know why your build is running. It may be triggered by a person, or cron, or a commit. Once you make sure it's cron that triggered the build, you may want to explore git log for the time of the latest commit.
def currentBuildReason() {
def timerCause = currentBuild.rawBuild.getCause(hudson.triggers.TimerTrigger.TimerTriggerCause)
if (timerCause) { echo "Build reason: Build was started by timer"; return "TIMER" }
timerCause = currentBuild.rawBuild.getCause(org.jenkinsci.plugins.parameterizedscheduler.ParameterizedTimerTriggerCause)
if (timerCause) { echo "Build reason: Build was started by parameterized timer"; return "TIMER" }
def userCause = currentBuild.rawBuild.getCause(hudson.model.Cause$UserIdCause)
if (userCause) { echo "Build reason: Build was started by user"; return "USER" }
println "here are the causes"
echo "${currentBuild.buildCauses}"
return "UNKNOWN"
}
and later:
if ( currentBuildReason() == "TIMER" and env.BRANCH == "master" ) {
def gitLog = sh script: "git log", returnStdout: true
// look for the date, and stop the build
Finally, you can run steps on condition. One way to do it is to define a parameter for your job, indicating whether the integration tests should run. This will allow a person to run them even if not nightly, when they select the box. E.g.
pipeline {
parameters {
booleanParam(name: 'RUN_LONG_TEST', defaultValue: false,
description: "Should long-running tests execute?")
}
and later
stage('Integration tests') {
agent { node { label "integration_test_slave" }}
when {
beforeAgent true
expression {params.RUN_LONG_TEST}
}
steps {
// run integration test
}
}
To schedule the nightly with the param selected, you need the parameterizedCron plugin:
pipeline {
agent any
triggers {
// schedule a nightly build at random time after 0am on dev branch, 1am on master
parameterizedCron(env.BRANCH_NAME == 'dev' ? '0 * * * * % RUN_LONG_TEST=true' : '')
parameterizedCron(env.BRANCH_NAME == 'master' ? '1 * * * * % RUN_LONG_TEST=true'' : '')
Alternatively, you may disregard the parameter if only the nightlies should run it, and analyze the value of currentBuildReason(). When it equals to TIMER, these tests should run.

Jenkins how to set all jobs with some prefix "blah" in a view to stop if it exceeds time limit

We have jenkin's jobs start with some prefix, some times execution getting too long. We want to stop all those jobs, how can we do it?
You can add timeouts to the stages which are taking more time than expected.
Example stage:
stage("Long-Process") {
steps {
timeout(time: 20, unit: 'MINUTES') {
sh "......"
}
}
}
The Pipleine gets aborted if this stage takes more than 20 minutes. Hope this helps.

Jenkins exclude days from scheduled

How can I exclude specific days from a Jenkins scheduld?
For example: every 5 minutes from 7 to 17 and from monday to friday.
H/05 7-17 * * 1-5
But, if the day of the week is a public holiday, it should not run. How can I configure this?
Thx
Currently Jenkins crontab does not support a complicate logic such public holidays exclusion. However, there are some options out there you can use to accomplish that:
First
For my project I create my own holiday database, which it is a file containing the days I want to exclude eg.:
# /path/to/holidays
# New Year's Day
01-01-2017
# Christmas
12-25-2017
and I check it using a Jenkins shell script, as it was proposed here.
For example for the above file format:
#!/bin/bash
TODAY="`date +%m-%d-%Y`"
if grep -q $TODAY /path/to/holidays; then
echo Skipping holiday for $*
exit 0
fi
$*
Second
A more robust solution but more complicate is to create your own plugin based on the
Run Condition Example Plugin in which you exclude the public holidays of your Country such as this plugin.
There is another option now, with the Jenkins Working Hours Plugin.
Using the plugin, you can configure both working hours:
Or Holidays as 'Excluded Days':
The working hours plugin allows you to set up a schedule of allowable build times; projects can opt in to use the schedule to prevent them from running outside of configured allowable build times. If a build is scheduled during non-working hours then it is kept in the build queue until the next allowable time.
Jobs opt in via the enforceBuildSchedule job parameter, which is provided by this plugin. It can optionally take in a branches parameter to limit it's usage to only those branches. This only works in MultiBranchPipelines.
Usage
Sample job (scripted pipeline):
node {
properties([enforceBuildSchedule()])
stage('Do some stuff') {
echo 'this can wait til morning'
}
}
Sample job (declarative pipeline):
pipeline {
agent any
options {
enforceBuildSchedule()
}
stages {
stage('Do some stuff') {
steps {
echo 'this can wait til morning'
}
}
}
}
Sample job with branches parameter (works in both declarative and scripted):
node {
properties([enforceBuildSchedule(branches: ['dev', 'qa', 'prod')])
stage('Do some stuff') {
echo 'this can wait til morning'
}
}
Using the idea of one of the answers and using Declarative + Script block I created this:
stage('Check if Today is Holiday') {
steps {
// Based on idea from https://stackoverflow.com/a/41219757/7820857
script {
IS_HOLIDAY = sh(script: 'grep -q $(date +%Y-%m-%d) /etc/holidays', returnStatus: true)
if (IS_HOLIDAY == 0) {
currentBuild.result = 'ABORTED'
error ('Today is Holiday according to the file /etc/holidays inside the Jenkins server')
}
}
}
}
This will depend on the file /etc/holidays inside the Jenkins server. Adding this additional Stage before will help you to identify if the day mentioned is holiday and exit with error message, or not and continue with the rest of the stages.
I will like that the Working Hours Plugin worked for this but they queue the jobs in case that the day is inside the Excluded days, but I need to cancel the job execution. A feature request exist for that user case.

Resources