Skip to content
Permalink
Browse files

Merge pull request #78 from jenkinsci/JENKINS-40370

[JENKINS-40370] Make stage.when more declarative
  • Loading branch information...
abayer committed Jan 9, 2017
2 parents 1e14524 + cadb16c commit cf217be38d68c3ef7dda7e80539ce10a3c4cf8f6
Showing with 2,028 additions and 278 deletions.
  1. +28 −1 SYNTAX.md
  2. +1 −1 ...i/src/main/java/org/jenkinsci/plugins/pipeline/modeldefinition/ast/AbstractModelASTCodeBlock.java
  3. +54 −10 ...line-model-api/src/main/java/org/jenkinsci/plugins/pipeline/modeldefinition/ast/ModelASTWhen.java
  4. +42 −0 ...-api/src/main/java/org/jenkinsci/plugins/pipeline/modeldefinition/ast/ModelASTWhenExpression.java
  5. +2 −0 ...el-api/src/main/java/org/jenkinsci/plugins/pipeline/modeldefinition/validator/ModelValidator.java
  6. +17 −6 pipeline-model-api/src/main/resources/ast-schema.json
  7. +2 −30 ...ve-agent/src/main/java/org/jenkinsci/plugins/pipeline/modeldefinition/agent/DeclarativeAgent.java
  8. +4 −50 ...rc/main/java/org/jenkinsci/plugins/pipeline/modeldefinition/agent/DeclarativeAgentDescriptor.java
  9. +4 −7 ...nt/src/main/java/org/jenkinsci/plugins/pipeline/modeldefinition/agent/DeclarativeAgentScript.java
  10. +64 −0 ...rc/main/java/org/jenkinsci/plugins/pipeline/modeldefinition/withscript/WithScriptDescribable.java
  11. +86 −0 ...src/main/java/org/jenkinsci/plugins/pipeline/modeldefinition/withscript/WithScriptDescriptor.java
  12. +39 −0 ...ent/src/main/java/org/jenkinsci/plugins/pipeline/modeldefinition/withscript/WithScriptScript.java
  13. +27 −1 ...line-model-definition/src/main/groovy/org/jenkinsci/plugins/pipeline/modeldefinition/Utils.groovy
  14. +2 −2 ...odel-definition/src/main/groovy/org/jenkinsci/plugins/pipeline/modeldefinition/model/Stage.groovy
  15. +48 −0 ...ion/src/main/groovy/org/jenkinsci/plugins/pipeline/modeldefinition/model/StageConditionals.groovy
  16. +14 −3 ...efinition/src/main/groovy/org/jenkinsci/plugins/pipeline/modeldefinition/parser/JSONParser.groovy
  17. +16 −3 ...finition/src/main/groovy/org/jenkinsci/plugins/pipeline/modeldefinition/parser/ModelParser.groovy
  18. +96 −64 ...rc/main/groovy/org/jenkinsci/plugins/pipeline/modeldefinition/validator/ModelValidatorImpl.groovy
  19. +3 −8 ...model-definition/src/main/java/org/jenkinsci/plugins/pipeline/modeldefinition/agent/impl/Any.java
  20. +2 −2 ...ition/src/main/java/org/jenkinsci/plugins/pipeline/modeldefinition/agent/impl/DockerPipeline.java
  21. +2 −2 .../java/org/jenkinsci/plugins/pipeline/modeldefinition/agent/impl/DockerPipelineFromDockerfile.java
  22. +2 −2 ...del-definition/src/main/java/org/jenkinsci/plugins/pipeline/modeldefinition/agent/impl/Label.java
  23. +2 −3 ...odel-definition/src/main/java/org/jenkinsci/plugins/pipeline/modeldefinition/agent/impl/None.java
  24. +54 −0 ...rc/main/java/org/jenkinsci/plugins/pipeline/modeldefinition/when/DeclarativeStageConditional.java
  25. +103 −0 ...va/org/jenkinsci/plugins/pipeline/modeldefinition/when/DeclarativeStageConditionalDescriptor.java
  26. +38 −0 ...n/java/org/jenkinsci/plugins/pipeline/modeldefinition/when/DeclarativeStageConditionalScript.java
  27. +84 −0 ...ion/src/main/java/org/jenkinsci/plugins/pipeline/modeldefinition/when/impl/BranchConditional.java
  28. +103 −0 ...rc/main/java/org/jenkinsci/plugins/pipeline/modeldefinition/when/impl/EnvironmentConditional.java
  29. +55 −0 ...src/main/java/org/jenkinsci/plugins/pipeline/modeldefinition/when/impl/ExpressionConditional.java
  30. +9 −11 ...n/src/main/resources/org/jenkinsci/plugins/pipeline/modeldefinition/ClosureModelTranslator.groovy
  31. +1 −0 ...-definition/src/main/resources/org/jenkinsci/plugins/pipeline/modeldefinition/Messages.properties
  32. +2 −7 .../src/main/resources/org/jenkinsci/plugins/pipeline/modeldefinition/MethodsToListTranslator.groovy
  33. +19 −1 ...inition/src/main/resources/org/jenkinsci/plugins/pipeline/modeldefinition/ModelInterpreter.groovy
  34. +6 −11 ...nition/src/main/resources/org/jenkinsci/plugins/pipeline/modeldefinition/OptionsTranslator.groovy
  35. +90 −0 ...c/main/resources/org/jenkinsci/plugins/pipeline/modeldefinition/StageConditionalTranslator.groovy
  36. +51 −0 ...ion/src/main/resources/org/jenkinsci/plugins/pipeline/modeldefinition/agent/impl/AnyScript.groovy
  37. +6 −7 ...g/jenkinsci/plugins/pipeline/modeldefinition/agent/impl/DockerPipelineFromDockerfileScript.groovy
  38. +5 −5 ...n/resources/org/jenkinsci/plugins/pipeline/modeldefinition/agent/impl/DockerPipelineScript.groovy
  39. +3 −3 ...n/src/main/resources/org/jenkinsci/plugins/pipeline/modeldefinition/agent/impl/LabelScript.groovy
  40. +2 −3 ...on/src/main/resources/org/jenkinsci/plugins/pipeline/modeldefinition/agent/impl/NoneScript.groovy
  41. +41 −0 ...resources/org/jenkinsci/plugins/pipeline/modeldefinition/when/impl/BranchConditionalScript.groovy
  42. +42 −0 ...rces/org/jenkinsci/plugins/pipeline/modeldefinition/when/impl/EnvironmentConditionalScript.groovy
  43. +50 −0 ...urces/org/jenkinsci/plugins/pipeline/modeldefinition/when/impl/ExpressionConditionalScript.groovy
  44. +9 −0 ...definition/src/test/java/org/jenkinsci/plugins/pipeline/modeldefinition/AbstractModelDefTest.java
  45. +15 −0 ...el-definition/src/test/java/org/jenkinsci/plugins/pipeline/modeldefinition/BasicModelDefTest.java
  46. +38 −0 ...-model-definition/src/test/java/org/jenkinsci/plugins/pipeline/modeldefinition/ValidatorTest.java
  47. +2 −13 .../test/java/org/jenkinsci/plugins/pipeline/modeldefinition/agent/impl/LabelAndOtherFieldAgent.java
  48. +4 −2 pipeline-model-definition/src/test/resources/basicWhen.groovy
  49. +3 −1 pipeline-model-definition/src/test/resources/booleanParamBuildStep.groovy
  50. +39 −0 pipeline-model-definition/src/test/resources/errors/emptyWhen.groovy
  51. +40 −0 pipeline-model-definition/src/test/resources/errors/unknownWhenConditional.groovy
  52. +46 −0 pipeline-model-definition/src/test/resources/errors/whenInvalidParameterType.groovy
  53. +50 −0 pipeline-model-definition/src/test/resources/errors/whenMissingRequiredParameter.groovy
  54. +50 −0 pipeline-model-definition/src/test/resources/errors/whenUnknownParameter.groovy
  55. +7 −4 pipeline-model-definition/src/test/resources/json/basicWhen.json
  56. +17 −0 pipeline-model-definition/src/test/resources/json/errors/emptyWhen.json
  57. +23 −0 pipeline-model-definition/src/test/resources/json/errors/unknownWhenConditional.json
  58. +38 −0 pipeline-model-definition/src/test/resources/json/errors/whenInvalidParameterType.json
  59. +48 −0 pipeline-model-definition/src/test/resources/json/errors/whenMissingRequiredParameter.json
  60. +64 −0 pipeline-model-definition/src/test/resources/json/errors/whenUnknownParameter.json
  61. +7 −4 pipeline-model-definition/src/test/resources/json/skippedWhen.json
  62. +38 −0 pipeline-model-definition/src/test/resources/json/whenBranch.json
  63. +57 −0 pipeline-model-definition/src/test/resources/json/whenEnv.json
  64. +4 −5 ...es/org/jenkinsci/plugins/pipeline/modeldefinition/agent/impl/LabelAndOtherFieldAgentScript.groovy
  65. +4 −2 pipeline-model-definition/src/test/resources/skippedWhen.groovy
  66. +4 −2 pipeline-model-definition/src/test/resources/when/simpleWhen.groovy
  67. +4 −2 pipeline-model-definition/src/test/resources/when/whenException.groovy
  68. +46 −0 pipeline-model-definition/src/test/resources/whenBranch.groovy
  69. +50 −0 pipeline-model-definition/src/test/resources/whenEnv.groovy
