From f1216ea80a2593f1d5180ad1a3f6051dcd63c174 Mon Sep 17 00:00:00 2001 From: Foivos Zakkak Date: Wed, 20 Dec 2023 10:21:44 +0200 Subject: [PATCH] [CI] Report synchronization workflow failures on GitHub issues --- .github/quarkus-ecosystem-issue.java | 128 +++++++++++++++------ .github/workflows/github-issue-updater.yml | 2 +- .github/workflows/repo-sync.yml | 4 + 3 files changed, 99 insertions(+), 35 deletions(-) diff --git a/.github/quarkus-ecosystem-issue.java b/.github/quarkus-ecosystem-issue.java index 58d468ca63bc..1062cd58fb53 100644 --- a/.github/quarkus-ecosystem-issue.java +++ b/.github/quarkus-ecosystem-issue.java @@ -31,6 +31,8 @@ import java.io.UncheckedIOException; import java.util.HashMap; import java.util.List; +import java.util.function.BiConsumer; +import java.util.function.Function; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; @@ -41,18 +43,20 @@ description = "Takes care of updating an issue depending on the status of the build") class Report implements Runnable { - @Option(names = "token", description = "Github token to use when calling the Github API") + @Option(names = "token", description = "Github token to use when calling the Github API", required = true) private String token; - @Option(names = "thisRepo", description = "The repository for which we are reporting the CI status") + @Option(names = "thisRepo", description = "The repository for which we are reporting the CI status", required = true) private String thisRepo; - @Option(names = "runId", description = "The ID of the Github Action run for which we are reporting the CI status") + @Option(names = "runId", description = "The ID of the Github Action run for which we are reporting the CI status", required = true) private String runId; @Option(names = "--dry-run", description = "Whether to actually update the issue or not") private boolean dryRun; + private final HashMap issues = new HashMap<>(); + @Override public void run() { try { @@ -68,7 +72,6 @@ public void run() { System.exit(0); } - final HashMap issues = new HashMap<>(); final HashMap> failedMandrelJobs = new HashMap<>(); // Get the github issue number and repository from the logs @@ -102,36 +105,7 @@ public void run() { listJobs.forEach(job -> { // Each configuration starts with the Set distribution job if (job.getName().contains("Set distribution")) { - String fullContent = getJobsLogs(job, "issue-number", "issue-repo"); - if (!fullContent.isEmpty()) { - // Get the issue number and repository for mandrel issues - Matcher issueNumberMatcher = Pattern.compile(" issue-number: (\\d+)").matcher(fullContent); - Matcher issueRepoMatcher = Pattern.compile(" issue-repo: (.*)").matcher(fullContent); - if (issueNumberMatcher.find() && issueRepoMatcher.find()) { - int issueNumber = Integer.parseInt(issueNumberMatcher.group(1)); - String issueRepo = issueRepoMatcher.group(1); - - System.out.println(String.format("Found issue https://github.com/%s/issues/%s in logs for job %s", issueRepo, issueNumber, job.getName())); - try { - GHRepository issueRepository = github.getRepository(issueRepo); - GHIssue issue = issueRepository.getIssue(issueNumber); - if (issue == null) { - System.out.println(String.format("Unable to find the issue %s in project %s", issueNumber, issueRepo)); - System.exit(-1); - } else { - System.out.println(String.format("Report issue found: %s - %s", issue.getTitle(), issue.getHtmlUrl().toString())); - System.out.println(String.format("The issue is currently %s", issue.getState().toString())); - Object oldIssue = issues.put(issue, job.getName().split(" / ")[0]); - if (oldIssue != null) { - System.out.println("WARNING: The issue has already been seen, please check the workflow configuration"); - }; - } - } catch (IOException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - } - } + processLogs(github, job, this::processITJobs, "issue-number", "issue-repo"); } else if (job.getConclusion().equals(Conclusion.FAILURE) && (job.getName().contains("Q IT") || job.getName().contains("Mandrel build"))) { for (GHIssue issue: issues.keySet()) { if (job.getName().startsWith(issues.get(issue))) { @@ -206,6 +180,8 @@ public void run() { } } + } else if (job.getName().startsWith("Keep graal/master in sync")) { + processLogs(github, job, this::processSyncJobs, "issue-number", "issue-repo"); } }); @@ -252,6 +228,90 @@ public void run() { } } + + private void processLogs(GitHub github, GHWorkflowJob job, BiConsumer process, String... filters) { + String fullContent = getJobsLogs(job, filters); + if (fullContent.isEmpty()) { + return; + } + // Get the issue number and repository for mandrel issues + Matcher issueNumberMatcher = Pattern.compile(" issue-number: (\\d+)").matcher(fullContent); + Matcher issueRepoMatcher = Pattern.compile(" issue-repo: (.*)").matcher(fullContent); + if (issueNumberMatcher.find() && issueRepoMatcher.find()) { + int issueNumber = Integer.parseInt(issueNumberMatcher.group(1)); + String issueRepo = issueRepoMatcher.group(1); + + System.out.println(String.format("Found issue https://github.com/%s/issues/%s in logs for job %s", issueRepo, issueNumber, job.getName())); + try { + GHRepository issueRepository = github.getRepository(issueRepo); + GHIssue issue = issueRepository.getIssue(issueNumber); + process.accept(issue, job); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + } + + private void processITJobs(GHIssue issue, GHWorkflowJob job) { + if (issue == null) { + System.out.println(String.format("Unable to find the issue %s in project %s", issue.getNumber(), issue.getRepository().getName())); + System.exit(-1); + } else { + System.out.println(String.format("Report issue found: %s - %s", issue.getTitle(), issue.getHtmlUrl().toString())); + System.out.println(String.format("The issue is currently %s", issue.getState().toString())); + Object oldIssue = issues.put(issue, job.getName().split(" / ")[0]); + if (oldIssue != null) { + System.out.println("WARNING: The issue has already been seen, please check the workflow configuration"); + }; + } + } + + private void processSyncJobs(GHIssue issue, GHWorkflowJob job) { + try { + if (issue == null) { + System.out.println(String.format("Unable to find the issue %s in project %s", issue.getNumber(), issue.getRepository().getName())); + System.exit(-1); + } else { + System.out.println(String.format("Report issue found: %s - %s", issue.getTitle(), issue.getHtmlUrl().toString())); + System.out.println(String.format("The issue is currently %s", issue.getState().toString())); + if (job.getConclusion().equals(Conclusion.SUCCESS)) { + if (isOpen(issue)) { + String comment = String.format("Synchronization fixed:\n* Link to latest CI run: https://github.com/%s/actions/runs/%s", thisRepo, runId); + if (!dryRun) { + // close issue with a comment + issue.comment(comment); + issue.close(); + } + System.out.println(String.format("Comment added on issue %s\n%s\n, the issue has also been closed", issue.getHtmlUrl().toString(), comment)); + } else { + System.out.println("Nothing to do - the synchronization passed and the issue is already closed"); + } + } else if (job.getConclusion().equals(Conclusion.FAILURE)) { + StringBuilder sb = new StringBuilder(); + if (isOpen(issue)) { + sb.append("The synchronization is still failing!\n\n"); + } else { + sb.append("Unfortunately, the synchronization failed!\n\n"); + if (!dryRun) { + issue.reopen(); + } + System.out.println("The issue has been re-opened"); + } + sb.append(String.format("Link to failing CI run: %s", job.getHtmlUrl())); + String comment = sb.toString(); + if (!dryRun) { + issue.comment(comment); + } + System.out.println(String.format("\nComment added on issue %s\n\n%s\n", issue.getHtmlUrl().toString(), comment)); + } + } + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + private void processFailedJob(StringBuilder sb, GHWorkflowJob job) { sb.append(String.format("* [%s](%s)\n", job.getName(), job.getHtmlUrl())); GHWorkflowJob.Step step = job.getSteps().stream().filter(s -> s.getConclusion().equals(Conclusion.FAILURE)).findFirst().get(); diff --git a/.github/workflows/github-issue-updater.yml b/.github/workflows/github-issue-updater.yml index f80bc2735f08..69b0ecc308be 100644 --- a/.github/workflows/github-issue-updater.yml +++ b/.github/workflows/github-issue-updater.yml @@ -2,7 +2,7 @@ name: GitHub Issue Updater on: workflow_run: - workflows: ["Nightly CI", "Weekly CI"] + workflows: ["Nightly CI", "Weekly CI", "Repo Sync"] types: - completed branches: diff --git a/.github/workflows/repo-sync.yml b/.github/workflows/repo-sync.yml index b593d042f38c..a395974b3e6b 100644 --- a/.github/workflows/repo-sync.yml +++ b/.github/workflows/repo-sync.yml @@ -22,6 +22,10 @@ jobs: with: ref: graal/master fetch-depth: 0 + - name: Echo issue repo and number + run: | + echo "issue-repo: graalvm/mandrel" + echo "issue-number: 649" - name: Sync branch run: | git config --local user.email "fzakkak@redhat.com"