diff --git a/src/bun.js/api/bun/process.zig b/src/bun.js/api/bun/process.zig index 198289a92741f..fe982a202dd37 100644 --- a/src/bun.js/api/bun/process.zig +++ b/src/bun.js/api/bun/process.zig @@ -962,6 +962,7 @@ pub const PosixSpawnOptions = struct { stdin: Stdio = .ignore, stdout: Stdio = .ignore, stderr: Stdio = .ignore, + ipc: ?bun.FileDescriptor = null, extra_fds: []const Stdio = &.{}, cwd: []const u8 = "", detached: bool = false, @@ -1031,6 +1032,7 @@ pub const WindowsSpawnOptions = struct { stdin: Stdio = .ignore, stdout: Stdio = .ignore, stderr: Stdio = .ignore, + ipc: ?bun.FileDescriptor = null, extra_fds: []const Stdio = &.{}, cwd: []const u8 = "", detached: bool = false, @@ -1077,6 +1079,7 @@ pub const PosixSpawnResult = struct { stdin: ?bun.FileDescriptor = null, stdout: ?bun.FileDescriptor = null, stderr: ?bun.FileDescriptor = null, + ipc: ?bun.FileDescriptor = null, extra_pipes: std.ArrayList(bun.FileDescriptor) = std.ArrayList(bun.FileDescriptor).init(bun.default_allocator), memfds: [3]bool = .{ false, false, false }, @@ -1260,6 +1263,11 @@ pub fn spawnProcessPosix( attr.set(@intCast(flags)) catch {}; attr.resetSignals() catch {}; + if (options.ipc) |ipc| { + try actions.inherit(ipc); + spawned.ipc = ipc; + } + const stdio_options: [3]PosixSpawnOptions.Stdio = .{ options.stdin, options.stdout, options.stderr }; const stdios: [3]*?bun.FileDescriptor = .{ &spawned.stdin, &spawned.stdout, &spawned.stderr }; @@ -1765,6 +1773,7 @@ pub const sync = struct { stdin: Stdio = .ignore, stdout: Stdio = .inherit, stderr: Stdio = .inherit, + ipc: ?bun.FileDescriptor = null, cwd: []const u8 = "", detached: bool = false, @@ -1799,6 +1808,8 @@ pub const sync = struct { .stdin = this.stdin.toStdio(), .stdout = this.stdout.toStdio(), .stderr = this.stderr.toStdio(), + .ipc = this.ipc, + .cwd = this.cwd, .detached = this.detached, .use_execve_on_macos = this.use_execve_on_macos, diff --git a/src/cli/run_command.zig b/src/cli/run_command.zig index 469d9bb0f74e5..84b62dfe6d07f 100644 --- a/src/cli/run_command.zig +++ b/src/cli/run_command.zig @@ -341,6 +341,12 @@ pub const RunCommand = struct { combined_script, }; + const ipc_fd = if (!Environment.isWindows) blk: { + const node_ipc_fd = bun.getenvZ("NODE_CHANNEL_FD") orelse break :blk null; + const fd = std.fmt.parseInt(u32, node_ipc_fd, 10) catch break :blk null; + break :blk bun.toFD(@as(i32, @intCast(fd))); + } else null; // TODO: implement on Windows + const spawn_result = switch ((bun.spawnSync(&.{ .argv = &argv, .argv0 = shell_bin.ptr, @@ -353,6 +359,7 @@ pub const RunCommand = struct { .stderr = .inherit, .stdout = .inherit, .stdin = .inherit, + .ipc = ipc_fd, .windows = if (Environment.isWindows) .{ .loop = JSC.EventLoopHandle.init(JSC.MiniEventLoop.initGlobal(env)), diff --git a/test/js/bun/spawn/bun-ipc-inherit.test.ts b/test/js/bun/spawn/bun-ipc-inherit.test.ts new file mode 100644 index 0000000000000..1cbd1247ed7cc --- /dev/null +++ b/test/js/bun/spawn/bun-ipc-inherit.test.ts @@ -0,0 +1,28 @@ +import { spawn, spawnSync, env } from "bun"; +import fs from "node:fs/promises"; +import { describe, expect, it } from "bun:test"; +import { bunExe, isWindows } from "harness"; +import path from "path"; + +it.todoIf(isWindows)("spawning a bun package script should inherit the ipc fd", async () => { + await fs.writeFile( + path.join(process.cwd(), "package.json"), + JSON.stringify({ + scripts: { + test: `${bunExe()} -e 'process.send("hello")'`, + }, + }), + ); + + let testMessage; + + const child = spawn([bunExe(), "run", "test"], { + ipc: message => { + testMessage = message; + }, + stdio: ["inherit", "inherit", "inherit"], + }); + + await child.exited; + expect(testMessage).toBe("hello"); +});