package org.jfrog.bamboo.task;
import com.atlassian.bamboo.build.ErrorLogEntry;
import com.atlassian.bamboo.build.logger.BuildLogger;
import com.atlassian.bamboo.build.logger.interceptors.ErrorMemorisingInterceptor;
import com.atlassian.bamboo.build.test.TestCollationService;
import com.atlassian.bamboo.configuration.AdministrationConfiguration;
import com.atlassian.bamboo.process.EnvironmentVariableAccessor;
import com.atlassian.bamboo.process.ExternalProcessBuilder;
import com.atlassian.bamboo.process.ProcessService;
import com.atlassian.bamboo.task.TaskContext;
import com.atlassian.bamboo.task.TaskException;
import com.atlassian.bamboo.task.TaskResult;
import com.atlassian.bamboo.task.TaskResultBuilder;
import com.atlassian.bamboo.v2.build.BuildContext;
import com.atlassian.bamboo.v2.build.agent.capability.Capability;
import com.atlassian.bamboo.v2.build.agent.capability.CapabilityContext;
import com.atlassian.bamboo.v2.build.agent.capability.ReadOnlyCapabilitySet;
import com.atlassian.spring.container.ContainerManager;
import com.atlassian.utils.process.ExternalProcess;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.SystemUtils;
import org.apache.log4j.Logger;
import org.apache.tools.ant.types.Commandline;
import org.jetbrains.annotations.NotNull;
import org.jfrog.bamboo.builder.BuilderDependencyHelper;
import org.jfrog.bamboo.builder.GradleInitScriptHelper;
import org.jfrog.bamboo.context.AbstractBuildContext;
import org.jfrog.bamboo.context.GradleBuildContext;
import org.jfrog.bamboo.util.ConfigurationPathHolder;
import org.jfrog.bamboo.util.PluginProperties;
import org.jfrog.bamboo.util.TaskUtils;
import org.jfrog.gradle.plugin.artifactory.task.BuildInfoBaseTask;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.jar.JarFile;
import java.util.zip.ZipEntry;
/**
* Invocation of the Gradle build.
*
* @author Tomer Cohen
*/
public class ArtifactoryGradleTask extends ArtifactoryTaskType {
public static final String TASK_NAME = "artifactoryGradleTask";
public static final String EXECUTABLE_NAME = SystemUtils.IS_OS_WINDOWS ? "gradle.bat" : "gradle";
public static final String EXECUTABLE_WRAPPER_NAME = SystemUtils.IS_OS_WINDOWS ? "./gradlew.bat" : "./gradlew";
private static final Logger log = Logger.getLogger(ArtifactoryGradleTask.class);
private static final String GRADLE_KEY = "system.builder.gradle.";
private final ProcessService processService;
private final CapabilityContext capabilityContext;
private BuilderDependencyHelper dependencyHelper;
private String gradleDependenciesDir = null;
private AdministrationConfiguration administrationConfiguration;
public ArtifactoryGradleTask(final ProcessService processService,
final EnvironmentVariableAccessor environmentVariableAccessor, final CapabilityContext capabilityContext,
TestCollationService testCollationService) {
super(testCollationService, environmentVariableAccessor);
this.processService = processService;
this.capabilityContext = capabilityContext;
dependencyHelper = new BuilderDependencyHelper("artifactoryGradleBuilder");
ContainerManager.autowireComponent(dependencyHelper);
}
@Override
@NotNull
public TaskResult execute(@NotNull TaskContext context) throws TaskException {
BuildLogger logger = getBuildLogger(context);
String artifactoryPluginVersion = getArtifactoryVersion();
logger.addBuildLogEntry("Bamboo Artifactory Plugin version: " + artifactoryPluginVersion);
final ErrorMemorisingInterceptor errorLines = new ErrorMemorisingInterceptor();
logger.getInterceptorStack().add(errorLines);
GradleBuildContext gradleBuildContext = createBuildContext(context);
initEnvironmentVariables(gradleBuildContext);
long serverId = gradleBuildContext.getArtifactoryServerId();
File rootDirectory = context.getRootDirectory();
try {
gradleDependenciesDir = extractGradleDependencies(serverId, rootDirectory, gradleBuildContext);
} catch (IOException e) {
gradleDependenciesDir = null;
logger.addBuildLogEntry(new ErrorLogEntry(
"Error occurred while preparing Artifactory Gradle Runner dependencies. Build Info support is " +
"disabled: " + e.getMessage()));
log.error("Error occurred while preparing Artifactory Gradle Runner dependencies. " +
"Build Info support is disabled.", e);
}
String gradleCommandLine = getExecutable(gradleBuildContext);
if (StringUtils.isBlank(gradleCommandLine)) {
log.error(logger.addErrorLogEntry("Gradle executable is not defined!"));
return TaskResultBuilder.newBuilder(context).failed().build();
}
List<String> command = Lists.newArrayList(gradleCommandLine);
String switches = gradleBuildContext.getSwitches();
if (StringUtils.isNotBlank(switches)) {
String[] switchTokens = StringUtils.split(switches, ' ');
command.addAll(Arrays.asList(switchTokens));
}
String tasks = gradleBuildContext.getTasks();
if (gradleBuildContext.releaseManagementContext.isActivateReleaseManagement()) {
String altTasks = gradleBuildContext.releaseManagementContext.getAlternativeTasks();
if (StringUtils.isNotBlank(altTasks)) {
tasks = altTasks;
}
}
if (StringUtils.isNotBlank(tasks)) {
String[] taskTokens = StringUtils.split(tasks, ' ');
command.addAll(Arrays.asList(taskTokens));
}
ConfigurationPathHolder pathHolder = getGradleInitScriptFile(context, gradleBuildContext, artifactoryPluginVersion);
if (pathHolder != null) {
if (!gradleBuildContext.useArtifactoryGradlePlugin()) {
command.add("-I");
command.add(Commandline.quoteArgument(pathHolder.getInitScriptPath()));
}
TaskUtils.appendBuildInfoPropertiesArgument(command, pathHolder.getClientConfPath());
command.add(BuildInfoBaseTask.BUILD_INFO_TASK_NAME);
}
String subDirectory = gradleBuildContext.getBuildScript();
if (StringUtils.isNotBlank(subDirectory)) {
rootDirectory = new File(rootDirectory, subDirectory);
}
// Override the JAVA_HOME according to the build configuration:
String jdkPath = getConfiguredJdkPath(buildParamsOverrideManager, gradleBuildContext, capabilityContext);
environmentVariables.put("JAVA_HOME", jdkPath);
log.debug("Running Gradle command: " + command.toString());
ExternalProcessBuilder processBuilder =
new ExternalProcessBuilder().workingDirectory(rootDirectory).command(command).env(environmentVariables);
try {
ExternalProcess process = processService.createExternalProcess(context, processBuilder);
process.execute();
if (process.getHandler() != null && !process.getHandler().succeeded()) {
String externalProcessOutput = getErrorMessage(process);
logger.addBuildLogEntry(externalProcessOutput);
log.debug("Process command error: " + externalProcessOutput);
}
return collectTestResults(gradleBuildContext, context, process);
} finally {
context.getBuildContext().getBuildResult().addBuildErrors(errorLines.getErrorStringList());
}
}
private GradleBuildContext createBuildContext(TaskContext context) {
Map<String, String> combinedMap = Maps.newHashMap();
combinedMap.putAll(context.getConfigurationMap());
BuildContext parentBuildContext = context.getBuildContext().getParentBuildContext();
if (parentBuildContext != null) {
Map<String, String> customBuildData = parentBuildContext.getBuildResult().getCustomBuildData();
combinedMap.putAll(customBuildData);
}
return new GradleBuildContext(combinedMap);
}
private ConfigurationPathHolder getGradleInitScriptFile(TaskContext taskContext, GradleBuildContext buildContext,
String artifactoryPluginVersion) {
File gradleJarFile = new File(gradleDependenciesDir, PluginProperties
.getPluginProperty(PluginProperties.GRADLE_DEPENDENCY_FILENAME_KEY));
if (!gradleJarFile.exists()) {
log.warn("Unable to locate the Gradle extractor. Build-info task will not be added.");
return null;
}
InputStream initScriptStream = null;
JarFile gradleJar = null;
try {
gradleJar = new JarFile(gradleJarFile);
ZipEntry initScriptEntry = gradleJar.getEntry("initscripttemplate.gradle");
if (initScriptEntry == null) {
log.warn("Unable to locate the Gradle init script. Build-info task will not be added.");
return null;
}
initScriptStream = gradleJar.getInputStream(initScriptEntry);
if (initScriptStream == null) {
log.warn("Unable to locate the gradle init script template. Build-info task will not be added.");
return null;
}
String scriptTemplate = IOUtils.toString(initScriptStream);
GradleInitScriptHelper initScriptHelper = new GradleInitScriptHelper();
initScriptHelper.init(buildParamsOverrideManager, taskContext.getBuildContext());
initScriptHelper.setAdministrationConfiguration(administrationConfiguration);
return initScriptHelper
.createAndGetGradleInitScriptPath(gradleDependenciesDir, buildContext, taskContext.getBuildLogger(),
scriptTemplate, environmentVariableAccessor.getEnvironment(taskContext),
environmentVariableAccessor.getEnvironment(), artifactoryPluginVersion);
} catch (IOException e) {
log.warn("Unable to read from the Gradle extractor jar. Build-info task will not be added: " +
e.getMessage());
return null;
} finally {
IOUtils.closeQuietly(initScriptStream);
try {
if (gradleJar != null) {
gradleJar.close();
}
} catch (IOException e) {
log.warn("Unable to close the Gradle extractor jar: " + e.getMessage());
}
}
}
public String getExecutable(AbstractBuildContext buildContext) throws TaskException {
if ((buildContext instanceof GradleBuildContext) && ((GradleBuildContext) buildContext).isUseGradleWrapper()) {
String gradleWrapperLocation = ((GradleBuildContext) buildContext).getGradleWrapperLocation();
if (StringUtils.isNotBlank(gradleWrapperLocation)) {
return gradleWrapperLocation;
}
return EXECUTABLE_WRAPPER_NAME;
} else {
ReadOnlyCapabilitySet capabilitySet = capabilityContext.getCapabilitySet();
if (capabilitySet == null) {
return null;
}
Capability capability = capabilitySet.getCapability(GRADLE_KEY + buildContext.getExecutable());
if (capability == null) {
throw new TaskException(
"Gradle capability: " + buildContext.getExecutable() + " is not defined, please check " +
"job configuration");
}
String path = capability.getValue() + File.separator + "bin" + File.separator + EXECUTABLE_NAME;
if (!new File(path).exists()) {
throw new TaskException("Executable '" + EXECUTABLE_NAME + "' does not exist at path '" + path + "'");
}
return path;
}
}
/**
* Extracts the Artifactory Gradle recorder and all the needed to dependencies
*
* @return Path of recorder and dependency jar folder if extraction succeeded. Null if not
*/
private String extractGradleDependencies(long artifactoryServerId, File rootDirectory,
GradleBuildContext context) throws IOException {
if (artifactoryServerId == -1) {
return null;
}
return dependencyHelper.downloadDependenciesAndGetPath(rootDirectory, context,
PluginProperties.getPluginProperty(PluginProperties.GRADLE_DEPENDENCY_FILENAME_KEY));
}
public void setAdministrationConfiguration(AdministrationConfiguration administrationConfiguration) {
this.administrationConfiguration = administrationConfiguration;
}
}