Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pull Request: Choose status which abort the flow build #67

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion src/main/groovy/com/cloudbees/plugins/flow/FlowDSL.groovy
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -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 {

Expand Down Expand Up @@ -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()
}
}
Expand Down
21 changes: 20 additions & 1 deletion src/main/java/com/cloudbees/plugins/flow/BuildFlow.java
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,8 @@ public class BuildFlow extends Project<BuildFlow, FlowRun> implements TopLevelIt

private String dsl;
private String dslFile;

private String abortWhenWorseThan;
private boolean useAbortWhenWorseThan;
private boolean buildNeedsWorkspace;


Expand Down Expand Up @@ -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) {
Expand Down
25 changes: 22 additions & 3 deletions src/main/java/com/cloudbees/plugins/flow/FlowRun.java
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,9 @@ public class FlowRun extends Build<BuildFlow, FlowRun> {

private String dsl;
private String dslFile;

private boolean buildNeedsWorkspace;
private boolean useAbortWhenWorseThan;
private String abortWhenWorseThan;

private JobInvocation.Start startJob;

Expand Down Expand Up @@ -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));
Expand All @@ -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();
}
Expand Down Expand Up @@ -156,6 +171,10 @@ public void run() {
}
}

public String getAbortWhenWorseThan() {
return abortWhenWorseThan;
}

protected class BuildWithWorkspaceRunnerImpl extends AbstractRunner {

private final String dsl;
Expand Down
19 changes: 19 additions & 0 deletions src/main/resources/com/cloudbees/plugins/flow/BuildFlow/configure-entries.jelly
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,25 @@
<j:getStatic var="permission" className="hudson.model.Hudson" field="RUN_SCRIPTS"/>
<f:textarea class="fixed-width" readonly="${h.hasPermission(it,permission) ? null : 'readonly'}" codemirror-mode="clike" codemirror-config="mode: 'text/x-groovy', lineNumbers: true, matchBrackets: true, onBlur: function(editor){editor.save()}" checkMethod="POST" />
</f:entry>
<f:optionalBlock field="useAbortWhenWorseThan" title="${%Change sub build abort state}" checked="${instance.useAbortWhenWorseThan}">
<f:entry field="abortWhenWorseThan" title="${%Stop process, when sub build is worse than:}">
<j:getStatic var="permission" className="hudson.model.Hudson" field="RUN_SCRIPTS"/>
<select name="abortWhenWorseThan" readonly="${h.hasPermission(it,permission) ? null : 'readonly'}">
<j:choose>
<j:when test="${it.abortWhenWorseThan.equals('SUCCESS')}"><option value="SUCCESS" selected="selected">SUCCESS</option></j:when>
<j:otherwise><option value="SUCCESS">SUCCESS</option></j:otherwise>
</j:choose>
<j:choose>
<j:when test="${it.abortWhenWorseThan.equals('UNSTABLE')}"><option value="UNSTABLE" selected="selected">UNSTABLE</option></j:when>
<j:otherwise><option value="UNSTABLE">UNSTABLE</option></j:otherwise>
</j:choose>
<j:choose>
<j:when test="${it.abortWhenWorseThan.equals('FAILURE')}"><option value="FAILURE" selected="selected">FAILURE</option></j:when>
<j:otherwise><option value="FAILURE">FAILURE</option></j:otherwise>
</j:choose>
</select>
</f:entry>
</f:optionalBlock>
</f:section>

<p:config-publishers2 />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout" xmlns:t="/lib/hudson" xmlns:f="/lib/form" xmlns:i="jelly:fmt" xmlns:p="/lib/hudson/project">
<div>
<div>
The order is SUCCESS &lt; UNSTABLE &lt; FAILURE &lt; ABORT
</div>
<j:forEach items="${app.getExtensionList('com.cloudbees.plugins.flow.BuildFlowDSLExtension')}" var="ext">
<st:include page="help-dsl" from="${ext}" optional="true"/>
</j:forEach>
</div>
</j:jelly>
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout" xmlns:t="/lib/hudson" xmlns:f="/lib/form" xmlns:i="jelly:fmt" xmlns:p="/lib/hudson/project">
<div>
<div>
Abort the build, when one single step in the build process is worse than your value.
</div>
<j:forEach items="${app.getExtensionList('com.cloudbees.plugins.flow.BuildFlowDSLExtension')}" var="ext">
<st:include page="help-dsl" from="${ext}" optional="true"/>
</j:forEach>
</div>
</j:jelly>
134 changes: 130 additions & 4 deletions src/test/groovy/com/cloudbees/plugins/flow/BuildTest.groovy
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -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()
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would like to see testcases for parallel, ignore and guard.

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()
}
Expand Down
12 changes: 12 additions & 0 deletions src/test/groovy/com/cloudbees/plugins/flow/DSLTestCase.groovy
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down
Loading