diff options
-rw-r--r-- | cli/tests/unit/command_test.ts | 29 | ||||
-rw-r--r-- | runtime/ops/process.rs | 55 |
2 files changed, 75 insertions, 9 deletions
diff --git a/cli/tests/unit/command_test.ts b/cli/tests/unit/command_test.ts index ce3af08fb..5f56a0c22 100644 --- a/cli/tests/unit/command_test.ts +++ b/cli/tests/unit/command_test.ts @@ -424,6 +424,31 @@ Deno.test({ permissions: { run: true } }, function commandSyncNotFound() { ); }); +Deno.test({ permissions: { run: true, read: true } }, function cwdNotFound() { + assertThrows( + () => + new Deno.Command(Deno.execPath(), { + cwd: Deno.cwd() + "/non-existent-directory", + }).output(), + Deno.errors.NotFound, + "No such cwd", + ); +}); + +Deno.test( + { permissions: { run: true, read: true } }, + function cwdNotDirectory() { + assertThrows( + () => + new Deno.Command(Deno.execPath(), { + cwd: Deno.execPath(), + }).output(), + Deno.errors.NotFound, + "cwd is not a directory", + ); + }, +); + Deno.test( { permissions: { run: true, read: true } }, async function commandFailedWithCode() { @@ -892,12 +917,12 @@ Deno.test( assertThrows( () => new Deno.Command("doesntexist").outputSync(), Error, - "Failed to spawn: doesntexist", + "Failed to spawn 'doesntexist'", ); await assertRejects( async () => await new Deno.Command("doesntexist").output(), Error, - "Failed to spawn: doesntexist", + "Failed to spawn 'doesntexist'", ); }, ); diff --git a/runtime/ops/process.rs b/runtime/ops/process.rs index a5150950e..51c9f961a 100644 --- a/runtime/ops/process.rs +++ b/runtime/ops/process.rs @@ -286,12 +286,53 @@ fn spawn_child( // We want to kill child when it's closed command.kill_on_drop(true); - let mut child = command.spawn().with_context(|| { - format!( - "Failed to spawn: {}", - command.as_std().get_program().to_string_lossy() - ) - })?; + let mut child = match command.spawn() { + Ok(child) => child, + Err(err) => { + let command = command.as_std(); + let command_name = command.get_program().to_string_lossy(); + + if let Some(cwd) = command.get_current_dir() { + // launching a sub process always depends on the real + // file system so using these methods directly is ok + #[allow(clippy::disallowed_methods)] + if !cwd.exists() { + return Err( + std::io::Error::new( + std::io::ErrorKind::NotFound, + format!( + "Failed to spawn '{}': No such cwd '{}'", + command_name, + cwd.to_string_lossy() + ), + ) + .into(), + ); + } + + #[allow(clippy::disallowed_methods)] + if !cwd.is_dir() { + return Err( + std::io::Error::new( + std::io::ErrorKind::NotFound, + format!( + "Failed to spawn '{}': cwd is not a directory '{}'", + command_name, + cwd.to_string_lossy() + ), + ) + .into(), + ); + } + } + + return Err(AnyError::from(err).context(format!( + "Failed to spawn '{}'", + command.get_program().to_string_lossy() + ))); + } + }; + let pid = child.id().expect("Process ID should be set."); let stdin_rid = child @@ -362,7 +403,7 @@ fn op_spawn_sync( let mut command = create_command(state, args, "Deno.Command().outputSync()")?; let output = command.output().with_context(|| { format!( - "Failed to spawn: {}", + "Failed to spawn '{}'", command.get_program().to_string_lossy() ) })?; |