init reads its configuration from the repository root, and says so when it cannot #22

Closed
mi wants to merge 3 commits from mi/init-config-root AGit into main
2 changed files with 34 additions and 6 deletions
Showing only changes of commit 2cddd76c88 - Show all commits
@@ -297,20 +297,25 @@ class InitCommand(
* already loadable (re-running `init --systemd` on an installed instance); during * already loadable (re-running `init --systemd` on an installed instance); during
* the very first bootstrap they stay unset and the defaults (no directives) apply. * the very first bootstrap they stay unset and the defaults (no directives) apply.
*/ */
private fun loadedSystemdConfig(): de.hoennig.werkator.config.SystemdConfig = loadedServerConfig().systemd private fun loadedSystemdConfig(root: Path): de.hoennig.werkator.config.SystemdConfig = loadedServerConfig(root).systemd
/** Loaded once per run, so a broken configuration is reported once and not per caller. */ /** Loaded once per run, so a broken configuration is reported once and not per caller. */
private var cachedServerConfig: de.hoennig.werkator.config.ServerConfig? = null private var cachedServerConfig: de.hoennig.werkator.config.ServerConfig? = null
/** /**
* Read from the repository root like every other file this command touches — the
* layers sit there, not in whatever directory the process happens to run in, and
* an applied fragment must reach the generated unit even when `init` is invoked
* from a subdirectory.
*
* A configuration error here is not fatal — the units are still generated with defaults — * 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` * 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. * looks exactly like an unset `publicBaseUrl` and the host integration is skipped silently.
*/ */
private fun loadedServerConfig(): de.hoennig.werkator.config.ServerConfig = private fun loadedServerConfig(root: Path): de.hoennig.werkator.config.ServerConfig =
cachedServerConfig ?: run { cachedServerConfig ?: run {
try { try {
configLoader.load(Paths.get(".")).server configLoader.load(root).server
} catch (e: Exception) { } catch (e: Exception) {
println("Warning: the effective configuration could not be loaded (${e.message})") println("Warning: the effective configuration could not be loaded (${e.message})")
println(" continuing with default server settings — check the generated unit and host files") println(" continuing with default server settings — check the generated unit and host files")
@@ -340,8 +345,8 @@ class InitCommand(
javaExecutable = javaExecutableResolver(), javaExecutable = javaExecutableResolver(),
jarPath = jarPath, jarPath = jarPath,
envFile = envFile, envFile = envFile,
memoryMax = loadedSystemdConfig().memoryMax, memoryMax = loadedSystemdConfig(root).memoryMax,
tasksMax = loadedSystemdConfig().tasksMax, tasksMax = loadedSystemdConfig(root).tasksMax,
), ),
) )
println("created ${unitFile.toFile().relativeTo(normalizedWorkingDir.toFile())}") println("created ${unitFile.toFile().relativeTo(normalizedWorkingDir.toFile())}")
@@ -364,7 +369,7 @@ class InitCommand(
// generated host integration like the units: only meaningful behind a web // generated host integration like the units: only meaningful behind a web
// frontend, so it needs a public base URL; unused elsewhere and harmless // frontend, so it needs a public base URL; unused elsewhere and harmless
val server = loadedServerConfig() val server = loadedServerConfig(root)
if (server.publicBaseUrl.isNotBlank()) { if (server.publicBaseUrl.isNotBlank()) {
val htaccessFile = werkatorDir.resolve(SystemdServiceFiles.HTACCESS_NAME) val htaccessFile = werkatorDir.resolve(SystemdServiceFiles.HTACCESS_NAME)
htaccessFile.toFile().writeText(SystemdServiceFiles.htaccessContent(server.port)) htaccessFile.toFile().writeText(SystemdServiceFiles.htaccessContent(server.port))
@@ -253,6 +253,29 @@ class InitCommandTest : FunSpec() {
initCommand.run() initCommand.run()
} }
test("--systemd reads the configuration from the repository root, not the current directory") {
val tempDir = Files.createTempDirectory("werkator-init-test")
// written before the run, so `init` keeps it instead of creating a template
tempDir.resolve(".werkator.yml").toFile().writeText(
"server:\n publicBaseUrl: \"https://werkator.example.org/\"\n port: 18099\n",
)
initCommand.workingDir = tempDir
initCommand.systemd = true
initCommand.jarPathResolver = { Paths.get("/home/ci/bin/werkator.jar") }
initCommand.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"
initCommand.run()
// the host integration is generated only when the root's config has a public base URL
val htaccess = tempDir.resolve(".git/werkator/${SystemdServiceFiles.HTACCESS_NAME}")
htaccess.toFile().shouldExist()
htaccess.toFile().readText() shouldContain "18099"
tempDir.resolve(".git/werkator/${SystemdServiceFiles.MAINTENANCE_PAGE_NAME}").toFile().shouldExist()
}
test("--systemd warns once when the effective configuration cannot be loaded") { test("--systemd warns once when the effective configuration cannot be loaded") {
val tempDir = Files.createTempDirectory("werkator-init-test") val tempDir = Files.createTempDirectory("werkator-init-test")
val brokenLoader = mockk<de.hoennig.werkator.config.ConfigLoader>() val brokenLoader = mockk<de.hoennig.werkator.config.ConfigLoader>()