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.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.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.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.ArtifactoryBuildInfoPropertyHelper; import org.jfrog.bamboo.builder.BuilderDependencyHelper; import org.jfrog.bamboo.context.AbstractBuildContext; import org.jfrog.bamboo.context.IvyBuildContext; import org.jfrog.bamboo.util.IvyPropertyHelper; import org.jfrog.bamboo.util.PluginProperties; import org.jfrog.bamboo.util.TaskUtils; import org.jfrog.build.api.BuildInfoConfigProperties; import java.io.File; import java.io.IOException; import java.util.Arrays; import java.util.List; import java.util.Map; /** * Invocation of the Ant/Ivy task. * * @author Tomer Cohen */ public class ArtifactoryIvyTask extends ArtifactoryTaskType { public static final String TASK_NAME = "artifactoryIvyTask"; public static final String EXECUTABLE_NAME = SystemUtils.IS_OS_WINDOWS ? "ant.bat" : "ant"; private static final Logger log = Logger.getLogger(ArtifactoryIvyTask.class); private static final String IVY_KEY = "system.builder.ivy."; private final ProcessService processService; private final EnvironmentVariableAccessor environmentVariableAccessor; private final CapabilityContext capabilityContext; private BuilderDependencyHelper dependencyHelper; private String ivyDependenciesDir = ""; private String buildInfoPropertiesFile = ""; private boolean activateBuildInfoRecording; public ArtifactoryIvyTask(final ProcessService processService, final EnvironmentVariableAccessor environmentVariableAccessor, final CapabilityContext capabilityContext, TestCollationService testCollationService) { super(testCollationService, environmentVariableAccessor); this.processService = processService; this.environmentVariableAccessor = environmentVariableAccessor; this.capabilityContext = capabilityContext; dependencyHelper = new BuilderDependencyHelper("artifactoryIvyBuilder"); 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); Map<String, String> combinedMap = Maps.newHashMap(); combinedMap.putAll(context.getConfigurationMap()); combinedMap.putAll(context.getBuildContext().getBuildDefinition().getCustomConfiguration()); IvyBuildContext ivyBuildContext = new IvyBuildContext(combinedMap); initEnvironmentVariables(ivyBuildContext); File rootDirectory = context.getRootDirectory(); long serverId = ivyBuildContext.getArtifactoryServerId(); try { ivyDependenciesDir = extractIvyDependencies(serverId, rootDirectory, ivyBuildContext); log.info(logger.addBuildLogEntry("Ivy dependency directory found at: " + ivyDependenciesDir)); } catch (IOException ioe) { ivyDependenciesDir = null; logger.addBuildLogEntry(new ErrorLogEntry( "Error occurred while preparing Artifactory Ivy Runner dependencies. Build Info support is " + "disabled: " + ioe.getMessage())); log.error("Error occurred while preparing Artifactory Ivy Runner dependencies. " + "Build Info support is disabled.", ioe); } if (ivyDependenciesDir == null) { String message = "Ivy dependency directory not found."; logger.addErrorLogEntry(message); log.error(message); } String executable = getExecutable(ivyBuildContext); if (StringUtils.isBlank(executable)) { log.error(logger.addErrorLogEntry("Cannot find ivy executable")); return TaskResultBuilder.newBuilder(context).failed().build(); } Map<String, String> globalEnv = environmentVariableAccessor.getEnvironment(); Map<String, String> environment = Maps.newHashMap(globalEnv); if (StringUtils.isNotBlank(ivyDependenciesDir)) { ArtifactoryBuildInfoPropertyHelper propertyHelper = new IvyPropertyHelper(); propertyHelper.init(buildParamsOverrideManager, context.getBuildContext()); buildInfoPropertiesFile = propertyHelper.createFileAndGetPath(ivyBuildContext, context.getBuildLogger(), environmentVariableAccessor.getEnvironment(context), globalEnv, artifactoryPluginVersion); if (StringUtils.isNotBlank(buildInfoPropertiesFile)) { activateBuildInfoRecording = true; environment.put(BuildInfoConfigProperties.PROP_PROPS_FILE, buildInfoPropertiesFile); } } List<String> command = Lists.newArrayList(executable); if (activateBuildInfoRecording) { command.add("-lib"); command.add(Commandline.quoteArgument(ivyDependenciesDir)); command.add("-listener"); command.add(Commandline.quoteArgument("org.jfrog.build.extractor.listener.ArtifactoryBuildListener")); TaskUtils.appendBuildInfoPropertiesArgument(command, buildInfoPropertiesFile); } String buildFile = ivyBuildContext.getBuildFile(); if (StringUtils.isNotBlank(buildFile)) { command.addAll(Arrays.asList("-f", buildFile)); } String targets = ivyBuildContext.getTargets(); if (StringUtils.isNotBlank(targets)) { String[] targetTokens = StringUtils.split(targets, ' '); command.addAll(Arrays.asList(targetTokens)); } String antOpts = ivyBuildContext.getAntOpts(); if (StringUtils.isNotBlank(antOpts)) { environment.put("ANT_OPTS", antOpts); } if (StringUtils.isNotBlank(ivyBuildContext.getEnvironmentVariables())) { environment.putAll(environmentVariableAccessor .splitEnvironmentAssignments(ivyBuildContext.getEnvironmentVariables(), false)); } String subDirectory = ivyBuildContext.getWorkingSubDirectory(); if (StringUtils.isNotBlank(subDirectory)) { rootDirectory = new File(rootDirectory, subDirectory); } // Override the JAVA_HOME according to the build configuration: String jdkPath = getConfiguredJdkPath(buildParamsOverrideManager, ivyBuildContext, capabilityContext); environment.put("JAVA_HOME", jdkPath); log.debug("Running Ant command: " + command.toString()); ExternalProcessBuilder processBuilder = new ExternalProcessBuilder().workingDirectory(rootDirectory).command(command) .env(environment); 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 TaskResultBuilder.newBuilder(context) .checkReturnCode(process).build(); } finally { context.getBuildContext().getBuildResult().addBuildErrors(errorLines.getErrorStringList()); } } /** * Extracts the Artifactory Ivy recorder and all the needed to dependencies * * @return Path of recorder and dependency jar folder if extraction succeeded. Null if not */ private String extractIvyDependencies(long artifactoryServerId, File rootDirectory, IvyBuildContext context) throws IOException { if (artifactoryServerId == -1) { return null; } return dependencyHelper.downloadDependenciesAndGetPath(rootDirectory, context, PluginProperties.getPluginProperty(PluginProperties.IVY_DEPENDENCY_FILENAME_KEY)); } public String getExecutable(AbstractBuildContext buildContext) throws TaskException { ReadOnlyCapabilitySet capabilitySet = capabilityContext.getCapabilitySet(); if (capabilitySet == null) { return null; } Capability capability = capabilitySet.getCapability(IVY_KEY + buildContext.getExecutable()); if (capability == null) { throw new TaskException("Ivy capability: " + buildContext.getExecutable() + " is not defined, please check job configuration"); } final String path = new StringBuilder(capability.getValue()) .append(File.separator) .append("bin") .append(File.separator) .append(EXECUTABLE_NAME) .toString(); if (!new File(path).exists()) { throw new TaskException("Executable '" + EXECUTABLE_NAME + "' does not exist at path '" + path + "'"); } return path; } }