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