diff --git a/packages/exec/__tests__/exec.test.ts b/packages/exec/__tests__/exec.test.ts index aff2ceb4..130f3823 100644 --- a/packages/exec/__tests__/exec.test.ts +++ b/packages/exec/__tests__/exec.test.ts @@ -11,7 +11,7 @@ import * as io from '@actions/io' /* eslint-disable @typescript-eslint/unbound-method */ const IS_WINDOWS = process.platform === 'win32' -const SPAWN_WAIT_FOR_FILE = path.join( +const SPAWN_WAIT_SCRIPT = path.join( __dirname, 'scripts', 'spawn-wait-for-file.js' @@ -379,9 +379,11 @@ describe('@actions/exec', () => { } } - const args = [SPAWN_WAIT_FOR_FILE, `file=${semaphorePath}`] - - const exitCode = await exec.exec(`"${nodePath}"`, args, _testExecOptions) + const exitCode = await exec.exec( + `"${nodePath}"`, + [SPAWN_WAIT_SCRIPT, `file=${semaphorePath}`], + _testExecOptions + ) expect(exitCode).toBe(0) expect( @@ -408,10 +410,12 @@ describe('@actions/exec', () => { } } - const args = [SPAWN_WAIT_FOR_FILE, `file=${semaphorePath}`, 'exitCode=123'] - await exec - .exec(`"${nodePath}"`, args, _testExecOptions) + .exec( + `"${nodePath}"`, + [SPAWN_WAIT_SCRIPT, `file=${semaphorePath}`, 'exitCode=123'], + _testExecOptions + ) .then(() => { throw new Error('Should not have succeeded') }) @@ -446,10 +450,12 @@ describe('@actions/exec', () => { } } - const args = [SPAWN_WAIT_FOR_FILE, `file=${semaphorePath}`, 'stderr=true'] - await exec - .exec(`"${nodePath}"`, args, _testExecOptions) + .exec( + `"${nodePath}"`, + [SPAWN_WAIT_SCRIPT, `file=${semaphorePath}`, 'stderr=true'], + _testExecOptions + ) .then(() => { throw new Error('Should not have succeeded') }) @@ -466,7 +472,7 @@ describe('@actions/exec', () => { ).toBe(1) fs.unlinkSync(semaphorePath) - }) + }, 10000) it('Exec roots relative tool path using unrooted options.cwd', async () => { let exitCode: number diff --git a/packages/exec/__tests__/scripts/spawn-wait-for-file.js b/packages/exec/__tests__/scripts/spawn-wait-for-file.js index 09e6a912..6c777491 100644 --- a/packages/exec/__tests__/scripts/spawn-wait-for-file.js +++ b/packages/exec/__tests__/scripts/spawn-wait-for-file.js @@ -1,51 +1,39 @@ const childProcess = require('child_process') const path = require('path') -function parseArgs() { - const result = {} - for (const arg of process.argv.slice(2)) { - const equalsIndex = arg.indexOf('=') - if (equalsIndex === -1) { - continue - } - const key = arg.slice(0, equalsIndex) - const value = arg.slice(equalsIndex + 1) - result[key] = value +// Parse args +const args = {} +for (const arg of process.argv.slice(2)) { + const idx = arg.indexOf('=') + if (idx !== -1) { + args[arg.slice(0, idx)] = arg.slice(idx + 1) } - - return result } -const args = parseArgs() const filePath = args.file if (!filePath) { throw new Error('file is not specified') } +// Spawn wait-for-file.js with inherited stdio +// This creates a grandchild process that holds the stdio handles open +// after this process (the child) exits const waitScript = path.join(__dirname, 'wait-for-file.js') -const waitArgs = [waitScript, `file=${filePath}`] +const child = childProcess.spawn(process.execPath, [waitScript, `file=${filePath}`], { + stdio: ['ignore', 'inherit', 'inherit'], + detached: process.platform !== 'win32' +}) -// Spawn with inherited stdio and detached on Unix, non-detached on Windows -// This keeps the streams open after parent exits -const isWindows = process.platform === 'win32' -const spawnOptions = { - stdio: 'inherit', - detached: !isWindows -} - -// On Windows, we need to hide the window -if (isWindows) { - spawnOptions.windowsHide = true -} - -const waitProcess = childProcess.spawn(process.execPath, waitArgs, spawnOptions) - -// Unref so parent doesn't wait for child -waitProcess.unref() +// Don't wait for child to exit +child.unref() +// Handle optional stderr output (must happen BEFORE we exit) if (args.stderr === 'true') { process.stderr.write('hi') } -const exitCode = args.exitCode ? parseInt(args.exitCode, 10) : 0 -process.exit(exitCode) +// Small delay to ensure child has started and inherited handles +setTimeout(() => { + const exitCode = args.exitCode ? parseInt(args.exitCode, 10) : 0 + process.exit(exitCode) +}, 50)