Run CI orchestration and setup as Java 25 source programs
Some checks failed
Build, test and publish / verify (push) Failing after 11s
Some checks failed
Build, test and publish / verify (push) Failing after 11s
This commit is contained in:
71
ci/Commands.java
Normal file
71
ci/Commands.java
Normal file
@@ -0,0 +1,71 @@
|
||||
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();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user