Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Jenkins: setting environment variables from a global function

I have a multi-branch pipeline project with a scripted Jenkinsfile in SCM. I want to set environment variables in my pipelines from a shared global function. My shared global function looks like this:

#!/usr/bin/groovy

def call(String envName = 'staging') {

  def json = libraryResource 'env-config.yaml'
  def config = readYaml text: json
  def envObj = config.environments.find{it.name == envName}
  environment {
    PROJECT = ${envObj.project}
    ARTIFACTS_REPOSITORY = ${envObj.artifacts_repository}
    DOCKER_REGISTRY_PREFIX = ${envObj.docker_registry_prefix}
  }
  println "${envObj}"
}

But in my Jenkinsfile, I don't see these set:

timestamps {
  withCredentials([[$class: 'UsernamePasswordMultiBinding', credentialsId: 'xxx', usernameVariable: 'USERNAME', passwordVariable: 'PASSWORD']]) {
    node('klm') {
      try {
        def revisionTag
        loadEnvConfig('staging')
        println "project=${env.PROJECT}, repo=${env.ARTIFACTS_REPOSITORY}"
        ...

env.PROJECT and env.ARTIFACTS_REPOSITORY are both null. If I return the envObj from the function and print the properties in the pipeline, I can see them. Is it possible to set environment variables from my function?

Edit: I just realized that I am unable to set any environment variables in my Jenkinsfile in a multi-branch pipeline. E.g. both these patterns don't work

timestamps {
  withCredentials([[$class: 'UsernamePasswordMultiBinding', credentialsId: 'xxx', usernameVariable: 'USERNAME', passwordVariable: 'PASSWORD']]) {
    node('klm') {
      try {
        environment {
          FOO = "bar"
        }
        println "project=${env.FOO}"
        ...

timestamps {
  withCredentials([[$class: 'UsernamePasswordMultiBinding', credentialsId: 'xxx', usernameVariable: 'USERNAME', passwordVariable: 'PASSWORD']]) {
    node('klm') {
      try {
        withEnv(["FOO=BAR"]) {
        println "project=${env.FOO}"
        ...
like image 939
golfradio Avatar asked Feb 07 '18 15:02

golfradio


People also ask

How do you set an environment variable globally?

Search and select System (Control Panel). Click on the Advanced system settings link and then click Environment Variables. Under the section System Variables, select the environment variable you want to edit, and click Edit. If the environment variable you want doesn't exist, click New.

Which Jenkins variable is provided by default and can also be set globally?

Jenkins environment variable is a global variable, exposed via env variable and used in Jenkins pipelines and anywhere into Jenkinsfile. Any value stored as the environment variable in the env variable is of string type.

How do you transfer workspace and environment variable in a pipeline to the next job?

Under Build Environment check Set environment variables through a file. give the path of that file here. If the environment variable is created in the first job then again you can save all the environment variable in a file and browse it using the above method. Install this plugin and go to job configuration paeg.


1 Answers

I'm not totally sure what environment is and why it isn't failing the run for you. PROJECT = ${envObj.project} is not valid Groovy, so that block is not getting executed. It looks like you are trying to use the environment directive for declarative pipelines, but, you are using scripted pipelines. The options below are only valid for scripted pipelines due to limitations of shared libraries and declarative pipelines.

In your current code, you aren't modifying any global script variables or returning values to be used in other steps, so they do not take any effect. You have a couple different approaches depending on the API you want to present to consumers.

The first option is to just modify the env global variable:

env.PROJECT = envObj.project
env.ARTIFACTS_REPOSITORY = envObj.artifacts_repository
env.DOCKER_REGISTRY_PREFIX = envObj.docker_registry_prefix

These will modify the global env state that will be available everywhere else in the script (unless they are overwritten or removed).

Another option, is to write the method using the existing withEnv step and allow consumers to supply a Closure body. Here is an example where I have renamed the function from loadEnvConfig to withEnvConfig:

def call(String envName = 'staging', Closure body) {
  // Previous code... 
  withEnv([
      "PROJECT=${envObj.project}",
      "ARTIFACTS_REPOSITORY=${envObj.artifacts_repository}",
      "DOCKER_REGISTRY_PREFIX=${envObj.docker_registry_prefix}",
  ]) {
    body()
  }
}

Jenkinsfile

withEnvConfig('staging') {
  println "In block project=${env.PROJECT}, repo=${env.ARTIFACTS_REPOSITORY}"
}
println "Out of block project=${env.PROJECT}, repo=${env.ARTIFACTS_REPOSITORY}"

Inside of the body they are set to the values of withEnv. Outside of the block, they are reset.

I much prefer this sort of pattern to modifying global state.

like image 63
mkobit Avatar answered Nov 15 '22 04:11

mkobit