Is it possible to set different values of parametrized crons on a single pipeline on a condition in groovy. So that first execution will set a diff cron and another execution will set a different cron depends upon a condition in groovy.
I cant use a branch variable as I want to execute the pipeline with same branch but with different crons in different builds.
You can do something like the below.
def flag = false
def cron1 = 'H/15 * * * *'
def cron2 = 'H/30 * * * *'
pipeline {
agent any
triggers{ cron(flag ? cron1 : cron2) }
stages {
stage("A"){
steps{
script {
echo "Test"
}
}
}
}
}
or this
def flag = false
def cronExp = ''
if(flag) {
cronExp = 'H/15 * * * *'
} else {
cronExp = 'H/30 * * * *'
}
pipeline {
agent any
triggers{ cron(cronExp) }
stages {
stage("A"){
steps{
script {
echo "Test"
}
}
}
}
}
Related
I have a pipeline which I just added 2 parameters to build release or debug (parameters are called release or debug). The pipeline uses cron syntax to check for changes in the SCM every 10 mins, the pipeline checks for every commit and then build release (C++ program) but I would like to build debug once a day, let's say everyday every coomit pushed from 12 to 13 will be build in debug. All of this without me having to run the pipeline and changing the parameter manually (it is set to release by default). Is there any way to do this? This is a very short version of what the pipeline looks like:
pipeline {
stages {
stage('Setup parameters') {
steps {
script {
properties([
parameters([
choice(
defaultValue: 'RELEASE',
choices: ['RELEASE', 'DEBUG'],
name: 'BUILD_CONFIG'
),
])
])
}
}
}
stage('Build release'){
when {
expression {
return params.BUILD_CONFIG == 'RELEASE'
}
}
steps{
script {
def msbuild = tool name: 'MSBuild', type: 'hudson.plugins.msbuild.MsBuildInstallation'
bat "\"${msbuild}\" /Source/project-GRDK.sln /t:Rebuild /p:configuration=\"Release Steam D3D11\""
}
}
}
stage('Build debug'){
when {
expression {
return params.BUILD_CONFIG == 'DEBUG'
}
}
steps{
script {
def msbuild = tool name: 'MSBuild', type: 'hudson.plugins.msbuild.MsBuildInstallation'
bat "\"${msbuild}\" /Source/project-GRDK.sln /t:Rebuild /p:configuration=\"Debug Steam D3D11\""
}
}
}
}
}
parameterizedCron plugin does what you need:
pipeline {
agent any
parameters {
choice(name: 'BUILD_CONFIG', choices: ['RELEASE', 'DEBUG'], defaultValue: 'RELEASE')
}
triggers {
parameterizedCron('''
1,2,3,4,5,6,7,8,9,10 * * * * % BUILD_CONFIG=RELEASE
12 * * * * % BUILD_CONFIG=DEBUG
''')
}
Another option would be to create a second job, which triggers the build job with the right parameters:
pipeline {
agent any
triggers {
cron('H 12 * * *')
}
stages {
stage('Build xxx debug') {
steps {
build job: "your-job-name-here", parameters: [
choice(name: 'BUILD_CONFIG', value: 'DEBUG')
]
}
}
}
}
It is possible to determine the cause of the build with currentBuild.rawBuild.getCause(Class<T> type). The type you are looking for is UserIdCause. Following would build a stage in case the job was not triggered by an user (manually). In this stage steps are from Build debug stage.
stage('Build debug if time triggered') {
when {
expression {
return currentBuild.rawBuild.getCause(hudson.model.Cause$UserIdCause) == null
}
}
steps {
script {
def msbuild = tool name: 'MSBuild', type: 'hudson.plugins.msbuild.MsBuildInstallation'
bat "\"${msbuild}\" /Source/project-GRDK.sln /t:Rebuild /p:configuration=\"Debug Steam D3D11\""
}
}
You will also need to add an expression to Build release and Build debug stages, in order to prevent building if the job is not triggered by an user (manually).
stage('Build release'){
when {
allOf {
expression {
return params.BUILD_CONFIG == 'RELEASE'
}
expression {
return currentBuild.rawBuild.getCause(hudson.model.Cause$UserIdCause) != null
}
}
}
...
Docu:
https://javadoc.jenkins-ci.org/hudson/model/Cause.html
https://javadoc.jenkins-ci.org/hudson/model/Run.html
How to differentiate build triggers in Jenkins Pipeline
EDIT
If you want to keep everything in one pipeline, then you need to create two new variables. Following code creates Calendar object for 12 am today and converts it to milliseconds.
Calendar date = new GregorianCalendar()
date.set(Calendar.HOUR_OF_DAY, 12);
date.set(Calendar.MINUTE, 0);
date.set(Calendar.SECOND, 0);
date.set(Calendar.MILLISECOND, 0);
def start = date.getTime().getTime()
In same way you could create a Calendar object for 1 pm today (e.g. end). With currentBuild.rawBuild.getTimestamp() you get Calendar object, when the build was scheduled. If the scheduled time is between start and end set for example a boolean variable and check it in the pipeline when block.
def buildDebug = false
def scheduled = currentBuild.rawBuild.getTimestamp().getTime().getTime()
if(scheduled > start && scheduled < end)
buildDebug = true
...
stage('Build release'){
when {
allOf {
expression {
return params.BUILD_CONFIG == 'RELEASE'
}
expression {
return currentBuild.rawBuild.getCause(hudson.model.Cause$UserIdCause) == null
}
expression {
return buildDebug == false
}
}
}
...
stage('Build debug'){
when {
allOf {
expression {
return params.BUILD_CONFIG == 'RELEASE'
}
expression {
return currentBuild.rawBuild.getCause(hudson.model.Cause$UserIdCause) == null
}
expression {
return buildDebug == true
}
}
}
How to create a Java Date object of midnight today and midnight tomorrow?
I've set a cron job to run every night, however I only want it to run stage B within the Jenkinsfile not all of them.
pipeline {
agent any
triggers {
cron('#midnight')
}
}
stages {
stage('A') {
...
}
stage('B'){
when {
allOf {
expression { env.CHANGE_TARGET == "master" }
branch "PR-*"
}
}
steps {
sh """
echo 'running script'
make run-script
"""
}
}
stage('C') {
...
}
Without removing the conditionals in Stage B, I can't seem to figure out how to specify the cron to explicitly only run Stage B of the Jenkinsfile - I need to run that makefile script only when those conditionals are met OR during the daily midnight cron job
You can achieve what you want with the Parameterized Scheduler Plugin which enables you to define cron triggers that trigger the job with a specific environment variable, you can then use this variable as a condition to determine which step to execute.
in your case you can use that environment variable in the when directive of each stage to determine if it should run or not according to the variable.
Something like:
pipeline {
agent any
parameters {
booleanParam(name: 'MIDNIGHT_BUILD', defaultValue: 'true', description: 'Midnight build')
}
triggers {
parameterizedCron('''
0 * * * * %MIDNIGHT_BUILD=true
''')
}
stages {
stage('A') {
when {
expression { !env.MIDNIGHT_BUILD }
}
steps {
...
}
}
stage('B') {
when {
expression { env.MIDNIGHT_BUILD || env.CHANGE_TARGET == "master" }
}
steps {
sh """
echo 'running script'
make run-script
"""
}
}
stage('C') {
when {
expression { !env.MIDNIGHT_BUILD }
}
steps {
...
}
}
}
}
I have issue porting scripted to declarative pipeline. I used to have in scripted:
//Scripted
def myEnv = [:]
stage ('Prepare my env') { [...] myEnv = ... }
stage ('Fancy stuff') {
node() {
withEnv(myEnv) {
// here use what is defined in myEnv
}
}
stage ('Fancy stuff2') {
node() {
withEnv(myEnv) {
// here use what is defined in myEnv
} }
}
and now in declarative I would like to have
//Declarative
def myEnv = [:]
pipeline {
agent none
stage('Prepare my env') {
steps {
script {
[...]
myEnv = ...
}
}
}
stages {
environment { myEnv }
stage('Fancy stuff') {
[...]
}
stage('Fancy stuff2') {
[...]
}
} }
when I try to run this, it fails withEnv
org.codehaus.groovy.control.MultipleCompilationErrorsException:
startup failed: WorkflowScript: xx: "myEnv" is not a valid environment
expression. Use "key = value" pairs with valid Java/shell keys.
Fair enough.
What should I do to be able to use declarative environment { } to avoid using withEnv(myEnv) one in every further steps?
it seems that the part you are missing is the usage of environment clause.
Instead of
environment { myEnv }
It should be
environment { myEnvVal = myEnv }
Just as the error method mentions this should be key = value pair.
Your issue comes from the type of your variable myEnv. You define it as a map when you do def myEnv = [:].
So it works with withEnv that takes a map as parameter but it does not work with environment {...} that takes only "key = value" statements.
The solution depends on how you add environment variables contained in myEnv.
The simplest way is using environment directive by listing all the key/values contained in your former variable myEnv:
pipeline{
agent none
environment {
test1 = 'test-1'
test2 = 'test-2'
}
stages{
stage('Fancy stuff'){
steps{
echo "${test1}"
}
}
stage('Fancy stuff2'){
steps{
echo "${test2}"
}
}
}
}
But you also do it the scripted way :
pipeline{
agent none
stages{
stage('Prepare my env') {
steps {
script {
def test = []
for (int i = 1; i < 3; ++i) {
test[i] = 'test-' + i.toString()
}
test1 = test[1]
test2 = test[2]
}
}
}
stage('Fancy stuff'){
steps{
echo "${test1}"
}
}
stage('Fancy stuff2'){
steps{
echo "${test2}"
}
}
}
}
I need to run jenkins pipeline every 5 minutes and trigger stages only if /tmp/*.json exists. How to do that ?
I have the skeleton :
pipeline {
agent any
environment {
def JSON_PATH = '/tmp/*.json'
}
triggers {
cron('*/5 * * * *')
}
stages {
[...]
}
}
You're on the right track. To check file existence, you can use ls and read the exit status.
stage('Check file existence') {
steps {
script {
fileExists = sh(returnStatus: true, script: 'ls /tmp/*.json') == 0
}
}
}
stage('Do actions') {
when {
expression { fileExists }
}
steps {
<insert desired steps here>
}
}
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
}