From dfab9e247559bf258b0aa35e4160cc91b45e0abc Mon Sep 17 00:00:00 2001 From: Robert Sandell Date: Wed, 26 Oct 2016 11:04:37 +0200 Subject: [PATCH] [JENKINS-37781] Adding Stage.when to specify if a stage should run or not --- .../ast/ModelASTScriptBlock.java | 8 +- .../modeldefinition/ast/ModelASTStage.java | 29 +++++++ .../modeldefinition/ast/ModelASTWhen.java | 51 +++++++++++ .../validator/ModelValidator.java | 3 + .../modeldefinition/model/Stage.groovy | 9 ++ .../modeldefinition/parser/JSONParser.groovy | 15 ++++ .../modeldefinition/parser/ModelParser.groovy | 16 +++- .../validator/ModelValidatorImpl.groovy | 6 ++ .../modeldefinition/ModelInterpreter.groovy | 85 ++++++++++++------- .../modeldefinition/WhenStageTest.java | 57 +++++++++++++ .../src/test/resources/simpleWhen.groovy | 48 +++++++++++ 11 files changed, 290 insertions(+), 37 deletions(-) create mode 100644 pipeline-model-api/src/main/java/org/jenkinsci/plugins/pipeline/modeldefinition/ast/ModelASTWhen.java create mode 100644 pipeline-model-definition/src/test/java/org/jenkinsci/plugins/pipeline/modeldefinition/WhenStageTest.java create mode 100644 pipeline-model-definition/src/test/resources/simpleWhen.groovy diff --git a/pipeline-model-api/src/main/java/org/jenkinsci/plugins/pipeline/modeldefinition/ast/ModelASTScriptBlock.java b/pipeline-model-api/src/main/java/org/jenkinsci/plugins/pipeline/modeldefinition/ast/ModelASTScriptBlock.java index 08f382bbf..a872bd6e7 100644 --- a/pipeline-model-api/src/main/java/org/jenkinsci/plugins/pipeline/modeldefinition/ast/ModelASTScriptBlock.java +++ b/pipeline-model-api/src/main/java/org/jenkinsci/plugins/pipeline/modeldefinition/ast/ModelASTScriptBlock.java @@ -7,13 +7,17 @@ */ public class ModelASTScriptBlock extends ModelASTStep { public ModelASTScriptBlock(Object sourceLocation) { + this(sourceLocation, "script"); + } + + protected ModelASTScriptBlock(Object sourceLocation, String name) { super(sourceLocation); - this.setName("script"); + this.setName(name); } @Override public String toGroovy() { - StringBuilder result = new StringBuilder("script {\n"); + StringBuilder result = new StringBuilder(getName()).append(" {\n"); if (getArgs() != null && getArgs() instanceof ModelASTSingleArgument && ((ModelASTSingleArgument) getArgs()).getValue()!=null diff --git a/pipeline-model-api/src/main/java/org/jenkinsci/plugins/pipeline/modeldefinition/ast/ModelASTStage.java b/pipeline-model-api/src/main/java/org/jenkinsci/plugins/pipeline/modeldefinition/ast/ModelASTStage.java index 5cf459281..6e5176b43 100644 --- a/pipeline-model-api/src/main/java/org/jenkinsci/plugins/pipeline/modeldefinition/ast/ModelASTStage.java +++ b/pipeline-model-api/src/main/java/org/jenkinsci/plugins/pipeline/modeldefinition/ast/ModelASTStage.java @@ -18,6 +18,7 @@ public final class ModelASTStage extends ModelASTElement { private ModelASTAgent agent; private List branches = new ArrayList(); private ModelASTPostStage post; + private ModelASTWhen when; public ModelASTStage(Object sourceLocation) { super(sourceLocation); @@ -35,6 +36,9 @@ public JSONObject toJSON() { if (agent != null) { o.accumulate("agent", agent.toJSON()); } + if (when != null) { + o.accumulate("when", when.toJSON()); + } if (post != null) { o.accumulate("post", post.toJSON()); @@ -52,6 +56,9 @@ public void validate(final ModelValidator validator) { if (agent != null) { agent.validate(validator); } + if (when != null) { + when.validate(validator); + } if (post != null) { post.validate(validator); } @@ -66,6 +73,10 @@ public String toGroovy() { result.append(agent.toGroovy()); } + if (when != null) { + result.append(when.toGroovy()); + } + result.append("steps {\n"); if (branches.size() > 1) { result.append("parallel("); @@ -107,6 +118,10 @@ public void removeSourceLocation() { if (post != null) { post.removeSourceLocation(); } + + if (when != null) { + when.removeSourceLocation(); + } } public String getName() { @@ -141,6 +156,14 @@ public void setPost(ModelASTPostStage post) { this.post = post; } + public ModelASTWhen getWhen() { + return when; + } + + public void setWhen(ModelASTWhen when) { + this.when = when; + } + @Override public String toString() { return "ModelASTStage{" + @@ -148,6 +171,7 @@ public String toString() { ", agent=" + agent + ", branches=" + branches + ", post=" + post + + ", when=" + when + "}"; } @@ -174,6 +198,10 @@ public boolean equals(Object o) { if (getPost() != null ? !getPost().equals(that.getPost()) : that.getPost() != null) { return false; } + if (getWhen() != null ? !getWhen().equals(that.getWhen()) : that.getWhen() != null) { + return false; + } + return getBranches() != null ? getBranches().equals(that.getBranches()) : that.getBranches() == null; } @@ -184,6 +212,7 @@ public int hashCode() { result = 31 * result + (getName() != null ? getName().hashCode() : 0); result = 31 * result + (getAgent() != null ? getAgent().hashCode() : 0); result = 31 * result + (getBranches() != null ? getBranches().hashCode() : 0); + result = 31 * result + (getWhen() != null ? getWhen().hashCode() : 0); return result; } } diff --git a/pipeline-model-api/src/main/java/org/jenkinsci/plugins/pipeline/modeldefinition/ast/ModelASTWhen.java b/pipeline-model-api/src/main/java/org/jenkinsci/plugins/pipeline/modeldefinition/ast/ModelASTWhen.java new file mode 100644 index 000000000..0eb35b72f --- /dev/null +++ b/pipeline-model-api/src/main/java/org/jenkinsci/plugins/pipeline/modeldefinition/ast/ModelASTWhen.java @@ -0,0 +1,51 @@ +/* + * The MIT License + * + * Copyright (c) 2016, 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.ast; + +import org.jenkinsci.plugins.pipeline.modeldefinition.validator.ModelValidator; + +/** + * Represents a block for when/if a {@link ModelASTStage} will be executed or not. + */ +public class ModelASTWhen extends ModelASTScriptBlock { //Model wise we don't really extend it but there are plenty of reuse + public ModelASTWhen(Object sourceLocation) { + super(sourceLocation, "when"); + } + + @Override + public String toString() { + return "ModelASTWhen{" + + "name='" + getName() + '\'' + + ", args=" + getArgs() + + "}"; + } + + @Override + public void validate(ModelValidator validator) { + super.validate(validator); + validator.validateElement(this); + } +} diff --git a/pipeline-model-api/src/main/java/org/jenkinsci/plugins/pipeline/modeldefinition/validator/ModelValidator.java b/pipeline-model-api/src/main/java/org/jenkinsci/plugins/pipeline/modeldefinition/validator/ModelValidator.java index 832a4e9a8..cd88a1bae 100644 --- a/pipeline-model-api/src/main/java/org/jenkinsci/plugins/pipeline/modeldefinition/validator/ModelValidator.java +++ b/pipeline-model-api/src/main/java/org/jenkinsci/plugins/pipeline/modeldefinition/validator/ModelValidator.java @@ -45,6 +45,7 @@ import org.jenkinsci.plugins.pipeline.modeldefinition.ast.ModelASTTools; import org.jenkinsci.plugins.pipeline.modeldefinition.ast.ModelASTTrigger; import org.jenkinsci.plugins.pipeline.modeldefinition.ast.ModelASTTriggers; +import org.jenkinsci.plugins.pipeline.modeldefinition.ast.ModelASTWhen; public interface ModelValidator { @@ -68,6 +69,8 @@ public interface ModelValidator { boolean validateElement(ModelASTStep step); + boolean validateElement(ModelASTWhen when); + boolean validateElement(ModelASTMethodCall methodCall); boolean validateElement(ModelASTJobProperties jobProperties); diff --git a/pipeline-model-definition/src/main/groovy/org/jenkinsci/plugins/pipeline/modeldefinition/model/Stage.groovy b/pipeline-model-definition/src/main/groovy/org/jenkinsci/plugins/pipeline/modeldefinition/model/Stage.groovy index f06fc34d8..afc75aa13 100644 --- a/pipeline-model-definition/src/main/groovy/org/jenkinsci/plugins/pipeline/modeldefinition/model/Stage.groovy +++ b/pipeline-model-definition/src/main/groovy/org/jenkinsci/plugins/pipeline/modeldefinition/model/Stage.groovy @@ -50,6 +50,9 @@ public class Stage implements NestedModel, Serializable { @Whitelisted PostStage post + @Whitelisted + Closure when + @Whitelisted Stage name(String n) { this.name = n @@ -80,6 +83,12 @@ public class Stage implements NestedModel, Serializable { return this } + @Whitelisted + Stage when(Closure when) { + this.when = when + return this + } + @Override @Whitelisted public void modelFromMap(Map m) { diff --git a/pipeline-model-definition/src/main/groovy/org/jenkinsci/plugins/pipeline/modeldefinition/parser/JSONParser.groovy b/pipeline-model-definition/src/main/groovy/org/jenkinsci/plugins/pipeline/modeldefinition/parser/JSONParser.groovy index e43def3c2..80d05cb51 100644 --- a/pipeline-model-definition/src/main/groovy/org/jenkinsci/plugins/pipeline/modeldefinition/parser/JSONParser.groovy +++ b/pipeline-model-definition/src/main/groovy/org/jenkinsci/plugins/pipeline/modeldefinition/parser/JSONParser.groovy @@ -61,6 +61,7 @@ import org.jenkinsci.plugins.pipeline.modeldefinition.ast.ModelASTPostBuild import org.jenkinsci.plugins.pipeline.modeldefinition.ast.ModelASTScriptBlock import org.jenkinsci.plugins.pipeline.modeldefinition.ast.ModelASTTools import org.jenkinsci.plugins.pipeline.modeldefinition.ast.ModelASTTreeStep +import org.jenkinsci.plugins.pipeline.modeldefinition.ast.ModelASTWhen import org.jenkinsci.plugins.pipeline.modeldefinition.validator.ErrorCollector import org.jenkinsci.plugins.pipeline.modeldefinition.validator.JSONErrorCollector import org.jenkinsci.plugins.pipeline.modeldefinition.validator.ModelValidator @@ -186,6 +187,13 @@ class JSONParser { stage.post = parsePostStage(object) } } + + if (j.has("when")) { + def object = j.getJSONObject("when") + if (!object.isNullObject()) { + stage.when = parseWhen(object) + } + } return stage } @@ -391,6 +399,13 @@ class JSONParser { return scriptBlock } + public @CheckForNull ModelASTWhen parseWhen(JSONObject j) { + ModelASTWhen scriptBlock = new ModelASTWhen(j) + scriptBlock.args = parseArgumentList(j.getJSONObject("arguments")) + + return scriptBlock + } + public @CheckForNull ModelASTTreeStep parseTreeStep(JSONObject j) { ModelASTTreeStep step = new ModelASTTreeStep(j) step.name = j.getString("name") diff --git a/pipeline-model-definition/src/main/groovy/org/jenkinsci/plugins/pipeline/modeldefinition/parser/ModelParser.groovy b/pipeline-model-definition/src/main/groovy/org/jenkinsci/plugins/pipeline/modeldefinition/parser/ModelParser.groovy index 30857660f..a721dd5ed 100644 --- a/pipeline-model-definition/src/main/groovy/org/jenkinsci/plugins/pipeline/modeldefinition/parser/ModelParser.groovy +++ b/pipeline-model-definition/src/main/groovy/org/jenkinsci/plugins/pipeline/modeldefinition/parser/ModelParser.groovy @@ -64,6 +64,7 @@ import org.jenkinsci.plugins.pipeline.modeldefinition.ast.ModelASTScriptBlock import org.jenkinsci.plugins.pipeline.modeldefinition.ast.ModelASTStep import org.jenkinsci.plugins.pipeline.modeldefinition.ast.ModelASTTools import org.jenkinsci.plugins.pipeline.modeldefinition.ast.ModelASTTreeStep +import org.jenkinsci.plugins.pipeline.modeldefinition.ast.ModelASTWhen import org.jenkinsci.plugins.pipeline.modeldefinition.validator.ErrorCollector import org.jenkinsci.plugins.pipeline.modeldefinition.validator.ModelValidator import org.jenkinsci.plugins.pipeline.modeldefinition.validator.ModelValidatorImpl @@ -322,6 +323,9 @@ class ModelParser { case 'agent': stage.agent = parseAgent(s) break + case 'when': + stage.when = parseWhen(s) + break case 'steps': def stepsBlock = matchBlockStatement(s); stage.branches.addAll(parseStepsBlock(asBlock(stepsBlock.body.code))) @@ -579,11 +583,19 @@ class ModelParser { return thisStep } + + public ModelASTWhen parseWhen(Statement st) { + return parseScriptBlockInternal(st, new ModelASTWhen(st), "When") + } + /** * Parses a statement into a {@link ModelASTScriptBlock} */ public ModelASTScriptBlock parseScriptBlock(Statement st) { - ModelASTScriptBlock scriptBlock = new ModelASTScriptBlock(st) + return parseScriptBlockInternal(st, new ModelASTScriptBlock(st), "Script") + } + + private T parseScriptBlockInternal(Statement st, T scriptBlock, String pronoun) { // TODO: Probably error out for cases with parameters? def bs = matchBlockStatement(st); if (bs != null) { @@ -591,7 +603,7 @@ class ModelParser { groovyBlock.value = ModelASTValue.fromConstant(getSourceText(bs.body.code), bs.body.code) scriptBlock.args = groovyBlock } else { - errorCollector.error(scriptBlock, "Script step without a script block") + errorCollector.error(scriptBlock, "${pronoun} step without a block") } return scriptBlock diff --git a/pipeline-model-definition/src/main/groovy/org/jenkinsci/plugins/pipeline/modeldefinition/validator/ModelValidatorImpl.groovy b/pipeline-model-definition/src/main/groovy/org/jenkinsci/plugins/pipeline/modeldefinition/validator/ModelValidatorImpl.groovy index 341a428ef..01e9397ca 100644 --- a/pipeline-model-definition/src/main/groovy/org/jenkinsci/plugins/pipeline/modeldefinition/validator/ModelValidatorImpl.groovy +++ b/pipeline-model-definition/src/main/groovy/org/jenkinsci/plugins/pipeline/modeldefinition/validator/ModelValidatorImpl.groovy @@ -59,6 +59,7 @@ import org.jenkinsci.plugins.pipeline.modeldefinition.ast.ModelASTValue import org.jenkinsci.plugins.pipeline.modeldefinition.ast.ModelASTBuildCondition import org.jenkinsci.plugins.pipeline.modeldefinition.ast.ModelASTAgent import org.jenkinsci.plugins.pipeline.modeldefinition.ast.ModelASTTools +import org.jenkinsci.plugins.pipeline.modeldefinition.ast.ModelASTWhen import org.jenkinsci.plugins.pipeline.modeldefinition.model.BuildCondition import org.jenkinsci.plugins.pipeline.modeldefinition.model.Agent import org.jenkinsci.plugins.pipeline.modeldefinition.model.JobProperties @@ -191,6 +192,11 @@ class ModelValidatorImpl implements ModelValidator { return valid } + public boolean validateElement(ModelASTWhen when) { + //TODO can we evaluate if the closure will return something that can be tries for true? + } + + public boolean validateElement(@Nonnull ModelASTStep step) { boolean valid = true diff --git a/pipeline-model-definition/src/main/resources/org/jenkinsci/plugins/pipeline/modeldefinition/ModelInterpreter.groovy b/pipeline-model-definition/src/main/resources/org/jenkinsci/plugins/pipeline/modeldefinition/ModelInterpreter.groovy index 6bbe93c8f..abd2e979b 100644 --- a/pipeline-model-definition/src/main/resources/org/jenkinsci/plugins/pipeline/modeldefinition/ModelInterpreter.groovy +++ b/pipeline-model-definition/src/main/resources/org/jenkinsci/plugins/pipeline/modeldefinition/ModelInterpreter.groovy @@ -90,43 +90,45 @@ public class ModelInterpreter implements Serializable { for (int i = 0; i < root.stages.getStages().size(); i++) { Stage thisStage = root.stages.getStages().get(i) - script.stage(thisStage.name) { - if (firstError == null) { - nodeOrDockerOrNone(thisStage.agent) { - try { - catchRequiredContextForNode(root.agent) { - setUpDelegate(thisStage.steps.closure).call() - }.call() - } catch (Exception e) { - script.echo "Error in stages execution: ${e.getMessage()}" - script.getProperty("currentBuild").result = Result.FAILURE - if (firstError == null) { - firstError = e - } - } finally { - // And finally, run the post stage steps. - List postClosures = thisStage.satisfiedPostStageConditions(root, script.getProperty("currentBuild")) + runStageOrNot(thisStage, firstError) { + script.stage(thisStage.name) { + if (firstError == null) { + nodeOrDockerOrNone(thisStage.agent) { + try { + catchRequiredContextForNode(root.agent) { + setUpDelegate(thisStage.steps.closure).call() + }.call() + } catch (Exception e) { + script.echo "Error in stages execution: ${e.getMessage()}" + script.getProperty("currentBuild").result = Result.FAILURE + if (firstError == null) { + firstError = e + } + } finally { + // And finally, run the post stage steps. + List postClosures = thisStage.satisfiedPostStageConditions(root, script.getProperty("currentBuild")) - catchRequiredContextForNode(thisStage.agent != null ? thisStage.agent : root.agent, false) { - if (postClosures.size() > 0) { - script.echo("Post stage") //TODO should this be a nested stage instead? - try { - for (int ni = 0; ni < postClosures.size(); ni++) { - setUpDelegate(postClosures.get(ni)).call() - } - } catch (Exception e) { - script.echo "Error in stage post: ${e.getMessage()}" - script.getProperty("currentBuild").result = Result.FAILURE - if (firstError == null) { - firstError = e + catchRequiredContextForNode(thisStage.agent != null ? thisStage.agent : root.agent, false) { + if (postClosures.size() > 0) { + script.echo("Post stage") //TODO should this be a nested stage instead? + try { + for (int ni = 0; ni < postClosures.size(); ni++) { + setUpDelegate(postClosures.get(ni)).call() + } + } catch (Exception e) { + script.echo "Error in stage post: ${e.getMessage()}" + script.getProperty("currentBuild").result = Result.FAILURE + if (firstError == null) { + firstError = e + } } } - } - }.call() - } - }.call() + }.call() + } + }.call() + } } - } + }.call() } try { @@ -287,4 +289,21 @@ public class ModelInterpreter implements Serializable { } } } + + def runStageOrNot(Stage stage, Throwable firstError, Closure body) { + script.echo "Run stage or not!" + if (stage.when != null && firstError == null) { + script.echo "One when!" + return { + if (stage.when.call()) { + body.call() + } + } + } else { + script.echo "No when!" + return { + body.call() + } + } + } } diff --git a/pipeline-model-definition/src/test/java/org/jenkinsci/plugins/pipeline/modeldefinition/WhenStageTest.java b/pipeline-model-definition/src/test/java/org/jenkinsci/plugins/pipeline/modeldefinition/WhenStageTest.java new file mode 100644 index 000000000..0b849269b --- /dev/null +++ b/pipeline-model-definition/src/test/java/org/jenkinsci/plugins/pipeline/modeldefinition/WhenStageTest.java @@ -0,0 +1,57 @@ +/* + * The MIT License + * + * Copyright (c) 2016, 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; + +import hudson.model.Result; +import hudson.model.Slave; +import org.jenkinsci.plugins.pipeline.modeldefinition.model.Stage; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.IOException; + +/** + * Tests {@link Stage#when} + */ +public class WhenStageTest extends AbstractModelDefTest { + + private static Slave s; + + @BeforeClass + public static void setUpAgent() throws Exception { + s = j.createOnlineSlave(); + s.setLabelString("here"); + } + + @Test + public void simpleWhen() throws Exception { + env(s).put("SECOND_STAGE", "NOPE").set(); + ExpectationsBuilder expect = expect("simpleWhen"); + expect.logContains("One", "Hello", "Should I run?").logNotContains("Two", "World").go(); + env(s).put("SECOND_STAGE", "RUN").set(); + expect.resetForNewRun(Result.SUCCESS).logContains("One", "Hello", "Should I run?", "Two", "World").go(); + } +} diff --git a/pipeline-model-definition/src/test/resources/simpleWhen.groovy b/pipeline-model-definition/src/test/resources/simpleWhen.groovy new file mode 100644 index 000000000..0c94b040a --- /dev/null +++ b/pipeline-model-definition/src/test/resources/simpleWhen.groovy @@ -0,0 +1,48 @@ +/* + * The MIT License + * + * Copyright (c) 2016, 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 label: "here" + stages { + stage("One") { + when { + echo "Should I run?" + return true + } + steps { + echo "Hello" + } + } + stage("Two") { + when { + echo "Should I run?" + return env.SECOND_STAGE == "RUN" + } + steps { + echo "World" + } + } + } +} \ No newline at end of file