Skip to content

Commit

Permalink
Start of automatic labelling
Browse files Browse the repository at this point in the history
  • Loading branch information
Matyrobbrt committed Dec 14, 2024
1 parent 34f3639 commit 1fd33bc
Show file tree
Hide file tree
Showing 9 changed files with 134 additions and 16 deletions.
5 changes: 5 additions & 0 deletions src/main/graphql/com/github/api/fragments.graphql
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
fragment IssueInfo on Issue {
number
labels(first: 100) {
nodes {
name
}
}
}

fragment PullRequestInfo on PullRequest {
Expand Down
17 changes: 14 additions & 3 deletions src/main/java/net/neoforged/automation/Main.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,30 @@
import net.neoforged.automation.command.Commands;
import net.neoforged.automation.util.AuthUtil;
import net.neoforged.automation.util.GHAction;
import net.neoforged.automation.webhook.handler.AutomaticLabelHandler;
import net.neoforged.automation.webhook.handler.CommandHandler;
import net.neoforged.automation.webhook.handler.ConfigurationUpdateHandler;
import net.neoforged.automation.webhook.handler.LabelLockHandler;
import net.neoforged.automation.webhook.handler.MergeConflictCheckHandler;
import net.neoforged.automation.webhook.handler.PRActionRunnerHandler;
import net.neoforged.automation.webhook.handler.ReleaseMessageHandler;
import net.neoforged.automation.webhook.handler.neo.VersionLabelHandler;
import net.neoforged.automation.webhook.impl.GitHubEvent;
import net.neoforged.automation.webhook.impl.WebhookHandler;
import org.kohsuke.github.GitHubAccessor;
import org.kohsuke.github.GitHubBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.nio.file.Path;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;

public class Main {
private static final String NEOFORGE = "neoforged/NeoForge";
public static final Logger LOGGER = LoggerFactory.getLogger("Reactionable");

public static void main(String[] args) throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
var startupConfig = StartupConfiguration.load(Path.of("config.properties"));

Expand All @@ -41,6 +49,8 @@ public static void main(String[] args) throws IOException, NoSuchAlgorithmExcept
})
.post("/webhook", webhook)
.start(startupConfig.getInt("port", 8080));

LOGGER.warn("Started up! Logged as {} on GitHub", GitHubAccessor.getApp(gitHub).getSlug());
}

public static WebhookHandler setupWebhookHandlers(StartupConfiguration startupConfig, WebhookHandler handler, Configuration.RepoLocation location) throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
Expand All @@ -56,8 +66,9 @@ public static WebhookHandler setupWebhookHandlers(StartupConfiguration startupCo
.build()))
.registerFilteredHandler(GitHubEvent.ISSUES, new LabelLockHandler(), GHAction.LABELED, GHAction.UNLABELED)
.registerFilteredHandler(GitHubEvent.WORKFLOW_RUN, new PRActionRunnerHandler(), GHAction.COMPLETED)
.registerFilteredHandler(GitHubEvent.ISSUE_COMMENT, new CommandHandler(
Commands.register(new CommandDispatcher<>())
), GHAction.CREATED);
.registerFilteredHandler(GitHubEvent.ISSUE_COMMENT, new CommandHandler(Commands.register(new CommandDispatcher<>())), GHAction.CREATED)
.registerFilteredHandler(GitHubEvent.PULL_REQUEST, new AutomaticLabelHandler(), GHAction.OPENED, GHAction.REOPENED)

.registerFilteredHandler(GitHubEvent.PULL_REQUEST, new VersionLabelHandler().forRepository(NEOFORGE), GHAction.OPENED, GHAction.REOPENED);
}
}
4 changes: 4 additions & 0 deletions src/main/java/net/neoforged/automation/util/GHAction.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
package net.neoforged.automation.util;

