Search in sources :

Example 1 with GHPullRequestReviewState

use of org.kohsuke.github.GHPullRequestReviewState in project pull-request-workflow by avano.

the class GHClient method getReviews.

/**
 * Gets the reviews for given PR. User can review multiple times, so for each user it returns the latest review.
 *
 * @param pr pull request instance
 * @return map with the user login as key and last review state as value
 */
public Map<GHUser, GHPullRequestReviewState> getReviews(GHPullRequest pr) {
    LOG.trace("PR #{}: Listing reviews", pr.getNumber());
    Map<GHUser, GHPullRequestReviewState> response = new HashMap<>();
    try {
        for (GHPullRequestReview review : pr.listReviews().toList()) {
            response.put(review.getUser(), review.getState());
        }
    } catch (IOException e) {
        LOG.error("PR #{}: Unable to get user from review", pr.getNumber(), e);
    }
    return response;
}
Also used : GHPullRequestReviewState(org.kohsuke.github.GHPullRequestReviewState) HashMap(java.util.HashMap) GHPullRequestReview(org.kohsuke.github.GHPullRequestReview) GHUser(org.kohsuke.github.GHUser) IOException(java.io.IOException)

Example 2 with GHPullRequestReviewState

use of org.kohsuke.github.GHPullRequestReviewState 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 GHPullRequestReviewState

use of org.kohsuke.github.GHPullRequestReviewState in project pull-request-workflow by avano.

the class MergeHandler method merge.

/**
 * Merges the PR if all prerequisities are fulfilled.
 *
 * @param msg {@link BusMessage} instance
 */
