diff --git a/jest.config.js b/jest.config.js index f25dbdc9f3..2892968146 100644 --- a/jest.config.js +++ b/jest.config.js @@ -5,7 +5,7 @@ module.exports = { testEnvironment: 'node', testMatch: ['**/__tests__/*.test.ts'], transform: { - '^.+\\.ts$': 'ts-jest' + '^.+\\.ts$': ['ts-jest', {isolatedModules: true, diagnostics: {warnOnly: true}}] }, verbose: true } diff --git a/packages/core/RELEASES.md b/packages/core/RELEASES.md index 697016601f..3afc9050de 100644 --- a/packages/core/RELEASES.md +++ b/packages/core/RELEASES.md @@ -1,6 +1,10 @@ # @actions/core Releases -### 1.11.1 +## 2.0.0 +- Add support for Node 24 [#2110](https://github.com/actions/toolkit/pull/2110) +- Bump @actions/http-client from 2.0.1 to 3.0.0 + +## 1.11.1 - Fix uses of `crypto.randomUUID` on Node 18 and earlier [#1842](https://github.com/actions/toolkit/pull/1842) ### 1.11.0 diff --git a/packages/core/package-lock.json b/packages/core/package-lock.json index 95cf58d255..924750d615 100644 --- a/packages/core/package-lock.json +++ b/packages/core/package-lock.json @@ -1,16 +1,16 @@ { "name": "@actions/core", - "version": "1.11.1", + "version": "2.0.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@actions/core", - "version": "1.11.1", + "version": "2.0.0", "license": "MIT", "dependencies": { "@actions/exec": "^1.1.1", - "@actions/http-client": "^2.0.1" + "@actions/http-client": "^3.0.0" }, "devDependencies": { "@types/node": "^16.18.112" @@ -25,11 +25,13 @@ } }, "node_modules/@actions/http-client": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.1.0.tgz", - "integrity": "sha512-BonhODnXr3amchh4qkmjPMUO8mFi/zLaaCeCAJZqch8iQqyDnVIkySjB38VHAC8IJ+bnlgfOqlhpyCUZHlQsqw==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-3.0.0.tgz", + "integrity": "sha512-1s3tXAfVMSz9a4ZEBkXXRQD4QhY3+GAsWSbaYpeknPOKEeyRiU3lH+bHiLMZdo2x/fIeQ/hscL1wCkDLVM2DZQ==", + "license": "MIT", "dependencies": { - "tunnel": "^0.0.6" + "tunnel": "^0.0.6", + "undici": "^5.28.5" } }, "node_modules/@actions/io": { @@ -37,6 +39,15 @@ "resolved": "https://registry.npmjs.org/@actions/io/-/io-1.1.3.tgz", "integrity": "sha512-wi9JjgKLYS7U/z8PPbco+PvTb/nRWjeoFlJ1Qer83k/3C5PHQi28hiVdeE2kHXmIL99mQFawx8qt/JPjZilJ8Q==" }, + "node_modules/@fastify/busboy": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.1.1.tgz", + "integrity": "sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==", + "license": "MIT", + "engines": { + "node": ">=14" + } + }, "node_modules/@types/node": { "version": "16.18.112", "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.112.tgz", @@ -50,6 +61,18 @@ "engines": { "node": ">=0.6.11 <=0.7.0 || >=0.7.3" } + }, + "node_modules/undici": { + "version": "5.29.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-5.29.0.tgz", + "integrity": "sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg==", + "license": "MIT", + "dependencies": { + "@fastify/busboy": "^2.0.0" + }, + "engines": { + "node": ">=14.0" + } } }, "dependencies": { @@ -62,11 +85,12 @@ } }, "@actions/http-client": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.1.0.tgz", - "integrity": "sha512-BonhODnXr3amchh4qkmjPMUO8mFi/zLaaCeCAJZqch8iQqyDnVIkySjB38VHAC8IJ+bnlgfOqlhpyCUZHlQsqw==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-3.0.0.tgz", + "integrity": "sha512-1s3tXAfVMSz9a4ZEBkXXRQD4QhY3+GAsWSbaYpeknPOKEeyRiU3lH+bHiLMZdo2x/fIeQ/hscL1wCkDLVM2DZQ==", "requires": { - "tunnel": "^0.0.6" + "tunnel": "^0.0.6", + "undici": "^5.28.5" } }, "@actions/io": { @@ -74,6 +98,11 @@ "resolved": "https://registry.npmjs.org/@actions/io/-/io-1.1.3.tgz", "integrity": "sha512-wi9JjgKLYS7U/z8PPbco+PvTb/nRWjeoFlJ1Qer83k/3C5PHQi28hiVdeE2kHXmIL99mQFawx8qt/JPjZilJ8Q==" }, + "@fastify/busboy": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.1.1.tgz", + "integrity": "sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==" + }, "@types/node": { "version": "16.18.112", "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.112.tgz", @@ -84,6 +113,14 @@ "version": "0.0.6", "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==" + }, + "undici": { + "version": "5.29.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-5.29.0.tgz", + "integrity": "sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg==", + "requires": { + "@fastify/busboy": "^2.0.0" + } } } } diff --git a/packages/core/package.json b/packages/core/package.json index 6d60010e37..e8eae1641b 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@actions/core", - "version": "1.11.1", + "version": "2.0.0", "description": "Actions core lib", "keywords": [ "github", @@ -37,7 +37,7 @@ }, "dependencies": { "@actions/exec": "^1.1.1", - "@actions/http-client": "^2.0.1" + "@actions/http-client": "^3.0.0" }, "devDependencies": { "@types/node": "^16.18.112" diff --git a/packages/exec/__tests__/exec.test.ts b/packages/exec/__tests__/exec.test.ts index 1f83ce4aac..aff2ceb427 100644 --- a/packages/exec/__tests__/exec.test.ts +++ b/packages/exec/__tests__/exec.test.ts @@ -11,6 +11,11 @@ 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( + __dirname, + 'scripts', + 'spawn-wait-for-file.js' +) let outstream: stream.Writable let errstream: stream.Writable @@ -365,35 +370,18 @@ describe('@actions/exec', () => { fs.writeFileSync(semaphorePath, '') const nodePath = await io.which('node', true) - const scriptPath = path.join(__dirname, 'scripts', 'wait-for-file.js') const debugList: string[] = [] const _testExecOptions = getExecOptions() _testExecOptions.delay = 500 - _testExecOptions.windowsVerbatimArguments = true _testExecOptions.listeners = { debug: (data: string) => { debugList.push(data) } } - let exitCode: number - if (IS_WINDOWS) { - const toolName: string = await io.which('cmd.exe', true) - const args = [ - '/D', // Disable execution of AutoRun commands from registry. - '/E:ON', // Enable command extensions. Note, command extensions are enabled by default, unless disabled via registry. - '/V:OFF', // Disable delayed environment expansion. Note, delayed environment expansion is disabled by default, unless enabled via registry. - '/S', // Will cause first and last quote after /C to be stripped. - '/C', - `"start "" /B "${nodePath}" "${scriptPath}" "file=${semaphorePath}""` - ] - exitCode = await exec.exec(`"${toolName}"`, args, _testExecOptions) - } else { - const toolName: string = await io.which('bash', true) - const args = ['-c', `node '${scriptPath}' 'file=${semaphorePath}' &`] + const args = [SPAWN_WAIT_FOR_FILE, `file=${semaphorePath}`] - exitCode = await exec.exec(`"${toolName}"`, args, _testExecOptions) - } + const exitCode = await exec.exec(`"${nodePath}"`, args, _testExecOptions) expect(exitCode).toBe(0) expect( @@ -411,36 +399,19 @@ describe('@actions/exec', () => { fs.writeFileSync(semaphorePath, '') const nodePath = await io.which('node', true) - const scriptPath = path.join(__dirname, 'scripts', 'wait-for-file.js') const debugList: string[] = [] const _testExecOptions = getExecOptions() _testExecOptions.delay = 500 - _testExecOptions.windowsVerbatimArguments = true _testExecOptions.listeners = { debug: (data: string) => { debugList.push(data) } } - let toolName: string - let args: string[] - if (IS_WINDOWS) { - toolName = await io.which('cmd.exe', true) - args = [ - '/D', // Disable execution of AutoRun commands from registry. - '/E:ON', // Enable command extensions. Note, command extensions are enabled by default, unless disabled via registry. - '/V:OFF', // Disable delayed environment expansion. Note, delayed environment expansion is disabled by default, unless enabled via registry. - '/S', // Will cause first and last quote after /C to be stripped. - '/C', - `"start "" /B "${nodePath}" "${scriptPath}" "file=${semaphorePath}"" & exit /b 123` - ] - } else { - toolName = await io.which('bash', true) - args = ['-c', `node '${scriptPath}' 'file=${semaphorePath}' & exit 123`] - } + const args = [SPAWN_WAIT_FOR_FILE, `file=${semaphorePath}`, 'exitCode=123'] await exec - .exec(`"${toolName}"`, args, _testExecOptions) + .exec(`"${nodePath}"`, args, _testExecOptions) .then(() => { throw new Error('Should not have succeeded') }) @@ -465,40 +436,20 @@ describe('@actions/exec', () => { fs.writeFileSync(semaphorePath, '') const nodePath = await io.which('node', true) - const scriptPath = path.join(__dirname, 'scripts', 'wait-for-file.js') const debugList: string[] = [] const _testExecOptions = getExecOptions() _testExecOptions.delay = 500 _testExecOptions.failOnStdErr = true - _testExecOptions.windowsVerbatimArguments = true _testExecOptions.listeners = { debug: (data: string) => { debugList.push(data) } } - let toolName: string - let args: string[] - if (IS_WINDOWS) { - toolName = await io.which('cmd.exe', true) - args = [ - '/D', // Disable execution of AutoRun commands from registry. - '/E:ON', // Enable command extensions. Note, command extensions are enabled by default, unless disabled via registry. - '/V:OFF', // Disable delayed environment expansion. Note, delayed environment expansion is disabled by default, unless enabled via registry. - '/S', // Will cause first and last quote after /C to be stripped. - '/C', - `"start "" /B "${nodePath}" "${scriptPath}" "file=${semaphorePath}"" & echo hi 1>&2` - ] - } else { - toolName = await io.which('bash', true) - args = [ - '-c', - `node '${scriptPath}' 'file=${semaphorePath}' & echo hi 1>&2` - ] - } + const args = [SPAWN_WAIT_FOR_FILE, `file=${semaphorePath}`, 'stderr=true'] await exec - .exec(`"${toolName}"`, args, _testExecOptions) + .exec(`"${nodePath}"`, args, _testExecOptions) .then(() => { throw new Error('Should not have succeeded') }) diff --git a/packages/exec/__tests__/scripts/spawn-wait-for-file.js b/packages/exec/__tests__/scripts/spawn-wait-for-file.js new file mode 100644 index 0000000000..09e6a912fd --- /dev/null +++ b/packages/exec/__tests__/scripts/spawn-wait-for-file.js @@ -0,0 +1,51 @@ +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 + } + + return result +} + +const args = parseArgs() +const filePath = args.file +if (!filePath) { + throw new Error('file is not specified') +} + +const waitScript = path.join(__dirname, 'wait-for-file.js') +const waitArgs = [waitScript, `file=${filePath}`] + +// 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() + +if (args.stderr === 'true') { + process.stderr.write('hi') +} + +const exitCode = args.exitCode ? parseInt(args.exitCode, 10) : 0 +process.exit(exitCode) diff --git a/packages/exec/__tests__/scripts/stdoutputspecial.js b/packages/exec/__tests__/scripts/stdoutputspecial.js index 6a641cd021..95f0ff427c 100644 --- a/packages/exec/__tests__/scripts/stdoutputspecial.js +++ b/packages/exec/__tests__/scripts/stdoutputspecial.js @@ -1,8 +1,8 @@ //first half of © character -process.stdout.write(Buffer.from([0xC2]), (err) => { +process.stdout.write(Buffer.from([0xC2]), () => { //write in the callback so that the second byte is sent separately setTimeout(() => { process.stdout.write(Buffer.from([0xA9])) //second half of © character - }, 5000) + }, 100) }) diff --git a/packages/glob/__tests__/hash-files.test.ts b/packages/glob/__tests__/hash-files.test.ts index 51ed3ee6ac..bba3f63f65 100644 --- a/packages/glob/__tests__/hash-files.test.ts +++ b/packages/glob/__tests__/hash-files.test.ts @@ -4,6 +4,7 @@ import {hashFiles} from '../src/glob' import {promises as fs} from 'fs' const IS_WINDOWS = process.platform === 'win32' +const ORIGINAL_GITHUB_WORKSPACE = process.env['GITHUB_WORKSPACE'] /** * These test focus on the ability of globber to find files @@ -12,6 +13,16 @@ const IS_WINDOWS = process.platform === 'win32' describe('globber', () => { beforeAll(async () => { await io.rmRF(getTestTemp()) + process.env['GITHUB_WORKSPACE'] = __dirname + }) + + afterAll(async () => { + if (ORIGINAL_GITHUB_WORKSPACE) { + process.env['GITHUB_WORKSPACE'] = ORIGINAL_GITHUB_WORKSPACE + } else { + delete process.env['GITHUB_WORKSPACE'] + } + await io.rmRF(getTestTemp()) }) it('basic hashfiles test', async () => {