fix: merge remote-tracking branch 'upstream/main'

This commit is contained in:
Matthew Matl 2025-07-23 15:25:26 -07:00
commit 66a45156ed
No known key found for this signature in database
68 changed files with 16254 additions and 8048 deletions

View file

@ -0,0 +1,187 @@
import * as core from '@actions/core';
import * as cache from '@actions/cache';
import * as path from 'path';
import * as glob from '@actions/glob';
import osm from 'os';
import * as utils from '../src/cache-utils';
import {restoreCache} from '../src/cache-restore';
describe('cache-restore', () => {
process.env['GITHUB_WORKSPACE'] = path.join(__dirname, 'data');
if (!process.env.RUNNER_OS) {
process.env.RUNNER_OS = 'Linux';
}
const platform = process.env.RUNNER_OS;
const arch = 'arm64';
const commonPath = '/some/random/path';
const npmCachePath = `${commonPath}/npm`;
const pnpmCachePath = `${commonPath}/pnpm`;
const yarn1CachePath = `${commonPath}/yarn1`;
const yarn2CachePath = `${commonPath}/yarn2`;
const yarnFileHash =
'b8a0bae5243251f7c07dd52d1f78ff78281dfefaded700a176261b6b54fa245b';
const npmFileHash =
'abf7c9b306a3149dcfba4673e2362755503bcceaab46f0e4e6fee0ade493e20c';
const pnpmFileHash =
'26309058093e84713f38869c50cf1cee9b08155ede874ec1b44ce3fca8c68c70';
const cachesObject = {
[npmCachePath]: npmFileHash,
[pnpmCachePath]: pnpmFileHash,
[yarn1CachePath]: yarnFileHash,
[yarn2CachePath]: yarnFileHash
};
function findCacheFolder(command: string) {
switch (command) {
case 'npm config get cache':
return npmCachePath;
case 'pnpm store path --silent':
return pnpmCachePath;
case 'yarn cache dir':
return yarn1CachePath;
case 'yarn config get cacheFolder':
return yarn2CachePath;
default:
return 'packge/not/found';
}
}
let saveStateSpy: jest.SpyInstance;
let infoSpy: jest.SpyInstance;
let debugSpy: jest.SpyInstance;
let setOutputSpy: jest.SpyInstance;
let getCommandOutputSpy: jest.SpyInstance;
let restoreCacheSpy: jest.SpyInstance;
let hashFilesSpy: jest.SpyInstance;
let archSpy: jest.SpyInstance;
beforeEach(() => {
// core
infoSpy = jest.spyOn(core, 'info');
infoSpy.mockImplementation(() => undefined);
debugSpy = jest.spyOn(core, 'debug');
debugSpy.mockImplementation(() => undefined);
setOutputSpy = jest.spyOn(core, 'setOutput');
setOutputSpy.mockImplementation(() => undefined);
saveStateSpy = jest.spyOn(core, 'saveState');
saveStateSpy.mockImplementation(() => undefined);
// glob
hashFilesSpy = jest.spyOn(glob, 'hashFiles');
hashFilesSpy.mockImplementation((pattern: string) => {
if (pattern.includes('package-lock.json')) {
return npmFileHash;
} else if (pattern.includes('pnpm-lock.yaml')) {
return pnpmFileHash;
} else if (pattern.includes('yarn.lock')) {
return yarnFileHash;
} else {
return '';
}
});
// cache
restoreCacheSpy = jest.spyOn(cache, 'restoreCache');
restoreCacheSpy.mockImplementation(
(cachePaths: Array<string>, key: string) => {
if (!cachePaths || cachePaths.length === 0) {
return undefined;
}
const cachPath = cachePaths[0];
const fileHash = cachesObject[cachPath];
if (key.includes(fileHash)) {
return key;
}
return undefined;
}
);
// cache-utils
getCommandOutputSpy = jest.spyOn(utils, 'getCommandOutput');
// os
archSpy = jest.spyOn(osm, 'arch');
archSpy.mockImplementation(() => arch);
});
describe('Validate provided package manager', () => {
it.each([['npm7'], ['npm6'], ['pnpm6'], ['yarn1'], ['yarn2'], ['random']])(
'Throw an error because %s is not supported',
async packageManager => {
await expect(restoreCache(packageManager, '')).rejects.toThrow(
`Caching for '${packageManager}' is not supported`
);
}
);
});
describe('Restore dependencies', () => {
it.each([
['yarn', '2.1.2', yarnFileHash],
['yarn', '1.2.3', yarnFileHash],
['npm', '', npmFileHash],
['pnpm', '', pnpmFileHash]
])(
'restored dependencies for %s',
async (packageManager, toolVersion, fileHash) => {
getCommandOutputSpy.mockImplementation((command: string) => {
if (command.includes('version')) {
return toolVersion;
} else {
return findCacheFolder(command);
}
});
await restoreCache(packageManager, '');
expect(hashFilesSpy).toHaveBeenCalled();
expect(infoSpy).toHaveBeenCalledWith(
`Cache restored from key: node-cache-${platform}-${arch}-${packageManager}-${fileHash}`
);
expect(infoSpy).not.toHaveBeenCalledWith(
`${packageManager} cache is not found`
);
expect(setOutputSpy).toHaveBeenCalledWith('cache-hit', true);
}
);
});
describe('Dependencies changed', () => {
it.each([
['yarn', '2.1.2', yarnFileHash],
['yarn', '1.2.3', yarnFileHash],
['npm', '', npmFileHash],
['pnpm', '', pnpmFileHash]
])(
'dependencies are changed %s',
async (packageManager, toolVersion, fileHash) => {
getCommandOutputSpy.mockImplementation((command: string) => {
if (command.includes('version')) {
return toolVersion;
} else {
return findCacheFolder(command);
}
});
restoreCacheSpy.mockImplementationOnce(() => undefined);
await restoreCache(packageManager, '');
expect(hashFilesSpy).toHaveBeenCalled();
expect(infoSpy).toHaveBeenCalledWith(
`${packageManager} cache is not found`
);
expect(setOutputSpy).toHaveBeenCalledWith('cache-hit', false);
}
);
});
afterEach(() => {
jest.resetAllMocks();
jest.clearAllMocks();
});
});

View file

@ -0,0 +1,401 @@
import * as core from '@actions/core';
import * as cache from '@actions/cache';
import path from 'path';
import * as utils from '../src/cache-utils';
import {
PackageManagerInfo,
isCacheFeatureAvailable,
supportedPackageManagers,
isGhes,
resetProjectDirectoriesMemoized
} from '../src/cache-utils';
import fs from 'fs';
import * as cacheUtils from '../src/cache-utils';
import * as glob from '@actions/glob';
import {Globber} from '@actions/glob';
import {MockGlobber} from './mock/glob-mock';
describe('cache-utils', () => {
const versionYarn1 = '1.2.3';
let debugSpy: jest.SpyInstance;
let getCommandOutputSpy: jest.SpyInstance;
let isFeatureAvailable: jest.SpyInstance;
let info: jest.SpyInstance;
let warningSpy: jest.SpyInstance;
let fsRealPathSyncSpy: jest.SpyInstance;
beforeEach(() => {
console.log('::stop-commands::stoptoken');
process.env['GITHUB_WORKSPACE'] = path.join(__dirname, 'data');
debugSpy = jest.spyOn(core, 'debug');
debugSpy.mockImplementation(msg => {});
info = jest.spyOn(core, 'info');
warningSpy = jest.spyOn(core, 'warning');
isFeatureAvailable = jest.spyOn(cache, 'isFeatureAvailable');
getCommandOutputSpy = jest.spyOn(utils, 'getCommandOutput');
fsRealPathSyncSpy = jest.spyOn(fs, 'realpathSync');
fsRealPathSyncSpy.mockImplementation(dirName => {
return dirName;
});
});
afterEach(() => {
jest.resetAllMocks();
jest.clearAllMocks();
//jest.restoreAllMocks();
});
afterAll(async () => {
console.log('::stoptoken::');
jest.restoreAllMocks();
}, 100000);
describe('getPackageManagerInfo', () => {
it.each<[string, PackageManagerInfo | null]>([
['npm', utils.supportedPackageManagers.npm],
['pnpm', utils.supportedPackageManagers.pnpm],
['yarn', utils.supportedPackageManagers.yarn],
['yarn1', null],
['yarn2', null],
['npm7', null]
])('getPackageManagerInfo for %s is %o', async (packageManager, result) => {
getCommandOutputSpy.mockImplementationOnce(() => versionYarn1);
await expect(utils.getPackageManagerInfo(packageManager)).resolves.toBe(
result
);
});
});
it('isCacheFeatureAvailable for GHES is false', () => {
isFeatureAvailable.mockImplementation(() => false);
process.env['GITHUB_SERVER_URL'] = 'https://www.test.com';
expect(isCacheFeatureAvailable()).toBeFalsy();
expect(warningSpy).toHaveBeenCalledWith(
'Cache action is only supported on GHES version >= 3.5. If you are on version >=3.5 Please check with GHES admin if Actions cache service is enabled or not.'
);
});
it('isCacheFeatureAvailable for GHES has an interhal error', () => {
isFeatureAvailable.mockImplementation(() => false);
process.env['GITHUB_SERVER_URL'] = '';
isCacheFeatureAvailable();
expect(warningSpy).toHaveBeenCalledWith(
'The runner was not able to contact the cache service. Caching will be skipped'
);
});
it('isCacheFeatureAvailable for GHES is available', () => {
isFeatureAvailable.mockImplementation(() => true);
expect(isCacheFeatureAvailable()).toStrictEqual(true);
});
afterEach(() => {
process.env['GITHUB_SERVER_URL'] = '';
jest.resetAllMocks();
jest.clearAllMocks();
});
describe('getCacheDirectoriesPaths', () => {
let existsSpy: jest.SpyInstance;
let lstatSpy: jest.SpyInstance;
let globCreateSpy: jest.SpyInstance;
beforeEach(() => {
existsSpy = jest.spyOn(fs, 'existsSync');
existsSpy.mockImplementation(() => true);
lstatSpy = jest.spyOn(fs, 'lstatSync');
lstatSpy.mockImplementation(arg => ({
isDirectory: () => true
}));
globCreateSpy = jest.spyOn(glob, 'create');
globCreateSpy.mockImplementation(
(pattern: string): Promise<Globber> =>
MockGlobber.create(['/foo', '/bar'])
);
resetProjectDirectoriesMemoized();
});
afterEach(() => {
existsSpy.mockRestore();
lstatSpy.mockRestore();
globCreateSpy.mockRestore();
});
it.each([
[supportedPackageManagers.npm, ''],
[supportedPackageManagers.npm, '/dir/file.lock'],
[supportedPackageManagers.npm, '/**/file.lock'],
[supportedPackageManagers.pnpm, ''],
[supportedPackageManagers.pnpm, '/dir/file.lock'],
[supportedPackageManagers.pnpm, '/**/file.lock']
])(
'getCacheDirectoriesPaths should return one dir for non yarn',
async (packageManagerInfo, cacheDependency) => {
getCommandOutputSpy.mockImplementation(() => 'foo');
const dirs = await cacheUtils.getCacheDirectories(
packageManagerInfo,
cacheDependency
);
expect(dirs).toEqual(['foo']);
// to do not call for a version
// call once for get cache folder
expect(getCommandOutputSpy).toHaveBeenCalledTimes(1);
}
);
it('getCacheDirectoriesPaths should return one dir for yarn without cacheDependency', async () => {
getCommandOutputSpy.mockImplementation(() => 'foo');
const dirs = await cacheUtils.getCacheDirectories(
supportedPackageManagers.yarn,
''
);
expect(dirs).toEqual(['foo']);
});
it.each([
[supportedPackageManagers.npm, ''],
[supportedPackageManagers.npm, '/dir/file.lock'],
[supportedPackageManagers.npm, '/**/file.lock'],
[supportedPackageManagers.pnpm, ''],
[supportedPackageManagers.pnpm, '/dir/file.lock'],
[supportedPackageManagers.pnpm, '/**/file.lock'],
[supportedPackageManagers.yarn, ''],
[supportedPackageManagers.yarn, '/dir/file.lock'],
[supportedPackageManagers.yarn, '/**/file.lock']
])(
'getCacheDirectoriesPaths should throw for getCommandOutput returning empty',
async (packageManagerInfo, cacheDependency) => {
getCommandOutputSpy.mockImplementation((command: string) =>
// return empty string to indicate getCacheFolderPath failed
// --version still works
command.includes('version') ? '1.' : ''
);
await expect(
cacheUtils.getCacheDirectories(packageManagerInfo, cacheDependency)
).rejects.toThrow(); //'Could not get cache folder path for /dir');
}
);
it.each([
[supportedPackageManagers.yarn, '/dir/file.lock'],
[supportedPackageManagers.yarn, '/**/file.lock']
])(
'getCacheDirectoriesPaths should nothrow in case of having not directories',
async (packageManagerInfo, cacheDependency) => {
lstatSpy.mockImplementation(arg => ({
isDirectory: () => false
}));
await cacheUtils.getCacheDirectories(
packageManagerInfo,
cacheDependency
);
expect(warningSpy).toHaveBeenCalledTimes(1);
expect(warningSpy).toHaveBeenCalledWith(
`No existing directories found containing cache-dependency-path="${cacheDependency}"`
);
}
);
it.each(['1.1.1', '2.2.2'])(
'getCacheDirectoriesPaths yarn v%s should return one dir without cacheDependency',
async version => {
getCommandOutputSpy.mockImplementationOnce(() => version);
getCommandOutputSpy.mockImplementationOnce(() => `foo${version}`);
const dirs = await cacheUtils.getCacheDirectories(
supportedPackageManagers.yarn,
''
);
expect(dirs).toEqual([`foo${version}`]);
}
);
it.each(['1.1.1', '2.2.2'])(
'getCacheDirectoriesPaths yarn v%s should return 2 dirs with globbed cacheDependency',
async version => {
let dirNo = 1;
getCommandOutputSpy.mockImplementation((command: string) =>
command.includes('version') ? version : `file_${version}_${dirNo++}`
);
globCreateSpy.mockImplementation(
(pattern: string): Promise<Globber> =>
MockGlobber.create(['/tmp/dir1/file', '/tmp/dir2/file'])
);
const dirs = await cacheUtils.getCacheDirectories(
supportedPackageManagers.yarn,
'/tmp/**/file'
);
expect(dirs).toEqual([`file_${version}_1`, `file_${version}_2`]);
}
);
it.each(['1.1.1', '2.2.2'])(
'getCacheDirectoriesPaths yarn v%s should return 2 dirs with globbed cacheDependency expanding to duplicates',
async version => {
let dirNo = 1;
getCommandOutputSpy.mockImplementation((command: string) =>
command.includes('version') ? version : `file_${version}_${dirNo++}`
);
globCreateSpy.mockImplementation(
(pattern: string): Promise<Globber> =>
MockGlobber.create([
'/tmp/dir1/file',
'/tmp/dir2/file',
'/tmp/dir1/file'
])
);
const dirs = await cacheUtils.getCacheDirectories(
supportedPackageManagers.yarn,
'/tmp/**/file'
);
expect(dirs).toEqual([`file_${version}_1`, `file_${version}_2`]);
}
);
it.each(['1.1.1', '2.2.2'])(
'getCacheDirectoriesPaths yarn v%s should return 2 uniq dirs despite duplicate cache directories',
async version => {
let dirNo = 1;
getCommandOutputSpy.mockImplementation((command: string) =>
command.includes('version')
? version
: `file_${version}_${dirNo++ % 2}`
);
globCreateSpy.mockImplementation(
(pattern: string): Promise<Globber> =>
MockGlobber.create([
'/tmp/dir1/file',
'/tmp/dir2/file',
'/tmp/dir3/file'
])
);
const dirs = await cacheUtils.getCacheDirectories(
supportedPackageManagers.yarn,
'/tmp/**/file'
);
expect(dirs).toEqual([`file_${version}_1`, `file_${version}_0`]);
expect(getCommandOutputSpy).toHaveBeenCalledTimes(6);
expect(getCommandOutputSpy).toHaveBeenCalledWith(
'yarn --version',
'/tmp/dir1'
);
expect(getCommandOutputSpy).toHaveBeenCalledWith(
'yarn --version',
'/tmp/dir2'
);
expect(getCommandOutputSpy).toHaveBeenCalledWith(
'yarn --version',
'/tmp/dir3'
);
expect(getCommandOutputSpy).toHaveBeenCalledWith(
version.startsWith('1.')
? 'yarn cache dir'
: 'yarn config get cacheFolder',
'/tmp/dir1'
);
expect(getCommandOutputSpy).toHaveBeenCalledWith(
version.startsWith('1.')
? 'yarn cache dir'
: 'yarn config get cacheFolder',
'/tmp/dir2'
);
expect(getCommandOutputSpy).toHaveBeenCalledWith(
version.startsWith('1.')
? 'yarn cache dir'
: 'yarn config get cacheFolder',
'/tmp/dir3'
);
}
);
it.each(['1.1.1', '2.2.2'])(
'getCacheDirectoriesPaths yarn v%s should return 4 dirs with multiple globs',
async version => {
// simulate wrong indents
const cacheDependencyPath = `/tmp/dir1/file
/tmp/dir2/file
/tmp/**/file
`;
globCreateSpy.mockImplementation(
(pattern: string): Promise<Globber> =>
MockGlobber.create([
'/tmp/dir1/file',
'/tmp/dir2/file',
'/tmp/dir3/file',
'/tmp/dir4/file'
])
);
let dirNo = 1;
getCommandOutputSpy.mockImplementation((command: string) =>
command.includes('version') ? version : `file_${version}_${dirNo++}`
);
const dirs = await cacheUtils.getCacheDirectories(
supportedPackageManagers.yarn,
cacheDependencyPath
);
expect(dirs).toEqual([
`file_${version}_1`,
`file_${version}_2`,
`file_${version}_3`,
`file_${version}_4`
]);
}
);
});
});
describe('isGhes', () => {
const pristineEnv = process.env;
beforeEach(() => {
jest.resetModules();
process.env = {...pristineEnv};
});
afterAll(() => {
process.env = pristineEnv;
});
it('returns false when the GITHUB_SERVER_URL environment variable is not defined', () => {
delete process.env['GITHUB_SERVER_URL'];
expect(isGhes()).toBeFalsy();
});
it('returns false when the GITHUB_SERVER_URL environment variable is set to github.com', () => {
process.env['GITHUB_SERVER_URL'] = 'https://github.com';
expect(isGhes()).toBeFalsy();
});
it('returns false when the GITHUB_SERVER_URL environment variable is set to a GitHub Enterprise Cloud-style URL', () => {
process.env['GITHUB_SERVER_URL'] = 'https://contoso.ghe.com';
expect(isGhes()).toBeFalsy();
});
it('returns false when the GITHUB_SERVER_URL environment variable has a .localhost suffix', () => {
process.env['GITHUB_SERVER_URL'] = 'https://mock-github.localhost';
expect(isGhes()).toBeFalsy();
});
it('returns true when the GITHUB_SERVER_URL environment variable is set to some other URL', () => {
process.env['GITHUB_SERVER_URL'] = 'https://src.onpremise.fabrikam.com';
expect(isGhes()).toBeTruthy();
});
});

