package org.jfrog.bamboo.util.generic; import com.atlassian.bamboo.build.logger.BuildLogger; import com.atlassian.bamboo.util.BuildUtils; import com.atlassian.bamboo.utils.EscapeChars; import com.atlassian.bamboo.v2.build.BuildContext; import com.atlassian.bamboo.v2.build.trigger.DependencyTriggerReason; import com.atlassian.bamboo.v2.build.trigger.ManualBuildTriggerReason; import com.atlassian.bamboo.v2.build.trigger.TriggerReason; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Multimap; import com.google.common.collect.Sets; import org.apache.commons.io.FilenameUtils; import org.apache.commons.lang.StringUtils; import org.apache.log4j.Logger; import org.jfrog.bamboo.builder.BaseBuildInfoHelper; import org.jfrog.bamboo.configuration.BuildParamsOverrideManager; import org.jfrog.bamboo.context.GenericContext; import org.jfrog.bamboo.util.TaskUtils; import org.jfrog.build.api.*; import org.jfrog.build.api.builder.ArtifactBuilder; import org.jfrog.build.api.builder.BuildInfoBuilder; import org.jfrog.build.api.builder.ModuleBuilder; import org.jfrog.build.api.util.FileChecksumCalculator; import org.jfrog.build.client.DeployDetails; import org.jfrog.build.extractor.BuildInfoExtractorUtils; import org.jfrog.build.extractor.clientConfiguration.ClientProperties; import org.jfrog.build.extractor.clientConfiguration.IncludeExcludePatterns; import org.jfrog.build.extractor.clientConfiguration.PatternMatcher; import org.jfrog.build.extractor.clientConfiguration.util.PublishedItemsHelper; import org.joda.time.DateTime; import org.joda.time.Interval; import java.io.File; import java.io.IOException; import java.security.NoSuchAlgorithmException; import java.util.*; /** * @author Tomer Cohen */ public class GenericBuildInfoHelper extends BaseBuildInfoHelper { private static final Logger log = Logger.getLogger(GenericBuildInfoHelper.class); private final Map<String, String> env; private final String vcsRevision; private final String vcsUrl; public GenericBuildInfoHelper(Map<String, String> env, String vcsRevision, String vcsUrl) { this.env = env; this.vcsRevision = vcsRevision; this.vcsUrl = vcsUrl; } public Build extractBuildInfo(BuildContext buildContext, BuildLogger buildLogger, GenericContext context, String username) { String url = determineBambooBaseUrl(); StringBuilder summaryUrl = new StringBuilder(url); if (!url.endsWith("/")) { summaryUrl.append("/"); } String buildUrl = summaryUrl.append("browse/"). append(EscapeChars.forFormSubmission(buildContext.getBuildResultKey())).toString(); DateTime start = new DateTime(buildContext.getBuildResult().getCustomBuildData().get("buildTimeStamp")); DateTime end = new DateTime(); long duration = -1; if (start.isBefore(end)) { duration = new Interval(start, end).toDurationMillis(); } else { log.warn(buildLogger.addErrorLogEntry("Agent machine time is lower than the server machine time, please synchronize them.")); } BuildInfoBuilder builder = new BuildInfoBuilder(buildContext.getPlanName()) .number(String.valueOf(buildContext.getBuildNumber())).type(BuildType.GENERIC) .agent(new Agent("Bamboo", BuildUtils.getVersionAndBuild())).artifactoryPrincipal(username) .startedDate(new Date()).durationMillis(duration).url(buildUrl); if (StringUtils.isNotBlank(vcsRevision)) { builder.vcsRevision(vcsRevision); } if (StringUtils.isNotBlank(vcsUrl)) { builder.vcsUrl(vcsUrl); } String principal = getTriggeringUserNameRecursively(buildContext); if (StringUtils.isBlank(principal)) { principal = "auto"; } builder.principal(principal); if (context.isIncludeEnvVars()) { Map<String, String> bambooProps = filterAndGetGlobalVariables(); bambooProps.putAll(env); bambooProps = TaskUtils.getEscapedEnvMap(bambooProps); IncludeExcludePatterns patterns = new IncludeExcludePatterns(context.getEnvVarsIncludePatterns(), context.getEnvVarsExcludePatterns()); for (Map.Entry<String, String> prop : bambooProps.entrySet()) { String varKey = prop.getKey(); if (PatternMatcher.pathConflicts(varKey, patterns)) { continue; } builder.addProperty(BuildInfoProperties.BUILD_INFO_ENVIRONMENT_PREFIX + varKey, prop.getValue()); } } return builder.build(); } private List<Artifact> convertDeployDetailsToArtifacts(Set<DeployDetails> details) { List<Artifact> result = Lists.newArrayList(); for (DeployDetails detail : details) { String ext = FilenameUtils.getExtension(detail.getFile().getName()); Artifact artifact = new ArtifactBuilder(detail.getFile().getName()).md5(detail.getMd5()) .sha1(detail.getSha1()).type(ext).build(); result.add(artifact); } return result; } private String getTriggeringUserNameRecursively(BuildContext context) { String principal = null; TriggerReason triggerReason = context.getTriggerReason(); if (triggerReason instanceof ManualBuildTriggerReason) { principal = ((ManualBuildTriggerReason) triggerReason).getUserName(); if (StringUtils.isBlank(principal)) { BuildContext parentContext = context.getParentBuildContext(); if (parentContext != null) { principal = getTriggeringUserNameRecursively(parentContext); } } } return principal; } public Set<DeployDetails> createDeployDetailsAndAddToBuildInfo(Build build, Multimap<String, File> filesMap, BuildContext buildContext, GenericContext genericContext) throws IOException, NoSuchAlgorithmException { Set<DeployDetails> details = Sets.newHashSet(); Map<String, String> dynamicPropertyMap = getDynamicPropertyMap(build); String repoKey = overrideParam(genericContext.getRepoKey(), BuildParamsOverrideManager.OVERRIDE_ARTIFACTORY_DEPLOY_REPO); for (Map.Entry<String, File> entry : filesMap.entries()) { details.addAll(buildDeployDetailsFromFileSet(entry, repoKey, dynamicPropertyMap)); } List<Artifact> artifacts = convertDeployDetailsToArtifacts(details); ModuleBuilder moduleBuilder = new ModuleBuilder().id(buildContext.getPlanName() + ":" + buildContext.getBuildNumber()) .artifacts(artifacts); build.setModules(Lists.newArrayList(moduleBuilder.build())); return details; } private Map<String, String> getDynamicPropertyMap(Build build) { Map<String, String> filteredPropertyMap = new HashMap<>(); if (build.getProperties() != null) { for (Map.Entry<Object, Object> entry : build.getProperties().entrySet()) { String key = entry.getKey().toString(); if (StringUtils.startsWith(key, ClientProperties.PROP_DEPLOY_PARAM_PROP_PREFIX)) { filteredPropertyMap.put( StringUtils.removeStart(key, ClientProperties.PROP_DEPLOY_PARAM_PROP_PREFIX), (String) entry.getValue()); } } } return filteredPropertyMap; } private Set<DeployDetails> buildDeployDetailsFromFileSet(Map.Entry<String, File> fileEntry, String targetRepository, Map<String, String> propertyMap) throws IOException, NoSuchAlgorithmException { Set<DeployDetails> result = Sets.newHashSet(); String targetPath = fileEntry.getKey(); File artifactFile = fileEntry.getValue(); String path = PublishedItemsHelper.calculateTargetPath(targetPath, artifactFile); path = StringUtils.replace(path, "//", "/"); Map<String, String> checksums = FileChecksumCalculator.calculateChecksums(artifactFile, "SHA1", "MD5"); DeployDetails.Builder deployDetails = new DeployDetails.Builder().file(artifactFile).md5(checksums.get("MD5")) .sha1(checksums.get("SHA1")).targetRepository(targetRepository).artifactPath(path); addCommonProperties(deployDetails); deployDetails.addProperties(propertyMap); result.add(deployDetails.build()); return result; } private void addCommonProperties(DeployDetails.Builder details) { details.addProperty(BuildInfoFields.BUILD_NAME, context.getPlanName()); details.addProperty(BuildInfoFields.BUILD_NUMBER, String.valueOf(context.getBuildNumber())); if (StringUtils.isNotBlank(vcsRevision)) { details.addProperty(BuildInfoFields.VCS_REVISION, vcsRevision); } if (StringUtils.isNotBlank(vcsUrl)) { details.addProperty(BuildInfoFields.VCS_URL, vcsUrl); } String buildTimeStampVal = context.getBuildResult().getCustomBuildData().get("buildTimeStamp"); long buildTimeStamp = System.currentTimeMillis(); if (StringUtils.isNotBlank(buildTimeStampVal)) { buildTimeStamp = new DateTime(buildTimeStampVal).getMillis(); } String buildTimeStampString = String.valueOf(buildTimeStamp); details.addProperty(BuildInfoFields.BUILD_TIMESTAMP, buildTimeStampString); addBuildParentProperties(details, context.getTriggerReason()); } private void addBuildParentProperties(DeployDetails.Builder details, TriggerReason triggerReason) { if (triggerReason instanceof DependencyTriggerReason) { String triggeringBuildResultKey = ((DependencyTriggerReason) triggerReason).getTriggeringBuildResultKey(); if (StringUtils.isNotBlank(triggeringBuildResultKey) && (StringUtils.split(triggeringBuildResultKey, "-").length == 3)) { String triggeringBuildKey = triggeringBuildResultKey.substring(0, triggeringBuildResultKey.lastIndexOf("-")); String triggeringBuildNumber = triggeringBuildResultKey.substring(triggeringBuildResultKey.lastIndexOf("-") + 1); String parentBuildName = getBuildName(triggeringBuildKey); if (StringUtils.isBlank(parentBuildName)) { log.error("Received a null build parent name."); } details.addProperty(BuildInfoFields.BUILD_PARENT_NAME, parentBuildName); details.addProperty(BuildInfoFields.BUILD_PARENT_NUMBER, triggeringBuildNumber); } } } }