Last active
June 1, 2024 23:48
-
-
Save ruxo/2c29ec0b4f7e0f282a84abbc58584685 to your computer and use it in GitHub Desktop.
A helper library to assist running multiple command lines and combine their output into the current console. See `sample.fsx` about how to use it. To run the sample, use the command line `dotnet fsi sample.fsx`
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[<AutoOpen>] | |
module RunHelpers | |
#r "nuget: FSharp.Control.Reactive" | |
open System | |
open System.Diagnostics | |
open System.IO | |
open System.Threading | |
open FSharp.Control.Reactive | |
printfn "run-helpers 0.1.0" | |
module private Process = | |
let private streamToObservable (stream: StreamReader) = | |
let observable = Subject.broadcast | |
let rec loop () = | |
async { | |
let! line = stream.ReadLineAsync() |> Async.AwaitTask | |
if line = null then | |
observable |> Subject.onCompleted |> ignore | |
return () | |
else | |
observable |> Subject.onNext line |> ignore | |
return! loop () | |
} | |
Async.StartAsTask (loop ()) |> ignore | |
observable :> IObservable<string> | |
let getProcessOutput (p: Process) = | |
let toObservable = streamToObservable >> Observable.map (fun line -> $"[{p.Id}] {line}") | |
let standard = p.StandardOutput |> toObservable | |
let error = p.StandardError |> toObservable | |
Observable.mergeSeq [standard; error] | |
let run (commands: string) = | |
let parts = commands.Split(' ', StringSplitOptions.RemoveEmptyEntries) | |
let start_info = match parts.Length with | |
| 0 -> failwith "No command provided" | |
| 1 -> ProcessStartInfo(parts[0]) | |
| 2 -> ProcessStartInfo(parts[0], parts[1]) | |
| _ -> ProcessStartInfo(parts[0], parts[1..]) | |
start_info.UseShellExecute <- false | |
start_info.RedirectStandardInput <- true | |
start_info.RedirectStandardOutput <- true | |
start_info.RedirectStandardError <- true | |
start_info | |
let startFrom (paths: string seq) (start_info: ProcessStartInfo) = | |
let current = Directory.GetCurrentDirectory() | |
let full_path = paths |> Seq.toArray |> Path.Combine | |
start_info.WorkingDirectory <- full_path | |
full_path |> Directory.SetCurrentDirectory | |
let p = start_info |> Process.Start | |
Directory.SetCurrentDirectory current | |
p | |
[<Literal>] | |
let private ControlCSequence = "\x3" | |
let consumeOutput (processes: Process seq) = | |
use wait_to_quit = new AutoResetEvent(false) | |
let checkProcessesExited() = | |
if processes |> Seq.forall _.HasExited then | |
wait_to_quit.Set() |> ignore | |
true | |
else | |
false | |
let output = processes |> Seq.map Process.getProcessOutput | |
|> Observable.mergeSeq | |
use subscription = | |
output |> Observable.subscribeObserver | |
{ new IObserver<string> with | |
member _.OnNext value = printfn $"%s{value}" | |
member _.OnError ex = printfn $"Error: {ex.Message}" | |
member _.OnCompleted () = checkProcessesExited() |> ignore | |
} | |
Console.CancelKeyPress.Add( | |
fun args -> | |
processes |> Seq.iter (fun p -> p.StandardInput.WriteLine ControlCSequence) | |
let ``continue`` = not <| checkProcessesExited() | |
args.Cancel <- ``continue`` | |
printfn $"Cancel: {``continue``}" | |
) | |
wait_to_quit.WaitOne() |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#load "run-helpers.fsx" | |
[<Literal>] | |
let dms_path = @"C:\Workspace\TCRB\Document" | |
let all_processes = | |
[ run "dotnet run" |> startFrom [ dms_path; @"dms\src\DMS.Api" ] | |
run "dotnet run" |> startFrom [ dms_path; @"dms\src\DMS.Background"] | |
run "yarn.cmd dev" |> startFrom [ dms_path; "dms-fe" ] | |
] | |
printfn "Start!" | |
all_processes |> consumeOutput | |
printfn "Done!" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment