Skip to main content

Crate processkit

Crate processkit 

Source
Expand description

processkit — child-process management for Rust.

Two layers:

Async throughout (tokio). Errors are the structured Error; a non-zero exit is reported in ProcessResult, not raised, until you call ProcessResult::ensure_success.

Beyond this page, the repository ships a narrative guide set — a task-oriented cookbook (“I want to …” → snippet), a deep guide per capability, and every per-platform caveat collected in one place.

Run vocabulary — one verb, one meaning, at every layer (Command, ProcessRunner/ProcessRunnerExt, CliClient):

  • run — require a zero exit and return stdout as a String, trailing whitespace trimmed (trim_end: the final newline is noise, but leading whitespace can be significant). run_unit — the same, discarding the output.
  • output — return the full ProcessResult; a non-zero exit is not an error here. (Command splits the verb by payload: output_string / output_bytes.)
  • exit_code — the exit code, with a missing code surfaced as an error. (On a ProcessResult, code is the plain Option<i32> accessor — None for a timeout/signal kill, never a -1 sentinel.)
  • probe — run a predicate and read its exit code as a bool: 0true, 1false, anything else is an error (git diff --quiet, …).
use processkit::Command;

// Capture output; a non-zero exit does not error on its own.
let result = Command::new("git").args(["rev-parse", "HEAD"]).output_string().await?;
println!("HEAD is {}", result.stdout().trim());

// Or require success and get trimmed stdout directly.
let version = Command::new("cargo").arg("--version").run().await?;

§Recipes

use processkit::{Command, Error};

// Exit code *is* the answer (0 = yes, 1 = no; anything else errors):
let clean = Command::new("git").args(["diff", "--quiet"]).probe().await?;

// Retry a transient failure (replays the command; classifier inspects the error):
let fetched = Command::new("git")
    .args(["fetch", "--quiet"])
    .timeout(Duration::from_secs(10))
    .retry(3, Duration::from_millis(200), |e| {
        matches!(e, Error::Timeout { .. })
            || e.diagnostic().is_some_and(|m| m.contains("Could not resolve host"))
    })
    .run()
    .await;

// A friendly failure message — stderr, falling back to stdout (git writes
// `CONFLICT …` / `nothing to commit` there):
if let Err(e) = Command::new("git").args(["merge", "topic"]).run().await {
    eprintln!("merge failed: {}", e.diagnostic().unwrap_or("(no output)"));
}

// Set an env var once for every command (typed CLI wrapper):
use processkit::CliClient;
let git = CliClient::new("git").default_env("GIT_TERMINAL_PROMPT", "0");
let _ = git.run(git.command(["status", "--porcelain"])).await?;

§Features

Every flag is additive and gates visibility only — the kill-on-drop tree guarantee is unconditional in every configuration.

  • stats (default) — resource measurement: ProcessGroupStats, ProcessGroup::stats (plus the sample_stats time-series sampler), the per-process RunningProcess::cpu_time/peak_memory_bytes diagnostics, and the RunningProcess::profile run summary. Disable (default-features = false) to compile the accounting code out.
  • process-control (default) — tree control beyond contain+kill: Signal and ProcessGroup::{signal, suspend, resume, members, adopt}.
  • limits — whole-tree resource caps: ResourceLimits, the memory_max/max_processes/cpu_quota builders on ProcessGroupOptions, and Error::ResourceLimit. Implies stats.
  • mock — the mockall-generated MockRunner for consumers’ tests.
  • tracingtracing events on the processkit target: spawn and exit (program/pid/mechanism), timeout and cancellation firing, group terminate/shutdown, retry attempts, supervisor restarts and storm pauses, and teardown anomalies (stdin-writer failures, pump overruns). Never logs argv or environment values.
  • cancellation — first-class run cancellation: Command::cancel_on ties a run to a CancellationToken; cancelling it kills the tree and every consuming path resolves to Error::Cancelled. Re-exports CancellationToken (from tokio-util).
  • record — record/replay cassettes over the ProcessRunner seam: RecordReplayRunner records real Invocation → ProcessResult pairs to a JSON fixture once, then replays them hermetically — no subprocess in CI. Pulls in serde + serde_json.

Macros§

