diff --git a/src/main/java/org/jenkinsci/plugins/workflow/steps/EnvStep.java b/src/main/java/org/jenkinsci/plugins/workflow/steps/EnvStep.java index 57a29744..05c93ce0 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/steps/EnvStep.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/steps/EnvStep.java @@ -24,6 +24,7 @@ package org.jenkinsci.plugins.workflow.steps; +import edu.umd.cs.findbugs.annotations.NonNull; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import hudson.EnvVars; import hudson.Extension; @@ -34,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.StaplerRequest; @@ -103,7 +106,7 @@ private static final class ExpanderImpl extends EnvironmentExpander { } } - @Extension public static class DescriptorImpl extends StepDescriptor { + @Extension public static class DescriptorImpl extends StepDescriptor implements CustomDescribableModel { @Override public String getFunctionName() { return "withEnv"; @@ -150,11 +153,34 @@ private static final class ExpanderImpl extends EnvironmentExpander { } } return b.toString(); + } else if (overrides instanceof Map) { + return ((Map) overrides).keySet() + .stream() + .filter(e -> e instanceof String) + .map(Object::toString) + .collect(Collectors.joining(", ")); } else { return null; } } + @NonNull + @Override + public Map customInstantiate(@NonNull Map arguments) { + Map 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 toKeyValueList(Map map) { + return map.entrySet().stream() + .map(m -> (String) m.getKey() + "=" + (m.getValue() == null ? "" : m.getValue().toString())) + .collect(Collectors.toList()); + } } } diff --git a/src/test/java/org/jenkinsci/plugins/workflow/steps/EnvStepTest.java b/src/test/java/org/jenkinsci/plugins/workflow/steps/EnvStepTest.java index 2922b2dd..acb71ea2 100644 --- a/src/test/java/org/jenkinsci/plugins/workflow/steps/EnvStepTest.java +++ b/src/test/java/org/jenkinsci/plugins/workflow/steps/EnvStepTest.java @@ -158,6 +158,52 @@ public class EnvStepTest { }); } + @Test public void mapArguments() { + story.addStep(new Statement() { + @Override public void evaluate() throws Throwable { + WorkflowJob p = story.j.jenkins.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 = story.j.assertBuildStatusSuccess(p.scheduleBuild2(0)); + story.j.assertLogContains("a=1 b=2 c=hello world d=true e=null", b); + List 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() { + story.addStep(new Statement() { + @Override public void evaluate() throws Throwable { + WorkflowJob p = story.j.jenkins.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 = story.j.assertBuildStatusSuccess(p.scheduleBuild2(0)); + story.j.assertLogContains("A=1 B=2 C=hello world D=true E=null", b); + List 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 Exception { story.addStep(new Statement() { @Override public void evaluate() throws Throwable {