Skip to content

Commit 7b86df6

Browse files
authored
Merge pull request jenkinsci#32 from jglick/Jenkinsfile
Added a Jenkinsfile
2 parents fbafd78 + 0bbfe40 commit 7b86df6

File tree

8 files changed

+61
-45
lines changed

8 files changed

+61
-45
lines changed

Jenkinsfile

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
buildPlugin(jenkinsVersions: [null, '2.32.3'])

pom.xml

+3-2
Original file line numberDiff line numberDiff line change
@@ -64,12 +64,13 @@
6464
<properties>
6565
<jenkins.version>1.642.3</jenkins.version>
6666
<jenkins-test-harness.version>2.18</jenkins-test-harness.version>
67+
<workflow-step-api-plugin.version>2.7</workflow-step-api-plugin.version>
6768
</properties>
6869
<dependencies>
6970
<dependency>
7071
<groupId>org.jenkins-ci.plugins.workflow</groupId>
7172
<artifactId>workflow-step-api</artifactId>
72-
<version>2.7</version>
73+
<version>${workflow-step-api-plugin.version}</version>
7374
</dependency>
7475
<dependency>
7576
<groupId>org.jenkins-ci.plugins</groupId>
@@ -107,7 +108,7 @@
107108
<dependency>
108109
<groupId>org.jenkins-ci.plugins.workflow</groupId>
109110
<artifactId>workflow-step-api</artifactId>
110-
<version>2.6</version>
111+
<version>${workflow-step-api-plugin.version}</version>
111112
<classifier>tests</classifier>
112113
<scope>test</scope>
113114
</dependency>

src/main/java/org/jenkinsci/plugins/workflow/support/pickles/ComputerPickle.java

+2
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626

