Compare commits

..

2 Commits

Author SHA1 Message Date
Peng Xiao
8454a14c98 fix: change from using env to os module 2023-12-26 17:11:46 +08:00
Peng Xiao
2c3ea16a59 fix: add arch to cached path 2023-12-26 16:59:32 +08:00
14 changed files with 226 additions and 293 deletions

View File

@@ -162,6 +162,9 @@ jobs:
[.nvmrc, .tool-versions, .tool-versions-node, package.json] [.nvmrc, .tool-versions, .tool-versions-node, package.json]
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- name: Remove volta from package.json
shell: bash
run: cat <<< "$(jq 'del(.volta)' ./__tests__/data/package.json)" > ./__tests__/data/package.json
- name: Setup node from node version file - name: Setup node from node version file
uses: ./ uses: ./
with: with:
@@ -180,22 +183,7 @@ jobs:
- name: Setup node from node version file - name: Setup node from node version file
uses: ./ uses: ./
with: with:
node-version-file: '__tests__/data/package-volta.json' node-version-file: '__tests__/data/package.json'
- name: Verify node
run: __tests__/verify-node.sh 16
version-file-volta-extends:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
steps:
- uses: actions/checkout@v4
- name: Setup node from node version file
uses: ./
with:
node-version-file: '__tests__/data/package-volta-extends.json'
- name: Verify node - name: Verify node
run: __tests__/verify-node.sh 16 run: __tests__/verify-node.sh 16

View File

