How i can trigger build from jenkinsfile using cron syntax? - jenkins

im using multibranch jenkins style each branch has its own jenkinsfile, i have added triggers in jenkinsfile but it didnt trigger anything on the specified time (8pm), im not sure if im missing something
agent {
node {
label 'master'
}
}
triggers {
cron(env.APP_NAME == 'DICTIONARY' ? '00 20 * * *' : '')
}
stages {
stage('SCM Checkout') {
steps {
git(branch: 'test', url: 'https://gitlab.testral.ba/amramework.git', poll: true, credentialsId: 'GitlabCred')
}
}

For the change in cron to catch, you need to run your Jenkinsfile once manually in the correct branch. After that, check in "View Configuration" that your cron succeeded ("Build periodically" should be checked and contain the schedule).
If it has not, it could be that, at the time when triggers are evaluated, your env.APP_NAME differs from 'DICTIONARY'.
To debug the env, you may add the following:
println "env.APP_NAME is ${env.APP_NAME}" // will run before pipeline
pipeline {
agent {
node {
As a side-note, it's recommended using H instead of minutes, so not all hourly builds fall exactly on the hour:
triggers {
cron(env.APP_NAME == 'DICTIONARY' ? 'H 20 * * *' : '')
}

Related

Jenkins pipeline with a conditional trigger

My Jenkins pipeline is as follow:
pipeline {
triggers {
cron('H */5 * * *')
}
stages {
stage('Foo') {
...
}
}
}
The repository is part of a Github Organization on Jenkins - every branch or PR pushed results in a Jenkins job being created for that branch or PR.
I would like the trigger to only be run on the "main" branch because we don't need all branches and PRs to be run on a cron schedule; we only need them to be run on new commits which they already do.
Is it possible?
yes - it's possible. To schedule cron trigger only for a specific branch you can do it like this in your Jenkinsfile:
String cron_string = (scm.branches[0].name == "main") ? 'H */5 * * *' : ''
pipeline {
triggers {
cron(cron_string)
}
// whatever other code, options, stages etc. is in your pipeline ...
}
What it does:
Initialize a variable based on a branch name. For main branch it sets requested cron configuration, otherwise there's no scheduling (empty string is set).
Use this variable within pipeline
Further comments:
it's possible to use it also with parameterizedCron (in a case you'd want / need to).
you can use also some other variables for getting branch name, e.g: env.BRANCH_NAME instead of scm.branches[0].name. Whatever fits your needs...
This topic and solution is discussed also in Jenkins community: https://issues.jenkins.io/browse/JENKINS-42643?focusedCommentId=293221#comment-293221
EDIT: actually a similar question that leads to the same configuration - here on Stack: "Build Periodically" with a Multi-branch Pipeline in Jenkins
You can simply add a when condition to your pipeline.
when { branch 'main' }

Jenkins declarative pipeline with different triggers per branch

I am looking to implement different cron triggers per branch in a declarative pipeline jenkins job. At the minute, I am only triggering hourly builds on our dev branch:
String cron_string = BRANCH_NAME == "dev" ? "#hourly" : ""
pipeline {
triggers {
cron(cron_string)
}
//stages, options and more code here...
}
My aim would be to have two separate cron strings that would trigger builds at different times in separate branches (eg: hourly builds in dev, every three hours builds in master), however the execution would be identical. My question is, can I do something like the code block below or should I take a different approach?
String cron_string_1 = BRANCH_NAME == "dev" ? "0 8/20 ? * MON-FRY" : ""
String cron_string_2 = BRANCH_NAME == "master" ? "0 8/20/3 ? * MON-FRY" : ""
pipeline {
triggers {
cron(cron_string)
}
//stages, options and more code here...
}
This worked for me (using scripted pipelines):
if (BRANCH_NAME == "dev") {
properties(
[
pipelineTriggers([cron('0 8,13,17 * * *')])
]
)
}

Separate triggers for different branches in Jenkinsfile

I'm working with a Multi-Branch Jenkins build, of which I want the develop branch to build periodically every two weeks and leave the master branch manual.
Our pipelines are pipelines as code, so I cannot set the config for the desired branches.
I'd like to build our develop branch once every 2 weeks on a sunday, so far I have found some different things.
Right now I've come to this schedule:
triggers {
cron('00 12 /2 7')
}
But I do not know how to make it branch specific.
I'm trying this right now to see if it works, should trigger a develop build every 5 mins or break.
triggers {
when (env.BRANCH_NAME == 'develop') {
cron('H/5 * * * *')
}
}
when is not allowed in the triggers block.
I have found a 'solution' on the Jenkins jira which is this:
String cron_string = BRANCH_NAME == "develop" ? "00 12 /2 7" : ""
pipeline {
agent none
triggers { cron(cron_string) }
stages {
// do something
}
}
I have found a 'solution' on the Jenkins jira which is this:
String cron_string = BRANCH_NAME == "develop" ? "00 12 /2 7" : ""
pipeline {
agent none
triggers { cron(cron_string) }
stages {
// do something
}
}

How to make sure list of parameters are updated before running a Jenkins pipeline?

A Jenkins pipeline project is configured to fetch its Jenkinsfile from a Git repo:
If I change the list of parameters, for example, from:
properties([
parameters([
string(name: 'FOO', description: 'Choose foo')
])
])
to:
properties([
parameters([
string(name: 'FOO', description: 'Choose foo'),
string(name: 'BAR', description: 'Choose bar')
])
])
And run the build, the first run does not show the newly added BAR parameter:
As the updated Jenkins file expects the BAR parameter to be present, this causes the first build after the change to fail as the user is not presented with an input to enter this value.
Is there a way to prevent this? To make sure the Jenkinsfile is up-to-date before showing the parameter entry page?
Short answer: No. It would be nice if there was some facility for parsing and processing the Jenkinsfile separate from the build, but there's not.
Jenkins does not know about the new parameters until it retrieves, parses, and runs the Jenkinsfile, and the only way to do that is to...run a build.
In effect, the build history will always be "one run behind" the Jenkinsfile; when you change something in the Jenkinsfile, the next build will run with the "old" Jenkinsfile, but pick up and process the new Jenkinsfile for the build after that.
The only solution to this problem afaik is to manually add an "skip_run" boolean parameter, than add a when{} clause to every stage of the job.
properties([
parameters([
BooleanParameter(name: 'skip_run', description: 'Skips all stages. Used to update parameters in case of changes.', default: False)
])
])
...
stage('Doing Stuff') {
when {
expression { return params.skip_run ==~ /(?i)(N|NO|F|FALSE|OFF|STOP)/ }
}
steps {
...
}
}
This is, of course, very prone to error.
Alternatively, you could add a single stage as the very beginning of the pipeline and fail the build on purpose.
stage('Update Build Info only') {
when {
expression { return params.skip_run ==~ /(?i)(Y|YES|T|TRUE|ON|RUN)/ }
}
steps {
error("This was done deliberately to update the build info.")
}
}
UPDATE:
Thanks to Abort current build from pipeline in Jenkins, i came up with this solution:
To prevent the build from actually appearing red, you could wrap this with a try - catch and exit the build gracefully.
final updateOnly = 'updateOnly'
try {
stage('Update Build Info only') {
when {
expression { return params.skip_run ==~ /(?i)(Y|YES|T|TRUE|ON|RUN)/ }
}
steps {
error(updateOnly)
}
}
...
//other stages here
...
} catch (e) {
if (e.message == updateOnly) {
currentBuild.result = 'ABORTED'
echo('Skipping the Job to update the build info')
// return here instead of throwing error to keep the build "green"
return
}
// normal error handling
throw e
}
I have a function that skips the build unless the job has all the required parameters, something like:
if (job.hasParameters(['FOO', 'BAR'])) {
// pipeline code
}
An issue was reported a few years ago in Jenkins related with this issue
https://issues.jenkins-ci.org/browse/JENKINS-41929
Still open, so no elegant solution yet.
try..
parameters {
string(name: 'GRADLE_ARGS', defaultValue: '--console=plain', description: 'Gradle arguments')
}
environment{
GRADLE_ARGS = "${params.GRADLE_ARGS}"
}

How to disable automatic build from scm change in Jenkinsfile

I have a Jenkinsfile that I've set up with a cron for a pipelineTriggers parameter. I can't seem to figure out how to disable the job from building from a merge to the master branch of the repo. Is there a way in the Jenkinsfile to disable the automatic build from an scm change?
If you're using a Multibranch Pipeline, you should be able to do this on the job's Configure page:
Scroll down to "Branch Sources"
Under "Property strategy", choose "Named branches get different properties"
Click "Add exception", enter "master" as the branch name
Click "Add property", choose "Suppress automatic SCM triggering"
Save
That would prevent changes to the master branch from triggering a build of the corresponding job.
For declarative pipelines, use the when directive with a triggeredBy condition, e.g.
when { triggeredBy 'TimerTrigger' }
With the multibranch pipeline, I could not figure out a way to prevent the next build being triggered. As a workaround, I added the following code to my Jenkinsfile (using scripted syntax), to abort the following build if the only changes contain "[ci-skip]" in the commit message:
def abortBuildIfTriggeredBySkippableCommit() {
def changeSetCount = 0;
def ciSkipCount = 0;
if (currentBuild.changeSets != null) {
for (changeSetList in currentBuild.changeSets) {
for (changeSet in changeSetList) {
changeSetCount++;
if (changeSet.msg.contains('[ci-skip]')) {
ciSkipCount++;
}
}
}
}
if (changeSetCount > 0 && changeSetCount == ciSkipCount) {
currentBuild.result = 'NOT_BUILT'
error("Stopping to prevent auto trigger. All commits contained [ci-skip]")
}
}
Note that this code assumes you are using the git plugin, and that the objects in currentBuild.changeSets will be GitChangeSetList.
In a jenkins job you can navigate to advanced source code management
Select behavior Dont trigger build on commit notification
This disables the Started by an SCM change
For people still looking for a solution, go to configuration for the multi branch pipeline, under Property Strategy, choose "Suppress Automatic SCM Triggering".
Note: This is available on cloudbees version of Jenkins. I am not sure, if it matters.
https://support.cloudbees.com/hc/en-us/articles/360026953651-Preventing-builds-from-getting-triggered-when-creating-a-new-multibranch-Pipeline-or-Organization-Folder?page=29
This is what I came up with. I was hoping for something less messy, but this does seem to work:
I have this as the build's properties:
properties([
pipelineTriggers([cron('H H 7 * *')])
])
I then have this function that defines the source of the build:
// check if the job was started by a timer
#NonCPS
def jobStartedByWhat() {
def startedByWhat = ''
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")) {
startedByWhat = 'timer'
}
if (causeDescription.contains("Started by user")) {
startedByWhat = 'user'
}
}
}
} catch(theError) {
echo "Error getting build cause: ${theError}"
}
return startedByWhat
}
def startedByWhat = jobStartedByWhat()
I can then evaluate the function at runtime so that if a build gets triggered because of a merge to master, it will not actually run:
node {
try {
checkout scm
if (env.BRANCH_NAME == 'master') {
if (startedByWhat == 'timer' || startedByWhat == 'user') {
..... RUN THE BUILD .....
} else {
.... EXIT WITHOUT RUNNING THE BUILD ....
I stumbled upon this as well. IMO an acceptable solution would be a filter for commit messages when checking out source code - this feature exists for regular Jobs but is missing for multibranch pipelines, see https://issues.jenkins-ci.org/browse/JENKINS-48296.
For those not using the git plugin, this method is a workaround for scripted pipelines (inspired by scarswell's answer):
def abortBuildIfTriggeredBySkippableCommit() {
lastCommitMessage = sh(
script: "${gitBinary} --no-pager log -1 --pretty=%B",
returnStdout: true
)
if (lastCommitMessage != null &&
lastCommitMessage.toString().contains('[maven-release-plugin]')) {
currentBuild.result = 'ABORTED'
error("Stopping build, it was triggered by the maven release plugin")
}
}
For declarative pipelines, there is a much more simple answer now. From the docs:
overrideIndexTriggers
Allows overriding default treatment of branch indexing triggers. If branch indexing triggers are disabled at the multibranch or organization label, options { overrideIndexTriggers(true) } will enable them for this job only. Otherwise, options { overrideIndexTriggers(false) } will disable branch indexing triggers for this job only.
It's a little backwards conceptually, but assuming your jobs are triggering off github webhooks by default, you set overrideIndexTriggers(false) to disable the automatic triggering.
If you are using Pipeline script from SCM then comment out the triggers section(either SCMPoll/BuildPeriodically option ) in Jenkins file as shown below.
//triggers {cron ('H/15 * * * *')}
//pipelineTriggers([pollSCM('H/15 * * * *')])
If you are using Pipeline script then disable the PollSCM/Build periodically(whichever is used) option.
One could disable the scm build trigger by disabling the webhook notification from git.
if (currentBuild.getBuildCauses().toString().contains('BranchIndexingCause') || currentBuild.getBuildCauses().toString().contains('Branch event')) {
print "INFO: Build skipped due to trigger being Branch Indexing"
currentBuild.result = 'ABORTED' // optional, gives a better hint to the user that it's been skipped, rather than the default which shows it's successful
return
}
timestamps{
node
{
stage('Getting Build Parameters')
{
print('build job')
}
}
}
if (currentBuild.getBuildCauses().toString().contains('BranchIndexingCause') || currentBuild.getBuildCauses().toString().contains('Branch event')) {
print "INFO: Build skipped due to trigger being Branch Indexing"
currentBuild.result = 'ABORTED' // optional, gives a better hint to the user that it's been skipped, rather than the default which shows it's successful
return
}
if (currentBuild.getBuildCauses().toString().contains('BranchIndexingCause') || currentBuild.getBuildCauses().toString().contains('Branch event')) {
print "INFO: Build skipped due to trigger being Branch Indexing"
currentBuild.result = 'ABORTED' // optional, gives a better hint to the user that it's been skipped, rather than the default which shows it's successful
return
}

Resources