mirror of
https://github.com/actions/setup-node.git
synced 2026-09-21 08:43:25 +00:00
Merge 07c11e9db09b3ce2094ba3bd71cfc34540fe1402 into db0fb3a98ed314b69026b6e8c9be26a7e2fe65a8
This commit is contained in:
commit
86f345fa55
@ -14,6 +14,9 @@ import osm from 'os';
|
|||||||
import path from 'path';
|
import path from 'path';
|
||||||
|
|
||||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||||
|
const initialRunnerTemp = process.env['RUNNER_TEMP'];
|
||||||
|
const nodeVersionsManifestUrl =
|
||||||
|
'https://raw.githubusercontent.com/actions/node-versions/main/versions-manifest.json';
|
||||||
|
|
||||||
// Mock @actions modules before importing anything that depends on them
|
// Mock @actions modules before importing anything that depends on them
|
||||||
jest.unstable_mockModule('@actions/core', () => ({
|
jest.unstable_mockModule('@actions/core', () => ({
|
||||||
@ -164,6 +167,7 @@ describe('setup-node', () => {
|
|||||||
console.log('::stop-commands::stoptoken'); // Disable executing of runner commands when running tests in actions
|
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_PATH'] = ''; // Stub out ENV file functionality so we can verify it writes to standard out
|
||||||
process.env['GITHUB_OUTPUT'] = ''; // Stub out ENV file functionality so we can verify it writes to standard out
|
process.env['GITHUB_OUTPUT'] = ''; // Stub out ENV file functionality so we can verify it writes to standard out
|
||||||
|
delete process.env['RUNNER_TEMP'];
|
||||||
inputs = {};
|
inputs = {};
|
||||||
inSpy = core.getInput as jest.Mock;
|
inSpy = core.getInput as jest.Mock;
|
||||||
inSpy.mockImplementation((name: any) => inputs[name]);
|
inSpy.mockImplementation((name: any) => inputs[name]);
|
||||||
@ -233,7 +237,9 @@ describe('setup-node', () => {
|
|||||||
|
|
||||||
getJsonSpy.mockImplementation((url: any) => {
|
getJsonSpy.mockImplementation((url: any) => {
|
||||||
let res: any;
|
let res: any;
|
||||||
if (url.includes('/rc')) {
|
if (url === nodeVersionsManifestUrl) {
|
||||||
|
throw new Error('Unable to download raw manifest');
|
||||||
|
} else if (url.includes('/rc')) {
|
||||||
res = <INodeVersion[]>nodeTestDistRc;
|
res = <INodeVersion[]>nodeTestDistRc;
|
||||||
} else if (url.includes('/nightly')) {
|
} else if (url.includes('/nightly')) {
|
||||||
res = <INodeVersion[]>nodeTestDistNightly;
|
res = <INodeVersion[]>nodeTestDistNightly;
|
||||||
@ -273,6 +279,9 @@ describe('setup-node', () => {
|
|||||||
|
|
||||||
afterAll(async () => {
|
afterAll(async () => {
|
||||||
console.log('::stoptoken::'); // Re-enable executing of runner commands when running tests in actions
|
console.log('::stoptoken::'); // Re-enable executing of runner commands when running tests in actions
|
||||||
|
if (initialRunnerTemp !== undefined) {
|
||||||
|
process.env['RUNNER_TEMP'] = initialRunnerTemp;
|
||||||
|
}
|
||||||
jest.restoreAllMocks();
|
jest.restoreAllMocks();
|
||||||
}, 100000);
|
}, 100000);
|
||||||
|
|
||||||
@ -649,7 +658,7 @@ describe('setup-node', () => {
|
|||||||
expect(logSpy).toHaveBeenCalledWith(
|
expect(logSpy).toHaveBeenCalledWith(
|
||||||
'Attempt to resolve the latest version from manifest...'
|
'Attempt to resolve the latest version from manifest...'
|
||||||
);
|
);
|
||||||
expect(dbgSpy).toHaveBeenCalledWith('No manifest cached');
|
expect(dbgSpy).not.toHaveBeenCalledWith('No manifest cached');
|
||||||
expect(dbgSpy).toHaveBeenCalledWith(
|
expect(dbgSpy).toHaveBeenCalledWith(
|
||||||
'Getting manifest from actions/node-versions@main'
|
'Getting manifest from actions/node-versions@main'
|
||||||
);
|
);
|
||||||
@ -677,7 +686,7 @@ describe('setup-node', () => {
|
|||||||
expect(logSpy).toHaveBeenCalledWith(
|
expect(logSpy).toHaveBeenCalledWith(
|
||||||
'Attempt to resolve the latest version from manifest...'
|
'Attempt to resolve the latest version from manifest...'
|
||||||
);
|
);
|
||||||
expect(dbgSpy).toHaveBeenCalledWith('No manifest cached');
|
expect(dbgSpy).not.toHaveBeenCalledWith('No manifest cached');
|
||||||
expect(dbgSpy).toHaveBeenCalledWith(
|
expect(dbgSpy).toHaveBeenCalledWith(
|
||||||
'Getting manifest from actions/node-versions@main'
|
'Getting manifest from actions/node-versions@main'
|
||||||
);
|
);
|
||||||
@ -716,7 +725,7 @@ describe('setup-node', () => {
|
|||||||
expect(logSpy).toHaveBeenCalledWith(
|
expect(logSpy).toHaveBeenCalledWith(
|
||||||
'Attempt to resolve the latest version from manifest...'
|
'Attempt to resolve the latest version from manifest...'
|
||||||
);
|
);
|
||||||
expect(dbgSpy).toHaveBeenCalledWith('No manifest cached');
|
expect(dbgSpy).not.toHaveBeenCalledWith('No manifest cached');
|
||||||
expect(dbgSpy).toHaveBeenCalledWith(
|
expect(dbgSpy).toHaveBeenCalledWith(
|
||||||
'Getting manifest from actions/node-versions@main'
|
'Getting manifest from actions/node-versions@main'
|
||||||
);
|
);
|
||||||
@ -1089,6 +1098,166 @@ describe('setup-node', () => {
|
|||||||
}, 10000);
|
}, 10000);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('manifest retrieval', () => {
|
||||||
|
let runnerTemp: string;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
runnerTemp = fs.mkdtempSync(path.join(osm.tmpdir(), 'setup-node-test-'));
|
||||||
|
process.env['RUNNER_TEMP'] = runnerTemp;
|
||||||
|
os.platform = 'linux';
|
||||||
|
os.arch = 'x64';
|
||||||
|
inputs['node-version'] = 'lts/erbium';
|
||||||
|
findSpy.mockImplementation(() => '');
|
||||||
|
getJsonSpy.mockImplementation((url: string) => ({
|
||||||
|
result:
|
||||||
|
url === nodeVersionsManifestUrl ? nodeTestManifest : nodeTestDist
|
||||||
|
}));
|
||||||
|
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);
|
||||||
|
getExecOutputSpy.mockImplementation(async () => ({
|
||||||
|
stdout: 'v12.16.2\n',
|
||||||
|
stderr: '',
|
||||||
|
exitCode: 0
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
fs.rmSync(runnerTemp, {recursive: true, force: true});
|
||||||
|
delete process.env['RUNNER_TEMP'];
|
||||||
|
});
|
||||||
|
|
||||||
|
it('fetches the raw manifest without a GitHub API call', async () => {
|
||||||
|
await main.run();
|
||||||
|
|
||||||
|
expect(getJsonSpy).toHaveBeenCalledWith(nodeVersionsManifestUrl);
|
||||||
|
expect(getManifestSpy).not.toHaveBeenCalled();
|
||||||
|
expect(setFailedSpy).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('reuses the manifest across action invocations', async () => {
|
||||||
|
await main.run();
|
||||||
|
getJsonSpy.mockClear();
|
||||||
|
getManifestSpy.mockClear();
|
||||||
|
|
||||||
|
await main.run();
|
||||||
|
|
||||||
|
expect(getJsonSpy).not.toHaveBeenCalled();
|
||||||
|
expect(getManifestSpy).not.toHaveBeenCalled();
|
||||||
|
expect(setFailedSpy).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('refreshes the manifest when check-latest is enabled', async () => {
|
||||||
|
let installedVersion = '12.16.2';
|
||||||
|
let manifest = <IToolRelease[]>nodeTestManifest;
|
||||||
|
getJsonSpy.mockImplementation((url: string) => ({
|
||||||
|
result: url === nodeVersionsManifestUrl ? manifest : nodeTestDist
|
||||||
|
}));
|
||||||
|
cacheSpy.mockImplementation(
|
||||||
|
async (
|
||||||
|
_sourceDirectory: string,
|
||||||
|
_tool: string,
|
||||||
|
version: string,
|
||||||
|
arch: string
|
||||||
|
) => {
|
||||||
|
installedVersion = version;
|
||||||
|
return path.normalize(`/cache/node/${version}/${arch}`);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
getExecOutputSpy.mockImplementation(async () => ({
|
||||||
|
stdout: `v${installedVersion}\n`,
|
||||||
|
stderr: '',
|
||||||
|
exitCode: 0
|
||||||
|
}));
|
||||||
|
inputs['node-version'] = '12';
|
||||||
|
|
||||||
|
await main.run();
|
||||||
|
|
||||||
|
const latestVersion = '12.17.0';
|
||||||
|
manifest = [
|
||||||
|
{
|
||||||
|
version: latestVersion,
|
||||||
|
stable: true,
|
||||||
|
release_url: `https://github.com/actions/node-versions/releases/tag/${latestVersion}`,
|
||||||
|
files: [
|
||||||
|
{
|
||||||
|
filename: `node-${latestVersion}-linux-x64.tar.gz`,
|
||||||
|
arch: 'x64',
|
||||||
|
platform: 'linux',
|
||||||
|
download_url: `https://github.com/actions/node-versions/releases/download/${latestVersion}/node-${latestVersion}-linux-x64.tar.gz`
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
...nodeTestManifest
|
||||||
|
];
|
||||||
|
inputs['check-latest'] = 'true';
|
||||||
|
|
||||||
|
await main.run();
|
||||||
|
|
||||||
|
expect(getJsonSpy).toHaveBeenCalledTimes(2);
|
||||||
|
expect(logSpy).toHaveBeenCalledWith(`Resolved as '${latestVersion}'`);
|
||||||
|
expect(setFailedSpy).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('reuses the LTS manifest when check-latest is enabled', async () => {
|
||||||
|
inputs['check-latest'] = 'true';
|
||||||
|
|
||||||
|
await main.run();
|
||||||
|
|
||||||
|
expect(getJsonSpy).toHaveBeenCalledTimes(1);
|
||||||
|
expect(getManifestSpy).not.toHaveBeenCalled();
|
||||||
|
expect(setFailedSpy).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it.each([
|
||||||
|
['empty', '[]'],
|
||||||
|
['malformed', '{']
|
||||||
|
])('replaces an %s cached manifest', async (_, cachedManifest) => {
|
||||||
|
await main.run();
|
||||||
|
const [manifestFile] = fs.readdirSync(runnerTemp);
|
||||||
|
fs.writeFileSync(path.join(runnerTemp, manifestFile), cachedManifest);
|
||||||
|
getJsonSpy.mockClear();
|
||||||
|
getManifestSpy.mockClear();
|
||||||
|
|
||||||
|
await main.run();
|
||||||
|
|
||||||
|
expect(getJsonSpy).toHaveBeenCalledWith(nodeVersionsManifestUrl);
|
||||||
|
expect(getManifestSpy).not.toHaveBeenCalled();
|
||||||
|
expect(setFailedSpy).not.toHaveBeenCalled();
|
||||||
|
|
||||||
|
getJsonSpy.mockClear();
|
||||||
|
await main.run();
|
||||||
|
expect(getJsonSpy).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('continues when the manifest cannot be cached', async () => {
|
||||||
|
fs.rmSync(runnerTemp, {recursive: true, force: true});
|
||||||
|
|
||||||
|
await main.run();
|
||||||
|
|
||||||
|
expect(getJsonSpy).toHaveBeenCalledWith(nodeVersionsManifestUrl);
|
||||||
|
expect(getManifestSpy).not.toHaveBeenCalled();
|
||||||
|
expect(setFailedSpy).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it.each([
|
||||||
|
['fails', () => Promise.reject(new Error('Unable to download manifest'))],
|
||||||
|
['is invalid', () => Promise.resolve({result: []})]
|
||||||
|
])(
|
||||||
|
'falls back to the GitHub API when the raw manifest %s',
|
||||||
|
async (_, result) => {
|
||||||
|
getJsonSpy.mockImplementationOnce(result);
|
||||||
|
|
||||||
|
await main.run();
|
||||||
|
|
||||||
|
expect(getJsonSpy).toHaveBeenCalledWith(nodeVersionsManifestUrl);
|
||||||
|
expect(getManifestSpy).toHaveBeenCalledTimes(1);
|
||||||
|
expect(setFailedSpy).not.toHaveBeenCalled();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
describe('node version verification', () => {
|
describe('node version verification', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
os.platform = 'linux';
|
os.platform = 'linux';
|
||||||
|
|||||||
105
dist/setup/index.js
vendored
105
dist/setup/index.js
vendored
@ -85937,6 +85937,7 @@ class Batch {
|
|||||||
//# sourceMappingURL=Batch.js.map
|
//# sourceMappingURL=Batch.js.map
|
||||||
;// CONCATENATED MODULE: external "node:fs"
|
;// CONCATENATED MODULE: external "node:fs"
|
||||||
const external_node_fs_namespaceObject = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("node:fs");
|
const external_node_fs_namespaceObject = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("node:fs");
|
||||||
|
var external_node_fs_default = /*#__PURE__*/__nccwpck_require__.n(external_node_fs_namespaceObject);
|
||||||
;// CONCATENATED MODULE: ./node_modules/@azure/storage-blob/dist/esm/utils/utils.js
|
;// CONCATENATED MODULE: ./node_modules/@azure/storage-blob/dist/esm/utils/utils.js
|
||||||
// Copyright (c) Microsoft Corporation.
|
// Copyright (c) Microsoft Corporation.
|
||||||
// Licensed under the MIT License.
|
// Licensed under the MIT License.
|
||||||
@ -100979,12 +100980,23 @@ class NightlyNodejs extends BasePrereleaseNodejs {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
;// CONCATENATED MODULE: external "node:path"
|
||||||
|
const external_node_path_namespaceObject = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("node:path");
|
||||||
|
var external_node_path_default = /*#__PURE__*/__nccwpck_require__.n(external_node_path_namespaceObject);
|
||||||
;// CONCATENATED MODULE: ./src/distributions/official_builds/official_builds.ts
|
;// CONCATENATED MODULE: ./src/distributions/official_builds/official_builds.ts
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const nodeVersionsManifestFile = 'setup-node-versions-manifest.json';
|
||||||
|
const nodeVersionsManifestUrl = 'https://raw.githubusercontent.com/actions/node-versions/main/versions-manifest.json';
|
||||||
|
const invalidManifestMessage = 'The manifest fetched is empty, truncated, or does not contain any valid tool release entries.';
|
||||||
|
/** @param {unknown} manifest */
|
||||||
|
function isValidManifest(manifest) {
|
||||||
|
return Array.isArray(manifest) && manifest.length > 0;
|
||||||
|
}
|
||||||
class OfficialBuilds extends BaseDistribution {
|
class OfficialBuilds extends BaseDistribution {
|
||||||
constructor(nodeInfo) {
|
constructor(nodeInfo) {
|
||||||
super(nodeInfo);
|
super(nodeInfo);
|
||||||
@ -101007,7 +101019,16 @@ class OfficialBuilds extends BaseDistribution {
|
|||||||
}
|
}
|
||||||
if (this.nodeInfo.checkLatest) {
|
if (this.nodeInfo.checkLatest) {
|
||||||
core_info('Attempt to resolve the latest version from manifest...');
|
core_info('Attempt to resolve the latest version from manifest...');
|
||||||
const resolvedVersion = await this.resolveVersionFromManifest(this.nodeInfo.versionSpec, this.nodeInfo.stable, osArch, manifest);
|
let resolvedVersion;
|
||||||
|
try {
|
||||||
|
manifest ??= await this.getManifest();
|
||||||
|
const info = await this.getInfoFromManifest(this.nodeInfo.versionSpec, this.nodeInfo.stable, osArch, manifest);
|
||||||
|
resolvedVersion = info?.resolvedVersion;
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
core_info('Unable to resolve version from manifest...');
|
||||||
|
core_debug(error.message);
|
||||||
|
}
|
||||||
if (resolvedVersion) {
|
if (resolvedVersion) {
|
||||||
this.nodeInfo.versionSpec = resolvedVersion;
|
this.nodeInfo.versionSpec = resolvedVersion;
|
||||||
core_info(`Resolved as '${resolvedVersion}'`);
|
core_info(`Resolved as '${resolvedVersion}'`);
|
||||||
@ -101058,14 +101079,14 @@ class OfficialBuilds extends BaseDistribution {
|
|||||||
}
|
}
|
||||||
const installedDir = toolPath;
|
const installedDir = toolPath;
|
||||||
if (this.osPlat != 'win32') {
|
if (this.osPlat != 'win32') {
|
||||||
toolPath = external_path_default().join(toolPath, 'bin');
|
toolPath = external_node_path_default().join(toolPath, 'bin');
|
||||||
}
|
}
|
||||||
addPath(toolPath);
|
addPath(toolPath);
|
||||||
await this.verifyNodeVersion(installedDir);
|
await this.verifyNodeVersion(installedDir);
|
||||||
}
|
}
|
||||||
addToolPath(toolPath) {
|
addToolPath(toolPath) {
|
||||||
if (this.osPlat != 'win32') {
|
if (this.osPlat != 'win32') {
|
||||||
toolPath = external_path_default().join(toolPath, 'bin');
|
toolPath = external_node_path_default().join(toolPath, 'bin');
|
||||||
}
|
}
|
||||||
addPath(toolPath);
|
addPath(toolPath);
|
||||||
}
|
}
|
||||||
@ -101104,6 +101125,28 @@ class OfficialBuilds extends BaseDistribution {
|
|||||||
return `${url}/dist`;
|
return `${url}/dist`;
|
||||||
}
|
}
|
||||||
async getManifest() {
|
async getManifest() {
|
||||||
|
const runnerTemp = process.env['RUNNER_TEMP'];
|
||||||
|
const manifestPath = runnerTemp
|
||||||
|
? external_node_path_default().join(runnerTemp, nodeVersionsManifestFile)
|
||||||
|
: undefined;
|
||||||
|
const cachedManifest = this.nodeInfo.checkLatest
|
||||||
|
? undefined
|
||||||
|
: this.getCachedManifest(manifestPath);
|
||||||
|
if (cachedManifest) {
|
||||||
|
return cachedManifest;
|
||||||
|
}
|
||||||
|
core_debug(`Getting manifest from ${nodeVersionsManifestUrl}`);
|
||||||
|
try {
|
||||||
|
const { result } = await this.httpClient.getJson(nodeVersionsManifestUrl);
|
||||||
|
if (!isValidManifest(result)) {
|
||||||
|
throw new Error(invalidManifestMessage);
|
||||||
|
}
|
||||||
|
this.cacheManifest(manifestPath, result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
core_debug(`Unable to get manifest from ${nodeVersionsManifestUrl}: ${error instanceof Error ? error.message : String(error)}`);
|
||||||
|
}
|
||||||
let lastError;
|
let lastError;
|
||||||
const maxAttempts = 3;
|
const maxAttempts = 3;
|
||||||
core_debug(`Getting manifest from actions/node-versions@main`);
|
core_debug(`Getting manifest from actions/node-versions@main`);
|
||||||
@ -101112,13 +101155,14 @@ class OfficialBuilds extends BaseDistribution {
|
|||||||
const manifest = await getManifestFromRepo('actions', 'node-versions', this.nodeInfo.mirror && this.nodeInfo.mirrorToken
|
const manifest = await getManifestFromRepo('actions', 'node-versions', this.nodeInfo.mirror && this.nodeInfo.mirrorToken
|
||||||
? this.nodeInfo.mirrorToken
|
? this.nodeInfo.mirrorToken
|
||||||
: this.nodeInfo.auth, 'main');
|
: this.nodeInfo.auth, 'main');
|
||||||
if (Array.isArray(manifest) && manifest.length > 0) {
|
if (isValidManifest(manifest)) {
|
||||||
|
this.cacheManifest(manifestPath, manifest);
|
||||||
return manifest;
|
return manifest;
|
||||||
}
|
}
|
||||||
lastError = new Error(`The manifest fetched is empty, truncated, or does not contain any valid tool release entries.`);
|
lastError = new Error(invalidManifestMessage);
|
||||||
}
|
}
|
||||||
catch (err) {
|
catch (error) {
|
||||||
lastError = err instanceof Error ? err : new Error(String(err));
|
lastError = error instanceof Error ? error : new Error(String(error));
|
||||||
}
|
}
|
||||||
core_debug(`Attempt ${attempt}/${maxAttempts} to fetch the manifest failed: ${lastError.message}`);
|
core_debug(`Attempt ${attempt}/${maxAttempts} to fetch the manifest failed: ${lastError.message}`);
|
||||||
if (attempt < maxAttempts) {
|
if (attempt < maxAttempts) {
|
||||||
@ -101128,6 +101172,41 @@ class OfficialBuilds extends BaseDistribution {
|
|||||||
}
|
}
|
||||||
throw new Error(`Failed to fetch a valid manifest after ${maxAttempts} attempts. Last error: ${lastError?.message}`);
|
throw new Error(`Failed to fetch a valid manifest after ${maxAttempts} attempts. Last error: ${lastError?.message}`);
|
||||||
}
|
}
|
||||||
|
/** @param {string | undefined} manifestPath */
|
||||||
|
getCachedManifest(manifestPath) {
|
||||||
|
if (!manifestPath) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const manifest = JSON.parse(external_node_fs_default().readFileSync(manifestPath, 'utf8'));
|
||||||
|
if (isValidManifest(manifest)) {
|
||||||
|
core_debug(`Found manifest in ${manifestPath}`);
|
||||||
|
return manifest;
|
||||||
|
}
|
||||||
|
core_debug(`Ignoring invalid manifest in ${manifestPath}`);
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
if (error.code !== 'ENOENT') {
|
||||||
|
core_debug(`Unable to read manifest from ${manifestPath}: ${error instanceof Error ? error.message : String(error)}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @param {string | undefined} manifestPath
|
||||||
|
* @param {tc.IToolRelease[]} manifest
|
||||||
|
*/
|
||||||
|
cacheManifest(manifestPath, manifest) {
|
||||||
|
if (!manifestPath) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
external_node_fs_default().writeFileSync(manifestPath, JSON.stringify(manifest));
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
core_debug(`Unable to cache manifest in ${manifestPath}: ${error instanceof Error ? error.message : String(error)}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
resolveLtsAliasFromManifest(versionSpec, stable, manifest) {
|
resolveLtsAliasFromManifest(versionSpec, stable, manifest) {
|
||||||
const alias = versionSpec.split('lts/')[1]?.toLowerCase();
|
const alias = versionSpec.split('lts/')[1]?.toLowerCase();
|
||||||
if (!alias) {
|
if (!alias) {
|
||||||
@ -101152,16 +101231,6 @@ class OfficialBuilds extends BaseDistribution {
|
|||||||
core_debug(`Found LTS release '${release.version}' for Node version '${versionSpec}'`);
|
core_debug(`Found LTS release '${release.version}' for Node version '${versionSpec}'`);
|
||||||
return release.version.split('.')[0];
|
return release.version.split('.')[0];
|
||||||
}
|
}
|
||||||
async resolveVersionFromManifest(versionSpec, stable, osArch, manifest) {
|
|
||||||
try {
|
|
||||||
const info = await this.getInfoFromManifest(versionSpec, stable, osArch, manifest);
|
|
||||||
return info?.resolvedVersion;
|
|
||||||
}
|
|
||||||
catch (err) {
|
|
||||||
core_info('Unable to resolve version from manifest...');
|
|
||||||
core_debug(err.message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
async getInfoFromManifest(versionSpec, stable, osArch, manifest) {
|
async getInfoFromManifest(versionSpec, stable, osArch, manifest) {
|
||||||
let info = null;
|
let info = null;
|
||||||
if (!manifest) {
|
if (!manifest) {
|
||||||
@ -101186,7 +101255,7 @@ class OfficialBuilds extends BaseDistribution {
|
|||||||
}
|
}
|
||||||
async verifyNodeVersion(installedDir) {
|
async verifyNodeVersion(installedDir) {
|
||||||
// tool-cache layout: <root>/node/<version>/<arch>
|
// tool-cache layout: <root>/node/<version>/<arch>
|
||||||
const expectedVersion = 'v' + external_path_default().basename(external_path_default().dirname(installedDir));
|
const expectedVersion = 'v' + external_node_path_default().basename(external_node_path_default().dirname(installedDir));
|
||||||
let actualVersion = '';
|
let actualVersion = '';
|
||||||
try {
|
try {
|
||||||
const { stdout } = await getExecOutput('node', ['--version'], {
|
const { stdout } = await getExecOutput('node', ['--version'], {
|
||||||
|
|||||||
@ -1,7 +1,9 @@
|
|||||||
|
import fs from 'node:fs';
|
||||||
|
import path from 'node:path';
|
||||||
|
|
||||||
import * as core from '@actions/core';
|
import * as core from '@actions/core';
|
||||||
import * as tc from '@actions/tool-cache';
|
|
||||||
import path from 'path';
|
|
||||||
import * as exec from '@actions/exec';
|
import * as exec from '@actions/exec';
|
||||||
|
import * as tc from '@actions/tool-cache';
|
||||||
|
|
||||||
import BaseDistribution from '../base-distribution.js';
|
import BaseDistribution from '../base-distribution.js';
|
||||||
import {NodeInputs, INodeVersion, INodeVersionInfo} from '../base-models.js';
|
import {NodeInputs, INodeVersion, INodeVersionInfo} from '../base-models.js';
|
||||||
@ -10,6 +12,17 @@ interface INodeRelease extends tc.IToolRelease {
|
|||||||
lts?: string;
|
lts?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const nodeVersionsManifestFile = 'setup-node-versions-manifest.json';
|
||||||
|
const nodeVersionsManifestUrl =
|
||||||
|
'https://raw.githubusercontent.com/actions/node-versions/main/versions-manifest.json';
|
||||||
|
const invalidManifestMessage =
|
||||||
|
'The manifest fetched is empty, truncated, or does not contain any valid tool release entries.';
|
||||||
|
|
||||||
|
/** @param {unknown} manifest */
|
||||||
|
function isValidManifest(manifest: unknown): manifest is tc.IToolRelease[] {
|
||||||
|
return Array.isArray(manifest) && manifest.length > 0;
|
||||||
|
}
|
||||||
|
|
||||||
export default class OfficialBuilds extends BaseDistribution {
|
export default class OfficialBuilds extends BaseDistribution {
|
||||||
constructor(nodeInfo: NodeInputs) {
|
constructor(nodeInfo: NodeInputs) {
|
||||||
super(nodeInfo);
|
super(nodeInfo);
|
||||||
@ -43,12 +56,20 @@ export default class OfficialBuilds extends BaseDistribution {
|
|||||||
|
|
||||||
if (this.nodeInfo.checkLatest) {
|
if (this.nodeInfo.checkLatest) {
|
||||||
core.info('Attempt to resolve the latest version from manifest...');
|
core.info('Attempt to resolve the latest version from manifest...');
|
||||||
const resolvedVersion = await this.resolveVersionFromManifest(
|
let resolvedVersion: string | undefined;
|
||||||
|
try {
|
||||||
|
manifest ??= await this.getManifest();
|
||||||
|
const info = await this.getInfoFromManifest(
|
||||||
this.nodeInfo.versionSpec,
|
this.nodeInfo.versionSpec,
|
||||||
this.nodeInfo.stable,
|
this.nodeInfo.stable,
|
||||||
osArch,
|
osArch,
|
||||||
manifest
|
manifest
|
||||||
);
|
);
|
||||||
|
resolvedVersion = info?.resolvedVersion;
|
||||||
|
} catch (error) {
|
||||||
|
core.info('Unable to resolve version from manifest...');
|
||||||
|
core.debug((error as Error).message);
|
||||||
|
}
|
||||||
if (resolvedVersion) {
|
if (resolvedVersion) {
|
||||||
this.nodeInfo.versionSpec = resolvedVersion;
|
this.nodeInfo.versionSpec = resolvedVersion;
|
||||||
core.info(`Resolved as '${resolvedVersion}'`);
|
core.info(`Resolved as '${resolvedVersion}'`);
|
||||||
@ -191,6 +212,33 @@ export default class OfficialBuilds extends BaseDistribution {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async getManifest(): Promise<tc.IToolRelease[]> {
|
private async getManifest(): Promise<tc.IToolRelease[]> {
|
||||||
|
const runnerTemp = process.env['RUNNER_TEMP'];
|
||||||
|
const manifestPath = runnerTemp
|
||||||
|
? path.join(runnerTemp, nodeVersionsManifestFile)
|
||||||
|
: undefined;
|
||||||
|
const cachedManifest = this.nodeInfo.checkLatest
|
||||||
|
? undefined
|
||||||
|
: this.getCachedManifest(manifestPath);
|
||||||
|
if (cachedManifest) {
|
||||||
|
return cachedManifest;
|
||||||
|
}
|
||||||
|
|
||||||
|
core.debug(`Getting manifest from ${nodeVersionsManifestUrl}`);
|
||||||
|
try {
|
||||||
|
const {result} = await this.httpClient.getJson<tc.IToolRelease[]>(
|
||||||
|
nodeVersionsManifestUrl
|
||||||
|
);
|
||||||
|
if (!isValidManifest(result)) {
|
||||||
|
throw new Error(invalidManifestMessage);
|
||||||
|
}
|
||||||
|
this.cacheManifest(manifestPath, result);
|
||||||
|
return result;
|
||||||
|
} catch (error) {
|
||||||
|
core.debug(
|
||||||
|
`Unable to get manifest from ${nodeVersionsManifestUrl}: ${error instanceof Error ? error.message : String(error)}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
let lastError: Error | undefined;
|
let lastError: Error | undefined;
|
||||||
const maxAttempts = 3;
|
const maxAttempts = 3;
|
||||||
core.debug(`Getting manifest from actions/node-versions@main`);
|
core.debug(`Getting manifest from actions/node-versions@main`);
|
||||||
@ -204,14 +252,13 @@ export default class OfficialBuilds extends BaseDistribution {
|
|||||||
: this.nodeInfo.auth,
|
: this.nodeInfo.auth,
|
||||||
'main'
|
'main'
|
||||||
);
|
);
|
||||||
if (Array.isArray(manifest) && manifest.length > 0) {
|
if (isValidManifest(manifest)) {
|
||||||
|
this.cacheManifest(manifestPath, manifest);
|
||||||
return manifest;
|
return manifest;
|
||||||
}
|
}
|
||||||
lastError = new Error(
|
lastError = new Error(invalidManifestMessage);
|
||||||
`The manifest fetched is empty, truncated, or does not contain any valid tool release entries.`
|
} catch (error) {
|
||||||
);
|
lastError = error instanceof Error ? error : new Error(String(error));
|
||||||
} catch (err) {
|
|
||||||
lastError = err instanceof Error ? err : new Error(String(err));
|
|
||||||
}
|
}
|
||||||
core.debug(
|
core.debug(
|
||||||
`Attempt ${attempt}/${maxAttempts} to fetch the manifest failed: ${lastError.message}`
|
`Attempt ${attempt}/${maxAttempts} to fetch the manifest failed: ${lastError.message}`
|
||||||
@ -228,6 +275,55 @@ export default class OfficialBuilds extends BaseDistribution {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @param {string | undefined} manifestPath */
|
||||||
|
private getCachedManifest(
|
||||||
|
manifestPath: string | undefined
|
||||||
|
): tc.IToolRelease[] | undefined {
|
||||||
|
if (!manifestPath) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const manifest: unknown = JSON.parse(
|
||||||
|
fs.readFileSync(manifestPath, 'utf8')
|
||||||
|
);
|
||||||
|
if (isValidManifest(manifest)) {
|
||||||
|
core.debug(`Found manifest in ${manifestPath}`);
|
||||||
|
return manifest;
|
||||||
|
}
|
||||||
|
core.debug(`Ignoring invalid manifest in ${manifestPath}`);
|
||||||
|
} catch (error) {
|
||||||
|
if ((error as NodeJS.ErrnoException).code !== 'ENOENT') {
|
||||||
|
core.debug(
|
||||||
|
`Unable to read manifest from ${manifestPath}: ${error instanceof Error ? error.message : String(error)}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string | undefined} manifestPath
|
||||||
|
* @param {tc.IToolRelease[]} manifest
|
||||||
|
*/
|
||||||
|
private cacheManifest(
|
||||||
|
manifestPath: string | undefined,
|
||||||
|
manifest: tc.IToolRelease[]
|
||||||
|
): void {
|
||||||
|
if (!manifestPath) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
fs.writeFileSync(manifestPath, JSON.stringify(manifest));
|
||||||
|
} catch (error) {
|
||||||
|
core.debug(
|
||||||
|
`Unable to cache manifest in ${manifestPath}: ${error instanceof Error ? error.message : String(error)}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private resolveLtsAliasFromManifest(
|
private resolveLtsAliasFromManifest(
|
||||||
versionSpec: string,
|
versionSpec: string,
|
||||||
stable: boolean,
|
stable: boolean,
|
||||||
@ -272,26 +368,6 @@ export default class OfficialBuilds extends BaseDistribution {
|
|||||||
return release.version.split('.')[0];
|
return release.version.split('.')[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
private async resolveVersionFromManifest(
|
|
||||||
versionSpec: string,
|
|
||||||
stable: boolean,
|
|
||||||
osArch: string,
|
|
||||||
manifest: tc.IToolRelease[] | undefined
|
|
||||||
): Promise<string | undefined> {
|
|
||||||
try {
|
|
||||||
const info = await this.getInfoFromManifest(
|
|
||||||
versionSpec,
|
|
||||||
stable,
|
|
||||||
osArch,
|
|
||||||
manifest
|
|
||||||
);
|
|
||||||
return info?.resolvedVersion;
|
|
||||||
} catch (err) {
|
|
||||||
core.info('Unable to resolve version from manifest...');
|
|
||||||
core.debug((err as Error).message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async getInfoFromManifest(
|
private async getInfoFromManifest(
|
||||||
versionSpec: string,
|
versionSpec: string,
|
||||||
stable: boolean,
|
stable: boolean,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user