I’ve been doing a bit of Jenkins plugin development lately and I have found that documentation can be a bit scarce. Like many projects out there, they show you how to drive a nail and then expect you to go off and build a house. I’m posting a little bit of what I have created in hopes of either sharing some much needed documentation or being shown a better way to do things.

While building my plugin I needed a way to inject Jenkins environment variables, called EnvVars in Jenkins API into some input. Not being able to find any built-in utilities to do this, I wrote my own. So, without further to-do, a util that has been useful (for met at least):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
import hudson.EnvVars;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class EnvTemplater {

    private static final Pattern VARIABLE_PATTERN = Pattern.compile(
            "(\\$\\{?[A-Za-z_]+\\}?)");
    private static final Pattern VARIABLE_NAME_PATTERN = Pattern.compile("([A-Za-z_]+)");

    /**
     * Given an input string, find environment var patterns and inject them into
     * the string from the current environment variables. The input of the string really
     * isn't important to this function, only the format of the variables within the string.
     * We look for two forms of variables:
     *  - $VAR_NAME
     *  - ${VAR_NAME}
     *
     * Note: The environment variables that we are using are not _true_ environment 
     * variables, but rather Jenkins Environment variables (the same ones you would normally 
     * use to template within a normal Jenkins job).
     *
     * @param input The input string to be templated (possibly containing variable 
     *              expressions)
     * @param vars  The vars to use to inject into the input string
     * @return      A string where the variables have been replaced by Jenkins evn variable 
     *              value, if the variable exists.
     */
    public static String templateString(String input, EnvVars vars) {
        if (vars == null || input == null) {
            return input;
        }

        String output = input;
        Matcher match = VARIABLE_PATTERN.matcher(input);
        int replacementOffset = 0;

        // iterate through all of the variables that we find
        while (match.find()) {

            // find the variable name (without the $ and {} characters)
            String matchGroup = match.group();
            Matcher varNameMatcher = VARIABLE_NAME_PATTERN.matcher(matchGroup);
            while (varNameMatcher.find()) {
                // pull out variable name, replace using bounds of outer `match`
                String varNameMatchGroup = varNameMatcher.group(1);
                int startPos = match.start(1);
                int endPos = match.end(1);
                if (vars.containsKey(varNameMatchGroup)) {
                    String replacement = vars.get(varNameMatchGroup, matchGroup);
                    output = output.substring(0, replacementOffset + startPos)
                            + replacement
                            + output.substring(replacementOffset + endPos, output.length());

                    // replacement offset is used during string replacement to account for 
                    // the string growing or shrinking as we replace values (and the regex 
                    // positions being out of date).
                    replacementOffset +=  replacement.length() - (endPos - startPos);
                }
            }
        }
        return output;
    }
}