/
TracPublisher.java
178 lines (148 loc) · 5 KB
/
TracPublisher.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
package jenkins.plugins.tracpublisher;
import hudson.Extension;
import hudson.Launcher;
import hudson.model.BuildListener;
import hudson.model.Result;
import hudson.model.AbstractBuild;
import hudson.model.AbstractProject;
import hudson.scm.ChangeLogSet;
import hudson.scm.ChangeLogSet.Entry;
import hudson.tasks.BuildStepDescriptor;
import hudson.tasks.BuildStepMonitor;
import hudson.tasks.Notifier;
import hudson.tasks.Publisher;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import net.sf.json.JSONObject;
import org.apache.xmlrpc.XmlRpcException;
import org.apache.xmlrpc.client.XmlRpcClient;
import org.apache.xmlrpc.client.XmlRpcClientConfigImpl;
import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.StaplerRequest;
/**
* A notifier that attaches comments to Trac tickets upon successful build. The
* notifier searches the changesets for references to ticket numbers and adds a
* link to the jenkins build to those issues. It enables people to easily find
* builds containing changes related to issues.
*
* @author batkinson
*/
public class TracPublisher extends Notifier {
public String buildServerAddress;
public String rpcAddress;
public String username;
public String password;
@DataBoundConstructor
public TracPublisher(String buildServerAddress, String rpcAddress,
String username, String password) {
this.buildServerAddress = buildServerAddress;
this.rpcAddress = rpcAddress;
this.username = username;
this.password = password;
}
public BuildStepMonitor getRequiredMonitorService() {
return BuildStepMonitor.NONE;
}
@Override
public boolean needsToRunAfterFinalized() {
return true;
}
Pattern issuePattern = Pattern.compile("[#](\\d+)");
@Override
public boolean perform(AbstractBuild<?, ?> build, Launcher launcher,
BuildListener listener) throws InterruptedException, IOException {
Result result = build.getResult();
if (Result.SUCCESS.equals(result)) {
// Build set of affected issues for this build using commit msgs
Set<Integer> affectedIssues = new HashSet<Integer>();
ChangeLogSet<? extends Entry> changes = build.getChangeSet();
for (Entry change : changes) {
String message = change.getMsg();
Matcher matcher = issuePattern.matcher(message);
while (matcher.find()) {
String issueString = matcher.group(1);
Integer issue = Integer.parseInt(issueString);
affectedIssues.add(issue);
}
}
if (affectedIssues.size() > 0)
listener.getLogger().format(
"Updating %d Trac issue(s): server=%s, user=%s\n",
affectedIssues.size(), rpcAddress, username);
// Run through the issues and update them with the build
for (Integer issue : affectedIssues) {
try {
String buildDN = build.getFullDisplayName();
listener.getLogger().format("Updating issue %d with %s\n:",
issue, buildDN);
updateIssue(issue, buildDN, build.getUrl());
} catch (XmlRpcException e) {
e.printStackTrace();
}
}
}
return true;
}
@SuppressWarnings("rawtypes")
private void updateIssue(Integer issueNumber, String buildName, String url)
throws MalformedURLException, XmlRpcException {
XmlRpcClient client = new XmlRpcClient();
XmlRpcClientConfigImpl config = new XmlRpcClientConfigImpl();
config.setBasicUserName(username);
config.setBasicPassword(password);
config.setServerURL(new URL(rpcAddress));
client.setConfig(config);
String message = String.format("Referenced in build [%s/%s %s]",
buildServerAddress, url, buildName);
Object[] params = new Object[] { issueNumber, message, new HashMap(),
Boolean.FALSE };
client.execute("ticket.update", params);
}
@Extension
public static final class DescriptorImpl extends
BuildStepDescriptor<Publisher> {
public DescriptorImpl() {
load();
}
private String buildServerAddress;
private String rpcAddress;
private String username;
private String password;
@Override
public boolean isApplicable(Class<? extends AbstractProject> jobType) {
return true;
}
@Override
public String getDisplayName() {
return "Add link to Trac issues";
}
@Override
public boolean configure(StaplerRequest req, JSONObject json)
throws hudson.model.Descriptor.FormException {
buildServerAddress = json.getString("buildServerAddress");
rpcAddress = json.getString("rpcAddress");
username = json.getString("username");
password = json.getString("password");
save();
return super.configure(req, json);
}
public String getBuildServerAddress() {
return buildServerAddress;
}
public String getRpcAddress() {
return rpcAddress;
}
public String getUsername() {
return username;
}
public String getPassword() {
return password;
}
}
}