From 352ca2676ac98a6aefcca11d801ef56dc4f5081a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andre=CC=81=20Ohlrogge?= Date: Tue, 13 Oct 2015 08:40:28 +0200 Subject: [PATCH] Pull Request: Choose status which abort the flow build --- .../com/cloudbees/plugins/flow/FlowDSL.groovy | 4 +- .../com/cloudbees/plugins/flow/BuildFlow.java | 21 ++- .../com/cloudbees/plugins/flow/FlowRun.java | 25 +++- .../flow/BuildFlow/configure-entries.jelly | 19 +++ .../BuildFlow/help-abortWhenWorseThan.jelly | 11 ++ .../help-useAbortWhenWorseThan.jelly | 11 ++ .../cloudbees/plugins/flow/BuildTest.groovy | 134 +++++++++++++++++- .../cloudbees/plugins/flow/DSLTestCase.groovy | 12 ++ .../cloudbees/plugins/flow/GuardTest.groovy | 76 ++++++++++ .../cloudbees/plugins/flow/IgnoreTest.groovy | 62 ++++++++ .../plugins/flow/ParallelTest.groovy | 51 +++++++ 11 files changed, 417 insertions(+), 9 deletions(-) mode change 100644 => 100755 src/main/groovy/com/cloudbees/plugins/flow/FlowDSL.groovy mode change 100644 => 100755 src/main/java/com/cloudbees/plugins/flow/BuildFlow.java mode change 100644 => 100755 src/main/java/com/cloudbees/plugins/flow/FlowRun.java mode change 100644 => 100755 src/main/resources/com/cloudbees/plugins/flow/BuildFlow/configure-entries.jelly create mode 100755 src/main/resources/com/cloudbees/plugins/flow/BuildFlow/help-abortWhenWorseThan.jelly create mode 100755 src/main/resources/com/cloudbees/plugins/flow/BuildFlow/help-useAbortWhenWorseThan.jelly mode change 100644 => 100755 src/test/groovy/com/cloudbees/plugins/flow/BuildTest.groovy mode change 100644 => 100755 src/test/groovy/com/cloudbees/plugins/flow/DSLTestCase.groovy diff --git a/src/main/groovy/com/cloudbees/plugins/flow/FlowDSL.groovy b/src/main/groovy/com/cloudbees/plugins/flow/FlowDSL.groovy old mode 100644 new mode 100755 index 1ea35e4..e4c1c69 --- a/src/main/groovy/com/cloudbees/plugins/flow/FlowDSL.groovy +++ b/src/main/groovy/com/cloudbees/plugins/flow/FlowDSL.groovy @@ -43,6 +43,7 @@ import java.util.logging.Logger import static hudson.model.Result.FAILURE import static hudson.model.Result.SUCCESS +import hudson.model.Result public class FlowDSL { @@ -201,7 +202,8 @@ public class FlowDelegate { * Check flow status and stop if unexpected failure is detected */ private void statusCheck() { - if (flowRun.state.result.isWorseThan(SUCCESS)) { + if (flowRun.state.result.isWorseThan(Result.fromString(flowRun.getAbortWhenWorseThan()))) { + println("Abort execution, because one of the last builds is worse than " + flowRun.getAbortWhenWorseThan()) fail() } } diff --git a/src/main/java/com/cloudbees/plugins/flow/BuildFlow.java b/src/main/java/com/cloudbees/plugins/flow/BuildFlow.java old mode 100644 new mode 100755 index 0d5dc9a..4a6c262 --- a/src/main/java/com/cloudbees/plugins/flow/BuildFlow.java +++ b/src/main/java/com/cloudbees/plugins/flow/BuildFlow.java @@ -65,7 +65,8 @@ public class BuildFlow extends Project implements TopLevelIt private String dsl; private String dslFile; - + private String abortWhenWorseThan; + private boolean useAbortWhenWorseThan; private boolean buildNeedsWorkspace; @@ -97,11 +98,29 @@ public void setDslFile(String dslFile) { this.dslFile = dslFile; } + public String getAbortWhenWorseThan() { + return abortWhenWorseThan; + } + + public void setAbortWhenWorseThan(String abortWhenWorseThan) { + this.abortWhenWorseThan = abortWhenWorseThan; + } + + public boolean isUseAbortWhenWorseThan() { + return useAbortWhenWorseThan; + } + + public void setUseAbortWhenWorseThan(boolean useAbortWhenWorseThan) { + this.useAbortWhenWorseThan = useAbortWhenWorseThan; + } + @Override protected void submit(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException, FormException { super.submit(req, rsp); JSONObject json = req.getSubmittedForm(); this.buildNeedsWorkspace = json.containsKey("buildNeedsWorkspace"); + this.useAbortWhenWorseThan = json.containsKey("useAbortWhenWorseThan"); + this.abortWhenWorseThan = (useAbortWhenWorseThan && json.containsKey("abortWhenWorseThan")) ? json.getString("abortWhenWorseThan") : "SUCCESS"; if (Jenkins.getInstance().hasPermission(Jenkins.RUN_SCRIPTS)) { this.dsl = json.getString("dsl"); if (this.buildNeedsWorkspace) { diff --git a/src/main/java/com/cloudbees/plugins/flow/FlowRun.java b/src/main/java/com/cloudbees/plugins/flow/FlowRun.java old mode 100644 new mode 100755 index 664a1d3..9ef4cf2 --- a/src/main/java/com/cloudbees/plugins/flow/FlowRun.java +++ b/src/main/java/com/cloudbees/plugins/flow/FlowRun.java @@ -60,8 +60,9 @@ public class FlowRun extends Build { private String dsl; private String dslFile; - private boolean buildNeedsWorkspace; + private boolean useAbortWhenWorseThan; + private String abortWhenWorseThan; private JobInvocation.Start startJob; @@ -91,6 +92,8 @@ private void setup(BuildFlow job) { this.dsl = job.getDsl(); this.dslFile = job.getDslFile(); this.buildNeedsWorkspace = job.getBuildNeedsWorkspace(); + this.useAbortWhenWorseThan = job.isUseAbortWhenWorseThan(); + this.abortWhenWorseThan = (useAbortWhenWorseThan && job.getAbortWhenWorseThan() != null) ? job.getAbortWhenWorseThan() : SUCCESS.toString(); startJob.buildStarted(this); jobsGraph.addVertex(startJob); state.set(new FlowState(SUCCESS, startJob)); @@ -103,16 +106,28 @@ private void setup(BuildFlow job) { /* package */ Run waitForCompletion(JobInvocation job) throws ExecutionException, InterruptedException { job.waitForCompletion(); - getState().setResult(job.getResult()); + if(useCustomAbortAndStateResultIsBetterOrEqualJobResult(job)) { + getState().setResult(job.getResult()); + } else if(!useAbortWhenWorseThan) { + getState().setResult(job.getResult()); + } return job.getBuild(); } /* package */ Run waitForFinalization(JobInvocation job) throws ExecutionException, InterruptedException { job.waitForFinalization(); - getState().setResult(job.getResult()); + if(useCustomAbortAndStateResultIsBetterOrEqualJobResult(job)) { + getState().setResult(job.getResult()); + } else if(!useAbortWhenWorseThan) { + getState().setResult(job.getResult()); + } return job.getBuild(); } + private boolean useCustomAbortAndStateResultIsBetterOrEqualJobResult(JobInvocation job) throws ExecutionException, InterruptedException { + return useAbortWhenWorseThan && getState().getResult().isBetterOrEqualTo(job.getResult()); + } + /* package */ FlowState getState() { return state.get(); } @@ -156,6 +171,10 @@ public void run() { } } + public String getAbortWhenWorseThan() { + return abortWhenWorseThan; + } + protected class BuildWithWorkspaceRunnerImpl extends AbstractRunner { private final String dsl; diff --git a/src/main/resources/com/cloudbees/plugins/flow/BuildFlow/configure-entries.jelly b/src/main/resources/com/cloudbees/plugins/flow/BuildFlow/configure-entries.jelly old mode 100644 new mode 100755 index 3a03973..9e3efb3 --- a/src/main/resources/com/cloudbees/plugins/flow/BuildFlow/configure-entries.jelly +++ b/src/main/resources/com/cloudbees/plugins/flow/BuildFlow/configure-entries.jelly @@ -42,6 +42,25 @@ + + + + + + diff --git a/src/main/resources/com/cloudbees/plugins/flow/BuildFlow/help-abortWhenWorseThan.jelly b/src/main/resources/com/cloudbees/plugins/flow/BuildFlow/help-abortWhenWorseThan.jelly new file mode 100755 index 0000000..d0a5e5a --- /dev/null +++ b/src/main/resources/com/cloudbees/plugins/flow/BuildFlow/help-abortWhenWorseThan.jelly @@ -0,0 +1,11 @@ + + +
+
+ The order is SUCCESS < UNSTABLE < FAILURE < ABORT +
+ + + +
+
\ No newline at end of file diff --git a/src/main/resources/com/cloudbees/plugins/flow/BuildFlow/help-useAbortWhenWorseThan.jelly b/src/main/resources/com/cloudbees/plugins/flow/BuildFlow/help-useAbortWhenWorseThan.jelly new file mode 100755 index 0000000..8dfa2df --- /dev/null +++ b/src/main/resources/com/cloudbees/plugins/flow/BuildFlow/help-useAbortWhenWorseThan.jelly @@ -0,0 +1,11 @@ + + +
+
+ Abort the build, when one single step in the build process is worse than your value. +
+ + + +
+
diff --git a/src/test/groovy/com/cloudbees/plugins/flow/BuildTest.groovy b/src/test/groovy/com/cloudbees/plugins/flow/BuildTest.groovy old mode 100644 new mode 100755 index ee17635..f9bbd93 --- a/src/test/groovy/com/cloudbees/plugins/flow/BuildTest.groovy +++ b/src/test/groovy/com/cloudbees/plugins/flow/BuildTest.groovy @@ -131,20 +131,146 @@ class BuildTest extends DSLTestCase { println flow.jobsGraph.edgeSet() } - public void testSequentialBuildsWithFailure() { + public void testSequentialBuildsExpectFailureWithDefaultSettings() { def jobs = createJobs(["job1", "job2", "job3"]) def willFail = createFailJob("willFail") - def notRan = createJob("notRan") + def willNotRun = createJob("willNotRun") def flow = run(""" build("job1") build("job2") build("job3") build("willFail") - build("notRan") + build("willNotRun") + """) + assertAllSuccess(jobs) + assertFailure(willFail) + assertDidNotRun(willNotRun) + assert FAILURE == flow.result + println flow.jobsGraph.edgeSet() + } + + public void testSequentialBuildsExpectUnstableWithDefaultSettings() { + def jobs = createJobs(["job1", "job2", "job3"]) + def willBeUnstable = createUnstableJob("willBeUnstable") + def willNotRun = createJob("willNotRun") + def flow = run(""" + build("job1") + build("job2") + build("job3") + build("willBeUnstable") + build("willNotRun") """) assertAllSuccess(jobs) + assertUnstable(willBeUnstable) + assertDidNotRun(willNotRun) + assert UNSTABLE == flow.result + println flow.jobsGraph.edgeSet() + } + + public void testSequentialBuildsExpectUnstableWithSuccessSet() { + def jobs = createJobs(["job1", "job2", "job3"]) + def willBeUnstable = createUnstableJob("willBeUnstable") + def willNotRun = createJob("willNotRun") + def flow = runWithAbortWhenWorseThan(""" + build("job1") + build("job2") + build("job3") + build("willBeUnstable") + build("willNotRun") + """, SUCCESS) + assertAllSuccess(jobs) + assertUnstable(willBeUnstable) + assertDidNotRun(willNotRun) + assert UNSTABLE == flow.result + println flow.jobsGraph.edgeSet() + } + + public void testSequentialBuildsExpectFailureWithSuccessSet() { + def jobs = createJobs(["job1", "job2", "job3"]) + def willFail = createFailJob("willFail") + def willNotRun = createJob("willNotRun") + def flow = runWithAbortWhenWorseThan(""" + build("job1") + build("job2") + build("job3") + build("willFail") + build("willNotRun") + """, SUCCESS) + assertAllSuccess(jobs) + assertFailure(willFail) + assertDidNotRun(willNotRun) + assert FAILURE == flow.result + println flow.jobsGraph.edgeSet() + } + + public void testSequentialBuildsExpectUnstableWithUnstableSet() { + def jobs = createJobs(["job1", "job2", "job3"]) + def willBeUnstable = createUnstableJob("willBeUnstable") + def willRun = createJob("willRun") + def flow = runWithAbortWhenWorseThan(""" + build("job1") + build("job2") + build("job3") + build("willBeUnstable") + build("willRun") + """, UNSTABLE) + assertAllSuccess(jobs) + assertUnstable(willBeUnstable) + assertRan(willRun) + assert UNSTABLE == flow.result + println flow.jobsGraph.edgeSet() + } + + public void testSequentialBuildsExpectFailureWithUnstableSet() { + def jobs = createJobs(["job1", "job2", "job3"]) + def willFail = createFailJob("willFail") + def willNotRun = createJob("willNotRun") + def flow = runWithAbortWhenWorseThan(""" + build("job1") + build("job2") + build("job3") + build("willFail") + build("willNotRun") + """, UNSTABLE) + assertAllSuccess(jobs) + assertFailure(willFail) + assertDidNotRun(willNotRun) + assert FAILURE == flow.result + println flow.jobsGraph.edgeSet() + } + + public void testSequentialBuildsExpectUnstableWithFailureSet() { + def jobs = createJobs(["job1", "job2", "job3"]) + def willBeUnstable = createUnstableJob("willBeUnstable") + def willRun = createJob("willRun") + def flow = runWithAbortWhenWorseThan(""" + build("job1") + build("job2") + build("job3") + build("willBeUnstable") + build("willRun") + """, FAILURE) + assertAllSuccess(jobs) + assertUnstable(willBeUnstable) + assertRan(willRun) + assert UNSTABLE == flow.result + println flow.jobsGraph.edgeSet() + } + + public void testSequentialBuildsExpectFailureWithFailureSet() { + def jobs = createJobs(["job1", "job2", "job3"]) + def willFail = createFailJob("willFail") + def willRun = createJob("willRun") + def flow = runWithAbortWhenWorseThan(""" + build("job1") + build("job2") + build("job3") + build("willFail") + build("willRun") + """, FAILURE) + assertAllSuccess(jobs) assertFailure(willFail) - assertDidNotRun(notRan) + assertRan(willRun) assert FAILURE == flow.result println flow.jobsGraph.edgeSet() } diff --git a/src/test/groovy/com/cloudbees/plugins/flow/DSLTestCase.groovy b/src/test/groovy/com/cloudbees/plugins/flow/DSLTestCase.groovy old mode 100644 new mode 100755 index e2dfa7e..481c272 --- a/src/test/groovy/com/cloudbees/plugins/flow/DSLTestCase.groovy +++ b/src/test/groovy/com/cloudbees/plugins/flow/DSLTestCase.groovy @@ -91,6 +91,14 @@ abstract class DSLTestCase extends HudsonTestCase { return flow.scheduleBuild2(0).get() } + def runWithAbortWhenWorseThan = { script, abortWhenWorseThan -> + BuildFlow flow = new BuildFlow(Jenkins.instance, getName()) + flow.dsl = script + flow.abortWhenWorseThan = abortWhenWorseThan.toString() + flow.useAbortWhenWorseThan = true + return flow.scheduleBuild2(0).get() + } + def runWithWorkspace = { script -> BuildFlow flow = new BuildFlow(Jenkins.instance, getName()) flow.dsl = script @@ -120,6 +128,10 @@ abstract class DSLTestCase extends HudsonTestCase { assert 0 == job.builds.size() } + def assertRan = { job -> + assert 0 < job.builds.size() + } + def assertAllSuccess = { jobs -> jobs.each { assertNotNull("job ${it.name} didn't run", it.builds.lastBuild) diff --git a/src/test/groovy/com/cloudbees/plugins/flow/GuardTest.groovy b/src/test/groovy/com/cloudbees/plugins/flow/GuardTest.groovy index 215993a..382c70b 100644 --- a/src/test/groovy/com/cloudbees/plugins/flow/GuardTest.groovy +++ b/src/test/groovy/com/cloudbees/plugins/flow/GuardTest.groovy @@ -27,6 +27,7 @@ package com.cloudbees.plugins.flow import static hudson.model.Result.SUCCESS import hudson.model.Job import static hudson.model.Result.FAILURE +import static hudson.model.Result.UNSTABLE class GuardTest extends DSLTestCase { @@ -45,6 +46,81 @@ class GuardTest extends DSLTestCase { assert SUCCESS == ret.result } + public void testGuardWithUnstableJobAndUnstableSetAsAbortResult() { + def jobs = createJobs(["job1", "job2", "job3", "clean"]) + def unstableJob = createUnstableJob("unstableJob") + def ret = runWithAbortWhenWorseThan(""" + guard { + build("job1") + build("job2") + build("unstableJob") + build("job3") + } rescue { + build("clean") + } + """, UNSTABLE) + assertAllSuccess(jobs) + assertRan(unstableJob) + assert UNSTABLE == ret.result + } + + public void testGuardWithFailureJobAndUnstableSetAsAbortResult() { + def jobs = createJobs(["job1", "job2", "job3", "clean"]) + def failureJob = createFailJob("failureJob") + def ret = runWithAbortWhenWorseThan(""" + guard { + build("job1") + build("job2") + build("failureJob") + build("job3") + } rescue { + build("clean") + } + """, UNSTABLE) + assertSuccess(jobs.get(0)) + assertSuccess(jobs.get(1)) + assertSuccess(jobs.get(3)) + assertDidNotRun(jobs.get(2)) + assertRan(failureJob) + assert FAILURE == ret.result + } + + public void testGuardWithUnstableJobAndFailureSetAsAbortResult() { + def jobs = createJobs(["job1", "job2", "job3", "clean"]) + def unstableJob = createUnstableJob("unstableJob") + def ret = runWithAbortWhenWorseThan(""" + guard { + build("job1") + build("job2") + build("unstableJob") + build("job3") + } rescue { + build("clean") + } + """, FAILURE) + assertAllSuccess(jobs) + assertRan(unstableJob) + assert UNSTABLE == ret.result + } + + public void testGuardWithFailureJobAndFailureSetAsAbortResult() { + def jobs = createJobs(["job1", "job2", "job3", "clean"]) + def failureJob = createFailJob("failureJob") + def ret = runWithAbortWhenWorseThan(""" + guard { + build("job1") + build("job2") + build("failureJob") + build("job3") + } rescue { + build("clean") + } + """, FAILURE) + assertAllSuccess(jobs) + assertRan(failureJob) + assert FAILURE == ret.result + } + /*public void testGuardWithFail() { Job job1 = createJob("job1"); def failure = createFailJob("fails") diff --git a/src/test/groovy/com/cloudbees/plugins/flow/IgnoreTest.groovy b/src/test/groovy/com/cloudbees/plugins/flow/IgnoreTest.groovy index 7798a46..b8f6546 100644 --- a/src/test/groovy/com/cloudbees/plugins/flow/IgnoreTest.groovy +++ b/src/test/groovy/com/cloudbees/plugins/flow/IgnoreTest.groovy @@ -75,4 +75,66 @@ class IgnoreTest extends DSLTestCase { assert FAILURE == flow.result } + public void testIgnoreJobFailureButRunUnstable() { + Job willUnstable = createUnstableJob("willUnstable") + Job willFail = createFailJob("willFail"); + Job wontRun= createJob("wontRun"); + def flow = runWithAbortWhenWorseThan(""" + ignore(FAILURE) { + build("willUnstable") + build("willFail") + build("wontRun") + } + """, UNSTABLE) + assertRan(willUnstable) + assertFailure(willFail) + assertDidNotRun(wontRun) + assert SUCCESS == flow.result + } + + public void testIgnoreJobFailureButRunFailure() { + Job willUnstable = createUnstableJob("willUnstable") + Job willFail = createFailJob("willFail"); + Job willRun= createJob("willRun"); + def flow = runWithAbortWhenWorseThan(""" + ignore(FAILURE) { + build("willUnstable") + build("willFail") + build("willRun") + } + """, FAILURE) + assertRan(willUnstable) + assertFailure(willFail) + assertRan(willRun) + assert SUCCESS == flow.result + } + + public void testIgnoreJobUnstableButRunUnstable() { + Job willUnstable = createUnstableJob("willUnstable") + Job willRun= createJob("willRun"); + def flow = runWithAbortWhenWorseThan(""" + ignore(UNSTABLE) { + build("willUnstable") + build("willRun") + } + """, UNSTABLE) + assertRan(willUnstable) + assertRan(willRun) + assert SUCCESS == flow.result + } + + public void testIgnoreJobUnstableButRunFailure() { + Job willUnstable = createUnstableJob("willUnstable") + Job willRun= createJob("willRun"); + def flow = runWithAbortWhenWorseThan(""" + ignore(UNSTABLE) { + build("willUnstable") + build("willRun") + } + """, FAILURE) + assertRan(willUnstable) + assertRan(willRun) + assert SUCCESS == flow.result + } + } diff --git a/src/test/groovy/com/cloudbees/plugins/flow/ParallelTest.groovy b/src/test/groovy/com/cloudbees/plugins/flow/ParallelTest.groovy index f006952..7468a3f 100644 --- a/src/test/groovy/com/cloudbees/plugins/flow/ParallelTest.groovy +++ b/src/test/groovy/com/cloudbees/plugins/flow/ParallelTest.groovy @@ -27,6 +27,7 @@ package com.cloudbees.plugins.flow import static hudson.model.Result.SUCCESS import hudson.model.Result import static hudson.model.Result.FAILURE +import static hudson.model.Result.UNSTABLE class ParallelTest extends DSLTestCase { @@ -62,6 +63,56 @@ class ParallelTest extends DSLTestCase { println flow.jobsGraph.edgeSet() } + public void testUnstableOnParallelBeUnstableButRunTheFollowingJobsWhenAbortStatusSetToUnstable() { + createJobs(["job1", "job2"]) + createUnstableJob("willBeUnstable") + def job4 = createJob("job4") + def flow = runWithAbortWhenWorseThan(""" + parallel ( + { build("job1") }, + { build("job2") }, + { build("willBeUnstable") } + ) + build("job4") + """, UNSTABLE) + assertRan(job4) + assert UNSTABLE == flow.result + println flow.jobsGraph.edgeSet() + } + + public void testFailOnParallelFailedWhenAbortStatusSetToUnstable() { + createJobs(["job1", "job2"]) + createFailJob("willFail") + def job4 = createJob("job4") + def flow = runWithAbortWhenWorseThan(""" + parallel ( + { build("job1") }, + { build("job2") }, + { build("willfail") } + ) + build("job4") + """, UNSTABLE) + assertDidNotRun(job4) + assert FAILURE == flow.result + println flow.jobsGraph.edgeSet() + } + + public void testFailOnParallelFailedButRunTheFollowingJobsWhenAbortStatusSetToFailure() { + createJobs(["job1", "job2"]) + createFailJob("willFail") + def job4 = createJob("job4") + def flow = runWithAbortWhenWorseThan(""" + parallel ( + { build("job1") }, + { build("job2") }, + { build("willfail") } + ) + build("job4") + """, FAILURE) + assertRan(job4) + assert FAILURE == flow.result + println flow.jobsGraph.edgeSet() + } public void testFailOnJobSequenceFailed() { def jobs = createJobs(["job1", "job2", "job3"])