DanOpi
DanOpi

Reputation: 143

Is there a way to set environment variables based on branch in a Declarative Jenkinsfile?

I'm hoping to find a way to remove duplicated stages in a Declarative Jenkinsfile by loading environment variables based on the branch.

Currently I have something like:

@Library("MySharedLibrary@v1") _

String tagBaseDev = "repo.org/myspace/image:dev"
String tagBaseTest = "repo.org/myspace/image:test"
String tagBaseProd = "repo.org/myspace/image:prod"

pipeline {
  agent none
  stages {
    // Always Run This
    stage ('Maven Build and Unit Tests') {
      agent {label 'docker-slave'}
      steps {
        sharedLibraryBuild mavenGoals:'clean package', additionalProps:['ci.env':'']
        stash 'artifacts'
      }
    }
    // Dev Only
    stage ('Build Dev Docker Image and Push') {
      when {
        branch 'dev'
      }
      agent {label 'docker-slave'}
      steps {
        unstash 'artifacts'
        sharedLibraryDockerImageBuildPush tag:"$tagBaseDev"
      }
    }
    // Test Only
    stage ('Build Test Docker Image and Push') {
      when {
        branch 'test'
      }
      agent {label 'docker-slave'}
      steps {
        unstash 'artifacts'
        sharedLibraryDockerImageBuildPush tag:"$tagBaseTest"
      }
    }
    // Prod Only
    stage ('Build Prod Docker Image and Push') {
      when {
        branch 'prod'
      }
      agent {label 'docker-slave'}
      steps {
        unstash 'artifacts'
        sharedLibraryDockerImageBuildPush tag:"$tagBaseProd"
      }
    }
  }
}

I want to be able to reduce that into one stage block and dynamically load in the needed $tagBaseXXX based on branch. This is just an example but I'm planning to have four or five variables that will have different values for each environment.

My thought was to create EnvDev, EnvTest, and EnvProd maps with corresponding values and then create a EnvMap which is a Map that correlates the branch name to Environment Map. For instance:

def EnvDev = [
  url: "dev.com",
  tag: "dev",
  var: "Dev Var"
]

def EnvProd = [
  url: "prod.com",
  tag: "prod",
  var: "prod Var"
]

def EnvMap = [
  dev: EnvDev,
  prod: EnvProd
]

I then try to create a Shared Library call that looks something like this:

def call(String branch, Map envMapping) {
    Map use_me = envMapping.get("${branch}")
    String url = use_me.get("URL")
    echo ("${url}")
}

With the idea being to pass the Map and pull the corresponding environment map based on the branch and then use the variables as needed.

So I have something like this:

@Library("MySharedLibrary@v1") _

def EnvDev = [
  url: "dev.com",
  tag: "dev",
  var: "Dev Var"
]

def EnvProd = [
  url: "prod.com",
  tag: "prod",
  var: "prod Var"
]

def EnvMap = [
  dev: EnvDev,
  prod: EnvProd
]

pipeline {
  agent {label 'docker-slave'}
  stages {
    stage ('Test Env Vars') {
      steps {
        echo "$env.GIT_BRANCH"
        sharedLibrarySetupEnv branch: "$env.GIT_BRANCH", evnMapping: EnvMap
      }
    }
  }
}

But I get the following error:

hudson.remoting.ProxyException: groovy.lang.MissingMethodException: No signature of method: setupEnv.call() is applicable for argument types: (java.util.LinkedHashMap) values: [[branch:dev, env_mapping:[dev:[url:dev.com, tag:dev, var:Dev Var], ...]]]

Possible solutions: call(java.lang.String, java.util.Map), wait(), any(), wait(long), main([Ljava.lang.String;), each(groovy.lang.Closure)

Is there an easier way to accomplish what I'm trying to do?

This is my first time trying to write a Shared Library function so I'm guessing it may just be some Groovy syntax/concept I'm not familiar with.

Thanks!

Upvotes: 3

Views: 2954

Answers (3)

DanOpi
DanOpi

Reputation: 143

The problem was with how I was trying to invoke the Shared Library function. I thought I was able to reference the variable names which led to the Jenkinsfile/pipeline passing a LinkedHashMap to the shared library and not two separate variables.

There are two solutions to this:

  1. Have the Shared Library call method take in a Map<String, Object> parms and within the call reference the variables with parms.varname.

Shared Library:

def call(Map<String, Object> parms) {
    echo "${parms.branch}"
    Map use_this_map = parms.envMapping.get(branch)
}

Jenkinsfile:

setupEnv branch: "$env.GIT_BRANCH", envMapping: EnvMap
  1. Don't pass the variable names in the Jenkinsfile and have the Shared Library call method take in corresponding variables.

Shared Library:

def call(String branch, Map<String, Map> envMapping) {
    echo "${branch}"
    Map use_this_map = envMapping.get(branch)
}

Jenkinsfile:

setupEnv $env.GIT_BRANCH, EnvMap

Upvotes: 1

yong
yong

Reputation: 13712

your function signature is def call(String branch, Map envMapping), but your calling is branch: xxx, env_mapping:xxx.

Change to sharedLibrarySetupEnv branch: "$env.GIT_BRANCH", envMapping: EnvMap

Upvotes: 2

user_9090
user_9090

Reputation: 1974

You can use the variable BRANCH_NAME and put it in a condition like below:-

if (env.BRANCH_NAME == master)
{
    //set all the environment variable you need
} else {
    //variable required if the condition doesn't match
}

You can use REGEX in the condition.

Upvotes: 0

Related Questions