From 7fd473b88b1fc38ef4a9598fe705657f633a9545 Mon Sep 17 00:00:00 2001 From: mhoennig Date: Fri, 4 Sep 2026 17:47:16 +0200 Subject: [PATCH] Warn instead of silently defaulting when init cannot load the config MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `init --systemd` read the effective configuration through a catch-all that swallowed every exception and continued with a default `ServerConfig`. A repository whose `.werkator.yml` failed validation for any reason then looked exactly like one with no `publicBaseUrl`: the `.htaccess` and the maintenance page were skipped without a word, during initial deployment setup of all times. The fallback stays — the units are still generated — but the exception message is now printed. The configuration is loaded once per run so the three readers inside `createSystemdFiles` do not repeat the warning. Co-Authored-By: Claude Opus 5 --- .../hoennig/werkator/commands/InitCommand.kt | 23 +++++++++++++++---- .../werkator/commands/InitCommandTest.kt | 22 ++++++++++++++++++ 2 files changed, 40 insertions(+), 5 deletions(-) diff --git a/src/main/kotlin/de/hoennig/werkator/commands/InitCommand.kt b/src/main/kotlin/de/hoennig/werkator/commands/InitCommand.kt index f7a8616..a824341 100644 --- a/src/main/kotlin/de/hoennig/werkator/commands/InitCommand.kt +++ b/src/main/kotlin/de/hoennig/werkator/commands/InitCommand.kt @@ -48,6 +48,7 @@ class InitCommand( internal var javaExecutableResolver: () -> Path = { Paths.get(System.getProperty("java.home"), "bin", "java") } override fun run() { + cachedServerConfig = null val normalizedWorkingDir = workingDir.toAbsolutePath().normalize() val root = try { @@ -298,12 +299,24 @@ class InitCommand( */ private fun loadedSystemdConfig(): de.hoennig.werkator.config.SystemdConfig = loadedServerConfig().systemd + /** Loaded once per run, so a broken configuration is reported once and not per caller. */ + private var cachedServerConfig: de.hoennig.werkator.config.ServerConfig? = null + + /** + * A configuration error here is not fatal — the units are still generated with defaults — + * but it must not pass for "nothing configured": without the warning a broken `.werkator.yml` + * looks exactly like an unset `publicBaseUrl` and the host integration is skipped silently. + */ private fun loadedServerConfig(): de.hoennig.werkator.config.ServerConfig = - try { - configLoader.load(Paths.get(".")).server - } catch (_: Exception) { - de.hoennig.werkator.config - .ServerConfig() + cachedServerConfig ?: run { + try { + configLoader.load(Paths.get(".")).server + } catch (e: Exception) { + println("Warning: the effective configuration could not be loaded (${e.message})") + println(" continuing with default server settings — check the generated unit and host files") + de.hoennig.werkator.config + .ServerConfig() + }.also { cachedServerConfig = it } } private fun createSystemdFiles( diff --git a/src/test/kotlin/de/hoennig/werkator/commands/InitCommandTest.kt b/src/test/kotlin/de/hoennig/werkator/commands/InitCommandTest.kt index 14ae12d..2d16448 100644 --- a/src/test/kotlin/de/hoennig/werkator/commands/InitCommandTest.kt +++ b/src/test/kotlin/de/hoennig/werkator/commands/InitCommandTest.kt @@ -252,5 +252,27 @@ class InitCommandTest : FunSpec() { // This should not throw IllegalArgumentException initCommand.run() } + + test("--systemd warns once when the effective configuration cannot be loaded") { + val tempDir = Files.createTempDirectory("werkator-init-test") + val brokenLoader = mockk() + every { brokenLoader.load(any()) } throws IllegalStateException("gitea.owner is required") + val command = InitCommand(gitService, brokenLoader) + command.workingDir = tempDir + command.systemd = true + command.jarPathResolver = { Paths.get("/home/ci/bin/werkator.jar") } + command.javaExecutableResolver = { Paths.get("/usr/bin/java") } + + every { gitService.getTopLevel(tempDir) } returns tempDir + every { gitService.getOriginUrl(tempDir) } returns "https://git.example.org/my-org/my-repo.git" + + val console = captureConsole { command.run() } + + console.stdout shouldContain "gitea.owner is required" + // the three readers of the configuration must not repeat the warning + console.stdout.windowed("Warning:".length).count { it == "Warning:" } shouldBe 1 + // the units are still written with the defaults + tempDir.resolve(".git/werkator/${SystemdServiceFiles.unitName(tempDir)}").toFile().shouldExist() + } } }