package org.jfrog.bamboo.task;
import com.atlassian.bamboo.build.logger.BuildLogger;
import com.atlassian.bamboo.builder.BuildState;
import com.atlassian.bamboo.process.EnvironmentVariableAccessor;
import com.atlassian.bamboo.repository.RepositoryException;
import com.atlassian.bamboo.task.*;
import com.atlassian.bamboo.v2.build.BuildContext;
import com.atlassian.bamboo.v2.build.CurrentBuildResult;
import com.atlassian.bamboo.variable.CustomVariableContext;
import com.atlassian.plugin.Plugin;
import com.atlassian.plugin.PluginAccessor;
import com.atlassian.spring.container.ContainerManager;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.jetbrains.annotations.NotNull;
import org.jfrog.bamboo.admin.ServerConfig;
import org.jfrog.bamboo.admin.ServerConfigManager;
import org.jfrog.bamboo.configuration.BuildParamsOverrideManager;
import org.jfrog.bamboo.context.GenericContext;
import org.jfrog.bamboo.util.BuildInfoLog;
import org.jfrog.bamboo.util.ConstantValues;
import org.jfrog.bamboo.util.generic.GenericBuildInfoHelper;
import org.jfrog.bamboo.util.generic.GenericData;
import org.jfrog.bamboo.util.version.VcsHelper;
import org.jfrog.build.api.Build;
import org.jfrog.build.client.DeployDetails;
import org.jfrog.build.extractor.BuildInfoExtractorUtils;
import org.jfrog.build.extractor.clientConfiguration.client.ArtifactoryBuildInfoClient;
import org.jfrog.build.extractor.clientConfiguration.util.PublishedItemsHelper;
import java.io.File;
import java.io.IOException;
import java.security.NoSuchAlgorithmException;
import java.util.Map;
import java.util.Set;
import static org.jfrog.bamboo.util.ConstantValues.BUILD_RESULT_COLLECTION_ACTIVATED_PARAM;
import static org.jfrog.bamboo.util.ConstantValues.BUILD_RESULT_SELECTED_SERVER_PARAM;
/**
* @author Tomer Cohen
*/
public class ArtifactoryGenericDeployTask implements TaskType {
public static final String TASK_NAME = "artifactoryGenericTask";
private static final Logger log = Logger.getLogger(ArtifactoryGenericDeployTask.class);
private final EnvironmentVariableAccessor environmentVariableAccessor;
private PluginAccessor pluginAccessor;
private BuildLogger logger;
private GenericBuildInfoHelper buildInfoHelper;
private CustomVariableContext customVariableContext;
private BuildParamsOverrideManager buildParamsOverrideManager;
public ArtifactoryGenericDeployTask(EnvironmentVariableAccessor environmentVariableAccessor) {
this.environmentVariableAccessor = environmentVariableAccessor;
ContainerManager.autowireComponent(this);
this.buildParamsOverrideManager = new BuildParamsOverrideManager(customVariableContext);
}
@SuppressWarnings("unused")
public void setPluginAccessor(PluginAccessor pluginAccessor) {
this.pluginAccessor = pluginAccessor;
}
public void setCustomVariableContext(CustomVariableContext customVariableContext) {
this.customVariableContext = customVariableContext;
}
@Override
@NotNull
public TaskResult execute(@NotNull TaskContext taskContext) throws TaskException {
logger = taskContext.getBuildLogger();
logger.addBuildLogEntry("Bamboo Artifactory Plugin version: " + getArtifactoryVersion());
if (!taskContext.isFinalising()) {
log.error(logger.addErrorLogEntry("Artifactory Generic Deploy Task must run as a final Task!"));
return TaskResultBuilder.newBuilder(taskContext).failed().build();
}
BuildContext context = taskContext.getBuildContext();
CurrentBuildResult result = context.getBuildResult();
// if build wasn't a success don't do anything
if (result.getBuildState().equals(BuildState.FAILED)) {
log.error(logger.addErrorLogEntry("Build failed, not deploying to Artifactory."));
return TaskResultBuilder.newBuilder(taskContext).success().build();
}
GenericContext genericContext = new GenericContext(taskContext.getConfigurationMap());
Map<String, String> env = Maps.newHashMap();
env.putAll(environmentVariableAccessor.getEnvironment(taskContext));
env.putAll(environmentVariableAccessor.getEnvironment());
String vcsRevision = VcsHelper.getRevisionKey(context);
if (StringUtils.isBlank(vcsRevision)) {
vcsRevision = "";
}
String vcsUrl = VcsHelper.getVcsUrl(context);
if (StringUtils.isBlank(vcsUrl)) {
vcsUrl = "";
}
buildInfoHelper = new GenericBuildInfoHelper(env, vcsRevision, vcsUrl);
buildInfoHelper.init(buildParamsOverrideManager, context);
try {
File sourceCodeDirectory = getWorkingDirectory(context, taskContext);
if (sourceCodeDirectory == null) {
log.error(logger.addErrorLogEntry("No build directory found!"));
return TaskResultBuilder.newBuilder(taskContext).success().build();
}
Multimap<String, File> filesMap = buildTargetPathToFiles(sourceCodeDirectory, genericContext);
deploy(filesMap, genericContext, taskContext);
} catch (Exception e) {
String message = "Exception occurred while executing task";
logger.addErrorLogEntry(message, e);
log.error(message, e);
return TaskResultBuilder.newBuilder(taskContext).failedWithError().build();
}
Map<String, String> customBuildData = result.getCustomBuildData();
if (genericContext.isPublishBuildInfo() && !customBuildData.containsKey(BUILD_RESULT_COLLECTION_ACTIVATED_PARAM)) {
customBuildData.put(BUILD_RESULT_COLLECTION_ACTIVATED_PARAM, "true");
}
return TaskResultBuilder.newBuilder(taskContext).success().build();
}
private File getWorkingDirectory(BuildContext context, TaskContext taskContext) throws RepositoryException {
File checkoutDir = VcsHelper.getCheckoutDirectory(context);
if (checkoutDir != null) {
return checkoutDir;
} else {
return taskContext.getWorkingDirectory();
}
}
private Multimap<String, File> buildTargetPathToFiles(File directory, GenericContext context)
throws IOException {
Multimap<String, File> result = HashMultimap.create();
String deployPattern = context.getDeployPattern();
deployPattern = StringUtils.replace(deployPattern, "\r\n", "\n");
deployPattern = StringUtils.replace(deployPattern, ",", "\n");
// Collect all published items pair in the form of 'pattern -> targetPath'
Multimap<String, String> pairs = PublishedItemsHelper.getPublishedItemsPatternPairs(deployPattern);
if (pairs.isEmpty()) {
return result;
}
// Collect all found items and put them into the result in the form of 'targetPath -> File'
for (Map.Entry<String, String> entry : pairs.entries()) {
Multimap<String, File> filesMap = PublishedItemsHelper.buildPublishingData(directory, entry.getKey(),
entry.getValue());
if (filesMap != null) {
log.info(logger.addBuildLogEntry(
"For pattern: " + entry.getKey() + " " + filesMap.size() + " artifacts were found"));
result.putAll(filesMap);
} else {
log.warn(logger.addBuildLogEntry("For pattern: " + entry.getKey() + " no artifacts were found"));
}
}
return result;
}
private void deploy(Multimap<String, File> filesMap, GenericContext context, TaskContext taskContext) throws IOException, NoSuchAlgorithmException {
ServerConfigManager serverConfigManager = ServerConfigManager.getInstance();
ServerConfig serverConfig = serverConfigManager.getServerConfigById(context.getSelectedServerId());
if (serverConfig == null) {
throw new IllegalArgumentException("Could not find Artifactpry server. Please check the Artifactory server in the task configuration.");
}
String username = buildInfoHelper.overrideParam(serverConfigManager.substituteVariables(context.getUsername()),
BuildParamsOverrideManager.OVERRIDE_ARTIFACTORY_DEPLOYER_USERNAME);
if (StringUtils.isBlank(username)) {
username = serverConfigManager.substituteVariables(serverConfig.getUsername());
}
String password = buildInfoHelper.overrideParam(serverConfigManager.substituteVariables(context.getPassword()),
BuildParamsOverrideManager.OVERRIDE_ARTIFACTORY_DEPLOYER_PASSWORD);
if (StringUtils.isBlank(password)) {
password = serverConfigManager.substituteVariables(serverConfig.getPassword());
}
String serverUrl = serverConfigManager.substituteVariables(serverConfig.getUrl());
org.jfrog.build.api.util.Log bambooBuildInfoLog = new BuildInfoLog(ArtifactoryGenericDeployTask.log, taskContext.getBuildLogger());
ArtifactoryBuildInfoClient client =
new ArtifactoryBuildInfoClient(serverUrl, username, password, bambooBuildInfoLog);
try {
BuildContext buildContext = taskContext.getBuildContext();
Build build = buildInfoHelper.extractBuildInfo(buildContext, taskContext.getBuildLogger(), context, username);
Set<DeployDetails> details = buildInfoHelper.createDeployDetailsAndAddToBuildInfo(build, filesMap, buildContext, context);
/**
* Look for dependencies from the Generic resolve task, if exists!
* */
getDependenciesFromContext(taskContext, build);
for (DeployDetails detail : details) {
StringBuilder deploymentPathBuilder = new StringBuilder(serverUrl);
deploymentPathBuilder.append("/").append(detail.getTargetRepository());
if (!detail.getArtifactPath().startsWith("/")) {
deploymentPathBuilder.append("/");
}
deploymentPathBuilder.append(detail.getArtifactPath());
client.deployArtifact(detail);
}
if (context.isPublishBuildInfo()) {
client.sendBuildInfo(build);
buildContext.getBuildResult().getCustomBuildData().put(BUILD_RESULT_SELECTED_SERVER_PARAM, serverUrl);
}
} finally {
client.close();
}
}
private void getDependenciesFromContext(TaskContext taskContext, Build build) throws IOException {
String genericJson = taskContext.getBuildContext().getParentBuildContext().getBuildResult().
getCustomBuildData().get("genericJson");
if (StringUtils.isNotBlank(genericJson)) {
GenericData genericData = BuildInfoExtractorUtils.jsonStringToGeneric(genericJson, GenericData.class);
//Assumption: There is only one module for Generic
build.getModules().get(0).setDependencies(genericData.getDependencies());
build.setBuildDependencies(genericData.getBuildDependencies());
}
}
public String getArtifactoryVersion() {
Plugin plugin = pluginAccessor.getPlugin(ConstantValues.ARTIFACTORY_PLUGIN_KEY);
if (plugin != null) {
return plugin.getPluginInformation().getVersion();
}
return StringUtils.EMPTY;
}
}