Search in sources :

Example 1 with ConsumeEvent

use of io.quarkus.vertx.ConsumeEvent in project pull-request-workflow by avano.

the class ConflictHandler method checkForConflict.

/**
 * Checks if a PR merge caused conflict in other opened PRs, if so a comment is added to each open PR and it's assigned back to the author to fix.
 *
 * @param msg {@link BusMessage} instance
 */
@Log
@ConsumeEvent(Constants.PR_CHECK_CONFLICT)
public void checkForConflict(BusMessage msg) {
    GHClient client = msg.client();
    ConflictMessage cm = msg.get(ConflictMessage.class);
    if (client.getRepositoryConfiguration().conflictMessage() == null || client.getRepositoryConfiguration().conflictMessage().isEmpty()) {
        LOG.debug("Skipping conflict detection - conflict message not set");
        return;
    }
    for (GHPullRequest openPullRequest : cm.getOpenPullRequests()) {
        try {
            openPullRequest.refresh();
            if ("dirty".equals(openPullRequest.getMergeableState())) {
                LOG.info("PR #{}: Caused conflict in PR #{}", cm.getMergedPrId(), openPullRequest.getNumber());
                client.postComment(openPullRequest, client.getRepositoryConfiguration().conflictMessage().replace("<ID>", cm.getMergedPrId() + ""));
                client.assignToAuthor(openPullRequest);
                List<String> removeLabels = new ArrayList<>();
                removeLabels.addAll(client.getRepositoryConfiguration().approvedLabels());
                removeLabels.addAll(client.getRepositoryConfiguration().reviewRequestedLabels());
                eventBus.publish(Constants.EDIT_LABELS, new BusMessage(client, new LabelsMessage(openPullRequest, client.getRepositoryConfiguration().changesRequestedLabels(), removeLabels)));
            } else {
                LOG.trace("PR #{}: No conflict in PR #{}", cm.getMergedPrId(), openPullRequest.getNumber());
            }
        } catch (IOException e) {
            LOG.error("PR #{}: Unable to process PR", openPullRequest.getNumber(), e);
        }
    }
}
Also used : GHPullRequest(org.kohsuke.github.GHPullRequest) GHClient(com.github.avano.pr.workflow.gh.GHClient) BusMessage(com.github.avano.pr.workflow.message.BusMessage) ArrayList(java.util.ArrayList) IOException(java.io.IOException) ConflictMessage(com.github.avano.pr.workflow.message.ConflictMessage) LabelsMessage(com.github.avano.pr.workflow.message.LabelsMessage) Log(com.github.avano.pr.workflow.handler.interceptor.Log) ConsumeEvent(io.quarkus.vertx.ConsumeEvent)

Example 2 with ConsumeEvent

use of io.quarkus.vertx.ConsumeEvent in project pull-request-workflow by avano.

the class LifecycleHandler method handlePrUpdated.

/**
 * Handles the
 * <a href="https://docs.github.com/en/developers/webhooks-and-events/webhooks/webhook-events-and-payloads#pull_request">pull request</a>
 * synchronized event.
 * <p>
 * When the PR is updated (new commits are pushed to the PR), it dismisses all reviews, clears internal tracking and set the labels and
 * assignees to the correct state.
 * <p>
 * All users with a review on the PR will be assigned to the PR and re-requested for review.
 *
 * @param msg {@link BusMessage} instance
 */
