Tuesday, March 22, 2016

Environment variables injecter for Jenkins

Based on my previous post about injecting environment variables in Jenkins to be used between steps and in post-build steps.

I created a groovy script which injects passed environment variables and even treats them as shell comands.

inject_env_vars.groovy
// Inject environment variables using Groovy

import hudson.model.*
import hudson.AbortException
import groovy.json.StringEscapeUtils


def build = Thread.currentThread().executable


def injectEnvVars(envVars) {
    for (item in envVars) {
        build.addAction(
            new ParametersAction([
                new StringParameterValue(item.key, item.value)
            ])
        )
    }
}

def bash(cmd, env) {

    cmd = cmd as String

    // create a process for the shell
    pb = new ProcessBuilder(["bash", "-c", cmd])
    // make job workspace directory as the current one
    pb.directory(new File(env['WORKSPACE']))
    pb.environment().putAll(env)
    // capture messages sent to stderr
    pb.redirectErrorStream(true)
    shell = pb.start()
    shell.getOutputStream().close()
    // capture the output from the command
    def shellIn = shell.getInputStream()

    def reader = new BufferedReader(new InputStreamReader(shellIn))
    def builder = new StringBuilder()
    while ( (line = reader.readLine()) != null ) {
       builder.append(line)
       builder.append(System.getProperty("line.separator"))
    }
    result = builder.toString()

    // wait for the shell to finish and get the return code
    def exitStatus = shell.waitFor()

    try {
        shellIn.close();
    } catch (IOException ignoreMe) {}

    if (exitStatus) {
        throw new AbortException(result)
    }
    return result
}

def readPropsFile(filePath) {
    // https://en.wikipedia.org/wiki/.properties
    def props = new Properties()
    new File(filePath).withInputStream {
        stream -> props.load(stream)
    }
    return props
}

def readEnvSh(filePath) {
    vars = [:]
    file = new File(filePath)
    file.eachLine { line ->
      def matcher = (line =~ '^(.*)=(.*)$')
      if (matcher) {
        def name = matcher.group(1)
        def value = matcher.group(2)
        if (value.startsWith('"')) { value = value[1..-2] }
        vars[name] = value
      }
    }
    return vars
}

// add some useful environment variables
env = build.getEnvironment(listener)
if (!env.containsKey('BUILD_USER')) {
    def userCause = build.getCause(hudson.model.Cause$UserIdCause)
    def userName = userCause?.userId ?: 'Jenkins'
    injectEnvVars([
        'BUILD_USER': userName,
        'JOB_DIR': "${env.WORKSPACE}/../../jobs/${env.JOB_NAME}",
        'BUILD_DIR': "${env.WORKSPACE}/../../jobs/${env.JOB_NAME}/builds/${env.BUILD_ID}",
    ])
}

// inject build result string: http://javadoc.jenkins-ci.org/hudson/model/Result.html
injectEnvVars(['BUILD_RESULT': "${build.result}"])

for (item in binding.variables.clone()) {
    def varName = item.key
    def varValue = item.value
    if (!(varValue instanceof String)) {
        // skip values injected by Jenkins Groovy plugin
        continue
    }
    if (varName == '_') {
        // run the given script code
        new GroovyShell(
            new Binding([
                'env': build.getEnvironment(listener),
                 'injectEnvVars': this.&injectEnvVars,
                 'readEnvSh': this.&readEnvSh,
            ])
        ).evaluate(varValue)
    } else {
        varValue = StringEscapeUtils.escapeJava(varValue)
        varValue = bash("echo \"${varValue}\"", build.getEnvironment(listener))
        injectEnvVars(["${varName}": varValue])
    }
}

Usage examples:

hipchat_message='${BUILD_USER} <a href="$BUILD_URL">started deploying backend</a> to <b>${project} ${target}</b>'
evaluate(new File("/home/jenkins/workspace/devops/inject_env_vars.groovy"))

hipchat_message='$(/home/jenkins/workspace/devops/deploy/get_deploy_info.py)'
evaluate(new File("/home/jenkins/workspace/devops/inject_env_vars.groovy"))


// variable `_` is considered to contain script code inside `inject_env_vars.groovy`
_='''
_env = readEnvSh("${env.WORKSPACE}/env.sh")

if (env['BUILD_RESULT'] == 'SUCCESS') {
    // single quoted values will be expanded when passed to injectEnvVars
    _env['hipchat_message'] = 'Server build succeded: <a href="https://$SERVER_NAME/">$SERVER_NAME</a> (<a href="$BUILD_URL">Job</a>)'
} else {
    ...
}

injectEnvVars(_env)
'''
evaluate(new File("/home/jenkins/workspace/devops/inject_env_vars.groovy"))

These should be run as System Groovy script.

I hope this could be someday implemented as a plugin.

No comments:

Post a Comment