2727
import org.jenkinsci.plugins.workflow.pickles.Pickle;
2828
import com.google.common.util.concurrent.ListenableFuture;
29+
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
2930
import hudson.Extension;
3031
import hudson.model.Computer;
3132
import jenkins.model.Jenkins;
@@ -46,6 +47,7 @@ private ComputerPickle(Computer v) {
4647
@Override
4748
public ListenableFuture<Computer> rehydrate(FlowExecutionOwner owner) {
4849
return new TryRepeatedly<Computer>(1) {
50+
@SuppressFBWarnings(value="RCN_REDUNDANT_NULLCHECK_OF_NONNULL_VALUE", justification="TODO 1.653+ switch to Jenkins.getInstanceOrNull")
4951
@Override
5052
protected Computer tryResolve() {
5153
Jenkins j = Jenkins.getInstance();

src/main/java/org/jenkinsci/plugins/workflow/support/pickles/WorkspaceListLeasePickle.java

+2
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
package org.jenkinsci.plugins.workflow.support.pickles;
2626

2727
import com.google.common.util.concurrent.ListenableFuture;
28+
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
2829
import hudson.Extension;
2930
import hudson.FilePath;
3031
import hudson.model.Computer;
@@ -48,6 +49,7 @@ private WorkspaceListLeasePickle(WorkspaceList.Lease lease) {
4849

4950
@Override public ListenableFuture<?> rehydrate(FlowExecutionOwner owner) {
5051
return new TryRepeatedly<WorkspaceList.Lease>(1) {
52+
@SuppressFBWarnings(value="RCN_REDUNDANT_NULLCHECK_OF_NONNULL_VALUE", justification="TODO 1.653+ switch to Jenkins.getInstanceOrNull")
5153
@Override protected WorkspaceList.Lease tryResolve() throws InterruptedException {
5254
Jenkins j = Jenkins.getInstance();
5355
if (j == null) {

src/main/java/org/jenkinsci/plugins/workflow/support/steps/ExecutorStepExecution.java

+6-1
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,7 @@ private Object readResolve() {
263263
return new PlaceholderExecutable();
264264
}
265265

266+
@SuppressFBWarnings(value="RCN_REDUNDANT_NULLCHECK_OF_NONNULL_VALUE", justification="TODO 1.653+ switch to Jenkins.getInstanceOrNull")
266267
@Override public Label getAssignedLabel() {
267268
if (label == null) {
268269
return null;
@@ -277,6 +278,7 @@ private Object readResolve() {
277278
}
278279
}
279280

281+
@SuppressFBWarnings(value="RCN_REDUNDANT_NULLCHECK_OF_NONNULL_VALUE", justification="TODO 1.653+ switch to Jenkins.getInstanceOrNull")
280282
@Override public Node getLastBuiltOn() {
281283
if (label == null) {
282284
return null;
@@ -592,7 +594,10 @@ private final class PlaceholderExecutable implements ContinuableExecutable {
592594
if (masterExecutor != null) {
593595
masterExecutor.interrupt();
594596
} else { // anomalous state; perhaps build already aborted but this was left behind; let user manually cancel executor slot
595-
super.getExecutor().recordCauseOfInterruption(r, listener);
597+
Executor thisExecutor = super.getExecutor();
598+
if (thisExecutor != null) {
599+
thisExecutor.recordCauseOfInterruption(r, listener);
600+
}
596601
completed(null);
597602
}
598603
}

src/test/java/org/jenkinsci/plugins/workflow/EnvWorkflowTest.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -109,9 +109,9 @@ public class EnvWorkflowTest {
109109
p.setDefinition(new CpsFlowDefinition("node('remote') {echo(/running in ${env.WORKSPACE}/)}", true));
110110
WorkflowRun b2 = r.assertBuildStatusSuccess(p.scheduleBuild2(0));
111111
r.assertLogContains("running in " + remote.getWorkspaceFor(p), b2);
112-
p.setDefinition(new CpsFlowDefinition("node('remote') {ws('workspace/foo') {echo(/running in ${env.WORKSPACE}, really ?/)}}", true)); // JENKINS-41446
112+
p.setDefinition(new CpsFlowDefinition("node('remote') {ws('workspace/foo') {echo(/running in ${env.WORKSPACE}/)}}", true)); // JENKINS-41446
113113
WorkflowRun b3 = r.assertBuildStatusSuccess(p.scheduleBuild2(0));
114-
r.assertLogContains("/workspace/foo, really ?" , b3); // '/workspace/foo' is in the log, testing with ', really' to ensure we catch the environment variable
114+
r.assertLogContains("running in " + remote.getRootPath().child("workspace/foo"), b3);
115115
}
116116

117117
}

src/test/java/org/jenkinsci/plugins/workflow/steps/durable_task/ShellStepTest.java

+14-12
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import hudson.model.Result;
1111
import hudson.slaves.DumbSlave;
1212
import hudson.slaves.EnvironmentVariablesNodeProperty;
13+
import hudson.tasks.BatchFile;
1314
import hudson.tasks.Shell;
1415
import java.io.File;
1516
import java.io.Serializable;
@@ -29,7 +30,7 @@
2930
import org.jenkinsci.plugins.workflow.steps.StepConfigTester;
3031
import org.jenkinsci.plugins.workflow.support.visualization.table.FlowGraphTable;
3132
import org.jenkinsci.plugins.workflow.support.visualization.table.FlowGraphTable.Row;
32-
import org.junit.Assert;
33+
import static org.junit.Assert.*;
3334
import org.junit.Assume;
3435
import org.junit.ClassRule;
3536
import org.junit.Rule;
@@ -41,7 +42,7 @@
4142
import org.jvnet.hudson.test.TestExtension;
4243
import org.kohsuke.stapler.DataBoundConstructor;
4344

44-
public class ShellStepTest extends Assert {
45+
public class ShellStepTest {
4546

4647
@ClassRule
4748
public static BuildWatcher buildWatcher = new BuildWatcher();
@@ -122,17 +123,17 @@ public boolean apply(File tmp) {
122123
DumbSlave slave = j.createSlave("slave", null, null);
123124
FreeStyleProject f = j.createFreeStyleProject("f"); // the control
124125
f.setAssignedNode(slave);
125-
f.getBuildersList().add(new Shell("echo PATH=$PATH"));
126+
f.getBuildersList().add(Functions.isWindows() ? new BatchFile("echo Path=%Path%") : new Shell("echo PATH=$PATH"));
126127
WorkflowJob p = j.jenkins.createProject(WorkflowJob.class, "p");
127-
p.setDefinition(new CpsFlowDefinition("node('slave') {sh 'echo PATH=$PATH'}", true));
128+
p.setDefinition(new CpsFlowDefinition("node('slave') {isUnix() ? sh('echo PATH=$PATH') : bat('echo Path=%Path%')}", true));
128129
// First check: syntax recommended in /help/system-config/nodeEnvironmentVariables.html.
129-
slave.getNodeProperties().add(new EnvironmentVariablesNodeProperty(new EnvironmentVariablesNodeProperty.Entry("PATH+ACME", "/opt/acme/bin")));
130-
j.assertLogContains(":/opt/acme/bin:/", j.buildAndAssertSuccess(f)); // JRE also gets prepended
131-
j.assertLogContains("PATH=/opt/acme/bin:/", j.buildAndAssertSuccess(p));
130+
slave.getNodeProperties().add(new EnvironmentVariablesNodeProperty(new EnvironmentVariablesNodeProperty.Entry("PATH+ACME", Functions.isWindows() ? "C:\\acme\\bin" : "/opt/acme/bin")));
131+
j.assertLogContains(Functions.isWindows() ? ";C:\\acme\\bin;" : ":/opt/acme/bin:/", j.buildAndAssertSuccess(f)); // JRE also gets prepended
132+
j.assertLogContains(Functions.isWindows() ? "Path=C:\\acme\\bin;" : "PATH=/opt/acme/bin:/", j.buildAndAssertSuccess(p));
132133
// Second check: recursive expansion.
133-
slave.getNodeProperties().replace(new EnvironmentVariablesNodeProperty(new EnvironmentVariablesNodeProperty.Entry("PATH", "/opt/acme/bin:$PATH")));
134-
j.assertLogContains(":/opt/acme/bin:/", j.buildAndAssertSuccess(f));
135-
j.assertLogContains("PATH=/opt/acme/bin:/", j.buildAndAssertSuccess(p));
134+
slave.getNodeProperties().replace(new EnvironmentVariablesNodeProperty(new EnvironmentVariablesNodeProperty.Entry("PATH", Functions.isWindows() ? "C:\\acme\\bin;$PATH" : "/opt/acme/bin:$PATH")));
135+
j.assertLogContains(Functions.isWindows() ? ";C:\\acme\\bin;" : ":/opt/acme/bin:/", j.buildAndAssertSuccess(f));
136+
j.assertLogContains(Functions.isWindows() ? "Path=C:\\acme\\bin;" : "PATH=/opt/acme/bin:/", j.buildAndAssertSuccess(p));
136137
}
137138

138139
@Test public void launcherDecorator() throws Exception {
@@ -183,7 +184,7 @@ public DescriptorImpl() {
183184
@Issue("JENKINS-40734")
184185
@Test public void envWithShellChar() throws Exception {
185186
WorkflowJob p = j.jenkins.createProject(WorkflowJob.class, "p");
186-
p.setDefinition(new CpsFlowDefinition("node {withEnv(['MONEY=big$$money']) {sh 'echo \"MONEY=$MONEY\"'}}", true));
187+
p.setDefinition(new CpsFlowDefinition("node {withEnv(['MONEY=big$$money']) {isUnix() ? sh('echo \"MONEY=$MONEY\"') : bat('echo \"MONEY=%MONEY%\"')}}", true));
187188
j.assertLogContains("MONEY=big$$money", j.buildAndAssertSuccess(p));
188189
}
189190

@@ -214,6 +215,7 @@ public DescriptorImpl() {
214215

215216
@Issue("JENKINS-26133")
216217
@Test public void returnStdout() throws Exception {
218+
Assume.assumeTrue("TODO Windows equivalent TBD", new File("/usr/bin/tr").canExecute());
217219
WorkflowJob p = j.jenkins.createProject(WorkflowJob.class, "p");
218220
p.setDefinition(new CpsFlowDefinition("def msg; node {msg = sh(script: 'echo hello world | tr [a-z] [A-Z]', returnStdout: true).trim()}; echo \"it said ${msg}\""));
219221
j.assertLogContains("it said HELLO WORLD", j.assertBuildStatusSuccess(p.scheduleBuild2(0)));
@@ -224,7 +226,7 @@ public DescriptorImpl() {
224226
@Issue("JENKINS-26133")
225227
@Test public void returnStatus() throws Exception {
226228
WorkflowJob p = j.jenkins.createProject(WorkflowJob.class, "p");
227-
p.setDefinition(new CpsFlowDefinition("node {echo \"truth is ${sh script: 'true', returnStatus: true} but falsity is ${sh script: 'false', returnStatus: true}\"}"));
229+
p.setDefinition(new CpsFlowDefinition("node {echo \"truth is ${isUnix() ? sh(script: 'true', returnStatus: true) : bat(script: 'echo', returnStatus: true)} but falsity is ${isUnix() ? sh(script: 'false', returnStatus: true) : bat(script: 'type nonexistent' , returnStatus: true)}\"}", true));
228230
j.assertLogContains("truth is 0 but falsity is 1", j.assertBuildStatusSuccess(p.scheduleBuild2(0)));
229231
}
230232

src/test/java/org/jenkinsci/plugins/workflow/support/steps/ExecutorStepTest.java

+31-28
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
package org.jenkinsci.plugins.workflow.support.steps;
2626

2727
import hudson.FilePath;
28+
import hudson.Functions;
2829
import hudson.init.InitMilestone;
2930
import hudson.init.Initializer;
3031
import hudson.model.Computer;
@@ -57,11 +58,11 @@
5758
import java.util.Collections;
5859
import java.util.HashSet;
5960
import java.util.List;
60-
import java.util.concurrent.atomic.AtomicReference;
6161
import java.util.logging.ConsoleHandler;
6262
import java.util.logging.Handler;
6363
import java.util.logging.Level;
6464
import java.util.logging.Logger;
65+
import java.util.regex.Pattern;
6566
import jenkins.model.Jenkins;
6667
import org.apache.commons.io.FileUtils;
6768
import org.apache.tools.ant.util.JavaEnvUtils;
@@ -80,13 +81,15 @@
8081
import org.jenkinsci.plugins.workflow.test.steps.SemaphoreStep;
8182
import org.junit.AfterClass;
8283
import static org.junit.Assert.*;
84+
import org.junit.Assume;
8385
import org.junit.ClassRule;
8486
import org.junit.Rule;
8587
import org.junit.Test;
8688
import org.junit.rules.TemporaryFolder;
8789
import org.junit.runners.model.Statement;
8890
import org.jvnet.hudson.test.BuildWatcher;
8991
import org.jvnet.hudson.test.Issue;
92+
import org.jvnet.hudson.test.JenkinsRule;
9093
import org.jvnet.hudson.test.MockAuthorizationStrategy;
9194
import org.jvnet.hudson.test.RestartableJenkinsRule;
9295
import org.jvnet.hudson.test.recipes.LocalData;
@@ -114,11 +117,9 @@ public class ExecutorStepTest {
114117
WorkflowJob p = story.j.jenkins.createProject(WorkflowJob.class, "demo");
115118
p.setDefinition(new CpsFlowDefinition(
116119
"node('" + s.getNodeName() + "') {\n" +
117-
" sh('echo before=`basename $PWD`')\n" +
118-
" sh('echo ONSLAVE=$ONSLAVE')\n" +
120+
" isUnix() ? sh('echo ONSLAVE=$ONSLAVE') : bat('echo ONSLAVE=%ONSLAVE%')\n" +
119121
" semaphore 'wait'\n" +
120-
" sh('echo after=$PWD')\n" +
121-
"}"));
122+
"}", true));
122123

123124
WorkflowRun b = p.scheduleBuild2(0).waitForStart();
124125
SemaphoreStep.waitForStart("wait/1", b);
@@ -131,7 +132,6 @@ public class ExecutorStepTest {
131132
SemaphoreStep.success("wait/1", null);
132133
story.j.assertBuildStatusSuccess(story.j.waitForCompletion(b));
133134

134-
story.j.assertLogContains("before=demo", b);
135135
story.j.assertLogContains("ONSLAVE=true", b);
136136

137137
FlowGraphWalker walker = new FlowGraphWalker(b.getExecution());
@@ -190,6 +190,7 @@ private void startJnlpProc() throws Exception {
190190
}
191191

192192
@Test public void buildShellScriptAcrossRestart() throws Exception {
193+
Assume.assumeFalse("TODO not sure how to write a corresponding batch script", Functions.isWindows());
193194
story.addStep(new Statement() {
194195
@SuppressWarnings("SleepWhileInLoop")
195196
@Override public void evaluate() throws Throwable {
@@ -211,7 +212,7 @@ private void startJnlpProc() throws Exception {
211212
"node('dumbo') {\n" +
212213
" sh 'touch \"" + f2 + "\"; while [ -f \"" + f1 + "\" ]; do sleep 1; done; echo finished waiting; rm \"" + f2 + "\"'\n" +
213214
" echo 'OK, done'\n" +
214-
"}"));
215+
"}", true));
215216
WorkflowRun b = p.scheduleBuild2(0).waitForStart();
216217
while (!f2.isFile()) {
217218
Thread.sleep(100);
@@ -242,6 +243,7 @@ private void startJnlpProc() throws Exception {
242243
}
243244

244245
@Test public void buildShellScriptAcrossDisconnect() throws Exception {
246+
Assume.assumeFalse("TODO not sure how to write a corresponding batch script", Functions.isWindows());
245247
story.addStep(new Statement() {
246248
@SuppressWarnings("SleepWhileInLoop")
247249
@Override public void evaluate() throws Throwable {
@@ -261,7 +263,7 @@ private void startJnlpProc() throws Exception {
261263
"node('dumbo') {\n" +
262264
" sh 'touch \"" + f2 + "\"; while [ -f \"" + f1 + "\" ]; do sleep 1; done; echo finished waiting; rm \"" + f2 + "\"'\n" +
263265
" echo 'OK, done'\n" +
264-
"}"));
266+
"}", true));
265267
WorkflowRun b = p.scheduleBuild2(0).waitForStart();
266268
while (!f2.isFile()) {
267269
Thread.sleep(100);
@@ -291,22 +293,18 @@ private void startJnlpProc() throws Exception {
291293
}
292294

293295
@Test public void buildShellScriptQuick() throws Exception {
294-
final AtomicReference<String> dir = new AtomicReference<String>();
295296
story.addStep(new Statement() {
296297
@Override public void evaluate() throws Throwable {
297298
DumbSlave s = story.j.createOnlineSlave();
298299
s.getNodeProperties().add(new EnvironmentVariablesNodeProperty(new EnvironmentVariablesNodeProperty.Entry("ONSLAVE", "true")));
299300

300301
WorkflowJob p = story.j.jenkins.createProject(WorkflowJob.class, "demo");
301-
dir.set(s.getRemoteFS() + "/workspace/" + p.getFullName());
302302
p.setDefinition(new CpsFlowDefinition(
303303
"node('" + s.getNodeName() + "') {\n" +
304-
" sh('pwd; echo ONSLAVE=$ONSLAVE')\n" +
305-
"}"));
304+
" isUnix() ? sh('echo ONSLAVE=$ONSLAVE') : bat('echo ONSLAVE=%ONSLAVE%')\n" +
305+
"}", true));
306306

307307
WorkflowRun b = story.j.assertBuildStatusSuccess(p.scheduleBuild2(0));
308-
309-
story.j.assertLogContains(dir.get(), b);
310308
story.j.assertLogContains("ONSLAVE=true", b);
311309
}
312310
});
@@ -321,14 +319,14 @@ private void startJnlpProc() throws Exception {
321319
WorkflowJob p = story.j.jenkins.createProject(WorkflowJob.class, "demo");
322320
p.setDefinition(new CpsFlowDefinition(
323321
"node('slave') {\n" + // this locks the WS
324-
" sh('echo default=`basename $PWD`')\n" +
322+
" echo(/default=${pwd()}/)\n" +
325323
" ws {\n" + // and this locks a second one
326-
" sh('echo before=`basename $PWD`')\n" +
324+
" echo(/before=${pwd()}/)\n" +
327325
" semaphore 'wait'\n" +
328-
" sh('echo after=`basename $PWD`')\n" +
326+
" echo(/after=${pwd()}/)\n" +
329327
" }\n" +
330328
"}"
331-
));
329+
, true));
332330
p.save();
333331
WorkflowRun b1 = p.scheduleBuild2(0).waitForStart();
334332
SemaphoreStep.waitForStart("wait/1", b1);
@@ -339,6 +337,12 @@ private void startJnlpProc() throws Exception {
339337
}
340338
});
341339
story.addStep(new Statement() {
340+
void assertLogMatches(WorkflowRun build, String regexp) throws IOException { // TODO add to JenkinsRule
341+
String log = JenkinsRule.getLog(build);
342+
if (!Pattern.compile(regexp, Pattern.MULTILINE).matcher(log).find()) { // assertMatches present in some utility extension to JUnit/Hamcrest but not in our test CP
343+
fail(build + " log does not match /" + regexp + "/: " + log);
344+
}
345+
}
342346
@Override public void evaluate() throws Throwable {
343347
WorkflowJob p = (WorkflowJob) story.j.jenkins.getItem("demo");
344348
WorkflowRun b = p.getLastBuild();
@@ -349,18 +353,17 @@ private void startJnlpProc() throws Exception {
349353
story.j.waitUntilNoActivity();
350354
story.j.assertBuildStatusSuccess(b1);
351355
story.j.assertBuildStatusSuccess(b2);
352-
// TODO once got ‘InvalidClassException: cannot bind non-proxy descriptor to a proxy class’ inside BourneShellScript.doLaunch
353-
story.j.assertLogContains("default=demo", b1);
354-
story.j.assertLogContains("before=demo@2", b1);
355-
story.j.assertLogContains("after=demo@2", b1);
356-
story.j.assertLogContains("default=demo@3", b2);
357-
story.j.assertLogContains("before=demo@4", b2);
358-
story.j.assertLogContains("after=demo@4", b2);
356+
assertLogMatches(b1, "^default=.+demo$");
357+
assertLogMatches(b1, "^before=.+demo@2$");
358+
assertLogMatches(b1, "^after=.+demo@2$");
359+
assertLogMatches(b2, "^default=.+demo@3$");
360+
assertLogMatches(b2, "^before=.+demo@4$");
361+
assertLogMatches(b2, "^after=.+demo@4$");
359362
SemaphoreStep.success("wait/3", null);
360363
WorkflowRun b3 = story.j.assertBuildStatusSuccess(p.scheduleBuild2(0));
361-
story.j.assertLogContains("default=demo", b3);
362-
story.j.assertLogContains("before=demo@2", b3);
363-
story.j.assertLogContains("after=demo@2", b3);
364+
assertLogMatches(b3, "^default=.+demo$");
365+
assertLogMatches(b3, "^before=.+demo@2$");
366+
assertLogMatches(b3, "^after=.+demo@2$");
364367
}
365368
});
366369
}

0 commit comments

Comments
 (0)