Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
JENKINS-20705 - Environment Script does not work on Windows Master
  • Loading branch information
dawidmalina committed Nov 16, 2015
1 parent 545b348 commit 2ad196a
Show file tree
Hide file tree
Showing 9 changed files with 167 additions and 35 deletions.
55 changes: 35 additions & 20 deletions src/main/java/com/lookout/jenkins/EnvironmentScript.java
Expand Up @@ -17,6 +17,7 @@
import hudson.tasks.BuildWrapper;
import hudson.tasks.BuildWrapperDescriptor;
import hudson.tasks.Shell;
import hudson.util.ListBoxModel;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
Expand All @@ -34,18 +35,26 @@
import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.StaplerRequest;

import com.lookout.jenkins.commands.Commands;
import com.lookout.jenkins.commands.PowerShell;
import com.lookout.jenkins.commands.Shebangs;
import com.lookout.jenkins.commands.UnixShell;
import com.lookout.jenkins.commands.WinBatch;

/**
* Runs a specific chunk of code before each build, parsing output for new environment variables.
*
* @author Jørgen P. Tjernø
*/
public class EnvironmentScript extends BuildWrapper implements MatrixAggregatable {
private final String script;
private final String scriptType;
private final boolean onlyRunOnParent;

@DataBoundConstructor
public EnvironmentScript(String script, boolean onlyRunOnParent) {
public EnvironmentScript(String script, String scriptType, boolean onlyRunOnParent) {
this.script = script;
this.scriptType = scriptType;
this.onlyRunOnParent = onlyRunOnParent;
}

Expand All @@ -56,6 +65,14 @@ public String getScript() {
return script;
}

/**
* We will use this from the <tt>config.jelly</tt>.
* @return
*/
public String getScriptType() {
return scriptType;
}

/**
* @return Whether or not we only run this on the {@link MatrixBuild} parent, or on the individual {@link MatrixRun}
* s.
Expand Down Expand Up @@ -103,10 +120,18 @@ private Environment generateEnvironment(AbstractBuild<?, ?> build,
ByteArrayOutputStream commandOutput = new ByteArrayOutputStream();
int returnCode = -1;
try {
// Calculate extension
String extension = ".sh";
if (Commands.POWER_SHELL.equals(scriptType)) {
extension = ".ps1";
} else if (Commands.BATCH_SCRIPT.equals(scriptType)) {
extension = ".bat";
}

// Make sure prefix will always be more than 3 letters
final String prefix = "env-" + build.getProject().getName();
// Create a file in the system temporary directory with our script in it.
scriptFile = ws.createTextTempFile(prefix, ".sh", script, false);
scriptFile = ws.createTextTempFile(prefix, extension, script, false);

// Then we execute the script, putting STDOUT in commandOutput.
returnCode = launcher.launch().cmds(buildCommandLine(scriptFile))
Expand Down Expand Up @@ -173,25 +198,15 @@ public void buildEnvVars(Map<String, String> env) {

// Mostly stolen from hudson.tasks.Shell.buildCommandLine.
public String[] buildCommandLine(FilePath scriptFile) {
// Respect shebangs
if (script.startsWith("#!")) {
// Find first line, or just entire script if it's one line.
int end = script.indexOf('\n');
if (end < 0)
end = script.length();

String shell = script.substring(0, end).trim();
shell = shell.substring(2);

List<String> args = new ArrayList<String>(Arrays.asList(Util.tokenize(shell)));
args.add(scriptFile.getRemote());

return args.toArray(new String[args.size()]);
if (Commands.POWER_SHELL.equals(scriptType)) {
return PowerShell.buildCommandLine(scriptFile);
} else if (Commands.BATCH_SCRIPT.equals(scriptType)) {
return WinBatch.buildCommandLine(scriptFile);
} else {
Shell.DescriptorImpl shellDescriptor = Jenkins.getInstance()
.getDescriptorByType(Shell.DescriptorImpl.class);
String shell = shellDescriptor.getShellOrDefault(scriptFile.getChannel());
return new String[] { shell, "-xe", scriptFile.getRemote() };
if (Commands.isShebangs(script)) {
return Shebangs.parseCommandLine(script, scriptFile);
}
return UnixShell.buildCommandLine(scriptFile);
}
}

Expand Down
15 changes: 15 additions & 0 deletions src/main/java/com/lookout/jenkins/commands/Commands.java
@@ -0,0 +1,15 @@
package com.lookout.jenkins.commands;

public class Commands {

public final static String POWER_SHELL = "powerShell";
public final static String BATCH_SCRIPT = "batchScript";

public static boolean isShebangs(String script) {
if (script.startsWith("#!")) {
return true;
}
return false;
}

}
21 changes: 21 additions & 0 deletions src/main/java/com/lookout/jenkins/commands/PowerShell.java
@@ -0,0 +1,21 @@
package com.lookout.jenkins.commands;

import java.util.ArrayList;
import java.util.List;

import hudson.FilePath;

public class PowerShell {

// Mostly stolen from managed-scripts-plugin
public static String[] buildCommandLine(FilePath scriptFile) {
List<String> cml = new ArrayList<String>();
cml.add("powershell.exe");
cml.add("-ExecutionPolicy");
cml.add("ByPass");
cml.add("& \'" + scriptFile.getRemote() + "\'");

return (String[]) cml.toArray(new String[cml.size()]);
}

}
26 changes: 26 additions & 0 deletions src/main/java/com/lookout/jenkins/commands/Shebangs.java
@@ -0,0 +1,26 @@
package com.lookout.jenkins.commands;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import hudson.FilePath;
import hudson.Util;

public class Shebangs {

public static String[] parseCommandLine(String script, FilePath scriptFile) {
// Find first line, or just entire script if it's one line.
int end = script.indexOf('\n');
if (end < 0)
end = script.length();

String interpreter = script.substring(0, end).trim();
interpreter = interpreter.substring(2);

List<String> cml = new ArrayList<String>(Arrays.asList(Util.tokenize(interpreter)));
cml.add(scriptFile.getRemote());

return cml.toArray(new String[cml.size()]);
}
}
25 changes: 25 additions & 0 deletions src/main/java/com/lookout/jenkins/commands/UnixShell.java
@@ -0,0 +1,25 @@
package com.lookout.jenkins.commands;

import java.util.ArrayList;
import java.util.List;

import hudson.FilePath;
import hudson.tasks.Shell;
import jenkins.model.Jenkins;

public class UnixShell {

public static String[] buildCommandLine(FilePath scriptFile) {
Shell.DescriptorImpl shellDescriptor = Jenkins.getInstance()
.getDescriptorByType(Shell.DescriptorImpl.class);
final String shell = shellDescriptor.getShellOrDefault(scriptFile.getChannel());

List<String> cml = new ArrayList<String>();
cml.add(shell);
cml.add("-xe");
cml.add(scriptFile.getRemote());

return (String[]) cml.toArray(new String[cml.size()]);
}

}
21 changes: 21 additions & 0 deletions src/main/java/com/lookout/jenkins/commands/WinBatch.java
@@ -0,0 +1,21 @@
package com.lookout.jenkins.commands;

import java.util.ArrayList;
import java.util.List;

import hudson.FilePath;

public class WinBatch {

// Mostly stolen from managed-scripts-plugin
public static String[] buildCommandLine(FilePath scriptFile) {
List<String> cml = new ArrayList<String>();
cml.add("cmd");
cml.add("/c");
cml.add("call");
cml.add(scriptFile.getRemote());

return (String[]) cml.toArray(new String[cml.size()]);
}

}
Expand Up @@ -3,8 +3,7 @@
<j:when test="${descriptor.isMatrix(request)}">
<f:entry title="${%Run only on parent}"
help="${resURL}/plugin/environment-script/help-onlyRunOnParent.html">
<f:checkbox name="onlyRunOnParent"
checked="${instance.shouldOnlyRunOnParent()}" />
<f:checkbox name="onlyRunOnParent" checked="${instance.shouldOnlyRunOnParent()}" />
</f:entry>
</j:when>
<j:otherwise>
Expand All @@ -14,10 +13,17 @@
</j:otherwise>
</j:choose>

<f:entry name="scriptType" title="Choose Script Type" field="scriptType">
<select name="scriptType">
<f:option value="unixScript" selected="${instance.getScriptType() == 'unixScript'}">Unix script</f:option>
<f:option value="powerShell" selected="${instance.getScriptType() == 'powerShell'}">Powershell script</f:option>
<f:option value="batchScript" selected="${instance.getScriptType() == 'batchScript'}">Batch script</f:option>
</select>
</f:entry>

<f:entry title="${%Script content}"
description="This script is executed before each build, and any output it produces will be evaluated as environment variables (key=value)."
help="${resURL}/plugin/environment-script/help-script.html">
<f:textarea name="script"
value="${instance.getScript()}" />
<f:textarea name="script" value="${instance.getScript()}" />
</f:entry>
</j:jelly>
Expand Up @@ -42,7 +42,7 @@ public MatrixTestJob(String script, boolean onlyRunOnParent) throws Exception {
project.setExecutionStrategy(new DefaultMatrixExecutionStrategyImpl(true, null, null, null));

project.setAxes(new AxisList(new Axis("axis", "value1", "value2")));
project.getBuildWrappersList().add(new EnvironmentScript(script, onlyRunOnParent));
project.getBuildWrappersList().add(new EnvironmentScript(script, scriptType, onlyRunOnParent));
countBuilder = new CountBuilder();
project.getBuildersList().add(countBuilder);
build = jenkins.buildAndAssertSuccess(project);
Expand All @@ -64,6 +64,7 @@ public MatrixTestJob(String script, boolean onlyRunOnParent) throws Exception {
// Generate a random directory that we pass to the shell script.
File tempDir = Files.createTempDir();
String script = String.format(SCRIPT_COUNTER, tempDir.getPath());
String scriptType = "unixScript";

@Test
public void testWithParentOnly() throws Exception {
Expand Down
22 changes: 12 additions & 10 deletions src/test/java/com/lookout/jenkins/EnvironmentScriptTest.java
Expand Up @@ -26,16 +26,18 @@ class TestJob {
public FreeStyleBuild build;
public TaskListener listener;

public TestJob(String script) throws Exception {
public TestJob(String script, String scriptType) throws Exception {
listener = new StreamTaskListener(System.err, Charset.defaultCharset());
project = jenkins.createFreeStyleProject();
project.getBuildWrappersList().add(new EnvironmentScript(script, false));
project.getBuildWrappersList().add(new EnvironmentScript(script, scriptType, false));
project.setScm(new SingleFileSCM("envs", "foo_var=bar"));
build = jenkins.buildAndAssertSuccess(project);
jenkins.waitUntilNoActivity();
}
}

final static String UNIX_SCRIPT = "unixScript";

final static String SCRIPT_SIMPLE_VARIABLES =
"echo var1=one\n"
+ "echo var2=two\n"
Expand All @@ -60,13 +62,13 @@ public TestJob(String script) throws Exception {
+ "hello=world";

public void testWithEmptyScript() throws Exception {
TestJob job = new TestJob("");
TestJob job = new TestJob("", UNIX_SCRIPT);
assertEquals(Result.SUCCESS, job.build.getResult());
}

@Test
public void testWithSimpleVariables() throws Exception {
TestJob job = new TestJob(SCRIPT_SIMPLE_VARIABLES);
TestJob job = new TestJob(SCRIPT_SIMPLE_VARIABLES, UNIX_SCRIPT);
assertEquals(Result.SUCCESS, job.build.getResult());

EnvVars vars = job.build.getEnvironment(job.listener);
Expand All @@ -77,7 +79,7 @@ public void testWithSimpleVariables() throws Exception {

@Test
public void testWithDependentVariables() throws Exception {
TestJob job = new TestJob(SCRIPT_DEPENDENT_VARIABLES);
TestJob job = new TestJob(SCRIPT_DEPENDENT_VARIABLES, UNIX_SCRIPT);
assertEquals(Result.SUCCESS, job.build.getResult());

EnvVars vars = job.build.getEnvironment(job.listener);
Expand All @@ -89,7 +91,7 @@ public void testWithDependentVariables() throws Exception {

@Test
public void testWithOverridenVariables() throws Exception {
TestJob job = new TestJob(SCRIPT_OVERRIDDEN_VARIABLES);
TestJob job = new TestJob(SCRIPT_OVERRIDDEN_VARIABLES, UNIX_SCRIPT);
assertEquals(Result.SUCCESS, job.build.getResult());

EnvVars vars = job.build.getEnvironment(job.listener);
Expand All @@ -99,30 +101,30 @@ public void testWithOverridenVariables() throws Exception {

@Test
public void testReadingFileFromSCM() throws Exception {
TestJob job = new TestJob("cat envs");
TestJob job = new TestJob("cat envs", UNIX_SCRIPT);
assertEquals(Result.SUCCESS, job.build.getResult());
assertEquals("bar", job.build.getEnvironment(job.listener).get("foo_var"));
}

@Test
public void testWorkingDirectory() throws Exception {
TestJob job = new TestJob("echo hi >was_run");
TestJob job = new TestJob("echo hi >was_run", UNIX_SCRIPT);

// Make sure that the $PWD of the script is $WORKSPACE.
assertTrue(job.build.getWorkspace().child("was_run").exists());
}

@Test
public void testWithShebang() throws Exception {
TestJob job = new TestJob(SCRIPT_SHEBANG);
TestJob job = new TestJob(SCRIPT_SHEBANG, UNIX_SCRIPT);

assertEquals(Result.SUCCESS, job.build.getResult());
EnvVars vars = job.build.getEnvironment(job.listener);
assertEquals("world", vars.get("hello"));
}

public void testUTFHandling () throws Exception {
TestJob job = new TestJob(SCRIPT_UTF8);
TestJob job = new TestJob(SCRIPT_UTF8, UNIX_SCRIPT);
assertEquals(Result.SUCCESS, job.build.getResult());

EnvVars vars = job.build.getEnvironment(job.listener);
Expand Down

0 comments on commit 2ad196a

Please sign in to comment.