package com.hubspot.blazar.util; import static com.hubspot.blazar.base.ModuleBuild.State.CANCELLED; import static com.hubspot.blazar.base.ModuleBuild.State.SKIPPED; import static com.hubspot.blazar.base.ModuleBuild.State.SUCCEEDED; import java.util.Set; import com.google.common.base.Optional; import com.google.inject.Inject; import com.google.inject.Singleton; import com.hubspot.blazar.base.GitInfo; import com.hubspot.blazar.base.ModuleBuild; import com.hubspot.blazar.base.RepositoryBuild; import com.hubspot.blazar.data.service.BranchService; import com.hubspot.blazar.data.service.ModuleBuildService; import com.hubspot.blazar.data.service.ModuleService; import com.ullink.slack.simpleslackapi.SlackAttachment; @Singleton public class SlackMessageBuildingUtils { private BranchService branchService; private ModuleService moduleService; private ModuleBuildService moduleBuildService; private BlazarUrlHelper blazarUrlHelper; @Inject public SlackMessageBuildingUtils(BranchService branchService, ModuleService moduleService, ModuleBuildService moduleBuildService, BlazarUrlHelper blazarUrlHelper) { this.branchService = branchService; this.moduleService = moduleService; this.moduleBuildService = moduleBuildService; this.blazarUrlHelper = blazarUrlHelper; } /** * This constructs a slack attachment to send along with a message so that users * can get a visual for the state of the build in addition to text. * * The information contained in the attachment is: * - Color: Red for failure, Green success, and Yellow for states with less clear Failure / Success connotations * - A link to the build history page for the build * - A list of the modules that failed or were "unstable". * */ public SlackAttachment buildSlackAttachment(RepositoryBuild build) { String title = buildTitle(build); SlackAttachment attachment = makeAttachmentJustWithTitle(title); attachment.setTitleLink(blazarUrlHelper.getBlazarUiLink(build)); attachment.setColor(getColor(build.getState())); if (build.getState().equals(RepositoryBuild.State.SUCCEEDED)) { return attachment; } attachment.setText("The failing modules were:"); Set<ModuleBuild> builtModules = moduleBuildService.getByRepositoryBuild(build.getId().get()); for (ModuleBuild b : builtModules) { ModuleBuild.State moduleBuildState = b.getState(); // SUCCEEDED & Skipped & CANCELLED are not states we highlight in slack attachment fields if (moduleBuildState == SUCCEEDED || moduleBuildState == CANCELLED || moduleBuildState == SKIPPED) { continue; } String moduleName = moduleService.get(b.getModuleId()).get().getName(); boolean repoBuildWasUnstable = build.getState() == RepositoryBuild.State.UNSTABLE; boolean repoBuildFailed = build.getState().isFailed(); if (repoBuildFailed && !repoBuildWasUnstable) { attachment.addField(moduleName, b.getState().name().toLowerCase(), true); continue; } Optional<ModuleBuild> maybePrevious = moduleBuildService.getPreviousBuild(b); if (maybePrevious.isPresent()) { String unstableMessage = String.format("Unstable, last attempted build was in branch build #%d", maybePrevious.get().getBuildNumber()); attachment.addField(moduleName, unstableMessage, false); } else { attachment.addField(moduleName, "This module has never built.", true); } } return attachment; } private String buildTitle(RepositoryBuild build) { GitInfo gitInfo = branchService.get(build.getBranchId()).get(); return String.format("Repository Build %s-%s#%d finished with state %s", gitInfo.getRepository(), gitInfo.getBranch(), build.getBuildNumber(), build.getState().toString().toLowerCase()); } private String getColor(RepositoryBuild.State state) { String color; switch (state) { case SUCCEEDED: color = "good"; break; case CANCELLED: color = "warning"; break; case UNSTABLE: color = "danger"; break; case FAILED: color = "danger"; break; default: color = "blue"; break; } return color; } private static SlackAttachment makeAttachmentJustWithTitle(String title) { return new SlackAttachment(title, title, "", ""); } }