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);
}
}
}
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)));
}
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;
}
}
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;
}
}
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()));
}
Aggregations