Repository F# setup
open System
open System.IO
open System.Threading
open System.Threading.Tasks
open Axial
open Axial.Console
open Axial.FileSystem
open Axial.PlatformService
open Axial.Process

Worked examples

Each command is a value until a Flow runner reaches the host boundary.

A command-line tool that shows progress

A command-line entry point can forward output through the console and choose its status from Exit. Process.tryRun preserves the cause so the entry point can report startup failures and interruptions.

Show page setup
// Setup for the checked examples on this page.
open Axial
open Axial.Process
open Axial.Process.DSL
/// Fails the docs test when an example's result differs from the value shown.
let shouldEqual expected actual =
    if actual <> expected then failwithf "Expected %A but got %A" expected actual
Verified
open Axial.Process.DSL

// In a program, mark this [<EntryPoint>].
let main _ =
    let outcome =
        cmd $"dotnet --info"
        |> stdoutToConsole
        |> Process.tryRun
    match outcome with
    | Exit.Success result -> result.ExitCode
    | Exit.Failure(Cause.Fail error) ->
        eprintfn "%s" (ProcessError.describe error)
        ProcessError.exitCode error
    | Exit.Failure cause ->
        eprintfn "%A" cause
        1
Requires & types
Requires: open Axial; open Axial.Process; open Axial.Process.DSL (packages: Axial, Axial.Process)
val main: 'a -> int

Console forwarding is configured lazily; Process.tryRun starts the command.

Use a command as one step in an application

Use toFlow when the command is one step among other application work. The application supplies its own IProcess service and decides how to use the result. git rev-parse --short HEAD reads the enclosing Git checkout, so this example needs a reachable HEAD; replace it with a deterministic command such as cmd $"printf v1.2.3" in a project without Git.

Verified
let gitRevision : Flow<#IHasProcess, ProcessError, string> =
    flow {
        let! result =
            cmd $"git rev-parse --short HEAD"
            |> toFlow

        return result.StdOut.Trim()
    }

let buildLabel =
    flow {
        let! revision = gitRevision
        return $"build-{revision}"
    }

let appEnvironment = ProcessHostEnvironment.live
Requires & types
Requires: open Axial; open Axial.Process; open Axial.Process.DSL (packages: Axial, Axial.Process)
val gitRevision: Flow<ProcessHostEnvironment,ProcessError,string>
val buildLabel: Flow<ProcessHostEnvironment,ProcessError,string>
val appEnvironment: ProcessHostEnvironment
Verified
match Flow.run appEnvironment buildLabel with
| Exit.Success label -> label.StartsWith "build-" |> shouldEqual true
| other -> failwithf "unexpected %A" other
Requires & types
Requires: open Axial (packages: Axial)

toFlow uses batch capture defaults. Flow.run executes the enclosing application workflow with its explicit environment.

Stream lines into workflow decisions

streamStdoutLines yields only stdout lines. Its terminal returns values and completion separately.

Verified
let collectEvents =
    cmd $"printf '{{\"event\":\"ready\"}}\n{{\"event\":\"done\"}}\n'"
    |> streamStdoutLines
    |> ProcessStream.runCollect
    |> Flow.map fst

Requires & types
Requires: open Axial; open Axial.Process; open Axial.Process.DSL (packages: Axial, Axial.Process)
val collectEvents: Flow<ProcessHostEnvironment,ProcessError,string list>
Verified
Flow.run appEnvironment collectEvents
|> shouldEqual (Exit.Success [ """{"event":"ready"}"""; """{"event":"done"}""" ])
Requires & types
Requires: open Axial (packages: Axial)

The consumer pulls the stream with backpressure. Interrupting the enclosing Flow interrupts the native process topology and waits for its cleanup. Only ProcessStream.take and ProcessStream.takeWhile stop the process early and report the stop as ProcessCompletion.StoppedEarly; after ProcessStream.toFlowStream, process completion is discarded, so a consumer that stops early there simply ends the stream. See streaming output.

For more checked command-line patterns, see DSL recipes.

For command construction and pipelines, continue with commands, input, and pipelines. For output destinations and bounded capture, see output capture and destinations.