@Log
@ConsumeEvent(Constants.PR_UPDATED)
public void handlePrUpdated(BusMessage msg) {
    GHClient client = msg.client();
    GHPullRequest pr = msg.get(GHPullRequest.class);
    LOG.info("PR #{}: Pull request updated - dismissing all reviews", pr.getNumber());
    // Dismiss all approved/changes requested reviews, since the PR was updated
    try {
        pr.listReviews().toList().stream().filter(r -> r.getState() == GHPullRequestReviewState.APPROVED || r.getState() == GHPullRequestReviewState.CHANGES_REQUESTED).forEach(r -> {
            try {
                r.dismiss(client.getRepositoryConfiguration().reviewDismissMessage());
            } catch (IOException e) {
                LOG.error("PR #{}: Unable to dismiss review: " + e, pr.getNumber());
            }
        });
    } catch (IOException e) {
        LOG.error("PR #{}: Unable to list all pull request reviews: " + e, pr.getNumber());
    }
    // Re-apply labels to current state
    List<String> addLabels = new ArrayList<>();
    List<String> removeLabels = new ArrayList<>();
    Map<GHUser, GHPullRequestReviewState> reviews = client.getReviews(pr);
    if (!reviews.isEmpty()) {
        // Request review from all previous reviewers (except for the author of the PR - if he responds to some comment, it is counted as
        // "commented" review)
        // Set the assignees back to requested reviewers
        // Don't add "review requested labels", as that will be done by the "review requested" event
        List<GHUser> reviewers = reviews.keySet().stream().filter(u -> !u.equals(client.getAuthor(pr))).collect(Collectors.toList());
        client.requestReviewers(pr, reviewers);
        client.setAssignees(pr, reviewers);
    }
    removeLabels.addAll(client.getRepositoryConfiguration().changesRequestedLabels());
    removeLabels.addAll(client.getRepositoryConfiguration().approvedLabels());
    removeLabels.addAll(client.getRepositoryConfiguration().commentedLabels());
    eventBus.publish(Constants.EDIT_LABELS, new BusMessage(client, new LabelsMessage(pr, addLabels, removeLabels)));
}
Also used : BaseHandler(com.github.avano.pr.workflow.handler.base.BaseHandler) GHPullRequest(org.kohsuke.github.GHPullRequest) GHPullRequestReviewState(org.kohsuke.github.GHPullRequestReviewState) IOException(java.io.IOException) ConsumeEvent(io.quarkus.vertx.ConsumeEvent) Collectors(java.util.stream.Collectors) ArrayList(java.util.ArrayList) Constants(com.github.avano.pr.workflow.config.Constants) List(java.util.List) GHUser(org.kohsuke.github.GHUser) GHClient(com.github.avano.pr.workflow.gh.GHClient) LabelsMessage(com.github.avano.pr.workflow.message.LabelsMessage) Map(java.util.Map) BusMessage(com.github.avano.pr.workflow.message.BusMessage) Log(com.github.avano.pr.workflow.handler.interceptor.Log) GHPullRequestReviewState(org.kohsuke.github.GHPullRequestReviewState) GHClient(com.github.avano.pr.workflow.gh.GHClient) BusMessage(com.github.avano.pr.workflow.message.BusMessage) ArrayList(java.util.ArrayList) IOException(java.io.IOException) GHUser(org.kohsuke.github.GHUser) LabelsMessage(com.github.avano.pr.workflow.message.LabelsMessage) GHPullRequest(org.kohsuke.github.GHPullRequest) Log(com.github.avano.pr.workflow.handler.interceptor.Log) ConsumeEvent(io.quarkus.vertx.ConsumeEvent)

Example 3 with ConsumeEvent

use of io.quarkus.vertx.ConsumeEvent in project pull-request-workflow by avano.

the class ReviewSubmittedHandler method handleReview.

/**
 * Handles the
 * <a href="https://docs.github.com/en/developers/webhooks-and-events/webhooks/webhook-events-and-payloads#pull_request_review">pull request review</a> submitted event.
 * <p>
 * When the user provided a review, he is removed from the assignee list.
 * <p>
 * When the PR is approved and there are no changes-requested reviews from other reviewers, approved label is added to the PR and it is tried
 * to merge the PR, otherwise the approved review is ignored.
 * <p>
 * When changes are requested, the PR is assigned back to author and corresponding label is added and approved label is removed.
 * <p>
 * When the PR is commented (not only PR's "comment only" action, but also all conversation), commented label is applied and if the author of
 * the comment is in the assignees list, he is removed (as he provided a "review")
 *
 * @param msg {@link BusMessage} instance
 */
