# PR#124: Managed nginx Settings and systemd Files Split into Their Steps > **WARNING:** This document describes only the change applied in this PR. > It may already be outdated once the next PR is merged. > Historic PR-documentation is not maintained along with new PRs — treat it as a snapshot, not as current documentation. ## The Problem Two methods of the installation are above the Kotlin target of 15, measured by the nightly mutation build of 2026-10-05: | Method | Mutation-CRAP | Complexity | Mutation score | |-------------------------------------|---------------|------------|----------------| | `NginxProxyManager.resolveSettings` | 27.44 | 18 | 0.69 | | `InitCommand.createSystemdFiles` | 17.40 | 5 | 0.21 | `resolveSettings` checks every setting of the managed nginx and builds its settings in one method; no test tried invalid ports, an invalid upstream host or a configured state directory. `createSystemdFiles` writes six files and prints what it wrote and how to install it; no test read that output, so 38 of 48 mutants survived. ## Non-Goals - No change in behaviour: the same settings are refused with the same warnings, the same files are written with the same lines printed. - The other groups above 15 follow in their own PRs. ## The Scenarios ### Feature: the managed nginx refuses what it cannot run #### Scenario#124.01: every setting nginx cannot run with is refused, a configured state directory read as the host means it So that a misconfigured nginx is refused before a container starts, and a valid one is never refused. - **Given** the managed nginx is enabled - **When** a port is 0 or above 65535, both ports are the same, the upstream host is no host name, or `server.port` takes the HTTPS port - **Then** no settings are resolved, and the warning names the reason - **When** the ports are 1 and 65535, or an upstream host of its own is configured - **Then** the settings carry them - **When** `stateDir` starts with `~/`, or is relative - **Then** it is the home directory's, or the repository's ##### Verified by - [NginxProxyManagerUnitTest](../../src/test/kotlin/de/hoennig/werkator/server/NginxProxyManagerUnitTest.kt) `every setting nginx cannot run with is refused, the ports up to their limits accepted (Scenario#124.01)`, `a configured state directory has its ~ expanded and is read relative to the repository (Scenario#124.01)`, and the earlier tests of the server name and the `server.port` collision ### Feature: init --systemd says what it did #### Scenario#124.02: init --systemd says what it created and how to install it So that the person running `init --systemd` can follow the printed steps without reading the code. - **Given** a repository whose configuration has a public base URL - **When** `init --systemd` runs - **Then** it prints one `created` line for the unit, the environment file, the two Docker cleanup units, the `.htaccess` and the maintenance page - **and** the `ln -sf` and `systemctl --user` lines that install all three units - **When** it runs again - **Then** it says that the environment file already exists and is not overwritten - **When** the jar path cannot be determined - **Then** it prints the error and writes no unit ##### Verified by - [InitCommandUnitTest](../../src/test/kotlin/de/hoennig/werkator/commands/InitCommandUnitTest.kt) `--systemd says what it created and how to install it, and keeps a host's environment file (Scenario#124.02)`, `--systemd without a resolvable jar path generates no unit file` ## The Solution `resolveSettings` keeps the order: disabled, then the checks, then the settings. `refusalOf` returns the reason a configuration cannot run, in the words of the former warnings, and `null` when it can; `portsValid` and `stateDirOf` hold the port check and the state directory. `createSystemdFiles` hands the writing to a `GeneratedFiles` helper, which writes a file and prints its `created` line, and to one step per kind of file: `writeServiceUnit`, `writeEnvFile`, `writeHostFiles`; `printInstallHints` prints the install lines. A local PIT run of `NginxProxyManager` and `InitCommand` afterwards: | Method | Mutation-CRAP | Complexity | Mutation score | |-------------------------------------|---------------|------------|----------------| | `NginxProxyManager.resolveSettings` | 5.58 | 5 | 0.71 | | `NginxProxyManager.refusalOf` | 7 | 7 | 1.00 | | `NginxProxyManager.portsValid` | 8.30 | 8 | 0.83 | | `NginxProxyManager.stateDirOf` | 3.14 | 3 | 0.75 | | `InitCommand.createSystemdFiles` | 2.09 | 2 | 0.71 | | `InitCommand.writeEnvFile` | 2.15 | 2 | 0.67 | | `InitCommand.writeHostFiles` | 2 | 2 | 1.00 | | `InitCommand.printInstallHints` | 1 | 1 | 0.86 | The highest score left in both classes is `InitCommand.runningVersion` at 10.75. The project maximum is now `FileArtifactStore.prune` at 23.50, so the gate goes down from 28 to 24. ## Additional Changes - `maxMutationCrap` in `build.gradle.kts` lowered from 28 to 24. - Version 1.37.5 with its entry in `releases.html`. 🤖 Generated with [Claude Code](https://claude.com/claude-code)