I am trying to create a Jenkinsfile to handle different steps in prod vs dev environments. I was attempting to use the anyOf pattern with an expression that checks the JOB_URL environmental variable to determine which build server/build instruction to follow.
Jenkinsfile ends up looking something like the below:
stage('In Prod') {
when {
allOf {
expression { params.P1 == 'x' }
expression { params.P2 == 'y' }
expression { env.JOB_URL.contains('prod_server.com') }
}
}
...
}
stage('In Dev') {
when {
allOf {
expression { params.P1 == 'x' }
expression { params.P2 == 'y' }
expression { env.JOB_URL.contains('dev_server.com') }
}
}
...
}
Expected Behavior:
In Dev -> run In Dev step
In Prod -> run In Prod step
Actual Behavior:
In Dev -> run In Prod AND In Dev step
In Prod -> run In Prod step
Yes, I have checked to make sure that JOB_URL on the dev does not contain prod_server.com.
I have also tried !env.JOB_URL.contains('dev_server.com') as an additional expression for the prod step with the same result.
I only know enough groovy to get through Jenkins, and am somewhat new to Jenkins pipeline syntax, so maybe I've misunderstood the behavior here, but from what I understand from the Jenkins expression documentation:
when returning strings from your expressions they must be
converted to booleans or return null to evaluate to false. Simply
returning "0" or "false" will still evaluate to "true".
And as a sanity check, I confirmed that the groovy documentation says contains should be returning a boolean.
You can use a regular expression comparator in the expression to check the one of these environment variables:
built-in: JENKINS_URL and BUILD_URL (source built-in var)
plugin-provided JOB_URL (exists but can't find source)
Note: environment variable are exposed through the reserved environment variable map env (using env variable), e.g. server_url = env.JENKINS_URL.
Try something like this:
pipeline {
agent none
parameters {
string(name: 'P1', defaultValue: 'x', description: '')
string(name: 'P2', defaultValue: 'y', description: '')
}
stages {
stage('Init') {
steps {
echo "params = ${params.toString()}"
echo "env.JENKINS_URL = ${env.JENKINS_URL}"
}
}
stage('In Prod') {
when {
allOf {
expression { params.P1 == 'x' }
expression { params.P2 == 'y' }
expression { env.JENKINS_URL ==~ /.*prod_server.com.*/ }
}
}
steps {
echo "Prod"
}
}
stage('In Dev') {
when {
allOf {
expression { params.P1 == 'x' }
expression { params.P2 == 'y' }
expression { env.JENKINS_URL ==~ /.*dev_server.com.*/ }
}
}
steps {
echo "DEV"
}
}
}
}
Related
I'm defining a Jenkins declarative pipeline and having a hard time configuring a step to not execute if two strings are equal.
I've tried several things but string comparison doesn't work.
Here's my current state:
stages {
stage('Check if image has changed') {
steps {
script {
OLD_DIGEST = sh(returnStdout: true, script: "podman manifest inspect registry/myimage:11 2>/dev/null | jq .config.digest").trim()
NEW_DIGEST = sh(returnStdout: true, script: "podman inspect --format='sha256:{{.Id}}' myimage:11-tmp").trim()
}
sh "echo previous digest:${OLD_DIGEST}, new digest:${NEW_DIGEST}"
}
}
stage('Release') {
when {
allOf {
expression { env.RELEASE != null && env.RELEASE == "true" }
expression { env.OLD_DIGEST != env.NEW_DIGEST }
}
}
steps {
sh "echo Releasing image..."
sh "podman image push myimage:11-tmp registry/myimage:11.${DATE_TIME}"
sh "podman image push myimage:11-tmp registry/myimage:11"
}
}
}
More specifically, the issues lies in the when:
allOf {
expression { env.RELEASE != null && env.RELEASE == "true" }
expression { env.OLD_DIGEST != env.NEW_DIGEST }
}
The first expression works fine but I can't make the second work: even if OLD_DIGEST and NEW_DIGEST are different, the step is skipped.
Example output:
previous digest:sha256:736fd651afdffad2ee48a55a3fbab8de85552f183602d5bfedf0e74f90690e32, new digest:sha256:9003077f080f905d9b1a960b7cf933f04756df9560663196b65425beaf21203d
...
Stage "Release" skipped due to when conditional
I've also tried expression { OLD_DIGEST != NEW_DIGEST } (removing the env.) but now the result is the opposite: even when both strings are equals, the step is NOT skipped.
Output in this case:
previous digest:sha256:8d966d43262b818073ea23127dedb61a43963a7fafc5cffdca85141bb4aada57, new digest:sha256:8d966d43262b818073ea23127dedb61a43963a7fafc5cffdca85141bb4aada57
...
Releasing image...
I'm wondering if the issue lies in the expression or allOf at some point.
According to my tests in the latest 2023 version, env variables are initialized on any stage, so the previous values are being overrided.
Note: Inside when, the env vars has the default values, ignoring the expected values put in the previous stage. After that, in the steps, has the expected values(updated in the previous stage)
If you use global variables instead env variables, it works. I simulates your podman output with echo.
def OLD_DIGEST
def NEW_DIGEST
pipeline {
agent any
environment {
RELEASE = "true"
}
stages {
stage('Check if image has changed') {
steps {
script {
OLD_DIGEST = sh(returnStdout: true, script: "echo '1'").trim()
NEW_DIGEST = sh(returnStdout: true, script: "echo '1'").trim()
}
sh "echo previous digest:${OLD_DIGEST}, new digest:${NEW_DIGEST}"
}
}
stage('Release') {
when {
allOf {
expression { env.RELEASE != null && env.RELEASE == "true" }
expression { OLD_DIGEST != NEW_DIGEST }
}
}
steps {
sh "echo Releasing image..."
}
}
}
}
when OLD_DIGEST = 1 && NEW_DIGEST = 1 , the stage is skipped
if there are different, the stage is executed
The root cause of my issue was the output of my two strings to compare which was indeed different: one was "xxx" while the other was xxx but Jenkins output doesn't show the double quotes.
The correct Jenkins comparison, as stated in the comments, is expression { OLD_DIGEST != NEW_DIGEST } (without env.).
I would like to set a boolean variable to true if one of my environment variables equals a string. I only want to run my test stage if this RUN_TESTS var is true.
pipeline{
environment {
RUN_TESTS = expression { "${env.JOB_BASE_NAME}" == 'Test Pipeline' }
}
stages{
stage('test'){
when {
expression { RUN_TESTS }
}
steps{
// run my tests.......
}
}
}
The above is not working though.
How can I set a boolean variable based on the value of an environment variable that I can then use to conditionally run a pipeline stage?
It looks like you do not have to set environment variable in this case. You can evaluate expression directly in your stage:
pipeline{
stages{
stage('test'){
when {
expression { "${env.JOB_BASE_NAME}" == 'Test Pipeline' }
}
steps{
// run my tests.......
}
}
}
On the other hand if you still have to set the environment variable there are few ways to do it. Jenkins says:
Environment variable values must either be single quoted, double quoted, or function calls.
Wrap it in double quoted expression:
environment {
RUN_TESTS = "${env.JOB_BASE_NAME == 'Test Pipeline'}"
}
Wrap this expression in the function call, something like this:
def run_tests() {
return "${env.JOB_BASE_NAME}" == 'Test Pipeline'
}
pipeline{
environment {
RUN_TESTS = run_tests()
}
stages{
stage('test'){
when {
expression { RUN_TESTS }
}
steps{
// run my tests.......
}
}
}
But in general I would go for first approach
I want by pipeline to run on any branch that matches the pattern alerta/ (and anything beyond the slash (/).
So I have tried the following 2 approaches in terms of Jenkinsfile
expression {
env.BRANCH_NAME ==~ 'alerta\/.*'
}
expression {
env.BRANCH_NAME == '*/alerta/*'
}
both of them have the corresponding stages being skipped.
How should I format my when conditional to meet my purpose?
You forgot to return the state in the expression
expression
Execute the stage when the specified Groovy expression evaluates to
true, for example: when { expression { return params.DEBUG_BUILD } }
Note that when returning strings from your expressions they must be
converted to booleans or return null to evaluate to false. Simply
returning "0" or "false" will still evaluate to "true".
pipeline {
agent any
stages {
stage('alerta-branch'){
when{
expression {
return env.BRANCH_NAME ==~ /alerta\/.*/
}
}
steps {
echo 'run this stage - ony if the branch contains alerta in branch name'
}
}
}
}
This should work for you
I'm trying to skip a stage based a groovy variable and that variable value will be calculated in another stage.
In the below example, Validate stage is conditionally skipped based Environment variable VALIDATION_REQUIRED which I will pass while building/triggering the Job. --- This is working as expected.
Whereas the Build stage always runs even though isValidationSuccess variable is set as false.
I tried changing the when condition expression like { return "${isValidationSuccess}" == true ; } or { return "${isValidationSuccess}" == 'true' ; } but none worked.
When printing the variable it shows as 'false'
def isValidationSuccess = true
pipeline {
agent any
stages(checkout) {
// GIT checkout here
}
stage("Validate") {
when {
environment name: 'VALIDATION_REQUIRED', value: 'true'
}
steps {
if(some_condition){
isValidationSuccess = false;
}
}
}
stage("Build") {
when {
expression { return "${isValidationSuccess}"; }
}
steps {
sh "echo isValidationSuccess:${isValidationSuccess}"
}
}
}
At what phase does the when condition will be evaluated.
Is it possible to skip the stage based on the variable using when?
Based on a few SO answers, I can think of adding conditional block as below, But when options look clean approach. Also, the stage view shows nicely when that particular stage is skipped.
script {
if(isValidationSuccess){
// Do the build
}else {
try {
currentBuild.result = 'ABORTED'
} catch(Exception err) {
currentBuild.result = 'FAILURE'
}
error('Build not happened')
}
}
References:
https://jenkins.io/blog/2017/01/19/converting-conditional-to-pipeline/
stage("Build") {
when {
expression { isValidationSuccess == true }
}
steps {
// do stuff
}
}
when validates boolean values so this should be evaluate to true or false.
Source
I have three stages in jenkins pipeline script viz (1) precheck, (2) build-prod & (3) build-dr.
I have stated the input step for manual trigger at stage "build-dr"
My pipeline is condition based i.e based on user parameter in precheck stage .
Condition1: "precheck" -> "build-prod" and then "build-dr" is executed.
Condition2: "precheck" and then "build-dr" is executed (skips build-prod).
I need the input step in Condition1 and is working fine however the input step should not be executed for Condition2 i.e no popup msg with input step. Please let me know how can we put a condition around input step in stage 3 build-dr so it does not execute input step when the pipleline skips (2) build-prod.
Jenkins pipeline Script Code:
agent any
stages {
stage ("Pre-Check Parameters") {
steps {
echo "Pre-Check called in pipeline"
}
}
stage ("build-prod") {
when {
expression { params.region == 'prod_only' || params.region == 'prod_and_dr' }
}
steps {
build 'job4'
}
}
stage ("build-dr") {
input{
message "Proceed or Abort"
submitter "user1,admin"
parameters {
string(name:'username', defaultValue: 'user1', description: 'Username of the user pressing Ok')
}
}
when {
expression { params.region == 'dr_only' || params.region == 'prod_and_dr'}
}
steps {
build 'job5'
}
}
}
}
Kindly suggest.
You are currently using the input directive as described here but this prevents you to make this input conditional. You actually have to use the Input Step. Instead of adding the input field directly below the stage directive you move this into the steps block of your stage and add a script block around it to use if/else conditionals.
And take care to remove the curly brackets around you input step and to add a colon after each property.
What you have to do now is to adapt this line to your requirements:
if(Condition1 == true) Depenending on the value of your parameter.
stage("build-dr") {
when {
expression { params.region == 'dr_only' || params.region == 'prod_and_dr'}
}
steps {
script {
if(Condition1 == true) {
input message: "Proceed or Abort", submitter: "user1,admin",
parameters: [string(name:'username', defaultValue: 'user1', description: 'Username of the user pressing Ok')]
}
}
build 'job5'
}
}
Alternatively you can use an environment declaration block to declare a variable and assign a specific value to it if your second stage will be executed. But any environment value will always be typed as a String this is important for the if/else conditional. A good way to assign a value to the variable would be to add a post section to your second stage.
pipeline {
agent any
environment {
STAGE_2_EXECUTED = "0"
}
stages{
stage("....") {
steps {
....
}
}
stage("Second stage") {
when {
expression { /* Your condition */ }
}
steps {
....
}
post {
always {
script {
STAGE_2_EXECUTED = "1";
}
}
}
}
stage("Third Stage") {
steps {
script {
if(STAGE_2_EXECUTED == "1") {
input message: "Proceed or Abort", submitter: "user1,admin",
parameters: [string(name:'username', defaultValue: 'user1', description: 'Username of the user pressing Ok')]
}
}
build 'job5'
}
}
}
}