@Log
@ConsumeEvent(Constants.PR_REVIEW_SUBMITTED)
public void handleReview(BusMessage msg) {
    GHClient client = msg.client();
    GHPullRequestReview review = msg.get(GHPullRequestReview.class);
    GHPullRequest pr = msg.get(BusMessage.INFO_PR_KEY, GHPullRequest.class);
    List<String> addLabels = new ArrayList<>();
    List<String> removeLabels = new ArrayList<>();
    switch(review.getState()) {
        case APPROVED:
            LOG.info("PR #{}: Approved by {}", pr.getNumber(), msg.getSender().getLogin());
            List<GHUser> assignees = new ArrayList<>(pr.getAssignees());
            if (assignees.contains(msg.getSender())) {
                assignees.remove(msg.getSender());
                LOG.info("PR #{}: Removing {} from assignees", pr.getNumber(), msg.getSender().getLogin());
                client.setAssignees(pr, assignees);
            } else {
                LOG.debug("PR #{}: {} wasn't in assignees list and provided a review", pr.getNumber(), msg.getSender().getLogin());
            }
            // Only add approved label if there are no "changes required" reviews left
            if (!client.changesRequested(pr)) {
                addLabels.addAll(client.getRepositoryConfiguration().approvedLabels());
                removeLabels.addAll(client.getRepositoryConfiguration().changesRequestedLabels());
                eventBus.publish(Constants.CHECK_RUN_CREATE, new BusMessage(client, new CheckRunMessage(pr, GHCheckRun.Status.COMPLETED, GHCheckRun.Conclusion.SUCCESS)));
            }
            // Review was provided, dismiss review requested label
            removeLabels.addAll(client.getRepositoryConfiguration().reviewRequestedLabels());
            eventBus.publish(Constants.EDIT_LABELS, new BusMessage(client, new LabelsMessage(pr, addLabels, removeLabels)));
            // Don't merge here, it will be handled by the checkrun event
            break;
        case CHANGES_REQUESTED:
            LOG.info("PR #{}: Changes requested by {}", pr.getNumber(), msg.getSender().getLogin());
            client.assignToAuthor(pr);
            eventBus.publish(Constants.CHECK_RUN_CREATE, new BusMessage(client, new CheckRunMessage(pr, GHCheckRun.Status.COMPLETED, GHCheckRun.Conclusion.FAILURE)));
            removeLabels.addAll(client.getRepositoryConfiguration().approvedLabels());
            removeLabels.addAll(client.getRepositoryConfiguration().reviewRequestedLabels());
            eventBus.publish(Constants.EDIT_LABELS, new BusMessage(client, new LabelsMessage(pr, client.getRepositoryConfiguration().changesRequestedLabels(), removeLabels)));
            break;
        case COMMENTED:
            LOG.info("PR #{}: Commented by {}", pr.getNumber(), msg.getSender().getLogin());
            addLabels.addAll(client.getRepositoryConfiguration().commentedLabels());
            eventBus.publish(Constants.EDIT_LABELS, new BusMessage(client, new LabelsMessage(pr, addLabels, removeLabels)));
            break;
        default:
            LOG.info("PR #{}: Ignoring review state {}", pr.getNumber(), review.getState().name().toLowerCase());
            break;
    }
}
Also used : GHPullRequest(org.kohsuke.github.GHPullRequest) GHClient(com.github.avano.pr.workflow.gh.GHClient) BusMessage(com.github.avano.pr.workflow.message.BusMessage) CheckRunMessage(com.github.avano.pr.workflow.message.CheckRunMessage) GHPullRequestReview(org.kohsuke.github.GHPullRequestReview) ArrayList(java.util.ArrayList) GHUser(org.kohsuke.github.GHUser) LabelsMessage(com.github.avano.pr.workflow.message.LabelsMessage) Log(com.github.avano.pr.workflow.handler.interceptor.Log) ConsumeEvent(io.quarkus.vertx.ConsumeEvent)

Example 4 with ConsumeEvent

use of io.quarkus.vertx.ConsumeEvent in project pull-request-workflow by avano.

the class PullRequest method handlePullRequestEvent.

/**
 * Handles the incoming pull request event.
 */
@Log
@ConsumeEvent(Constants.PULL_REQUEST_EVENT)
public void handlePullRequestEvent(JsonObject jsonEvent) {
    if (!init(jsonEvent, GHEventPayload.PullRequest.class)) {
        return;
    }
    BusMessage msg = new BusMessage(client, event.getPullRequest()).withSender(event.getSender());
    switch(jsonEvent.getString("action")) {
        case "reopened":
            eventBus.publish(Constants.PR_REOPENED, msg);
            break;
        case "review_requested":
            msg.with(BusMessage.REQUESTED_REVIEWER, jsonEvent.getJsonObject(Constants.JSON_REQUESTED_REVIEWER).getString(Constants.JSON_REQUESTED_REVIEWER_LOGIN));
            eventBus.publish(Constants.PR_REVIEW_REQUESTED, msg);
            break;
        case "review_request_removed":
            msg.with(BusMessage.REQUESTED_REVIEWER, jsonEvent.getJsonObject(Constants.JSON_REQUESTED_REVIEWER).getString(Constants.JSON_REQUESTED_REVIEWER_LOGIN));
            eventBus.publish(Constants.PR_REVIEW_REQUEST_REMOVED, msg);
            break;
        case "ready_for_review":
            eventBus.publish(Constants.PR_READY_FOR_REVIEW, msg);
            break;
        case "synchronize":
            eventBus.publish(Constants.PR_UPDATED, msg);
            break;
        case "unlabeled":
            msg.with(BusMessage.LABEL, jsonEvent.getJsonObject(Constants.JSON_LABEL).getString(Constants.JSON_LABEL_NAME));
            eventBus.publish(Constants.PR_UNLABELED, msg);
            break;
        default:
            LOG.debug("Ignoring pull request action \"{}\"", jsonEvent.getString("action"));
            break;
    }
}
Also used : BusMessage(com.github.avano.pr.workflow.message.BusMessage) Log(com.github.avano.pr.workflow.handler.interceptor.Log) ConsumeEvent(io.quarkus.vertx.ConsumeEvent)

