From fb368902216e1162d92b993416b598736a64658d Mon Sep 17 00:00:00 2001 From: Matyrobbrt Date: Sun, 15 Dec 2024 01:07:49 +0200 Subject: [PATCH] Add a label to keep a PR rebased --- .../graphql/com/github/api/fragments.graphql | 1 + .../neoforged/automation/Configuration.java | 6 +++++ .../handler/MergeConflictCheckHandler.java | 18 ++++++++++++- .../webhook/label/KeepRebasedHandler.java | 25 +++++++++++++++++++ .../webhook/label/LabelHandler.java | 3 ++- 5 files changed, 51 insertions(+), 2 deletions(-) create mode 100644 src/main/java/net/neoforged/automation/webhook/label/KeepRebasedHandler.java diff --git a/src/main/graphql/com/github/api/fragments.graphql b/src/main/graphql/com/github/api/fragments.graphql index 4cb175a..abe79ea 100644 --- a/src/main/graphql/com/github/api/fragments.graphql +++ b/src/main/graphql/com/github/api/fragments.graphql @@ -9,6 +9,7 @@ fragment IssueInfo on Issue { fragment PullRequestInfo on PullRequest { mergeable + mergeStateStatus number permalink title diff --git a/src/main/java/net/neoforged/automation/Configuration.java b/src/main/java/net/neoforged/automation/Configuration.java index abf3956..91f144b 100644 --- a/src/main/java/net/neoforged/automation/Configuration.java +++ b/src/main/java/net/neoforged/automation/Configuration.java @@ -29,6 +29,12 @@ public record RepoConfiguration(Boolean enabled, @JsonDeserialize(contentUsing = labelHandlers = labelHandlers == null ? Map.of() : labelHandlers; } public static final RepoConfiguration DEFAULT = new RepoConfiguration(true, Map.of(), null, null); + + @Nullable + public T getLabelOfType(String label, Class type) { + var handler = labelHandlers.get(label); + return type.isInstance(handler) ? (T) handler : null; + } } private static final ObjectMapper MAPPER = new ObjectMapper(new YAMLFactory().disable(YAMLGenerator.Feature.WRITE_DOC_START_MARKER).enable(YAMLGenerator.Feature.LITERAL_BLOCK_STYLE)); diff --git a/src/main/java/net/neoforged/automation/webhook/handler/MergeConflictCheckHandler.java b/src/main/java/net/neoforged/automation/webhook/handler/MergeConflictCheckHandler.java index 08bcf26..22066e8 100644 --- a/src/main/java/net/neoforged/automation/webhook/handler/MergeConflictCheckHandler.java +++ b/src/main/java/net/neoforged/automation/webhook/handler/MergeConflictCheckHandler.java @@ -3,13 +3,16 @@ import com.github.api.GetPullRequestQuery; import com.github.api.GetPullRequestsQuery; import com.github.api.fragment.PullRequestInfo; +import com.github.api.type.MergeStateStatus; import com.github.api.type.MergeableState; import com.github.api.type.PullRequestState; +import net.neoforged.automation.Configuration; import net.neoforged.automation.util.GHAction; import net.neoforged.automation.util.Label; import net.neoforged.automation.webhook.impl.GitHubEvent; import net.neoforged.automation.webhook.impl.MultiEventHandler; import net.neoforged.automation.webhook.impl.WebhookHandler; +import net.neoforged.automation.webhook.label.KeepRebasedHandler; import org.kohsuke.github.GHPullRequest; import org.kohsuke.github.GHPullRequestReview; import org.kohsuke.github.GHPullRequestReviewState; @@ -28,7 +31,7 @@ import java.util.concurrent.TimeUnit; public final class MergeConflictCheckHandler implements MultiEventHandler { - private static final long PR_BASE_TIME = 3; + public static final long PR_BASE_TIME = 3; private static final ScheduledThreadPoolExecutor SERVICE = new ScheduledThreadPoolExecutor(1); private static final Set IN_PROGRESS_REPOS = Collections.synchronizedSet(new HashSet<>()); @@ -97,6 +100,19 @@ public static MergeableState checkConflict(GitHub gitHub, PullRequestInfo info) final int number = info.number(); final GHRepository repo = gitHub.getRepository(info.repository().nameWithOwner()); final GHPullRequest pr = repo.getPullRequest(number); + + // If the PR is mergeable but behind and it has a keep-rebased label, we shall rebase it + if (state == MergeableState.MERGEABLE && info.mergeStateStatus() == MergeStateStatus.BEHIND) { + var conf = Configuration.get(repo); + for (PullRequestInfo.Node node : info.labels().nodes()) { + if (conf.getLabelOfType(node.name(), KeepRebasedHandler.class) != null) { + pr.updateBranch(); + break; + } + } + return state; + } + if (hasLabel && state == MergeableState.MERGEABLE) { // We don't have conflicts but the PR has the label... remove it. Label.NEEDS_REBASE.remove(pr); diff --git a/src/main/java/net/neoforged/automation/webhook/label/KeepRebasedHandler.java b/src/main/java/net/neoforged/automation/webhook/label/KeepRebasedHandler.java new file mode 100644 index 0000000..d358641 --- /dev/null +++ b/src/main/java/net/neoforged/automation/webhook/label/KeepRebasedHandler.java @@ -0,0 +1,25 @@ +package net.neoforged.automation.webhook.label; + +import com.github.api.GetPullRequestQuery; +import com.github.api.type.MergeableState; +import net.neoforged.automation.webhook.handler.MergeConflictCheckHandler; +import org.kohsuke.github.GHIssue; +import org.kohsuke.github.GHLabel; +import org.kohsuke.github.GHUser; +import org.kohsuke.github.GitHub; +import org.kohsuke.github.GitHubAccessor; + +public class KeepRebasedHandler implements LabelHandler { + @Override + public void onLabelAdded(GitHub gitHub, GHUser actor, GHIssue issue, GHLabel label) throws Exception { + while (MergeConflictCheckHandler.checkConflict(gitHub, GitHubAccessor.graphQl(gitHub, GetPullRequestQuery.builder() + .owner(issue.getRepository().getOwnerName()) + .name(issue.getRepository().getName()) + .number(issue.getNumber()) + .build()) + .repository() + .pullRequest().fragments().pullRequestInfo()) == MergeableState.UNKNOWN) { + Thread.sleep(MergeConflictCheckHandler.PR_BASE_TIME * 1000); + } + } +} diff --git a/src/main/java/net/neoforged/automation/webhook/label/LabelHandler.java b/src/main/java/net/neoforged/automation/webhook/label/LabelHandler.java index 6a74578..65e8451 100644 --- a/src/main/java/net/neoforged/automation/webhook/label/LabelHandler.java +++ b/src/main/java/net/neoforged/automation/webhook/label/LabelHandler.java @@ -16,7 +16,8 @@ public interface LabelHandler { Map> TYPES = Map.of( "lock", LockLabelHandler.class, - "merge", MergeLabelHandler.class + "merge", MergeLabelHandler.class, + "keep-rebased", KeepRebasedHandler.class ); default void onLabelAdded(GitHub gitHub, GHUser actor, GHIssue issue, GHLabel label) throws Exception {