diff --git a/ci/Setup.java b/ci/Setup.java index c1ed5eb..4cf4a4c 100644 --- a/ci/Setup.java +++ b/ci/Setup.java @@ -108,10 +108,20 @@ class Setup { Pipeline.check(actual.contains("v0.2.13"), "Expected runner v0.2.13"); privateWrite(runnerDir.resolve("config.yaml"),Files.readString(root.resolve("ci/runner/config.yaml"))); Files.copy(root.resolve("ci/runner/agiledancers-runner.service"),units.resolve("agiledancers-runner.service"),StandardCopyOption.REPLACE_EXISTING); + Files.copy(root.resolve("ci/runner/WaitForDocker.java"),runnerDir.resolve("WaitForDocker.java"),StandardCopyOption.REPLACE_EXISTING); String dockerHost = env.get("DOCKER_HOST"); if (dockerHost == null || dockerHost.isBlank()) dockerHost = commands.execute(List.of("docker","context","inspect","--format","{{.Endpoints.docker.Host}}"),root,env,null,false).checked(); String path = javaHome + "/bin:" + executable(env,"node").getParent() + ":" + executable(env,"mvn").getParent() + ":/usr/local/bin:/usr/bin:/bin"; privateWrite(configDir.resolve("environment"),"JAVA_HOME=" + quoted(javaHome) + "\nPATH=" + quoted(path) + "\nDOCKER_HOST=" + quoted(dockerHost) + "\n"); + boolean desktop = dockerHost.endsWith("/.docker/desktop/docker.sock"); + Path overrides = units.resolve("agiledancers-runner.service.d"); + Path desktopOverride = overrides.resolve("10-agiledancers-docker-desktop.conf"); + if (desktop) { + Files.createDirectories(overrides); + Files.copy(root.resolve("ci/runner/docker-desktop.conf"),desktopOverride,StandardCopyOption.REPLACE_EXISTING); + } else { + Files.deleteIfExists(desktopOverride); + } commands.execute(List.of("systemctl","--user","daemon-reload"),root,env,null,true).checked(); if (prepareOnly) { System.out.println("Runner installed; registration and startup still required."); return; } commands.execute(List.of("docker","info"),root,env,null,false).checked(); @@ -123,6 +133,11 @@ class Setup { "--name","agiledancers-mini-pc","--labels","agiledancers-mini-pc:host","--token",token),runnerDir,env,null,true).checked(); Files.setPosixFilePermissions(runnerDir.resolve(".runner"),PosixFilePermissions.fromString("rw-------")); } + if (desktop) { + commands.execute(List.of("systemctl","--user","enable","--now","docker-desktop.service"),root,env,null,true).checked(); + // Replace any previous default.target link with the desktop-login target. + commands.execute(List.of("systemctl","--user","disable","agiledancers-runner.service"),root,env,null,true).checked(); + } commands.execute(List.of("systemctl","--user","enable","--now","agiledancers-runner.service"),root,env,null,true).checked(); System.out.println("Runner registered and started; keep the mini-PC and Docker available."); } diff --git a/ci/runner/WaitForDocker.java b/ci/runner/WaitForDocker.java new file mode 100644 index 0000000..27c8aab --- /dev/null +++ b/ci/runner/WaitForDocker.java @@ -0,0 +1,35 @@ +import java.time.Duration; +import java.util.concurrent.TimeUnit; + +/** Source-launcher startup probe; uses the service's configured DOCKER_HOST. */ +class WaitForDocker { + public static void main(String[] args) throws Exception { + long seconds = args.length == 0 ? 120 : Long.parseLong(args[0]); + if (seconds < 1 || seconds > 300) throw new IllegalArgumentException("Timeout must be 1–300 seconds"); + long deadline = System.nanoTime() + Duration.ofSeconds(seconds).toNanos(); + System.out.println("Waiting for the configured Docker engine..."); + while (System.nanoTime() < deadline) { + Process process = new ProcessBuilder("docker", "info", "--format", "{{.ServerVersion}}") + .redirectOutput(ProcessBuilder.Redirect.DISCARD) + .redirectError(ProcessBuilder.Redirect.DISCARD) + .start(); + try { + long remaining = Math.max(1, deadline - System.nanoTime()); + if (process.waitFor(Math.min(remaining, TimeUnit.SECONDS.toNanos(5)), TimeUnit.NANOSECONDS) + && process.exitValue() == 0) { + System.out.println("Docker is available; starting the Gitea runner."); + return; + } + } finally { + if (process.isAlive()) { + process.destroyForcibly(); + process.waitFor(); + } + } + long remaining = deadline - System.nanoTime(); + if (remaining > 0) TimeUnit.NANOSECONDS.sleep(Math.min(remaining, TimeUnit.SECONDS.toNanos(2))); + } + System.err.println("Docker did not become available within " + seconds + " seconds; runner not started."); + System.exit(1); + } +} diff --git a/ci/runner/agiledancers-runner.service b/ci/runner/agiledancers-runner.service index 29d4dd6..3007e90 100644 --- a/ci/runner/agiledancers-runner.service +++ b/ci/runner/agiledancers-runner.service @@ -7,6 +7,8 @@ Wants=network-online.target Type=simple WorkingDirectory=%h/.local/share/agiledancers-runner EnvironmentFile=%h/.config/agiledancers-runner/environment +ExecStartPre=/usr/bin/env java %h/.local/share/agiledancers-runner/WaitForDocker.java 120 +TimeoutStartSec=150 ExecStart=%h/.local/bin/act_runner daemon --config %h/.local/share/agiledancers-runner/config.yaml Restart=on-failure RestartSec=10 diff --git a/ci/runner/docker-desktop.conf b/ci/runner/docker-desktop.conf new file mode 100644 index 0000000..a3ffb02 --- /dev/null +++ b/ci/runner/docker-desktop.conf @@ -0,0 +1,9 @@ +# Desktop-specific override: start after graphical login, alongside Docker Desktop. +[Unit] +Requires=docker-desktop.service +After=graphical-session.target docker-desktop.service +PartOf=graphical-session.target docker-desktop.service + +[Install] +WantedBy= +WantedBy=graphical-session.target diff --git a/docs/gitea-ci.md b/docs/gitea-ci.md index 6b2d3e7..9ef61ca 100644 --- a/docs/gitea-ci.md +++ b/docs/gitea-ci.md @@ -158,10 +158,17 @@ Een bestaande registratie wordt hergebruikt. Er worden geen secrets in de repository geschreven. Bewaar het tokenbestand alleen zolang registratie/herstel dat nodig heeft en behandel `.runner` als credential. -De gebruikersservice start bij een gebruikerssessie. Automatisch doorlopen zonder -inloggen vereist daarnaast een bewuste keuze voor user lingering en een Docker- -daemon die zonder desktopsessie beschikbaar is. S4 belooft geen beschikbaarheid -wanneer de mini-pc uitstaat. Herstarten/stoppen: +Op de mini-pc starten Docker Desktop en de runner automatisch na grafisch +aanmelden. Bij een Docker Desktop-socket installeert de installer hiervoor +`~/.config/systemd/user/agiledancers-runner.service.d/10-agiledancers-docker-desktop.conf` +en schakelt beide services in voor `graphical-session.target`. De runner wacht +via `~/.local/share/agiledancers-runner/WaitForDocker.java` maximaal 120 seconden +tot de ingestelde Docker-engine bereikbaar is. Bij een mislukte start probeert +systemd het na tien seconden opnieuw. De runner volgt stoppen en herstarten van +Docker Desktop en stopt bij afmelden. + +User lingering staat op deze pc uit: vóór aanmelden starten deze services niet. +S4 belooft geen beschikbaarheid wanneer de mini-pc uitstaat. Herstarten/stoppen: ```sh systemctl --user restart agiledancers-runner