@Log
@ConsumeEvent(Constants.PR_MERGE)
public void merge(BusMessage msg) {
    GHClient client = msg.client();
    GHPullRequest pr = msg.get(GHPullRequest.class);
    try {
        // Refresh the PR to work with latest state
        pr.refresh();
        if (pr.isMerged()) {
            LOG.info("PR #{}: Not merging - already merged", pr.getNumber());
            return;
        }
        if (pr.isDraft()) {
            LOG.info("PR #{}: Not merging - draft state", pr.getNumber());
            return;
        }
        if (pr.getLabels().stream().map(GHLabel::getName).collect(Collectors.toSet()).contains(client.getRepositoryConfiguration().wipLabel())) {
            LOG.info("PR #{}: Not merging - work in progress", pr.getNumber());
            return;
        }
        // Check if all required checks passed
        final String targetBranch = pr.getBase().getRef();
        Set<String> requiredChecks = client.getRequiredChecks(targetBranch);
        if (requiredChecks != null && !requiredChecks.isEmpty()) {
            LOG.info("PR #{}: Required checks: {}", pr.getNumber(), String.join(", ", requiredChecks));
            final StringBuilder logMsg = new StringBuilder("PR #").append(pr.getNumber()).append(": Checks - ");
            client.getChecks(pr).forEach((name, result) -> {
                logMsg.append("[").append(name).append(": ").append(result).append("], ");
                if ("success".equalsIgnoreCase(result)) {
                    requiredChecks.remove(name);
                }
            });
            LOG.info(logMsg.substring(0, logMsg.length() - 2));
            if (!requiredChecks.isEmpty()) {
                LOG.info("PR #{}: Not merging - some of the required checks did not pass", pr.getNumber());
                return;
            }
        } else {
            LOG.debug("PR #{}: No required checks defined for branch {}", pr.getNumber(), targetBranch);
        }
        if (pr.getMergeable() == null || !pr.getMergeable()) {
            LOG.warn("PR #{}: Not merging - not mergeable", pr.getNumber());
            return;
        }
        if (Constants.DEPENDABOT_NAME.equals(client.getAuthor(pr).getLogin()) && client.getRepositoryConfiguration().automergeDependabot()) {
            LOG.info("PR #{}: Automerging dependabot PR", pr.getNumber());
            mergePullRequest(msg);
            return;
        }
        if (client.getRepositoryConfiguration().automergeOwnerPRs() && client.getAuthor(pr).getLogin().equals(client.getRepositoryConfiguration().repository().split("/")[0])) {
            LOG.info("PR #{}: Automerging owner's PR", pr.getNumber());
            mergePullRequest(msg);
            return;
        }
        Map<GHUser, GHPullRequestReviewState> reviews = client.getReviews(pr);
        if (reviews.size() == 0) {
            LOG.info("PR #{}: Not merging - no reviews", pr.getNumber());
            return;
        }
        if (reviews.values().stream().noneMatch(r -> r == GHPullRequestReviewState.APPROVED)) {
            LOG.info("PR #{}: Not merging - no approvals", pr.getNumber());
            return;
        }
        if (client.changesRequested(pr)) {
            LOG.info("PR #{}: Not merging - at least one \"changes requested\" review present", pr.getNumber());
            return;
        }
        if (ApprovalStrategy.ALL == client.getRepositoryConfiguration().approvalStrategy()) {
            if (pr.getRequestedReviewers().size() != reviews.size() || reviews.values().stream().anyMatch(r -> r != GHPullRequestReviewState.APPROVED)) {
                LOG.info("PR #{}: Not merging - approval from some reviewer missing (using \"all\" strategy)", pr.getNumber());
                return;
            }
        }
        mergePullRequest(msg);
    } catch (IOException e) {
        LOG.error("PR #{}: Unable to process merge", pr.getNumber(), e);
    }
}
Also used : GHLabel(org.kohsuke.github.GHLabel) GHPullRequestReviewState(org.kohsuke.github.GHPullRequestReviewState) BaseHandler(com.github.avano.pr.workflow.handler.base.BaseHandler) GHPullRequest(org.kohsuke.github.GHPullRequest) ConflictMessage(com.github.avano.pr.workflow.message.ConflictMessage) GHPullRequestReviewState(org.kohsuke.github.GHPullRequestReviewState) Collection(java.util.Collection) Set(java.util.Set) IOException(java.io.IOException) ConsumeEvent(io.quarkus.vertx.ConsumeEvent) Collectors(java.util.stream.Collectors) ApprovalStrategy(com.github.avano.pr.workflow.config.ApprovalStrategy) GHPerson(org.kohsuke.github.GHPerson) 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) Map(java.util.Map) BusMessage(com.github.avano.pr.workflow.message.BusMessage) GHLabel(org.kohsuke.github.GHLabel) Log(com.github.avano.pr.workflow.handler.interceptor.Log) GHPullRequest(org.kohsuke.github.GHPullRequest) GHClient(com.github.avano.pr.workflow.gh.GHClient) GHUser(org.kohsuke.github.GHUser) IOException(java.io.IOException) Log(com.github.avano.pr.workflow.handler.interceptor.Log) ConsumeEvent(io.quarkus.vertx.ConsumeEvent)

Aggregations

IOException (java.io.IOException)3 GHPullRequestReviewState (org.kohsuke.github.GHPullRequestReviewState)3 GHUser (org.kohsuke.github.GHUser)3 Constants (com.github.avano.pr.workflow.config.Constants)2 GHClient (com.github.avano.pr.workflow.gh.GHClient)2 BaseHandler (com.github.avano.pr.workflow.handler.base.BaseHandler)2 Log (com.github.avano.pr.workflow.handler.interceptor.Log)2 BusMessage (com.github.avano.pr.workflow.message.BusMessage)2 ConsumeEvent (io.quarkus.vertx.ConsumeEvent)2 List (java.util.List)2 Map (java.util.Map)2 Collectors (java.util.stream.Collectors)2 GHPullRequest (org.kohsuke.github.GHPullRequest)2 ApprovalStrategy (com.github.avano.pr.workflow.config.ApprovalStrategy)1 ConflictMessage (com.github.avano.pr.workflow.message.ConflictMessage)1 LabelsMessage (com.github.avano.pr.workflow.message.LabelsMessage)1 ArrayList (java.util.ArrayList)1 Collection (java.util.Collection)1 HashMap (java.util.HashMap)1 Set (java.util.Set)1