diff --git a/bots/pr/src/main/java/org/openjdk/skara/bots/pr/CheckRun.java b/bots/pr/src/main/java/org/openjdk/skara/bots/pr/CheckRun.java index de1b1664a..5c724032a 100644 --- a/bots/pr/src/main/java/org/openjdk/skara/bots/pr/CheckRun.java +++ b/bots/pr/src/main/java/org/openjdk/skara/bots/pr/CheckRun.java @@ -110,17 +110,48 @@ private boolean isTargetBranchAllowed() { return matcher.matches(); } - private List issues() { + private List issues(boolean withCsr) { var issue = Issue.fromStringRelaxed(pr.title()); if (issue.isPresent()) { var issues = new ArrayList(); issues.add(issue.get()); issues.addAll(SolvesTracker.currentSolved(pr.repository().forge().currentUser(), comments)); + if (withCsr) { + getCsrIssue(issue.get()).ifPresent(issues::add); + } return issues; } return List.of(); } + private Optional getCsrIssue(Issue issue) { + var issueProject = issueProject(); + if (issueProject == null) { + return Optional.empty(); + } + var jbsIssue = issueProject.issue(issue.shortId()); + if (jbsIssue.isEmpty()) { + return Optional.empty(); + } + org.openjdk.skara.issuetracker.Issue csr = null; + for (var link : jbsIssue.get().links()) { + var relationship = link.relationship(); + if (relationship.isEmpty() || !relationship.get().equals("csr for")) { + continue; + } + csr = link.issue().orElse(null); + if (csr == null) { + log.warning("The CSR " + link + " of the issue " + issue + " does not exist"); + } else { + break; + } + } + if (csr != null) { + return Issue.fromStringRelaxed(csr.id() + ": " + csr.title()); + } + return Optional.empty(); + } + private IssueProject issueProject() { return workItem.bot.issueProject(); } @@ -182,7 +213,7 @@ private Map blockingIntegrationLabels() { private List botSpecificIntegrationBlockers() { var ret = new ArrayList(); - var issues = issues(); + var issues = issues(false); var issueProject = issueProject(); if (issueProject != null) { for (var currentIssue : issues) { @@ -454,7 +485,7 @@ private String getStatusMessage(List comments, List reviews, Pu progressBody.append(warningListToText(integrationBlockers)); } - var issues = issues(); + var issues = issues(true); var issueProject = issueProject(); if (issueProject != null && !issues.isEmpty()) { progressBody.append("\n\n### Issue"); @@ -480,6 +511,10 @@ private String getStatusMessage(List comments, List reviews, Pu progressBody.append(iss.get().webUrl()); progressBody.append("): "); progressBody.append(iss.get().title()); + var issueType = iss.get().properties().get("issuetype"); + if (issueType != null && "CSR".equals(issueType.asString())) { + progressBody.append(" (**CSR**)"); + } if (!relaxedEquals(iss.get().title(), currentIssue.description())) { progressBody.append(" ⚠️ Title mismatch between PR and JBS."); setExpiration(Duration.ofMinutes(10)); diff --git a/bots/pr/src/test/java/org/openjdk/skara/bots/pr/CheckTests.java b/bots/pr/src/test/java/org/openjdk/skara/bots/pr/CheckTests.java index 16bc59caa..52ea7375d 100644 --- a/bots/pr/src/test/java/org/openjdk/skara/bots/pr/CheckTests.java +++ b/bots/pr/src/test/java/org/openjdk/skara/bots/pr/CheckTests.java @@ -24,6 +24,7 @@ import org.junit.jupiter.api.*; import org.openjdk.skara.forge.*; +import org.openjdk.skara.issuetracker.Link; import org.openjdk.skara.json.JSON; import org.openjdk.skara.test.*; @@ -1126,6 +1127,53 @@ void issueInSummaryExternalUpdate(TestInfo testInfo) throws IOException { } } + @Test + void issueWithCsr(TestInfo testInfo) throws IOException { + try (var credentials = new HostCredentials(testInfo); + var tempFolder = new TemporaryDirectory()) { + var author = credentials.getHostedRepository(); + var reviewer = credentials.getHostedRepository(); + var bot = credentials.getHostedRepository(); + var issues = credentials.getIssueProject(); + var censusBuilder = credentials.getCensusBuilder() + .addAuthor(author.forge().currentUser().id()) + .addReviewer(reviewer.forge().currentUser().id()); + var checkBot = PullRequestBot.newBuilder().repo(bot).issueProject(issues) + .censusRepo(censusBuilder.build()).build(); + + // Populate the projects repository + var localRepo = CheckableRepository.init(tempFolder.path(), author.repositoryType(), + Path.of("appendable.txt"), Set.of("issues"), null); + var masterHash = localRepo.resolve("master").orElseThrow(); + localRepo.push(masterHash, author.url(), "master", true); + + var mainIssue = issues.createIssue("The main issue", List.of("main"), Map.of("issuetype", JSON.of("Bug"))); + var csrIssue = issues.createIssue("The csr issue", List.of("csr"), Map.of("issuetype", JSON.of("CSR"))); + + // Make a change with a corresponding PR + var editHash = CheckableRepository.appendAndCommit(localRepo); + localRepo.push(editHash, author.url(), "edit", true); + var pr = credentials.createPullRequest(author, "master", "edit", mainIssue.id() + ": " + mainIssue.title()); + + // PR should have one issue + TestBotRunner.runPeriodicItems(checkBot); + assertTrue(pr.body().contains("### Issue")); + assertFalse(pr.body().contains("### Issues")); + assertTrue(pr.body().contains("The main issue")); + assertFalse(pr.body().contains("The csr issue (**CSR**)")); + + // Require CSR + mainIssue.addLink(Link.create(csrIssue, "csr for").build()); + pr.addComment("/csr"); + + // PR should have two issues + TestBotRunner.runPeriodicItems(checkBot); + assertTrue(pr.body().contains("### Issues")); + assertTrue(pr.body().contains("The main issue")); + assertTrue(pr.body().contains("The csr issue (**CSR**)")); + } + } + @Test void cancelCheck(TestInfo testInfo) throws IOException { try (var credentials = new HostCredentials(testInfo);