66

I'm new to Jenkins pipeline; I'm defining a declarative syntax pipeline and I don't know if I can solve my problem, because I didn't find a solution.

In this example, I need to pass a variable to ansible plugin (in old version I use an ENV_VAR or injecting it from file with inject plugin) that variable comes from a script.

This is my perfect scenario (but it doesn't work because environment{}):

pipeline {
  agent { node { label 'jenkins-node'}}

  stages {
    stage('Deploy') {
      environment {
        ANSIBLE_CONFIG = '${WORKSPACE}/chimera-ci/ansible/ansible.cfg'
        VERSION = sh("python3.5 docker/get_version.py")
      }
      steps {
        ansiblePlaybook credentialsId: 'example-credential', extras: '-e version=${VERSION}', inventory: 'development', playbook: 'deploy.yml'
      }
    }
  }
}

I tried other ways to test how env vars work in other post, example:

pipeline {
  agent { node { label 'jenkins-node'}}

  stages {
    stage('PREPARE VARS') {
      steps {
        script {
          env['VERSION'] = sh(script: "python3.5 get_version.py")
        }
        echo env.VERSION
      }
    }
  }
}

but "echo env.VERSION" return null.

Also tried the same example with: - VERSION=python3.5 get_version.py - VERSION=python3.5 get_version.py > props.file (and try to inject it, but didnt found how)

If this is not possible I will do it in the ansible role.

UPDATE

There is another "issue" in Ansible Plugin, to use vars in extra vars it must have double quotes instead of single.

ansiblePlaybook credentialsId: 'example-credential', extras: "-e version=${VERSION}", inventory: 'development', playbook: 'deploy.yml'
Ermiya Eskandary
  • 15,323
  • 3
  • 31
  • 44
DEL
  • 663
  • 1
  • 6
  • 6
  • regarding single _versus_ double quotes, that's not a plug-in issue but a Groovy feature instead [(string interpolation)](https://groovy-lang.org/syntax.html#_double_quoted_string). – Helder Magalhães Nov 29 '22 at 22:59

7 Answers7

133

You can create variables before the pipeline block starts. You can have sh return stdout to assign to these variables. You don't have the same flexibility to assign to environment variables in the environment stanza. So substitute in python3.5 get_version.py where I have echo 0.0.1 in the script here (and make sure your python script just returns the version to stdout):

def awesomeVersion = 'UNKNOWN'

pipeline {
  agent { label 'docker' }
  stages {
    stage('build') {
      steps {
        script {
          awesomeVersion = sh(returnStdout: true, script: 'echo 0.0.1').trim()
        }
      }
    }
    stage('output_version') {
      steps {
        echo "awesomeVersion: ${awesomeVersion}"
      }
    }
  }
}

The output of the above pipeline is:

awesomeVersion: 0.0.1
burnettk
  • 13,557
  • 4
  • 51
  • 52
  • 1
    I believe if you want to set an actual environment variable, you'll prepend `env.`. `env.awesomeVersion = sh(...` – Will Nov 30 '17 at 19:33
  • 1
    you can read environment variables in a Jenkinsfile using env.SOMETHING. not sure if you can also update them that way. luckily, the OP didn't actually need environment variables. Kevin Higgins' answer is also worth a look. Your mileage may vary, since `echo` is usable in places where your custom scripts are not. – burnettk Dec 01 '17 at 15:31
  • 2
    FYI I tried this WITHOUT declaring the variable before the pipeline block, and it also works. I'm not against declaring the variable as shown, it makes it explicit that it is intended to be used in more than one stage, just want to say it isn't required. – Tony May 31 '18 at 14:28
  • Re: my previous comment, I used Jenkins ver. 2.111 – Tony May 31 '18 at 14:36
  • I would recommend using a `withEnv` wrapper in the 2nd stage, so that you can use the variable in the context of `sh`. Otherwise, it will print an empty string. At least it does with Jenkins 2.124. – Sergio Dec 05 '18 at 22:15
  • what if I want to set this during the pipeline? – Christian Matthew Feb 11 '20 at 22:00
  • Should also trim out the trailing newline character from the output of `sh` command – Sniper Jun 28 '21 at 11:23
31

In Jenkins 2.76 I was able to simplify the solution from @burnettk to:

pipeline {
  agent { label 'docker' }
  environment {
    awesomeVersion = sh(returnStdout: true, script: 'echo 0.0.1')
  }
  stages {
    stage('output_version') {
      steps {
        echo "awesomeVersion: ${awesomeVersion}"
      }
    }
  }
}
Kevin Somers-Higgins
  • 957
  • 1
  • 10
  • 12
  • 5
    I tried this one, but if I set awesomeVersion in another stage before output_verison stage, thats not updated in the variable. – Nirmal Jose Jan 28 '18 at 14:30
  • 5
    Note this solution is not a simplification of burnettk's solution. It is different in that it sets an environment variable. burnettk's solution uses a Groovy variable. That is why updating it in another stage does not work as desired. – Tony May 31 '18 at 14:40
  • 3
    Still +1 b/c it's quite useful to know you can set an environment variable with an `sh` step. – Tony May 31 '18 at 14:42
5

Using the "pipeline utility steps" plugin, you can define general vars available to all stages from a properties file. For example, let props.txt as:

version=1.0
fix=alfa

and mix script and declarative Jenkins pipeline as:

def props
def VERSION
def FIX
def RELEASE

node {
   props = readProperties file:'props.txt'
   VERSION = props['version']
   FIX = props['fix']
   RELEASE = VERSION + "_" + FIX
}

pipeline {
   stages {
      stage('Build') {
         echo ${RELEASE}
      }
   }
}
dpinya
  • 542
  • 7
  • 16
4

A possible variation of the main answer is to provide variable using another pipeline instead of a sh script.

example (set the variable pipeline) : my-set-env-variables pipeline

script
{
    env.my_dev_version = "0.0.4-SNAPSHOT"
    env.my_qa_version  = "0.0.4-SNAPSHOT"
    env.my_pp_version  = "0.0.2"
    env.my_prd_version = "0.0.2"
    echo " My versions  [DEV:${env.my_dev_version}] [QA:${env.my_qa_version}] [PP:${env.my_pp_version}] [PRD:${env.my_prd_version}]"
}

(use these variables) in a another pipeline my-set-env-variables-test

script 
{
    env.dev_version = "NOT DEFINED DEV"
    env.qa_version  = "NOT DEFINED QA"
    env.pp_version  = "NOT DEFINED PP"
    env.prd_version = "NOT DEFINED PRD"
}

stage('inject variables') {

    echo "PRE DEV version = ${env.dev_version}"
    script 
    {
       // call set variable job
       def variables = build job: 'my-set-env-variables'
       def vars = variables.getBuildVariables()
      //println "found variables" + vars
      env.dev_version = vars.my_dev_version
      env.qa_version  = vars.my_qa_version
      env.pp_version  = vars.my_pp_version
      env.prd_version = vars.my_prd_version
    }
}

stage('next job') {
    echo "NEXT JOB DEV version = ${env.dev_version}"
    echo "NEXT JOB QA version = ${env.qa_version}"
    echo "NEXT JOB PP version = ${env.pp_version}"
    echo "NEXT JOB PRD version = ${env.prd_version}"

}


Emmanuel B.
  • 226
  • 2
  • 7
1

For those who wants the environment's key to be dynamic, the following code can be used:

stage('Prepare Environment') {
    steps {
        script {
            def data = [
              "k1": "v1",
              "k2": "v2",
            ]
            data.each { key ,value ->
                env."$key" = value
                // env[key] = value // Deprecated, this can be used as well, but need approval in sandbox ScriptApproval page
            }
        }
    }
}
0

You can also dump all your vars into a file, and then use the '-e @file' syntax. This is very useful if you have many vars to populate.

steps {
  echo "hello World!!"
  sh """
  var1: ${params.var1}
  var2: ${params.var2}
  " > vars
  """
  ansiblePlaybook inventory: _inventory, playbook: 'test-playbook.yml', sudoUser: null, extras: '-e @vars'
}
krad
  • 1,321
  • 1
  • 15
  • 12
-1

You can do use library functions in the environments section, like so:

@Library('mylibrary') _ // contains functions.groovy with several functions.

pipeline {
  environment {
    ENV_VAR = functions.myfunc()
  }

  …

}
daevski
  • 101
  • 3
  • 9