diff --git a/src/main/java/io/quarkus/bot/PullRequestCommandHandler.java b/src/main/java/io/quarkus/bot/PullRequestCommandHandler.java new file mode 100644 index 00000000..01c33071 --- /dev/null +++ b/src/main/java/io/quarkus/bot/PullRequestCommandHandler.java @@ -0,0 +1,69 @@ +package io.quarkus.bot; + +import io.quarkiverse.githubapp.event.IssueComment; +import io.quarkus.bot.command.Command; +import io.quarkus.bot.config.QuarkusBotConfig; +import org.jboss.logging.Logger; +import org.kohsuke.github.GHEventPayload; +import org.kohsuke.github.GHIssue; +import org.kohsuke.github.GHOrganization; +import org.kohsuke.github.GHTeam; +import org.kohsuke.github.GHUser; + +import javax.enterprise.inject.Instance; +import javax.inject.Inject; +import java.io.IOException; +import java.util.Optional; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class PullRequestCommandHandler { + + private static final Logger LOG = Logger.getLogger(PullRequestCommandHandler.class); + + private static final String QUARKUS_BOT_NAME = "quarkus-bot"; + private static final String QUARKUS_TEAM_MEMBERS = "quarkus-push"; + private static final Pattern QUARKUS_BOT_MENTION = Pattern.compile("^@(?:quarkus-?)?bot\\s+([a-z _\\-]+)"); + + @Inject + QuarkusBotConfig quarkusBotConfig; + + @Inject + Instance commands; + + public void onComment(@IssueComment.Created @IssueComment.Edited GHEventPayload.IssueComment commentPayload) + throws IOException { + GHUser user = commentPayload.getComment().getUser(); + GHIssue issue = commentPayload.getIssue(); + + if (issue.isPullRequest() + && !QUARKUS_BOT_NAME.equals(user.getName())) { + Optional ghIssueConsumer = extractCommand(commentPayload.getComment().getBody()); + if (ghIssueConsumer.isPresent() && isQuarkusTeamMember(user, commentPayload.getOrganization())) { + if (!quarkusBotConfig.isDryRun()) { + ghIssueConsumer.get().run(issue); + } else { + LOG.info("Pull request #" + issue.getNumber() + " - Restart workflow."); + } + } + } + } + + private Optional extractCommand(String comment) { + Matcher matcher = QUARKUS_BOT_MENTION.matcher(comment); + if (matcher.matches()) { + String commandLabel = matcher.group(1); + return commands.stream().filter(command -> command.labels().contains(commandLabel)).findFirst(); + } + return Optional.empty(); + } + + private boolean isQuarkusTeamMember(GHUser user, GHOrganization organization) throws IOException { + GHTeam quarkusPushTeam = organization.getTeams().get(QUARKUS_TEAM_MEMBERS); + if (quarkusPushTeam == null) { + LOG.warn("Unable to find " + QUARKUS_TEAM_MEMBERS + " team in the organization."); + return false; + } + return quarkusPushTeam.hasMember(user); + } +} diff --git a/src/main/java/io/quarkus/bot/command/Command.java b/src/main/java/io/quarkus/bot/command/Command.java new file mode 100644 index 00000000..18fa1b6a --- /dev/null +++ b/src/main/java/io/quarkus/bot/command/Command.java @@ -0,0 +1,13 @@ +package io.quarkus.bot.command; + +import org.kohsuke.github.GHIssue; + +import java.util.List; + +public interface Command { + + List labels(); + + void run(GHIssue issue); + +} diff --git a/src/main/java/io/quarkus/bot/command/RerunWorkflowCommand.java b/src/main/java/io/quarkus/bot/command/RerunWorkflowCommand.java new file mode 100644 index 00000000..1a22533f --- /dev/null +++ b/src/main/java/io/quarkus/bot/command/RerunWorkflowCommand.java @@ -0,0 +1,56 @@ +package io.quarkus.bot.command; + +import io.quarkus.bot.workflow.WorkflowConstants; +import org.jboss.logging.Logger; +import org.kohsuke.github.GHIssue; +import org.kohsuke.github.GHPullRequest; +import org.kohsuke.github.GHRepository; +import org.kohsuke.github.GHWorkflowRun; + +import javax.enterprise.context.ApplicationScoped; +import java.io.IOException; +import java.util.Arrays; +import java.util.Comparator; +import java.util.List; +import java.util.Optional; + +@ApplicationScoped +public class RerunWorkflowCommand implements Command { + + private static final Logger LOG = Logger.getLogger(RerunWorkflowCommand.class); + + @Override + public List labels() { + return Arrays.asList("test", "retest"); + } + + @Override + public void run(GHIssue issue) { + try { + GHRepository repository = issue.getRepository(); + GHPullRequest pullRequest = repository.getPullRequest(issue.getNumber()); + + List ghWorkflowRuns = repository + .queryWorkflowRuns() + .branch(pullRequest.getHead().getRef()) + .status(GHWorkflowRun.Status.COMPLETED) + .list().toList(); + + Optional ghWorkflowRun = ghWorkflowRuns.stream() + .filter(workflowRun -> WorkflowConstants.QUARKUS_CI_WORKFLOW_NAME.equals(workflowRun.getName())) + .filter(workflowRun -> workflowRun.getHeadRepository().getOwnerName() + .equals(pullRequest.getHead().getRepository().getOwnerName())) + .sorted(Comparator.comparingLong(GHWorkflowRun::getId)) + .reduce((run, last) -> last); + + if (ghWorkflowRun.isPresent()) { + LOG.info("Rerunning workflow: " + ghWorkflowRun.get().getHtmlUrl()); + ghWorkflowRun.get().rerun(); + } else { + LOG.warn("Unable to find a workflow run to rerun"); + } + } catch (IOException e) { + LOG.error("Unable to run workflow", e); + } + } +}