public enum GHAction {
/** Comments are created */
CREATED,
/** PRs and issues are opened */
OPENED,
REOPENED,

LABELED,
UNLABELED,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package net.neoforged.automation.webhook.handler;

import com.github.api.GetPullRequestQuery;
import net.neoforged.automation.util.GHAction;
import net.neoforged.automation.webhook.impl.ActionBasedHandler;
import org.kohsuke.github.GHEventPayload;
import org.kohsuke.github.GHPullRequest;
import org.kohsuke.github.GitHub;
import org.kohsuke.github.GitHubAccessor;

import java.util.HashSet;
import java.util.Locale;
import java.util.Set;

public class AutomaticLabelHandler implements ActionBasedHandler<GHEventPayload.PullRequest> {
@Override
public void handle(GitHub gitHub, GHEventPayload.PullRequest payload, GHAction action) throws Exception {
var labels = getClosingIssueLabels(gitHub, payload.getPullRequest());
if (labels.contains("bug")) {
payload.getPullRequest().addLabels("bug");
} else if (labels.contains("enhancement")) {
payload.getPullRequest().addLabels("enhancement");
}
}

private Set<String> getClosingIssueLabels(GitHub gitHub, GHPullRequest pr) throws Exception {
var labels = new HashSet<String>();
for (var issueNode : GitHubAccessor.graphQl(gitHub, GetPullRequestQuery.builder()
.name(pr.getRepository().getName())
.owner(pr.getRepository().getOwnerName())
.number(pr.getNumber())
.build())
.repository()
.pullRequest()
.fragments()
.pullRequestInfo()
.closingIssuesReferences()
.nodes()) {
var issue = issueNode.fragments().issueInfo();
issue.labels().nodes().forEach(n -> labels.add(n.name().toLowerCase(Locale.ROOT)));
}
return labels;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package net.neoforged.automation.webhook.handler.neo;

import net.neoforged.automation.util.GHAction;
import net.neoforged.automation.webhook.impl.ActionBasedHandler;
import org.kohsuke.github.GHEventPayload;
import org.kohsuke.github.GHLabel;
import org.kohsuke.github.GitHub;

import java.net.URI;
import java.util.Properties;
import java.util.stream.Collectors;

public class VersionLabelHandler implements ActionBasedHandler<GHEventPayload.PullRequest> {
@Override
public void handle(GitHub gitHub, GHEventPayload.PullRequest payload, GHAction action) throws Exception {
var head = payload.getPullRequest().getHead();
var props = new Properties();
var urlLoc = "https://raw.githubusercontent.com/%s/refs/heads/%s/gradle.properties".formatted(head.getRepository().getFullName(), head.getRef());
try (var str = URI.create(urlLoc).toURL().openStream()) {
props.load(str);
}

var mcVersionProp = props.get("minecraft_version");

if (mcVersionProp != null) {
var mcVer = mcVersionProp.toString();

var toRemoveLabels = payload.getPullRequest().getLabels().stream()
.map(GHLabel::getName)
.filter(name -> name.matches("\\d+\\.\\d+(\\.\\d+)?"))
.collect(Collectors.toList());

if (!toRemoveLabels.remove(mcVer)) {
payload.getPullRequest().addLabels(mcVer);
}

if (!toRemoveLabels.isEmpty()) {
payload.getPullRequest().removeLabels(toRemoveLabels.toArray(String[]::new));
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,14 @@
import org.kohsuke.github.GHEventPayload;
import org.kohsuke.github.GitHub;

import java.io.IOException;

public interface ActionBasedHandler<T extends GHEventPayload> {
void handle(GitHub gitHub, T payload, GHAction action) throws Exception;

default ActionBasedHandler<T> forRepository(String name) {
return (gitHub, payload, act) -> {
if (payload.getRepository().getFullName().equalsIgnoreCase(name)) {
this.handle(gitHub, payload, act);
}
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,14 @@
import org.kohsuke.github.GHEventPayload;
import org.kohsuke.github.GitHub;

import java.io.IOException;

public interface EventHandler<T extends GHEventPayload> {
void handle(GitHub gitHub, T payload) throws Exception;

default EventHandler<T> and(EventHandler<T> other) {
default EventHandler<T> forRepository(String name) {
return (gitHub, payload) -> {
this.handle(gitHub, payload);
other.handle(gitHub, payload);
if (payload.getRepository().getFullName().equalsIgnoreCase(name)) {
this.handle(gitHub, payload);
}
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public class WebhookHandler implements Handler {
private final byte[] secretToken;
private final GitHub gitHub;

private final Map<GitHubEvent, EventHandler> handlers = new IdentityHashMap<>();
private final Map<GitHubEvent, EventHandler[]> handlers = new IdentityHashMap<>();

public WebhookHandler(StartupConfiguration configuration, GitHub gitHub) {
this.secretToken = configuration.get("webhookSecret", "").getBytes(StandardCharsets.UTF_8);
Expand All @@ -48,9 +48,12 @@ public WebhookHandler register(MultiEventHandler handler) {
public <T extends GHEventPayload> WebhookHandler registerHandler(GitHubEvent<T> event, EventHandler<T> handler) {
handlers.compute(event, (k, old) -> {
if (old != null) {
return old.and(handler);
var newArray = new EventHandler[old.length + 1];
System.arraycopy(old, 0, newArray, 0, old.length);
newArray[old.length] = handler;
return newArray;
}
return handler;
return new EventHandler[] {handler};
});
return this;
}
Expand All @@ -74,8 +77,8 @@ public void handle(@NotNull Context ctx) throws Exception {
return;
}

var handler = handlers.get(ev);
if (handler == null) {
var handlers = this.handlers.get(ev);
if (handlers == null) {
ctx.status(HttpStatus.OK).result("No handlers registered for event " + event);
return;
}
Expand All @@ -84,7 +87,9 @@ public void handle(@NotNull Context ctx) throws Exception {
try {
var payload = ev.parse(gitHub, bodyBytes);
if (Configuration.get(payload.getRepository()).enabled()) {
handler.handle(gitHub, payload);
for (EventHandler handler : handlers) {
handler.handle(gitHub, payload);
}
}
} catch (Exception exception) {
ctx.status(HttpStatus.INTERNAL_SERVER_ERROR).result("Failed to handle request: " + exception.getMessage());
Expand Down
2 changes: 2 additions & 0 deletions src/main/java/org/kohsuke/github/GitHubAccessor.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import net.neoforged.automation.util.ApolloReader;
import net.neoforged.automation.util.AuthUtil;
import org.apache.commons.io.input.ReaderInputStream;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.kohsuke.github.function.InputStreamFunction;

Expand Down Expand Up @@ -67,6 +68,7 @@ public static <T> T graphQl(GitHub gitHub, String query, InputStreamFunction<T>
.fetchStream(isF);
}

@NotNull
@SuppressWarnings("unchecked")
public static <T> T graphQl(GitHub gitHub, Operation<?, T, ?> call) throws IOException {
final Response<T> response = graphQl(gitHub, call.composeRequestBody().utf8(), in -> (Response<T>) READER.read(call, in));
Expand Down

0 comments on commit 1fd33bc

Please sign in to comment.