View file

@ -0,0 +1,595 @@
import * as core from '@actions/core';
import * as io from '@actions/io';
import * as tc from '@actions/tool-cache';
import * as httpm from '@actions/http-client';
import * as exec from '@actions/exec';
import * as cache from '@actions/cache';
import fs from 'fs';
import cp from 'child_process';
import osm from 'os';
import path from 'path';
import * as main from '../src/main';
import * as auth from '../src/authutil';
import {INodeVersion} from '../src/distributions/base-models';
import nodeTestManifest from './data/versions-manifest.json';
import nodeTestDist from './data/node-dist-index.json';
import nodeTestDistNightly from './data/node-nightly-index.json';
import nodeTestDistRc from './data/node-rc-index.json';
import nodeV8CanaryTestDist from './data/v8-canary-dist-index.json';
describe('setup-node', () => {
let inputs = {} as any;
let os = {} as any;
let inSpy: jest.SpyInstance;
let findSpy: jest.SpyInstance;
let findAllVersionsSpy: jest.SpyInstance;
let cnSpy: jest.SpyInstance;
let logSpy: jest.SpyInstance;
let warningSpy: jest.SpyInstance;
let getManifestSpy: jest.SpyInstance;
let getDistSpy: jest.SpyInstance;
let platSpy: jest.SpyInstance;
let archSpy: jest.SpyInstance;
let dlSpy: jest.SpyInstance;
let exSpy: jest.SpyInstance;
let cacheSpy: jest.SpyInstance;
let dbgSpy: jest.SpyInstance;
let whichSpy: jest.SpyInstance;
let existsSpy: jest.SpyInstance;
let readFileSyncSpy: jest.SpyInstance;
let mkdirpSpy: jest.SpyInstance;
let execSpy: jest.SpyInstance;
let authSpy: jest.SpyInstance;
let parseNodeVersionSpy: jest.SpyInstance;
let isCacheActionAvailable: jest.SpyInstance;
let getExecOutputSpy: jest.SpyInstance;
let getJsonSpy: jest.SpyInstance;
beforeEach(() => {
// @actions/core
console.log('::stop-commands::stoptoken'); // Disable executing of runner commands when running tests in actions
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
inputs = {};
inSpy = jest.spyOn(core, 'getInput');
inSpy.mockImplementation(name => inputs[name]);
// node
os = {};
platSpy = jest.spyOn(osm, 'platform');
platSpy.mockImplementation(() => os['platform']);
archSpy = jest.spyOn(osm, 'arch');
archSpy.mockImplementation(() => os['arch']);
execSpy = jest.spyOn(cp, 'execSync');
// @actions/tool-cache
findSpy = jest.spyOn(tc, 'find');
findAllVersionsSpy = jest.spyOn(tc, 'findAllVersions');
dlSpy = jest.spyOn(tc, 'downloadTool');
exSpy = jest.spyOn(tc, 'extractTar');
cacheSpy = jest.spyOn(tc, 'cacheDir');
getManifestSpy = jest.spyOn(tc, 'getManifestFromRepo');
// http-client
getJsonSpy = jest.spyOn(httpm.HttpClient.prototype, 'getJson');
// io
whichSpy = jest.spyOn(io, 'which');
existsSpy = jest.spyOn(fs, 'existsSync');
mkdirpSpy = jest.spyOn(io, 'mkdirP');
// @actions/tool-cache
isCacheActionAvailable = jest.spyOn(cache, 'isFeatureAvailable');
// disable authentication portion for installer tests
authSpy = jest.spyOn(auth, 'configAuthentication');
authSpy.mockImplementation(() => {});
// gets
getManifestSpy.mockImplementation(
() => <tc.IToolRelease[]>nodeTestManifest
);
getJsonSpy.mockImplementation(url => {
let res: any;
if (url.includes('/rc')) {
res = <INodeVersion[]>nodeTestDistRc;
} else if (url.includes('/nightly')) {
res = <INodeVersion[]>nodeTestDistNightly;
} else if (url.includes('/v8-canary')) {
res = <INodeVersion[]>nodeV8CanaryTestDist;
} else {
res = <INodeVersion[]>nodeTestDist;
}
return {result: res};
});
// writes
cnSpy = jest.spyOn(process.stdout, 'write');
logSpy = jest.spyOn(core, 'info');
dbgSpy = jest.spyOn(core, 'debug');
warningSpy = jest.spyOn(core, 'warning');
cnSpy.mockImplementation(line => {
// uncomment to debug
// process.stderr.write('write:' + line + '\n');
});
logSpy.mockImplementation(line => {
// uncomment to debug
// process.stderr.write('log:' + line + '\n');
});
dbgSpy.mockImplementation(msg => {
// uncomment to see debug output
// process.stderr.write(msg + '\n');
});
warningSpy.mockImplementation(msg => {
// uncomment to debug
// process.stderr.write('log:' + msg + '\n');
});
// @actions/exec
getExecOutputSpy = jest.spyOn(exec, 'getExecOutput');
getExecOutputSpy.mockImplementation(() => 'v16.15.0');
});
afterEach(() => {
jest.resetAllMocks();
jest.clearAllMocks();
//jest.restoreAllMocks();
});
afterAll(async () => {
console.log('::stoptoken::'); // Re-enable executing of runner commands when running tests in actions
jest.restoreAllMocks();
}, 100000);
//--------------------------------------------------
// Found in cache tests
//--------------------------------------------------
it('finds version in cache with stable true', async () => {
inputs['node-version'] = '20-v8-canary';
os['arch'] = 'x64';
inputs.stable = 'true';
const toolPath = path.normalize(
'/cache/node/20.0.0-v8-canary20221103f7e2421e91/x64'
);
findSpy.mockImplementation(() => toolPath);
findAllVersionsSpy.mockImplementation(() => [
'20.0.0-v8-canary20221103f7e2421e91',
'20.0.0-v8-canary20221030fefe1c0879',
'19.0.0-v8-canary202210172ec229fc56',
'20.0.0-v8-canary2022102310ff1e5a8d'
]);
await main.run();
expect(findSpy).toHaveBeenCalledWith(
'node',
'20.0.0-v8-canary20221103f7e2421e91',
'x64'
);
expect(logSpy).toHaveBeenCalledWith(`Found in cache @ ${toolPath}`);
});
it('finds version in cache and adds it to the path', async () => {
inputs['node-version'] = '20-v8-canary';
os['arch'] = 'x64';
inSpy.mockImplementation(name => inputs[name]);
const toolPath = path.normalize(
'/cache/node/20.0.0-v8-canary20221103f7e2421e91/x64'
);
findSpy.mockImplementation(() => toolPath);
findAllVersionsSpy.mockImplementation(() => [
'20.0.0-v8-canary20221103f7e2421e91',
'20.0.0-v8-canary20221030fefe1c0879',
'19.0.0-v8-canary202210172ec229fc56',
'20.0.0-v8-canary2022102310ff1e5a8d'
]);
await main.run();
const expPath = path.join(toolPath, 'bin');
expect(cnSpy).toHaveBeenCalledWith(`::add-path::${expPath}${osm.EOL}`);
});
it('handles unhandled find error and reports error', async () => {
os.platform = 'linux';
const errMsg = 'unhandled error message';
inputs['node-version'] = '20.0.0-v8-canary20221103f7e2421e91';
findSpy.mockImplementation(() => {
throw new Error(errMsg);
});
findAllVersionsSpy.mockImplementation(() => [
'20.0.0-v8-canary20221103f7e2421e91',
'20.0.0-v8-canary20221030fefe1c0879',
'19.0.0-v8-canary202210172ec229fc56',
'20.0.0-v8-canary2022102310ff1e5a8d'
]);
await main.run();
expect(cnSpy).toHaveBeenCalledWith('::error::' + errMsg + osm.EOL);
});
//--------------------------------------------------
// Manifest tests
//--------------------------------------------------
it('falls back to a version from node dist', async () => {
os.platform = 'linux';
os.arch = 'x64';
// a version which is not in the manifest but is in node dist
const versionSpec = '11.15.0';
inputs['node-version'] = versionSpec;
inputs['always-auth'] = false;
inputs['token'] = 'faketoken';
// ... but not in the local cache
findSpy.mockImplementation(() => '');
dlSpy.mockImplementation(async () => '/some/temp/path');
const toolPath = path.normalize('/cache/node/11.11.0/x64');
exSpy.mockImplementation(async () => '/some/other/temp/path');
cacheSpy.mockImplementation(async () => toolPath);
await main.run();
const expPath = path.join(toolPath, 'bin');
expect(dlSpy).toHaveBeenCalled();
expect(exSpy).toHaveBeenCalled();
expect(logSpy).toHaveBeenCalledWith(
'Not found in manifest. Falling back to download directly from Node'
);
expect(logSpy).toHaveBeenCalledWith(
`Attempting to download ${versionSpec}...`
);
expect(cnSpy).toHaveBeenCalledWith(`::add-path::${expPath}${osm.EOL}`);
});
it('does not find a version that does not exist', async () => {
os.platform = 'linux';
os.arch = 'x64';
const versionSpec = '23.0.0-v8-canary20221103f7e2421e91';
inputs['node-version'] = versionSpec;
findSpy.mockImplementation(() => '');
findAllVersionsSpy.mockImplementation(() => [
'20.0.0-v8-canary20221103f7e2421e91',
'20.0.0-v8-canary20221030fefe1c0879',
'19.0.0-v8-canary202210172ec229fc56',
'20.0.0-v8-canary2022102310ff1e5a8d'
]);
await main.run();
expect(cnSpy).toHaveBeenCalledWith(
`::error::Unable to find Node version '${versionSpec}' for platform ${os.platform} and architecture ${os.arch}.${osm.EOL}`
);
});
it('reports a failed download', async () => {
const errMsg = 'unhandled download message';
os.platform = 'linux';
os.arch = 'x64';
// a version which is in the manifest
const versionSpec = '19.0.0-v8-canary';
inputs['node-version'] = versionSpec;
inputs['always-auth'] = false;
inputs['token'] = 'faketoken';
findSpy.mockImplementation(() => '');
findAllVersionsSpy.mockImplementation(() => [
'20.0.0-v8-canary20221103f7e2421e91',
'20.0.0-v8-canary20221030fefe1c0879',
'20.0.0-v8-canary2022102310ff1e5a8d'
]);
dlSpy.mockImplementation(() => {
throw new Error(errMsg);
});
await main.run();
expect(cnSpy).toHaveBeenCalledWith(`::error::${errMsg}${osm.EOL}`);
});
it('acquires specified architecture of node', async () => {
for (const {arch, version, osSpec} of [
{
arch: 'x86',
version: '20.0.0-v8-canary20221022e83bcb6c41',
osSpec: 'win32'
},
{
arch: 'x86',
version: '20.0.0-v8-canary20221103f7e2421e91',
osSpec: 'win32'
}
]) {
os.platform = osSpec;
os.arch = arch;
const fileExtension = os.platform === 'win32' ? '7z' : 'tar.gz';
const platform = {
linux: 'linux',
darwin: 'darwin',
win32: 'win'
}[os.platform];
inputs['node-version'] = version;
inputs['architecture'] = arch;
inputs['always-auth'] = false;
inputs['token'] = 'faketoken';
const expectedUrl = `https://nodejs.org/download/v8-canary/v${version}/node-v${version}-${platform}-${arch}.${fileExtension}`;
// ... but not in the local cache
findSpy.mockImplementation(() => '');
findAllVersionsSpy.mockImplementation(() => []);
dlSpy.mockImplementation(async () => '/some/temp/path');
const toolPath = path.normalize(`/cache/node/${version}/${arch}`);
exSpy.mockImplementation(async () => '/some/other/temp/path');
cacheSpy.mockImplementation(async () => toolPath);
await main.run();
expect(dlSpy).toHaveBeenCalled();
expect(logSpy).toHaveBeenCalledWith(
`Acquiring ${version} - ${arch} from ${expectedUrl}`
);
}
}, 100000);
describe('nightly versions', () => {
it.each([
[
'20.0.0-v8-canary',
'20.0.0-v8-canary20221103f7e2421e91',
'https://nodejs.org/download/v8-canary/v20.0.0-v8-canary20221103f7e2421e91/node-v20.0.0-v8-canary20221103f7e2421e91-linux-x64.tar.gz'
],
[
'20-v8-canary',
'20.0.0-v8-canary20221103f7e2421e91',
'https://nodejs.org/download/v8-canary/v20.0.0-v8-canary20221103f7e2421e91/node-v20.0.0-v8-canary20221103f7e2421e91-linux-x64.tar.gz'
],
[
'19.0.0-v8-canary',
'19.0.0-v8-canary202210187d6960f23f',
'https://nodejs.org/download/v8-canary/v19.0.0-v8-canary202210187d6960f23f/node-v19.0.0-v8-canary202210187d6960f23f-linux-x64.tar.gz'
],
[
'19-v8-canary',
'19.0.0-v8-canary202210187d6960f23f',
'https://nodejs.org/download/v8-canary/v19.0.0-v8-canary202210187d6960f23f/node-v19.0.0-v8-canary202210187d6960f23f-linux-x64.tar.gz'
],
[
'19.0.0-v8-canary202210187d6960f23f',
'19.0.0-v8-canary202210187d6960f23f',
'https://nodejs.org/download/v8-canary/v19.0.0-v8-canary202210187d6960f23f/node-v19.0.0-v8-canary202210187d6960f23f-linux-x64.tar.gz'
]
])(
'finds the versions in the index.json and installs it',
async (input, expectedVersion, expectedUrl) => {
const toolPath = path.normalize(`/cache/node/${expectedVersion}/x64`);
findSpy.mockImplementation(() => '');
findAllVersionsSpy.mockImplementation(() => []);
dlSpy.mockImplementation(async () => '/some/temp/path');
exSpy.mockImplementation(async () => '/some/other/temp/path');
cacheSpy.mockImplementation(async () => toolPath);
inputs['node-version'] = input;
os['arch'] = 'x64';
os['platform'] = 'linux';
// act
await main.run();
// assert
expect(logSpy).toHaveBeenCalledWith(
`Acquiring ${expectedVersion} - ${os.arch} from ${expectedUrl}`
);
expect(logSpy).toHaveBeenCalledWith('Extracting ...');
expect(logSpy).toHaveBeenCalledWith('Adding to the cache ...');
expect(cnSpy).toHaveBeenCalledWith(
`::add-path::${path.join(toolPath, 'bin')}${osm.EOL}`
);
}
);
it.each([
[
'20.0.0-v8-canary20221103f7e2421e91',
'20.0.0-v8-canary20221103f7e2421e91'
],
['20.0.0-v8-canary', '20.0.0-v8-canary20221103f7e2421e91'],
['20-v8-canary', '20.0.0-v8-canary20221103f7e2421e91']
])(
'finds the %s version in the hostedToolcache',
async (input, expectedVersion) => {
const toolPath = path.normalize(`/cache/node/${expectedVersion}/x64`);
findSpy.mockReturnValue(toolPath);
findAllVersionsSpy.mockReturnValue([
'20.0.0-v8-canary20221103f7e2421e91',
'20.0.0-v8-canary20221030fefe1c0879',
'19.0.0-v8-canary202210172ec229fc56',
'20.0.0-v8-canary2022102310ff1e5a8d'
]);
inputs['node-version'] = input;
os['arch'] = 'x64';
os['platform'] = 'linux';
// act
await main.run();
// assert
expect(findAllVersionsSpy).toHaveBeenCalled();
expect(logSpy).toHaveBeenCalledWith(`Found in cache @ ${toolPath}`);
expect(cnSpy).toHaveBeenCalledWith(
`::add-path::${path.join(toolPath, 'bin')}${osm.EOL}`
);
}
);
it.each([
[
'20.0.0-v8-canary',
'20.0.0-v8-canary20221103f7e2421e91',
'20.0.0-v8-canary20221030fefe1c0879',
'https://nodejs.org/download/v8-canary/v20.0.0-v8-canary20221103f7e2421e91/node-v20.0.0-v8-canary20221103f7e2421e91-linux-x64.tar.gz'
],
[
'20-v8-canary',
'20.0.0-v8-canary20221103f7e2421e91',
'20.0.0-v8-canary20221030fefe1c0879',
'https://nodejs.org/download/v8-canary/v20.0.0-v8-canary20221103f7e2421e91/node-v20.0.0-v8-canary20221103f7e2421e91-linux-x64.tar.gz'
],
[
'19.0.0-v8-canary',
'19.0.0-v8-canary202210187d6960f23f',
'19.0.0-v8-canary202210172ec229fc56',
'https://nodejs.org/download/v8-canary/v19.0.0-v8-canary202210187d6960f23f/node-v19.0.0-v8-canary202210187d6960f23f-linux-x64.tar.gz'
],
[
'19-v8-canary',
'19.0.0-v8-canary202210187d6960f23f',
'19.0.0-v8-canary202210172ec229fc56',
'https://nodejs.org/download/v8-canary/v19.0.0-v8-canary202210187d6960f23f/node-v19.0.0-v8-canary202210187d6960f23f-linux-x64.tar.gz'
]
])(
'get %s version from dist if check-latest is true',
async (input, expectedVersion, foundVersion, expectedUrl) => {
const foundToolPath = path.normalize(`/cache/node/${foundVersion}/x64`);
const toolPath = path.normalize(`/cache/node/${expectedVersion}/x64`);
inputs['node-version'] = input;
inputs['check-latest'] = 'true';
os['arch'] = 'x64';
os['platform'] = 'linux';
findSpy.mockReturnValue(foundToolPath);
findAllVersionsSpy.mockReturnValue([
'20.0.0-v8-canary20221030fefe1c0879',
'19.0.0-v8-canary202210172ec229fc56',
'20.0.0-v8-canary2022102310ff1e5a8d'
]);
dlSpy.mockImplementation(async () => '/some/temp/path');
exSpy.mockImplementation(async () => '/some/other/temp/path');
cacheSpy.mockImplementation(async () => toolPath);
// act
await main.run();
// assert
expect(findAllVersionsSpy).toHaveBeenCalled();
expect(logSpy).toHaveBeenCalledWith(
`Acquiring ${expectedVersion} - ${os.arch} from ${expectedUrl}`
);
expect(logSpy).toHaveBeenCalledWith('Extracting ...');
expect(logSpy).toHaveBeenCalledWith('Adding to the cache ...');
expect(cnSpy).toHaveBeenCalledWith(
`::add-path::${path.join(toolPath, 'bin')}${osm.EOL}`
);
}
);
it.each([
[
'20.0.0-v8-canary',
'20.0.0-v8-canary20221103f7e2421e91',
'20.0.0-v8-canary20221030fefe1c0879',
'https://my_mirror.org/download/v8-canary/v20.0.0-v8-canary20221103f7e2421e91/node-v20.0.0-v8-canary20221103f7e2421e91-linux-x64.tar.gz'
],
[
'20-v8-canary',
'20.0.0-v8-canary20221103f7e2421e91',
'20.0.0-v8-canary20221030fefe1c0879',
'https://my_mirror.org/download/v8-canary/v20.0.0-v8-canary20221103f7e2421e91/node-v20.0.0-v8-canary20221103f7e2421e91-linux-x64.tar.gz'
],
[
'19.0.0-v8-canary',
'19.0.0-v8-canary202210187d6960f23f',
'19.0.0-v8-canary202210172ec229fc56',
'https://my_mirror.org/download/v8-canary/v19.0.0-v8-canary202210187d6960f23f/node-v19.0.0-v8-canary202210187d6960f23f-linux-x64.tar.gz'
],
[
'19-v8-canary',
'19.0.0-v8-canary202210187d6960f23f',
'19.0.0-v8-canary202210172ec229fc56',
'https://my_mirror.org/download/v8-canary/v19.0.0-v8-canary202210187d6960f23f/node-v19.0.0-v8-canary202210187d6960f23f-linux-x64.tar.gz'
]
])(
'get %s version from dist if check-latest is true',
async (input, expectedVersion, foundVersion, expectedUrl) => {
const foundToolPath = path.normalize(`/cache/node/${foundVersion}/x64`);
const toolPath = path.normalize(`/cache/node/${expectedVersion}/x64`);
inputs['node-version'] = input;
inputs['check-latest'] = 'true';
os['arch'] = 'x64';
os['platform'] = 'linux';
inputs['mirror'] = 'https://my_mirror.org';
inputs['mirror-token'] = 'faketoken';
findSpy.mockReturnValue(foundToolPath);
findAllVersionsSpy.mockReturnValue([
'20.0.0-v8-canary20221030fefe1c0879',
'19.0.0-v8-canary202210172ec229fc56',
'20.0.0-v8-canary2022102310ff1e5a8d'
]);
dlSpy.mockImplementation(async () => '/some/temp/path');
exSpy.mockImplementation(async () => '/some/other/temp/path');
cacheSpy.mockImplementation(async () => toolPath);
// act
await main.run();
// assert
expect(findAllVersionsSpy).toHaveBeenCalled();
expect(logSpy).toHaveBeenCalledWith(
`Acquiring ${expectedVersion} - ${os.arch} from ${expectedUrl}`
);
expect(logSpy).toHaveBeenCalledWith('Extracting ...');
expect(logSpy).toHaveBeenCalledWith('Adding to the cache ...');
expect(cnSpy).toHaveBeenCalledWith(
`::add-path::${path.join(toolPath, 'bin')}${osm.EOL}`
);
}
);
});
describe('setup-node v8 canary tests', () => {
it('v8 canary setup node flow with cached', async () => {
const versionSpec = 'v20-v8-canary';
inputs['node-version'] = versionSpec;
inputs['always-auth'] = false;
inputs['token'] = 'faketoken';
os.platform = 'linux';
os.arch = 'x64';
const versionExpected = 'v20.0.0-v8-canary20221103f7e2421e91';
findAllVersionsSpy.mockImplementation(() => [versionExpected]);
const toolPath = path.normalize(`/cache/node/${versionExpected}/x64`);
findSpy.mockImplementation(version => toolPath);
await main.run();
expect(cnSpy).toHaveBeenCalledWith(
`::add-path::${toolPath}${path.sep}bin${osm.EOL}`
);
expect(dlSpy).not.toHaveBeenCalled();
expect(exSpy).not.toHaveBeenCalled();
expect(cacheSpy).not.toHaveBeenCalled();
});
});
});

