Skip to content
Permalink
Browse files

Merge pull request #40 from abayer/jenkins-39244

[FIXED JENKINS-39244, FIXED JENKINS-39245] Add env and tools to stages
  • Loading branch information...
rsandell committed Oct 28, 2016
2 parents 869ab82 + 8f93d6f commit 83a83f675427cbc2eb6f4ba4bf8e487c1654fbf0
@@ -42,7 +42,7 @@ These are sections that are specified directly within the `pipeline` argument cl
* *Description*: A sequence of `key = value` pairs, which will be passed to the `withEnv` step the build will be
executed within.
* *Required*: No
* *Allowed In*: Top-level `pipeline` closure only.
* *Allowed In*: Top-level `pipeline` or `stage` closures only.
* *Parameters*: None
* *Takes a Closure*: Yes
* *Closure Contents*: One or more lines with `foo = 'bar'` variable name/value pairs.
@@ -146,10 +146,10 @@ stages {
```

### tools
* *Description*: A top-level section defining tools to auto-install and put on the PATH. This is ignored if `image none`
* *Description*: A section defining tools to auto-install and put on the PATH. This is ignored if `image none`
is specified.
* *Required*: No
* *Allowed In*: Top-level `pipeline` closure only.
* *Allowed In*: Top-level `pipeline` or `stage` closures only.
* *Parameters*: None
* *Takes a Closure*: Yes
* *Closure Contents*: Names and versions of tools configured in Jenkins to install.
@@ -18,6 +18,8 @@
private ModelASTAgent agent;
private List<ModelASTBranch> branches = new ArrayList<ModelASTBranch>();
private ModelASTPostStage post;
private ModelASTTools tools;
private ModelASTEnvironment environment;

public ModelASTStage(Object sourceLocation) {
super(sourceLocation);
@@ -40,6 +42,14 @@ public JSONObject toJSON() {
o.accumulate("post", post.toJSON());
}

if (tools != null) {
o.accumulate("tools", tools.toJSON());
}

if (environment != null) {
o.accumulate("environment", environment.toJSON());
}

return o;
}

@@ -55,6 +65,12 @@ public void validate(final ModelValidator validator) {
if (post != null) {
post.validate(validator);
}
if (tools != null) {
tools.validate(validator);
}
if (environment != null) {
environment.validate(validator);
}
}

@Override
@@ -65,7 +81,12 @@ public String toGroovy() {
if (agent != null) {
result.append(agent.toGroovy());
}

if (tools != null) {
result.append(tools.toGroovy());
}
if (environment != null) {
result.append(environment.toGroovy());
}
result.append("steps {\n");
if (branches.size() > 1) {
result.append("parallel(");
@@ -107,6 +128,12 @@ public void removeSourceLocation() {
if (post != null) {
post.removeSourceLocation();
}
if (tools != null) {
tools.removeSourceLocation();
}
if (environment != null) {
environment.removeSourceLocation();
}
}

public String getName() {
@@ -141,13 +168,31 @@ public void setPost(ModelASTPostStage post) {
this.post = post;
}

public ModelASTTools getTools() {
return tools;
}

public void setTools(ModelASTTools tools) {
this.tools = tools;
}

public ModelASTEnvironment getEnvironment() {
return environment;
}

public void setEnvironment(ModelASTEnvironment environment) {
this.environment = environment;
}

@Override
public String toString() {
return "ModelASTStage{" +
"name='" + name + '\'' +
", agent=" + agent +
", branches=" + branches +
", post=" + post +
", tools=" + tools +
", environment=" + environment +
"}";
}

@@ -174,6 +219,12 @@ public boolean equals(Object o) {
if (getPost() != null ? !getPost().equals(that.getPost()) : that.getPost() != null) {
return false;
}
if (getTools() != null ? !getTools().equals(that.getTools()) : that.getTools() != null) {
return false;
}
if (getEnvironment() != null ? !getEnvironment().equals(that.getEnvironment()) : that.getEnvironment() != null) {
return false;
}
return getBranches() != null ? getBranches().equals(that.getBranches()) : that.getBranches() == null;

}
@@ -184,6 +235,9 @@ 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 + (getPost() != null ? getPost().hashCode() : 0);
result = 31 * result + (getTools() != null ? getTools().hashCode() : 0);
result = 31 * result + (getEnvironment() != null ? getEnvironment().hashCode() : 0);
return result;
}
}
@@ -50,6 +50,10 @@ public class Stage implements NestedModel, Serializable {
@Whitelisted
PostStage post

Tools tools

Environment environment

@Whitelisted
Stage name(String n) {
this.name = n
@@ -80,6 +84,29 @@ public class Stage implements NestedModel, Serializable {
return this
}

Stage tools(Tools tools) {
this.tools = tools
return this
}

Stage environment(Environment environment) {
this.environment = environment
return this
}

/**
* Helper method for translating the key/value pairs in the {@link Environment} into a list of "key=value" strings
* suitable for use with the withEnv step.
*
* @return a list of "key=value" strings.
*/
List<String> getEnvVars() {
return environment.collect { k, v ->
"${k}=${v}"
}
}


@Override
@Whitelisted
public void modelFromMap(Map<String,Object> m) {
@@ -180,18 +180,25 @@ class JSONParser {
stage.branches.add(parseBranch(o))
}

if (j.has("environment")) {
stage.environment = parseEnvironment(j.getJSONArray("environment"))
}

if (j.has("tools")) {
stage.tools = parseTools(j.getJSONArray("tools"))
}

if (j.has("post")) {
def object = j.getJSONObject("post")
if (!object.isNullObject()) {
stage.post = parsePostStage(object)
}
}

return stage

}



public @CheckForNull ModelASTBranch parseBranch(JSONObject j) {
ModelASTBranch branch = new ModelASTBranch(j)
branch.name = j.getString("name")
@@ -329,6 +329,12 @@ class ModelParser {
case 'post':
stage.post = parsePostStage(s)
break;
case 'tools':
stage.tools = parseTools(s)
break
case 'environment':
stage.environment = parseEnvironment(s)
break
default:
errorCollector.error(stage, "Unknown stage section '${name}'")
}
@@ -322,6 +322,12 @@
"post": {
"$ref": "#/definitions/notifications"
},
"tools": {
"$ref": "#/definitions/tools"
},
"environment": {
"$ref": "#/definitions/environment"
},
"branches": {
"type": "array",
"items": {
@@ -28,6 +28,7 @@ import hudson.FilePath
import hudson.Launcher
import hudson.model.Result
import org.jenkinsci.plugins.pipeline.modeldefinition.model.Agent
import org.jenkinsci.plugins.pipeline.modeldefinition.model.Environment
import org.jenkinsci.plugins.pipeline.modeldefinition.model.Root
import org.jenkinsci.plugins.pipeline.modeldefinition.model.Stage
import org.jenkinsci.plugins.pipeline.modeldefinition.model.Tools
@@ -76,7 +77,7 @@ public class ModelInterpreter implements Serializable {
}

// Entire build, including notifications, runs in the withEnv.
script.withEnv(root.getEnvVars()) {
withEnvBlock(root.getEnvVars()) {
// Stage execution and post-build actions run in try/catch blocks, so we still run post-build actions
// even if the build fails, and we still send notifications if the build and/or post-build actions fail.
// We save the caught error, if any, for throwing at the end of the build.
@@ -91,41 +92,46 @@ public class ModelInterpreter implements Serializable {
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<Closure> postClosures = thisStage.satisfiedPostStageConditions(root, script.getProperty("currentBuild"))
withEnvBlock(thisStage.getEnvVars()) {
if (firstError == null) {
nodeOrDockerOrNone(thisStage.agent) {
toolsBlock(thisStage.agent ?: root.agent, thisStage.tools) {
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<Closure> 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()
}
}

@@ -170,7 +176,7 @@ public class ModelInterpreter implements Serializable {
firstError = e
}
}
}
}.call()
if (firstError != null) {
throw firstError
}
@@ -204,6 +210,20 @@ public class ModelInterpreter implements Serializable {
}
}

def withEnvBlock(List<String> envVars, Closure body) {
if (envVars != null && !envVars.isEmpty()) {
return {
script.withEnv(envVars) {
body.call()
}
}
} else {
return {
body.call()
}
}
}

def toolsBlock(Agent agent, Tools tools, Closure body) {
// If there's no agent, don't install tools in the first place.
if (agent.hasAgent() && tools != null) {
@@ -118,6 +118,8 @@ public void setUp() throws Exception {
"simpleJobProperties",
"simpleTriggers",
"simpleParameters",
"toolsInStage",
"environmentInStage",
"stringsNeedingEscapeLogic"
);

@@ -54,6 +54,16 @@ public void simpleEnvironment() throws Exception {
j.assertLogContains("FOO is BAR", b);
}

@Test
public void environmentInStage() throws Exception {
prepRepoWithJenkinsfile("environmentInStage");

WorkflowRun b = getAndStartBuild();
j.assertBuildStatusSuccess(j.waitForCompletion(b));
j.assertLogContains("[Pipeline] { (foo)", b);
j.assertLogContains("FOO is BAR", b);
}

@Test
public void nonLiteralEnvironment() throws Exception {
prepRepoWithJenkinsfile("nonLiteralEnvironment");
Oops, something went wrong.

0 comments on commit 83a83f6

Please sign in to comment.
You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.