diff --git a/docs/prs/2026-08-31-PR#1-rename-gittally-to-werkator.md b/docs/prs/2026-08-31-PR#1-rename-gittally-to-werkator.md index 405ba08..8f6c079 100644 --- a/docs/prs/2026-08-31-PR#1-rename-gittally-to-werkator.md +++ b/docs/prs/2026-08-31-PR#1-rename-gittally-to-werkator.md @@ -141,6 +141,9 @@ The deployment to vm4006 found the gap the hard way: the move renames the *direc The instance came up with empty credentials and no host build definitions, without an error, which is the failure this PR exists to prevent. `StateDirMigration` now renames the configuration along with the directory, unless one under the current name is already there, and `ConfigFiles` carries the intermediate path as a third candidate for a directory somebody moved by hand. +The same deployment found the second half of it: `init --systemd` runs `init`, whose "does it already exist" check knew only the current name, so it wrote a fresh template `.werkator.yml` beside the repository's committed `.gittally.yml` — and the current name wins, so the repository would have started building the template's `./gradlew test` instead of what it says it builds. +Both checks now ask `ConfigFiles`, which is the same question the loader asks. + **A one-time move for the state.** [`StateDirMigration`](../../src/main/kotlin/de/hoennig/werkator/StateDirMigration.kt) renames `.git/gittally` to `.git/werkator` from `CliRunner`, before any command resolves a path under it and therefore before the second Spring context of `server` exists. A fallback was rejected here: unlike a configuration, the state is written, so a fallback would have to decide on every write which of two directories wins, where a one-time move decides once and leaves a single path behind. diff --git a/src/main/kotlin/de/hoennig/werkator/commands/InitCommand.kt b/src/main/kotlin/de/hoennig/werkator/commands/InitCommand.kt index 449ba4d..d4a1a3c 100644 --- a/src/main/kotlin/de/hoennig/werkator/commands/InitCommand.kt +++ b/src/main/kotlin/de/hoennig/werkator/commands/InitCommand.kt @@ -1,6 +1,7 @@ package de.hoennig.werkator.commands import de.hoennig.werkator.SecretFiles +import de.hoennig.werkator.config.ConfigFiles import de.hoennig.werkator.git.GitService import org.springframework.beans.factory.ObjectProvider import org.springframework.boot.info.BuildProperties @@ -99,7 +100,8 @@ class InitCommand( detected: DetectedValues, normalizedWorkingDir: Path, ) { - val file = root.resolve(".git/werkator/.werkator.yml") + val existing = ConfigFiles.firstExisting(root, ConfigFiles.repoInstall) + val file = root.resolve(existing) if (file.toFile().exists()) { println("${file.toFile().relativeTo(normalizedWorkingDir.toFile())} already exists — not overwritten") return @@ -123,7 +125,11 @@ class InitCommand( detected: DetectedValues, normalizedWorkingDir: Path, ) { - val file = root.resolve(".werkator.yml") + // under either name: writing a second one beside a config under the previous + // name would shadow it, and a repository would silently start building + // something else than what it says it builds + val existing = ConfigFiles.firstExisting(root) + val file = root.resolve(existing) if (file.toFile().exists()) { println("${file.toFile().relativeTo(normalizedWorkingDir.toFile())} already exists — not overwritten") return diff --git a/src/test/kotlin/de/hoennig/werkator/commands/InitCommandTest.kt b/src/test/kotlin/de/hoennig/werkator/commands/InitCommandTest.kt index 62e7b63..57dd631 100644 --- a/src/test/kotlin/de/hoennig/werkator/commands/InitCommandTest.kt +++ b/src/test/kotlin/de/hoennig/werkator/commands/InitCommandTest.kt @@ -2,6 +2,7 @@ package de.hoennig.werkator.commands import de.hoennig.werkator.git.GitService import io.kotest.core.spec.style.FunSpec +import io.kotest.matchers.booleans.shouldBeFalse import io.kotest.matchers.file.shouldExist import io.kotest.matchers.shouldBe import io.kotest.matchers.string.shouldContain @@ -38,6 +39,25 @@ class InitCommandTest : FunSpec() { repoContent shouldContain "account: \"\"" // no user in https URL } + test("does not write a second config beside one under the previous name") { + val tempDir = Files.createTempDirectory("werkator-init-test") + initCommand.workingDir = tempDir + Files.createDirectories(tempDir.resolve(".git/gittally")) + tempDir.resolve(".gittally.yml").toFile().writeText("gitea:\n owner: kept\n") + tempDir.resolve(".git/gittally/.gittally.yml").toFile().writeText("git:\n account: kept\n") + + every { gitService.getTopLevel(tempDir) } returns tempDir + every { gitService.getOriginUrl(tempDir) } returns "https://git.example.org/my-org/my-repo.git" + + initCommand.run() + + // a second file would shadow the first one, and the repository would + // silently build something else than what its configuration says + Files.exists(tempDir.resolve(".werkator.yml")).shouldBeFalse() + Files.exists(tempDir.resolve(".git/werkator/.werkator.yml")).shouldBeFalse() + tempDir.resolve(".gittally.yml").toFile().readText() shouldContain "owner: kept" + } + test("creates the secrets config and its directory readable only by the owner") { val tempDir = Files.createTempDirectory("werkator-init-test") initCommand.workingDir = tempDir