Example 5 with ConsumeEvent

use of io.quarkus.vertx.ConsumeEvent in project quarkus by quarkusio.

the class VertxProcessor method build.

@BuildStep
@Record(ExecutionTime.RUNTIME_INIT)
VertxBuildItem build(CoreVertxBuildItem vertx, VertxRecorder recorder, List<EventConsumerBusinessMethodItem> messageConsumerBusinessMethods, BuildProducer<GeneratedClassBuildItem> generatedClass, AnnotationProxyBuildItem annotationProxy, LaunchModeBuildItem launchMode, ShutdownContextBuildItem shutdown, BuildProducer<ServiceStartBuildItem> serviceStart, BuildProducer<ReflectiveClassBuildItem> reflectiveClass, List<MessageCodecBuildItem> codecs, RecorderContext recorderContext) {
    Map<String, ConsumeEvent> messageConsumerConfigurations = new HashMap<>();
    ClassOutput classOutput = new GeneratedClassGizmoAdaptor(generatedClass, true);
    for (EventConsumerBusinessMethodItem businessMethod : messageConsumerBusinessMethods) {
        String invokerClass = EventBusConsumer.generateInvoker(businessMethod.getBean(), businessMethod.getMethod(), businessMethod.getConsumeEvent(), classOutput);
        messageConsumerConfigurations.put(invokerClass, annotationProxy.builder(businessMethod.getConsumeEvent(), ConsumeEvent.class).withDefaultValue("value", businessMethod.getBean().getBeanClass().toString()).build(classOutput));
        reflectiveClass.produce(new ReflectiveClassBuildItem(false, false, invokerClass));
    }
    Map<Class<?>, Class<?>> codecByClass = new HashMap<>();
    for (MessageCodecBuildItem messageCodecItem : codecs) {
        codecByClass.put(recorderContext.classProxy(messageCodecItem.getType()), recorderContext.classProxy(messageCodecItem.getCodec()));
    }
    recorder.configureVertx(vertx.getVertx(), messageConsumerConfigurations, launchMode.getLaunchMode(), shutdown, codecByClass);
    serviceStart.produce(new ServiceStartBuildItem("vertx"));
    return new VertxBuildItem(recorder.forceStart(vertx.getVertx()));
}
Also used : ServiceStartBuildItem(io.quarkus.deployment.builditem.ServiceStartBuildItem) HashMap(java.util.HashMap) GeneratedClassGizmoAdaptor(io.quarkus.deployment.GeneratedClassGizmoAdaptor) CoreVertxBuildItem(io.quarkus.vertx.core.deployment.CoreVertxBuildItem) ClassOutput(io.quarkus.gizmo.ClassOutput) ReflectiveClassBuildItem(io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem) ConsumeEvent(io.quarkus.vertx.ConsumeEvent) BuildStep(io.quarkus.deployment.annotations.BuildStep) Record(io.quarkus.deployment.annotations.Record)

Aggregations

ConsumeEvent (io.quarkus.vertx.ConsumeEvent)14 Log (com.github.avano.pr.workflow.handler.interceptor.Log)11 GHPullRequest (org.kohsuke.github.GHPullRequest)8 BusMessage (com.github.avano.pr.workflow.message.BusMessage)7 GHClient (com.github.avano.pr.workflow.gh.GHClient)6 LabelsMessage (com.github.avano.pr.workflow.message.LabelsMessage)6 IOException (java.io.IOException)4 ArrayList (java.util.ArrayList)4 CheckRunMessage (com.github.avano.pr.workflow.message.CheckRunMessage)3 GHUser (org.kohsuke.github.GHUser)3 Constants (com.github.avano.pr.workflow.config.Constants)2 BaseHandler (com.github.avano.pr.workflow.handler.base.BaseHandler)2 ConflictMessage (com.github.avano.pr.workflow.message.ConflictMessage)2 List (java.util.List)2 Map (java.util.Map)2 Collectors (java.util.stream.Collectors)2 GHPullRequestReviewState (org.kohsuke.github.GHPullRequestReviewState)2 JsonProcessingException (com.fasterxml.jackson.core.JsonProcessingException)1 ApprovalStrategy (com.github.avano.pr.workflow.config.ApprovalStrategy)1 CommitStatusMessage (com.github.avano.pr.workflow.message.CommitStatusMessage)1