@@ -131,7 +131,7 @@ stages {
label 'some-node'
}
when {
env.BRANCH == 'master'
branch "master"
}
steps {
checkout scm
@@ -164,6 +164,33 @@ stages {
}
```

#### when
* *Description*: A block within a `stage` that determines whether the stage should be executed.
* *Required*: No
* *Parameters*: None
* *Takes a Closure*: Yes
* *Closure Contents*: One or more of the possible `when` conditions, provided by an extension point.
* *Built-In `when` Conditions*:
* `branch` - run when the branch being built matches the branch pattern given.
* `environment` - run when the environment contains a given environment variable with a given value.
* `expression` - run when an arbitrary Groovy expression returning a boolean is true.
* *Examples*:
```groovy
when {
branch "*/master"
}
when {
environment name: "FOO", value: "SOME_OTHER_VALUE"
}
when {
expression {
return "foo" == "bar"
}
}
```

#### script
* *Description*: A block within a `stage`'s steps that can contain Pipeline code not subject to the "declarative" subset
described below.
@@ -33,7 +33,7 @@
/**
* Represents the special step which are executed without validation against the declarative subset.
* @see ModelASTScriptBlock
* @see ModelASTWhen
* @see ModelASTWhenExpression
*/
public abstract class AbstractModelASTCodeBlock extends ModelASTStep {

@@ -25,29 +25,73 @@

package org.jenkinsci.plugins.pipeline.modeldefinition.ast;

import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
import org.jenkinsci.plugins.pipeline.modeldefinition.validator.ModelValidator;

import java.util.ArrayList;
import java.util.List;

/**
* Represents a block for when/if a {@link ModelASTStage} will be executed or not.
* If {@link ModelASTStage} will be executed or not.
*/
public class ModelASTWhen extends AbstractModelASTCodeBlock {
public class ModelASTWhen extends ModelASTElement {

private List<ModelASTStep> conditions = new ArrayList<>();

public ModelASTWhen(Object sourceLocation) {
super(sourceLocation, "when");
super(sourceLocation);
}

public List<ModelASTStep> getConditions() {
return conditions;
}

public void setConditions(List<ModelASTStep> conditions) {
this.conditions = conditions;
}

@Override
public Object toJSON() {
final JSONArray a = new JSONArray();
for (ModelASTStep c: conditions) {
a.add(c.toJSON());
}
return new JSONObject().accumulate("conditions", a);
}

@Override
public JSONObject toJSON() {
JSONObject o = new JSONObject();
if (getArgs() != null) {
o.accumulate("arguments", getArgs().toJSON());
public String toGroovy() {
StringBuilder result = new StringBuilder("when {\n");
for (ModelASTStep c: conditions) {
result.append(c.toGroovy()).append("\n");
}
return o;
result.append("}\n");
return result.toString();
}

@Override
public void validate(ModelValidator validator) {
super.validate(validator);
public void removeSourceLocation() {
super.removeSourceLocation();
for (ModelASTStep c: conditions) {
c.removeSourceLocation();
}
}

@Override
public String toString() {
return "ModelASTWhen{" +
"conditions=" + conditions +
"}";
}

@Override
public void validate(final ModelValidator validator) {
validator.validateElement(this);
for (ModelASTStep s : conditions) {
validator.validateWhenCondition(s);
}
}


}
@@ -0,0 +1,42 @@
/*
* 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;

/**
* Code expression {@link ModelASTStage} will be executed or not.
*/
public class ModelASTWhenExpression extends AbstractModelASTCodeBlock {
public ModelASTWhenExpression(Object sourceLocation) {
super(sourceLocation, "expression");
}

@Override
public void validate(ModelValidator validator) {
// no-op - we don't do validation of script blocks
}
}
@@ -88,4 +88,6 @@
boolean validateElement(ModelASTStage stage);

boolean validateElement(ModelASTStages stages);

boolean validateWhenCondition(ModelASTStep condition);
}
@@ -320,17 +320,28 @@
"additionalProperties": false
},
"when": {
"description": "Closure to evaluate if the stage should run or not",
"description": "Conditions to evaluate whether the stage should run or not",
"type": "object",
"properties": {
"arguments": {
"$ref": "#/definitions/rawArgument"
"conditions": {
"type": "array",
"minItems": 1,
"items": {
"anyOf": [
{
"$ref": "#/definitions/step"
},
{
"$ref": "#/definitions/treeStep"
}
]
}
}

},

"required": [
"conditions"
],
"additionalProperties": false

},
"post": {
"description": "An array of build conditions with blocks of steps to run if those conditions are satisfied at the end of the build while still on the image/node the build ran on",
@@ -25,41 +25,13 @@
package org.jenkinsci.plugins.pipeline.modeldefinition.agent;

import hudson.ExtensionPoint;
import hudson.model.AbstractDescribableImpl;
import org.jenkinsci.plugins.workflow.cps.CpsScript;
import org.jenkinsci.plugins.workflow.cps.CpsThread;

import java.io.Serializable;
import org.jenkinsci.plugins.pipeline.modeldefinition.withscript.WithScriptDescribable;

/**
* Implementations for {@link DeclarativeAgentDescriptor} - pluggable agent backends for Declarative Pipelines.
*
* @author Andrew Bayer
*/
public abstract class DeclarativeAgent extends AbstractDescribableImpl<DeclarativeAgent> implements Serializable, ExtensionPoint {

/**
* ONLY TO BE RUN FROM WITHIN A CPS THREAD. Parses the script source and loads it.
* TODO: Decide if we want to cache the resulting objects or just *shrug* and re-parse them every time.
*
* @return The script object for this declarative agent.
* @throws Exception if the script source cannot be loaded or we're called from outside a CpsThread.
*/
@SuppressWarnings("unchecked")
public DeclarativeAgentScript getScript(CpsScript cpsScript) throws Exception {
CpsThread c = CpsThread.current();
if (c == null)
throw new IllegalStateException("Expected to be called from CpsThread");

return (DeclarativeAgentScript) cpsScript.getClass()
.getClassLoader()
.loadClass(getDescriptor().getDeclarativeAgentScriptClass())
.getConstructor(CpsScript.class, DeclarativeAgent.class)
.newInstance(cpsScript, this);
}
public abstract class DeclarativeAgent<A extends DeclarativeAgent<A>> extends WithScriptDescribable<A> implements ExtensionPoint {

@Override
public DeclarativeAgentDescriptor getDescriptor() {
return (DeclarativeAgentDescriptor) super.getDescriptor();
}
}
@@ -24,7 +24,7 @@
package org.jenkinsci.plugins.pipeline.modeldefinition.agent;

import hudson.ExtensionList;
import hudson.model.Descriptor;
import org.jenkinsci.plugins.pipeline.modeldefinition.withscript.WithScriptDescriptor;
import org.jenkinsci.plugins.structs.SymbolLookup;
import org.jenkinsci.plugins.structs.describable.DescribableModel;
import org.jenkinsci.plugins.structs.describable.UninstantiatedDescribable;
@@ -33,59 +33,13 @@
import javax.annotation.Nullable;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/**
* Descriptor for {@link DeclarativeAgent}.
*
* @author Andrew Bayer
*/
public abstract class DeclarativeAgentDescriptor extends Descriptor<DeclarativeAgent> {

/**
* The name for this agent type. Defaults to the first string in the {@code Symbol} on the class.
*
* @return The name.
*/
public @Nonnull String getName() {
Set<String> symbolValues = SymbolLookup.getSymbolValue(this);
if (symbolValues.isEmpty()) {
throw new IllegalArgumentException("Declarative Agent descriptor class " + this.getClass().getName()
+ " does not have a @Symbol and does not override getName().");
}
return symbolValues.iterator().next();
}

/**
* The full package and class name for the {@link DeclarativeAgentScript} class corresponding to this. Defaults to
* the {@link DeclarativeAgent} class name with "Script" appended to the end.
*
* @return The class name, defaulting to the {@link DeclarativeAgent} {@link #clazz} class name with "Script" appended.
*/
public @Nonnull String getDeclarativeAgentScriptClass() {
return clazz.getName() + "Script";
}

/**
* Creates an instance of the corresponding {@link DeclarativeAgent} from the given arguments.
*
* @param arguments A map of strings/objects to be passed to the constructor.
* @return An instantiated {@link DeclarativeAgent}
* @throws Exception
*/
public DeclarativeAgent newInstance(Map<String,Object> arguments) throws Exception {
return new DescribableModel<>(clazz).instantiate(arguments);
}

/**
* Creates an instance of the corresponding {@link DeclarativeAgent} with no arguments.
*
* @return An instantiated {@link DeclarativeAgent}
* @throws Exception
*/
public DeclarativeAgent newInstance() throws Exception {
return clazz.newInstance();
}
public abstract class DeclarativeAgentDescriptor<A extends DeclarativeAgent<A>> extends WithScriptDescriptor<A> {

/**
* Get all {@link DeclarativeAgentDescriptor}s.
@@ -162,7 +116,7 @@ public DeclarativeAgent newInstance() throws Exception {
* @return The instantiated {@link DeclarativeAgent} instance, or null if the name isn't found.
* @throws Exception
*/
public static @Nullable DeclarativeAgent instanceForName(@Nonnull String name,
public static @Nullable DeclarativeAgent<?> instanceForName(@Nonnull String name,
Map<String,Object> arguments) throws Exception {
DeclarativeAgentDescriptor descriptor = byName(name);

@@ -181,7 +135,7 @@ public DeclarativeAgent newInstance() throws Exception {
* @return The instantiated {@link DeclarativeAgent} instance.
* @throws Exception
*/
public static @Nonnull DeclarativeAgent instanceForDescriptor(@Nonnull DeclarativeAgentDescriptor descriptor,
public static @Nonnull DeclarativeAgent<?> instanceForDescriptor(@Nonnull DeclarativeAgentDescriptor<?> descriptor,
Map<String,Object> arguments) throws Exception {
if (zeroArgModels().keySet().contains(descriptor.getName()) ||
(noRequiredArgsModels().keySet().contains(descriptor.getName()) &&
@@ -24,17 +24,14 @@
package org.jenkinsci.plugins.pipeline.modeldefinition.agent;

import groovy.lang.Closure;
import org.jenkinsci.plugins.pipeline.modeldefinition.withscript.WithScriptScript;
import org.jenkinsci.plugins.workflow.cps.CpsScript;

import java.io.Serializable;

public abstract class DeclarativeAgentScript implements Serializable {
protected CpsScript script;
protected DeclarativeAgent declarativeAgent;
public abstract class DeclarativeAgentScript<A extends DeclarativeAgent<A>> extends WithScriptScript<A> {

public DeclarativeAgentScript(CpsScript s, DeclarativeAgent a) {
this.script = s;
this.declarativeAgent = a;
public DeclarativeAgentScript(CpsScript s, A a) {
super(s, a);
}

public abstract Closure run(Closure body);
Oops, something went wrong.

0 comments on commit cf217be

Please sign in to comment.
You can’t perform that action at this time.