Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement Mustache templates for messages #32

Open
wants to merge 20 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,7 @@ target
.settings
bin
work

# IDEA files
.idea/
*.iml
38 changes: 37 additions & 1 deletion README.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,40 @@

Started with a fork of the Campfire plugin:

https://github.com/jgp/hudson_campfire_plugin
https://github.com/jgp/hudson_campfire_plugin

## Mustache template updates

The plugin has been updated to use [Mustache.java](https://github.com/spullara/mustache.java) to
generate the HipChat notifications - this allows for easier customisation, as well as adding custom
build variables to be output.

The default templates are:

* Start message: ```{{build.project.displayName}} - {{build.displayName}}: {{trigger}} {{{link}}}```
* Complete message: ```{{build.project.displayName}} - {{build.displayName}}: {{status}} after {{build.durationString}}```

There is also a per-Job configurable "Message suffix" - this is also a Mustache template and is
used when you want to keep the same template overall and just override something that gets appended
to the default messages.

### Available parameters

#### Start message

* **build** - instance of [AbstractBuild](http://javadoc.jenkins-ci.org/hudson/model/AbstractBuild.html) -
most of the information comes from this object (eg. ```{{build.project.displayName}}```)
* **trigger** - a string describing why the build was started - tries to use the "changes" if available, otherwise
the "cause", and finally falls back to the string "Started..."
* **link** - link to the build in Jenkins - note the triple-bracket to prevent HTML from being escaped

#### Completed message

* **build** - see above
* **status** - string describing the state of the build (eg. success, fail, etc)
* **link** - link to the build in Jenkins

### Using custom build parameters

Custom build parameters are available through ```{{build.buildVariables}}``` - for example if you have
a parameter called ENVIRONMENT then you would use ```{{build.buildVariables.ENVIRONMENT}}```.
9 changes: 7 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,14 @@
<parent>
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>plugin</artifactId>
<version>1.447</version>
<version>1.500</version>
</parent>

<!-- keeping original groupId -->
<groupId>org.jvnet.hudson.plugins</groupId>
<artifactId>hipchat</artifactId>
<packaging>hpi</packaging>
<version>0.1.2-SNAPSHOT</version>
<version>0.1.5-SNAPSHOT</version>
<name>Jenkins HipChat Plugin</name>
<description>A Build status publisher that notifies channels on a HipChat server</description>
<url>http://wiki.jenkins-ci.org/display/JENKINS/HipChat+Plugin</url>
Expand Down Expand Up @@ -77,6 +77,11 @@
<version>1.0.4</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.github.spullara.mustache.java</groupId>
<artifactId>compiler</artifactId>
<version>0.8.9</version>
</dependency>
</dependencies>

<build>
Expand Down
166 changes: 88 additions & 78 deletions src/main/java/jenkins/plugins/hipchat/ActiveNotifier.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package jenkins.plugins.hipchat;

import com.github.mustachejava.DefaultMustacheFactory;
import com.github.mustachejava.Mustache;
import com.github.mustachejava.MustacheFactory;
import hudson.Util;
import hudson.model.AbstractBuild;
import hudson.model.AbstractProject;
Expand All @@ -10,10 +13,9 @@
import hudson.scm.ChangeLogSet.Entry;
import org.apache.commons.lang.StringUtils;

import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.*;
import java.util.logging.Logger;

@SuppressWarnings("rawtypes")
Expand All @@ -22,10 +24,12 @@ public class ActiveNotifier implements FineGrainedNotifier {
private static final Logger logger = Logger.getLogger(HipChatListener.class.getName());

HipChatNotifier notifier;
MustacheFactory mustacheFactory;

public ActiveNotifier(HipChatNotifier notifier) {
super();
this.notifier = notifier;
this.mustacheFactory = new DefaultMustacheFactory();
}

private HipChatService getHipChat(AbstractBuild r) {
Expand All @@ -41,15 +45,31 @@ public void started(AbstractBuild build) {
String changes = getChanges(build);
CauseAction cause = build.getAction(CauseAction.class);

if (changes != null) {
notifyStart(build, changes);
} else if (cause != null) {
MessageBuilder message = new MessageBuilder(notifier, build);
message.append(cause.getShortDescription());
notifyStart(build, message.appendOpenLink().toString());
} else {
notifyStart(build, getBuildStatusMessage(build));
String trigger;
if (changes != null)
{
trigger = changes;
}
else if (cause != null)
{
trigger = cause.getShortDescription();
}
else
{
trigger = "Starting...";
}

Map<String, Object> messageParams = new HashMap<String,Object>();
messageParams.put("build", build);
messageParams.put("trigger", trigger);
messageParams.put("link", getOpenLink(build));

HipChatNotifier.HipChatJobProperty jobProperty = getJobPropertyForBuild(build);
String messageTemplate = getMessageTemplate(jobProperty.getMessageTemplateStarted(),
jobProperty.getMessageTemplateSuffix(),
"{{build.project.displayName}} - {{build.displayName}}: {{trigger}} {{{link}}}");

notifyStart(build, applyMessageTemplate(messageTemplate, messageParams));
}

private void notifyStart(AbstractBuild build, String message) {
Expand All @@ -69,12 +89,28 @@ public void completed(AbstractBuild r) {
|| (result == Result.NOT_BUILT && jobProperty.getNotifyNotBuilt())
|| (result == Result.SUCCESS && jobProperty.getNotifySuccess())
|| (result == Result.UNSTABLE && jobProperty.getNotifyUnstable())) {
getHipChat(r).publish(getBuildStatusMessage(r), getBuildColor(r));

String messageTemplate = getMessageTemplate(jobProperty.getMessageTemplateCompleted(),
jobProperty.getMessageTemplateSuffix(),
"{{build.project.displayName}} - {{build.displayName}}: {{{status}}} after {{build.durationString}} {{{link}}}");

Map<String,Object> messageParams = new HashMap<String, Object>();
messageParams.put("build", r);
messageParams.put("status", getStatusMessage(r));
messageParams.put("link", getOpenLink(r));

getHipChat(r).publish(applyMessageTemplate(messageTemplate, messageParams), getBuildColor(r));
}

}

String getChanges(AbstractBuild r) {
private HipChatNotifier.HipChatJobProperty getJobPropertyForBuild(AbstractBuild r)
{
AbstractProject<?, ?> project = r.getProject();
return project.getProperty(HipChatNotifier.HipChatJobProperty.class);
}

private String getChanges(AbstractBuild r) {
if (!r.hasChangeSetComputed()) {
logger.info("No change set computed...");
return null;
Expand All @@ -96,13 +132,13 @@ String getChanges(AbstractBuild r) {
for (Entry entry : entries) {
authors.add(entry.getAuthor().getDisplayName());
}
MessageBuilder message = new MessageBuilder(notifier, r);
StringBuilder message = new StringBuilder();
message.append("Started by changes from ");
message.append(StringUtils.join(authors, ", "));
message.append(" (");
message.append(files.size());
message.append(" file(s) changed)");
return message.appendOpenLink().toString();
return message.toString();
}

static String getBuildColor(AbstractBuild r) {
Expand All @@ -116,75 +152,49 @@ static String getBuildColor(AbstractBuild r) {
}
}

String getBuildStatusMessage(AbstractBuild r) {
MessageBuilder message = new MessageBuilder(notifier, r);
message.appendStatusMessage();
message.appendDuration();
return message.appendOpenLink().toString();
}

public static class MessageBuilder {
private StringBuffer message;
private HipChatNotifier notifier;
private AbstractBuild build;

public MessageBuilder(HipChatNotifier notifier, AbstractBuild build) {
this.notifier = notifier;
this.message = new StringBuffer();
this.build = build;
startMessage();
}

public MessageBuilder appendStatusMessage() {
message.append(getStatusMessage(build));
return this;
private String getMessageTemplate(String baseTemplate, String suffixTemplate, String defaultTemplate)
{
StringBuilder template = new StringBuilder();
if (baseTemplate == null || StringUtils.isBlank(baseTemplate))
{
template.append(defaultTemplate);
}

static String getStatusMessage(AbstractBuild r) {
if (r.isBuilding()) {
return "Starting...";
}
Result result = r.getResult();
if (result == Result.SUCCESS) return "Success";
if (result == Result.FAILURE) return "<b>FAILURE</b>";
if (result == Result.ABORTED) return "ABORTED";
if (result == Result.NOT_BUILT) return "Not built";
if (result == Result.UNSTABLE) return "Unstable";
return "Unknown";
}

public MessageBuilder append(String string) {
message.append(string);
return this;
if (suffixTemplate != null && StringUtils.isNotBlank(suffixTemplate)) {
template.append(" ");
template.append(suffixTemplate);
}
return template.toString();
}

public MessageBuilder append(Object string) {
message.append(string.toString());
return this;
}

private MessageBuilder startMessage() {
message.append(build.getProject().getDisplayName());
message.append(" - ");
message.append(build.getDisplayName());
message.append(" ");
return this;
}
String applyMessageTemplate(String messageTemplate, Map<String,Object> messageParams)
{
StringWriter messageWriter = new StringWriter();

public MessageBuilder appendOpenLink() {
String url = notifier.getBuildServerUrl() + build.getUrl();
message.append(" (<a href='").append(url).append("'>Open</a>)");
return this;
}
Mustache mustache = this.mustacheFactory.compile(new StringReader(messageTemplate), "message");
mustache.execute(messageWriter, messageParams);
return messageWriter.toString();
}

public MessageBuilder appendDuration() {
message.append(" after ");
message.append(build.getDurationString());
return this;
private String getStatusMessage(AbstractBuild r) {
if (r.isBuilding()) {
return "Starting...";
}
Result result = r.getResult();
if (result == Result.SUCCESS) return "Success";
if (result == Result.FAILURE) return "<b>FAILURE</b>";
if (result == Result.ABORTED) return "ABORTED";
if (result == Result.NOT_BUILT) return "Not built";
if (result == Result.UNSTABLE) return "Unstable";
return "Unknown";
}

public String toString() {
return message.toString();
}
private String getOpenLink(AbstractBuild build)
{
StringBuilder builder = new StringBuilder("(<a href='");
builder.append(notifier.getBuildServerUrl());
builder.append(build.getUrl());
builder.append("'>Open</a>)");
return builder.toString();
}
}
Loading