After PR#111 BuildDefinition.applyTo had the highest Mutation-CRAP score of Werkator: 34 at a complexity of 34.
A score is never below the complexity, so no test could bring it under the limit of 15.
The method laid a build definition over its branch's settings in one copy of 21 fields, each with its own ?: fallback, the docker and werkdock blocks nested inside.
What the definition says falls into three groups, but the method did not show them:
what the build runs, the rules the instance runs it under, and the sandbox it runs in.
Only one test checked a single field of it (Scenario#72.01); every other field was held only by tests of the config loader.
Non-Goals
No change in behaviour: which settings a definition can say, how they inherit, and which are pinned stay as they are.
The Scenarios
Feature: A build definition laid over its branch's settings
Scenario#112.01: A definition that says nothing runs the branch's settings unchanged
So that builds.<name> with only a trigger builds exactly what the branch describes.
Given a branch with a non-default value for every setting
When an empty definition, or one with empty docker and werkdock blocks, is applied
DockerOverrides.applyTo and WerkdockOverrides.applyTo lay each sandbox's overrides over the branch's sandbox of that kind.
The overrides and their config class now stand side by side, where Scenario#72.03 already holds that every setting of the one exists in the other.
Measured with PIT as the nightly build runs it:
Method
Before
After
BuildDefinition.applyTo
34
no mutants
BuildDefinition.withWork
—
6
BuildDefinition.withRules
—
5
BuildDefinition.withSandbox
—
5
DockerOverrides.applyTo
—
8
WerkdockOverrides.applyTo
—
6
Every mutant of the new methods is killed, none is INCONCLUSIVE; each score equals its complexity.
The gate maxMutationCrap falls from 35 to 34: the highest score left is CapacitySizing.cpu at 33.17.
Additional Changes
Version 1.34.9 with its release note.
BuildExecutorUnitTest, Scenario#108.05, waits for the executing build's process before it cancels the build: a build counts as running before its process is started, and a cancel in between left no process to see terminated (List is empty, seen once in this PR's local build).
## The Problem
After PR#111 `BuildDefinition.applyTo` had the highest Mutation-CRAP score of Werkator: 34 at a complexity of 34.
A score is never below the complexity, so no test could bring it under the limit of 15.
The method laid a build definition over its branch's settings in one `copy` of 21 fields, each with its own `?:` fallback, the docker and werkdock blocks nested inside.
What the definition says falls into three groups, but the method did not show them:
what the build runs, the rules the instance runs it under, and the sandbox it runs in.
Only one test checked a single field of it (Scenario#72.01); every other field was held only by tests of the config loader.
## Non-Goals
- No change in behaviour: which settings a definition can say, how they inherit, and which are pinned stay as they are.
## The Scenarios
### Feature: A build definition laid over its branch's settings
#### Scenario#112.01: A definition that says nothing runs the branch's settings unchanged
So that `builds.<name>` with only a trigger builds exactly what the branch describes.
- **Given** a branch with a non-default value for every setting
- **When** an empty definition, or one with empty docker and werkdock blocks, is applied
- **Then** the result equals the branch's settings
##### Verified by
- [BuildDefinitionUnitTest](../../src/test/kotlin/de/hoennig/werkator/config/BuildDefinitionUnitTest.kt) — Scenario#112.01
#### Scenario#112.02: Every setting a definition says replaces the branch's own
So that no key of `builds.<name>` is read and then silently dropped.
- **Given** the same branch, and a definition saying every setting, docker and werkdock included, with other values
- **When** the definition is applied
- **Then** every setting is the definition's
##### Verified by
- [BuildDefinitionUnitTest](../../src/test/kotlin/de/hoennig/werkator/config/BuildDefinitionUnitTest.kt) — Scenario#112.02
#### Scenario#112.03: A definition saying some settings of a block keeps the rest of that block
So that `docker.image` alone does not reset the branch's network, socket or environment.
- **Given** the same branch, and a definition saying only the timeout, the docker image and the werkdock rootfs
- **When** the definition is applied
- **Then** those three are the definition's, everything else is the branch's
##### Verified by
- [BuildDefinitionUnitTest](../../src/test/kotlin/de/hoennig/werkator/config/BuildDefinitionUnitTest.kt) — Scenario#112.03
## The Solution
`applyTo` lays the definition over the branch in three blocks:
| Block | Settings |
| ------------- | ------------------------------------------------------------------------ |
| `withWork` | `buildCommand`, `cleanCommand`, `artifactDirs`, `stdoutLog`, `stderrLog` |
| `withRules` | `requirePullRequest`, `statusContext`, `timeout`, `restartsInstance` |
| `withSandbox` | `docker`, `werkdock` |
`DockerOverrides.applyTo` and `WerkdockOverrides.applyTo` lay each sandbox's overrides over the branch's sandbox of that kind.
The overrides and their config class now stand side by side, where Scenario#72.03 already holds that every setting of the one exists in the other.
Measured with PIT as the nightly build runs it:
| Method | Before | After |
| ----------------------------- | ------ | ---------- |
| `BuildDefinition.applyTo` | 34 | no mutants |
| `BuildDefinition.withWork` | — | 6 |
| `BuildDefinition.withRules` | — | 5 |
| `BuildDefinition.withSandbox` | — | 5 |
| `DockerOverrides.applyTo` | — | 8 |
| `WerkdockOverrides.applyTo` | — | 6 |
Every mutant of the new methods is killed, none is `INCONCLUSIVE`; each score equals its complexity.
The gate `maxMutationCrap` falls from 35 to 34: the highest score left is `CapacitySizing.cpu` at 33.17.
## Additional Changes
- Version 1.34.9 with its release note.
- `BuildExecutorUnitTest`, Scenario#108.05, waits for the executing build's process before it cancels the build: a build counts as running before its process is started, and a cancel in between left no process to see terminated (`List is empty`, seen once in this PR's local build).
## Follow-up PRs
- `CapacitySizing.cpu` (33.17) in its own PR.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
BuildDefinition.applyTo had complexity 34 and Mutation-CRAP 34, which no
test can bring below the limit of 15.
- withWork, withRules, withSandbox lay the definition over the branch.
- DockerOverrides.applyTo and WerkdockOverrides.applyTo each apply
themselves to their sandbox.
- Scenario#112.01-03 test the whole application of a definition; every
mutant of the new methods is killed. Gate 35 -> 34.
- BuildExecutorUnitTest 108.05 waits for the process before cancelling.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
mi
merged commit 52f42f850e into main2026-10-04 18:51:23 +02:00
mi
deleted branch mihoe/build-definition-apply-by-block2026-10-04 18:51:23 +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
After PR#111
BuildDefinition.applyTohad the highest Mutation-CRAP score of Werkator: 34 at a complexity of 34.A score is never below the complexity, so no test could bring it under the limit of 15.
The method laid a build definition over its branch's settings in one
copyof 21 fields, each with its own?:fallback, the docker and werkdock blocks nested inside.What the definition says falls into three groups, but the method did not show them:
what the build runs, the rules the instance runs it under, and the sandbox it runs in.
Only one test checked a single field of it (Scenario#72.01); every other field was held only by tests of the config loader.
Non-Goals
The Scenarios
Feature: A build definition laid over its branch's settings
Scenario#112.01: A definition that says nothing runs the branch's settings unchanged
So that
builds.<name>with only a trigger builds exactly what the branch describes.Verified by
Scenario#112.02: Every setting a definition says replaces the branch's own
So that no key of
builds.<name>is read and then silently dropped.Verified by
Scenario#112.03: A definition saying some settings of a block keeps the rest of that block
So that
docker.imagealone does not reset the branch's network, socket or environment.Verified by
The Solution
applyTolays the definition over the branch in three blocks:withWorkbuildCommand,cleanCommand,artifactDirs,stdoutLog,stderrLogwithRulesrequirePullRequest,statusContext,timeout,restartsInstancewithSandboxdocker,werkdockDockerOverrides.applyToandWerkdockOverrides.applyTolay each sandbox's overrides over the branch's sandbox of that kind.The overrides and their config class now stand side by side, where Scenario#72.03 already holds that every setting of the one exists in the other.
Measured with PIT as the nightly build runs it:
BuildDefinition.applyToBuildDefinition.withWorkBuildDefinition.withRulesBuildDefinition.withSandboxDockerOverrides.applyToWerkdockOverrides.applyToEvery mutant of the new methods is killed, none is
INCONCLUSIVE; each score equals its complexity.The gate
maxMutationCrapfalls from 35 to 34: the highest score left isCapacitySizing.cpuat 33.17.Additional Changes
BuildExecutorUnitTest, Scenario#108.05, waits for the executing build's process before it cancels the build: a build counts as running before its process is started, and a cancel in between left no process to see terminated (List is empty, seen once in this PR's local build).Follow-up PRs
CapacitySizing.cpu(33.17) in its own PR.🤖 Generated with Claude Code