Skip to content

Commit a1b7d82

Browse files
committed
Sync kind labels with issue types
1 parent c93a896 commit a1b7d82

10 files changed

Lines changed: 279 additions & 1 deletion

File tree

pom.xml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
<quarkus-github-app.version>2.5.1</quarkus-github-app.version>
2424
<commons-io.version>2.15.1</commons-io.version>
2525
<hamcrest.version>2.2</hamcrest.version>
26-
<mockito-core.version>5.10.0</mockito-core.version>
26+
<mockito-core.version>5.20.0</mockito-core.version>
2727
</properties>
2828
<dependencyManagement>
2929
<dependencies>
@@ -131,6 +131,7 @@
131131
<version>${surefire-plugin.version}</version>
132132
<configuration>
133133
<systemPropertyVariables>
134+
<argLine>@{argLine} -javaagent:${org.mockito:mockito-core:jar}</argLine>
134135
<java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager>
135136
<maven.home>${maven.home}</maven.home>
136137
</systemPropertyVariables>
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
package org.keycloak.gh.bot;
2+
3+
import io.quarkiverse.githubapp.GitHubEvent;
4+
import io.quarkiverse.githubapp.event.RawEvent;
5+
import io.vertx.core.json.JsonArray;
6+
import io.vertx.core.json.JsonObject;
7+
import org.jboss.logging.Logger;
8+
import org.kohsuke.github.GHIssue;
9+
import org.kohsuke.github.GitHub;
10+
11+
import java.io.IOException;
12+
import java.util.Collections;
13+
import java.util.List;
14+
15+
public class SyncKindLabels {
16+
17+
private static final Logger logger = Logger.getLogger(SyncKindLabels.class);
18+
19+
void onTyped(@RawEvent(event = "issues", action = "typed") GitHubEvent event, GitHub gitHub) throws IOException {
20+
onTypeChange(event, true, gitHub);
21+
}
22+
23+
void onUntyped(@RawEvent(event = "issues", action = "untyped") GitHubEvent event, GitHub gitHub) throws IOException {
24+
onTypeChange(event, false, gitHub);
25+
}
26+
27+
void onTypeChange(GitHubEvent event, boolean typed, GitHub gitHub) throws IOException {
28+
JsonObject payload = event.getParsedPayload();
29+
30+
JsonObject type = payload.getJsonObject("type");
31+
String typeName = type != null ? type.getString("name") : null;
32+
String labelName = "kind/" + typeName;
33+
34+
JsonObject issue = payload.getJsonObject("issue");
35+
int issueNumber = issue.getInteger("number");
36+
37+
JsonArray labels = issue.getJsonArray("labels");
38+
List<String> kindLabels;
39+
if (labels != null) {
40+
kindLabels = labels.stream().map(o -> (JsonObject) o).map(o -> o.getString("name")).filter(l -> l.startsWith("kind/")).toList();
41+
} else {
42+
kindLabels = Collections.emptyList();
43+
}
44+
45+
String labelToAdd = null;
46+
List<String> labelsToRemove;
47+
48+
if (typed) {
49+
if (!kindLabels.contains(labelName)) {
50+
labelToAdd = labelName;
51+
}
52+
labelsToRemove = kindLabels.stream().filter(l -> !l.equals(labelName)).toList();
53+
} else {
54+
labelsToRemove = kindLabels;
55+
}
56+
57+
if (labelToAdd != null || !labelsToRemove.isEmpty()) {
58+
logger.infov("issue={0}, typed={1}, labelToAdd={2}, labelsToRemove={3}", issueNumber, typed, labelToAdd, labelsToRemove);
59+
60+
GHIssue ghIssue = gitHub.getRepository(event.getRepositoryOrThrow()).getIssue(issue.getInteger("number"));
61+
if (labelToAdd != null) {
62+
ghIssue.addLabels(labelToAdd);
63+
}
64+
if (!labelsToRemove.isEmpty()) {
65+
ghIssue.removeLabels(labelsToRemove.toArray(new String[0]));
66+
}
67+
}
68+
}
69+
70+
}
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
package org.keycloak.gh.bot;
2+
3+
import io.quarkiverse.githubapp.GitHubEvent;
4+
import io.vertx.core.json.JsonObject;
5+
import org.junit.jupiter.api.AfterEach;
6+
import org.junit.jupiter.api.Test;
7+
import org.kohsuke.github.GHIssue;
8+
import org.kohsuke.github.GHRepository;
9+
import org.kohsuke.github.GitHub;
10+
11+
import java.io.IOException;
12+
import java.io.InputStream;
13+
import java.nio.charset.StandardCharsets;
14+
15+
import static org.mockito.Mockito.mock;
16+
import static org.mockito.Mockito.verify;
17+
import static org.mockito.Mockito.verifyNoMoreInteractions;
18+
import static org.mockito.Mockito.when;
19+
20+
public class SyncKindLabelsTest {
21+
22+
private JsonObject payload;
23+
private GitHub gitHub;
24+
private GHRepository repository;
25+
private GitHubEvent event;
26+
private GHIssue issue;
27+
28+
private void runEvent(boolean typed, String payloadResource) throws IOException {
29+
payload = new JsonObject(getResource(payloadResource));
30+
31+
gitHub = mock(GitHub.class);
32+
event = mock(GitHubEvent.class);
33+
repository = mock(GHRepository.class);
34+
issue = mock(GHIssue.class);
35+
36+
when(event.getParsedPayload()).thenReturn(payload);
37+
when(event.getRepositoryOrThrow()).thenReturn("myorg/myrep");
38+
when(gitHub.getRepository("myorg/myrep")).thenReturn(repository);
39+
when(repository.getIssue(1234)).thenReturn(issue);
40+
41+
SyncKindLabels syncKindLabels = new SyncKindLabels();
42+
if (typed) {
43+
syncKindLabels.onTyped(event, gitHub);
44+
} else {
45+
syncKindLabels.onUntyped(event, gitHub);
46+
}
47+
}
48+
49+
@AfterEach
50+
public void after() {
51+
verifyNoMoreInteractions(issue);
52+
}
53+
54+
@Test
55+
public void testTypedLabelMissing() throws IOException {
56+
runEvent(true, "typed-label-missing.json");
57+
verify(issue).addLabels("kind/cve");
58+
}
59+
60+
@Test
61+
public void testTypedLabelMissingHasAnother() throws IOException {
62+
runEvent(true, "typed-label-missing-has-another.json");
63+
verify(issue).addLabels("kind/cve");
64+
verify(issue).removeLabels("kind/bug");
65+
}
66+
67+
@Test
68+
public void testTypedLabelMatches() throws IOException {
69+
runEvent(true, "typed-label-matches.json");
70+
}
71+
72+
@Test
73+
public void testTypedLabelMatchesHasAnother() throws IOException {
74+
runEvent(true, "typed-label-matches-has-another.json");
75+
verify(issue).removeLabels("kind/bug");
76+
}
77+
78+
@Test
79+
public void testUntypedWithLabels() throws IOException {
80+
runEvent(false,"untyped-with-labels.json");
81+
verify(issue).removeLabels("kind/bug", "kind/cve");
82+
}
83+
84+
@Test
85+
public void testUntypedWithoutLabels() throws IOException {
86+
runEvent(false,"untyped-without-labels.json");
87+
}
88+
89+
private String getResource(String name) throws IOException {
90+
try (InputStream is = getClass().getResourceAsStream(name)) {
91+
if (is == null) {
92+
throw new RuntimeException("Resource not found " + name);
93+
}
94+
return new String(is.readAllBytes(), StandardCharsets.UTF_8);
95+
}
96+
}
97+
98+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"issue": {
3+
"number": 1234,
4+
"labels": [
5+
{
6+
"name": "status/triage"
7+
},
8+
{
9+
"name": "kind/cve"
10+
},
11+
{
12+
"name": "kind/bug"
13+
}
14+
]
15+
},
16+
"type": {
17+
"name": "cve"
18+
}
19+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"issue": {
3+
"number": 1234,
4+
"labels": [
5+
{
6+
"name": "status/triage"
7+
},
8+
{
9+
"name": "kind/cve"
10+
}
11+
]
12+
},
13+
"type": {
14+
"name": "cve"
15+
}
16+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"issue": {
3+
"number": 1234,
4+
"labels": [
5+
{
6+
"name": "status/triage"
7+
},
8+
{
9+
"name": "kind/bug"
10+
}
11+
]
12+
},
13+
"type": {
14+
"name": "cve"
15+
}
16+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"issue": {
3+
"number": 1234,
4+
"labels": [
5+
{
6+
"name": "status/triage"
7+
}
8+
]
9+
},
10+
"type": {
11+
"name": "cve"
12+
}
13+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"issue": {
3+
"number": 1234,
4+
"labels": [
5+
{
6+
"name": "status/triage"
7+
}
8+
]
9+
},
10+
"type": {
11+
"name": "cve"
12+
}
13+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"issue": {
3+
"number": 1234,
4+
"labels": [
5+
{
6+
"name": "status/triage"
7+
},
8+
{
9+
"name": "kind/bug"
10+
},
11+
{
12+
"name": "kind/cve"
13+
}
14+
]
15+
},
16+
"type": {
17+
"name": "cve"
18+
}
19+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"issue": {
3+
"number": 1234,
4+
"labels": [
5+
{
6+
"name": "status/triage"
7+
}
8+
]
9+
},
10+
"type": {
11+
"name": "cve"
12+
}
13+
}

0 commit comments

Comments
 (0)