From 60edb5dd545a775178f52524783378180af0d1f8 Mon Sep 17 00:00:00 2001 From: Dmitry Shibanov Date: Wed, 7 Feb 2024 05:42:16 +0100 Subject: [PATCH] Add support for arm64 Windows (#927) * add support for arm64 Windows * revert 7z to exe * add comment --------- Co-authored-by: aparnajyothi-y <147696841+aparnajyothi-y@users.noreply.github.com> --- __tests__/main.test.ts | 11 ++++++++ __tests__/official-installer.test.ts | 3 +++ dist/cache-save/index.js | 4 ++- dist/setup/index.js | 36 +++++++++++++++++++++----- src/distributions/base-distribution.ts | 30 +++++++++++++++++---- src/util.ts | 5 ++-- 6 files changed, 75 insertions(+), 14 deletions(-) diff --git a/__tests__/main.test.ts b/__tests__/main.test.ts index e54e5ea..501741a 100644 --- a/__tests__/main.test.ts +++ b/__tests__/main.test.ts @@ -2,6 +2,7 @@ import * as core from '@actions/core'; import * as exec from '@actions/exec'; import * as tc from '@actions/tool-cache'; import * as cache from '@actions/cache'; +import * as io from '@actions/io'; import fs from 'fs'; import path from 'path'; @@ -24,6 +25,10 @@ describe('main tests', () => { let startGroupSpy: jest.SpyInstance; let endGroupSpy: jest.SpyInstance; + let whichSpy: jest.SpyInstance; + + let existsSpy: jest.SpyInstance; + let getExecOutputSpy: jest.SpyInstance; let getNodeVersionFromFileSpy: jest.SpyInstance; @@ -55,6 +60,8 @@ describe('main tests', () => { inSpy = jest.spyOn(core, 'getInput'); inSpy.mockImplementation(name => inputs[name]); + whichSpy = jest.spyOn(io, 'which'); + getExecOutputSpy = jest.spyOn(exec, 'getExecOutput'); findSpy = jest.spyOn(tc, 'find'); @@ -140,6 +147,10 @@ describe('main tests', () => { return {stdout: obj[command], stderr: '', exitCode: 0}; }); + whichSpy.mockImplementation(cmd => { + return `some/${cmd}/path`; + }); + await util.printEnvDetailsAndSetOutput(); expect(setOutputSpy).toHaveBeenCalledWith('node-version', obj['node']); diff --git a/__tests__/official-installer.test.ts b/__tests__/official-installer.test.ts index 2d36c19..2d8f17c 100644 --- a/__tests__/official-installer.test.ts +++ b/__tests__/official-installer.test.ts @@ -248,6 +248,9 @@ describe('setup-node', () => { const toolPath = path.normalize('/cache/node/12.16.2/x64'); exSpy.mockImplementation(async () => '/some/other/temp/path'); cacheSpy.mockImplementation(async () => toolPath); + whichSpy.mockImplementation(cmd => { + return `some/${cmd}/path`; + }); await main.run(); diff --git a/dist/cache-save/index.js b/dist/cache-save/index.js index 35d5494..3ced1ad 100644 --- a/dist/cache-save/index.js +++ b/dist/cache-save/index.js @@ -83336,6 +83336,7 @@ Object.defineProperty(exports, "__esModule", ({ value: true })); exports.unique = exports.printEnvDetailsAndSetOutput = exports.getNodeVersionFromFile = void 0; const core = __importStar(__nccwpck_require__(2186)); const exec = __importStar(__nccwpck_require__(1514)); +const io = __importStar(__nccwpck_require__(7436)); const fs_1 = __importDefault(__nccwpck_require__(7147)); const path_1 = __importDefault(__nccwpck_require__(1017)); function getNodeVersionFromFile(versionFilePath) { @@ -83387,7 +83388,8 @@ function printEnvDetailsAndSetOutput() { return __awaiter(this, void 0, void 0, function* () { core.startGroup('Environment details'); const promises = ['node', 'npm', 'yarn'].map((tool) => __awaiter(this, void 0, void 0, function* () { - const output = yield getToolVersion(tool, ['--version']); + const pathTool = yield io.which(tool, false); + const output = pathTool ? yield getToolVersion(tool, ['--version']) : ''; return { tool, output }; })); const tools = yield Promise.all(promises); diff --git a/dist/setup/index.js b/dist/setup/index.js index c0eade6..90558a8 100644 --- a/dist/setup/index.js +++ b/dist/setup/index.js @@ -93110,7 +93110,11 @@ class BaseDistribution { const fileName = this.osPlat == 'win32' ? `node-v${version}-win-${osArch}` : `node-v${version}-${this.osPlat}-${osArch}`; - const urlFileName = this.osPlat == 'win32' ? `${fileName}.7z` : `${fileName}.tar.gz`; + const urlFileName = this.osPlat == 'win32' + ? this.nodeInfo.arch === 'arm64' + ? `${fileName}.zip` + : `${fileName}.7z` + : `${fileName}.tar.gz`; const initialUrl = this.getDistributionUrl(); const url = `${initialUrl}/v${version}/${urlFileName}`; return { @@ -93194,10 +93198,23 @@ class BaseDistribution { let extPath; info = info || {}; // satisfy compiler, never null when reaches here if (this.osPlat == 'win32') { - const _7zPath = path.join(__dirname, '../..', 'externals', '7zr.exe'); - extPath = yield tc.extract7z(downloadPath, undefined, _7zPath); + const extension = this.nodeInfo.arch === 'arm64' ? '.zip' : '.7z'; + // Rename archive to add extension because after downloading + // archive does not contain extension type and it leads to some issues + // on Windows runners without PowerShell Core. + // + // For default PowerShell Windows it should contain extension type to unpack it. + if (extension === '.zip') { + const renamedArchive = `${downloadPath}.zip`; + fs_1.default.renameSync(downloadPath, renamedArchive); + extPath = yield tc.extractZip(renamedArchive); + } + else { + const _7zPath = path.join(__dirname, '../..', 'externals', '7zr.exe'); + extPath = yield tc.extract7z(downloadPath, undefined, _7zPath); + } // 7z extracts to folder matching file name - const nestedPath = path.join(extPath, path.basename(info.fileName, '.7z')); + const nestedPath = path.join(extPath, path.basename(info.fileName, extension)); if (fs_1.default.existsSync(nestedPath)) { extPath = nestedPath; } @@ -93229,7 +93246,12 @@ class BaseDistribution { dataFileName = `osx-${osArch}-tar`; break; case 'win32': - dataFileName = `win-${osArch}-exe`; + if (this.nodeInfo.arch === 'arm64') { + dataFileName = `win-${osArch}-zip`; + } + else { + dataFileName = `win-${osArch}-exe`; + } break; default: throw new Error(`Unexpected OS '${this.osPlat}'`); @@ -93783,6 +93805,7 @@ Object.defineProperty(exports, "__esModule", ({ value: true })); exports.unique = exports.printEnvDetailsAndSetOutput = exports.getNodeVersionFromFile = void 0; const core = __importStar(__nccwpck_require__(2186)); const exec = __importStar(__nccwpck_require__(1514)); +const io = __importStar(__nccwpck_require__(7436)); const fs_1 = __importDefault(__nccwpck_require__(7147)); const path_1 = __importDefault(__nccwpck_require__(1017)); function getNodeVersionFromFile(versionFilePath) { @@ -93834,7 +93857,8 @@ function printEnvDetailsAndSetOutput() { return __awaiter(this, void 0, void 0, function* () { core.startGroup('Environment details'); const promises = ['node', 'npm', 'yarn'].map((tool) => __awaiter(this, void 0, void 0, function* () { - const output = yield getToolVersion(tool, ['--version']); + const pathTool = yield io.which(tool, false); + const output = pathTool ? yield getToolVersion(tool, ['--version']) : ''; return { tool, output }; })); const tools = yield Promise.all(promises); diff --git a/src/distributions/base-distribution.ts b/src/distributions/base-distribution.ts index edac6b9..cf5bb54 100644 --- a/src/distributions/base-distribution.ts +++ b/src/distributions/base-distribution.ts @@ -112,7 +112,11 @@ export default abstract class BaseDistribution { ? `node-v${version}-win-${osArch}` : `node-v${version}-${this.osPlat}-${osArch}`; const urlFileName: string = - this.osPlat == 'win32' ? `${fileName}.7z` : `${fileName}.tar.gz`; + this.osPlat == 'win32' + ? this.nodeInfo.arch === 'arm64' + ? `${fileName}.zip` + : `${fileName}.7z` + : `${fileName}.tar.gz`; const initialUrl = this.getDistributionUrl(); const url = `${initialUrl}/v${version}/${urlFileName}`; @@ -215,12 +219,24 @@ export default abstract class BaseDistribution { let extPath: string; info = info || ({} as INodeVersionInfo); // satisfy compiler, never null when reaches here if (this.osPlat == 'win32') { - const _7zPath = path.join(__dirname, '../..', 'externals', '7zr.exe'); - extPath = await tc.extract7z(downloadPath, undefined, _7zPath); + const extension = this.nodeInfo.arch === 'arm64' ? '.zip' : '.7z'; + // Rename archive to add extension because after downloading + // archive does not contain extension type and it leads to some issues + // on Windows runners without PowerShell Core. + // + // For default PowerShell Windows it should contain extension type to unpack it. + if (extension === '.zip') { + const renamedArchive = `${downloadPath}.zip`; + fs.renameSync(downloadPath, renamedArchive); + extPath = await tc.extractZip(renamedArchive); + } else { + const _7zPath = path.join(__dirname, '../..', 'externals', '7zr.exe'); + extPath = await tc.extract7z(downloadPath, undefined, _7zPath); + } // 7z extracts to folder matching file name const nestedPath = path.join( extPath, - path.basename(info.fileName, '.7z') + path.basename(info.fileName, extension) ); if (fs.existsSync(nestedPath)) { extPath = nestedPath; @@ -260,7 +276,11 @@ export default abstract class BaseDistribution { dataFileName = `osx-${osArch}-tar`; break; case 'win32': - dataFileName = `win-${osArch}-exe`; + if (this.nodeInfo.arch === 'arm64') { + dataFileName = `win-${osArch}-zip`; + } else { + dataFileName = `win-${osArch}-exe`; + } break; default: throw new Error(`Unexpected OS '${this.osPlat}'`); diff --git a/src/util.ts b/src/util.ts index cc6ac31..bbe25dd 100644 --- a/src/util.ts +++ b/src/util.ts @@ -1,5 +1,6 @@ import * as core from '@actions/core'; import * as exec from '@actions/exec'; +import * as io from '@actions/io'; import fs from 'fs'; import path from 'path'; @@ -61,9 +62,9 @@ export function getNodeVersionFromFile(versionFilePath: string): string | null { export async function printEnvDetailsAndSetOutput() { core.startGroup('Environment details'); - const promises = ['node', 'npm', 'yarn'].map(async tool => { - const output = await getToolVersion(tool, ['--version']); + const pathTool = await io.which(tool, false); + const output = pathTool ? await getToolVersion(tool, ['--version']) : ''; return {tool, output}; });