Files
agiledancers/generated/ci/Commands.java
Wanja Krah c6a495a93c
All checks were successful
Build, test and publish / verify (push) Successful in 8m54s
S19: consume versioned engineering resources and move landscape documentation
2026-11-08 15:23:10 +01:00

97 lines
3.6 KiB
Java

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.*;
import java.time.*;
import java.util.*;
import java.util.concurrent.*;
/** Executes argument lists directly, without shell interpolation or credential logging. */
class Commands {
record Result(int code, String output) {
String checked() {
if (code != 0) throw new Failure(code, "Command failed with exit status " + code);
return output.strip();
}
}
static class Failure extends RuntimeException {
final int exitCode;
Failure(int exitCode, String message) {
super(message);
this.exitCode = exitCode;
}
}
Instant deadline;
volatile Process active;
Path log;
void renew(Duration timeout) {
deadline = Instant.now().plus(timeout);
}
void cancel() {
Process process = active;
if (process != null) stop(process);
}
Commands(Duration timeout) {
deadline = Instant.now().plus(timeout);
}
Result execute(List<String> args, Path cwd, Map<String, String> env, String input, boolean echo) throws Exception {
if (!Instant.now().isBefore(deadline)) throw new Failure(124, "CI time limit exceeded");
ProcessBuilder builder =
new ProcessBuilder(args).directory(cwd.toFile()).redirectErrorStream(true);
builder.environment().clear();
builder.environment().putAll(env);
Process process = builder.start();
active = process;
ByteArrayOutputStream captured = new ByteArrayOutputStream();
Path destination = log;
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
Future<?> reader = executor.submit(() -> {
try (InputStream stream = process.getInputStream();
OutputStream file = destination == null
? OutputStream.nullOutputStream()
: Files.newOutputStream(
destination, StandardOpenOption.CREATE, StandardOpenOption.APPEND)) {
byte[] buffer = new byte[8192];
int count;
while ((count = stream.read(buffer)) != -1) {
captured.write(buffer, 0, count);
if (echo) {
System.out.write(buffer, 0, count);
System.out.flush();
}
file.write(buffer, 0, count);
}
} catch (IOException e) {
throw new UncheckedIOException(e);
}
});
try {
try (OutputStream stdin = process.getOutputStream()) {
if (input != null) stdin.write(input.getBytes(StandardCharsets.UTF_8));
}
if (!process.waitFor(
Math.max(1, Duration.between(Instant.now(), deadline).toMillis()), TimeUnit.MILLISECONDS)) {
stop(process);
throw new Failure(124, "CI time limit exceeded");
}
reader.get(10, TimeUnit.SECONDS);
return new Result(process.exitValue(), captured.toString(StandardCharsets.UTF_8));
} finally {
if (process.isAlive()) stop(process);
active = null;
}
}
}
private static void stop(Process process) {
process.descendants().forEach(ProcessHandle::destroyForcibly);
process.destroyForcibly();
}
}