Skip to content

v2.4.0

Choose a tag to compare

@github-actions github-actions released this 11 Jul 16:41

Added

  • Command.Groups(gids) (and the pipe-friendly Command.groups): set the child's Unix supplementary groups, replacing the inherited set — the missing third leg of a privilege drop next to Uid/Gid. A bare Uid/Gid/User drop clears the parent's supplementary groups (so a child dropped to a service user loses that user's docker/video/adm membership); pass the target user's gids here to grant them back, or [] to keep the cleared default. Applied by the same setpriv helper (mapped to setpriv --groups), so it is honoured only alongside a Uid/Gid drop — set without one it fails the spawn with ProcessError.Spawn rather than being silently ignored. Unix-only: on Windows it fails with ProcessError.Unsupported, exactly like Uid/Gid. Each gid must be non-negative (rejected at the builder boundary with ArgumentOutOfRangeException, naming the offending index).
  • The ProcessKit, ProcessKit.Extensions.DependencyInjection, and ProcessKit.Extensions.Hosting packages now declare trimming/NativeAOT compatibility (IsTrimmable/IsAotCompatible), so a consumer that publishes a PublishTrimmed/NativeAOT app no longer gets "assembly was not verified" warnings for them; a CI smoke publishes and runs a NativeAOT consumer that spawns, captures, and contains a child on both Linux (linux-x64) and Windows (win-x64). See docs/platform-support.md — including the documented boundary that ProcessKit.Testing is not trim/AOT-safe (its reflection-based System.Text.Json cassettes), which is fine because it is a test-only dependency.
  • Command.InheritStdin (and the pipe-friendly Command.inheritStdin): hand the child the parent process's own standard input directly — inherited at the OS level, with no pipe and no feeder — for interactive/console programs (an editor launched by git commit, a tool that prompts on the terminal, a pipe from the parent's own stdin). The stdin analogue of StdioMode.Inherit. Incompatible with a feeder Stdin source and KeepStdinOpen (rejected at the builder boundary in either chaining order); RunningProcess.TakeStdin returns None for an inherited-stdin child. Repeatable under Retry/supervision and supported by the ProcessKit.Testing record/replay cassette (keyed by a stable "inherit" marker).
  • OutputJsonAsync<'T>: a typed JSON verb alongside ParseAsync/TryParseAsync, available on Command, any IProcessRunner (via the ProcessRunnerExtensions seam, so ScriptedRunner and other test doubles get it for free), CliClient, and Pipeline. Deserializes the captured stdout via the in-box System.Text.Json (no new package dependency), with an overload accepting a JsonSerializerOptions; invalid JSON becomes ProcessError.Parse and a non-zero exit becomes ProcessError.Exit, never a raised exception. From F#, Runner.outputJson is the module-function form.
  • ProcessKit.Extensions.Hosting: opt-in IHealthCheck support for a hosted process. AddProcessKitHostedProcessHealthCheck(name) registers a keyed HostedProcessHealthCheck (same key as AddProcessKitHostedProcess) mapping supervision state to Healthy (running, including in-policy restarts) / Degraded (the failure-storm guard is pausing restarts) / Unhealthy (supervision not active — not started, or ended in an error/exhausted budget/give-up/stop predicate). HostedProcessService gains the observable IsSupervisionActive, RestartCount, and IsStormPaused the check reads, updated live from Supervisor.OnRestart/OnStormPause rather than only once supervision ends. The only added dependency is Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions; see docs/dependency-injection.md for how to wire the registered check into your own health-checks pipeline.
  • Exec.which(program): preflight-resolve a program to a full path without spawning it — a doctor/install-wizard check ("is this tool installed?") cheaper and side-effect-free next to probing availability by actually running the program. Reuses the same PATH/PATHEXT-aware lookup the spawn path itself falls back on to name the directories it searched (including full PATHEXT semantics on Windows), so which and an actual spawn of the same program name never disagree on found-vs-not-found; returns the resolved path on success or a typed ProcessError.NotFound (Searched names the probed PATH for a bare name) otherwise. CliClient.EnsureAvailableAsync() is the same check for a client's own program — always a local host check, never delegated to an injected IProcessRunner test double.

Changed

  • AddProcessKit(IConfiguration) / AddProcessKitGroup(IConfiguration) are now annotated [RequiresUnreferencedCode]/[RequiresDynamicCode]: because they bind ProcessKitOptions from configuration by reflection, a trimmed/NativeAOT app that calls them now gets a precise warning pointing at the overload — use the Action<ProcessKitOptions> overload from an AOT app. No effect on a non-trimmed build.

Fixed

  • RunningProcess.CpuTime / PeakMemoryBytes / ProfileAsync no longer report a pid-reused stranger's metrics: each child's pid identity (OS-reported creation time) is captured once at spawn and re-checked before every metrics read, so a pid the OS recycled for an unrelated process after the original child was reaped now yields None instead of that stranger's CPU/memory.
  • ProcessStdin.FinishAsync no longer risks surfacing an IOException to the caller when the
  • HostedProcessService no longer silently discards a user-configured Supervisor.StopWhen (set via AddProcessKitHostedProcess's configureSupervisor callback): it now combines the caller's predicate with its own host-shutdown stop condition instead of overwriting it, so a predicate like "stop once the child exits 0 with a marker in stdout" still ends supervision as configured.
  • RunningProcess.Kill()/StopAsync() (and the per-run timeout/pump-fault kills) no longer touch the
  • RunningProcess.WaitForLineAsync now reports the clamped (armable) timeout in ProcessError.NotReady
  • ProcessGroup.StartAsync now guards the shared-group RunningProcess handle construction exactly like
  • ProcessKit.Extensions.Hosting's internal TrackingRunner now honours Command.CancelOn and always