1
__tests__/data/.nvmrc Normal file
View file

@ -0,0 +1 @@
v20

View file

@ -0,0 +1 @@
nodejs 20.0.0

View file

@ -0,0 +1 @@
node 20.0.0

1401
__tests__/data/package-lock.json generated Normal file

File diff suppressed because it is too large Load diff

View file

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

View file

@ -0,0 +1,5 @@
{
"engines": {
"node": "^20.0.0"
}
}

1256
__tests__/data/pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load diff

689
__tests__/data/yarn.lock Normal file
View file

@ -0,0 +1,689 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
accepts@^1.3.8:
version "1.3.8"
resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e"
integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==
dependencies:
mime-types "~2.1.34"
negotiator "0.6.3"
accepts@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/accepts/-/accepts-2.0.0.tgz#bbcf4ba5075467f3f2131eab3cffc73c2f5d7895"
integrity sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==
dependencies:
mime-types "^3.0.0"
negotiator "^1.0.0"
array-flatten@3.0.0, array-flatten@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-3.0.0.tgz#6428ca2ee52c7b823192ec600fa3ed2f157cd541"
integrity sha512-zPMVc3ZYlGLNk4mpK1NzP2wg0ml9t7fUgDsayR5Y5rSzxQilzR9FGu/EH2jQOcKSAeAfWeylyW8juy3OkWRvNA==
body-parser@^1.20.3:
version "1.20.3"
resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.3.tgz#1953431221c6fb5cd63c4b36d53fab0928e548c6"
integrity sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==
dependencies:
bytes "3.1.2"
content-type "~1.0.5"
debug "2.6.9"
depd "2.0.0"
destroy "1.2.0"
http-errors "2.0.0"
iconv-lite "0.4.24"
on-finished "2.4.1"
qs "6.13.0"
raw-body "2.5.2"
type-is "~1.6.18"
unpipe "1.0.0"
body-parser@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-2.0.1.tgz#979de4a43468c5624403457fd6d45f797faffbaf"
integrity sha512-PagxbjvuPH6tv0f/kdVbFGcb79D236SLcDTs6DrQ7GizJ88S1UWP4nMXFEo/I4fdhGRGabvFfFjVGm3M7U8JwA==
dependencies:
bytes "3.1.2"
content-type "~1.0.5"
debug "3.1.0"
destroy "1.2.0"
http-errors "2.0.0"
iconv-lite "0.5.2"
on-finished "2.4.1"
qs "6.13.0"
raw-body "^3.0.0"
type-is "~1.6.18"
unpipe "1.0.0"
bytes@3.1.2, bytes@^3.1.2:
version "3.1.2"
resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5"
integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==
call-bind@^1.0.7:
version "1.0.7"
resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.7.tgz#06016599c40c56498c18769d2730be242b6fa3b9"
integrity sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==
dependencies:
es-define-property "^1.0.0"
es-errors "^1.3.0"
function-bind "^1.1.2"
get-intrinsic "^1.2.4"
set-function-length "^1.2.1"
content-disposition@^0.5.4:
version "0.5.4"
resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe"
integrity sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==
dependencies:
safe-buffer "5.2.1"
content-disposition@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-1.0.0.tgz#844426cb398f934caefcbb172200126bc7ceace2"
integrity sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==
dependencies:
safe-buffer "5.2.1"
content-type@^1.0.5, content-type@~1.0.4, content-type@~1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.5.tgz#8b773162656d1d1086784c8f23a54ce6d73d7918"
integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==
cookie-signature@^1.0.7, cookie-signature@^1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.2.1.tgz#790dea2cce64638c7ae04d9fabed193bd7ccf3b4"
integrity sha512-78KWk9T26NhzXtuL26cIJ8/qNHANyJ/ZYrmEXFzUmhZdjpBv+DlWlOANRTGBt48YcyslsLrj0bMLFTmXvLRCOw==
cookie@0.6.0:
version "0.6.0"
resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.6.0.tgz#2798b04b071b0ecbff0dbb62a505a8efa4e19051"
integrity sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==
cookie@^0.5.0:
version "0.5.0"
resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.5.0.tgz#d1f5d71adec6558c58f389987c366aa47e994f8b"
integrity sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==
debug@2.6.9:
version "2.6.9"
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==
dependencies:
ms "2.0.0"
debug@3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261"
integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==
dependencies:
ms "2.0.0"
debug@4.3.6:
version "4.3.6"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.6.tgz#2ab2c38fbaffebf8aa95fdfe6d88438c7a13c52b"
integrity sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==
dependencies:
ms "2.1.2"
debug@^4.0.0, debug@^4.3.5:
version "4.3.7"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.7.tgz#87945b4151a011d76d95a198d7111c865c360a52"
integrity sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==
dependencies:
ms "^2.1.3"
define-data-property@^1.1.4:
version "1.1.4"
resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.4.tgz#894dc141bb7d3060ae4366f6a0107e68fbe48c5e"
integrity sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==
dependencies:
es-define-property "^1.0.0"
es-errors "^1.3.0"
gopd "^1.0.1"
depd@2.0.0, depd@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df"
integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==
depd@~1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9"
integrity sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==
destroy@1.2.0, destroy@^1.1.0, destroy@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015"
integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==
ee-first@1.1.1, ee-first@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==
encodeurl@^2.0.0, encodeurl@~2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-2.0.0.tgz#7b8ea898077d7e409d3ac45474ea38eaf0857a58"
integrity sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==
encodeurl@~1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59"
integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==
es-define-property@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.0.tgz#c7faefbdff8b2696cf5f46921edfb77cc4ba3845"
integrity sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==
dependencies:
get-intrinsic "^1.2.4"
es-errors@^1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f"
integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==
escape-html@^1.0.3, escape-html@~1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988"
integrity sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==
etag@^1.8.1, etag@~1.8.1:
version "1.8.1"
resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887"
integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==
express@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/express/-/express-5.0.0.tgz#744f9ec86025a01aeca99e4300aa4fc050d493c7"
integrity sha512-V4UkHQc+B7ldh1YC84HCXHwf60M4BOMvp9rkvTUWCK5apqDC1Esnbid4wm6nFyVuDy8XMfETsJw5lsIGBWyo0A==
dependencies:
accepts "^2.0.0"
body-parser "^2.0.1"
content-disposition "^1.0.0"
content-type "~1.0.4"
cookie "0.6.0"
cookie-signature "^1.2.1"
debug "4.3.6"
depd "2.0.0"
encodeurl "~2.0.0"
escape-html "~1.0.3"
etag "~1.8.1"
finalhandler "^2.0.0"
fresh "2.0.0"
http-errors "2.0.0"
merge-descriptors "^2.0.0"
methods "~1.1.2"
mime-types "^3.0.0"
on-finished "2.4.1"
once "1.4.0"
parseurl "~1.3.3"
proxy-addr "~2.0.7"
qs "6.13.0"
range-parser "~1.2.1"
router "^2.0.0"
safe-buffer "5.2.1"
send "^1.1.0"
serve-static "^2.1.0"
setprototypeof "1.2.0"
statuses "2.0.1"
type-is "^2.0.0"
utils-merge "1.0.1"
vary "~1.1.2"
finalhandler@^1.2.0:
version "1.3.1"
resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.3.1.tgz#0c575f1d1d324ddd1da35ad7ece3df7d19088019"
integrity sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==
dependencies:
debug "2.6.9"
encodeurl "~2.0.0"
escape-html "~1.0.3"
on-finished "2.4.1"
parseurl "~1.3.3"
statuses "2.0.1"
unpipe "~1.0.0"
finalhandler@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-2.0.0.tgz#9d3c79156dfa798069db7de7dd53bc37546f564b"
integrity sha512-MX6Zo2adDViYh+GcxxB1dpO43eypOGUOL12rLCOTMQv/DfIbpSJUy4oQIIZhVZkH9e+bZWKMon0XHFEju16tkQ==
dependencies:
debug "2.6.9"
encodeurl "~1.0.2"
escape-html "~1.0.3"
on-finished "2.4.1"
parseurl "~1.3.3"
statuses "2.0.1"
unpipe "~1.0.0"
forwarded@0.2.0, forwarded@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811"
integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==
fresh@0.5.2, fresh@^0.5.2:
version "0.5.2"
resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7"
integrity sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==
fresh@2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/fresh/-/fresh-2.0.0.tgz#8dd7df6a1b3a1b3a5cf186c05a5dd267622635a4"
integrity sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==
function-bind@^1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c"
integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==
get-intrinsic@^1.1.3, get-intrinsic@^1.2.4:
version "1.2.4"
resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.4.tgz#e385f5a4b5227d449c3eabbad05494ef0abbeadd"
integrity sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==
dependencies:
es-errors "^1.3.0"
function-bind "^1.1.2"
has-proto "^1.0.1"
has-symbols "^1.0.3"
hasown "^2.0.0"
gopd@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c"
integrity sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==
dependencies:
get-intrinsic "^1.1.3"
has-property-descriptors@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz#963ed7d071dc7bf5f084c5bfbe0d1b6222586854"
integrity sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==
dependencies:
es-define-property "^1.0.0"
has-proto@^1.0.1:
version "1.0.3"
resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.0.3.tgz#b31ddfe9b0e6e9914536a6ab286426d0214f77fd"
integrity sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==
has-symbols@^1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8"
integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==
hasown@^2.0.0:
version "2.0.2"
resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003"
integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==
dependencies:
function-bind "^1.1.2"
http-errors@2.0.0, http-errors@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3"
integrity sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==
dependencies:
depd "2.0.0"
inherits "2.0.4"
setprototypeof "1.2.0"
statuses "2.0.1"
toidentifier "1.0.1"
http-errors@^1.8.0:
version "1.8.1"
resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.8.1.tgz#7c3f28577cbc8a207388455dbd62295ed07bd68c"
integrity sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==
dependencies:
depd "~1.1.2"
inherits "2.0.4"
setprototypeof "1.2.0"
statuses ">= 1.5.0 < 2"
toidentifier "1.0.1"
iconv-lite@0.4.24:
version "0.4.24"
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"
integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==
dependencies:
safer-buffer ">= 2.1.2 < 3"
iconv-lite@0.5.2:
version "0.5.2"
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.5.2.tgz#af6d628dccfb463b7364d97f715e4b74b8c8c2b8"
integrity sha512-kERHXvpSaB4aU3eANwidg79K8FlrN77m8G9V+0vOR3HYaRifrlwMEpT7ZBJqLSEIHnEgJTHcWK82wwLwwKwtag==
dependencies:
safer-buffer ">= 2.1.2 < 3"
iconv-lite@0.6.3, iconv-lite@^0.6.0:
version "0.6.3"
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501"
integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==
dependencies:
safer-buffer ">= 2.1.2 < 3.0.0"
inherits@2.0.4, inherits@^2.0.4:
version "2.0.4"
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
ipaddr.js@1.9.1:
version "1.9.1"
resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3"
integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==
ipaddr.js@^2.2.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-2.2.0.tgz#d33fa7bac284f4de7af949638c9d68157c6b92e8"
integrity sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA==
is-promise@4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-4.0.0.tgz#42ff9f84206c1991d26debf520dd5c01042dd2f3"
integrity sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==
media-typer@0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748"
integrity sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==
media-typer@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-1.1.0.tgz#6ab74b8f2d3320f2064b2a87a38e7931ff3a5561"
integrity sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==
merge-descriptors@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-2.0.0.tgz#ea922f660635a2249ee565e0449f951e6b603808"
integrity sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==
methods@^1.1.2, methods@~1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee"
integrity sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==
mime-db@1.52.0:
version "1.52.0"
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70"
integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==
mime-db@^1.51.0, mime-db@^1.53.0:
version "1.53.0"
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.53.0.tgz#3cb63cd820fc29896d9d4e8c32ab4fcd74ccb447"
integrity sha512-oHlN/w+3MQ3rba9rqFr6V/ypF10LSkdwUysQL7GkXoTgIWeV+tcXGA852TBxH+gsh8UWoyhR1hKcoMJTuWflpg==
mime-types@^2.1.35, mime-types@~2.1.24, mime-types@~2.1.34:
version "2.1.35"
resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a"
integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==
dependencies:
mime-db "1.52.0"
mime-types@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-3.0.0.tgz#148453a900475522d095a445355c074cca4f5217"
integrity sha512-XqoSHeCGjVClAmoGFG3lVFqQFRIrTVw2OH3axRqAcfaw+gHWIfnASS92AV+Rl/mk0MupgZTRHQOjxY6YVnzK5w==
dependencies:
mime-db "^1.53.0"
mime@1.6.0, mime@^1.6.0:
version "1.6.0"
resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1"
integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==
ms@2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==
ms@2.1.2:
version "2.1.2"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
ms@2.1.3, ms@^2.1.2, ms@^2.1.3:
version "2.1.3"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
negotiator@0.6.3, negotiator@^0.6.3:
version "0.6.3"
resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd"
integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==
negotiator@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-1.0.0.tgz#b6c91bb47172d69f93cfd7c357bbb529019b5f6a"
integrity sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==
object-inspect@^1.13.1:
version "1.13.2"
resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.2.tgz#dea0088467fb991e67af4058147a24824a3043ff"
integrity sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==
on-finished@2.4.1, on-finished@^2.4.0, on-finished@^2.4.1:
version "2.4.1"
resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f"
integrity sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==
dependencies:
ee-first "1.1.1"
once@1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==
dependencies:
wrappy "1"
parseurl@^1.3.3, parseurl@~1.3.3:
version "1.3.3"
resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4"
integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==
path-to-regexp@^8.0.0:
version "8.1.0"
resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-8.1.0.tgz#4d687606ed0be8ed512ba802eb94d620cb1a86f0"
integrity sha512-Bqn3vc8CMHty6zuD+tG23s6v2kwxslHEhTj4eYaVKGIEB+YX/2wd0/rgXLFD9G9id9KCtbVy/3ZgmvZjpa0UdQ==
proxy-addr@^2.0.7, proxy-addr@~2.0.7:
version "2.0.7"
resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025"
integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==
dependencies:
forwarded "0.2.0"
ipaddr.js "1.9.1"
qs@6.13.0, qs@^6.8.0:
version "6.13.0"
resolved "https://registry.yarnpkg.com/qs/-/qs-6.13.0.tgz#6ca3bd58439f7e245655798997787b0d88a51906"
integrity sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==
dependencies:
side-channel "^1.0.6"
range-parser@^1.2.1, range-parser@~1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031"
integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==
raw-body@2.5.2, raw-body@^2.4.1:
version "2.5.2"
resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.2.tgz#99febd83b90e08975087e8f1f9419a149366b68a"
integrity sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==
dependencies:
bytes "3.1.2"
http-errors "2.0.0"
iconv-lite "0.4.24"
unpipe "1.0.0"
raw-body@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-3.0.0.tgz#25b3476f07a51600619dae3fe82ddc28a36e5e0f"
integrity sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==
dependencies:
bytes "3.1.2"
http-errors "2.0.0"
iconv-lite "0.6.3"
unpipe "1.0.0"
router@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/router/-/router-2.0.0.tgz#8692720b95de83876870d7bc638dd3c7e1ae8a27"
integrity sha512-dIM5zVoG8xhC6rnSN8uoAgFARwTE7BQs8YwHEvK0VCmfxQXMaOuA1uiR1IPwsW7JyK5iTt7Od/TC9StasS2NPQ==
dependencies:
array-flatten "3.0.0"
is-promise "4.0.0"
methods "~1.1.2"
parseurl "~1.3.3"
path-to-regexp "^8.0.0"
setprototypeof "1.2.0"
utils-merge "1.0.1"
safe-buffer@5.2.1, safe-buffer@^5.2.1:
version "5.2.1"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
"safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0", safer-buffer@^2.1.2:
version "2.1.2"
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
send@0.19.0, send@^0.19.0:
version "0.19.0"
resolved "https://registry.yarnpkg.com/send/-/send-0.19.0.tgz#bbc5a388c8ea6c048967049dbeac0e4a3f09d7f8"
integrity sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==
dependencies:
debug "2.6.9"
depd "2.0.0"
destroy "1.2.0"
encodeurl "~1.0.2"
escape-html "~1.0.3"
etag "~1.8.1"
fresh "0.5.2"
http-errors "2.0.0"
mime "1.6.0"
ms "2.1.3"
on-finished "2.4.1"
range-parser "~1.2.1"
statuses "2.0.1"
send@^1.0.0, send@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/send/-/send-1.1.0.tgz#4efe6ff3bb2139b0e5b2648d8b18d4dec48fc9c5"
integrity sha512-v67WcEouB5GxbTWL/4NeToqcZiAWEq90N888fczVArY8A79J0L4FD7vj5hm3eUMua5EpoQ59wa/oovY6TLvRUA==
dependencies:
debug "^4.3.5"
destroy "^1.2.0"
encodeurl "^2.0.0"
escape-html "^1.0.3"
etag "^1.8.1"
fresh "^0.5.2"
http-errors "^2.0.0"
mime-types "^2.1.35"
ms "^2.1.3"
on-finished "^2.4.1"
range-parser "^1.2.1"
statuses "^2.0.1"
serve-static@^1.15.0:
version "1.16.2"
resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.16.2.tgz#b6a5343da47f6bdd2673848bf45754941e803296"
integrity sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==
dependencies:
encodeurl "~2.0.0"
escape-html "~1.0.3"
parseurl "~1.3.3"
send "0.19.0"
serve-static@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-2.1.0.tgz#1b4eacbe93006b79054faa4d6d0a501d7f0e84e2"
integrity sha512-A3We5UfEjG8Z7VkDv6uItWw6HY2bBSBJT1KtVESn6EOoOr2jAxNhxWCLY3jDE2WcuHXByWju74ck3ZgLwL8xmA==
dependencies:
encodeurl "^2.0.0"
escape-html "^1.0.3"
parseurl "^1.3.3"
send "^1.0.0"
set-function-length@^1.2.1:
version "1.2.2"
resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.2.2.tgz#aac72314198eaed975cf77b2c3b6b880695e5449"
integrity sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==
dependencies:
define-data-property "^1.1.4"
es-errors "^1.3.0"
function-bind "^1.1.2"
get-intrinsic "^1.2.4"
gopd "^1.0.1"
has-property-descriptors "^1.0.2"
setprototypeof@1.2.0, setprototypeof@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424"
integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==
side-channel@^1.0.6:
version "1.0.6"
resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.6.tgz#abd25fb7cd24baf45466406b1096b7831c9215f2"
integrity sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==
dependencies:
call-bind "^1.0.7"
es-errors "^1.3.0"
get-intrinsic "^1.2.4"
object-inspect "^1.13.1"
statuses@2.0.1, statuses@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63"
integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==
"statuses@>= 1.5.0 < 2":
version "1.5.0"
resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c"
integrity sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==
toidentifier@1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35"
integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==
type-is@^1.6.18, type-is@~1.6.18:
version "1.6.18"
resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131"
integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==
dependencies:
media-typer "0.3.0"
mime-types "~2.1.24"
type-is@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/type-is/-/type-is-2.0.0.tgz#7d249c2e2af716665cc149575dadb8b3858653af"
integrity sha512-gd0sGezQYCbWSbkZr75mln4YBidWUN60+devscpLF5mtRDUpiaTvKpBNrdaCvel1NdR2k6vclXybU5fBd2i+nw==
dependencies:
content-type "^1.0.5"
media-typer "^1.1.0"
mime-types "^3.0.0"
unpipe@1.0.0, unpipe@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec"
integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==
utils-merge@1.0.1, utils-merge@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713"
integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==
vary@^1.1.2, vary@~1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc"
integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==
wrappy@1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==

