Last active
November 2, 2022 12:19
-
-
Save dburriss/7b689cb0f505c3b784d14e96d5fa05b7 to your computer and use it in GitHub Desktop.
Finding and running a process in F# in a reliable way.
This file contains 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
// ======================================================== | |
// String: String module | |
// Helpers for working with strings | |
// ======================================================== | |
module String = | |
let isNotEmpty s = not (String.IsNullOrEmpty s) | |
let join (ss: string seq) = String.Join("", ss) | |
let lower (s: string) = s.ToLowerInvariant() | |
let trim (s: string) = s.Trim() | |
let replace (oldValue: string) newValue (s: string) = s.Replace(oldValue,newValue) | |
let split (by: string) (s: string) = s.Split(by) | |
let endsWith (tail: string) (s: string) = s.EndsWith(tail) | |
let startsWith (head: string) (s: string) = s.StartsWith(head) | |
let contains (search: string) (s: string) = s.Contains(search) | |
// ======================================================== | |
// Env: Env module | |
// Helper functions for interacting with the local environment | |
// ======================================================== | |
type OS = | Windows | Linux | OSX | FreeBSD | |
module Env = | |
open System.Runtime.InteropServices | |
let os() = | |
if RuntimeInformation.IsOSPlatform(OSPlatform.Windows) then Windows | |
elif RuntimeInformation.IsOSPlatform(OSPlatform.Linux) then Linux | |
elif RuntimeInformation.IsOSPlatform(OSPlatform.OSX) then OSX | |
else FreeBSD | |
let varNames() : string seq = seq { for k in Environment.GetEnvironmentVariables().Keys -> k :?> string } | |
let path() = | |
Environment.GetEnvironmentVariable("PATH") | |
let tryGet name = | |
let value = Environment.GetEnvironmentVariable(name) | |
if isNull value then None | |
else Some value | |
let executables os = | |
match os with | |
| Windows -> [".exe";".bat";".cmd";".ps1"] | |
| _ -> ["";".sh"] | |
let dirSep() = string IO.Path.DirectorySeparatorChar | |
// ======================================================== | |
// Proc: Proc module | |
// For calling CLI process on the machine | |
// ======================================================== | |
module Proc = | |
let private fileName (dir: string) command ext = | |
let pathSplit = Env.dirSep() | |
let path = if IO.Path.EndsInDirectorySeparator(dir) then dir else dir + pathSplit | |
$"{path}{command}{ext}" | |
let private searchDirectoryForCommand extensions command dir = | |
let fullFilePath = fileName dir command | |
let toCheck = | |
extensions | |
|> List.map fullFilePath | |
toCheck |> List.filter (fun f -> IO.File.Exists(f)) | |
let private fetchCommand os envPATH command = | |
let extensions = Env.executables os | |
let dirs = envPATH |> String.split (string IO.Path.PathSeparator) |> List.ofArray | |
let executables = | |
dirs | |
|> List.map (searchDirectoryForCommand extensions command) | |
|> List.concat | |
executables |> List.tryHead | |
let run command args workingDir = | |
let commandOpt = fetchCommand (Env.os()) (Env.path()) command | |
match commandOpt with | |
| Some cmd -> | |
Debug.WriteLine(sprintf "RUNNING COMMAND: %s" cmd) | |
let info = ProcessStartInfo(cmd, args) | |
info.WorkingDirectory <- workingDir //must be full path not using ~/ | |
info.UseShellExecute <- false | |
info.RedirectStandardOutput <- true | |
info.RedirectStandardError <- true | |
let p = Process.Start(info) | |
let output = | |
[| | |
while not p.StandardOutput.EndOfStream do | |
let o = p.StandardOutput.ReadLine() | |
if String.isNotEmpty o then o | |
|] | |
let err = p.StandardError.ReadToEnd() | |
p.WaitForExit() | |
if p.ExitCode = 0 || String.IsNullOrEmpty(err) then | |
Ok output | |
else Error (p.ExitCode, err) | |
| None -> Error(-1, $"Unable to find executable ({command}). Make sure it appears in your PATH.") |
This file contains 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
Proc.run "npm" "audit --json" "./project-dir" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment