PageRenderTime 49ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/modules/test/jenkins-results-parser/src/main/java/com/liferay/jenkins/results/parser/PullRequest.java

http://github.com/liferay/liferay-portal
Java | 725 lines | 523 code | 186 blank | 16 comment | 49 complexity | 39785f573723baa7e6b962e1fac7e23a MD5 | raw file
Possible License(s): LGPL-2.0
  1. /**
  2. * Copyright (c) 2000-present Liferay, Inc. All rights reserved.
  3. *
  4. * This library is free software; you can redistribute it and/or modify it under
  5. * the terms of the GNU Lesser General Public License as published by the Free
  6. * Software Foundation; either version 2.1 of the License, or (at your option)
  7. * any later version.
  8. *
  9. * This library is distributed in the hope that it will be useful, but WITHOUT
  10. * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
  11. * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
  12. * details.
  13. */
  14. package com.liferay.jenkins.results.parser;
  15. import com.liferay.jenkins.results.parser.JenkinsResultsParserUtil.HttpRequestMethod;
  16. import java.io.File;
  17. import java.io.IOException;
  18. import java.text.ParseException;
  19. import java.text.SimpleDateFormat;
  20. import java.util.ArrayList;
  21. import java.util.Date;
  22. import java.util.List;
  23. import java.util.Objects;
  24. import java.util.TimeZone;
  25. import java.util.regex.Matcher;
  26. import java.util.regex.Pattern;
  27. import org.apache.commons.lang.StringUtils;
  28. import org.json.JSONArray;
  29. import org.json.JSONObject;
  30. /**
  31. * @author Michael Hashimoto
  32. */
  33. public class PullRequest {
  34. public static boolean isValidGitHubPullRequestURL(String gitHubURL) {
  35. Matcher matcher = _gitHubPullRequestURLPattern.matcher(gitHubURL);
  36. if (matcher.find()) {
  37. return true;
  38. }
  39. return false;
  40. }
  41. public PullRequest(String gitHubURL) {
  42. this(gitHubURL, _NAME_TEST_SUITE_DEFAULT);
  43. }
  44. public PullRequest(String gitHubURL, String testSuiteName) {
  45. if ((testSuiteName == null) || testSuiteName.isEmpty()) {
  46. testSuiteName = _NAME_TEST_SUITE_DEFAULT;
  47. }
  48. _testSuiteName = testSuiteName;
  49. Matcher matcher = _gitHubPullRequestURLPattern.matcher(gitHubURL);
  50. if (!matcher.find()) {
  51. throw new RuntimeException("Invalid GitHub URL " + gitHubURL);
  52. }
  53. _gitHubRemoteGitRepositoryName = matcher.group(
  54. "gitHubRemoteGitRepositoryName");
  55. _number = Integer.parseInt(matcher.group("number"));
  56. _ownerUsername = matcher.group("owner");
  57. refresh();
  58. }
  59. public Comment addComment(String body) {
  60. body = body.replaceAll("(\\>)\\s+(\\<)", "$1$2");
  61. body = body.replace("&quot;", "\\&quot;");
  62. JSONObject dataJSONObject = new JSONObject();
  63. dataJSONObject.put("body", body);
  64. try {
  65. JSONObject responseJSONObject =
  66. JenkinsResultsParserUtil.toJSONObject(
  67. JenkinsResultsParserUtil.combine(
  68. _jsonObject.getString("issue_url"), "/comments"),
  69. dataJSONObject.toString());
  70. return new Comment(responseJSONObject);
  71. }
  72. catch (IOException ioException) {
  73. throw new RuntimeException(
  74. "Unable to post comment in GitHub pull request " + getURL(),
  75. ioException);
  76. }
  77. }
  78. public boolean addLabel(GitHubRemoteGitRepository.Label label) {
  79. if ((label == null) || hasLabel(label.getName())) {
  80. return true;
  81. }
  82. GitHubRemoteGitRepository gitHubRemoteGitRepository =
  83. getGitHubRemoteGitRepository();
  84. GitHubRemoteGitRepository.Label gitRepositoryLabel =
  85. gitHubRemoteGitRepository.getLabel(label.getName());
  86. if (gitRepositoryLabel == null) {
  87. System.out.println(
  88. JenkinsResultsParserUtil.combine(
  89. "GitHubRemoteGitRepository.Label ", label.getName(),
  90. " does not exist in ", getGitHubRemoteGitRepositoryName()));
  91. return false;
  92. }
  93. JSONArray jsonArray = new JSONArray();
  94. jsonArray.put(label.getName());
  95. String gitHubApiUrl = JenkinsResultsParserUtil.getGitHubApiUrl(
  96. getGitHubRemoteGitRepositoryName(), getOwnerUsername(),
  97. "issues/" + getNumber() + "/labels");
  98. try {
  99. _toString(gitHubApiUrl, jsonArray.toString());
  100. }
  101. catch (IOException ioException) {
  102. System.out.println("Unable to add label " + label.getName());
  103. ioException.printStackTrace();
  104. return false;
  105. }
  106. return true;
  107. }
  108. public void close() throws IOException {
  109. if (Objects.equals(getState(), "open")) {
  110. JSONObject postContentJSONObject = new JSONObject();
  111. postContentJSONObject.put("state", "closed");
  112. _toString(
  113. _jsonObject.getString("url"), postContentJSONObject.toString());
  114. }
  115. _jsonObject.put("state", "closed");
  116. }
  117. public List<Comment> getComments() {
  118. List<Comment> comments = new ArrayList<>();
  119. String gitHubApiUrl = JenkinsResultsParserUtil.getGitHubApiUrl(
  120. getGitHubRemoteGitRepositoryName(), getOwnerUsername(),
  121. "issues/" + getNumber() + "/comments?page=");
  122. int page = 1;
  123. while (true) {
  124. try {
  125. JSONArray jsonArray = JenkinsResultsParserUtil.toJSONArray(
  126. gitHubApiUrl + page, false);
  127. if (jsonArray.length() == 0) {
  128. break;
  129. }
  130. for (int i = 0; i < jsonArray.length(); i++) {
  131. comments.add(new Comment(jsonArray.getJSONObject(i)));
  132. }
  133. page++;
  134. }
  135. catch (IOException ioException) {
  136. throw new RuntimeException(
  137. "Unable to get pull request comments", ioException);
  138. }
  139. }
  140. return comments;
  141. }
  142. public String getCommonParentSHA() {
  143. return _commonParentSHA;
  144. }
  145. public GitHubRemoteGitCommit getGitHubRemoteGitCommit() {
  146. return GitCommitFactory.newGitHubRemoteGitCommit(
  147. getOwnerUsername(), getGitHubRemoteGitRepositoryName(),
  148. getSenderSHA());
  149. }
  150. public GitHubRemoteGitRepository getGitHubRemoteGitRepository() {
  151. if (_gitHubRemoteGitRepository == null) {
  152. _gitHubRemoteGitRepository =
  153. (GitHubRemoteGitRepository)
  154. GitRepositoryFactory.getRemoteGitRepository(
  155. "github.com", _gitHubRemoteGitRepositoryName,
  156. getOwnerUsername());
  157. }
  158. return _gitHubRemoteGitRepository;
  159. }
  160. public String getGitHubRemoteGitRepositoryName() {
  161. return _gitHubRemoteGitRepositoryName;
  162. }
  163. public String getGitRepositoryName() {
  164. return getGitHubRemoteGitRepositoryName();
  165. }
  166. public String getHtmlURL() {
  167. return _jsonObject.getString("html_url");
  168. }
  169. public String getJSON() {
  170. return _jsonObject.toString(4);
  171. }
  172. public JSONObject getJSONObject() {
  173. return _jsonObject;
  174. }
  175. public List<GitHubRemoteGitRepository.Label> getLabels() {
  176. return _labels;
  177. }
  178. public String getLiferayRemoteBranchSHA() {
  179. RemoteGitBranch liferayRemoteGitBranch = getLiferayRemoteGitBranch();
  180. return liferayRemoteGitBranch.getSHA();
  181. }
  182. public RemoteGitBranch getLiferayRemoteGitBranch() {
  183. if (_liferayRemoteGitBranch == null) {
  184. _liferayRemoteGitBranch = GitUtil.getRemoteGitBranch(
  185. getUpstreamBranchName(), new File("."),
  186. "git@github.com:liferay/" + getGitRepositoryName());
  187. }
  188. return _liferayRemoteGitBranch;
  189. }
  190. public String getLocalSenderBranchName() {
  191. return JenkinsResultsParserUtil.combine(
  192. getSenderUsername(), "-", getNumber(), "-", getSenderBranchName());
  193. }
  194. public String getNumber() {
  195. return String.valueOf(_number);
  196. }
  197. public String getOwnerUsername() {
  198. return _ownerUsername;
  199. }
  200. public String getReceiverUsername() {
  201. JSONObject baseJSONObject = _jsonObject.getJSONObject("base");
  202. JSONObject userJSONObject = baseJSONObject.getJSONObject("user");
  203. return userJSONObject.getString("login");
  204. }
  205. public String getSenderBranchName() {
  206. JSONObject headJSONObject = _jsonObject.getJSONObject("head");
  207. return headJSONObject.getString("ref");
  208. }
  209. public String getSenderRemoteURL() {
  210. return JenkinsResultsParserUtil.combine(
  211. "git@github.com:", getSenderUsername(), "/",
  212. getGitHubRemoteGitRepositoryName());
  213. }
  214. public String getSenderSHA() {
  215. JSONObject headJSONObject = _jsonObject.getJSONObject("head");
  216. return headJSONObject.getString("sha");
  217. }
  218. public JSONArray getSenderSHAStatuses() {
  219. JSONArray statusesJSONArray = null;
  220. try {
  221. statusesJSONArray = JenkinsResultsParserUtil.toJSONArray(
  222. _jsonObject.getString("statuses_url"));
  223. }
  224. catch (IOException ioException) {
  225. throw new RuntimeException(ioException);
  226. }
  227. return statusesJSONArray;
  228. }
  229. public String getSenderUsername() {
  230. JSONObject headJSONObject = _jsonObject.getJSONObject("head");
  231. JSONObject userJSONObject = headJSONObject.getJSONObject("user");
  232. return userJSONObject.getString("login");
  233. }
  234. public String getState() {
  235. return _jsonObject.getString("state");
  236. }
  237. public TestSuiteStatus getTestSuiteStatus() {
  238. return _testSuiteStatus;
  239. }
  240. public String getTitle() {
  241. return _jsonObject.getString("title");
  242. }
  243. public String getUpstreamBranchName() {
  244. JSONObject baseJSONObject = _jsonObject.getJSONObject("base");
  245. return baseJSONObject.getString("ref");
  246. }
  247. public String getUpstreamBranchSHA() {
  248. JSONObject baseJSONObject = _jsonObject.getJSONObject("base");
  249. return baseJSONObject.getString("sha");
  250. }
  251. public String getURL() {
  252. return JenkinsResultsParserUtil.getGitHubApiUrl(
  253. _gitHubRemoteGitRepositoryName, _ownerUsername, "pulls/" + _number);
  254. }
  255. public boolean hasLabel(String labelName) {
  256. for (GitHubRemoteGitRepository.Label label : _labels) {
  257. if (labelName.equals(label.getName())) {
  258. return true;
  259. }
  260. }
  261. return false;
  262. }
  263. public boolean isAutoCloseCommentAvailable() {
  264. if (_autoCloseCommentAvailable != null) {
  265. return _autoCloseCommentAvailable;
  266. }
  267. List<Comment> comments = getComments();
  268. for (Comment comment : comments) {
  269. String commentBody = comment.getBody();
  270. if (commentBody.contains("auto-close=\"false\"")) {
  271. _autoCloseCommentAvailable = true;
  272. return _autoCloseCommentAvailable;
  273. }
  274. }
  275. _autoCloseCommentAvailable = false;
  276. return _autoCloseCommentAvailable;
  277. }
  278. public void refresh() {
  279. try {
  280. _jsonObject = JenkinsResultsParserUtil.toJSONObject(
  281. getURL(), false);
  282. JSONArray commitsJSONArray = JenkinsResultsParserUtil.toJSONArray(
  283. _jsonObject.getString("commits_url"));
  284. JSONObject firstCommitJSONObject = commitsJSONArray.getJSONObject(
  285. 0);
  286. JSONArray parentsJSONArray = firstCommitJSONObject.getJSONArray(
  287. "parents");
  288. JSONObject firstParentJSONObject = parentsJSONArray.getJSONObject(
  289. 0);
  290. _commonParentSHA = firstParentJSONObject.getString("sha");
  291. _labels.clear();
  292. JSONArray labelJSONArray = _jsonObject.getJSONArray("labels");
  293. for (int i = 0; i < labelJSONArray.length(); i++) {
  294. JSONObject labelJSONObject = labelJSONArray.getJSONObject(i);
  295. _labels.add(
  296. new GitHubRemoteGitRepository.Label(
  297. labelJSONObject, getGitHubRemoteGitRepository()));
  298. }
  299. }
  300. catch (IOException ioException) {
  301. throw new RuntimeException(ioException);
  302. }
  303. }
  304. public void removeComment(Comment comment) {
  305. removeComment(comment.getId());
  306. }
  307. public void removeComment(String id) {
  308. String editCommentURL = _jsonObject.getString("issue_url");
  309. editCommentURL = editCommentURL.replaceFirst("issues/\\d+", "issues");
  310. try {
  311. _toString(
  312. JenkinsResultsParserUtil.combine(
  313. editCommentURL, "/comments/", id),
  314. HttpRequestMethod.DELETE);
  315. }
  316. catch (IOException ioException) {
  317. throw new RuntimeException(
  318. "Unable to delete comment in GitHub pull request " + getURL(),
  319. ioException);
  320. }
  321. }
  322. public void removeLabel(String labelName) {
  323. if (!hasLabel(labelName)) {
  324. return;
  325. }
  326. String path = JenkinsResultsParserUtil.combine(
  327. "issues/", getNumber(), "/labels/", labelName);
  328. String gitHubApiUrl = JenkinsResultsParserUtil.getGitHubApiUrl(
  329. getGitHubRemoteGitRepositoryName(), getOwnerUsername(), path);
  330. try {
  331. _toString(gitHubApiUrl, HttpRequestMethod.DELETE);
  332. refresh();
  333. }
  334. catch (IOException ioException) {
  335. System.out.println("Unable to remove label " + labelName);
  336. ioException.printStackTrace();
  337. }
  338. }
  339. public void resetAutoCloseCommentAvailable() {
  340. _autoCloseCommentAvailable = null;
  341. }
  342. public void setTestSuiteStatus(TestSuiteStatus testSuiteStatus) {
  343. setTestSuiteStatus(testSuiteStatus, null);
  344. }
  345. public void setTestSuiteStatus(
  346. TestSuiteStatus testSuiteStatus, String targetURL) {
  347. _testSuiteStatus = testSuiteStatus;
  348. StringBuilder sb = new StringBuilder();
  349. sb.append("ci:test");
  350. if (!_testSuiteName.equals(_NAME_TEST_SUITE_DEFAULT)) {
  351. sb.append(":");
  352. sb.append(_testSuiteName);
  353. }
  354. sb.append(" ");
  355. String testSuiteLabelPrefix = sb.toString();
  356. List<String> oldLabelNames = new ArrayList<>();
  357. for (GitHubRemoteGitRepository.Label label : getLabels()) {
  358. String name = label.getName();
  359. if (name.startsWith(testSuiteLabelPrefix)) {
  360. oldLabelNames.add(label.getName());
  361. }
  362. }
  363. for (String oldLabelName : oldLabelNames) {
  364. removeLabel(oldLabelName);
  365. }
  366. sb.append(" - ");
  367. sb.append(StringUtils.lowerCase(testSuiteStatus.toString()));
  368. GitHubRemoteGitRepository gitHubRemoteGitRepository =
  369. getGitHubRemoteGitRepository();
  370. GitHubRemoteGitRepository.Label testSuiteLabel =
  371. gitHubRemoteGitRepository.getLabel(sb.toString());
  372. if ((testSuiteLabel == null) &&
  373. gitHubRemoteGitRepository.addLabel(
  374. testSuiteStatus.getColor(), "", sb.toString())) {
  375. testSuiteLabel = gitHubRemoteGitRepository.getLabel(sb.toString());
  376. }
  377. addLabel(testSuiteLabel);
  378. if (targetURL == null) {
  379. return;
  380. }
  381. if (testSuiteStatus == TestSuiteStatus.MISSING) {
  382. return;
  383. }
  384. GitHubRemoteGitCommit gitHubRemoteGitCommit =
  385. getGitHubRemoteGitCommit();
  386. GitHubRemoteGitCommit.Status status =
  387. GitHubRemoteGitCommit.Status.valueOf(testSuiteStatus.toString());
  388. String context = _NAME_TEST_SUITE_DEFAULT;
  389. if (!_testSuiteName.equals(_NAME_TEST_SUITE_DEFAULT)) {
  390. context = "liferay/ci:test:" + _testSuiteName;
  391. }
  392. sb = new StringBuilder();
  393. sb.append("\"ci:test");
  394. if (!_testSuiteName.equals(_NAME_TEST_SUITE_DEFAULT)) {
  395. sb.append(":");
  396. sb.append(_testSuiteName);
  397. }
  398. sb.append("\"");
  399. if ((testSuiteStatus == TestSuiteStatus.ERROR) ||
  400. (testSuiteStatus == TestSuiteStatus.FAILURE)) {
  401. sb.append(" has FAILED.");
  402. }
  403. else if (testSuiteStatus == TestSuiteStatus.PENDING) {
  404. sb.append(" is running.");
  405. }
  406. else if (testSuiteStatus == TestSuiteStatus.SUCCESS) {
  407. sb.append(" has PASSED.");
  408. }
  409. gitHubRemoteGitCommit.setStatus(
  410. status, context, sb.toString(), targetURL);
  411. }
  412. public Comment updateComment(Comment comment) {
  413. return updateComment(comment.getBody(), comment.getId());
  414. }
  415. public Comment updateComment(String body, String id) {
  416. body = body.replaceAll("(\\>)\\s+(\\<)", "$1$2");
  417. body = body.replace("&quot;", "\\&quot;");
  418. try {
  419. String editCommentURL = _jsonObject.getString("issue_url");
  420. editCommentURL = editCommentURL.replaceFirst(
  421. "issues/\\d+", "issues");
  422. return new Comment(
  423. JenkinsResultsParserUtil.toJSONObject(
  424. JenkinsResultsParserUtil.combine(
  425. editCommentURL, "/comments/", id),
  426. false, HttpRequestMethod.PATCH));
  427. }
  428. catch (IOException ioException) {
  429. throw new RuntimeException(
  430. "Unable to update comment in GitHub pull request " + getURL(),
  431. ioException);
  432. }
  433. }
  434. public static class Comment {
  435. public Comment(JSONObject commentJSONObject) {
  436. _commentJSONObject = commentJSONObject;
  437. }
  438. public String getBody() {
  439. return _commentJSONObject.getString("body");
  440. }
  441. public Date getCreatedDate() {
  442. try {
  443. return _UtcIso8601SimpleDateFormat.parse(
  444. _commentJSONObject.getString("created_at"));
  445. }
  446. catch (ParseException parseException) {
  447. throw new RuntimeException(
  448. "Unable to parse created date " +
  449. _commentJSONObject.getString("created_at"),
  450. parseException);
  451. }
  452. }
  453. public String getId() {
  454. return String.valueOf(_commentJSONObject.getInt("id"));
  455. }
  456. public Date getModifiedDate() {
  457. try {
  458. return _UtcIso8601SimpleDateFormat.parse(
  459. _commentJSONObject.getString("modified_at"));
  460. }
  461. catch (ParseException parseException) {
  462. throw new RuntimeException(
  463. "Unable to parse modified date " +
  464. _commentJSONObject.getString("modified_at"),
  465. parseException);
  466. }
  467. }
  468. public String getUserLogin() {
  469. JSONObject userJSONObject = _commentJSONObject.getJSONObject(
  470. "user");
  471. return userJSONObject.getString("login");
  472. }
  473. private static final SimpleDateFormat _UtcIso8601SimpleDateFormat;
  474. static {
  475. SimpleDateFormat simpleDateFormat = new SimpleDateFormat(
  476. "yyyy-MM-dd'T'HH:mm:ss'Z'");
  477. simpleDateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
  478. _UtcIso8601SimpleDateFormat = simpleDateFormat;
  479. }
  480. private final JSONObject _commentJSONObject;
  481. }
  482. public static enum TestSuiteStatus {
  483. ERROR("fccdcc"), FAILURE("fccdcc"), MISSING("eeeeee"),
  484. PENDING("fff4c9"), SUCCESS("c7e8cb");
  485. public String getColor() {
  486. return _color;
  487. }
  488. private TestSuiteStatus(String color) {
  489. _color = color;
  490. }
  491. private final String _color;
  492. }
  493. protected String getIssueURL() {
  494. return _jsonObject.getString("issue_url");
  495. }
  496. protected void updateGithub() {
  497. JSONObject jsonObject = new JSONObject();
  498. List<String> labelNames = new ArrayList<>();
  499. for (GitHubRemoteGitRepository.Label label : _labels) {
  500. labelNames.add(label.getName());
  501. }
  502. jsonObject.put("labels", labelNames);
  503. try {
  504. JenkinsResultsParserUtil.toJSONObject(
  505. getIssueURL(), jsonObject.toString());
  506. }
  507. catch (IOException ioException) {
  508. throw new RuntimeException(ioException);
  509. }
  510. }
  511. private static String _toString(
  512. String url, HttpRequestMethod httpRequestMethod)
  513. throws IOException {
  514. return JenkinsResultsParserUtil.toString(
  515. url, true, 10, httpRequestMethod, null, 30, 5000, null);
  516. }
  517. private static String _toString(String url, String postContent)
  518. throws IOException {
  519. return JenkinsResultsParserUtil.toString(
  520. url, false, 10, null, postContent, 30, 5000, null);
  521. }
  522. private static final String _NAME_TEST_SUITE_DEFAULT = "default";
  523. private static final Pattern _gitHubPullRequestURLPattern = Pattern.compile(
  524. JenkinsResultsParserUtil.combine(
  525. "https://github.com/(?<owner>[^/]+)/",
  526. "(?<gitHubRemoteGitRepositoryName>[^/]+)/pull/(?<number>\\d+)"));
  527. private Boolean _autoCloseCommentAvailable;
  528. private String _commonParentSHA;
  529. private GitHubRemoteGitRepository _gitHubRemoteGitRepository;
  530. private String _gitHubRemoteGitRepositoryName;
  531. private JSONObject _jsonObject;
  532. private final List<GitHubRemoteGitRepository.Label> _labels =
  533. new ArrayList<>();
  534. private RemoteGitBranch _liferayRemoteGitBranch;
  535. private Integer _number;
  536. private String _ownerUsername;
  537. private final String _testSuiteName;
  538. private TestSuiteStatus _testSuiteStatus = TestSuiteStatus.MISSING;
  539. }