View file

@ -0,0 +1,656 @@
import * as core from '@actions/core';
import * as io from '@actions/io';
import * as tc from '@actions/tool-cache';
import * as httpm from '@actions/http-client';
import * as exec from '@actions/exec';
import * as cache from '@actions/cache';
import fs from 'fs';
import cp from 'child_process';
import osm from 'os';
import path from 'path';
import * as main from '../src/main';
import * as auth from '../src/authutil';
import {INodeVersion} from '../src/distributions/base-models';
import nodeTestManifest from './data/versions-manifest.json';
import nodeTestDist from './data/node-dist-index.json';
import nodeTestDistNightly from './data/node-nightly-index.json';
import nodeTestDistRc from './data/node-rc-index.json';
import nodeV8CanaryTestDist from './data/v8-canary-dist-index.json';
describe('setup-node', () => {
let inputs = {} as any;
let os = {} as any;
let inSpy: jest.SpyInstance;
let findSpy: jest.SpyInstance;
let findAllVersionsSpy: jest.SpyInstance;
let cnSpy: jest.SpyInstance;
let logSpy: jest.SpyInstance;
let warningSpy: jest.SpyInstance;
let getManifestSpy: jest.SpyInstance;
let getDistSpy: jest.SpyInstance;
let platSpy: jest.SpyInstance;
let archSpy: jest.SpyInstance;
let dlSpy: jest.SpyInstance;
let exSpy: jest.SpyInstance;
let cacheSpy: jest.SpyInstance;
let dbgSpy: jest.SpyInstance;
let whichSpy: jest.SpyInstance;
let existsSpy: jest.SpyInstance;
let mkdirpSpy: jest.SpyInstance;
let cpSpy: jest.SpyInstance;
let execSpy: jest.SpyInstance;
let authSpy: jest.SpyInstance;
let parseNodeVersionSpy: jest.SpyInstance;
let isCacheActionAvailable: jest.SpyInstance;
let getExecOutputSpy: jest.SpyInstance;
let getJsonSpy: jest.SpyInstance;
beforeEach(() => {
// @actions/core
console.log('::stop-commands::stoptoken'); // Disable executing of runner commands when running tests in actions
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['RUNNER_TEMP'] = '/runner_temp';
inputs = {};
inSpy = jest.spyOn(core, 'getInput');
inSpy.mockImplementation(name => inputs[name]);
// node
os = {};
platSpy = jest.spyOn(osm, 'platform');
platSpy.mockImplementation(() => os['platform']);
archSpy = jest.spyOn(osm, 'arch');
archSpy.mockImplementation(() => os['arch']);
execSpy = jest.spyOn(cp, 'execSync');
// @actions/tool-cache
findSpy = jest.spyOn(tc, 'find');
findAllVersionsSpy = jest.spyOn(tc, 'findAllVersions');
dlSpy = jest.spyOn(tc, 'downloadTool');
exSpy = jest.spyOn(tc, 'extractTar');
cacheSpy = jest.spyOn(tc, 'cacheDir');
getManifestSpy = jest.spyOn(tc, 'getManifestFromRepo');
// http-client
getJsonSpy = jest.spyOn(httpm.HttpClient.prototype, 'getJson');
// io
whichSpy = jest.spyOn(io, 'which');
existsSpy = jest.spyOn(fs, 'existsSync');
mkdirpSpy = jest.spyOn(io, 'mkdirP');
cpSpy = jest.spyOn(io, 'cp');
// @actions/tool-cache
isCacheActionAvailable = jest.spyOn(cache, 'isFeatureAvailable');
// disable authentication portion for installer tests
authSpy = jest.spyOn(auth, 'configAuthentication');
authSpy.mockImplementation(() => {});
getJsonSpy.mockImplementation(url => {
let res: any;
if (url.includes('/rc')) {
res = <INodeVersion[]>nodeTestDistRc;
} else if (url.includes('/nightly')) {
res = <INodeVersion[]>nodeTestDistNightly;
} else if (url.includes('/v8-canary')) {
res = <INodeVersion[]>nodeV8CanaryTestDist;
} else {
res = <INodeVersion[]>nodeTestDist;
}
return {result: res};
});
// writes
cnSpy = jest.spyOn(process.stdout, 'write');
logSpy = jest.spyOn(core, 'info');
dbgSpy = jest.spyOn(core, 'debug');
warningSpy = jest.spyOn(core, 'warning');
cnSpy.mockImplementation(line => {
// uncomment to debug
// process.stderr.write('write:' + line + '\n');
});
logSpy.mockImplementation(line => {
// uncomment to debug
// process.stderr.write('log:' + line + '\n');
});
dbgSpy.mockImplementation(msg => {
// uncomment to see debug output
// process.stderr.write(msg + '\n');
});
warningSpy.mockImplementation(msg => {
// uncomment to debug
// process.stderr.write('log:' + msg + '\n');
});
// @actions/exec
getExecOutputSpy = jest.spyOn(exec, 'getExecOutput');
getExecOutputSpy.mockImplementation(() => 'v16.15.0');
});
afterEach(() => {
jest.resetAllMocks();
jest.clearAllMocks();
//jest.restoreAllMocks();
});
afterAll(async () => {
console.log('::stoptoken::'); // Re-enable executing of runner commands when running tests in actions
jest.restoreAllMocks();
}, 100000);
//--------------------------------------------------
// Found in cache tests
//--------------------------------------------------
it('finds version in cache with stable true', async () => {
inputs['node-version'] = '16-nightly';
os['arch'] = 'x64';
inputs.stable = 'true';
const toolPath = path.normalize(
'/cache/node/16.0.0-nightly20210417bc31dc0e0f/x64'
);
findSpy.mockImplementation(() => toolPath);
findAllVersionsSpy.mockImplementation(() => [
'12.0.1',
'16.0.0-nightly20210415c3a5e15ebe',
'16.0.0-nightly20210417bc31dc0e0f',
'16.1.3'
]);
await main.run();
expect(findSpy).toHaveBeenCalledWith(
'node',
'16.0.0-nightly20210417bc31dc0e0f',
'x64'
);
expect(logSpy).toHaveBeenCalledWith(`Found in cache @ ${toolPath}`);
});
it('finds version in cache with stable false', async () => {
inputs['node-version'] = '16.0.0-nightly20210415c3a5e15ebe';
os['arch'] = 'x64';
inputs.stable = 'false';
const toolPath = path.normalize(
'/cache/node/16.0.0-nightly20210415c3a5e15ebe/x64'
);
findSpy.mockImplementation(() => toolPath);
findAllVersionsSpy.mockImplementation(() => [
'12.0.1',
'16.0.0-nightly20210415c3a5e15ebe',
'16.0.0-nightly20210417bc31dc0e0f',
'16.1.3'
]);
await main.run();
expect(findSpy).toHaveBeenCalledWith(
'node',
'16.0.0-nightly20210415c3a5e15ebe',
'x64'
);
expect(logSpy).toHaveBeenCalledWith(`Found in cache @ ${toolPath}`);
});
it('finds version in cache and adds it to the path', async () => {
inputs['node-version'] = '16-nightly';
os['arch'] = 'x64';
inSpy.mockImplementation(name => inputs[name]);
const toolPath = path.normalize(
'/cache/node/16.0.0-nightly20210417bc31dc0e0f/x64'
);
findSpy.mockImplementation(() => toolPath);
findAllVersionsSpy.mockImplementation(() => [
'12.0.1',
'16.0.0-nightly20210415c3a5e15ebe',
'16.0.0-nightly20210417bc31dc0e0f',
'16.1.3'
]);
await main.run();
expect(findSpy).toHaveBeenCalledWith(
'node',
'16.0.0-nightly20210417bc31dc0e0f',
'x64'
);
const expPath = path.join(toolPath, 'bin');
expect(cnSpy).toHaveBeenCalledWith(`::add-path::${expPath}${osm.EOL}`);
});
it('handles unhandled find error and reports error', async () => {
const errMsg = 'unhandled error message';
inputs['node-version'] = '16.0.0-nightly20210417bc31dc0e0f';
findAllVersionsSpy.mockImplementation(() => [
'12.0.1',
'16.0.0-nightly20210415c3a5e15ebe',
'16.0.0-nightly20210417bc31dc0e0f',
'16.1.3'
]);
findSpy.mockImplementation(() => {
throw new Error(errMsg);
});
await main.run();
expect(cnSpy).toHaveBeenCalledWith('::error::' + errMsg + osm.EOL);
});
it('falls back to a version from node dist', async () => {
os.platform = 'linux';
os.arch = 'x64';
// a version which is not in the manifest but is in node dist
const versionSpec = '13.13.1-nightly20200415947ddec091';
inputs['node-version'] = versionSpec;
inputs['always-auth'] = false;
inputs['token'] = 'faketoken';
// ... but not in the local cache
findSpy.mockImplementation(() => '');
findAllVersionsSpy.mockImplementation(() => []);
dlSpy.mockImplementation(async () => '/some/temp/path');
const toolPath = path.normalize(
'/cache/node/13.13.1-nightly20200415947ddec091/x64'
);
exSpy.mockImplementation(async () => '/some/other/temp/path');
cacheSpy.mockImplementation(async () => toolPath);
await main.run();
const expPath = path.join(toolPath, 'bin');
expect(dlSpy).toHaveBeenCalled();
expect(exSpy).toHaveBeenCalled();
expect(cnSpy).toHaveBeenCalledWith(`::add-path::${expPath}${osm.EOL}`);
});
it('windows: falls back to exe version if not in manifest and not in node dist', async () => {
os.platform = 'win32';
os.arch = 'x64';
// a version which is not in the manifest but is in node dist
const versionSpec = '13.13.1-nightly20200415947ddec091';
const workingUrls = [
`https://nodejs.org/download/nightly/v${versionSpec}/win-x64/node.exe`,
`https://nodejs.org/download/nightly/v${versionSpec}/win-x64/node.lib`
];
inputs['node-version'] = versionSpec;
inputs['always-auth'] = false;
inputs['token'] = 'faketoken';
// ... but not in the local cache
findSpy.mockImplementation(() => '');
findAllVersionsSpy.mockImplementation(() => []);
dlSpy.mockImplementation(async url => {
if (workingUrls.includes(url)) {
return '/some/temp/path';
}
throw new tc.HTTPError(404);
});
const toolPath = path.normalize(
'/cache/node/13.13.1-nightly20200415947ddec091/x64'
);
cacheSpy.mockImplementation(async () => toolPath);
mkdirpSpy.mockImplementation(async () => {});
cpSpy.mockImplementation(async () => {});
await main.run();
workingUrls.forEach(url => {
expect(dlSpy).toHaveBeenCalledWith(url, undefined, undefined);
});
expect(cnSpy).toHaveBeenCalledWith(`::add-path::${toolPath}${osm.EOL}`);
});
it('linux: does not fall back to exe version if not in manifest and not in node dist', async () => {
os.platform = 'linux';
os.arch = 'x64';
// a version which is not in the manifest but is in node dist
const versionSpec = '13.13.1-nightly20200415947ddec091';
const workingUrls = [
`https://nodejs.org/download/nightly/v${versionSpec}/win-x64/node.exe`,
`https://nodejs.org/download/nightly/v${versionSpec}/win-x64/node.lib`
];
inputs['node-version'] = versionSpec;
inputs['always-auth'] = false;
inputs['token'] = 'faketoken';
// ... but not in the local cache
findSpy.mockImplementation(() => '');
findAllVersionsSpy.mockImplementation(() => []);
dlSpy.mockImplementation(async url => {
if (workingUrls.includes(url)) {
return '/some/temp/path';
}
throw new tc.HTTPError(404);
});
const toolPath = path.normalize(
'/cache/node/13.13.1-nightly20200415947ddec091/x64'
);
cacheSpy.mockImplementation(async () => toolPath);
mkdirpSpy.mockImplementation(async () => {});
cpSpy.mockImplementation(async () => {});
await main.run();
workingUrls.forEach(url => {
expect(dlSpy).not.toHaveBeenCalledWith(url);
});
expect(cnSpy).toHaveBeenCalledWith(
`::error::Unexpected HTTP response: 404${osm.EOL}`
);
});
it('does not find a version that does not exist', async () => {
os.platform = 'linux';
os.arch = 'x64';
const versionSpec = '10.13.1-nightly20200415947ddec091';
inputs['node-version'] = versionSpec;
findSpy.mockImplementation(() => '');
findAllVersionsSpy.mockImplementation(() => []);
await main.run();
expect(cnSpy).toHaveBeenCalledWith(
`::error::Unable to find Node version '${versionSpec}' for platform ${os.platform} and architecture ${os.arch}.${osm.EOL}`
);
});
it('reports a failed download', async () => {
const errMsg = 'unhandled download message';
os.platform = 'linux';
os.arch = 'x64';
// a version which is in the manifest
const versionSpec = '18.0.0-nightly202204180699150267';
inputs['node-version'] = versionSpec;
inputs['always-auth'] = false;
inputs['token'] = 'faketoken';
findSpy.mockImplementation(() => '');
findAllVersionsSpy.mockImplementation(() => []);
dlSpy.mockImplementation(() => {
throw new Error(errMsg);
});
await main.run();
expect(cnSpy).toHaveBeenCalledWith(`::error::${errMsg}${osm.EOL}`);
});
it('acquires specified architecture of node', async () => {
for (const {arch, version, osSpec} of [
{
arch: 'x86',
version: '18.0.0-nightly202110204cb3e06ed8',
osSpec: 'win32'
},
{
arch: 'x86',
version: '20.0.0-nightly2022101987cdf7d412',
osSpec: 'win32'
}
]) {
os.platform = osSpec;
os.arch = arch;
const fileExtension = os.platform === 'win32' ? '7z' : 'tar.gz';
const platform = {
linux: 'linux',
darwin: 'darwin',
win32: 'win'
}[os.platform];
inputs['node-version'] = version;
inputs['architecture'] = arch;
inputs['always-auth'] = false;
inputs['token'] = 'faketoken';
const expectedUrl = `https://nodejs.org/download/nightly/v${version}/node-v${version}-${platform}-${arch}.${fileExtension}`;
// ... but not in the local cache
findSpy.mockImplementation(() => '');
findAllVersionsSpy.mockImplementation(() => []);
dlSpy.mockImplementation(async () => '/some/temp/path');
const toolPath = path.normalize(`/cache/node/${version}/${arch}`);
exSpy.mockImplementation(async () => '/some/other/temp/path');
cacheSpy.mockImplementation(async () => toolPath);
await main.run();
expect(dlSpy).toHaveBeenCalled();
expect(logSpy).toHaveBeenCalledWith(
`Acquiring ${version} - ${arch} from ${expectedUrl}`
);
}
}, 100000);
it('acquires specified architecture of node from mirror', async () => {
for (const {arch, version, osSpec} of [
{
arch: 'x86',
version: '18.0.0-nightly202110204cb3e06ed8',
osSpec: 'win32'
},
{
arch: 'x86',
version: '20.0.0-nightly2022101987cdf7d412',
osSpec: 'win32'
}
]) {
os.platform = osSpec;
os.arch = arch;
const fileExtension = os.platform === 'win32' ? '7z' : 'tar.gz';
const platform = {
linux: 'linux',
darwin: 'darwin',
win32: 'win'
}[os.platform];
inputs['node-version'] = version;
inputs['architecture'] = arch;
inputs['always-auth'] = false;
inputs['token'] = 'faketoken';
inputs['mirror'] = 'https://my-mirror.org';
inputs['mirror-token'] = 'my-mirror-token';
const expectedUrl = `https://my-mirror.org/download/nightly/v${version}/node-v${version}-${platform}-${arch}.${fileExtension}`;
// ... but not in the local cache
findSpy.mockImplementation(() => '');
findAllVersionsSpy.mockImplementation(() => []);
dlSpy.mockImplementation(async () => '/some/temp/path');
const toolPath = path.normalize(`/cache/node/${version}/${arch}`);
exSpy.mockImplementation(async () => '/some/other/temp/path');
cacheSpy.mockImplementation(async () => toolPath);
await main.run();
expect(dlSpy).toHaveBeenCalled();
expect(logSpy).toHaveBeenCalledWith(
`Acquiring ${version} - ${arch} from ${expectedUrl}`
);
}
}, 100000);
describe('nightly versions', () => {
it.each([
[
'17.5.0-nightly',
'17.5.0-nightly20220209e43808936a',
'https://nodejs.org/download/nightly/v17.5.0-nightly20220209e43808936a/node-v17.5.0-nightly20220209e43808936a-linux-x64.tar.gz'
],
[
'17-nightly',
'17.5.0-nightly20220209e43808936a',
'https://nodejs.org/download/nightly/v17.5.0-nightly20220209e43808936a/node-v17.5.0-nightly20220209e43808936a-linux-x64.tar.gz'
],
[
'18.0.0-nightly',
'18.0.0-nightly20220419bde889bd4e',
'https://nodejs.org/download/nightly/v18.0.0-nightly20220419bde889bd4e/node-v18.0.0-nightly20220419bde889bd4e-linux-x64.tar.gz'
],
[
'18-nightly',
'18.0.0-nightly20220419bde889bd4e',
'https://nodejs.org/download/nightly/v18.0.0-nightly20220419bde889bd4e/node-v18.0.0-nightly20220419bde889bd4e-linux-x64.tar.gz'
],
[
'20.0.0-nightly',
'20.0.0-nightly2022101987cdf7d412',
'https://nodejs.org/download/nightly/v20.0.0-nightly2022101987cdf7d412/node-v20.0.0-nightly2022101987cdf7d412-linux-x64.tar.gz'
]
])(
'finds the versions in the index.json and installs it',
async (input, expectedVersion, expectedUrl) => {
const toolPath = path.normalize(`/cache/node/${expectedVersion}/x64`);
findSpy.mockImplementation(() => '');
findAllVersionsSpy.mockImplementation(() => []);
dlSpy.mockImplementation(async () => '/some/temp/path');
exSpy.mockImplementation(async () => '/some/other/temp/path');
cacheSpy.mockImplementation(async () => toolPath);
inputs['node-version'] = input;
os['arch'] = 'x64';
os['platform'] = 'linux';
// act
await main.run();
// assert
expect(logSpy).toHaveBeenCalledWith(
`Acquiring ${expectedVersion} - ${os.arch} from ${expectedUrl}`
);
expect(logSpy).toHaveBeenCalledWith('Extracting ...');
expect(logSpy).toHaveBeenCalledWith('Adding to the cache ...');
expect(cnSpy).toHaveBeenCalledWith(
`::add-path::${path.join(toolPath, 'bin')}${osm.EOL}`
);
}
);
it.each([
['17.5.0-nightly', '17.5.0-nightly20220209e43808936a'],
['17-nightly', '17.5.0-nightly20220209e43808936a'],
['20.0.0-nightly', '20.0.0-nightly2022101987cdf7d412']
])(
'finds the %s version in the hostedToolcache',
async (input, expectedVersion) => {
const toolPath = path.normalize(`/cache/node/${expectedVersion}/x64`);
findSpy.mockReturnValue(toolPath);
findAllVersionsSpy.mockReturnValue([
'17.5.0-nightly20220209e43808936a',
'17.5.0-nightly20220209e43808935a',
'20.0.0-nightly2022101987cdf7d412',
'20.0.0-nightly2022101987cdf7d411'
]);
inputs['node-version'] = input;
os['arch'] = 'x64';
os['platform'] = 'linux';
// act
await main.run();
// assert
expect(findAllVersionsSpy).toHaveBeenCalled();
expect(logSpy).toHaveBeenCalledWith(`Found in cache @ ${toolPath}`);
expect(cnSpy).toHaveBeenCalledWith(
`::add-path::${path.join(toolPath, 'bin')}${osm.EOL}`
);
}
);
it.each([
[
'17.5.0-nightly',
'17.5.0-nightly20220209e43808936a',
'17.0.0-nightly202110193f11666dc7',
'https://nodejs.org/download/nightly/v17.5.0-nightly20220209e43808936a/node-v17.5.0-nightly20220209e43808936a-linux-x64.tar.gz'
],
[
'17-nightly',
'17.5.0-nightly20220209e43808936a',
'17.0.0-nightly202110193f11666dc7',
'https://nodejs.org/download/nightly/v17.5.0-nightly20220209e43808936a/node-v17.5.0-nightly20220209e43808936a-linux-x64.tar.gz'
],
[
'18.0.0-nightly',
'18.0.0-nightly20220419bde889bd4e',
'18.0.0-nightly202204180699150267',
'https://nodejs.org/download/nightly/v18.0.0-nightly20220419bde889bd4e/node-v18.0.0-nightly20220419bde889bd4e-linux-x64.tar.gz'
],
[
'18-nightly',
'18.0.0-nightly20220419bde889bd4e',
'18.0.0-nightly202204180699150267',
'https://nodejs.org/download/nightly/v18.0.0-nightly20220419bde889bd4e/node-v18.0.0-nightly20220419bde889bd4e-linux-x64.tar.gz'
],
[
'20.0.0-nightly',
'20.0.0-nightly2022101987cdf7d412',
'20.0.0-nightly2022101987cdf7d411',
'https://nodejs.org/download/nightly/v20.0.0-nightly2022101987cdf7d412/node-v20.0.0-nightly2022101987cdf7d412-linux-x64.tar.gz'
]
])(
'get %s version from dist if check-latest is true',
async (input, expectedVersion, foundVersion, expectedUrl) => {
const foundToolPath = path.normalize(`/cache/node/${foundVersion}/x64`);
const toolPath = path.normalize(`/cache/node/${expectedVersion}/x64`);
inputs['node-version'] = input;
inputs['check-latest'] = 'true';
os['arch'] = 'x64';
os['platform'] = 'linux';
findSpy.mockReturnValue(foundToolPath);
findAllVersionsSpy.mockReturnValue([
'17.0.0-nightly202110193f11666dc7',
'18.0.0-nightly202204180699150267',
'20.0.0-nightly2022101987cdf7d411'
]);
dlSpy.mockImplementation(async () => '/some/temp/path');
exSpy.mockImplementation(async () => '/some/other/temp/path');
cacheSpy.mockImplementation(async () => toolPath);
// act
await main.run();
// assert
expect(findAllVersionsSpy).toHaveBeenCalled();
expect(logSpy).toHaveBeenCalledWith(
`Acquiring ${expectedVersion} - ${os.arch} from ${expectedUrl}`
);
expect(logSpy).toHaveBeenCalledWith('Extracting ...');
expect(logSpy).toHaveBeenCalledWith('Adding to the cache ...');
expect(cnSpy).toHaveBeenCalledWith(
`::add-path::${path.join(toolPath, 'bin')}${osm.EOL}`
);
}
);
});
});

