From 49f2765e0610ff1cf5261765772a1298cf5c6c77 Mon Sep 17 00:00:00 2001 From: Konstantin Kharlamov Date: Sat, 22 Mar 2025 00:39:02 +0300 Subject: [PATCH 1/2] bower.json: add missing aff dependency --- bower.json | 1 + 1 file changed, 1 insertion(+) diff --git a/bower.json b/bower.json index 1dab847..035a1a9 100644 --- a/bower.json +++ b/bower.json @@ -16,6 +16,7 @@ "package.json" ], "dependencies": { + "purescript-aff": "^7.0.0", "purescript-exceptions": "^6.0.0", "purescript-node-event-emitter": "https://github.com/purescript-node/purescript-node-event-emitter.git#^3.0.0", "purescript-foreign": "^7.0.0", From 60eec0199add5c509f8e28c0542396c84087b2ae Mon Sep 17 00:00:00 2001 From: Konstantin Kharlamov Date: Fri, 21 Mar 2025 13:04:49 +0300 Subject: [PATCH 2/2] ChildProcess: account for a system error when launching a process When a non-existing command is attempted to run, both status and signal will be null, which leads to ChildProcess module crashing. Obviously, this is incorrect behavior. The problem is more general than that though: any system error that would result in failing to run a process would result in the module crash. Fix that by checking for such case. Fixes: https://github.com/purescript-node/purescript-node-child-process/issues/65 --- CHANGELOG.md | 4 +++- src/Node/ChildProcess.purs | 16 ++++++++++++---- src/Node/ChildProcess/Types.purs | 8 +++++--- test/Main.purs | 1 + 4 files changed, 21 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ed829fd..6f3d761 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,8 @@ New features: Bugfixes: +- Account for child creation to possibly fail with system error (#66 by @Hi-Angel) + Other improvements: ## [v11.1.0](https://github.com/purescript-node/purescript-node-child-process/releases/tag/v11.1.0) - 2023-11-14 @@ -76,7 +78,7 @@ Breaking changes: - Moved from `Node.ChildProces` to `Node.ChildProces.Types` - Changed the `BySignal`'s constructor's arg type from `Signal` to `String` - Breaking changes made to the `Handle` type (#46 by @JordanMartinez) - + - Moved from `Node.ChildProces` to `Node.ChildProces.Types` - Converted `defaultOptions { override = Just 1}` pattern to `(_ { override = Just 1})` (#46 by @JordanMartinez) diff --git a/src/Node/ChildProcess.purs b/src/Node/ChildProcess.purs index 8921441..b425d7a 100644 --- a/src/Node/ChildProcess.purs +++ b/src/Node/ChildProcess.purs @@ -21,7 +21,7 @@ -- | defined in this library that doesn't exist in the Node docs. -- | It exists to allow the end-user to append additional values to the `safeStdio` value -- | used here. For example, --- | +-- | -- | ``` -- | spawn' file args (_ { appendStdio = Just [ fileDescriptor8, pipe, pipe ]}) -- | ``` @@ -86,7 +86,7 @@ module Node.ChildProcess import Prelude -import Data.Maybe (Maybe(..), fromMaybe) +import Data.Maybe (Maybe(..), fromMaybe, isJust) import Data.Nullable (Nullable, toMaybe, toNullable) import Data.Posix (Pid, Gid, Uid) import Data.Posix.Signal (Signal) @@ -237,7 +237,11 @@ spawnSync command args = (UnsafeCP.spawnSync command args) <#> \r -> , exitStatus: case toMaybe r.status, toMaybe r.signal of Just c, _ -> Normally c _, Just s -> BySignal s - _, _ -> unsafeCrashWith $ "Impossible: `spawnSync` child process neither exited nor was killed." + _, _ -> + if isJust $ toMaybe r.error then + BySysError + else + unsafeCrashWith $ "Impossible: `spawnSync` child process neither exited nor was killed." , error: toMaybe r.error } @@ -282,7 +286,11 @@ spawnSync' command args buildOpts = (UnsafeCP.spawnSync' command args opts) <#> , exitStatus: case toMaybe r.status, toMaybe r.signal of Just c, _ -> Normally c _, Just s -> BySignal s - _, _ -> unsafeCrashWith $ "Impossible: `spawnSync` child process neither exited nor was killed." + _, _ -> + if isJust $ toMaybe r.error then + BySysError + else + unsafeCrashWith $ "Impossible: `spawnSync` child process neither exited nor was killed." , error: toMaybe r.error } where diff --git a/src/Node/ChildProcess/Types.purs b/src/Node/ChildProcess/Types.purs index 98f1370..ece2f8e 100644 --- a/src/Node/ChildProcess/Types.purs +++ b/src/Node/ChildProcess/Types.purs @@ -52,7 +52,7 @@ foreign import data StdIO :: Type -- | Note: when used with `stdin`, piping the parent stdin to this stream -- | will not cause the child process to terminate when that parent stdin stream -- | ends via `Ctrl+D` user input. Rather, the child process will hang --- | until the parent process calls `Stream.end` on the child process' +-- | until the parent process calls `Stream.end` on the child process' -- | `stdin` stream. Since it's impossible to know when the user -- | inputs `Ctrl+D`, `inherit` should be used instead. pipe :: StdIO @@ -140,12 +140,14 @@ customShell = unsafeCoerce -- | what options were used. foreign import data StringOrBuffer :: Type --- | Specifies how a child process exited; normally (with an exit code), or --- | due to a signal. +-- | Specifies how a child process exited; normally (with an exit code), due to +-- | a signal or if it failed to even launch (e.g. if a command doesn't exist). data Exit = Normally Int | BySignal KillSignal + | BySysError instance showExit :: Show Exit where show (Normally x) = "Normally " <> show x show (BySignal sig) = "BySignal " <> (either show show $ fromKillSignal sig) + show BySysError = "BySysError" diff --git a/test/Main.purs b/test/Main.purs index 2b973f3..3ad0b1e 100644 --- a/test/Main.purs +++ b/test/Main.purs @@ -72,6 +72,7 @@ spawnLs = do Normally 0 -> log $ "ls exited with 0" Normally i -> liftEffect $ throw $ "ls had non-zero exit: " <> show i BySignal sig -> liftEffect $ throw $ "ls exited with sig: " <> show sig + BySysError -> liftEffect $ throw "ls exited with system error" nonExistentExecutable :: Aff Unit nonExistentExecutable = do