Skip to content

Commit

Permalink
[FIXED JENKINS-39244, FIXED JENKINS-39245] Add env and tools to stages
Browse files Browse the repository at this point in the history
Adds support for tools and environment sections inside individual stages.
  • Loading branch information
abayer committed Oct 25, 2016
1 parent d78fa19 commit 7d3724c
Show file tree
Hide file tree
Showing 13 changed files with 330 additions and 36 deletions.
Expand Up @@ -18,6 +18,8 @@ public final class ModelASTStage extends ModelASTElement {
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);
Expand All @@ -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;
}

Expand All @@ -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
Expand All @@ -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(");
Expand Down Expand Up @@ -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() {
Expand Down Expand Up @@ -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 +
"}";
}

Expand All @@ -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;

}
Expand All @@ -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;
}
}
Expand Up @@ -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
Expand Down Expand Up @@ -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) {
Expand Down
Expand Up @@ -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")
Expand Down
Expand Up @@ -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}'")
}
Expand Down
6 changes: 6 additions & 0 deletions pipeline-model-definition/src/main/resources/ast-schema.json
Expand Up @@ -322,6 +322,12 @@
"post": {
"$ref": "#/definitions/notifications"
},
"tools": {
"$ref": "#/definitions/tools"
},
"environment": {
"$ref": "#/definitions/environment"
},
"branches": {
"type": "array",
"items": {
Expand Down
Expand Up @@ -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
Expand Down Expand Up @@ -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.
Expand All @@ -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()
}
}

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

def withEnvBlock(List<String> envVars, Closure body) {
System.err.println("IN WITHENV BLOCK WITH ${envVars}")
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) {
Expand Down
Expand Up @@ -118,6 +118,8 @@ public void setUp() throws Exception {
"simpleJobProperties",
"simpleTriggers",
"simpleParameters",
"toolsInStage",
"environmentInStage",
"stringsNeedingEscapeLogic"
);

Expand Down
Expand Up @@ -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");
Expand Down
Expand Up @@ -60,6 +60,16 @@ public void simpleTools() throws Exception {
j.assertLogContains("Apache Maven 3.0.1", b);
}

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

WorkflowRun b = getAndStartBuild();
j.assertBuildStatusSuccess(j.waitForCompletion(b));
j.assertLogContains("[Pipeline] { (foo)", b);
j.assertLogContains("Apache Maven 3.0.1", b);
}

@Test
public void buildPluginParentPOM() throws Exception {
prepRepoWithJenkinsfile("buildPluginParentPOM");
Expand Down

0 comments on commit 7d3724c

Please sign in to comment.