View file

@ -0,0 +1,910 @@
import * as core from '@actions/core';
import * as io from '@actions/io';
import * as tc from '@actions/tool-cache';
import * as httpm from '@actions/http-client';
import * as exec from '@actions/exec';
import * as cache from '@actions/cache';
import fs from 'fs';
import cp from 'child_process';
import osm from 'os';
import path from 'path';
import * as main from '../src/main';
import * as auth from '../src/authutil';
import OfficialBuilds from '../src/distributions/official_builds/official_builds';
import {INodeVersion} from '../src/distributions/base-models';
import nodeTestManifest from './data/versions-manifest.json';
import nodeTestDist from './data/node-dist-index.json';
import nodeTestDistNightly from './data/node-nightly-index.json';
import nodeTestDistRc from './data/node-rc-index.json';
import nodeV8CanaryTestDist from './data/v8-canary-dist-index.json';
describe('setup-node', () => {
let build: OfficialBuilds;
let inputs = {} as any;
let os = {} as any;
let inSpy: jest.SpyInstance;
let findSpy: jest.SpyInstance;
let findAllVersionsSpy: jest.SpyInstance;
let cnSpy: jest.SpyInstance;
let logSpy: jest.SpyInstance;
let warningSpy: jest.SpyInstance;
let getManifestSpy: jest.SpyInstance;
let platSpy: jest.SpyInstance;
let archSpy: jest.SpyInstance;
let dlSpy: jest.SpyInstance;
let exSpy: jest.SpyInstance;
let cacheSpy: jest.SpyInstance;
let dbgSpy: jest.SpyInstance;
let whichSpy: jest.SpyInstance;
let existsSpy: jest.SpyInstance;
let readFileSyncSpy: jest.SpyInstance;
let mkdirpSpy: jest.SpyInstance;
let execSpy: jest.SpyInstance;
let authSpy: jest.SpyInstance;
let isCacheActionAvailable: jest.SpyInstance;
let getExecOutputSpy: jest.SpyInstance;
let getJsonSpy: jest.SpyInstance;
beforeEach(() => {
// @actions/core
console.log('::stop-commands::stoptoken'); // Disable executing of runner commands when running tests in actions
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
inputs = {};
inSpy = jest.spyOn(core, 'getInput');
inSpy.mockImplementation(name => inputs[name]);
// node
os = {};
platSpy = jest.spyOn(osm, 'platform');
platSpy.mockImplementation(() => os['platform']);
archSpy = jest.spyOn(osm, 'arch');
archSpy.mockImplementation(() => os['arch']);
execSpy = jest.spyOn(cp, 'execSync');
// @actions/tool-cache
findSpy = jest.spyOn(tc, 'find');
findAllVersionsSpy = jest.spyOn(tc, 'findAllVersions');
dlSpy = jest.spyOn(tc, 'downloadTool');
exSpy = jest.spyOn(tc, 'extractTar');
cacheSpy = jest.spyOn(tc, 'cacheDir');
getManifestSpy = jest.spyOn(tc, 'getManifestFromRepo');
// http-client
getJsonSpy = jest.spyOn(httpm.HttpClient.prototype, 'getJson');
// io
whichSpy = jest.spyOn(io, 'which');
existsSpy = jest.spyOn(fs, 'existsSync');
mkdirpSpy = jest.spyOn(io, 'mkdirP');
// @actions/tool-cache
isCacheActionAvailable = jest.spyOn(cache, 'isFeatureAvailable');
// disable authentication portion for installer tests
authSpy = jest.spyOn(auth, 'configAuthentication');
authSpy.mockImplementation(() => {});
// gets
getManifestSpy.mockImplementation(
() => <tc.IToolRelease[]>nodeTestManifest
);
getJsonSpy.mockImplementation(url => {
let res: any;
if (url.includes('/rc')) {
res = <INodeVersion[]>nodeTestDistRc;
} else if (url.includes('/nightly')) {
res = <INodeVersion[]>nodeTestDistNightly;
} else {
res = <INodeVersion[]>nodeTestDist;
}
return {result: res};
});
// writes
cnSpy = jest.spyOn(process.stdout, 'write');
logSpy = jest.spyOn(core, 'info');
dbgSpy = jest.spyOn(core, 'debug');
warningSpy = jest.spyOn(core, 'warning');
cnSpy.mockImplementation(line => {
// uncomment to debug
process.stderr.write('write:' + line + '\n');
});
logSpy.mockImplementation(line => {
// uncomment to debug
process.stderr.write('log:' + line + '\n');
});
dbgSpy.mockImplementation(msg => {
// uncomment to see debug output
// process.stderr.write(msg + '\n');
});
warningSpy.mockImplementation(msg => {
// uncomment to debug
// process.stderr.write('log:' + msg + '\n');
});
// @actions/exec
getExecOutputSpy = jest.spyOn(exec, 'getExecOutput');
getExecOutputSpy.mockImplementation(() => 'v16.15.0');
});
afterEach(() => {
jest.resetAllMocks();
jest.clearAllMocks();
//jest.restoreAllMocks();
});
afterAll(async () => {
console.log('::stoptoken::'); // Re-enable executing of runner commands when running tests in actions
jest.restoreAllMocks();
}, 100000);
//--------------------------------------------------
// Manifest find tests
//--------------------------------------------------
it.each([
['12.16.2', 'darwin', '12.16.2', 'Erbium'],
['12', 'linux', '12.16.2', 'Erbium'],
['10', 'win32', '10.20.1', 'Dubnium'],
['*', 'linux', '14.0.0', 'Fermium']
])(
'can find %s from manifest on %s',
async (versionSpec, platform, expectedVersion, expectedLts) => {
os.platform = platform;
os.arch = 'x64';
const versions: tc.IToolRelease[] | null = await tc.getManifestFromRepo(
'actions',
'node-versions',
'mocktoken'
);
expect(versions).toBeDefined();
const match = await tc.findFromManifest(versionSpec, true, versions);
expect(match).toBeDefined();
expect(match?.version).toBe(expectedVersion);
expect((match as any).lts).toBe(expectedLts);
}
);
//--------------------------------------------------
// Found in cache tests
//--------------------------------------------------
it('finds version in cache with stable true', async () => {
inputs['node-version'] = '12';
inputs.stable = 'true';
const toolPath = path.normalize('/cache/node/12.16.1/x64');
findSpy.mockImplementation(() => toolPath);
await main.run();
expect(logSpy).toHaveBeenCalledWith(`Found in cache @ ${toolPath}`);
});
it('finds version in cache with stable not supplied', async () => {
inputs['node-version'] = '12';
inSpy.mockImplementation(name => inputs[name]);
const toolPath = path.normalize('/cache/node/12.16.1/x64');
findSpy.mockImplementation(() => toolPath);
await main.run();
expect(logSpy).toHaveBeenCalledWith(`Found in cache @ ${toolPath}`);
});
it('finds version in cache and adds it to the path', async () => {
inputs['node-version'] = '12';
inSpy.mockImplementation(name => inputs[name]);
const toolPath = path.normalize('/cache/node/12.16.1/x64');
findSpy.mockImplementation(() => toolPath);
await main.run();
const expPath = path.join(toolPath, 'bin');
expect(cnSpy).toHaveBeenCalledWith(`::add-path::${expPath}${osm.EOL}`);
});
it('handles unhandled find error and reports error', async () => {
const errMsg = 'unhandled error message';
inputs['node-version'] = '12';
findSpy.mockImplementation(() => {
throw new Error(errMsg);
});
await main.run();
expect(cnSpy).toHaveBeenCalledWith('::error::' + errMsg + osm.EOL);
});
//--------------------------------------------------
// Manifest tests
//--------------------------------------------------
it('downloads a version from a manifest match', async () => {
os.platform = 'linux';
os.arch = 'x64';
// a version which is in the manifest
const versionSpec = '12.16.2';
const resolvedVersion = versionSpec;
inputs['node-version'] = versionSpec;
inputs['always-auth'] = false;
inputs['token'] = 'faketoken';
const expectedUrl =
'https://github.com/actions/node-versions/releases/download/12.16.2-20200507.95/node-12.16.2-linux-x64.tar.gz';
// ... but not in the local cache
findSpy.mockImplementation(() => '');
dlSpy.mockImplementation(async () => '/some/temp/path');
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();
const expPath = path.join(toolPath, 'bin');
expect(getExecOutputSpy).toHaveBeenCalledWith(
'node',
['--version'],
expect.anything()
);
expect(getExecOutputSpy).toHaveBeenCalledWith(
'npm',
['--version'],
expect.anything()
);
expect(getExecOutputSpy).toHaveBeenCalledWith(
'yarn',
['--version'],
expect.anything()
);
expect(dlSpy).toHaveBeenCalled();
expect(exSpy).toHaveBeenCalled();
expect(logSpy).toHaveBeenCalledWith(
`Acquiring ${resolvedVersion} - ${os.arch} from ${expectedUrl}`
);
expect(logSpy).toHaveBeenCalledWith(
`Attempting to download ${versionSpec}...`
);
expect(cnSpy).toHaveBeenCalledWith(`::add-path::${expPath}${osm.EOL}`);
});
it('falls back to a version from node dist from mirror', async () => {
os.platform = 'linux';
os.arch = 'x64';
// a version which is not in the manifest but is in node dist
const versionSpec = '11.15.0';
const mirror = 'https://my_mirror_url';
inputs['node-version'] = versionSpec;
inputs['always-auth'] = false;
inputs['token'] = 'faketoken';
inputs['mirror'] = mirror;
inputs['mirror-token'] = 'faketoken';
// ... but not in the local cache
findSpy.mockImplementation(() => '');
dlSpy.mockImplementation(async () => '/some/temp/path');
const toolPath = path.normalize('/cache/node/11.15.0/x64');
exSpy.mockImplementation(async () => '/some/other/temp/path');
cacheSpy.mockImplementation(async () => toolPath);
await main.run();
const expPath = path.join(toolPath, 'bin');
expect(getManifestSpy).toHaveBeenCalled();
expect(logSpy).toHaveBeenCalledWith(
`Attempting to download ${versionSpec}...`
);
expect(logSpy).toHaveBeenCalledWith(
`Not found in manifest. Falling back to download directly from ${mirror}`
);
expect(dlSpy).toHaveBeenCalled();
expect(exSpy).toHaveBeenCalled();
expect(cnSpy).toHaveBeenCalledWith(`::add-path::${expPath}${osm.EOL}`);
});
it('falls back to a version from node dist', async () => {
os.platform = 'linux';
os.arch = 'x64';
// a version which is not in the manifest but is in node dist
const versionSpec = '11.15.0';
inputs['node-version'] = versionSpec;
inputs['always-auth'] = false;
inputs['token'] = 'faketoken';
// ... but not in the local cache
findSpy.mockImplementation(() => '');
dlSpy.mockImplementation(async () => '/some/temp/path');
const toolPath = path.normalize('/cache/node/11.15.0/x64');
exSpy.mockImplementation(async () => '/some/other/temp/path');
cacheSpy.mockImplementation(async () => toolPath);
await main.run();
const expPath = path.join(toolPath, 'bin');
expect(getManifestSpy).toHaveBeenCalled();
expect(logSpy).toHaveBeenCalledWith(
`Attempting to download ${versionSpec}...`
);
expect(logSpy).toHaveBeenCalledWith(
'Not found in manifest. Falling back to download directly from Node'
);
expect(dlSpy).toHaveBeenCalled();
expect(exSpy).toHaveBeenCalled();
expect(cnSpy).toHaveBeenCalledWith(`::add-path::${expPath}${osm.EOL}`);
});
it('does not find a version that does not exist', async () => {
os.platform = 'linux';
os.arch = 'x64';
const versionSpec = '9.99.9';
inputs['node-version'] = versionSpec;
findSpy.mockImplementation(() => '');
await main.run();
expect(logSpy).toHaveBeenCalledWith(
'Not found in manifest. Falling back to download directly from Node'
);
expect(logSpy).toHaveBeenCalledWith(
`Attempting to download ${versionSpec}...`
);
expect(cnSpy).toHaveBeenCalledWith(
`::error::Unable to find Node version '${versionSpec}' for platform ${os.platform} and architecture ${os.arch}.${osm.EOL}`
);
});
it('reports a failed download', async () => {
const errMsg = 'unhandled download message';
os.platform = 'linux';
os.arch = 'x64';
// a version which is in the manifest
const versionSpec = '12.16.2';
const resolvedVersion = versionSpec;
inputs['node-version'] = versionSpec;
inputs['always-auth'] = false;
inputs['token'] = 'faketoken';
findSpy.mockImplementation(() => '');
dlSpy.mockImplementation(() => {
throw new Error(errMsg);
});
await main.run();
expect(cnSpy).toHaveBeenCalledWith(`::error::${errMsg}${osm.EOL}`);
});
it('reports when download failed but version exists', async () => {
os.platform = 'linux';
os.arch = 'x64';
// a version which is not in the manifest but is in node dist
const versionSpec = '11.15.0';
inputs['node-version'] = versionSpec;
inputs['always-auth'] = false;
inputs['token'] = 'faketoken';
// ... but not in the local cache
findSpy.mockImplementation(() => '');
dlSpy.mockImplementationOnce(async () => {
throw new tc.HTTPError(404);
});
await main.run();
expect(getManifestSpy).toHaveBeenCalled();
expect(logSpy).toHaveBeenCalledWith(
`Attempting to download ${versionSpec}...`
);
expect(logSpy).toHaveBeenCalledWith(
'Not found in manifest. Falling back to download directly from Node'
);
expect(dlSpy).toHaveBeenCalled();
expect(warningSpy).toHaveBeenCalledWith(
`Node version ${versionSpec} for platform ${os.platform} and architecture ${os.arch} was found but failed to download. ` +
'This usually happens when downloadable binaries are not fully updated at https://nodejs.org/. ' +
'To resolve this issue you may either fall back to the older version or try again later.'
);
});
it('acquires specified architecture of node', async () => {
for (const {arch, version, osSpec} of [
{arch: 'x86', version: '12.16.2', osSpec: 'win32'},
{arch: 'x86', version: '14.0.0', osSpec: 'win32'}
]) {
os.platform = osSpec;
os.arch = arch;
const fileExtension = os.platform === 'win32' ? '7z' : 'tar.gz';
const platform = {
linux: 'linux',
darwin: 'darwin',
win32: 'win'
}[os.platform];
inputs['node-version'] = version;
inputs['architecture'] = arch;
inputs['always-auth'] = false;
inputs['token'] = 'faketoken';
const expectedUrl =
arch === 'x64'
? `https://github.com/actions/node-versions/releases/download/${version}/node-${version}-${platform}-${arch}.zip`
: `https://nodejs.org/dist/v${version}/node-v${version}-${platform}-${arch}.${fileExtension}`;
// ... but not in the local cache
findSpy.mockImplementation(() => '');
dlSpy.mockImplementation(async () => '/some/temp/path');
const toolPath = path.normalize(`/cache/node/${version}/${arch}`);
exSpy.mockImplementation(async () => '/some/other/temp/path');
cacheSpy.mockImplementation(async () => toolPath);
await main.run();
expect(dlSpy).toHaveBeenCalled();
expect(logSpy).toHaveBeenCalledWith(
`Acquiring ${version} - ${arch} from ${expectedUrl}`
);
}
}, 100000);
describe('check-latest flag', () => {
it('use local version and dont check manifest if check-latest is not specified', async () => {
os.platform = 'linux';
os.arch = 'x64';
inputs['node-version'] = '12';
inputs['check-latest'] = 'false';
const toolPath = path.normalize('/cache/node/12.16.1/x64');
findSpy.mockReturnValue(toolPath);
await main.run();
expect(logSpy).toHaveBeenCalledWith(`Found in cache @ ${toolPath}`);
expect(logSpy).not.toHaveBeenCalledWith(
'Attempt to resolve the latest version from manifest...'
);
expect(dbgSpy).not.toHaveBeenCalledWith('No manifest cached');
expect(dbgSpy).not.toHaveBeenCalledWith(
'Getting manifest from actions/node-versions@main'
);
});
it('check latest version and resolve it from local cache', async () => {
os.platform = 'linux';
os.arch = 'x64';
inputs['node-version'] = '12';
inputs['check-latest'] = 'true';
const toolPath = path.normalize('/cache/node/12.16.2/x64');
findSpy.mockReturnValue(toolPath);
dlSpy.mockImplementation(async () => '/some/temp/path');
exSpy.mockImplementation(async () => '/some/other/temp/path');
cacheSpy.mockImplementation(async () => toolPath);
await main.run();
expect(logSpy).toHaveBeenCalledWith(
'Attempt to resolve the latest version from manifest...'
);
expect(dbgSpy).toHaveBeenCalledWith('No manifest cached');
expect(dbgSpy).toHaveBeenCalledWith(
'Getting manifest from actions/node-versions@main'
);
expect(logSpy).toHaveBeenCalledWith("Resolved as '12.16.2'");
expect(logSpy).toHaveBeenCalledWith(`Found in cache @ ${toolPath}`);
});
it('check latest version and install it from manifest', async () => {
os.platform = 'linux';
os.arch = 'x64';
inputs['node-version'] = '12';
inputs['check-latest'] = 'true';
findSpy.mockImplementation(() => '');
dlSpy.mockImplementation(async () => '/some/temp/path');
const toolPath = path.normalize('/cache/node/12.16.2/x64');
exSpy.mockImplementation(async () => '/some/other/temp/path');
cacheSpy.mockImplementation(async () => toolPath);
const expectedUrl =
'https://github.com/actions/node-versions/releases/download/12.16.2-20200507.95/node-12.16.2-linux-x64.tar.gz';
await main.run();
expect(logSpy).toHaveBeenCalledWith(
'Attempt to resolve the latest version from manifest...'
);
expect(dbgSpy).toHaveBeenCalledWith('No manifest cached');
expect(dbgSpy).toHaveBeenCalledWith(
'Getting manifest from actions/node-versions@main'
);
expect(logSpy).toHaveBeenCalledWith("Resolved as '12.16.2'");
expect(logSpy).toHaveBeenCalledWith(
`Acquiring 12.16.2 - ${os.arch} from ${expectedUrl}`
);
expect(logSpy).toHaveBeenCalledWith('Extracting ...');
});
it('fallback to dist if version if not found in manifest', async () => {
os.platform = 'linux';
os.arch = 'x64';
// a version which is not in the manifest but is in node dist
const versionSpec = '11';
inputs['node-version'] = versionSpec;
inputs['check-latest'] = 'true';
inputs['always-auth'] = false;
inputs['token'] = 'faketoken';
// ... but not in the local cache
findSpy.mockImplementation(() => '');
dlSpy.mockImplementation(async () => '/some/temp/path');
const toolPath = path.normalize('/cache/node/11.11.0/x64');
exSpy.mockImplementation(async () => '/some/other/temp/path');
cacheSpy.mockImplementation(async () => toolPath);
await main.run();
const expPath = path.join(toolPath, 'bin');
expect(dlSpy).toHaveBeenCalled();
expect(exSpy).toHaveBeenCalled();
expect(logSpy).toHaveBeenCalledWith(
'Attempt to resolve the latest version from manifest...'
);
expect(dbgSpy).toHaveBeenCalledWith('No manifest cached');
expect(dbgSpy).toHaveBeenCalledWith(
'Getting manifest from actions/node-versions@main'
);
expect(logSpy).toHaveBeenCalledWith(
`Failed to resolve version ${versionSpec} from manifest`
);
expect(logSpy).toHaveBeenCalledWith(
`Attempting to download ${versionSpec}...`
);
expect(cnSpy).toHaveBeenCalledWith(`::add-path::${expPath}${osm.EOL}`);
});
it('fallback to dist if manifest is not available', async () => {
os.platform = 'linux';
os.arch = 'x64';
// a version which is not in the manifest but is in node dist
const versionSpec = '12';
inputs['node-version'] = versionSpec;
inputs['check-latest'] = 'true';
inputs['always-auth'] = false;
inputs['token'] = 'faketoken';
// ... but not in the local cache
findSpy.mockImplementation(() => '');
getManifestSpy.mockImplementation(() => {
throw new Error('Unable to download manifest');
});
dlSpy.mockImplementation(async () => '/some/temp/path');
const toolPath = path.normalize('/cache/node/12.11.0/x64');
exSpy.mockImplementation(async () => '/some/other/temp/path');
cacheSpy.mockImplementation(async () => toolPath);
await main.run();
const expPath = path.join(toolPath, 'bin');
expect(dlSpy).toHaveBeenCalled();
expect(exSpy).toHaveBeenCalled();
expect(logSpy).toHaveBeenCalledWith(
'Attempt to resolve the latest version from manifest...'
);
expect(logSpy).toHaveBeenCalledWith(
'Unable to resolve version from manifest...'
);
expect(logSpy).toHaveBeenCalledWith(
`Failed to resolve version ${versionSpec} from manifest`
);
expect(logSpy).toHaveBeenCalledWith(
`Attempting to download ${versionSpec}...`
);
expect(cnSpy).toHaveBeenCalledWith(`::add-path::${expPath}${osm.EOL}`);
});
});
describe('LTS version', () => {
beforeEach(() => {
os.platform = 'linux';
os.arch = 'x64';
inputs.stable = 'true';
});
it.each([
['erbium', '12.16.2'],
['*', '14.0.0'],
['-1', '12.16.2']
])(
'find latest LTS version and resolve it from local cache (lts/%s)',
async (lts, expectedVersion) => {
// arrange
inputs['node-version'] = `lts/${lts}`;
const toolPath = path.normalize(`/cache/node/${expectedVersion}/x64`);
findSpy.mockReturnValue(toolPath);
// act
await main.run();
// assert
expect(logSpy).toHaveBeenCalledWith(
'Attempt to resolve LTS alias from manifest...'
);
expect(dbgSpy).toHaveBeenCalledWith(
'Getting manifest from actions/node-versions@main'
);
expect(dbgSpy).not.toHaveBeenCalledWith('No manifest cached');
expect(dbgSpy).toHaveBeenCalledWith(
`LTS alias '${lts}' for Node version 'lts/${lts}'`
);
expect(dbgSpy).toHaveBeenCalledWith(
`Found LTS release '${expectedVersion}' for Node version 'lts/${lts}'`
);
expect(logSpy).toHaveBeenCalledWith(`Found in cache @ ${toolPath}`);
expect(cnSpy).toHaveBeenCalledWith(
`::add-path::${path.join(toolPath, 'bin')}${osm.EOL}`
);
}
);
it.each([
[
'erbium',
'12.16.2',
'https://github.com/actions/node-versions/releases/download/12.16.2-20200507.95/node-12.16.2-linux-x64.tar.gz'
],
[
'*',
'14.0.0',
'https://github.com/actions/node-versions/releases/download/14.0.0-20200507.99/node-14.0.0-linux-x64.tar.gz'
],
[
'-1',
'12.16.2',
'https://github.com/actions/node-versions/releases/download/12.16.2-20200507.95/node-12.16.2-linux-x64.tar.gz'
]
])(
'find latest LTS version and install it from manifest (lts/%s)',
async (lts, expectedVersion, expectedUrl) => {
// arrange
inputs['node-version'] = `lts/${lts}`;
const toolPath = path.normalize(`/cache/node/${expectedVersion}/x64`);
findSpy.mockImplementation(() => '');
dlSpy.mockImplementation(async () => '/some/temp/path');
exSpy.mockImplementation(async () => '/some/other/temp/path');
cacheSpy.mockImplementation(async () => toolPath);
const expectedMajor = expectedVersion.split('.')[0];
// act
await main.run();
// assert
expect(logSpy).toHaveBeenCalledWith(
'Attempt to resolve LTS alias from manifest...'
);
expect(dbgSpy).toHaveBeenCalledWith(
'Getting manifest from actions/node-versions@main'
);
expect(dbgSpy).not.toHaveBeenCalledWith('No manifest cached');
expect(dbgSpy).toHaveBeenCalledWith(
`LTS alias '${lts}' for Node version 'lts/${lts}'`
);
expect(dbgSpy).toHaveBeenCalledWith(
`Found LTS release '${expectedVersion}' for Node version 'lts/${lts}'`
);
expect(logSpy).toHaveBeenCalledWith(
`Attempting to download ${expectedMajor}...`
);
expect(logSpy).toHaveBeenCalledWith(
`Acquiring ${expectedVersion} - ${os.arch} from ${expectedUrl}`
);
expect(logSpy).toHaveBeenCalledWith('Extracting ...');
expect(logSpy).toHaveBeenCalledWith('Adding to the cache ...');
expect(cnSpy).toHaveBeenCalledWith(
`::add-path::${path.join(toolPath, 'bin')}${osm.EOL}`
);
}
);
it('fail with unable to parse LTS alias (lts/)', async () => {
// arrange
inputs['node-version'] = 'lts/';
findSpy.mockImplementation(() => '');
// act
await main.run();
// assert
expect(logSpy).toHaveBeenCalledWith(
'Attempt to resolve LTS alias from manifest...'
);
expect(dbgSpy).toHaveBeenCalledWith(
'Getting manifest from actions/node-versions@main'
);
expect(cnSpy).toHaveBeenCalledWith(
`::error::Unable to parse LTS alias for Node version 'lts/'${osm.EOL}`
);
});
it('fail to find LTS version (lts/unknown)', async () => {
// arrange
inputs['node-version'] = 'lts/unknown';
findSpy.mockImplementation(() => '');
// act
await main.run();
// assert
expect(logSpy).toHaveBeenCalledWith(
'Attempt to resolve LTS alias from manifest...'
);
expect(dbgSpy).toHaveBeenCalledWith(
'Getting manifest from actions/node-versions@main'
);
expect(dbgSpy).toHaveBeenCalledWith(
`LTS alias 'unknown' for Node version 'lts/unknown'`
);
expect(cnSpy).toHaveBeenCalledWith(
`::error::Unable to find LTS release 'unknown' for Node version 'lts/unknown'.${osm.EOL}`
);
});
it('fail if manifest is not available', async () => {
// arrange
inputs['node-version'] = 'lts/erbium';
// ... but not in the local cache
findSpy.mockImplementation(() => '');
getManifestSpy.mockImplementation(() => {
throw new Error('Unable to download manifest');
});
// act
await main.run();
// assert
expect(logSpy).toHaveBeenCalledWith(
'Attempt to resolve LTS alias from manifest...'
);
expect(dbgSpy).toHaveBeenCalledWith(
'Getting manifest from actions/node-versions@main'
);
expect(cnSpy).toHaveBeenCalledWith(
`::error::Unable to download manifest${osm.EOL}`
);
});
});
describe('latest alias syntax', () => {
it.each(['latest', 'current', 'node'])(
'download the %s version if alias is provided',
async inputVersion => {
// Arrange
inputs['node-version'] = inputVersion;
os.platform = 'darwin';
os.arch = 'x64';
findSpy.mockImplementation(() => '');
getManifestSpy.mockImplementation(() => {
throw new Error('Unable to download manifest');
});
// Act
await main.run();
// assert
expect(logSpy).toHaveBeenCalledWith('Unable to download manifest');
expect(logSpy).toHaveBeenCalledWith('getting latest node version...');
}
);
});
describe('latest alias syntax from cache', () => {
it.each(['latest', 'current', 'node'])(
'download the %s version if alias is provided',
async inputVersion => {
// Arrange
inputs['node-version'] = inputVersion;
const expectedVersion = nodeTestDist[0];
os.platform = 'darwin';
os.arch = 'x64';
const toolPath = path.normalize(
`/cache/node/${expectedVersion.version}/x64`
);
findSpy.mockImplementation(() => toolPath);
// Act
await main.run();
// assert
expect(logSpy).toHaveBeenCalledWith('getting latest node version...');
expect(logSpy).toHaveBeenCalledWith(`Found in cache @ ${toolPath}`);
}
);
});
it('acquires specified architecture of node from mirror', async () => {
for (const {arch, version, osSpec} of [
{arch: 'x86', version: '12.16.2', osSpec: 'win32'},
{arch: 'x86', version: '14.0.0', osSpec: 'win32'}
]) {
os.platform = osSpec;
os.arch = arch;
const fileExtension = os.platform === 'win32' ? '7z' : 'tar.gz';
const platform = {
linux: 'linux',
darwin: 'darwin',
win32: 'win'
}[os.platform];
inputs['node-version'] = version;
inputs['architecture'] = arch;
inputs['always-auth'] = false;
inputs['token'] = 'faketoken';
inputs['mirror'] = 'https://my_mirror_url';
inputs['mirror-token'] = 'faketoken';
const expectedUrl =
arch === 'x64'
? `https://github.com/actions/node-versions/releases/download/${version}/node-${version}-${platform}-${arch}.zip`
: `https://my_mirror_url/dist/v${version}/node-v${version}-${platform}-${arch}.${fileExtension}`;
// ... but not in the local cache
findSpy.mockImplementation(() => '');
dlSpy.mockImplementation(async () => '/some/temp/path');
const toolPath = path.normalize(`/cache/node/${version}/${arch}`);
exSpy.mockImplementation(async () => '/some/other/temp/path');
cacheSpy.mockImplementation(async () => toolPath);
await main.run();
expect(dlSpy).toHaveBeenCalled();
expect(logSpy).toHaveBeenCalledWith(
`Acquiring ${version} - ${arch} from ${expectedUrl}`
);
}
}, 100000);
});