/*************************** GO-LICENSE-START*********************************
* Copyright 2016 ThoughtWorks, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ************************GO-LICENSE-END***********************************/
package com.thoughtworks.go.server.domain;
import com.thoughtworks.go.config.ArtifactPlan;
import com.thoughtworks.go.config.ArtifactPropertiesGenerator;
import com.thoughtworks.go.config.materials.Materials;
import com.thoughtworks.go.domain.*;
import com.thoughtworks.go.domain.builder.Builder;
import com.thoughtworks.go.remote.work.BuildAssignment;
import com.thoughtworks.go.util.command.EnvironmentVariableContext;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import static com.thoughtworks.go.domain.BuildCommand.*;
import static com.thoughtworks.go.domain.JobState.*;
public class BuildComposer {
private BuildAssignment assignment;
public BuildComposer(BuildAssignment assignment) {
this.assignment = assignment;
}
public BuildCommand compose() {
return BuildCommand.compose(
echoWithPrefix("Job Started: ${date}"),
prepare(),
build(),
reportAction("Job completed").runIf("any"))
.setOnCancel(BuildCommand.compose(
reportAction("Job is canceled"),
reportAction("Job completed")));
}
private BuildCommand prepare() {
return BuildCommand.compose(
reportAction("Start to prepare"),
reportCurrentStatus(Preparing),
refreshWorkingDir(),
updateMaterials());
}
private BuildCommand build() {
return BuildCommand.compose(
reportAction("Start to build"),
reportCurrentStatus(Building),
setupSecrets(),
setupEnvironmentVariables(),
runBuilders(),
BuildCommand.compose(
reportCompleting(),
reportCurrentStatus(Completing),
harvestProperties(),
uploadArtifacts()).setRunIfRecurisvely("any"));
}
private BuildCommand harvestProperties() {
List<ArtifactPropertiesGenerator> generators = assignment.getPlan().getPropertyGenerators();
List<BuildCommand> commands = new ArrayList<>();
for (ArtifactPropertiesGenerator generator : generators) {
BuildCommand command = BuildCommand.generateProperty(generator.getName(), generator.getSrc(), generator.getXpath()).setWorkingDirectory(workingDirectory());
commands.add(command);
}
return BuildCommand.compose(
reportAction("Start to create properties"),
BuildCommand.compose(commands));
}
private BuildCommand runBuilders() {
List<BuildCommand> commands = new ArrayList<>();
for (Builder builder : assignment.getBuilders()) {
commands.add(runSingleBuilder(builder));
}
return BuildCommand.compose(commands);
}
private BuildCommand runSingleBuilder(Builder builder) {
String runIfConfig = builder.resolvedRunIfConfig().toString();
return BuildCommand.compose(
echoWithPrefix("Current job status: passed"),
echoWithPrefix("Current job status: failed").runIf("failed"),
echoWithPrefix("Task: %s", builder.getDescription()).runIf(runIfConfig),
builder.buildCommand()
.runIf(runIfConfig)
.setOnCancel(runCancelTask(builder.getCancelBuilder()))).runIf(runIfConfig);
}
private BuildCommand runCancelTask(Builder cancelBuilder) {
if (cancelBuilder == null) {
return null;
}
return BuildCommand.compose(
echoWithPrefix("Cancel task: %s", cancelBuilder.getDescription()),
cancelBuilder.buildCommand(),
echoWithPrefix("Task is cancelled"));
}
private BuildCommand uploadArtifacts() {
List<BuildCommand> commands = new ArrayList<>();
for (ArtifactPlan ap : assignment.getPlan().getArtifactPlans()) {
commands.add(uploadArtifact(ap.getSrc(), ap.getDest(), ap.getArtifactType().isTest())
.setWorkingDirectory(workingDirectory()));
}
return BuildCommand.compose(
reportAction("Start to upload"),
BuildCommand.compose(commands),
generateTestReport());
}
private BuildCommand generateTestReport() {
List<String> srcs = new ArrayList<>();
for (ArtifactPlan ap : assignment.getPlan().getArtifactPlans()) {
if (ap.getArtifactType() == ArtifactType.unit) {
srcs.add(ap.getSrc());
}
}
return srcs.isEmpty() ? noop() : BuildCommand.generateTestReport(srcs, "testoutput").setWorkingDirectory(workingDirectory());
}
private BuildCommand setupSecrets() {
List<EnvironmentVariableContext.EnvironmentVariable> secrets = environmentVariableContext().getSecureEnvironmentVariables();
ArrayList<BuildCommand> commands = new ArrayList<>();
for (EnvironmentVariableContext.EnvironmentVariable secret : secrets) {
commands.add(secret(secret.value()));
}
return BuildCommand.compose(commands);
}
private BuildCommand setupEnvironmentVariables() {
EnvironmentVariableContext context = environmentVariableContext();
ArrayList<BuildCommand> commands = new ArrayList<>();
commands.add(export("GO_SERVER_URL"));
for (String property : context.getPropertyKeys()) {
commands.add(export(property, context.getProperty(property), context.isPropertySecure(property)));
}
return BuildCommand.compose(commands);
}
private BuildCommand reportAction(String action) {
return echoWithPrefix("%s %s on ${agent.hostname} [${agent.location}]", action, getJobIdentifier().buildLocatorForDisplay());
}
private BuildCommand updateMaterials() {
if (!assignment.getPlan().shouldFetchMaterials()) {
return echoWithPrefix("Skipping material update since stage is configured not to fetch materials");
}
MaterialRevisions materialRevisions = assignment.materialRevisions();
Materials materials = materialRevisions.getMaterials();
return BuildCommand.compose(
materials.cleanUpCommand(workingDirectory()),
echoWithPrefix("Start to update materials \n"),
materialRevisions.updateToCommand(workingDirectory()));
}
private BuildCommand refreshWorkingDir() {
return BuildCommand.compose(
cleanWorkingDir(),
mkdirs(workingDirectory()).setTest(test("-nd", workingDirectory())));
}
private BuildCommand cleanWorkingDir() {
if (!assignment.getPlan().shouldCleanWorkingDir()) {
return noop();
}
return BuildCommand.compose(
cleandir(workingDirectory()),
echoWithPrefix("Cleaning working directory \"$%s\" since stage is configured to clean working directory", workingDirectory())
).setTest(test("-d", workingDirectory()));
}
private String workingDirectory() {
return assignment.getWorkingDirectory().getPath();
}
private JobIdentifier getJobIdentifier() {
return assignment.getPlan().getIdentifier();
}
private EnvironmentVariableContext environmentVariableContext() {
JobPlan plan = assignment.getPlan();
EnvironmentVariableContext context = new EnvironmentVariableContext();
context.addAll(assignment.initialEnvironmentVariableContext());
context.setProperty("GO_TRIGGER_USER", assignment.getBuildApprover() , false);
getJobIdentifier().populateEnvironmentVariables(context);
assignment.materialRevisions().populateEnvironmentVariables(context, new File(workingDirectory()));
plan.applyTo(context);
return context;
}
}