-
Notifications
You must be signed in to change notification settings - Fork 89
Expand file tree
/
Copy pathGitlabPublisher.java
More file actions
262 lines (211 loc) · 9.22 KB
/
GitlabPublisher.java
File metadata and controls
262 lines (211 loc) · 9.22 KB
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
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
package jetbrains.buildServer.commitPublisher.gitlab;
import com.google.gson.Gson;
import com.intellij.openapi.diagnostic.Logger;
import java.util.LinkedHashMap;
import jetbrains.buildServer.commitPublisher.*;
import jetbrains.buildServer.serverSide.*;
import jetbrains.buildServer.serverSide.executors.ExecutorServices;
import jetbrains.buildServer.serverSide.impl.LogUtil;
import jetbrains.buildServer.vcs.VcsRoot;
import jetbrains.buildServer.vcs.VcsRootInstance;
import org.apache.http.entity.ContentType;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import jetbrains.buildServer.commitPublisher.gitlab.data.GitLabMRInfo;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.io.IOException;
import java.util.*;
import java.util.Collections;
import java.util.Map;
class GitlabPublisher extends HttpBasedCommitStatusPublisher {
private static final String REFS_HEADS = "refs/heads/";
private static final String REFS_TAGS = "refs/tags/";
private static final Logger LOG = Logger.getInstance(GitlabPublisher.class.getName());
private final Gson myGson = new Gson();
private final WebLinks myLinks;
GitlabPublisher(@NotNull CommitStatusPublisherSettings settings,
@NotNull SBuildType buildType, @NotNull String buildFeatureId,
@NotNull ExecutorServices executorServices, @NotNull WebLinks links,
@NotNull Map<String, String> params,
@NotNull CommitStatusPublisherProblems problems) {
super(settings, buildType, buildFeatureId, executorServices, params, problems);
myLinks = links;
}
@NotNull
@Override
public String getId() {
return Constants.GITLAB_PUBLISHER_ID;
}
@NotNull
@Override
public String toString() {
return "GitLab";
}
@Override
public boolean buildStarted(@NotNull SRunningBuild build, @NotNull BuildRevision revision) throws PublisherException {
publish(build, revision, GitlabBuildStatus.RUNNING, "Build started");
return true;
}
@Override
public boolean buildFinished(@NotNull SFinishedBuild build, @NotNull BuildRevision revision) throws PublisherException {
GitlabBuildStatus status = build.getBuildStatus().isSuccessful() ? GitlabBuildStatus.SUCCESS : GitlabBuildStatus.FAILED;
publish(build, revision, status, build.getStatusDescriptor().getText());
return true;
}
@Override
public boolean buildFailureDetected(@NotNull SRunningBuild build, @NotNull BuildRevision revision) throws PublisherException {
publish(build, revision, GitlabBuildStatus.FAILED, build.getStatusDescriptor().getText());
return true;
}
@Override
public boolean buildMarkedAsSuccessful(@NotNull SBuild build, @NotNull BuildRevision revision, boolean buildInProgress) throws PublisherException {
publish(build, revision, buildInProgress ? GitlabBuildStatus.RUNNING : GitlabBuildStatus.SUCCESS, "Build marked as successful");
return true;
}
@Override
public boolean buildInterrupted(@NotNull SFinishedBuild build, @NotNull BuildRevision revision) throws PublisherException {
publish(build, revision, GitlabBuildStatus.CANCELED, build.getStatusDescriptor().getText());
return true;
}
private void publish(@NotNull SBuild build,
@NotNull BuildRevision revision,
@NotNull GitlabBuildStatus status,
@NotNull String description) throws PublisherException {
VcsRootInstance root = revision.getRoot();
String apiUrl = getApiUrl();
if (null == apiUrl || apiUrl.length() == 0)
throw new PublisherException("Missing GitLab API URL parameter");
String pathPrefix = GitlabSettings.getPathPrefix(apiUrl);
Repository repository = parseRepository(root, pathPrefix);
if (repository == null)
throw new PublisherException("Cannot parse repository URL from VCS root " + root.getName());
String message = createMessage(status, build, revision, myLinks.getViewResultsUrl(build), description);
Pattern r = Pattern.compile("/merge-requests/([0-9]+)/");
Matcher m = r.matcher(revision.getRepositoryVersion().getVcsBranch());
if (m.find( )) {
String mr_number = m.group(1);
String url = GitlabSettings.getProjectsUrl(getApiUrl(), repository.owner(), repository.repositoryName()) + "/merge_requests/" + mr_number;
MRInfoResponseProcessor processorMR = new MRInfoResponseProcessor();
try {
HttpHelper.get(url, null, null, Collections.singletonMap("PRIVATE-TOKEN", getPrivateToken()), BaseCommitStatusPublisher.DEFAULT_CONNECTION_TIMEOUT, processorMR);
} catch (Exception ex) {
throw new PublisherException(String.format("GitLab publisher has failed to connect to %s/%s repository", repository.owner(), repository.repositoryName()), ex);
}
String urlCommit = getApiUrl() + "/projects/" + processorMR.source_project_id() + "/statuses/" + revision.getRevision();
postAsync(urlCommit, null, null, message, ContentType.APPLICATION_JSON, Collections.singletonMap("PRIVATE-TOKEN", getPrivateToken()), LogUtil.describe(build));
return;
}
try {
publish(revision.getRevision(), message, repository, LogUtil.describe(build));
} catch (Exception e) {
throw new PublisherException("Cannot publish status to GitLab for VCS root " +
revision.getRoot().getName() + ": " + e.toString(), e);
}
}
private void publish(@NotNull String commit, @NotNull String data, @NotNull Repository repository, @NotNull String buildDescription) throws Exception {
String url = GitlabSettings.getProjectsUrl(getApiUrl(), repository.owner(), repository.repositoryName()) + "/statuses/" + commit;
LOG.debug("Request url: " + url + ", message: " + data);
postAsync(url, null, null, data, ContentType.APPLICATION_JSON, Collections.singletonMap("PRIVATE-TOKEN", getPrivateToken()), buildDescription);
}
@NotNull
private String createMessage(@NotNull GitlabBuildStatus status,
@NotNull SBuild build,
@NotNull BuildRevision revision,
@NotNull String url,
@NotNull String description) {
RepositoryVersion repositoryVersion = revision.getRepositoryVersion();
String ref = repositoryVersion.getVcsBranch();
if (ref != null) {
if (ref.startsWith(REFS_HEADS)) {
ref = ref.substring(REFS_HEADS.length());
} else if (ref.startsWith(REFS_TAGS)) {
ref = ref.substring(REFS_TAGS.length());
} else {
ref = null;
}
}
final Map<String, String> data = new LinkedHashMap<String, String>();
data.put("state", status.getName());
data.put("name", build.getBuildTypeName());
data.put("target_url", url);
data.put("description", description);
if (ref != null)
data.put("ref", ref);
return myGson.toJson(data);
}
@Nullable
static Repository parseRepository(@NotNull VcsRoot root, @Nullable String pathPrefix) {
if ("jetbrains.git".equals(root.getVcsName())) {
String url = root.getProperty("url");
return url == null ? null : GitRepositoryParser.parseRepository(url, pathPrefix);
} else {
return null;
}
}
private String getApiUrl() {
return HttpHelper.stripTrailingSlash(myParams.get(Constants.GITLAB_API_URL));
}
private String getPrivateToken() {
return myParams.get(Constants.GITLAB_TOKEN);
}
private abstract class JsonResponseProcessor<T> extends DefaultHttpResponseProcessor {
private final Class<T> myInfoClass;
private T myInfo;
JsonResponseProcessor(Class<T> infoClass) {
myInfoClass = infoClass;
}
T getInfo() {
return myInfo;
}
@Override
public void processResponse(HttpResponse response) throws HttpPublisherException, IOException {
super.processResponse(response);
final HttpEntity entity = response.getEntity();
if (null == entity) {
throw new HttpPublisherException("GitLab publisher has received no response");
}
ByteArrayOutputStream bos = new ByteArrayOutputStream();
entity.writeTo(bos);
final String json = bos.toString("utf-8");
myInfo = myGson.fromJson(json, myInfoClass);
}
}
private class MRInfoResponseProcessor extends JsonResponseProcessor<GitLabMRInfo> {
private String _id;
private String _iid;
private String _source_project_id;
private String _target_project_id;
MRInfoResponseProcessor() {
super(GitLabMRInfo.class);
}
String id() {
return _id;
}
String iid() {
return _iid;
}
String source_project_id() {
return _source_project_id;
}
String target_project_id() {
return _target_project_id;
}
@Override
public void processResponse(HttpResponse response) throws HttpPublisherException, IOException {
super.processResponse(response);
GitLabMRInfo userInfo = getInfo();
if (null == userInfo || null == userInfo.id) {
throw new HttpPublisherException("GitLab publisher has received a malformed response");
}
_id = userInfo.id;
_iid = userInfo.iid;
_source_project_id = userInfo.source_project_id;
_target_project_id = userInfo.target_project_id;
}
}
}