cli_client
Scaffold a typed CLI-wrapper struct around a CliClient.

Structs§

CancellationTokencancellation
Re-exported (under the cancellation feature) so callers can use processkit::CancellationToken; without a direct tokio-util dependency. See Command::cancel_on. A token which can be used to signal a cancellation request to one or more tasks.
CliClient
Owns a CLI tool’s program name, ProcessRunner, and default timeout, and builds + runs Commands against them.
Command
A description of a child process to launch: program, arguments, working directory, environment, stdin source, and an optional timeout.
Encoding
An encoding as defined in the Encoding Standard.
Invocation
A captured record of one command a runner was asked to run.
JobRunner
The default runner: every run gets a fresh, private ProcessGroup owned by the run, so its tree is torn down when the run finishes (or its handle drops).
MockRunnermock
The mockall-generated mock of ProcessRunner (enabled by the mock feature), re-exported under a friendlier name. Runs a Command — to a captured result (output) or a live handle (start).
OutputBufferPolicy
Caps how many captured/streamed output lines are retained in memory.
Pipeline
A chain of Commands connected stdout→stdin — built with Command::pipe, extended with pipe, driven with output_string / run.
ProcessGroup
A container that ties the lifetime of a child-process tree to its own.
ProcessGroupOptions
Tuning for a ProcessGroup — graceful-shutdown timing and (with the limits feature) resource limits.
ProcessGroupStatsstats
A snapshot of a process group’s resource usage.
ProcessResult
The captured result of running a process to completion.
ProcessStdin
An interactive writer to a child’s standard input.
RecordReplayRunnerrecord
A ProcessRunner that records real runs to a JSON cassette, or replays a cassette hermetically (record feature).
RecordingRunner
Wraps another ProcessRunner, recording every Invocation before delegating, so tests can assert exactly what was run.
Reply
A canned reply: stdout/stderr text plus an exit code (or a timed-out run, or a parked-until-cancelled call).
ResourceLimitslimits
Resource limits enforced on a process group as a whole.
RunProfilestats
Resource summary of one finished run — produced by RunningProcess::profile.
RunningProcess
A handle to a process spawned by a runner.
ScriptedRunner
A ProcessRunner that returns canned Replys for matched commands.
StatsSamplerstats
A periodic ProcessGroupStats series — created by ProcessGroup::sample_stats.
Stdin
What to feed a child process on standard input.
StdoutLines
A Stream of the child’s standard-output lines (see RunningProcess::stdout_lines).
SupervisionOutcome
What a finished supervision reports — the last run plus the keeper’s telemetry.
Supervisor
Keeps a Command alive: runs it, classifies every exit against the RestartPolicy and the stop_when predicate, and restarts it after an exponential-backoff delay until supervision ends.

Enums§

Error
Errors produced when launching or running a child process.
Mechanism
The containment mechanism actually in effect for a process group.
Outcome
How a run ended — the explicit form of the code()/timed_out() pair.
OverflowMode
What to drop when a bounded output buffer is full.
RestartPolicy
When the supervisor restarts an exited child. See each variant; in every case stop_when and max_restarts can end supervision first.
Signalprocess-control
A signal to broadcast to every process in a ProcessGroup via signal.
StopReason
Why supervision ended.

Traits§

ProcessRunner
Runs a Command — to a captured result (output) or a live handle (start).
ProcessRunnerExt
Convenience methods available on every ProcessRunner (including &dyn ProcessRunner), layered over output.
StreamExt
An extension trait for the Stream trait that provides a variety of convenient combinator functions.

Functions§

output
Run program with args inside a private job and capture the result without erroring on a non-zero exit — for commands whose exit code is meaningful.
output_all
Run every command in commands, keeping at most concurrency of them live at once, and collect all their results in input order.
run
Run program with args inside a private job and return trimmed stdout, or an Error on a non-zero exit / spawn failure / timeout. A thin shim over Command; use the builder for a working directory, env, stdin, or timeout.
wait_all
Wait for all of several running processes to exit, returning their exit codes (None for a signal-killed run) in the same order as processes.
wait_any
Wait for whichever of several running processes exits first, returning its index in processes and its exit code (None for a signal-killed run, matching RunningProcess::wait).

Type Aliases§

Result
Crate result alias.