[groovy] Creating a Jenkins environment variable using Groovy

I think this is another simple question but I couldn't get any of the web solutions to work. My project takes in a version number. Each number can be separated by a '.' or a '_'. I want a variable that only displays the first two numbers.

I tried writing a groovy script that creates a Jenkins environment variable.
I want to take the first two digits instead of the entire string.

//Get the version parameter
def env = System.getenv()
def version = env['currentversion']
def m = version =~/\d{1,2}/
env = ['miniVersion':m[0].m[1]]

Am I doing this correctly, can I even create a new environment variable, and is there a better solution to this.

This question is related to groovy jenkins

The answer is


On my side it only worked this way by replacing an existing parameter.

def artifactNameParam = new StringParameterValue('CopyProjectArtifactName', 'bla bla bla')
build.replaceAction(new ParametersAction(artifactNameParam))

Additionally this script must be run with system groovy.

A groovy must be manually installed on that system and the bin dir of groovy must be added to path. Additionally in the lib folder I had to add jenkins-core.jar.

Then it was possible to modify a parameter in a groovy script and get the modified value in a batch script after to continue work.


The Jenkins EnvInject Plugin might be able to help you. It allows injecting environment variables into the build environment.

I know it has some ability to do scripting, so it might be able to do what you want. I have only used it to set simple properties (e.g. "LOG_PATH=${WORKSPACE}\logs").


Just had the same issue. Wanted to dynamically trigger parametrized downstream jobs based on the outcome of some groovy scripting.

Unfortunately on our Jenkins it's not possible to run System Groovy scripts. Therefore I had to do a small workaround:

  1. Run groovy script which creates a properties file where the environment variable to be set is specified

    def props = new File("properties.text")
    if (props.text == 'foo=bar') {
        props.text = 'foo=baz'
    } else {
        props.text = 'foo=bar'
    }
    
  2. Use env inject plugin to inject the variable written into this script

    Inject environment variable
    Property file path: properties.text
    

After that I was able to use the variable 'foo' as parameter for the parametrized trigger plugin. Some kind of workaround. But works!


As other answers state setting new ParametersAction is the way to inject one or more environment variables, but when a job is already parameterised adding new action won't take effect. Instead you'll see two links to a build parameters pointing to the same set of parameters and the one you wanted to add will be null.

Here is a snippet updating the parameters list in both cases (a parametrised and non-parametrised job):

import hudson.model.*

def build = Thread.currentThread().executable

def env = System.getenv()
def version = env['currentversion']
def m = version =~/\d{1,2}/
def minVerVal = m[0]+"."+m[1]

def newParams = null

def pl = new ArrayList<StringParameterValue>()
pl.add(new StringParameterValue('miniVersion', miniVerVal))

def oldParams = build.getAction(ParametersAction.class)

if(oldParams != null) {
  newParams = oldParams.createUpdated(pl)
  build.actions.remove(oldParams)
} else {
  newParams = new ParametersAction(pl)
}

build.addAction(newParams)

For me, the following also worked in Jenkins 2 (2.73.3)

Replace

def pa = new ParametersAction([new StringParameterValue("FOO", foo)])
build.addAction(pa)

with

def pa = new ParametersAction([new StringParameterValue("FOO", foo)], ["FOO"])
build.addAction(pa)

ParametersAction seems to have a second constructor which allows to pass in "additionalSafeParameters" https://github.com/jenkinsci/jenkins/blob/master/core/src/main/java/hudson/model/ParametersAction.java


For me the following worked on Jenkins 2.190.1 and was much simpler than some of the other workarounds:

matcher = manager.getLogMatcher('^.*Text we want comes next: (.*)$');

if (matcher.matches()) {
    def myVar = matcher.group(1);
    def envVar = new EnvVars([MY_ENV_VAR: myVar]);
    def newEnv = Environment.create(envVar);
    manager.build.environments.add(0, newEnv);
    // now the matched text from the LogMatcher is passed to an
    // env var we can access at $MY_ENV_VAR in post build steps
}

This was using the Groovy Script plugin with no additional changes to Jenkins.


You can also define a variable without the EnvInject Plugin within your Groovy System Script:

import hudson.model.*
def build = Thread.currentThread().executable
def pa = new ParametersAction([
  new StringParameterValue("FOO", "BAR")
])
build.addAction(pa)

Then you can access this variable in the next build step which (for example) is an windows batch command:

@echo off
Setlocal EnableDelayedExpansion
echo FOO=!FOO!

This echo will show you "FOO=BAR".

Regards


After searching around a bit, the best solution in my opinion makes use of hudson.model.EnvironmentContributingAction.

import hudson.model.EnvironmentContributingAction
import hudson.model.AbstractBuild 
import hudson.EnvVars

class BuildVariableInjector {

    def build
    def out

    def BuildVariableInjector(build, out) {
        this.build = build
        this.out = out
    }

    def addBuildEnvironmentVariable(key, value) {
        def action = new VariableInjectionAction(key, value)
        build.addAction(action)
        //Must call this for action to be added
        build.getEnvironment()
    }

    class VariableInjectionAction implements EnvironmentContributingAction {

        private String key
        private String value

        public VariableInjectionAction(String key, String value) {
            this.key = key
            this.value = value
        }

        public void buildEnvVars(AbstractBuild build, EnvVars envVars) {

            if (envVars != null && key != null && value != null) {
                envVars.put(key, value);
            }
        }

        public String getDisplayName() {
            return "VariableInjectionAction";
        }

        public String getIconFileName() {
            return null;
        }

        public String getUrlName() {
            return null;
        }
    }    
}

I use this class in a system groovy script (using the groovy plugin) within a job.

import hudson.model.*
import java.io.File;
import jenkins.model.Jenkins;    

def jenkinsRootDir = build.getEnvVars()["JENKINS_HOME"];
def parent = getClass().getClassLoader()
def loader = new GroovyClassLoader(parent)

def buildVariableInjector = loader.parseClass(new File(jenkinsRootDir + "/userContent/GroovyScripts/BuildVariableInjector.groovy")).newInstance(build, getBinding().out)

def projectBranchDependencies = [] 
//Some logic to set projectBranchDependencies variable

buildVariableInjector.addBuildEnvironmentVariable("projectBranchDependencies", projectBranchDependencies.join(","));

You can then access the projectBranchDependencies variable at any other point in your build, in my case, from an ANT script.

Note: I borrowed / modified the ideas for parts of this implementation from a blog post, but at the time of this posting I was unable to locate the original source in order to give due credit.


My environment was prior tooling such as Jenkins and was running with batch files (I know, I'm old). So those batch files (and their sub-batch files) are using environment variables. This was my piece of groovy script which injects environment variables. The names and parameters used are dummy ones.

// The process/batch which uses environment variables
def buildLabel = "SomeVersionNr"
def script = "startBuild.bat"
def processBuilder = new ProcessBuilder(script, buildLabel)

//Inject our environment variables
Map<String, String> env = processBuilder.environment()
env.put("ProjectRoot", "someLocation")
env.put("SomeVar", "Some")

Process p = processBuilder.start()
p.waitFor()

Of course if you set Jenkins up from scratch you would probably do it differently and share the variables in another way, or pass parameters around but this might come handy.