diff --git a/README.md b/README.md index b543fc0..dee0954 100644 --- a/README.md +++ b/README.md @@ -1,50 +1,30 @@ # Invex.Process -A small, focused .NET library for running external processes with first-class logging, real-time -output streaming, full output capture, and fail-fast error handling. +[![Build](https://github.com/Invex-Games/process/actions/workflows/Build.yml/badge.svg)](https://github.com/Invex-Games/process/actions/workflows/Build.yml) +[![NuGet](https://img.shields.io/nuget/v/Invex.Process.svg)](https://www.nuget.org/packages/Invex.Process) +[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/Invex-Games/process/blob/main/LICENSE.txt) +[![.NET](https://img.shields.io/badge/.NET-8.0%20%7C%209.0%20%7C%2010.0-512BD4.svg)](https://dotnet.microsoft.com/) -It wraps `System.Diagnostics.Process` behind a clean, injectable `IProcessRunner` abstraction so your -build targets, tools, and services can shell out to executables (`dotnet`, `git`, `npm`, …) without -re-implementing redirection, buffering, and exit-code handling every time. +A small, focused .NET library for running external processes with first-class logging, real-time output streaming, +full output capture, and fail-fast error handling. It wraps `System.Diagnostics.Process` behind a clean, injectable +`IProcessRunner` abstraction so your build targets, tools, and services can shell out to executables (`dotnet`, `git`, +`npm`, …) without re-implementing redirection, buffering, and exit-code handling every time. ## Features -### Sync and async APIs - -`Run` and `RunAsync` with `CancellationToken` support. - -### Real-time streaming - -Stdout and stderr are read asynchronously and logged line-by-line as the -process runs, instead of being buffered until exit. - -### Full output capture - -Complete stdout/stderr are always captured into the result, regardless of -log level, so you can inspect them even when logging is suppressed. - -### Fail-fast by default - -A non-zero exit code throws a descriptive exception (including command, -exit code, working directory, and stderr), or opt in to inspect failures yourself. - -### Configurable log levels - -Control how the invocation, stdout, and stderr are logged. - -### Per-line transforms - -Rewrite or suppress individual output/error lines (e.g. to redact secrets -or filter noisy progress output). - -### Working directory & environment variables - -Run from any directory and inject or remove -environment variables per invocation. - -### DI-ready - -Register once with `AddProcessRunner()` and inject `IProcessRunner` anywhere. +- **Sync and async APIs.** `Run` and `RunAsync`, the latter with `CancellationToken` support. +- **Real-time streaming.** Stdout and stderr are read asynchronously and logged line-by-line as the process runs, + instead of being buffered until exit. +- **Full output capture.** Complete stdout/stderr are always captured into the result, regardless of log level, so you + can inspect them even when logging is suppressed. +- **Fail-fast by default.** A non-zero exit code throws a descriptive exception (including command, exit code, working + directory, and stderr), or you opt in to inspect failures yourself. +- **Configurable log levels.** Control how the invocation, stdout, and stderr are logged. +- **Per-line transforms.** Rewrite or suppress individual output/error lines, for example to redact secrets or filter + noisy progress output. +- **Working directory and environment variables.** Run from any directory and inject or remove environment variables + per invocation. +- **DI-ready.** Register once with `AddProcessRunner()` and inject `IProcessRunner` anywhere. ## Installation @@ -52,9 +32,11 @@ Register once with `AddProcessRunner()` and inject `IProcessRunner` anywhere. dotnet add package Invex.Process ``` -Targets `net8.0`, `net9.0`, and `net10.0`. +The package targets `net8.0`, `net9.0`, and `net10.0`. It depends on +`Microsoft.Extensions.DependencyInjection.Abstractions` and `Microsoft.Extensions.Hosting.Abstractions`, and logs +through `ILogger`, so configure logging on your host (it is by default with `Host.CreateApplicationBuilder`). -## Getting started +## Quick start Register the process runner with your dependency injection container: @@ -85,30 +67,33 @@ public sealed class VersionReporter(IProcessRunner processRunner) ## Usage -### Passing arguments +### Configure the invocation -Arguments can be supplied as a single pre-joined string or as an array of tokens. When using an array, -empty and whitespace-only entries are removed before joining, which makes optional arguments easy: +Arguments can be a single pre-joined string or an array of tokens; in the array form, empty and whitespace-only entries +are removed before joining, which makes optional arguments easy. Set a working directory and add, override or remove +environment variables per call: ```csharp -// As a single string -var options = new ProcessRunOptions("git", "status --short"); - -// As tokens — empty entries are stripped automatically var verbose = true; -var options2 = new ProcessRunOptions("git", ["status", "--short", verbose ? "--verbose" : ""]); -``` +var status = new ProcessRunOptions("git", ["status", "--short", verbose ? "--verbose" : ""]); -### Handling failures +var result = processRunner.Run(new ProcessRunOptions("npm", "ci") +{ + WorkingDirectory = "/repo/frontend", + EnvironmentVariables = + { + ["NODE_ENV"] = "production", + ["NPM_TOKEN"] = null, // a null value removes the inherited variable + }, +}); +``` -By default, a non-zero exit code throws with a detailed message: +See [Running Processes](https://github.com/Invex-Games/process/blob/main/docs/running-processes.md). -```csharp -// Throws if the build fails — the exception includes the command, exit code, and stderr. -processRunner.Run(new ProcessRunOptions("dotnet", "build --configuration Release")); -``` +### Handle failures -Set `AllowFailedResult` to inspect the result yourself instead of throwing: +By default a non-zero exit code throws with a message that includes the command, exit code, working directory and +stderr. Set `AllowFailedResult` to inspect the result yourself instead: ```csharp var result = processRunner.Run(new ProcessRunOptions("dotnet", "format --verify-no-changes") @@ -122,25 +107,12 @@ if (result.ExitCode != 0) } ``` -### Working directory and environment variables +See [Handling Failures](https://github.com/Invex-Games/process/blob/main/docs/handling-failures.md). -```csharp -var result = processRunner.Run(new ProcessRunOptions("npm", "ci") -{ - WorkingDirectory = "/repo/frontend", - EnvironmentVariables = - { - ["NODE_ENV"] = "production", - ["CI"] = "true", - ["NPM_TOKEN"] = null, // a null value removes the inherited variable - }, -}); -``` +### Control logging -### Controlling logging - -Each invocation can tune how it is logged. The full output is still captured in the result regardless -of these settings: +Each invocation can tune how it is logged. The full output is still captured in the result regardless of these +settings: ```csharp var options = new ProcessRunOptions("dotnet", "test") @@ -151,23 +123,20 @@ var options = new ProcessRunOptions("dotnet", "test") }; ``` -> On a non-zero exit, stdout/stderr configured below `Information` are re-logged in full at -> `Information`/`Warning` respectively, so the failure is visible in the logs even when per-line -> logging was quiet. +> [!NOTE] +> On a non-zero exit, stdout/stderr configured below `Information` are re-logged in full at `Information`/`Warning` +> respectively, so the failure is visible in the logs even when per-line logging was quiet. + +See [Logging](https://github.com/Invex-Games/process/blob/main/docs/logging.md). -### Transforming or filtering output +### Transform or filter output -`TransformOutput` and `TransformError` are applied to each line before it is logged and captured. -Returning `null` suppresses the line entirely: +`TransformOutput` and `TransformError` are applied to each line before it is logged and captured. Returning `null` +suppresses the line entirely: ```csharp -var options = new ProcessRunOptions("deploy", "--token secret") +var options = new ProcessRunOptions("docker", "build .") { - // Redact secrets from logs and captured output - TransformOutput = line => line.Contains("token") - ? "[redacted]" - : line, - // Drop noisy progress lines completely TransformError = line => line.StartsWith("progress:") ? null @@ -175,22 +144,52 @@ var options = new ProcessRunOptions("deploy", "--token secret") }; ``` -## API overview - -| Type | Description | -|----------------------|--------------------------------------------------------------------------------------------------------------------------------------| -| `IProcessRunner` | Injectable service with `Run` and `RunAsync` methods. Registered as a singleton. | -| `ProcessRunOptions` | Immutable record configuring a single execution (name, args, working directory, env vars, log levels, transforms, failure handling). | -| `ProcessRunResult` | Result record exposing `ExitCode`, captured `Output`, captured `Error`, and the originating `RunOptions`. | -| `AddProcessRunner()` | `IServiceCollection` extension that registers `IProcessRunner`. | +See [Transforming Output](https://github.com/Invex-Games/process/blob/main/docs/transforming-output.md). -## Cancellation +### Cancel a long-running wait `RunAsync` accepts a `CancellationToken` that cancels the wait for the process to exit and throws -`OperationCanceledException`. Note that cancelling the token aborts the wait but does **not** kill the -underlying OS process. +`OperationCanceledException`. + +> [!IMPORTANT] +> Cancelling the token aborts the wait but does **not** kill the underlying OS process. + +See [Cancellation](https://github.com/Invex-Games/process/blob/main/docs/cancellation.md). + +## Documentation + +Full documentation: [https://invex-games.github.io/process/](https://invex-games.github.io/process/) + +| Guide | Contents | +|---------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------| +| [Introduction](https://github.com/Invex-Games/process/blob/main/docs/introduction.md) | The problem it solves, when to use it, and design principles. | +| [Getting Started](https://github.com/Invex-Games/process/blob/main/docs/getting-started.md) | Installation, registration, and your first process. | +| [Running Processes](https://github.com/Invex-Games/process/blob/main/docs/running-processes.md) | Arguments, working directories, environment variables, concurrency. | +| [Handling Failures](https://github.com/Invex-Games/process/blob/main/docs/handling-failures.md) | Fail-fast exceptions, `AllowFailedResult`, and startup failures. | +| [Logging](https://github.com/Invex-Games/process/blob/main/docs/logging.md) | What is logged, log levels, failure promotion, and secrets. | +| [Transforming Output](https://github.com/Invex-Games/process/blob/main/docs/transforming-output.md) | Redacting, filtering and normalizing output lines. | +| [Cancellation](https://github.com/Invex-Games/process/blob/main/docs/cancellation.md) | What cancelling `RunAsync` does and doesn't do. | +| [Testing Your Code](https://github.com/Invex-Games/process/blob/main/docs/testing.md) | Faking `IProcessRunner` in unit tests. | +| [Versioning](https://github.com/Invex-Games/process/blob/main/docs/versioning.md) | The Semantic Versioning policy and what counts as the public API. | +| [API Reference](https://github.com/Invex-Games/process/blob/main/api/index.md) | Every public type, with links into the generated reference. | + +## Development + +```shell +dotnet build Invex.Process.slnx +dotnet test Invex.Process.slnx +``` + +The documentation site is built with [DocFX](https://dotnet.github.io/docfx/) through the +[Atom](https://github.com/Invex-Games/atom) build in `_atom/`: + +```shell +atom BuildDocs # generate the site +atom ServeDocs # build and serve it locally +``` + +Without the `atom` tool installed, run the same targets with `dotnet run --project _atom -- BuildDocs`. ## License -Distributed under the terms described in -[LICENSE.txt](https://github.com/Invex-Games/process/blob/main/LICENSE.txt). +Licensed under the [MIT License](https://github.com/Invex-Games/process/blob/main/LICENSE.txt). diff --git a/api/index.md b/api/index.md index 703a9f1..79c7540 100644 --- a/api/index.md +++ b/api/index.md @@ -1,28 +1,23 @@ # API Reference -Welcome to the **Invex.Process** API reference. The reference pages in this section are generated -directly from the XML documentation in the source code, so they always match the shipped package. +The reference pages in this section are generated from the XML documentation in the source code, so they always +match the shipped package; the table below is a map of where to start. -## Namespace - -Everything lives in a single namespace: [`Invex.Process`](Invex.Process.yml). - -## Public surface at a glance +## Invex.Process | Type | Kind | Description | |------|------|-------------| -| [`IProcessRunner`](Invex.Process.IProcessRunner.yml) | interface | Injectable service for executing external processes. Exposes synchronous `Run` and asynchronous `RunAsync`. Registered as a singleton. | -| [`ProcessRunOptions`](Invex.Process.ProcessRunOptions.yml) | record | Immutable configuration for a single execution — executable name, arguments, working directory, environment variables, log levels, per-line transforms, and failure tolerance. | -| [`ProcessRunResult`](Invex.Process.ProcessRunResult.yml) | record | The result of a completed execution — exit code, full captured stdout/stderr, and the originating options. | -| [`ProcessHostExtensions`](Invex.Process.ProcessHostExtensions.yml) | static class | `IServiceCollection` extensions; call `AddProcessRunner()` once during host setup. | +| [`IProcessRunner`](xref:Invex.Process.IProcessRunner) | Interface | Injectable service for executing external processes. Exposes synchronous `Run` and asynchronous `RunAsync`. Registered as a singleton. | +| [`ProcessRunOptions`](xref:Invex.Process.ProcessRunOptions) | Record | Immutable configuration for a single execution: executable name, arguments, working directory, environment variables, log levels, per-line transforms, and failure tolerance. | +| [`ProcessRunResult`](xref:Invex.Process.ProcessRunResult) | Record | The result of a completed execution: exit code, full captured stdout/stderr, and the originating options. | +| [`ProcessHostExtensions`](xref:Invex.Process.ProcessHostExtensions) | Static class | `IServiceCollection` extensions; call `AddProcessRunner()` once during host setup. | ## Typical flow -1. Register the runner with [`AddProcessRunner()`](Invex.Process.ProcessHostExtensions.yml). -2. Inject [`IProcessRunner`](Invex.Process.IProcessRunner.yml) where you need to shell out. -3. Build a [`ProcessRunOptions`](Invex.Process.ProcessRunOptions.yml) describing the invocation. -4. Call `Run` or `RunAsync` and use the returned - [`ProcessRunResult`](Invex.Process.ProcessRunResult.yml). +1. Register the runner with `AddProcessRunner()` from [`ProcessHostExtensions`](xref:Invex.Process.ProcessHostExtensions). +2. Inject [`IProcessRunner`](xref:Invex.Process.IProcessRunner) where you need to shell out. +3. Build a [`ProcessRunOptions`](xref:Invex.Process.ProcessRunOptions) describing the invocation. +4. Call `Run` or `RunAsync` and use the returned [`ProcessRunResult`](xref:Invex.Process.ProcessRunResult). ```csharp var result = await processRunner.RunAsync( @@ -35,8 +30,5 @@ var result = await processRunner.RunAsync( var commitSha = result.Output.Trim(); ``` -## Versioning - -The public API surface follows Semantic Versioning — see [SemVer policy](SemVer.md) for what you -can rely on between releases. - +For conceptual documentation, see the [Introduction](../docs/introduction.md); for what you can rely on between +releases, see [Versioning](../docs/versioning.md). diff --git a/docfx-theme/public/main.css b/docfx-theme/public/main.css new file mode 100644 index 0000000..6426059 --- /dev/null +++ b/docfx-theme/public/main.css @@ -0,0 +1,6 @@ +#logo { + width: 32px !important; + height: 32px !important; + object-fit: contain !important; + margin-right: 8px !important; +} diff --git a/docfx.json b/docfx.json index 2448482..a5eabba 100644 --- a/docfx.json +++ b/docfx.json @@ -5,14 +5,15 @@ "src": [ { "files": [ - "src/Invex.Process/Invex.Process.csproj" + "src/**/*.csproj" ] } ], "dest": "api", "properties": { "TargetFramework": "net10.0" - } + }, + "filter": "docs/filterConfig.yml" } ], "build": { @@ -20,16 +21,14 @@ { "files": [ "toc.yml", - "index.md", - "README.md" + "index.md" ] }, { "files": [ - "api/**.yml", "api/toc.yml", - "api/index.md", - "api/SemVer.md" + "api/**.yml", + "api/index.md" ] }, { @@ -42,20 +41,19 @@ "resource": [ { "files": [ - "images/**" + "images/**", + "LICENSE.txt" ] } ], "output": "_site", - "globalMetadataFiles": [], - "fileMetadataFiles": [], "globalMetadata": { "_appTitle": "Invex.Process", "_appName": "Invex.Process", - "_appFooter": "Invex.Process — a process runner library for .NET", + "_appLogoPath": "images/icon.png", + "_appFooter": "Invex.Process · MIT License · Invex Games", "_enableSearch": true, "_disableContribution": false, - "_disableToc": false, "_gitContribute": { "repo": "https://github.com/Invex-Games/process", "branch": "main" @@ -65,10 +63,8 @@ "default", "modern" ], - "postProcessors": [], - "keepFileLink": false, - "disableGitFeatures": false + "theme": [ + "docfx-theme" + ] } } - - diff --git a/docs/cancellation.md b/docs/cancellation.md index 2e45726..ca6d8b6 100644 --- a/docs/cancellation.md +++ b/docs/cancellation.md @@ -1,6 +1,9 @@ # Cancellation -`RunAsync` accepts a `CancellationToken` that cancels the **wait** for the process to exit: +Read this page before putting a timeout on a process run: `RunAsync` accepts a `CancellationToken`, but it cancels +the **wait** for the process to exit, not the process itself. + +For example, to stop waiting after five minutes: ```csharp using var cts = new CancellationTokenSource(TimeSpan.FromMinutes(5)); @@ -57,3 +60,8 @@ catch (OperationCanceledException) The synchronous `Run` method has no cancellation support — it blocks until the process exits. Prefer `RunAsync` whenever a timeout or cooperative cancellation may be needed. +## See also + +- [Running Processes](running-processes.md): sync versus async execution. +- [Handling Failures](handling-failures.md): the other ways a run can end without a result. +- diff --git a/docs/filterConfig.yml b/docs/filterConfig.yml new file mode 100644 index 0000000..0984733 --- /dev/null +++ b/docs/filterConfig.yml @@ -0,0 +1,16 @@ +# Shared Invex API filter: documents the public Invex.* surface only. +apiRules: + - exclude: + hasAttribute: + uid: System.Runtime.CompilerServices.CompilerGeneratedAttribute + - exclude: + hasAttribute: + uid: System.ComponentModel.EditorBrowsableAttribute + ctorArguments: + - System.ComponentModel.EditorBrowsableState.Never + - exclude: + uidRegex: ^.*\.Internal\..*$ + - include: + uidRegex: ^Invex\. + - exclude: + uidRegex: ^(?!Invex\.) diff --git a/docs/getting-started.md b/docs/getting-started.md index 14144f0..f6403a6 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -1,5 +1,8 @@ # Getting Started +This page takes you from installing the package to running your first process and reading its result. Start here +if you are adding `Invex.Process` to a host, tool or build for the first time. + ## Installation ```shell @@ -71,7 +74,7 @@ The output is *always* fully captured — even if you turn logging down or off e ## Next steps -- [Running Processes](running-processes.md) — arguments, working directories, environment variables. -- [Handling Failures](handling-failures.md) — what happens when the exit code isn't zero. -- [Logging](logging.md) — tune how invocations and output are logged. - +- [Running Processes](running-processes.md): arguments, working directories, environment variables. +- [Handling Failures](handling-failures.md): what happens when the exit code isn't zero. +- [Logging](logging.md): tune how invocations and output are logged. +- , and in the API reference. diff --git a/docs/handling-failures.md b/docs/handling-failures.md index 7fa0200..b907080 100644 --- a/docs/handling-failures.md +++ b/docs/handling-failures.md @@ -1,5 +1,8 @@ # Handling Failures +This page explains what happens when a process exits with a non-zero code or cannot be started at all, and how +to opt out of the default exception when a failure is an expected outcome. + ## Fail-fast by default When a process exits with a **non-zero exit code**, `Run` and `RunAsync` throw an exception rather @@ -80,3 +83,8 @@ catch (Win32Exception ex) } ``` +## See also + +- [Logging](logging.md): failure promotion in detail. +- [Testing Your Code](testing.md): simulating failures in unit tests. +- , and diff --git a/docs/introduction.md b/docs/introduction.md index ce0a34c..d499500 100644 --- a/docs/introduction.md +++ b/docs/introduction.md @@ -1,8 +1,7 @@ # Introduction -**Invex.Process** is a small, focused .NET library for running external processes with -first-class logging, real-time output streaming, full output capture, and fail-fast error -handling. +**Invex.Process** runs external executables from .NET code through one injectable service. This page explains the +problem it solves, when it is (and is not) the right tool, and the principles behind its behavior. ## The problem @@ -34,6 +33,25 @@ One line of registration (`services.AddProcessRunner()`), one options record des run, one result record telling you *what happened* — and consistent logging and error handling everywhere. +## When to use it + +Invex.Process fits well when: + +- Build targets, CLI tools or services **shell out to command-line tools** and you want every call logged and checked + the same way. +- You want a **non-zero exit code to fail the operation** by default, with an actionable message. +- You want to **unit-test** code that runs processes without starting real ones. + +It is probably not the right tool when: + +- You need to **write to the process's standard input** or interact with it while it runs. Only stdout and stderr are + redirected. +- You need **hard timeouts that kill the process**. Cancellation stops the wait, not the process (see + [Cancellation](cancellation.md)). +- You need **binary output** or a raw stream. Output is read and captured line by line as text. +- You need **shell features** such as pipes, globbing or redirection. The executable is started directly with a single + argument string; run a shell explicitly if you need one. + ## Design principles - **Fail fast by default.** A non-zero exit code throws a descriptive exception that includes the @@ -46,7 +64,7 @@ everywhere. - **Stay testable.** Your code depends on the `IProcessRunner` interface, which is trivial to fake in unit tests — no real processes needed. -## Library shape +## Key concepts at a glance The entire public surface is four types in one namespace (`Invex.Process`): @@ -57,5 +75,9 @@ The entire public surface is four types in one namespace (`Invex.Process`): | `ProcessRunResult` | Immutable record describing the outcome. | | `ProcessHostExtensions` | `AddProcessRunner()` DI registration. | -Continue with [Getting Started](getting-started.md). +## Next steps + +- [Getting Started](getting-started.md): install the package and run your first process. +- [Running Processes](running-processes.md): arguments, working directories and environment variables. +- [API Reference](../api/index.md): the four public types. diff --git a/docs/logging.md b/docs/logging.md index 953f691..da15cc9 100644 --- a/docs/logging.md +++ b/docs/logging.md @@ -69,3 +69,8 @@ Two things to watch: `TransformError` to redact or suppress them — see [Transforming Output](transforming-output.md). +## See also + +- [Transforming Output](transforming-output.md): redacting and filtering lines before they are logged. +- [Handling Failures](handling-failures.md): what happens after a non-zero exit is logged. +- diff --git a/docs/running-processes.md b/docs/running-processes.md index 21725a1..dab1353 100644 --- a/docs/running-processes.md +++ b/docs/running-processes.md @@ -102,3 +102,9 @@ var fetch = git with { Args = "fetch --prune" }; `IProcessRunner` is stateless and registered as a singleton — it is safe to run multiple processes concurrently from multiple threads. +## See also + +- [Handling Failures](handling-failures.md): non-zero exit codes and startup failures. +- [Logging](logging.md): what the invocation line contains. +- [Cancellation](cancellation.md): cancelling `RunAsync`. +- and diff --git a/docs/testing.md b/docs/testing.md index b8f43f3..5894771 100644 --- a/docs/testing.md +++ b/docs/testing.md @@ -78,3 +78,8 @@ A.CallTo(() => processRunner.RunAsync(A._, A, and diff --git a/docs/toc.yml b/docs/toc.yml index fafbb45..6d37bad 100644 --- a/docs/toc.yml +++ b/docs/toc.yml @@ -2,16 +2,19 @@ href: introduction.md - name: Getting Started href: getting-started.md -- name: Running Processes - href: running-processes.md -- name: Handling Failures - href: handling-failures.md -- name: Logging - href: logging.md -- name: Transforming Output - href: transforming-output.md -- name: Cancellation - href: cancellation.md +- name: Guides + items: + - name: Running Processes + href: running-processes.md + - name: Handling Failures + href: handling-failures.md + - name: Logging + href: logging.md + - name: Transforming Output + href: transforming-output.md + - name: Cancellation + href: cancellation.md - name: Testing Your Code href: testing.md - +- name: Versioning + href: versioning.md diff --git a/docs/transforming-output.md b/docs/transforming-output.md index f838a5d..8de6df7 100644 --- a/docs/transforming-output.md +++ b/docs/transforming-output.md @@ -22,8 +22,12 @@ var options = new ProcessRunOptions("deploy", "--token " + token) }; ``` -The redaction applies to both the live log stream *and* the captured `result.Output`, so the secret -never leaves the process boundary in either form. +The redaction applies to both the live log stream *and* the captured `result.Output`. + +> [!WARNING] +> Transforms only see the process's output lines. The invocation line (`Run: deploy --token …`) is logged before the +> process starts and includes the full arguments and any environment variables you set, so a secret passed on the +> command line still reaches the log at `InvocationLogLevel`. See [Logging](logging.md#secrets-in-logs). ## Filtering noise @@ -49,7 +53,7 @@ var options = new ProcessRunOptions("git", "status --porcelain") }; ``` -## Behavior notes +## Behavioral notes - Transforms run on the process's output-reader callbacks, once per line, as lines arrive. Keep them fast and side-effect-free. @@ -58,3 +62,8 @@ var options = new ProcessRunOptions("git", "status --porcelain") - Transforms are per-invocation. For a shared policy, build a helper that returns pre-configured `ProcessRunOptions` instances, or wrap the delegate in a reusable method. +## See also + +- [Logging](logging.md): log levels and failure promotion. +- [Running Processes](running-processes.md): the rest of `ProcessRunOptions`. +- and diff --git a/api/SemVer.md b/docs/versioning.md similarity index 78% rename from api/SemVer.md rename to docs/versioning.md index a44aba2..188beeb 100644 --- a/api/SemVer.md +++ b/docs/versioning.md @@ -1,8 +1,10 @@ -# Semantic Versioning Policy +# Versioning -**Invex.Process** follows [Semantic Versioning 2.0.0](https://semver.org/spec/v2.0.0.html). -Version numbers take the form `MAJOR.MINOR.PATCH` and are derived automatically from the Git -history via GitVersion. +This page describes what a version number change means for you as a consumer of the package, so you know which +upgrades are safe to take without code changes. + +**Invex.Process** follows [Semantic Versioning 2.0.0](https://semver.org/spec/v2.0.0.html). Version numbers take the +form `MAJOR.MINOR.PATCH` and are derived automatically from the Git history via GitVersion. ## What the numbers mean @@ -37,3 +39,7 @@ unintended breaking changes fail the build before they ship. Pre-release packages (e.g. `2.1.0-beta.3`) may include API changes between pre-release builds without a MAJOR bump. They are intended for early feedback, not production use. +## See also + +- [Introduction](introduction.md): the library's design principles, which are part of its behavioral contract. +- [API Reference](../api/index.md): the public types covered by this policy. diff --git a/index.md b/index.md index f27da7b..1b12736 100644 --- a/index.md +++ b/index.md @@ -1,8 +1,14 @@ ---- -uid: index ---- +# Invex.Process - +A small, focused .NET library for running external processes with first-class logging, real-time output streaming, +full output capture, and fail-fast error handling. It wraps `System.Diagnostics.Process` behind a clean, injectable +`IProcessRunner` abstraction so your build targets, tools, and services can shell out to executables (`dotnet`, `git`, +`npm`, …) without re-implementing redirection, buffering, and exit-code handling every time. -Redirecting to [documentation home](README.md)... +## Start here +- [Introduction](docs/introduction.md): the problem it solves and when to use it. +- [Getting Started](docs/getting-started.md): install the package, register it and run your first process. +- [Running Processes](docs/running-processes.md): arguments, working directories and environment variables. +- [Handling Failures](docs/handling-failures.md): fail-fast exceptions and `AllowFailedResult`. +- [API Reference](api/index.md): every public type. diff --git a/toc.yml b/toc.yml index 0174e79..28fb420 100644 --- a/toc.yml +++ b/toc.yml @@ -1,5 +1,5 @@ - name: Docs href: docs/ + homepage: docs/introduction.md - name: API Reference href: api/ -