Skip to content

Commit

Permalink
[FIXED JENKINS-42039] Add option for skipping stages after unstable
Browse files Browse the repository at this point in the history
  • Loading branch information
abayer committed Feb 14, 2017
1 parent aaa0593 commit f88b0e8
Show file tree
Hide file tree
Showing 6 changed files with 139 additions and 4 deletions.
Expand Up @@ -307,6 +307,10 @@ public class Utils {
markStageWithTag(stageName, getStageStatusMetadata().tagName, getStageStatusMetadata().skippedForFailure)
}

static void markStageSkippedForUnstable(String stageName) {
markStageWithTag(stageName, getStageStatusMetadata().tagName, getStageStatusMetadata().skippedForFailure)
}

static void markStageSkippedForConditional(String stageName) {
markStageWithTag(stageName, getStageStatusMetadata().tagName, getStageStatusMetadata().skippedForConditional)
}
Expand Down
@@ -0,0 +1,51 @@
/*
* The MIT License
*
* Copyright (c) 2017, CloudBees, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/

package org.jenkinsci.plugins.pipeline.modeldefinition.options.impl;

import hudson.Extension;
import org.jenkinsci.Symbol;
import org.jenkinsci.plugins.pipeline.modeldefinition.options.DeclarativeOption;
import org.jenkinsci.plugins.pipeline.modeldefinition.options.DeclarativeOptionDescriptor;
import org.kohsuke.stapler.DataBoundConstructor;

import javax.annotation.Nullable;

public class SkipStagesAfterUnstable extends DeclarativeOption {
private Boolean skipStagesAfterUnstable;

@DataBoundConstructor
public SkipStagesAfterUnstable(@Nullable Boolean s) {
this.skipStagesAfterUnstable = s;
}

public boolean isSkipStagesAfterUnstable() {
return skipStagesAfterUnstable == null || skipStagesAfterUnstable;
}

@Extension @Symbol("skipStagesAfterUnstable")
public static class DescriptorImpl extends DeclarativeOptionDescriptor {

}
}
Expand Up @@ -29,6 +29,7 @@ import hudson.FilePath
import hudson.Launcher
import hudson.model.Result
import org.jenkinsci.plugins.pipeline.modeldefinition.model.*
import org.jenkinsci.plugins.pipeline.modeldefinition.options.impl.SkipStagesAfterUnstable
import org.jenkinsci.plugins.pipeline.modeldefinition.steps.CredentialWrapper
import org.jenkinsci.plugins.pipeline.modeldefinition.when.DeclarativeStageConditional
import org.jenkinsci.plugins.workflow.cps.CpsScript
Expand Down Expand Up @@ -80,7 +81,13 @@ public class ModelInterpreter implements Serializable {
Stage thisStage = root.stages.getStages().get(i)
try {
script.stage(thisStage.name) {
if (firstError == null) {
if (firstError != null) {
Utils.logToTaskListener("Stage '${thisStage.name}' skipped due to earlier failure(s)")
Utils.markStageSkippedForFailure(thisStage.name)
} else if (skipUnstable(root.options)) {
Utils.logToTaskListener("Stage '${thisStage.name}' skipped due to earlier stage(s) marking the build as unstable")
Utils.markStageSkippedForUnstable(thisStage.name)
} else {
withEnvBlock(thisStage.getEnvVars()) {
if (evaluateWhen(thisStage.when)) {
inDeclarativeAgent(thisStage, thisStage.agent) {
Expand All @@ -96,9 +103,6 @@ public class ModelInterpreter implements Serializable {
Utils.markStageSkippedForConditional(thisStage.name)
}
}
} else {
Utils.logToTaskListener("Stage '${thisStage.name}' skipped due to earlier failure(s)")
Utils.markStageSkippedForFailure(thisStage.name)
}
}
} catch (Exception e) {
Expand Down Expand Up @@ -175,6 +179,11 @@ public class ModelInterpreter implements Serializable {
}.call()
}

boolean skipUnstable(Options options) {
return script.getProperty("currentBuild").result == "UNSTABLE" &&
((SkipStagesAfterUnstable)options?.options?.get("skipStagesAfterUnstable"))?.skipStagesAfterUnstable
}

/**
* Execute a body closure within a "withEnv" block.
*
Expand Down
Expand Up @@ -135,6 +135,25 @@ public void allStagesExist() throws Exception {
assertNotNull(shouldBeFailedNode.getError());
}

@Issue("JENKINS-42039")
@Test
public void skipAfterUnstable() throws Exception {
WorkflowRun b = expect(Result.UNSTABLE, "skipAfterUnstable")
.logContains("[Pipeline] { (foo)", "hello", "[Pipeline] { (bar)")
.logNotContains("goodbye")
.go();

FlowExecution execution = b.getExecution();
List<FlowNode> heads = execution.getCurrentHeads();
DepthFirstScanner scanner = new DepthFirstScanner();
FlowNode startFoo = scanner.findFirstMatch(heads, null, Utils.isStageWithOptionalName("foo"));
assertNotNull(startFoo);
assertTrue(startFoo instanceof StepStartNode);
FlowNode endFoo = scanner.findFirstMatch(heads, null, Utils.endNodeForStage((StepStartNode)startFoo));
assertNotNull(endFoo);
assertEquals(GenericStatus.UNSTABLE, StatusAndTiming.computeChunkStatus(b, null, startFoo, endFoo, null));
}

@Test
public void validStepParameters() throws Exception {
expect("validStepParameters")
Expand Down
@@ -0,0 +1,48 @@
/*
* The MIT License
*
* Copyright (c) 2017, CloudBees, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/

pipeline {
agent none
options {
skipStagesAfterUnstable()
}
stages {
stage("foo") {
steps {
echo "hello"
script {
currentBuild.result = "UNSTABLE"
}
}
}
stage("bar") {
steps {
echo "goodbye"
}
}
}
}



Expand Up @@ -63,6 +63,10 @@ public static String getSkippedForFailure() {
return "SKIPPED_FOR_FAILURE";
}

public static String getSkippedForUnstable() {
return "SKIPPED_FOR_UNSTABLE";
}

public static String getSkippedForConditional() {
return "SKIPPED_FOR_CONDITIONAL";
}
Expand Down

0 comments on commit f88b0e8

Please sign in to comment.