- `CommandProcess`: one build command while it runs (log pumps, processor-time sampling, deadline via `awaitExit`, `destroyTree`). - `BuildLogs`: the three log files of a build, opened and closed together; `runBuildCommands` uses one `use`. - `CommandProcessIntegrationTest`: real processes, ~4 s, kept by the nightly mutation build. - runBuildCommands 20.78 -> 6.84; every BuildExecutor method below 15 (max settle 12.19). Gate stays 46 (NginxProxyManager.cleanupStaleContainers 45.04). PR-doc: `docs/prs/2026-10-04-PR#109-command-process.md` 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: mhoennig <michael@hoennig.de> Reviewed-on: #109
4.3 KiB
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
After PR#108 the rest of BuildExecutor above the limit was its process handling: copying a command's output into the logs, sampling its processor time, keeping its deadline, ending its process tree.
runBuildCommands stood at 20.78; it opened the three log files in three nested use blocks, and every use is several branches.
This part needs real processes, and the only test with real processes, BuildExecutorIntegrationTest, takes minutes and stays out of the nightly mutation build.
Non-Goals
- No change in behaviour: the same logs, the same deadline, the same termination.
BuildExecutorIntegrationTeststays out of the nightly run.
The Scenarios
Feature: A build command's process
Scenario#109.01: A command's output goes to its log and to the live log, and its exit code is the answer
So that the page that follows a build shows both streams, and each log only its own.
Verified by
- CommandProcessIntegrationTest — Scenario#109.01
Scenario#109.02: A command past its deadline is told, its whole tree is ended, and it reports a failure
So that a hanging build cannot outlive its timeout through a child process.
- Given a command that starts a child and waits for it, and a deadline of 300 ms
- When the command is awaited
- Then the timeout is reported once, the command and its child are gone, and the exit code is not 0
- And a command within its deadline ends with its own exit code
Verified by
- CommandProcessIntegrationTest — Scenario#109.02
Scenario#109.03: Ending a process ends its whole tree
So that a cancellation leaves no child running.
Verified by
- CommandProcessIntegrationTest — Scenario#109.03
Scenario#109.04: The processor time of a command is measured where it is the build's own, and only there
So that a Docker build records no processor time rather than the client's second.
Verified by
- CommandProcessIntegrationTest — Scenario#109.04
Scenario#109.05: A log name that is a path is refused before anything is opened
So that a build's output never lands outside its staging directory.
Verified by
- CommandProcessIntegrationTest — Scenario#109.05
The Solution
CommandProcessis one command while it runs: the pumps into the logs, the processor-time sampling,awaitExitwith the deadline,finish, anddestroyTreefor the whole process tree.BuildLogsopens the three logs of a build and closes them together;runBuildCommandsuses it in oneuse.BuildExecutorkeeps the order of the commands;startCommandandbecomeBuildProcessmake a started process the one a cancellation ends, preparation processes included;deadlineOftakes the build's one deadline.CommandProcessIntegrationTestruns real processes of a second or two, about four seconds in all, so the nightly run keeps it.
Measured with PIT as the nightly build runs it:
| Method | Before | After |
|---|---|---|
BuildExecutor.runBuildCommands |
20.78 | 6.84 |
destroyTree (was destroyProcessTree) |
12 | 4.94 |
awaitExit (was waitForBounded) |
9.49 | 4.03 |
| the processor-time sampling | 12 | 4.13 |
Every method of BuildExecutor is below 15 now; the highest is settle at 12.19.
The gate maxMutationCrap stays at 46: the highest score of Werkator is NginxProxyManager.cleanupStaleContainers at 45.04.
Additional Changes
- Version 1.34.6 with its release note.
Follow-up PRs
NginxProxyManager.cleanupStaleContainers(45.04),UiFormats.cpuTime(43.81) andMetricsConfiguration.monitoredDisks(42), the highest scores left.