@@ -2,16 +2,15 @@ import * as core from '@actions/core';
import * as cache from '@actions/cache'; import * as cache from '@actions/cache';
import * as path from 'path'; import * as path from 'path';
import * as glob from '@actions/glob'; import * as glob from '@actions/glob';
import osm from 'os';
import * as utils from '../src/cache-utils'; import * as utils from '../src/cache-utils';
import {restoreCache} from '../src/cache-restore'; import {restoreCache} from '../src/cache-restore';
describe('cache-restore', () => { describe('cache-restore', () => {
process.env['GITHUB_WORKSPACE'] = path.join(__dirname, 'data'); process.env['GITHUB_WORKSPACE'] = path.join(__dirname, 'data');
if (!process.env.RUNNER_OS) { const platform = 'Linux';
process.env.RUNNER_OS = 'Linux'; const arch = 'arm64';
}
const platform = process.env.RUNNER_OS;
const commonPath = '/some/random/path'; const commonPath = '/some/random/path';
const npmCachePath = `${commonPath}/npm`; const npmCachePath = `${commonPath}/npm`;
const pnpmCachePath = `${commonPath}/pnpm`; const pnpmCachePath = `${commonPath}/pnpm`;
@@ -52,6 +51,8 @@ describe('cache-restore', () => {
let getCommandOutputSpy: jest.SpyInstance; let getCommandOutputSpy: jest.SpyInstance;
let restoreCacheSpy: jest.SpyInstance; let restoreCacheSpy: jest.SpyInstance;
let hashFilesSpy: jest.SpyInstance; let hashFilesSpy: jest.SpyInstance;
let archSpy: jest.SpyInstance;
let platformSpy: jest.SpyInstance;
beforeEach(() => { beforeEach(() => {
// core // core
@@ -102,6 +103,13 @@ describe('cache-restore', () => {
// cache-utils // cache-utils
getCommandOutputSpy = jest.spyOn(utils, 'getCommandOutput'); getCommandOutputSpy = jest.spyOn(utils, 'getCommandOutput');
// os
archSpy = jest.spyOn(osm, 'arch');
archSpy.mockImplementation(() => arch);
platformSpy = jest.spyOn(osm, 'platform');
platformSpy.mockImplementation(() => platform);
}); });
describe('Validate provided package manager', () => { describe('Validate provided package manager', () => {
@@ -135,7 +143,7 @@ describe('cache-restore', () => {
await restoreCache(packageManager, ''); await restoreCache(packageManager, '');
expect(hashFilesSpy).toHaveBeenCalled(); expect(hashFilesSpy).toHaveBeenCalled();
expect(infoSpy).toHaveBeenCalledWith( expect(infoSpy).toHaveBeenCalledWith(
`Cache restored from key: node-cache-${platform}-${packageManager}-${fileHash}` `Cache restored from key: node-cache-${platform}-${arch}-${packageManager}-${fileHash}`
); );
expect(infoSpy).not.toHaveBeenCalledWith( expect(infoSpy).not.toHaveBeenCalledWith(
`${packageManager} cache is not found` `${packageManager} cache is not found`

View File

@@ -1,5 +0,0 @@
{
"volta": {
"extends": "./package-volta.json"
}
}

View File

@@ -1,8 +0,0 @@
{
"engines": {
"node": "^14.0.0"
},
"volta": {
"node": "16.0.0"
}
}

View File

@@ -1,5 +1,8 @@
{ {
"engines": { "engines": {
"node": "^14.0.0" "node": "^14.0.0"
},
"volta": {
"node": "16.0.0"
} }
} }

View File

@@ -2,7 +2,6 @@ import * as core from '@actions/core';
import * as exec from '@actions/exec'; import * as exec from '@actions/exec';
import * as tc from '@actions/tool-cache'; import * as tc from '@actions/tool-cache';
import * as cache from '@actions/cache'; import * as cache from '@actions/cache';
import * as io from '@actions/io';
import fs from 'fs'; import fs from 'fs';
import path from 'path'; import path from 'path';
@@ -25,13 +24,11 @@ describe('main tests', () => {
let startGroupSpy: jest.SpyInstance; let startGroupSpy: jest.SpyInstance;
let endGroupSpy: jest.SpyInstance; let endGroupSpy: jest.SpyInstance;
let whichSpy: jest.SpyInstance;
let existsSpy: jest.SpyInstance; let existsSpy: jest.SpyInstance;
let getExecOutputSpy: jest.SpyInstance; let getExecOutputSpy: jest.SpyInstance;
let getNodeVersionFromFileSpy: jest.SpyInstance; let parseNodeVersionSpy: jest.SpyInstance;
let cnSpy: jest.SpyInstance; let cnSpy: jest.SpyInstance;
let findSpy: jest.SpyInstance; let findSpy: jest.SpyInstance;
let isCacheActionAvailable: jest.SpyInstance; let isCacheActionAvailable: jest.SpyInstance;
@@ -44,7 +41,6 @@ describe('main tests', () => {
// node // node
os = {}; os = {};
console.log('::stop-commands::stoptoken'); console.log('::stop-commands::stoptoken');
process.env['GITHUB_WORKSPACE'] = path.join(__dirname, 'data');
process.env['GITHUB_PATH'] = ''; // Stub out ENV file functionality so we can verify it writes to standard out process.env['GITHUB_PATH'] = ''; // Stub out ENV file functionality so we can verify it writes to standard out
process.env['GITHUB_OUTPUT'] = ''; // Stub out ENV file functionality so we can verify it writes to standard out process.env['GITHUB_OUTPUT'] = ''; // Stub out ENV file functionality so we can verify it writes to standard out
infoSpy = jest.spyOn(core, 'info'); infoSpy = jest.spyOn(core, 'info');
@@ -60,18 +56,18 @@ describe('main tests', () => {
inSpy = jest.spyOn(core, 'getInput'); inSpy = jest.spyOn(core, 'getInput');
inSpy.mockImplementation(name => inputs[name]); inSpy.mockImplementation(name => inputs[name]);
whichSpy = jest.spyOn(io, 'which');
getExecOutputSpy = jest.spyOn(exec, 'getExecOutput'); getExecOutputSpy = jest.spyOn(exec, 'getExecOutput');
findSpy = jest.spyOn(tc, 'find'); findSpy = jest.spyOn(tc, 'find');
isCacheActionAvailable = jest.spyOn(cache, 'isFeatureAvailable'); isCacheActionAvailable = jest.spyOn(cache, 'isFeatureAvailable');
existsSpy = jest.spyOn(fs, 'existsSync');
cnSpy = jest.spyOn(process.stdout, 'write'); cnSpy = jest.spyOn(process.stdout, 'write');
cnSpy.mockImplementation(line => { cnSpy.mockImplementation(line => {
// uncomment to debug // uncomment to debug
process.stderr.write('write:' + line + '\n'); // process.stderr.write('write:' + line + '\n');
}); });
setupNodeJsSpy = jest.spyOn(OfficialBuilds.prototype, 'setupNodeJs'); setupNodeJsSpy = jest.spyOn(OfficialBuilds.prototype, 'setupNodeJs');
@@ -89,7 +85,7 @@ describe('main tests', () => {
jest.restoreAllMocks(); jest.restoreAllMocks();
}, 100000); }, 100000);
describe('getNodeVersionFromFile', () => { describe('parseNodeVersionFile', () => {
each` each`
contents | expected contents | expected
${'12'} | ${'12'} ${'12'} | ${'12'}
@@ -104,27 +100,10 @@ describe('main tests', () => {
${'unknown format'} | ${'unknown format'} ${'unknown format'} | ${'unknown format'}
${' 14.1.0 '} | ${'14.1.0'} ${' 14.1.0 '} | ${'14.1.0'}
${'{"volta": {"node": ">=14.0.0 <=17.0.0"}}'}| ${'>=14.0.0 <=17.0.0'} ${'{"volta": {"node": ">=14.0.0 <=17.0.0"}}'}| ${'>=14.0.0 <=17.0.0'}
${'{"volta": {"extends": "./package.json"}}'}| ${'18.0.0'}
${'{"engines": {"node": "17.0.0"}}'} | ${'17.0.0'} ${'{"engines": {"node": "17.0.0"}}'} | ${'17.0.0'}
${'{}'} | ${null} ${'{}'} | ${null}
`.it('parses "$contents"', ({contents, expected}) => { `.it('parses "$contents"', ({contents, expected}) => {
const existsSpy = jest.spyOn(fs, 'existsSync'); expect(util.parseNodeVersionFile(contents)).toBe(expected);
existsSpy.mockImplementation(() => true);
const readFileSpy = jest.spyOn(fs, 'readFileSync');
readFileSpy.mockImplementation(filePath => {
if (
typeof filePath === 'string' &&
path.basename(filePath) === 'package.json'
) {
// Special case for volta.extends
return '{"volta": {"node": "18.0.0"}}';
}
return contents;
});
expect(util.getNodeVersionFromFile('file')).toBe(expected);
}); });
}); });
@@ -147,10 +126,6 @@ describe('main tests', () => {
return {stdout: obj[command], stderr: '', exitCode: 0}; return {stdout: obj[command], stderr: '', exitCode: 0};
}); });
whichSpy.mockImplementation(cmd => {
return `some/${cmd}/path`;
});
await util.printEnvDetailsAndSetOutput(); await util.printEnvDetailsAndSetOutput();
expect(setOutputSpy).toHaveBeenCalledWith('node-version', obj['node']); expect(setOutputSpy).toHaveBeenCalledWith('node-version', obj['node']);
@@ -167,17 +142,10 @@ describe('main tests', () => {
describe('node-version-file flag', () => { describe('node-version-file flag', () => {
beforeEach(() => { beforeEach(() => {
delete inputs['node-version']; parseNodeVersionSpy = jest.spyOn(util, 'parseNodeVersionFile');
inputs['node-version-file'] = '.nvmrc';
getNodeVersionFromFileSpy = jest.spyOn(util, 'getNodeVersionFromFile');
}); });
afterEach(() => { it('not used if node-version is provided', async () => {
getNodeVersionFromFileSpy.mockRestore();
});
it('does not read node-version-file if node-version is provided', async () => {
// Arrange // Arrange
inputs['node-version'] = '12'; inputs['node-version'] = '12';
@@ -185,54 +153,107 @@ describe('main tests', () => {
await main.run(); await main.run();
// Assert // Assert
expect(inputs['node-version']).toBeDefined(); expect(parseNodeVersionSpy).toHaveBeenCalledTimes(0);
expect(inputs['node-version-file']).toBeDefined(); }, 10000);
expect(getNodeVersionFromFileSpy).not.toHaveBeenCalled();
it('not used if node-version-file not provided', async () => {
// Act
await main.run();
// Assert
expect(parseNodeVersionSpy).toHaveBeenCalledTimes(0);
});
it('reads node-version-file if provided', async () => {
// Arrange
const versionSpec = 'v14';
const versionFile = '.nvmrc';
const expectedVersionSpec = '14';
process.env['GITHUB_WORKSPACE'] = path.join(__dirname, 'data');
inputs['node-version-file'] = versionFile;
parseNodeVersionSpy.mockImplementation(() => expectedVersionSpec);
existsSpy.mockImplementationOnce(
input => input === path.join(__dirname, 'data', versionFile)
);
// Act
await main.run();
// Assert
expect(existsSpy).toHaveBeenCalledTimes(1);
expect(existsSpy).toHaveReturnedWith(true);
expect(parseNodeVersionSpy).toHaveBeenCalledWith(versionSpec);
expect(infoSpy).toHaveBeenCalledWith(
`Resolved ${versionFile} as ${expectedVersionSpec}`
);
}, 10000);
it('reads package.json as node-version-file if provided', async () => {
// Arrange
const versionSpec = fs.readFileSync(
path.join(__dirname, 'data/package.json'),
'utf-8'
);
const versionFile = 'package.json';
const expectedVersionSpec = '14';
process.env['GITHUB_WORKSPACE'] = path.join(__dirname, 'data');
inputs['node-version-file'] = versionFile;
parseNodeVersionSpy.mockImplementation(() => expectedVersionSpec);
existsSpy.mockImplementationOnce(
input => input === path.join(__dirname, 'data', versionFile)
);
// Act
await main.run();
// Assert
expect(existsSpy).toHaveBeenCalledTimes(1);
expect(existsSpy).toHaveReturnedWith(true);
expect(parseNodeVersionSpy).toHaveBeenCalledWith(versionSpec);
expect(infoSpy).toHaveBeenCalledWith(
`Resolved ${versionFile} as ${expectedVersionSpec}`
);
}, 10000);
it('both node-version-file and node-version are provided', async () => {
inputs['node-version'] = '12';
const versionSpec = 'v14';
const versionFile = '.nvmrc';
const expectedVersionSpec = '14';
process.env['GITHUB_WORKSPACE'] = path.join(__dirname, '..');
inputs['node-version-file'] = versionFile;
parseNodeVersionSpy.mockImplementation(() => expectedVersionSpec);
// Act
await main.run();
// Assert
expect(existsSpy).toHaveBeenCalledTimes(0);
expect(parseNodeVersionSpy).not.toHaveBeenCalled();
expect(warningSpy).toHaveBeenCalledWith( expect(warningSpy).toHaveBeenCalledWith(
'Both node-version and node-version-file inputs are specified, only node-version will be used' 'Both node-version and node-version-file inputs are specified, only node-version will be used'
); );
}); });
it('does not read node-version-file if node-version-file is not provided', async () => { it('should throw an error if node-version-file is not found', async () => {
// Arrange const versionFile = '.nvmrc';
delete inputs['node-version-file']; const versionFilePath = path.join(__dirname, '..', versionFile);
inputs['node-version-file'] = versionFile;
// Act inSpy.mockImplementation(name => inputs[name]);
await main.run(); existsSpy.mockImplementationOnce(
input => input === path.join(__dirname, 'data', versionFile)
// Assert
expect(getNodeVersionFromFileSpy).not.toHaveBeenCalled();
});
it('reads node-version-file', async () => {
// Arrange
const expectedVersionSpec = '14';
getNodeVersionFromFileSpy.mockImplementation(() => expectedVersionSpec);
// Act
await main.run();
// Assert
expect(getNodeVersionFromFileSpy).toHaveBeenCalled();
expect(infoSpy).toHaveBeenCalledWith(
`Resolved ${inputs['node-version-file']} as ${expectedVersionSpec}`
);
}, 10000);
it('should throw an error if node-version-file is not accessible', async () => {
// Arrange
inputs['node-version-file'] = 'non-existing-file';
const versionFilePath = path.join(
__dirname,
'data',
inputs['node-version-file']
); );
// Act // Act
await main.run(); await main.run();
// Assert // Assert
expect(getNodeVersionFromFileSpy).toHaveBeenCalled(); expect(existsSpy).toHaveBeenCalled();
expect(existsSpy).toHaveReturnedWith(false);
expect(parseNodeVersionSpy).not.toHaveBeenCalled();
expect(cnSpy).toHaveBeenCalledWith( expect(cnSpy).toHaveBeenCalledWith(
`::error::The specified node version file at: ${versionFilePath} does not exist${osm.EOL}` `::error::The specified node version file at: ${versionFilePath} does not exist${osm.EOL}`
); );

View File

@@ -248,9 +248,6 @@ describe('setup-node', () => {
const toolPath = path.normalize('/cache/node/12.16.2/x64'); const toolPath = path.normalize('/cache/node/12.16.2/x64');
exSpy.mockImplementation(async () => '/some/other/temp/path'); exSpy.mockImplementation(async () => '/some/other/temp/path');
cacheSpy.mockImplementation(async () => toolPath); cacheSpy.mockImplementation(async () => toolPath);
whichSpy.mockImplementation(cmd => {
return `some/${cmd}/path`;
});
await main.run(); await main.run();

View File

@@ -83329,67 +83329,54 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
step((generator = generator.apply(thisArg, _arguments || [])).next()); step((generator = generator.apply(thisArg, _arguments || [])).next());
}); });
}; };
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", ({ value: true })); Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.unique = exports.printEnvDetailsAndSetOutput = exports.getNodeVersionFromFile = void 0; exports.unique = exports.printEnvDetailsAndSetOutput = exports.parseNodeVersionFile = void 0;
const core = __importStar(__nccwpck_require__(2186)); const core = __importStar(__nccwpck_require__(2186));
const exec = __importStar(__nccwpck_require__(1514)); const exec = __importStar(__nccwpck_require__(1514));
const io = __importStar(__nccwpck_require__(7436)); function parseNodeVersionFile(contents) {
const fs_1 = __importDefault(__nccwpck_require__(7147)); var _a, _b, _c;
const path_1 = __importDefault(__nccwpck_require__(1017)); let nodeVersion;
function getNodeVersionFromFile(versionFilePath) {
var _a, _b, _c, _d, _e;
if (!fs_1.default.existsSync(versionFilePath)) {
throw new Error(`The specified node version file at: ${versionFilePath} does not exist`);
}
const contents = fs_1.default.readFileSync(versionFilePath, 'utf8');
// Try parsing the file as an NPM `package.json` file. // Try parsing the file as an NPM `package.json` file.
try { try {
const manifest = JSON.parse(contents); const manifest = JSON.parse(contents);
// Presume package.json file. // JSON can parse numbers, but that's handled later
if (typeof manifest === 'object' && !!manifest) { if (typeof manifest === 'object') {
// Support Volta. nodeVersion = (_a = manifest.volta) === null || _a === void 0 ? void 0 : _a.node;
// See https://docs.volta.sh/guide/understanding#managing-your-project if (!nodeVersion)
if ((_a = manifest.volta) === null || _a === void 0 ? void 0 : _a.node) { nodeVersion = (_b = manifest.engines) === null || _b === void 0 ? void 0 : _b.node;
return manifest.volta.node; // if contents are an object, we parsed JSON
}
if ((_b = manifest.engines) === null || _b === void 0 ? void 0 : _b.node) {
return manifest.engines.node;
}
// Support Volta workspaces.
// See https://docs.volta.sh/advanced/workspaces
if ((_c = manifest.volta) === null || _c === void 0 ? void 0 : _c.extends) {
const extendedFilePath = path_1.default.resolve(path_1.default.dirname(versionFilePath), manifest.volta.extends);
core.info('Resolving node version from ' + extendedFilePath);
return getNodeVersionFromFile(extendedFilePath);
}
// If contents are an object, we parsed JSON
// this can happen if node-version-file is a package.json // this can happen if node-version-file is a package.json
// yet contains no volta.node or engines.node // yet contains no volta.node or engines.node
// //
// If node-version file is _not_ JSON, control flow // if node-version file is _not_ json, control flow
// will not have reached these lines. // will not have reached these lines.
// //
// And because we've reached here, we know the contents // And because we've reached here, we know the contents
// *are* JSON, so no further string parsing makes sense. // *are* JSON, so no further string parsing makes sense.
return null; if (!nodeVersion) {
return null;
}
} }
} }
catch (_f) { catch (_d) {
core.info('Node version file is not JSON file'); core.info('Node version file is not JSON file');
} }
const found = contents.match(/^(?:node(js)?\s+)?v?(?<version>[^\s]+)$/m); if (!nodeVersion) {
return (_e = (_d = found === null || found === void 0 ? void 0 : found.groups) === null || _d === void 0 ? void 0 : _d.version) !== null && _e !== void 0 ? _e : contents.trim(); const found = contents.match(/^(?:node(js)?\s+)?v?(?<version>[^\s]+)$/m);
nodeVersion = (_c = found === null || found === void 0 ? void 0 : found.groups) === null || _c === void 0 ? void 0 : _c.version;
}
// In the case of an unknown format,
// return as is and evaluate the version separately.
if (!nodeVersion)
nodeVersion = contents.trim();
return nodeVersion;
} }
exports.getNodeVersionFromFile = getNodeVersionFromFile; exports.parseNodeVersionFile = parseNodeVersionFile;
function printEnvDetailsAndSetOutput() { function printEnvDetailsAndSetOutput() {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
core.startGroup('Environment details'); core.startGroup('Environment details');
const promises = ['node', 'npm', 'yarn'].map((tool) => __awaiter(this, void 0, void 0, function* () { const promises = ['node', 'npm', 'yarn'].map((tool) => __awaiter(this, void 0, void 0, function* () {
const pathTool = yield io.which(tool, false); const output = yield getToolVersion(tool, ['--version']);
const output = pathTool ? yield getToolVersion(tool, ['--version']) : '';
return { tool, output }; return { tool, output };
})); }));
const tools = yield Promise.all(promises); const tools = yield Promise.all(promises);

109
dist/setup/index.js vendored
View File

@@ -92559,6 +92559,7 @@ const core = __importStar(__nccwpck_require__(2186));
const glob = __importStar(__nccwpck_require__(8090)); const glob = __importStar(__nccwpck_require__(8090));
const path_1 = __importDefault(__nccwpck_require__(1017)); const path_1 = __importDefault(__nccwpck_require__(1017));
const fs_1 = __importDefault(__nccwpck_require__(7147)); const fs_1 = __importDefault(__nccwpck_require__(7147));
const os_1 = __importDefault(__nccwpck_require__(2037));
const constants_1 = __nccwpck_require__(9042); const constants_1 = __nccwpck_require__(9042);
const cache_utils_1 = __nccwpck_require__(1678); const cache_utils_1 = __nccwpck_require__(1678);
const restoreCache = (packageManager, cacheDependencyPath) => __awaiter(void 0, void 0, void 0, function* () { const restoreCache = (packageManager, cacheDependencyPath) => __awaiter(void 0, void 0, void 0, function* () {
@@ -92566,7 +92567,8 @@ const restoreCache = (packageManager, cacheDependencyPath) => __awaiter(void 0,
if (!packageManagerInfo) { if (!packageManagerInfo) {
throw new Error(`Caching for '${packageManager}' is not supported`); throw new Error(`Caching for '${packageManager}' is not supported`);
} }
const platform = process.env.RUNNER_OS; const platform = os_1.default.platform();
const arch = os_1.default.arch();
const cachePaths = yield (0, cache_utils_1.getCacheDirectories)(packageManagerInfo, cacheDependencyPath); const cachePaths = yield (0, cache_utils_1.getCacheDirectories)(packageManagerInfo, cacheDependencyPath);
core.saveState(constants_1.State.CachePaths, cachePaths); core.saveState(constants_1.State.CachePaths, cachePaths);
const lockFilePath = cacheDependencyPath const lockFilePath = cacheDependencyPath
@@ -92576,7 +92578,7 @@ const restoreCache = (packageManager, cacheDependencyPath) => __awaiter(void 0,
if (!fileHash) { if (!fileHash) {
throw new Error('Some specified paths were not resolved, unable to cache dependencies.'); throw new Error('Some specified paths were not resolved, unable to cache dependencies.');
} }
const keyPrefix = `node-cache-${platform}-${packageManager}`; const keyPrefix = `node-cache-${platform}-${arch}-${packageManager}`;
const primaryKey = `${keyPrefix}-${fileHash}`; const primaryKey = `${keyPrefix}-${fileHash}`;
core.debug(`primary key is ${primaryKey}`); core.debug(`primary key is ${primaryKey}`);
core.saveState(constants_1.State.CachePrimaryKey, primaryKey); core.saveState(constants_1.State.CachePrimaryKey, primaryKey);
@@ -93110,11 +93112,7 @@ class BaseDistribution {
const fileName = this.osPlat == 'win32' const fileName = this.osPlat == 'win32'
? `node-v${version}-win-${osArch}` ? `node-v${version}-win-${osArch}`
: `node-v${version}-${this.osPlat}-${osArch}`; : `node-v${version}-${this.osPlat}-${osArch}`;
const urlFileName = this.osPlat == 'win32' const urlFileName = this.osPlat == 'win32' ? `${fileName}.7z` : `${fileName}.tar.gz`;
? this.nodeInfo.arch === 'arm64'
? `${fileName}.zip`
: `${fileName}.7z`
: `${fileName}.tar.gz`;
const initialUrl = this.getDistributionUrl(); const initialUrl = this.getDistributionUrl();
const url = `${initialUrl}/v${version}/${urlFileName}`; const url = `${initialUrl}/v${version}/${urlFileName}`;
return { return {
@@ -93198,23 +93196,10 @@ class BaseDistribution {
let extPath; let extPath;
info = info || {}; // satisfy compiler, never null when reaches here info = info || {}; // satisfy compiler, never null when reaches here
if (this.osPlat == 'win32') { if (this.osPlat == 'win32') {
const extension = this.nodeInfo.arch === 'arm64' ? '.zip' : '.7z'; const _7zPath = path.join(__dirname, '../..', 'externals', '7zr.exe');
// Rename archive to add extension because after downloading extPath = yield tc.extract7z(downloadPath, undefined, _7zPath);
// 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 // 7z extracts to folder matching file name
const nestedPath = path.join(extPath, path.basename(info.fileName, extension)); const nestedPath = path.join(extPath, path.basename(info.fileName, '.7z'));
if (fs_1.default.existsSync(nestedPath)) { if (fs_1.default.existsSync(nestedPath)) {
extPath = nestedPath; extPath = nestedPath;
} }
@@ -93246,12 +93231,7 @@ class BaseDistribution {
dataFileName = `osx-${osArch}-tar`; dataFileName = `osx-${osArch}-tar`;
break; break;
case 'win32': case 'win32':
if (this.nodeInfo.arch === 'arm64') { dataFileName = `win-${osArch}-exe`;
dataFileName = `win-${osArch}-zip`;
}
else {
dataFileName = `win-${osArch}-exe`;
}
break; break;
default: default:
throw new Error(`Unexpected OS '${this.osPlat}'`); throw new Error(`Unexpected OS '${this.osPlat}'`);
@@ -93672,6 +93652,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
Object.defineProperty(exports, "__esModule", ({ value: true })); Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.run = void 0; exports.run = void 0;
const core = __importStar(__nccwpck_require__(2186)); const core = __importStar(__nccwpck_require__(2186));
const fs_1 = __importDefault(__nccwpck_require__(7147));
const os_1 = __importDefault(__nccwpck_require__(2037)); const os_1 = __importDefault(__nccwpck_require__(2037));
const auth = __importStar(__nccwpck_require__(7573)); const auth = __importStar(__nccwpck_require__(7573));
const path = __importStar(__nccwpck_require__(1017)); const path = __importStar(__nccwpck_require__(1017));
@@ -93746,7 +93727,10 @@ function resolveVersionInput() {
} }
if (versionFileInput) { if (versionFileInput) {
const versionFilePath = path.join(process.env.GITHUB_WORKSPACE, versionFileInput); const versionFilePath = path.join(process.env.GITHUB_WORKSPACE, versionFileInput);
const parsedVersion = (0, util_1.getNodeVersionFromFile)(versionFilePath); if (!fs_1.default.existsSync(versionFilePath)) {
throw new Error(`The specified node version file at: ${versionFilePath} does not exist`);
}
const parsedVersion = (0, util_1.parseNodeVersionFile)(fs_1.default.readFileSync(versionFilePath, 'utf8'));
if (parsedVersion) { if (parsedVersion) {
version = parsedVersion; version = parsedVersion;
} }
@@ -93798,67 +93782,54 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
step((generator = generator.apply(thisArg, _arguments || [])).next()); step((generator = generator.apply(thisArg, _arguments || [])).next());
}); });
}; };
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", ({ value: true })); Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.unique = exports.printEnvDetailsAndSetOutput = exports.getNodeVersionFromFile = void 0; exports.unique = exports.printEnvDetailsAndSetOutput = exports.parseNodeVersionFile = void 0;
const core = __importStar(__nccwpck_require__(2186)); const core = __importStar(__nccwpck_require__(2186));
const exec = __importStar(__nccwpck_require__(1514)); const exec = __importStar(__nccwpck_require__(1514));
const io = __importStar(__nccwpck_require__(7436)); function parseNodeVersionFile(contents) {
const fs_1 = __importDefault(__nccwpck_require__(7147)); var _a, _b, _c;
const path_1 = __importDefault(__nccwpck_require__(1017)); let nodeVersion;
function getNodeVersionFromFile(versionFilePath) {
var _a, _b, _c, _d, _e;
if (!fs_1.default.existsSync(versionFilePath)) {
throw new Error(`The specified node version file at: ${versionFilePath} does not exist`);
}
const contents = fs_1.default.readFileSync(versionFilePath, 'utf8');
// Try parsing the file as an NPM `package.json` file. // Try parsing the file as an NPM `package.json` file.
try { try {
const manifest = JSON.parse(contents); const manifest = JSON.parse(contents);
// Presume package.json file. // JSON can parse numbers, but that's handled later
if (typeof manifest === 'object' && !!manifest) { if (typeof manifest === 'object') {
// Support Volta. nodeVersion = (_a = manifest.volta) === null || _a === void 0 ? void 0 : _a.node;
// See https://docs.volta.sh/guide/understanding#managing-your-project if (!nodeVersion)
if ((_a = manifest.volta) === null || _a === void 0 ? void 0 : _a.node) { nodeVersion = (_b = manifest.engines) === null || _b === void 0 ? void 0 : _b.node;
return manifest.volta.node; // if contents are an object, we parsed JSON
}
if ((_b = manifest.engines) === null || _b === void 0 ? void 0 : _b.node) {
return manifest.engines.node;
}
// Support Volta workspaces.
// See https://docs.volta.sh/advanced/workspaces
if ((_c = manifest.volta) === null || _c === void 0 ? void 0 : _c.extends) {
const extendedFilePath = path_1.default.resolve(path_1.default.dirname(versionFilePath), manifest.volta.extends);
core.info('Resolving node version from ' + extendedFilePath);
return getNodeVersionFromFile(extendedFilePath);
}
// If contents are an object, we parsed JSON
// this can happen if node-version-file is a package.json // this can happen if node-version-file is a package.json
// yet contains no volta.node or engines.node // yet contains no volta.node or engines.node
// //
// If node-version file is _not_ JSON, control flow // if node-version file is _not_ json, control flow
// will not have reached these lines. // will not have reached these lines.
// //
// And because we've reached here, we know the contents // And because we've reached here, we know the contents
// *are* JSON, so no further string parsing makes sense. // *are* JSON, so no further string parsing makes sense.
return null; if (!nodeVersion) {
return null;
}
} }
} }
catch (_f) { catch (_d) {
core.info('Node version file is not JSON file'); core.info('Node version file is not JSON file');
} }
const found = contents.match(/^(?:node(js)?\s+)?v?(?<version>[^\s]+)$/m); if (!nodeVersion) {
return (_e = (_d = found === null || found === void 0 ? void 0 : found.groups) === null || _d === void 0 ? void 0 : _d.version) !== null && _e !== void 0 ? _e : contents.trim(); const found = contents.match(/^(?:node(js)?\s+)?v?(?<version>[^\s]+)$/m);
nodeVersion = (_c = found === null || found === void 0 ? void 0 : found.groups) === null || _c === void 0 ? void 0 : _c.version;
}
// In the case of an unknown format,
// return as is and evaluate the version separately.
if (!nodeVersion)
nodeVersion = contents.trim();
return nodeVersion;
} }
exports.getNodeVersionFromFile = getNodeVersionFromFile; exports.parseNodeVersionFile = parseNodeVersionFile;
function printEnvDetailsAndSetOutput() { function printEnvDetailsAndSetOutput() {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
core.startGroup('Environment details'); core.startGroup('Environment details');
const promises = ['node', 'npm', 'yarn'].map((tool) => __awaiter(this, void 0, void 0, function* () { const promises = ['node', 'npm', 'yarn'].map((tool) => __awaiter(this, void 0, void 0, function* () {
const pathTool = yield io.which(tool, false); const output = yield getToolVersion(tool, ['--version']);
const output = pathTool ? yield getToolVersion(tool, ['--version']) : '';
return { tool, output }; return { tool, output };
})); }));
const tools = yield Promise.all(promises); const tools = yield Promise.all(promises);

View File

@@ -84,8 +84,6 @@ When using the `package.json` input, the action will look for `volta.node` first
} }
``` ```
Otherwise, when [`volta.extends`](https://docs.volta.sh/advanced/workspaces) is defined, then it will resolve the corresponding file and look for `volta.node` or `engines.node` recursively.
## Architecture ## Architecture
You can use any of the [supported operating systems](https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners), and the compatible `architecture` can be selected using `architecture`. Values are `x86`, `x64`, `arm64`, `armv6l`, `armv7l`, `ppc64le`, `s390x` (not all of the architectures are available on all platforms). You can use any of the [supported operating systems](https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners), and the compatible `architecture` can be selected using `architecture`. Values are `x86`, `x64`, `arm64`, `armv6l`, `armv7l`, `ppc64le`, `s390x` (not all of the architectures are available on all platforms).

View File

@@ -3,6 +3,7 @@ import * as core from '@actions/core';
import * as glob from '@actions/glob'; import * as glob from '@actions/glob';
import path from 'path'; import path from 'path';
import fs from 'fs'; import fs from 'fs';
import os from 'os';
import {State} from './constants'; import {State} from './constants';
import { import {
@@ -20,7 +21,8 @@ export const restoreCache = async (
if (!packageManagerInfo) { if (!packageManagerInfo) {
throw new Error(`Caching for '${packageManager}' is not supported`); throw new Error(`Caching for '${packageManager}' is not supported`);
} }
const platform = process.env.RUNNER_OS; const platform = os.platform();
const arch = os.arch();
const cachePaths = await getCacheDirectories( const cachePaths = await getCacheDirectories(
packageManagerInfo, packageManagerInfo,
@@ -38,7 +40,7 @@ export const restoreCache = async (
); );
} }
const keyPrefix = `node-cache-${platform}-${packageManager}`; const keyPrefix = `node-cache-${platform}-${arch}-${packageManager}`;
const primaryKey = `${keyPrefix}-${fileHash}`; const primaryKey = `${keyPrefix}-${fileHash}`;
core.debug(`primary key is ${primaryKey}`); core.debug(`primary key is ${primaryKey}`);

View File

@@ -112,11 +112,7 @@ export default abstract class BaseDistribution {
? `node-v${version}-win-${osArch}` ? `node-v${version}-win-${osArch}`
: `node-v${version}-${this.osPlat}-${osArch}`; : `node-v${version}-${this.osPlat}-${osArch}`;
const urlFileName: string = const urlFileName: string =
this.osPlat == 'win32' this.osPlat == 'win32' ? `${fileName}.7z` : `${fileName}.tar.gz`;
? this.nodeInfo.arch === 'arm64'
? `${fileName}.zip`
: `${fileName}.7z`
: `${fileName}.tar.gz`;
const initialUrl = this.getDistributionUrl(); const initialUrl = this.getDistributionUrl();
const url = `${initialUrl}/v${version}/${urlFileName}`; const url = `${initialUrl}/v${version}/${urlFileName}`;
@@ -219,24 +215,12 @@ export default abstract class BaseDistribution {
let extPath: string; let extPath: string;
info = info || ({} as INodeVersionInfo); // satisfy compiler, never null when reaches here info = info || ({} as INodeVersionInfo); // satisfy compiler, never null when reaches here
if (this.osPlat == 'win32') { if (this.osPlat == 'win32') {
const extension = this.nodeInfo.arch === 'arm64' ? '.zip' : '.7z'; const _7zPath = path.join(__dirname, '../..', 'externals', '7zr.exe');
// Rename archive to add extension because after downloading extPath = await tc.extract7z(downloadPath, undefined, _7zPath);
// 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 // 7z extracts to folder matching file name
const nestedPath = path.join( const nestedPath = path.join(
extPath, extPath,
path.basename(info.fileName, extension) path.basename(info.fileName, '.7z')
); );
if (fs.existsSync(nestedPath)) { if (fs.existsSync(nestedPath)) {
extPath = nestedPath; extPath = nestedPath;
@@ -276,11 +260,7 @@ export default abstract class BaseDistribution {
dataFileName = `osx-${osArch}-tar`; dataFileName = `osx-${osArch}-tar`;
break; break;
case 'win32': case 'win32':
if (this.nodeInfo.arch === 'arm64') { dataFileName = `win-${osArch}-exe`;
dataFileName = `win-${osArch}-zip`;
} else {
dataFileName = `win-${osArch}-exe`;
}
break; break;
default: default:
throw new Error(`Unexpected OS '${this.osPlat}'`); throw new Error(`Unexpected OS '${this.osPlat}'`);

View File

@@ -1,5 +1,6 @@
import * as core from '@actions/core'; import * as core from '@actions/core';
import fs from 'fs';
import os from 'os'; import os from 'os';
import * as auth from './authutil'; import * as auth from './authutil';
@@ -7,7 +8,7 @@ import * as path from 'path';
import {restoreCache} from './cache-restore'; import {restoreCache} from './cache-restore';
import {isCacheFeatureAvailable} from './cache-utils'; import {isCacheFeatureAvailable} from './cache-utils';
import {getNodejsDistribution} from './distributions/installer-factory'; import {getNodejsDistribution} from './distributions/installer-factory';
import {getNodeVersionFromFile, printEnvDetailsAndSetOutput} from './util'; import {parseNodeVersionFile, printEnvDetailsAndSetOutput} from './util';
import {State} from './constants'; import {State} from './constants';
export async function run() { export async function run() {
@@ -98,7 +99,15 @@ function resolveVersionInput(): string {
versionFileInput versionFileInput
); );
const parsedVersion = getNodeVersionFromFile(versionFilePath); if (!fs.existsSync(versionFilePath)) {
throw new Error(
`The specified node version file at: ${versionFilePath} does not exist`
);
}
const parsedVersion = parseNodeVersionFile(
fs.readFileSync(versionFilePath, 'utf8')
);
if (parsedVersion) { if (parsedVersion) {
version = parsedVersion; version = parsedVersion;

View File

@@ -1,70 +1,52 @@
import * as core from '@actions/core'; import * as core from '@actions/core';
import * as exec from '@actions/exec'; import * as exec from '@actions/exec';
import * as io from '@actions/io';
import fs from 'fs'; export function parseNodeVersionFile(contents: string): string | null {
import path from 'path'; let nodeVersion: string | undefined;
export function getNodeVersionFromFile(versionFilePath: string): string | null {
if (!fs.existsSync(versionFilePath)) {
throw new Error(
`The specified node version file at: ${versionFilePath} does not exist`
);
}
const contents = fs.readFileSync(versionFilePath, 'utf8');
// Try parsing the file as an NPM `package.json` file. // Try parsing the file as an NPM `package.json` file.
try { try {
const manifest = JSON.parse(contents); const manifest = JSON.parse(contents);
// Presume package.json file. // JSON can parse numbers, but that's handled later
if (typeof manifest === 'object' && !!manifest) { if (typeof manifest === 'object') {
// Support Volta. nodeVersion = manifest.volta?.node;
// See https://docs.volta.sh/guide/understanding#managing-your-project if (!nodeVersion) nodeVersion = manifest.engines?.node;
if (manifest.volta?.node) {
return manifest.volta.node;
}
if (manifest.engines?.node) { // if contents are an object, we parsed JSON
return manifest.engines.node;
}
// Support Volta workspaces.
// See https://docs.volta.sh/advanced/workspaces
if (manifest.volta?.extends) {
const extendedFilePath = path.resolve(
path.dirname(versionFilePath),
manifest.volta.extends
);
core.info('Resolving node version from ' + extendedFilePath);
return getNodeVersionFromFile(extendedFilePath);
}
// If contents are an object, we parsed JSON
// this can happen if node-version-file is a package.json // this can happen if node-version-file is a package.json
// yet contains no volta.node or engines.node // yet contains no volta.node or engines.node
// //
// If node-version file is _not_ JSON, control flow // if node-version file is _not_ json, control flow
// will not have reached these lines. // will not have reached these lines.
// //
// And because we've reached here, we know the contents // And because we've reached here, we know the contents
// *are* JSON, so no further string parsing makes sense. // *are* JSON, so no further string parsing makes sense.
return null; if (!nodeVersion) {
return null;
}
} }
} catch { } catch {
core.info('Node version file is not JSON file'); core.info('Node version file is not JSON file');
} }
const found = contents.match(/^(?:node(js)?\s+)?v?(?<version>[^\s]+)$/m); if (!nodeVersion) {
return found?.groups?.version ?? contents.trim(); const found = contents.match(/^(?:node(js)?\s+)?v?(?<version>[^\s]+)$/m);
nodeVersion = found?.groups?.version;
}
// In the case of an unknown format,
// return as is and evaluate the version separately.
if (!nodeVersion) nodeVersion = contents.trim();
return nodeVersion as string;
} }
export async function printEnvDetailsAndSetOutput() { export async function printEnvDetailsAndSetOutput() {
core.startGroup('Environment details'); core.startGroup('Environment details');
const promises = ['node', 'npm', 'yarn'].map(async tool => { const promises = ['node', 'npm', 'yarn'].map(async tool => {
const pathTool = await io.which(tool, false); const output = await getToolVersion(tool, ['--version']);
const output = pathTool ? await getToolVersion(tool, ['--version']) : '';
return {tool, output}; return {tool, output};
}); });