diff --git a/pom.xml b/pom.xml index e173bae..4ccc2a4 100644 --- a/pom.xml +++ b/pom.xml @@ -54,6 +54,10 @@ io.quarkus quarkus-smallrye-health + + io.quarkus + quarkus-rest-client-reactive-jackson + org.glassfish jakarta.el diff --git a/src/main/java/io/quarkus/bot/AnalyzeWorkflowRunResults.java b/src/main/java/io/quarkus/bot/AnalyzeWorkflowRunResults.java index 869ff60..a321266 100644 --- a/src/main/java/io/quarkus/bot/AnalyzeWorkflowRunResults.java +++ b/src/main/java/io/quarkus/bot/AnalyzeWorkflowRunResults.java @@ -7,11 +7,12 @@ import java.util.Date; import java.util.List; import java.util.Optional; -import java.util.concurrent.Callable; import java.util.stream.Collectors; import javax.inject.Inject; +import io.quarkus.bot.workflow.ArtifactsAreReady; +import io.quarkus.bot.workflow.GHWorkflowJobComparator; import org.apache.commons.lang3.StringUtils; import org.awaitility.Awaitility; import org.awaitility.core.ConditionTimeoutException; @@ -107,7 +108,7 @@ void analyzeWorkflowResults(@WorkflowRun.Completed GHEventPayload.WorkflowRun wo List surefireReportsArtifacts = artifacts .stream() .filter(a -> a.getName().startsWith(WorkflowConstants.BUILD_REPORTS_ARTIFACT_PREFIX)) - .sorted((a1, a2) -> a1.getName().compareTo(a2.getName())) + .sorted(Comparator.comparing(GHArtifact::getName)) .collect(Collectors.toList()); List jobs = workflowRun.listJobs().toList() @@ -115,7 +116,7 @@ void analyzeWorkflowResults(@WorkflowRun.Completed GHEventPayload.WorkflowRun wo .sorted(GHWorkflowJobComparator.INSTANCE) .collect(Collectors.toList()); - Optional workflowReportOptional = workflowRunAnalyzer.getReport(workflowRun, pullRequest, jobs, + Optional workflowReportOptional = workflowRunAnalyzer.getErrorReport(workflowRun, pullRequest, jobs, surefireReportsArtifacts); if (workflowReportOptional.isEmpty()) { return; @@ -230,42 +231,4 @@ private Optional createCheckRun(GHWorkflowRun workflowRun, GHPullReq } } - private final static class GHWorkflowJobComparator implements Comparator { - - private static final GHWorkflowJobComparator INSTANCE = new GHWorkflowJobComparator(); - - private static final String INITIAL_JDK_PREFIX = "Initial JDK "; - - @Override - public int compare(GHWorkflowJob o1, GHWorkflowJob o2) { - if (o1.getName().startsWith(INITIAL_JDK_PREFIX) && !o2.getName().startsWith(INITIAL_JDK_PREFIX)) { - return -1; - } - if (!o1.getName().startsWith(INITIAL_JDK_PREFIX) && o2.getName().startsWith(INITIAL_JDK_PREFIX)) { - return 1; - } - - return o1.getName().compareTo(o2.getName()); - } - - } - - private final static class ArtifactsAreReady implements Callable { - private final GHWorkflowRun workflowRun; - private List artifacts; - - private ArtifactsAreReady(GHWorkflowRun workflowRun) { - this.workflowRun = workflowRun; - } - - @Override - public Boolean call() throws Exception { - artifacts = workflowRun.listArtifacts().toList(); - return !artifacts.isEmpty(); - } - - public List getArtifacts() { - return artifacts; - } - } } diff --git a/src/main/java/io/quarkus/bot/PushTestResults.java b/src/main/java/io/quarkus/bot/PushTestResults.java new file mode 100644 index 0000000..f99e4e6 --- /dev/null +++ b/src/main/java/io/quarkus/bot/PushTestResults.java @@ -0,0 +1,233 @@ +package io.quarkus.bot; + +import io.quarkiverse.githubapp.event.WorkflowRun; +import io.quarkus.bot.config.QuarkusBotConfig; +import io.quarkus.bot.test.JobResult; +import io.quarkus.bot.test.QuarkusStatusClient; +import io.quarkus.bot.test.TestResult; +import io.quarkus.bot.test.WorkflowResult; +import io.quarkus.bot.workflow.ArtifactsAreReady; +import io.quarkus.bot.workflow.GHWorkflowJobComparator; +import io.quarkus.bot.workflow.StackTraceUtils; +import io.quarkus.bot.workflow.WorkflowConstants; +import io.quarkus.bot.workflow.WorkflowReportFormatter; +import io.quarkus.bot.workflow.WorkflowRunAnalyzer; +import io.quarkus.bot.workflow.report.WorkflowReport; +import io.quarkus.bot.workflow.report.WorkflowReportJob; +import io.quarkus.bot.workflow.report.WorkflowReportModule; +import io.quarkus.bot.workflow.report.WorkflowReportTestCase; +import org.apache.commons.lang3.StringUtils; +import org.apache.maven.plugins.surefire.report.ReportTestCase; +import org.apache.maven.plugins.surefire.report.ReportTestSuite; +import org.awaitility.Awaitility; +import org.awaitility.core.ConditionTimeoutException; +import org.eclipse.microprofile.rest.client.inject.RestClient; +import org.jboss.logging.Logger; +import org.kohsuke.github.GHArtifact; +import org.kohsuke.github.GHCheckRun; +import org.kohsuke.github.GHCheckRun.AnnotationLevel; +import org.kohsuke.github.GHCheckRunBuilder; +import org.kohsuke.github.GHCheckRunBuilder.Annotation; +import org.kohsuke.github.GHCheckRunBuilder.Output; +import org.kohsuke.github.GHEvent; +import org.kohsuke.github.GHEventPayload; +import org.kohsuke.github.GHIssueState; +import org.kohsuke.github.GHPullRequest; +import org.kohsuke.github.GHWorkflow; +import org.kohsuke.github.GHWorkflowJob; +import org.kohsuke.github.GHWorkflowRun; + +import javax.enterprise.context.ApplicationScoped; +import javax.inject.Inject; +import java.io.IOException; +import java.time.Duration; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +@ApplicationScoped +public class PushTestResults { + + private static final Logger LOG = Logger.getLogger(PushTestResults.class); + + private static final int GITHUB_FIELD_LENGTH_HARD_LIMIT = 65000; + + @Inject + WorkflowRunAnalyzer workflowRunAnalyzer; + + @Inject + WorkflowReportFormatter workflowReportFormatter; + + @Inject + QuarkusBotConfig quarkusBotConfig; + + @RestClient + QuarkusStatusClient quarkusStatusClient; + + void analyzeWorkflowResults(@WorkflowRun.Completed GHEventPayload.WorkflowRun workflowRunPayload) + throws IOException { + GHWorkflowRun workflowRun = workflowRunPayload.getWorkflowRun(); + GHWorkflow workflow = workflowRunPayload.getWorkflow(); + + if (!WorkflowConstants.QUARKUS_CI_WORKFLOW_NAME.equals(workflow.getName())) { + return; + } + if (workflowRun.getEvent() != GHEvent.PULL_REQUEST) { + return; + } + + List artifacts; + try { + ArtifactsAreReady artifactsAreReady = new ArtifactsAreReady(workflowRun); + Awaitility.await() + .atMost(Duration.ofMinutes(5)) + .pollDelay(Duration.ofSeconds(5)) + .pollInterval(Duration.ofSeconds(30)) + .ignoreExceptions() + .until(artifactsAreReady); + artifacts = artifactsAreReady.getArtifacts(); + } catch (ConditionTimeoutException e) { + LOG.warn("Workflow run #" + workflowRun.getId() + + " - Unable to get the artifacts in a timely manner, ignoring them"); + return; + } + + Optional pullRequestOptional = getAssociatedPullRequest(workflowRun, artifacts); + if (pullRequestOptional.isEmpty()) { + LOG.error("Workflow run #" + workflowRun.getId() + " - Unable to find the associated pull request"); + return; + } + GHPullRequest pullRequest = pullRequestOptional.get(); + + HideOutdatedWorkflowRunResults.hideOutdatedWorkflowRunResults(quarkusBotConfig, pullRequest); + + if (pullRequest.isDraft()) { + return; + } + + List surefireReportsArtifacts = artifacts + .stream() + .filter(a -> a.getName().startsWith(WorkflowConstants.BUILD_REPORTS_ARTIFACT_PREFIX)) + .sorted((a1, a2) -> a1.getName().compareTo(a2.getName())) + .collect(Collectors.toList()); + + List jobs = workflowRun.listJobs().toList() + .stream() + .sorted(GHWorkflowJobComparator.INSTANCE) + .collect(Collectors.toList()); + + Optional workflowReportOptional = workflowRunAnalyzer.getTestReport(workflowRun, pullRequest, jobs, + surefireReportsArtifacts); + if (workflowReportOptional.isEmpty()) { + return; // the reason was logged by workflowRunAnalyzer + } + WorkflowReport workflowReport = workflowReportOptional.get(); + + List jobResults = new ArrayList<>(); + for (WorkflowReportJob job : workflowReport.getJobs()) { + List testResults = new ArrayList<>(); + for (WorkflowReportModule module : job.getModules()) { + for (ReportTestSuite reportTestSuite : module.getReportTestSuites()) { + for (ReportTestCase testCase : reportTestSuite.getTestCases()) { + if (!testCase.hasSkipped()) { + String testFullName = testCase.getFullName(); + TestResult result = new TestResult(testFullName, testCase.isSuccessful()); + testResults.add(result); + } + } + } + } + jobResults.add(new JobResult(job.getUrl(), job.getName(), testResults, job.getCompletedAt())); + } + + quarkusStatusClient.storeTestResults(new WorkflowResult(jobResults, workflowReport.getSha())); + } + + /** + * Unfortunately when the pull request is coming from a fork, the pull request is not in the payload + * so we use a dirty trick to get it. + * We use the sha as last resort as the workflow takes some time and the sha might not be associated to the pull request + * anymore. + */ + private Optional getAssociatedPullRequest(GHWorkflowRun workflowRun, List artifacts) + throws NumberFormatException, IOException { + Optional pullRequestNumberArtifact = artifacts.stream() + .filter(a -> a.getName().startsWith(WorkflowConstants.PULL_REQUEST_NUMBER_PREFIX)).findFirst(); + if (!pullRequestNumberArtifact.isEmpty()) { + GHPullRequest pullRequest = workflowRun.getRepository().getPullRequest( + Integer.valueOf( + pullRequestNumberArtifact.get().getName().replace(WorkflowConstants.PULL_REQUEST_NUMBER_PREFIX, + ""))); + return Optional.of(pullRequest); + } + + LOG.warn("Workflow run #" + workflowRun.getId() + " - Unable to get the pull request artifact, trying with sha"); + + List pullRequests = workflowRun.getRepository().queryPullRequests() + .state(GHIssueState.OPEN) + .head(workflowRun.getHeadRepository().getOwnerName() + ":" + workflowRun.getHeadBranch()) + .list().toList(); + if (!pullRequests.isEmpty()) { + return Optional.of(pullRequests.get(0)); + } + + return Optional.empty(); + } + + private Optional createCheckRun(GHWorkflowRun workflowRun, GHPullRequest pullRequest, + boolean artifactsAvailable, WorkflowReport workflowReport) { + if (!workflowReport.hasTestFailures() || quarkusBotConfig.isDryRun()) { + return Optional.empty(); + } + + try { + String name = "Build summary for " + workflowRun.getHeadSha(); + String summary = workflowReportFormatter.getCheckRunReportSummary(workflowReport, pullRequest, artifactsAvailable); + String checkRunReport = workflowReportFormatter.getCheckRunReport(workflowReport, true); + if (checkRunReport.length() > GITHUB_FIELD_LENGTH_HARD_LIMIT) { + checkRunReport = workflowReportFormatter.getCheckRunReport(workflowReport, false); + } + + Output checkRunOutput = new Output(name, summary).withText(checkRunReport); + + for (WorkflowReportJob workflowReportJob : workflowReport.getJobs()) { + if (!workflowReportJob.hasTestFailures()) { + continue; + } + + List annotatedWorkflowReportTestCases = workflowReportJob.getModules().stream() + .filter(m -> m.hasTestFailures()) + .flatMap(m -> m.getTestFailures().stream()) + .collect(Collectors.toList()); + + for (WorkflowReportTestCase workflowReportTestCase : annotatedWorkflowReportTestCases) { + checkRunOutput.add(new Annotation(workflowReportTestCase.getClassPath(), + StringUtils.isNumeric(workflowReportTestCase.getFailureErrorLine()) + ? Integer.valueOf(workflowReportTestCase.getFailureErrorLine()) + : 1, + AnnotationLevel.FAILURE, + StringUtils.isNotBlank(workflowReportTestCase.getFailureDetail()) ? StackTraceUtils + .firstLines(StackTraceUtils.abbreviate(workflowReportTestCase.getFailureDetail(), + GITHUB_FIELD_LENGTH_HARD_LIMIT), 3) + : "The test failed.") + .withTitle(StringUtils.abbreviate(workflowReportJob.getName(), 255)) + .withRawDetails( + StackTraceUtils.abbreviate(workflowReportTestCase.getFailureDetail(), + GITHUB_FIELD_LENGTH_HARD_LIMIT))); + } + } + + GHCheckRunBuilder checkRunBuilder = workflowRun.getRepository().createCheckRun(name, workflowRun.getHeadSha()) + .add(checkRunOutput) + .withConclusion(GHCheckRun.Conclusion.NEUTRAL) + .withCompletedAt(new Date()); + + return Optional.of(checkRunBuilder.create()); + } catch (Exception e) { + LOG.error("Pull request #" + pullRequest.getNumber() + " - Unable to create check run for test failures", e); + return Optional.empty(); + } + } +} diff --git a/src/main/java/io/quarkus/bot/test/JobResult.java b/src/main/java/io/quarkus/bot/test/JobResult.java new file mode 100644 index 0000000..d0b6449 --- /dev/null +++ b/src/main/java/io/quarkus/bot/test/JobResult.java @@ -0,0 +1,35 @@ +package io.quarkus.bot.test; + +import java.util.Date; +import java.util.List; + +public class JobResult { + private String jobUrl; + private String jobName; + private Date completedAt; + + private List tests; + + public JobResult(String jobUrl, String jobName, List tests, Date completedAt) { + this.jobUrl = jobUrl; + this.jobName = jobName; + this.tests = tests; + this.completedAt = completedAt; + } + + public String getJobUrl() { + return jobUrl; + } + + public String getJobName() { + return jobName; + } + + public List getTests() { + return tests; + } + + public Date getCompletedAt() { + return completedAt; + } +} diff --git a/src/main/java/io/quarkus/bot/test/QuarkusStatusClient.java b/src/main/java/io/quarkus/bot/test/QuarkusStatusClient.java new file mode 100644 index 0000000..c3bc787 --- /dev/null +++ b/src/main/java/io/quarkus/bot/test/QuarkusStatusClient.java @@ -0,0 +1,27 @@ +package io.quarkus.bot.test; + +import org.eclipse.microprofile.config.ConfigProvider; +import org.eclipse.microprofile.rest.client.annotation.ClientHeaderParam; +import org.eclipse.microprofile.rest.client.inject.RegisterRestClient; + +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.core.Response; + +@Path("/") +@RegisterRestClient(configKey = "quarkus-status") +public interface QuarkusStatusClient { + @POST + @Path("/test-results") + @ClientHeaderParam(name = "Authorization", value = "{basicAuthString}") + Response storeTestResults(WorkflowResult results); + + default String basicAuthString() { + String auth = ConfigProvider.getConfig() + .getOptionalValue("quarkus.status.auth-string", String.class) + .orElseThrow(() -> new IllegalStateException("No authorization string provided for quarkus-status client." + + " Please set `quarkus.status.auth-string` to the " + + "auth string to communicate with the quarkus-status application.")); + return String.format("Basic %s", auth); + } +} diff --git a/src/main/java/io/quarkus/bot/test/TestResult.java b/src/main/java/io/quarkus/bot/test/TestResult.java new file mode 100644 index 0000000..2046c07 --- /dev/null +++ b/src/main/java/io/quarkus/bot/test/TestResult.java @@ -0,0 +1,19 @@ +package io.quarkus.bot.test; + +public class TestResult { + private String name; + private boolean successful; + + public TestResult(String name, boolean successful) { + this.name = name; + this.successful = successful; + } + + public String getName() { + return name; + } + + public boolean isSuccessful() { + return successful; + } +} diff --git a/src/main/java/io/quarkus/bot/test/WorkflowResult.java b/src/main/java/io/quarkus/bot/test/WorkflowResult.java new file mode 100644 index 0000000..ec77435 --- /dev/null +++ b/src/main/java/io/quarkus/bot/test/WorkflowResult.java @@ -0,0 +1,24 @@ +package io.quarkus.bot.test; + +import java.util.List; + +public class WorkflowResult { + private String sha; + private List jobs; + + public WorkflowResult() { + } + + public WorkflowResult(List jobs, String sha) { + this.jobs = jobs; + this.sha = sha; + } + + public List getJobs() { + return jobs; + } + + public String getSha() { + return sha; + } +} diff --git a/src/main/java/io/quarkus/bot/workflow/ArtifactsAreReady.java b/src/main/java/io/quarkus/bot/workflow/ArtifactsAreReady.java new file mode 100644 index 0000000..5ef2e55 --- /dev/null +++ b/src/main/java/io/quarkus/bot/workflow/ArtifactsAreReady.java @@ -0,0 +1,26 @@ +package io.quarkus.bot.workflow; + +import org.kohsuke.github.GHArtifact; +import org.kohsuke.github.GHWorkflowRun; + +import java.util.List; +import java.util.concurrent.Callable; + +public final class ArtifactsAreReady implements Callable { + private final GHWorkflowRun workflowRun; + private List artifacts; + + public ArtifactsAreReady(GHWorkflowRun workflowRun) { + this.workflowRun = workflowRun; + } + + @Override + public Boolean call() throws Exception { + artifacts = workflowRun.listArtifacts().toList(); + return !artifacts.isEmpty(); + } + + public List getArtifacts() { + return artifacts; + } +} diff --git a/src/main/java/io/quarkus/bot/workflow/GHWorkflowJobComparator.java b/src/main/java/io/quarkus/bot/workflow/GHWorkflowJobComparator.java new file mode 100644 index 0000000..39a968e --- /dev/null +++ b/src/main/java/io/quarkus/bot/workflow/GHWorkflowJobComparator.java @@ -0,0 +1,25 @@ +package io.quarkus.bot.workflow; + +import org.kohsuke.github.GHWorkflowJob; + +import java.util.Comparator; + +public class GHWorkflowJobComparator implements Comparator { + + public static final GHWorkflowJobComparator INSTANCE = new GHWorkflowJobComparator(); + + private static final String INITIAL_JDK_PREFIX = "Initial JDK "; + + @Override + public int compare(GHWorkflowJob o1, GHWorkflowJob o2) { + if (o1.getName().startsWith(INITIAL_JDK_PREFIX) && !o2.getName().startsWith(INITIAL_JDK_PREFIX)) { + return -1; + } + if (!o1.getName().startsWith(INITIAL_JDK_PREFIX) && o2.getName().startsWith(INITIAL_JDK_PREFIX)) { + return 1; + } + + return o1.getName().compareTo(o2.getName()); + } + +} diff --git a/src/main/java/io/quarkus/bot/workflow/WorkflowRunAnalyzer.java b/src/main/java/io/quarkus/bot/workflow/WorkflowRunAnalyzer.java index a3f4187..1f6e04b 100644 --- a/src/main/java/io/quarkus/bot/workflow/WorkflowRunAnalyzer.java +++ b/src/main/java/io/quarkus/bot/workflow/WorkflowRunAnalyzer.java @@ -61,7 +61,87 @@ public class WorkflowRunAnalyzer { @Inject UrlShortener urlShortener; - public Optional getReport(GHWorkflowRun workflowRun, + public Optional getTestReport(GHWorkflowRun workflowRun, + GHPullRequest pullRequest, + List jobs, + List buildReportsArtifacts) throws IOException { + if (jobs.isEmpty()) { + LOG.error("Pull request #" + pullRequest.getNumber() + " - No jobs found"); + return Optional.empty(); + } + + GHRepository repository = workflowRun.getRepository(); + String pullRequestRepositoryName = pullRequest.getHead().getRepository().getFullName(); + String sha = workflowRun.getHeadSha(); + Path allBuildReportsDirectory = Files.createTempDirectory("build-reports-analyzer-"); + + try { + List workflowReportJobs = new ArrayList<>(); + + for (GHWorkflowJob job : jobs) { + Optional buildReportsArtifactOptional = buildReportsArtifacts.stream() + .filter(a -> a.getName().replace(WorkflowConstants.BUILD_REPORTS_ARTIFACT_PREFIX, "") + .equals(job.getName())) + .findFirst(); + + BuildReport buildReport = EMPTY_BUILD_REPORT; + List modules = Collections.emptyList(); + boolean errorDownloadingBuildReports = false; + if (buildReportsArtifactOptional.isPresent()) { + GHArtifact buildReportsArtifact = buildReportsArtifactOptional.get(); + Path jobDirectory = allBuildReportsDirectory.resolve(buildReportsArtifact.getName()); + try { + BuildReports buildReports = buildReportsUnarchiver.getBuildReports(buildReportsArtifact, jobDirectory); + + if (buildReports.getBuildReportPath() != null) { + buildReport = getBuildReport(pullRequest, buildReports.getBuildReportPath()); + } + + modules = getModules(pullRequest, buildReport, jobDirectory, buildReports.getTestResultsPaths(), + pullRequestRepositoryName, sha, true); + } catch (Exception e) { + errorDownloadingBuildReports = true; + LOG.error("Pull request #" + pullRequest.getNumber() + " - Unable to analyze build report for artifact " + + buildReportsArtifact.getName(), e); + } + } + + workflowReportJobs.add(new WorkflowReportJob(job.getName(), + getFailuresAnchor(job.getId()), + job.getConclusion(), + getFailingStep(job.getSteps()), + getJobUrl(job), + getRawLogsUrl(job, workflowRun.getHeadSha()), + buildReport, + modules, + errorDownloadingBuildReports, + job.getCompletedAt())); + } + + if (workflowReportJobs.isEmpty()) { + LOG.warn("Pull request #" + pullRequest.getNumber() + " - Report jobs empty"); + return Optional.empty(); + } + + WorkflowReport report = new WorkflowReport(sha, workflowReportJobs, + repository.getFullName().equals(pullRequestRepositoryName), + workflowRun.getConclusion(), workflowRun.getHtmlUrl().toString()); + + return Optional.of(report); + } finally { + try { + Files.walk(allBuildReportsDirectory) + .sorted(Comparator.reverseOrder()) + .map(Path::toFile) + .forEach(File::delete); + } catch (IOException e) { + LOG.error("Pull request #" + pullRequest.getNumber() + " - Unable to delete temp directory " + + allBuildReportsDirectory); + } + } + } + + public Optional getErrorReport(GHWorkflowRun workflowRun, GHPullRequest pullRequest, List jobs, List buildReportsArtifacts) throws IOException { @@ -81,7 +161,7 @@ public Optional getReport(GHWorkflowRun workflowRun, for (GHWorkflowJob job : jobs) { if (job.getConclusion() != Conclusion.FAILURE && job.getConclusion() != Conclusion.CANCELLED) { workflowReportJobs.add(new WorkflowReportJob(job.getName(), null, job.getConclusion(), null, null, null, - EMPTY_BUILD_REPORT, Collections.emptyList(), false)); + EMPTY_BUILD_REPORT, Collections.emptyList(), false, job.getCompletedAt())); continue; } @@ -103,10 +183,8 @@ public Optional getReport(GHWorkflowRun workflowRun, buildReport = getBuildReport(pullRequest, buildReports.getBuildReportPath()); } - modules = buildReportsArtifactOptional.isPresent() - ? getModules(pullRequest, buildReport, jobDirectory, buildReports.getTestResultsPaths(), - pullRequestRepositoryName, sha) - : Collections.emptyList(); + modules = getModules(pullRequest, buildReport, jobDirectory, buildReports.getTestResultsPaths(), + pullRequestRepositoryName, sha, false); } catch (Exception e) { errorDownloadingBuildReports = true; LOG.error("Pull request #" + pullRequest.getNumber() + " - Unable to analyze build report for artifact " @@ -122,7 +200,8 @@ public Optional getReport(GHWorkflowRun workflowRun, getRawLogsUrl(job, workflowRun.getHeadSha()), buildReport, modules, - errorDownloadingBuildReports)); + errorDownloadingBuildReports, + job.getCompletedAt())); } if (workflowReportJobs.isEmpty()) { @@ -167,7 +246,7 @@ private List getModules(GHPullRequest pullRequest, Path jobDirectory, Set testResultsPaths, String pullRequestRepository, - String sha) { + String sha, boolean keepSuccess) { List modules = new ArrayList<>(); Map moduleReportsMap = mapModuleReports(buildReport, testResultsPaths, jobDirectory); @@ -205,7 +284,7 @@ private List getModules(GHPullRequest pullRequest, reportTestSuites, workflowReportTestCases); - if (module.hasReportedFailures()) { + if (keepSuccess || module.hasReportedFailures()) { modules.add(module); } } diff --git a/src/main/java/io/quarkus/bot/workflow/report/WorkflowReportJob.java b/src/main/java/io/quarkus/bot/workflow/report/WorkflowReportJob.java index 116b73f..fbf99d8 100644 --- a/src/main/java/io/quarkus/bot/workflow/report/WorkflowReportJob.java +++ b/src/main/java/io/quarkus/bot/workflow/report/WorkflowReportJob.java @@ -1,5 +1,6 @@ package io.quarkus.bot.workflow.report; +import java.util.Date; import java.util.List; import java.util.stream.Collectors; @@ -25,10 +26,11 @@ public class WorkflowReportJob { private final List skippedModules; private final List modules; private final boolean errorDownloadingSurefireReports; + private final Date completedAt; public WorkflowReportJob(String name, String failuresAnchor, Conclusion conclusion, String failingStep, String url, String rawLogsUrl, BuildReport buildReport, List modules, - boolean errorDownloadingSurefireReports) { + boolean errorDownloadingSurefireReports, Date completedAt) { this.name = name; this.failuresAnchor = failuresAnchor; this.conclusion = conclusion; @@ -47,6 +49,7 @@ public WorkflowReportJob(String name, String failuresAnchor, Conclusion conclusi .collect(Collectors.toList()); this.modules = modules; this.errorDownloadingSurefireReports = errorDownloadingSurefireReports; + this.completedAt = completedAt; } public String getName() { @@ -61,6 +64,10 @@ public Conclusion getConclusion() { return conclusion; } + public Date getCompletedAt() { + return completedAt; + } + public String getConclusionEmoji() { // apparently, conclusion can sometimes be null... if (conclusion == null) { diff --git a/src/main/java/io/quarkus/bot/workflow/report/WorkflowReportModule.java b/src/main/java/io/quarkus/bot/workflow/report/WorkflowReportModule.java index c10713e..5025304 100644 --- a/src/main/java/io/quarkus/bot/workflow/report/WorkflowReportModule.java +++ b/src/main/java/io/quarkus/bot/workflow/report/WorkflowReportModule.java @@ -51,6 +51,10 @@ public List getTestFailures() { return failures; } + public List getReportTestSuites() { + return reportTestSuites; + } + public int getTestCount() { int testCount = 0; for (ReportTestSuite reportTestSuite : reportTestSuites) { diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 4dd85ba..720f6bc 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -5,6 +5,8 @@ quarkus.live-reload.instrumentation=false quarkus.qute.suffixes=md quarkus.qute.content-types."md"=text/markdown +%dev.quarkus-status/mp-rest/url=http://localhost:8282 + quarkus.openshift.labels."app"=quarkus-bot quarkus.openshift.annotations."kubernetes.io/tls-acme"=true quarkus.openshift.env.vars.QUARKUS_GITHUB_APP_APP_ID=90234 @@ -13,6 +15,8 @@ quarkus.openshift.env.vars.QUARKUS_GITHUB_APP_APP_NAME=quarkus-bot quarkus.openshift.env.secrets=quarkus-bot %dev.quarkus-bot.dry-run=true +# quarkus-bot:123 basic auth for the dev and test mode: +%dev.quarkus.status.auth-string=cXVhcmt1c2JvdDoxMjMK %test.quarkus-bot.dry-run=false %test.quarkus.github-app.app-id=0