diff --git a/src/main/resources/org/jenkinsci/plugins/workflow/steps/durable_task/DurableTaskStep/help-encoding.html b/src/main/resources/org/jenkinsci/plugins/workflow/steps/durable_task/DurableTaskStep/help-encoding.html
index 6434fa0d..0cf4977c 100644
--- a/src/main/resources/org/jenkinsci/plugins/workflow/steps/durable_task/DurableTaskStep/help-encoding.html
+++ b/src/main/resources/org/jenkinsci/plugins/workflow/steps/durable_task/DurableTaskStep/help-encoding.html
@@ -1,3 +1,12 @@
- Encoding of standard output, if it is being captured.
+ Encoding of process output.
+ In the case of returnStdout, applies to the return value of this step;
+ otherwise, or always for standard error, controls how text is copied to the build log.
+ If unspecified, uses the system default encoding of the node on which the step is run.
+ If there is any expectation that process output might include non-ASCII characters,
+ it is best to specify the encoding explicitly.
+ For example, if you have specific knowledge that a given process is going to be producing UTF-8
+ yet will be running on a node with a different system encoding
+ (typically Windows, since every Linux distribution has defaulted to UTF-8 for a long time),
+ you can ensure correct output by specifying: encoding: 'UTF-8'
diff --git a/src/test/java/org/jenkinsci/plugins/workflow/steps/durable_task/ShellStepTest.java b/src/test/java/org/jenkinsci/plugins/workflow/steps/durable_task/ShellStepTest.java
index 9b267319..5230dbb5 100644
--- a/src/test/java/org/jenkinsci/plugins/workflow/steps/durable_task/ShellStepTest.java
+++ b/src/test/java/org/jenkinsci/plugins/workflow/steps/durable_task/ShellStepTest.java
@@ -2,14 +2,15 @@
import com.google.common.base.Predicate;
import hudson.EnvVars;
+import hudson.FilePath;
import hudson.Functions;
import hudson.Launcher;
import hudson.LauncherDecorator;
-import hudson.Platform;
import hudson.model.BallColor;
import hudson.model.FreeStyleProject;
import hudson.model.Node;
import hudson.model.Result;
+import hudson.model.Slave;
import hudson.slaves.DumbSlave;
import hudson.slaves.EnvironmentVariablesNodeProperty;
import hudson.tasks.BatchFile;
@@ -39,10 +40,12 @@
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
import org.jvnet.hudson.test.BuildWatcher;
import org.jvnet.hudson.test.Issue;
import org.jvnet.hudson.test.JenkinsRule;
import org.jvnet.hudson.test.LoggerRule;
+import org.jvnet.hudson.test.SimpleCommandLauncher;
import org.jvnet.hudson.test.TestExtension;
import org.kohsuke.stapler.DataBoundConstructor;
@@ -52,6 +55,7 @@ public class ShellStepTest {
public static BuildWatcher buildWatcher = new BuildWatcher();
@Rule public JenkinsRule j = new JenkinsRule();
+ @Rule public TemporaryFolder tmp = new TemporaryFolder();
@Rule public LoggerRule logging = new LoggerRule();
@@ -197,7 +201,7 @@ public DescriptorImpl() {
s = new StepConfigTester(j).configRoundTrip(s);
assertEquals("echo hello", s.getScript());
assertFalse(s.isReturnStdout());
- assertEquals(DurableTaskStep.DurableTaskStepDescriptor.defaultEncoding, s.getEncoding());
+ assertNull(s.getEncoding());
assertFalse(s.isReturnStatus());
s.setReturnStdout(true);
s.setEncoding("ISO-8859-1");
@@ -207,12 +211,12 @@ public DescriptorImpl() {
assertEquals("ISO-8859-1", s.getEncoding());
assertFalse(s.isReturnStatus());
s.setReturnStdout(false);
- s.setEncoding(DurableTaskStep.DurableTaskStepDescriptor.defaultEncoding);
+ s.setEncoding("UTF-8");
s.setReturnStatus(true);
s = new StepConfigTester(j).configRoundTrip(s);
assertEquals("echo hello", s.getScript());
assertFalse(s.isReturnStdout());
- assertEquals(DurableTaskStep.DurableTaskStepDescriptor.defaultEncoding, s.getEncoding());
+ assertEquals("UTF-8", s.getEncoding());
assertTrue(s.isReturnStatus());
}
@@ -233,6 +237,30 @@ public DescriptorImpl() {
j.assertLogContains("truth is 0 but falsity is 1", j.assertBuildStatusSuccess(p.scheduleBuild2(0)));
}
+ @Issue("JENKINS-31096")
+ @Test public void encoding() throws Exception {
+ // Like JenkinsRule.createSlave but passing a system encoding:
+ Slave remote = new DumbSlave("remote", tmp.getRoot().getAbsolutePath(),
+ new SimpleCommandLauncher("'" + System.getProperty("java.home") + "/bin/java' -Dfile.encoding=ISO-8859-2 -jar '" + new File(j.jenkins.getJnlpJars("slave.jar").getURL().toURI()) + "'"));
+ j.jenkins.addNode(remote);
+ j.waitOnline(remote);
+ WorkflowJob p = j.createProject(WorkflowJob.class, "p");
+ FilePath ws;
+ while ((ws = remote.getWorkspaceFor(p)) == null) {
+ Thread.sleep(100);
+ }
+ ws.child("message").write("Čau ty vole!\n", "ISO-8859-2");
+ p.setDefinition(new CpsFlowDefinition("node('remote') {if (isUnix()) {sh 'cat message'} else {bat 'type message'}}", true));
+ j.assertLogContains("Čau ty vole!", j.buildAndAssertSuccess(p));
+ p.setDefinition(new CpsFlowDefinition("node('remote') {echo(/received: ${isUnix() ? sh(script: 'cat message', returnStdout: true) : bat(script: '@type message', returnStdout: true)}/)}", true)); // http://stackoverflow.com/a/8486061/12916
+ j.assertLogContains("received: Čau ty vole!", j.buildAndAssertSuccess(p));
+ p.setDefinition(new CpsFlowDefinition("node('remote') {if (isUnix()) {sh script: 'cat message', encoding: 'US-ASCII'} else {bat script: 'type message', encoding: 'US-ASCII'}}", true));
+ j.assertLogContains("�au ty vole!", j.buildAndAssertSuccess(p));
+ ws.child("message").write("¡Čau → there!\n", "UTF-8");
+ p.setDefinition(new CpsFlowDefinition("node('remote') {if (isUnix()) {sh script: 'cat message', encoding: 'UTF-8'} else {bat script: 'type message', encoding: 'UTF-8'}}", true));
+ j.assertLogContains("¡Čau → there!", j.buildAndAssertSuccess(p));
+ }
+
@Issue("JENKINS-34021")
@Test public void deadStep() throws Exception {
logging.record(DurableTaskStep.class, Level.INFO).record(CpsStepContext.class, Level.INFO).capture(100);