Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,9 @@
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import net.sf.json.JSONObject;
import org.jenkinsci.plugins.structs.describable.CustomDescribableModel;
import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.StaplerRequest2;

Expand Down Expand Up @@ -104,7 +106,7 @@
}
}

@Extension public static class DescriptorImpl extends StepDescriptor {
@Extension public static class DescriptorImpl extends StepDescriptor implements CustomDescribableModel {

@Override public String getFunctionName() {
return "withEnv";
Expand Down Expand Up @@ -152,11 +154,34 @@
}
}
return b.toString();
} else if (overrides instanceof Map) {

Check warning on line 157 in src/main/java/org/jenkinsci/plugins/workflow/steps/EnvStep.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Partially covered line

Line 157 is only partially covered, one branch is missing
return ((Map<?, ?>) overrides).keySet()
.stream()
.filter(e -> e instanceof String)
.map(Object::toString)
.collect(Collectors.joining(", "));
} else {
return null;
}
}

@NonNull
@Override
public Map<String, Object> customInstantiate(@NonNull Map<String, Object> arguments) {
Map<String, Object> r = new HashMap<>(arguments);
final String key = "overrides";
Object overrides = r.get(key);
if (overrides instanceof Map) {
r.put(key, toKeyValueList((Map<?, ?>) overrides));
}
return r;
}

private static List<String> toKeyValueList(Map<?, ?> map) {
return map.entrySet().stream()
.map(m -> (String) m.getKey() + "=" + (m.getValue() == null ? "" : m.getValue().toString()))
.collect(Collectors.toList());
}
}

}
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
<div>
<p>
A list of environment variables to set, each in the form <code>VARIABLE=value</code>
or <code>VARIABLE=</code> to unset variables otherwise defined.
You may also use the syntax <code>PATH+WHATEVER=/something</code>
to prepend <code>/something</code> to <code>$PATH</code>.
</p>
<p>
Environment variables to set can also be defined in a map, each entry of the form <code>[VARIABLE: 'value']</code>.
</p>
</div>
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,14 @@
}
</pre>
<p>(Note that here we are using single quotes in Groovy, so the variable expansion is being done by the Bourne shell, not Jenkins.)
<p>Environment variables to be set may also be defined in a map. For example:
<p><pre>
node {
withEnv([MYTOOL_HOME: '/usr/local/mytool']) {
sh '$MYTOOL_HOME/bin/start'
}
}
</pre>
</p>
<p>See the documentation for the <code>env</code> singleton for more information on environment variables.
</div>
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,48 @@ public class EnvStepTest {
});
}

@Test public void mapArguments() throws Throwable {
sessions.then(j -> {
WorkflowJob p = j.createProject(WorkflowJob.class, "p");
p.setDefinition(new CpsFlowDefinition(
"node {\n" +
" withEnv(a: 1, b: 2, c: 'hello world', d: true, e: null) {\n" +
" echo(/a=$a b=$b c=$c d=$d e=${env.e}/)" +
" }\n" +
"}", true));
WorkflowRun b = j.assertBuildStatusSuccess(p.scheduleBuild2(0));
j.assertLogContains("a=1 b=2 c=hello world d=true e=null", b);
List<FlowNode> coreStepNodes = new DepthFirstScanner().filteredNodes(
b.getExecution(),
Predicates.and(
new NodeStepTypePredicate("withEnv"),
n -> n instanceof StepStartNode && !((StepStartNode) n).isBody()));
assertThat(coreStepNodes, Matchers.hasSize(1));
assertEquals("a, b, c, d, e", ArgumentsAction.getStepArgumentsAsString(coreStepNodes.get(0)));
});
}

@Test public void mapArgumentsAsMap() throws Throwable {
sessions.then(j -> {
WorkflowJob p = j.createProject(WorkflowJob.class, "p");
p.setDefinition(new CpsFlowDefinition(
"node {\n" +
" withEnv([A: 1, B: 2, C: 'hello world', D: true, E: null]) {\n" +
" echo(/A=$A B=$B C=$C D=$D E=${env.E}/)\n" +
" }\n" +
"}", true));
WorkflowRun b = j.assertBuildStatusSuccess(p.scheduleBuild2(0));
j.assertLogContains("A=1 B=2 C=hello world D=true E=null", b);
List<FlowNode> coreStepNodes = new DepthFirstScanner().filteredNodes(
b.getExecution(),
Predicates.and(
new NodeStepTypePredicate("withEnv"),
n -> n instanceof StepStartNode && !((StepStartNode) n).isBody()));
assertThat(coreStepNodes, Matchers.hasSize(1));
assertEquals("A, B, C, D, E", ArgumentsAction.getStepArgumentsAsString(coreStepNodes.get(0)));
});
}

@Test public void configRoundTrip() throws Throwable {
sessions.then(j -> {
configRoundTrip(Collections.emptyList(), j);
Expand Down