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
WhenstateDir starts with ~/, or is relative
Then it is the home directory's, or the repository's
Verified by
NginxProxyManagerUnitTestevery 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
Wheninit --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--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.
> 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)
mi
changed title from WIP: Managed nginx settings and systemd files split into their steps to Managed nginx settings and systemd files split into their steps, gate down to 24 (v1.37.5)2026-10-05 19:09:30 +02:00
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
The Problem
Two methods of the installation are above the Kotlin target of 15, measured by the nightly mutation build of 2026-10-05:
NginxProxyManager.resolveSettingsInitCommand.createSystemdFilesresolveSettingschecks 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.createSystemdFileswrites 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
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.
server.porttakes the HTTPS portstateDirstarts with~/, or is relativeVerified by
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 theserver.portcollisionFeature: 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 --systemdcan follow the printed steps without reading the code.init --systemdrunscreatedline for the unit, the environment file, the two Docker cleanup units, the.htaccessand the maintenance pageln -sfandsystemctl --userlines that install all three unitsVerified by
--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 fileThe Solution
resolveSettingskeeps the order: disabled, then the checks, then the settings.refusalOfreturns the reason a configuration cannot run, in the words of the former warnings, andnullwhen it can;portsValidandstateDirOfhold the port check and the state directory.createSystemdFileshands the writing to aGeneratedFileshelper, which writes a file and prints itscreatedline, and to one step per kind of file:writeServiceUnit,writeEnvFile,writeHostFiles;printInstallHintsprints the install lines.A local PIT run of
NginxProxyManagerandInitCommandafterwards:NginxProxyManager.resolveSettingsNginxProxyManager.refusalOfNginxProxyManager.portsValidNginxProxyManager.stateDirOfInitCommand.createSystemdFilesInitCommand.writeEnvFileInitCommand.writeHostFilesInitCommand.printInstallHintsThe highest score left in both classes is
InitCommand.runningVersionat 10.75.The project maximum is now
FileArtifactStore.pruneat 23.50, so the gate goes down from 28 to 24.Additional Changes
maxMutationCrapinbuild.gradle.ktslowered from 28 to 24.releases.html.🤖 Generated with Claude Code
WIP: Managed nginx settings and systemd files split into their stepsto Managed nginx settings and systemd files split into their steps, gate down to 24 (v1.37.5)