Skip to content

Commit 300cd85

Browse files
iocanelgastaldi
andauthored
Gitea DevService Request and Org (#158)
* feat: allow control of gitea dev service via build items * feat: allow creating an organization in gitea * refactor: allow specifying multiple organizations * Debug StdOut when creating an org --------- Co-authored-by: George Gastaldi <[email protected]>
1 parent ed90d1d commit 300cd85

File tree

10 files changed

+364
-46
lines changed

10 files changed

+364
-46
lines changed

deployment/src/main/java/io/quarkus/jgit/deployment/GiteaContainer.java

Lines changed: 150 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,12 @@
44

55
import java.io.IOException;
66
import java.util.ArrayList;
7-
import java.util.Collections;
87
import java.util.List;
8+
import java.util.Optional;
99

1010
import org.jboss.logging.Logger;
1111
import org.testcontainers.containers.GenericContainer;
12+
import org.testcontainers.containers.Network;
1213

1314
import com.github.dockerjava.api.command.InspectContainerResponse;
1415

@@ -18,21 +19,34 @@ class GiteaContainer extends GenericContainer<GiteaContainer> {
1819
* Logger which will be used to capture container STDOUT and STDERR.
1920
*/
2021
private static final Logger log = Logger.getLogger(GiteaContainer.class);
21-
private static final int HTTP_PORT = 3000;
22+
static final int HTTP_PORT = 3000;
2223

2324
private JGitBuildTimeConfig.DevService devServiceConfig;
25+
private Optional<GiteaDevServiceRequestBuildItem> devServiceRequest;
26+
27+
private List<String> organizations = new ArrayList<>();
2428
private List<String> repositories = new ArrayList<>();
2529

26-
GiteaContainer(JGitBuildTimeConfig.DevService devServiceConfig) {
30+
GiteaContainer(JGitBuildTimeConfig.DevService devServiceConfig,
31+
Optional<GiteaDevServiceRequestBuildItem> devServiceRequest) {
2732
super("gitea/gitea:latest-rootless");
2833
this.devServiceConfig = devServiceConfig;
34+
this.devServiceRequest = devServiceRequest;
2935
withEnv("GITEA__security__INSTALL_LOCK", "true");
3036
withEnv("GITEA__server__DISABLE_SSH", "true");
3137
withExposedPorts(HTTP_PORT);
3238
withReuse(devServiceConfig.reuse());
3339
waitingFor(forListeningPorts(HTTP_PORT));
3440
// Needed for podman (see https://github.com/testcontainers/testcontainers-java/issues/7310)
3541
withStartupAttempts(2);
42+
43+
Optional<String> networkAlias = devServiceConfig.networkAlias()
44+
.or(() -> devServiceRequest.map(GiteaDevServiceRequestBuildItem::getAlias));
45+
networkAlias.ifPresent(alias -> {
46+
withNetworkAliases(alias);
47+
withNetwork(Network.SHARED);
48+
});
49+
3650
devServiceConfig.httpPort().ifPresent(port -> addFixedExposedPort(port, HTTP_PORT));
3751
if (devServiceConfig.showLogs()) {
3852
withLogConsumer(new JBossLoggingConsumer(log));
@@ -44,8 +58,23 @@ protected void containerIsStarted(InspectContainerResponse containerInfo, boolea
4458
if (!reused) {
4559
try {
4660
createAdminUser();
47-
for (String repository : devServiceConfig.repositories().orElse(Collections.emptyList())) {
48-
createRepository(this, repository);
61+
List<String> organizations = new ArrayList<>();
62+
List<String> repositories = new ArrayList<>();
63+
64+
devServiceConfig.organizations().ifPresent(o -> organizations.addAll(o));
65+
devServiceConfig.repositories().ifPresent(r -> repositories.addAll(r));
66+
67+
devServiceRequest.map(GiteaDevServiceRequestBuildItem::getOrganizations)
68+
.ifPresent(o -> organizations.addAll(o));
69+
devServiceRequest.map(GiteaDevServiceRequestBuildItem::getRepositories)
70+
.ifPresent(r -> repositories.addAll(r));
71+
72+
for (String org : organizations) {
73+
createOrganization(org);
74+
}
75+
76+
for (String repository : repositories) {
77+
createRepository(repository);
4978
}
5079
} catch (IOException | InterruptedException e) {
5180
throw new RuntimeException("Failed to create admin user", e);
@@ -70,15 +99,56 @@ private void createAdminUser() throws IOException, InterruptedException {
7099
};
71100
log.debug(String.join(" ", cmd));
72101
ExecResult execResult = execInContainer(cmd);
73-
log.info(execResult.getStdout());
102+
log.debug(execResult.getStdout());
74103
if (execResult.getExitCode() != 0) {
75104
throw new RuntimeException("Failed to create admin user: " + execResult.getStderr());
76105
}
77106
}
78107

79-
private void createRepository(GiteaContainer giteaContainer, String repository)
108+
private void createOrganization(String org)
109+
throws UnsupportedOperationException, IOException, InterruptedException {
110+
String httpUrl = "http://localhost:" + GiteaContainer.HTTP_PORT + "/api/v1/orgs";
111+
String data = """
112+
{"username":"%s"}
113+
"""
114+
.formatted(org);
115+
116+
String[] cmd = {
117+
"/usr/bin/curl",
118+
"-X",
119+
"POST",
120+
"--user",
121+
devServiceConfig.adminUsername() + ":" + devServiceConfig.adminPassword(),
122+
"-H",
123+
"Content-Type: application/json",
124+
"-d",
125+
data,
126+
httpUrl
127+
};
128+
129+
log.debug(String.join(" ", cmd));
130+
ExecResult execResult = execInContainer(cmd);
131+
log.debug(execResult.getStdout());
132+
if (execResult.getExitCode() != 0) {
133+
throw new RuntimeException("Failed to create organization: " + org + ":" + execResult.getStderr());
134+
}
135+
organizations.add(org);
136+
log.info("Created organization: " + org);
137+
}
138+
139+
private void createRepository(String repository) throws UnsupportedOperationException, IOException, InterruptedException {
140+
if (repository.contains("/")) {
141+
String orgName = repository.substring(0, repository.indexOf("/"));
142+
String repoName = repository.substring(repository.indexOf("/") + 1);
143+
createOrgRepository(orgName, repoName);
144+
} else {
145+
createUserRepository(repository);
146+
}
147+
}
148+
149+
private void createUserRepository(String repository)
80150
throws UnsupportedOperationException, IOException, InterruptedException {
81-
String httpUrl = "http://localhost:" + GiteaContainer.HTTP_PORT;
151+
String httpUrl = "http://localhost:" + GiteaContainer.HTTP_PORT + "/api/v1/user/repos";
82152
String data = """
83153
{"name":"%s", "private":false, "auto_init":true, "readme":"Default"}
84154
"""
@@ -94,17 +164,81 @@ private void createRepository(GiteaContainer giteaContainer, String repository)
94164
"Content-Type: application/json",
95165
"-d",
96166
data,
97-
httpUrl + "/api/v1/user/repos"
167+
httpUrl
98168
};
99169

100170
log.debug(String.join(" ", cmd));
101-
ExecResult execResult = giteaContainer.execInContainer(cmd);
102-
log.info(execResult.getStdout());
171+
ExecResult execResult = execInContainer(cmd);
172+
log.debug(execResult.getStdout());
103173
if (execResult.getExitCode() != 0) {
104174
throw new RuntimeException("Failed to create repository: " + repository + ":" + execResult.getStderr());
105175
}
106176
repositories.add(repository);
107-
log.info("Created repository: " + repository);
177+
log.info("Created user repository: " + repository);
178+
}
179+
180+
private void createOrgRepository(String org, String repository)
181+
throws UnsupportedOperationException, IOException, InterruptedException {
182+
183+
if (!organizationExists(org)) {
184+
createOrganization(org);
185+
}
186+
187+
String httpUrl = "http://localhost:" + GiteaContainer.HTTP_PORT + "/api/v1/orgs/" + org + "/repos";
188+
String data = """
189+
{"name":"%s", "private":false, "auto_init":true, "readme":"Default"}
190+
"""
191+
.formatted(repository);
192+
193+
String[] cmd = {
194+
"/usr/bin/curl",
195+
"-X",
196+
"POST",
197+
"--user",
198+
devServiceConfig.adminUsername() + ":" + devServiceConfig.adminPassword(),
199+
"-H",
200+
"Content-Type: application/json",
201+
"-d",
202+
data,
203+
httpUrl
204+
};
205+
206+
log.debug(String.join(" ", cmd));
207+
ExecResult execResult = execInContainer(cmd);
208+
log.debug(execResult.getStdout());
209+
if (execResult.getExitCode() != 0) {
210+
throw new RuntimeException("Failed to create repository: " + repository + ":" + execResult.getStderr());
211+
}
212+
repositories.add(org + "/" + repository);
213+
log.info("Created org repository: " + org + "/" + repository);
214+
}
215+
216+
private boolean organizationExists(String org) throws IOException, InterruptedException {
217+
String httpUrl = "http://localhost:" + GiteaContainer.HTTP_PORT + "/api/v1/orgs/" + org;
218+
219+
String[] cmd = {
220+
"/usr/bin/curl",
221+
"-X",
222+
"GET",
223+
"--user",
224+
devServiceConfig.adminUsername() + ":" + devServiceConfig.adminPassword(),
225+
"-H",
226+
"Content-Type: application/json",
227+
httpUrl
228+
};
229+
230+
log.debug(String.join(" ", cmd));
231+
ExecResult execResult = execInContainer(cmd);
232+
233+
if (execResult.getExitCode() == 0) {
234+
log.debug("Organization exists: " + org);
235+
return true;
236+
} else if (execResult.getExitCode() == 404) {
237+
log.debug("Organization does not exist: " + org);
238+
return false;
239+
} else {
240+
throw new RuntimeException("Error checking organization existence: " + execResult.getStderr());
241+
}
108242
}
109243

110244
public String getHttpUrl() {
@@ -118,4 +252,8 @@ public int getHttpPort() {
118252
public List<String> getRepositories() {
119253
return repositories;
120254
}
255+
256+
public List<String> getOrganizations() {
257+
return organizations;
258+
}
121259
}

deployment/src/main/java/io/quarkus/jgit/deployment/GiteaDevServiceInfoBuildItem.java

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package io.quarkus.jgit.deployment;
22

33
import java.util.List;
4+
import java.util.Optional;
5+
import java.util.OptionalInt;
46

57
import io.quarkus.builder.item.SimpleBuildItem;
68

@@ -11,15 +13,29 @@ public final class GiteaDevServiceInfoBuildItem extends SimpleBuildItem {
1113

1214
private final String host;
1315
private final int httpPort;
16+
17+
private final Optional<String> sharedNetworkHost;
18+
private final OptionalInt sharedNetworkHttpPort;
19+
1420
private final String adminUsername;
1521
private final String adminPassword;
22+
private final List<String> organizations;
1623
private final List<String> repositories;
1724

1825
public GiteaDevServiceInfoBuildItem(String host, int httpPort, String adminUsername, String adminPassword,
26+
List<String> organizations, List<String> repositories) {
27+
this(host, httpPort, Optional.empty(), OptionalInt.empty(), adminUsername, adminPassword, organizations, repositories);
28+
}
29+
30+
public GiteaDevServiceInfoBuildItem(String host, int httpPort, Optional<String> sharedNetworkHost,
31+
OptionalInt sharedNetworkHttpPort, String adminUsername, String adminPassword, List<String> organizations,
1932
List<String> repositories) {
2033
this.host = host;
2134
this.httpPort = httpPort;
35+
this.sharedNetworkHost = sharedNetworkHost;
36+
this.sharedNetworkHttpPort = sharedNetworkHttpPort;
2237
this.adminUsername = adminUsername;
38+
this.organizations = organizations;
2339
this.adminPassword = adminPassword;
2440
this.repositories = repositories;
2541
}
@@ -40,7 +56,19 @@ public String adminPassword() {
4056
return adminPassword;
4157
}
4258

59+
public List<String> organizations() {
60+
return organizations;
61+
}
62+
4363
public List<String> repositories() {
4464
return repositories;
4565
}
66+
67+
public Optional<String> sharedNetworkHost() {
68+
return sharedNetworkHost;
69+
}
70+
71+
public OptionalInt sharedNetworkHttpPort() {
72+
return sharedNetworkHttpPort;
73+
}
4674
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package io.quarkus.jgit.deployment;
2+
3+
import java.util.List;
4+
5+
import io.quarkus.builder.item.SimpleBuildItem;
6+
7+
/**
8+
* A build item that requests the creation of a Gitea dev service, if not already configured.
9+
*/
10+
public final class GiteaDevServiceRequestBuildItem extends SimpleBuildItem {
11+
12+
private String alias = "gitea-dev-service";
13+
private List<String> organizations;
14+
private List<String> repositories;
15+
16+
public GiteaDevServiceRequestBuildItem() {
17+
}
18+
19+
public GiteaDevServiceRequestBuildItem(String alias, List<String> organizations, List<String> repositories) {
20+
this.alias = alias;
21+
this.organizations = organizations;
22+
this.repositories = repositories;
23+
}
24+
25+
public String getAlias() {
26+
return alias;
27+
}
28+
29+
public List<String> getOrganizations() {
30+
return organizations;
31+
}
32+
33+
public List<String> getRepositories() {
34+
return repositories;
35+
}
36+
}

deployment/src/main/java/io/quarkus/jgit/deployment/JGitBuildTimeConfig.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,17 @@ interface DevService {
4848
@WithDefault("quarkus")
4949
String adminPassword();
5050

51+
/**
52+
* The organization to be created when the Dev Service starts.
53+
*/
54+
Optional<List<String>> organizations();
55+
5156
/**
5257
* Repositories to be created when the Dev Service starts.
58+
* A repository may optionally include an organization reference.
59+
* For example, "my-org/my-repo" will create a repository named "my-repo" in the "my-org" organization.
60+
* The organization will be created if missing.
61+
* If no organization is specified, the repository will be created as a user repository.
5362
*/
5463
@WithDefault("${quarkus.application.name}")
5564
Optional<List<String>> repositories();
@@ -59,5 +68,11 @@ interface DevService {
5968
*/
6069
@WithDefault("false")
6170
boolean reuse();
71+
72+
/**
73+
* The network alias for the container.
74+
* Other containers in the same network can use this alias to connect to this container.
75+
*/
76+
Optional<String> networkAlias();
6277
}
6378
}

0 commit comments

Comments
 (0)