diff --git a/README.markdown b/README.markdown index 4a579d5..b5a8287 100644 --- a/README.markdown +++ b/README.markdown @@ -38,6 +38,7 @@ Edit the main config file, usually `.BuildServer/config/main-config.xml` and add #general https://hooks.slack.com/services/YYYYYY/XXXXXXX/ZZZZZZZZZZZZ http://build.tapadoo.com/img/icons/TeamCity32.png + http://build.tapadoo.com/viewLog.html?buildTypeId=XXXXXXX&buildId=lastFinished ... ... @@ -64,6 +65,17 @@ Edit the plugin specific xml config, `plugin-settings.xml` probably somewhere in ``` +Alternatively, you may also override the alert selections that were specified in main-config.xml: + +``` + + + #blah + http://host/somelogo.png + + +``` + #Note on TeamCity version support I'm still using **TeamCity 7.1** , but a few tests on the free version of TeamCity 8 went fine, and it seems to work there also. Users have reported it working on version 9 also. @@ -72,9 +84,10 @@ I'm still using **TeamCity 7.1** , but a few tests on the free version of TeamCi * all xml config - needs web ui extensions for updating settings from GUI. Considering it. * channel can be changed per-project either by environmental variable (SLACK_CHANNEL (env var may be broken)) or by changing the project specific xml in the data directory. This could also use web ui extension UI for editing. -* All or nothing notifications. By default, all builds are posted. It can be disabled per project, but not currently by build config. + +* Project-level notification selection only. Notification settings per individual build configuration are not supported. # License -MIT License. \ No newline at end of file +MIT License. diff --git a/build.gradle b/build.gradle index 8c26794..17cf058 100644 --- a/build.gradle +++ b/build.gradle @@ -7,7 +7,7 @@ apply plugin: 'java' sourceCompatibility = 1.6 -version = '2.4.0' +version = '2.5.0' configurations { deploy diff --git a/src/main/java/com/tapadoo/slacknotifier/SlackConfigProcessor.java b/src/main/java/com/tapadoo/slacknotifier/SlackConfigProcessor.java index 41445d6..c02cce8 100644 --- a/src/main/java/com/tapadoo/slacknotifier/SlackConfigProcessor.java +++ b/src/main/java/com/tapadoo/slacknotifier/SlackConfigProcessor.java @@ -13,6 +13,7 @@ public class SlackConfigProcessor implements MainConfigProcessor { public static final String PREF_KEY_SLACK_DEF_CHANNEL = "slackDefaultChannel"; public static final String PREF_KEY_SLACK_POSTURL = "slackPostUrl"; public static final String PREF_KEY_SLACK_LOGOURL = "slackLogoUrl"; + public static final String PREF_KEY_BUILD_FAILED_PERMALINK = "buildFailedPermalink"; private static final java.lang.String PREF_CHILD_ELEMENT = "slackNotifier"; @@ -23,6 +24,7 @@ public class SlackConfigProcessor implements MainConfigProcessor { private String defaultChannel = "#general"; private String postUrl; private String logoUrl; + private String buildFailedPermalink; private boolean postSuccessful = true ; private boolean postStarted = false ; @@ -61,6 +63,14 @@ public void setDefaultChannel(String defaultChannel) { this.defaultChannel = defaultChannel; } + public String getBuildFailedPermalink(){ + return buildFailedPermalink; + } + + public void setBuildFailedPermalink(String permalink){ + this.buildFailedPermalink = permalink; + } + public String getPostUrl() { return postUrl; } @@ -89,6 +99,7 @@ public void readFrom(org.jdom.Element element) { defaultChannel = mainConfigElement.getChildText(PREF_KEY_SLACK_DEF_CHANNEL); postUrl = mainConfigElement.getChildText(PREF_KEY_SLACK_POSTURL); logoUrl = mainConfigElement.getChildText(PREF_KEY_SLACK_LOGOURL); + buildFailedPermalink = mainConfigElement.getChildText(PREF_KEY_BUILD_FAILED_PERMALINK); Attribute postSuccessfulAttr = mainConfigElement.getAttribute(ATTR_NAME_POST_SUCCESSFUL); Attribute postStartedAttr = mainConfigElement.getAttribute(ATTR_NAME_POST_STARTED); @@ -135,12 +146,14 @@ public void writeTo(org.jdom.Element element) { Element defChannelElement = new Element(PREF_KEY_SLACK_DEF_CHANNEL); Element postUrlElement = new Element(PREF_KEY_SLACK_POSTURL); Element logoUrlElement = new Element(PREF_KEY_SLACK_LOGOURL); + Element buildFailedPermalinkElement = new Element(PREF_KEY_BUILD_FAILED_PERMALINK); defChannelElement.setText(defaultChannel); mainConfigElement.addContent(defChannelElement); mainConfigElement.addContent(postUrlElement); mainConfigElement.addContent(logoUrlElement); + mainConfigElement.addContent(buildFailedPermalinkElement); element.addContent(mainConfigElement); diff --git a/src/main/java/com/tapadoo/slacknotifier/SlackProjectSettings.java b/src/main/java/com/tapadoo/slacknotifier/SlackProjectSettings.java index 609878e..4056638 100644 --- a/src/main/java/com/tapadoo/slacknotifier/SlackProjectSettings.java +++ b/src/main/java/com/tapadoo/slacknotifier/SlackProjectSettings.java @@ -13,17 +13,25 @@ public class SlackProjectSettings implements ProjectSettings { public static final String ELEMENT_LOGO_URL = "logoUrl"; public static final String ATTR_ENABLED = "enabled"; public static final String ELEMENT_CHANNEL = "channel"; + + private static final java.lang.String ATTR_NAME_POST_SUCCESSFUL = "postSuccessful"; + private static final java.lang.String ATTR_NAME_POST_STARTED = "postStarted"; + private static final java.lang.String ATTR_NAME_POST_FAILED = "postFailed"; + private String projectId; private String channel; private String logoUrl; - private boolean enabled = true ; + + private boolean enabled = true; + private Boolean postSuccessful = null; + private Boolean postStarted = null; + private Boolean postFailed = null; public SlackProjectSettings(String projectId) { - this.projectId = projectId ; + this.projectId = projectId; } - public SlackProjectSettings() - { + public SlackProjectSettings() { } @@ -43,11 +51,22 @@ public void setLogoUrl(String logoUrl) { this.logoUrl = logoUrl; } - public boolean isEnabled() - { + public boolean isEnabled() { return this.enabled; } + public boolean postSuccessfulSet() { return this.postSuccessful != null;} + + public boolean postSuccessfulEnabled() { return this.postSuccessful; } + + public boolean postStartedSet() { return this.postStarted != null;} + + public boolean postStartedEnabled() { return this.postStarted; } + + public boolean postFailedSet() { return this.postFailed != null;} + + public boolean postFailedEnabled() { return this.postFailed; } + public void dispose() { } @@ -57,25 +76,20 @@ public void readFrom(Element element) { Element logoElement = element.getChild(ELEMENT_LOGO_URL); Attribute enabledAttr = element.getAttribute(ATTR_ENABLED); - if( enabledAttr != null ) - { - try { - enabled = enabledAttr.getBooleanValue() ; - } catch (DataConversionException e) { - enabled = true ; - } - } - else - { - enabled = true ; - } + Attribute postSuccessfulAttr = element.getAttribute(ATTR_NAME_POST_SUCCESSFUL); + Attribute postStartedAttr = element.getAttribute(ATTR_NAME_POST_STARTED); + Attribute postFailedAttr = element.getAttribute(ATTR_NAME_POST_FAILED); + + enabled = tryGetBooleanAttributeValue(enabledAttr); + postSuccessful = tryGetBooleanAttributeValue(postSuccessfulAttr); + postFailed = tryGetBooleanAttributeValue(postFailedAttr); + postStarted = tryGetBooleanAttributeValue(postStartedAttr); - if( channelElement != null ) { + if (channelElement != null) { this.channel = channelElement.getText(); } - if( logoElement != null ) - { + if (logoElement != null) { this.logoUrl = logoElement.getText(); } } @@ -88,11 +102,32 @@ public void writeTo(Element element) { Element logoUrlElement = new Element(ELEMENT_LOGO_URL); logoUrlElement.setText(this.logoUrl); - Attribute enabledAttr = new Attribute(ATTR_ENABLED,Boolean.toString(enabled)) ; - element.setAttribute( enabledAttr ); + Attribute enabledAttr = new Attribute(ATTR_ENABLED, Boolean.toString(enabled)); + element.setAttribute(enabledAttr); + + Attribute postSuccessfulAttr = new Attribute(ATTR_NAME_POST_SUCCESSFUL, Boolean.toString(postSuccessful)); + element.setAttribute(postSuccessfulAttr); + + Attribute postFailedAttr = new Attribute(ATTR_NAME_POST_FAILED, Boolean.toString(postFailed)); + element.setAttribute(postFailedAttr); + + Attribute postStartedAttr = new Attribute(ATTR_NAME_POST_STARTED, Boolean.toString(postStarted)); + element.setAttribute(postStartedAttr); element.addContent(channelElement); element.addContent(logoUrlElement); } + private Boolean tryGetBooleanAttributeValue(Attribute attr) { + if (attr != null) { + try { + return attr.getBooleanValue(); + } catch (DataConversionException e) { + return null; + } + } + + return null; + } + } diff --git a/src/main/java/com/tapadoo/slacknotifier/SlackServerAdapter.java b/src/main/java/com/tapadoo/slacknotifier/SlackServerAdapter.java index 39472ea..ee56496 100644 --- a/src/main/java/com/tapadoo/slacknotifier/SlackServerAdapter.java +++ b/src/main/java/com/tapadoo/slacknotifier/SlackServerAdapter.java @@ -5,14 +5,15 @@ import com.google.gson.JsonArray; import com.google.gson.JsonObject; import jetbrains.buildServer.issueTracker.Issue; -import jetbrains.buildServer.serverSide.*; +import jetbrains.buildServer.serverSide.BuildServerAdapter; +import jetbrains.buildServer.serverSide.ProjectManager; +import jetbrains.buildServer.serverSide.SBuildServer; +import jetbrains.buildServer.serverSide.SRunningBuild; import jetbrains.buildServer.serverSide.settings.ProjectSettingsManager; import jetbrains.buildServer.users.SUser; import jetbrains.buildServer.users.UserSet; import jetbrains.buildServer.vcs.SelectPrevBuildPolicy; -import jetbrains.buildServer.vcs.VcsRoot; import org.joda.time.Duration; -import org.joda.time.Period; import org.joda.time.format.PeriodFormatter; import org.joda.time.format.PeriodFormatterBuilder; @@ -32,38 +33,41 @@ public class SlackServerAdapter extends BuildServerAdapter { private final SBuildServer buildServer; private final SlackConfigProcessor slackConfig; private final ProjectSettingsManager projectSettingsManager; - private final ProjectManager projectManager ; - private Gson gson ; + private final ProjectManager projectManager; + private Gson gson; - public SlackServerAdapter(SBuildServer sBuildServer, ProjectManager projectManager, ProjectSettingsManager projectSettingsManager , SlackConfigProcessor configProcessor) { + public SlackServerAdapter(SBuildServer sBuildServer, ProjectManager projectManager, ProjectSettingsManager projectSettingsManager, SlackConfigProcessor configProcessor) { + + this.projectManager = projectManager; + this.projectSettingsManager = projectSettingsManager; + this.buildServer = sBuildServer; + this.slackConfig = configProcessor; - this.projectManager = projectManager ; - this.projectSettingsManager = projectSettingsManager ; - this.buildServer = sBuildServer ; - this.slackConfig = configProcessor ; } - public void init() - { + public void init() { buildServer.addListener(this); } - private Gson getGson() - { - if( gson == null ) - { - gson = new GsonBuilder().create() ; + private Gson getGson() { + if (gson == null) { + gson = new GsonBuilder().create(); } - return gson ; + return gson; } @Override public void buildStarted(SRunningBuild build) { super.buildStarted(build); - if( !build.isPersonal() && slackConfig.postStarted() ) + SlackProjectSettings projectSettings = getProjectSettings(build); + + if (!build.isPersonal() && ( + (!projectSettings.postStartedSet() && slackConfig.postStarted()) || + (projectSettings.postStartedSet() && projectSettings.postStartedEnabled()) + )) { postStartedBuild(build); } @@ -73,55 +77,67 @@ public void buildStarted(SRunningBuild build) { public void buildFinished(SRunningBuild build) { super.buildFinished(build); - if( !build.isPersonal() && build.getBuildStatus().isSuccessful() && slackConfig.postSuccessful() ) - { + if (build.getBuildStatus().isSuccessful()) { processSuccessfulBuild(build); - } - else if ( !build.isPersonal() && build.getBuildStatus().isFailed() && slackConfig.postFailed() ) - { - postFailureBuild(build); - } - else - { + } else if (build.getBuildStatus().isFailed()) { + processFailedBuild(build); + } else { //TODO - modify in future if we care about other states } } - private void postStartedBuild(SRunningBuild build ) - { + private void postStartedBuild(SRunningBuild build) { //Could put other into here. Agents maybe? - String message = String.format("Project '%s' build started." , build.getFullName()); + String message = String.format("Project '%s' build started.", build.getFullName()); postToSlack(build, message, true); } - private void postFailureBuild(SRunningBuild build ) - { - String message = ""; - - PeriodFormatter durationFormatter = new PeriodFormatterBuilder() - .printZeroRarelyFirst() - .appendHours() - .appendSuffix(" hour", " hours") - .appendSeparator(" ") - .printZeroRarelyLast() - .appendMinutes() - .appendSuffix(" minute", " minutes") - .appendSeparator(" and ") - .appendSeconds() - .appendSuffix(" second", " seconds") - .toFormatter(); - - Duration buildDuration = new Duration(1000*build.getDuration()); - - message = String.format("Project '%s' build failed! ( %s )" , build.getFullName() , durationFormatter.print(buildDuration.toPeriod())); - - postToSlack(build, message, false); + + private void processFailedBuild(SRunningBuild build) { + SlackProjectSettings projectSettings = getProjectSettings(build); + + if (!build.isPersonal() && ( + (!projectSettings.postFailedSet() && slackConfig.postFailed()) || + (projectSettings.postFailedSet() && projectSettings.postFailedEnabled()) + )){ + String message = ""; + + PeriodFormatter durationFormatter = new PeriodFormatterBuilder() + .printZeroRarelyFirst() + .appendHours() + .appendSuffix(" hour", " hours") + .appendSeparator(" ") + .printZeroRarelyLast() + .appendMinutes() + .appendSuffix(" minute", " minutes") + .appendSeparator(" and ") + .appendSeconds() + .appendSuffix(" second", " seconds") + .toFormatter(); + + Duration buildDuration = new Duration(1000 * build.getDuration()); + + String buildFailedPermalink = this.slackConfig.getBuildFailedPermalink(); + + if (buildFailedPermalink != "") + message = String.format("Project '%s' (%s) build failed! ( %s )\n%s", build.getFullName(), build.getBranch().getDisplayName(), durationFormatter.print(buildDuration.toPeriod()), buildFailedPermalink); + else + message = String.format("Project '%s' (%s) build failed! ( %s )", build.getFullName(), build.getBranch().getDisplayName(), durationFormatter.print(buildDuration.toPeriod())); + + postToSlack(build, message, false); + } } private void processSuccessfulBuild(SRunningBuild build) { - String message = ""; + SlackProjectSettings projectSettings = getProjectSettings(build); + + if (!build.isPersonal() && ( + (!projectSettings.postSuccessfulSet() && slackConfig.postSuccessful()) || + (projectSettings.postSuccessfulSet() && projectSettings.postSuccessfulEnabled()) + )){ + String message = ""; - PeriodFormatter durationFormatter = new PeriodFormatterBuilder() + PeriodFormatter durationFormatter = new PeriodFormatterBuilder() .printZeroRarelyFirst() .appendHours() .appendSuffix(" hour", " hours") @@ -134,103 +150,94 @@ private void processSuccessfulBuild(SRunningBuild build) { .appendSuffix(" second", " seconds") .toFormatter(); - Duration buildDuration = new Duration(1000*build.getDuration()); + Duration buildDuration = new Duration(1000 * build.getDuration()); - message = String.format("Project '%s' built successfully in %s." , build.getFullName() , durationFormatter.print(buildDuration.toPeriod())); + message = String.format("Project '%s' (%s) built successfully in %s.", build.getFullName(), build.getBranch().getDisplayName(), durationFormatter.print(buildDuration.toPeriod())); - postToSlack(build, message, true); + postToSlack(build, message, true); + } } /** * Post a payload to slack with a message and good/bad color. Committer summary is automatically added as an attachment - * @param build the build the message is relating to - * @param message main message to include, 'Build X completed...' etc + * + * @param build the build the message is relating to + * @param message main message to include, 'Build X completed...' etc * @param goodColor true for 'good' builds, false for danger. */ private void postToSlack(SRunningBuild build, String message, boolean goodColor) { - try{ + try { URL url = new URL(slackConfig.getPostUrl()); - SlackProjectSettings projectSettings = (SlackProjectSettings) projectSettingsManager.getSettings(build.getProjectId(),"slackSettings"); + SlackProjectSettings projectSettings = (SlackProjectSettings) projectSettingsManager.getSettings(build.getProjectId(), "slackSettings"); - if( ! projectSettings.isEnabled() ) - { - return ; + if (!projectSettings.isEnabled()) { + return; } String iconUrl = projectSettings.getLogoUrl(); - if(iconUrl == null || iconUrl.length() < 1 ) - { - iconUrl = slackConfig.getLogoUrl() ; + if (iconUrl == null || iconUrl.length() < 1) { + iconUrl = slackConfig.getLogoUrl(); } String configuredChannel = build.getParametersProvider().get("SLACK_CHANNEL"); String channel = this.slackConfig.getDefaultChannel(); - if( configuredChannel != null && configuredChannel.length() > 0 ) - { - channel = configuredChannel ; - } - else if( projectSettings != null && projectSettings.getChannel() != null && projectSettings.getChannel().length() > 0 ) - { - channel = projectSettings.getChannel() ; + if (configuredChannel != null && configuredChannel.length() > 0) { + channel = configuredChannel; + } else if (projectSettings != null && projectSettings.getChannel() != null && projectSettings.getChannel().length() > 0) { + channel = projectSettings.getChannel(); } UserSet committers = build.getCommitters(SelectPrevBuildPolicy.SINCE_LAST_BUILD); StringBuilder committersString = new StringBuilder(); - for( SUser committer : committers.getUsers() ) - { - if( committer != null) - { - String committerName = committer.getName() ; - if( committerName == null || committerName.equals("") ) - { - committerName = committer.getUsername() ; + for (SUser committer : committers.getUsers()) { + if (committer != null) { + String committerName = committer.getName(); + if (committerName == null || committerName.equals("")) { + committerName = committer.getUsername(); } - if( committerName != null && !committerName.equals("")) - { + if (committerName != null && !committerName.equals("")) { committersString.append(committerName); committersString.append(","); } } } - if( committersString.length() > 0 ) - { - committersString.deleteCharAt(committersString.length()-1); //remove the last , + if (committersString.length() > 0) { + committersString.deleteCharAt(committersString.length() - 1); //remove the last , } String commitMsg = committersString.toString(); JsonObject payloadObj = new JsonObject(); - payloadObj.addProperty("channel" , channel); - payloadObj.addProperty("username" , "TeamCity"); + payloadObj.addProperty("channel", channel); + payloadObj.addProperty("username", "TeamCity"); payloadObj.addProperty("text", message); - payloadObj.addProperty("icon_url",iconUrl); + payloadObj.addProperty("icon_url", iconUrl); JsonArray attachmentsObj = new JsonArray(); - if( commitMsg.length() > 0 ) - { + if (commitMsg.length() > 0) { JsonObject attachment = new JsonObject(); - attachment.addProperty("fallback", "Changes by"+ commitMsg); - attachment.addProperty("color",( goodColor ? "good" : "danger")); + attachment.addProperty("fallback", "Changes by" + commitMsg); + attachment.addProperty("color", (goodColor ? "good" : "danger")); JsonArray fields = new JsonArray(); - JsonObject field = new JsonObject() ; + JsonObject field = new JsonObject(); - field.addProperty("title","Changes By"); - field.addProperty("value",commitMsg); + field.addProperty("title", "Changes By"); + field.addProperty("value", commitMsg); field.addProperty("short", true); fields.add(field); - attachment.add("fields",fields); + attachment.add("fields", fields); attachmentsObj.add(attachment); @@ -238,8 +245,7 @@ else if( projectSettings != null && projectSettings.getChannel() != null && proj //Do we have any issues? - if( build.isHasRelatedIssues() ) - { + if (build.isHasRelatedIssues()) { //We do! Collection issues = build.getRelatedIssues(); JsonObject issuesAttachment = new JsonObject(); @@ -247,8 +253,7 @@ else if( projectSettings != null && projectSettings.getChannel() != null && proj StringBuilder issueIds = new StringBuilder(); StringBuilder clickableIssueIds = new StringBuilder(); - for( Issue issue : issues ) - { + for (Issue issue : issues) { issueIds.append(','); issueIds.append(issue.getId()); @@ -261,25 +266,23 @@ else if( projectSettings != null && projectSettings.getChannel() != null && proj clickableIssueIds.append('>'); } - if( issueIds.length() > 0 ) - { + if (issueIds.length() > 0) { issueIds.deleteCharAt(0); //delete first ',' } - if( clickableIssueIds.length() > 0 ) - { + if (clickableIssueIds.length() > 0) { clickableIssueIds.deleteCharAt(0); //delete first ',' } - issuesAttachment.addProperty("fallback" , "Issues " + issueIds.toString()); + issuesAttachment.addProperty("fallback", "Issues " + issueIds.toString()); //Not sure what color, if any to use for this. For now, leave it the same as the committers one - issuesAttachment.addProperty("color",( goodColor ? "good" : "danger")); + issuesAttachment.addProperty("color", (goodColor ? "good" : "danger")); JsonArray fields = new JsonArray(); - JsonObject field = new JsonObject() ; + JsonObject field = new JsonObject(); - field.addProperty("title","Related Issues"); - field.addProperty("value",clickableIssueIds.toString()); + field.addProperty("title", "Related Issues"); + field.addProperty("value", clickableIssueIds.toString()); field.addProperty("short", true); fields.add(field); @@ -288,7 +291,7 @@ else if( projectSettings != null && projectSettings.getChannel() != null && proj attachmentsObj.add(issuesAttachment); } - if( attachmentsObj.size() > 0 ) { + if (attachmentsObj.size() > 0) { payloadObj.add("attachments", attachmentsObj); } @@ -298,24 +301,25 @@ else if( projectSettings != null && projectSettings.getChannel() != null && proj BufferedOutputStream bos = new BufferedOutputStream(conn.getOutputStream()); String payloadJson = getGson().toJson(payloadObj); - String bodyContents = "payload=" + payloadJson ; + String bodyContents = "payload=" + payloadJson; bos.write(bodyContents.getBytes("utf8")); bos.flush(); bos.close(); - int serverResponseCode = conn.getResponseCode() ; + int serverResponseCode = conn.getResponseCode(); conn.disconnect(); - conn = null ; - url = null ; + conn = null; + url = null; - } - catch ( MalformedURLException ex ) - { + } catch (MalformedURLException ex) { } catch (IOException e) { e.printStackTrace(); } } + private SlackProjectSettings getProjectSettings(SRunningBuild build) { + return (SlackProjectSettings) projectSettingsManager.getSettings(build.getProjectId(), "slackSettings"); + } } diff --git a/src/main/java/com/tapadoo/slacknotifier/webui/SlackAdminPage.java b/src/main/java/com/tapadoo/slacknotifier/webui/SlackAdminPage.java index 4eeba02..5826d93 100644 --- a/src/main/java/com/tapadoo/slacknotifier/webui/SlackAdminPage.java +++ b/src/main/java/com/tapadoo/slacknotifier/webui/SlackAdminPage.java @@ -15,7 +15,7 @@ */ public class SlackAdminPage extends AdminPage { - private final SlackConfigProcessor configProcesser; + private final SlackConfigProcessor configProcessor; public SlackAdminPage(PagePlaces pagePlaces, PluginDescriptor descriptor , SlackConfigProcessor configProcessor) { super(pagePlaces); @@ -25,7 +25,7 @@ public SlackAdminPage(PagePlaces pagePlaces, PluginDescriptor descriptor , Slack setPosition(PositionConstraint.after("clouds", "email", "jabber")); register(); - this.configProcesser = configProcessor ; + this.configProcessor = configProcessor ; } @Override @@ -41,8 +41,9 @@ public String getGroup() { public void fillModel(Map model, HttpServletRequest request) { super.fillModel(model, request); - model.put("defaultChannel" , configProcesser.getDefaultChannel()); - model.put("logoUrl" , configProcesser.getLogoUrl()); - model.put("postUrl" , configProcesser.getPostUrl()); + model.put("defaultChannel" , configProcessor.getDefaultChannel()); + model.put("logoUrl" , configProcessor.getLogoUrl()); + model.put("postUrl" , configProcessor.getPostUrl()); + model.put("buildFailedPermalink", configProcessor.getBuildFailedPermalink()); } } diff --git a/src/main/resources/buildServerResources/admin/slackAdminPage.jsp b/src/main/resources/buildServerResources/admin/slackAdminPage.jsp index 361bf62..9235532 100644 --- a/src/main/resources/buildServerResources/admin/slackAdminPage.jsp +++ b/src/main/resources/buildServerResources/admin/slackAdminPage.jsp @@ -7,6 +7,7 @@

Post URL : ${postUrl}
Default Logo URL : ${logoUrl}
+ Build Failed Permalink : ${buildFailedPermalink}

Configuration

@@ -19,6 +20,7 @@ <slackDefaultChannel>#testing</slackDefaultChannel> <slackPostUrl>https://hooks.slack.com/services/XXXXXXX/XXXXX/XXXXXXXX</slackPostUrl> <slackLogoUrl>http://build.tapadoo.com/img/icons/TeamCity32.png</slackLogoUrl> + <buildFailedPermalink>http://build.tapadoo.com/viewLog.html?buildTypeId=XXXXXXX&amp;buildId=lastFinished</buildFailedPermalink> </slackNotifier> ... ...