diff --git a/dist/setup/index.js b/dist/setup/index.js index 857ffcbe..20241564 100644 --- a/dist/setup/index.js +++ b/dist/setup/index.js @@ -73228,7 +73228,10 @@ class BaseDistribution { getNodeJsInfo() { return __awaiter(this, void 0, void 0, function* () { let toolPath = this.findVersionInHoostedToolCacheDirectory(); - if (!toolPath) { + if (toolPath) { + core.info(`Found in cache @ ${toolPath}`); + } + else { const nodeVersions = yield this.getNodejsVersions(); const versions = this.filterVersions(nodeVersions); const evaluatedVersion = this.evaluateVersions(versions); @@ -73270,7 +73273,6 @@ class BaseDistribution { } downloadNodejs(info) { return __awaiter(this, void 0, void 0, function* () { - let osPlat = os.platform(); let downloadPath = ''; try { downloadPath = yield tc.downloadTool(info.downloadUrl); @@ -73333,10 +73335,10 @@ class BaseDistribution { let extPath; info = info || {}; // satisfy compiler, never null when reaches here if (this.osPlat == 'win32') { - let _7zPath = path.join(__dirname, '../..', 'externals', '7zr.exe'); + const _7zPath = path.join(__dirname, '../..', 'externals', '7zr.exe'); extPath = yield tc.extract7z(downloadPath, undefined, _7zPath); // 7z extracts to folder matching file name - let nestedPath = path.join(extPath, path.basename(info.fileName, '.7z')); + const nestedPath = path.join(extPath, path.basename(info.fileName, '.7z')); if (fs_1.default.existsSync(nestedPath)) { extPath = nestedPath; } @@ -73356,12 +73358,11 @@ class BaseDistribution { return toolPath; }); } - getDistFileName(arch = os.arch()) { - let osPlat = os.platform(); + getDistFileName(arch) { let osArch = this.translateArchToDistUrl(arch); // node offers a json list of versions let dataFileName; - switch (osPlat) { + switch (this.osPlat) { case 'linux': dataFileName = `linux-${osArch}`; break; @@ -73372,12 +73373,12 @@ class BaseDistribution { dataFileName = `win-${osArch}-exe`; break; default: - throw new Error(`Unexpected OS '${osPlat}'`); + throw new Error(`Unexpected OS '${this.osPlat}'`); } return dataFileName; } filterVersions(nodeVersions) { - let versions = []; + const versions = []; const dataFileName = this.getDistFileName(this.nodeInfo.arch); nodeVersions.forEach((nodeVersion) => { // ensure this version supports your os and platform @@ -73582,8 +73583,8 @@ class OfficialBuilds extends base_distribution_1.default { } getNodeJsInfo() { return __awaiter(this, void 0, void 0, function* () { - let manifest = []; - let nodeVersions = []; + let manifest; + let nodeVersions; if (this.isLtsAlias(this.nodeInfo.versionSpec)) { core.info('Attempt to resolve LTS alias from manifest...'); // No try-catch since it's not possible to resolve LTS alias without manifest @@ -73596,10 +73597,26 @@ class OfficialBuilds extends base_distribution_1.default { this.nodeInfo.versionSpec = this.evaluateVersions(versions); core.info(`getting latest node version...`); } + if (this.nodeInfo.checkLatest) { + core.info('Attempt to resolve the latest version from manifest...'); + const osArch = this.translateArchToDistUrl(this.nodeInfo.arch); + const resolvedVersion = yield this.resolveVersionFromManifest(this.nodeInfo.versionSpec, osArch, manifest); + if (resolvedVersion) { + this.nodeInfo.versionSpec = resolvedVersion; + core.info(`Resolved as '${resolvedVersion}'`); + } + else { + core.info(`Failed to resolve version ${this.nodeInfo.versionSpec} from manifest`); + } + } let toolPath = this.findVersionInHoostedToolCacheDirectory(); - if (!toolPath) { + if (toolPath) { + core.info(`Found in cache @ ${toolPath}`); + } + else { try { - const versionInfo = yield this.getInfoFromManifest(this.nodeInfo.versionSpec, true, this.nodeInfo.auth, this.nodeInfo.arch, undefined); + core.info(`Attempting to download ${this.nodeInfo.versionSpec}...`); + const versionInfo = yield this.getInfoFromManifest(this.nodeInfo.versionSpec, this.nodeInfo.arch, manifest); if (versionInfo) { core.info(`Acquiring ${versionInfo.resolvedVersion} - ${versionInfo.arch} from ${versionInfo.downloadUrl}`); toolPath = yield tc.downloadTool(versionInfo.downloadUrl, undefined, this.nodeInfo.auth); @@ -73639,8 +73656,7 @@ class OfficialBuilds extends base_distribution_1.default { return versions[0]; } core.debug(`evaluating ${versions.length} versions`); - for (let i = 0; i < versions.length; i++) { - const potential = versions[i]; + for (let potential of versions) { const satisfied = semver.satisfies(potential, this.nodeInfo.versionSpec); if (satisfied) { version = potential; @@ -73687,8 +73703,21 @@ class OfficialBuilds extends base_distribution_1.default { core.debug(`Found LTS release '${release.version}' for Node version '${versionSpec}'`); return release.version.split('.')[0]; } - getInfoFromManifest(versionSpec, stable, auth, osArch = this.translateArchToDistUrl(os_1.default.arch()), manifest) { + resolveVersionFromManifest(versionSpec, osArch = this.translateArchToDistUrl(os_1.default.arch()), manifest) { return __awaiter(this, void 0, void 0, function* () { + try { + const info = yield this.getInfoFromManifest(versionSpec, osArch, manifest); + return info === null || info === void 0 ? void 0 : info.resolvedVersion; + } + catch (err) { + core.info('Unable to resolve version from manifest...'); + core.debug(err.message); + } + }); + } + getInfoFromManifest(versionSpec, osArch = this.translateArchToDistUrl(os_1.default.arch()), manifest) { + return __awaiter(this, void 0, void 0, function* () { + const stable = true; let info = null; if (!manifest) { core.debug('No manifest cached'); @@ -73854,514 +73883,6 @@ class CanaryBuild extends base_distribution_1.default { exports["default"] = CanaryBuild; -/***/ }), - -/***/ 2574: -/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { - -"use strict"; - -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k]; - result["default"] = mod; - return result; -}; -Object.defineProperty(exports, "__esModule", ({ value: true })); -const os_1 = __importDefault(__nccwpck_require__(2037)); -const assert = __importStar(__nccwpck_require__(9491)); -const core = __importStar(__nccwpck_require__(2186)); -const hc = __importStar(__nccwpck_require__(9925)); -const io = __importStar(__nccwpck_require__(7436)); -const tc = __importStar(__nccwpck_require__(7784)); -const path = __importStar(__nccwpck_require__(1017)); -const semver = __importStar(__nccwpck_require__(5911)); -const fs_1 = __importDefault(__nccwpck_require__(7147)); -var Distributions; -(function (Distributions) { - Distributions["DEFAULT"] = ""; - Distributions["CANARY"] = "-v8-canary"; - Distributions["NIGHTLY"] = "-nightly"; - Distributions["RC"] = "-rc"; -})(Distributions = exports.Distributions || (exports.Distributions = {})); -exports.distributionOf = (versionSpec) => { - if (versionSpec.includes(Distributions.CANARY)) - return Distributions.CANARY; - if (versionSpec.includes(Distributions.NIGHTLY)) - return Distributions.NIGHTLY; - if (semver.prerelease(versionSpec)) - return Distributions.RC; - return Distributions.DEFAULT; -}; -exports.semverVersionMatcherFactory = (range) => { - const matcher = (potential) => semver.satisfies(potential, range); - matcher.factory = exports.semverVersionMatcherFactory; - return matcher; -}; -exports.nightlyV8MatcherFactory = (version, distribution) => { - const { range, includePrerelease } = createRangePreRelease(version, distribution); - const matcher = (potential) => exports.distributionOf(potential) === distribution && - semver.satisfies(potential.replace(distribution, `${distribution}.`), range, { includePrerelease: includePrerelease }); - matcher.factory = exports.nightlyV8MatcherFactory; - return matcher; -}; -exports.splitVersionSpec = (versionSpec) => versionSpec.split(/-(.*)/s); -const createRangePreRelease = (versionSpec, distribution = '') => { - let range; - const [raw, prerelease] = exports.splitVersionSpec(versionSpec); - const isValidVersion = semver.valid(raw); - const rawVersion = (isValidVersion ? raw : semver.coerce(raw)); - if (`-${prerelease}` !== distribution) { - range = `${rawVersion}${`-${prerelease}`.replace(distribution, `${distribution}.`)}`; - } - else { - range = `${semver.validRange(`^${rawVersion}${distribution}`)}-0`; - } - return { range, includePrerelease: !isValidVersion }; -}; -function versionMatcherFactory(versionSpec) { - var _a; - const raw = exports.splitVersionSpec(versionSpec)[0]; - const validVersion = semver.valid(raw) ? raw : (_a = semver.coerce(raw)) === null || _a === void 0 ? void 0 : _a.version; - const distribution = exports.distributionOf(versionSpec); - if (validVersion) { - switch (distribution) { - case Distributions.CANARY: - case Distributions.NIGHTLY: - return exports.nightlyV8MatcherFactory(versionSpec, distribution); - case Distributions.RC: - case Distributions.DEFAULT: - return exports.semverVersionMatcherFactory(versionSpec); - } - } - else { - throw Error(`Invalid version input "${versionSpec}"`); - } -} -exports.versionMatcherFactory = versionMatcherFactory; -function getNode(versionSpec, stable, checkLatest, auth, arch = os_1.default.arch()) { - return __awaiter(this, void 0, void 0, function* () { - // Store manifest data to avoid multiple calls - let manifest; - let nodeVersions; - const osPlat = os_1.default.platform(); - const osArch = translateArchToDistUrl(arch); - const distribution = exports.distributionOf(versionSpec); - if (isLtsAlias(versionSpec)) { - core.info('Attempt to resolve LTS alias from manifest...'); - // No try-catch since it's not possible to resolve LTS alias without manifest - manifest = yield getManifest(auth); - versionSpec = resolveLtsAliasFromManifest(versionSpec, stable, manifest); - } - if (isLatestSyntax(versionSpec)) { - nodeVersions = yield getVersionsFromDist(versionSpec); - versionSpec = yield queryDistForMatch(versionSpec, arch, nodeVersions); - core.info(`getting latest node version ${versionSpec}...`); - } - if ((distribution === Distributions.NIGHTLY || - distribution === Distributions.CANARY) && - checkLatest) { - nodeVersions = yield getVersionsFromDist(versionSpec); - versionSpec = yield queryDistForMatch(versionSpec, arch, nodeVersions); - } - if (checkLatest && - distribution !== Distributions.NIGHTLY && - distribution !== Distributions.CANARY) { - core.info('Attempt to resolve the latest version from manifest...'); - const resolvedVersion = yield resolveVersionFromManifest(versionSpec, stable, auth, osArch, manifest); - if (resolvedVersion) { - versionSpec = resolvedVersion; - core.info(`Resolved as '${versionSpec}'`); - } - else { - core.info(`Failed to resolve version ${versionSpec} from manifest`); - } - } - // check cache - let toolPath; - if (distribution === Distributions.DEFAULT) { - toolPath = tc.find('node', versionSpec, osArch); - } - else { - const localVersionPaths = tc.findAllVersions('node', osArch); - const localVersion = evaluateVersions(localVersionPaths, versionSpec); - toolPath = localVersion && tc.find('node', localVersion, osArch); - } - // If not found in cache, download - if (toolPath) { - core.info(`Found in cache @ ${toolPath}`); - } - else { - core.info(`Attempting to download ${versionSpec}...`); - let downloadPath = ''; - let info = null; - // - // Try download from internal distribution (popular versions only) - // - try { - info = yield getInfoFromManifest(versionSpec, stable, auth, osArch, manifest); - if (info) { - core.info(`Acquiring ${info.resolvedVersion} - ${info.arch} from ${info.downloadUrl}`); - downloadPath = yield tc.downloadTool(info.downloadUrl, undefined, auth); - } - else { - core.info('Not found in manifest. Falling back to download directly from Node'); - } - } - catch (err) { - // Rate limit? - if (err instanceof tc.HTTPError && - (err.httpStatusCode === 403 || err.httpStatusCode === 429)) { - core.info(`Received HTTP status code ${err.httpStatusCode}. This usually indicates the rate limit has been exceeded`); - } - else { - core.info(err.message); - } - core.debug(err.stack); - core.info('Falling back to download directly from Node'); - } - // - // Download from nodejs.org - // - if (!downloadPath) { - info = yield getInfoFromDist(versionSpec, arch, nodeVersions); - if (!info) { - throw new Error(`Unable to find Node version '${versionSpec}' for platform ${osPlat} and architecture ${osArch}.`); - } - core.info(`Acquiring ${info.resolvedVersion} - ${info.arch} from ${info.downloadUrl}`); - try { - downloadPath = yield tc.downloadTool(info.downloadUrl); - } - catch (err) { - if (err instanceof tc.HTTPError && err.httpStatusCode == 404) { - return yield acquireNodeFromFallbackLocation(info.resolvedVersion, info.arch); - } - throw err; - } - } - // - // Extract - // - core.info('Extracting ...'); - let extPath; - info = info || {}; // satisfy compiler, never null when reaches here - if (osPlat == 'win32') { - let _7zPath = path.join(__dirname, '../..', 'externals', '7zr.exe'); - extPath = yield tc.extract7z(downloadPath, undefined, _7zPath); - // 7z extracts to folder matching file name - let nestedPath = path.join(extPath, path.basename(info.fileName, '.7z')); - if (fs_1.default.existsSync(nestedPath)) { - extPath = nestedPath; - } - } - else { - extPath = yield tc.extractTar(downloadPath, undefined, [ - 'xz', - '--strip', - '1' - ]); - } - // - // Install into the local tool cache - node extracts with a root folder that matches the fileName downloaded - // - core.info('Adding to the cache ...'); - toolPath = yield tc.cacheDir(extPath, 'node', info.resolvedVersion, info.arch); - core.info('Done'); - } - // - // a tool installer initimately knows details about the layout of that tool - // for example, node binary is in the bin folder after the extract on Mac/Linux. - // layouts could change by version, by platform etc... but that's the tool installers job - // - if (osPlat != 'win32') { - toolPath = path.join(toolPath, 'bin'); - } - // - // prepend the tools path. instructs the agent to prepend for future tasks - core.addPath(toolPath); - }); -} -exports.getNode = getNode; -function isLtsAlias(versionSpec) { - return versionSpec.startsWith('lts/'); -} -exports.isLtsAlias = isLtsAlias; -function getManifest(auth) { - core.debug('Getting manifest from actions/node-versions@main'); - return tc.getManifestFromRepo('actions', 'node-versions', auth, 'main'); -} -function resolveLtsAliasFromManifest(versionSpec, stable, manifest) { - var _a; - const alias = (_a = versionSpec.split('lts/')[1]) === null || _a === void 0 ? void 0 : _a.toLowerCase(); - if (!alias) { - throw new Error(`Unable to parse LTS alias for Node version '${versionSpec}'`); - } - core.debug(`LTS alias '${alias}' for Node version '${versionSpec}'`); - // Supported formats are `lts/`, `lts/*`, and `lts/-n`. Where asterisk means highest possible LTS and -n means the nth-highest. - const n = Number(alias); - const aliases = Object.fromEntries(manifest - .filter(x => x.lts && x.stable === stable) - .map(x => [x.lts.toLowerCase(), x]) - .reverse()); - const numbered = Object.values(aliases); - const release = alias === '*' - ? numbered[numbered.length - 1] - : n < 0 - ? numbered[numbered.length - 1 + n] - : aliases[alias]; - if (!release) { - throw new Error(`Unable to find LTS release '${alias}' for Node version '${versionSpec}'.`); - } - core.debug(`Found LTS release '${release.version}' for Node version '${versionSpec}'`); - return release.version.split('.')[0]; -} -exports.resolveLtsAliasFromManifest = resolveLtsAliasFromManifest; -function getInfoFromManifest(versionSpec, stable, auth, osArch = translateArchToDistUrl(os_1.default.arch()), manifest) { - return __awaiter(this, void 0, void 0, function* () { - let info = null; - if (!manifest) { - core.debug('No manifest cached'); - manifest = yield getManifest(auth); - } - const rel = yield tc.findFromManifest(versionSpec, stable, manifest, osArch); - if (rel && rel.files.length > 0) { - info = {}; - info.resolvedVersion = rel.version; - info.arch = rel.files[0].arch; - info.downloadUrl = rel.files[0].download_url; - info.fileName = rel.files[0].filename; - } - return info; - }); -} -function getInfoFromDist(versionSpec, arch = os_1.default.arch(), nodeVersions) { - return __awaiter(this, void 0, void 0, function* () { - let osPlat = os_1.default.platform(); - let osArch = translateArchToDistUrl(arch); - let version = yield queryDistForMatch(versionSpec, arch, nodeVersions); - if (!version) { - return null; - } - // - // Download - a tool installer intimately knows how to get the tool (and construct urls) - // - version = semver.clean(version) || ''; - let fileName = osPlat == 'win32' - ? `node-v${version}-win-${osArch}` - : `node-v${version}-${osPlat}-${osArch}`; - let urlFileName = osPlat == 'win32' ? `${fileName}.7z` : `${fileName}.tar.gz`; - const initialUrl = getNodejsDistUrl(versionSpec); - const url = `${initialUrl}/v${version}/${urlFileName}`; - return { - downloadUrl: url, - resolvedVersion: version, - arch: arch, - fileName: fileName - }; - }); -} -function resolveVersionFromManifest(versionSpec, stable, auth, osArch = translateArchToDistUrl(os_1.default.arch()), manifest) { - return __awaiter(this, void 0, void 0, function* () { - try { - const info = yield getInfoFromManifest(versionSpec, stable, auth, osArch, manifest); - return info === null || info === void 0 ? void 0 : info.resolvedVersion; - } - catch (err) { - core.info('Unable to resolve version from manifest...'); - core.debug(err.message); - } - }); -} -// TODO - should we just export this from @actions/tool-cache? Lifted directly from there -// - the answer from dsame@github.com - we have customized matcher and can not -// export `evaluateVersions` from tc. But it would be possible to modify tc to accept -// the matcher as an optional parameter to `evaluateVersions` -function evaluateVersions(versions, versionSpec) { - core.debug(`evaluating ${versions.length} versions`); - const matcher = versionMatcherFactory(versionSpec); - const version = versions.sort(semver.rcompare).find(matcher) || ''; - if (version) { - core.debug(`matched: ${version}`); - } - else { - core.debug('match not found'); - } - return version; -} -exports.evaluateVersions = evaluateVersions; -function getNodejsDistUrl(version) { - switch (exports.distributionOf(version)) { - case Distributions.CANARY: - return 'https://nodejs.org/download/v8-canary'; - case Distributions.NIGHTLY: - return 'https://nodejs.org/download/nightly'; - case Distributions.RC: - return 'https://nodejs.org/download/rc'; - case Distributions.DEFAULT: - return 'https://nodejs.org/dist'; - } -} -exports.getNodejsDistUrl = getNodejsDistUrl; -function queryDistForMatch(versionSpec, arch = os_1.default.arch(), nodeVersions) { - return __awaiter(this, void 0, void 0, function* () { - let osPlat = os_1.default.platform(); - let osArch = translateArchToDistUrl(arch); - // node offers a json list of versions - let dataFileName; - switch (osPlat) { - case 'linux': - dataFileName = `linux-${osArch}`; - break; - case 'darwin': - dataFileName = `osx-${osArch}-tar`; - break; - case 'win32': - dataFileName = `win-${osArch}-exe`; - break; - default: - throw new Error(`Unexpected OS '${osPlat}'`); - } - if (!nodeVersions) { - core.debug('No dist manifest cached'); - nodeVersions = yield getVersionsFromDist(versionSpec); - } - if (isLatestSyntax(versionSpec)) { - core.info(`getting latest node version...`); - return nodeVersions[0].version; - } - const versions = []; - nodeVersions.forEach((nodeVersion) => { - // ensure this version supports your os and platform - if (nodeVersion.files.indexOf(dataFileName) >= 0) { - versions.push(nodeVersion.version); - } - }); - // get the latest version that matches the version spec - const version = evaluateVersions(versions, versionSpec); - return version; - }); -} -exports.queryDistForMatch = queryDistForMatch; -function getVersionsFromDist(versionSpec) { - return __awaiter(this, void 0, void 0, function* () { - const distUrl = getNodejsDistUrl(versionSpec); - const dataUrl = `${distUrl}/index.json`; - let httpClient = new hc.HttpClient('setup-node', [], { - allowRetries: true, - maxRetries: 3 - }); - let response = yield httpClient.getJson(dataUrl); - return response.result || []; - }); -} -exports.getVersionsFromDist = getVersionsFromDist; -// For non LTS versions of Node, the files we need (for Windows) are sometimes located -// in a different folder than they normally are for other versions. -// Normally the format is similar to: https://nodejs.org/dist/v5.10.1/node-v5.10.1-win-x64.7z -// In this case, there will be two files located at: -// /dist/v5.10.1/win-x64/node.exe -// /dist/v5.10.1/win-x64/node.lib -// If this is not the structure, there may also be two files located at: -// /dist/v0.12.18/node.exe -// /dist/v0.12.18/node.lib -// This method attempts to download and cache the resources from these alternative locations. -// Note also that the files are normally zipped but in this case they are just an exe -// and lib file in a folder, not zipped. -function acquireNodeFromFallbackLocation(version, arch = os_1.default.arch()) { - return __awaiter(this, void 0, void 0, function* () { - const initialUrl = getNodejsDistUrl(version); - let osPlat = os_1.default.platform(); - let osArch = translateArchToDistUrl(arch); - // Create temporary folder to download in to - const tempDownloadFolder = 'temp_' + Math.floor(Math.random() * 2000000000); - const tempDirectory = process.env['RUNNER_TEMP'] || ''; - assert.ok(tempDirectory, 'Expected RUNNER_TEMP to be defined'); - const tempDir = path.join(tempDirectory, tempDownloadFolder); - yield io.mkdirP(tempDir); - let exeUrl; - let libUrl; - try { - exeUrl = `${initialUrl}/v${version}/win-${osArch}/node.exe`; - libUrl = `${initialUrl}/v${version}/win-${osArch}/node.lib`; - core.info(`Downloading only node binary from ${exeUrl}`); - const exePath = yield tc.downloadTool(exeUrl); - yield io.cp(exePath, path.join(tempDir, 'node.exe')); - const libPath = yield tc.downloadTool(libUrl); - yield io.cp(libPath, path.join(tempDir, 'node.lib')); - } - catch (err) { - if (err instanceof tc.HTTPError && err.httpStatusCode == 404) { - exeUrl = `${initialUrl}/v${version}/node.exe`; - libUrl = `${initialUrl}/v${version}/node.lib`; - const exePath = yield tc.downloadTool(exeUrl); - yield io.cp(exePath, path.join(tempDir, 'node.exe')); - const libPath = yield tc.downloadTool(libUrl); - yield io.cp(libPath, path.join(tempDir, 'node.lib')); - } - else { - throw err; - } - } - let toolPath = yield tc.cacheDir(tempDir, 'node', version, arch); - core.addPath(toolPath); - return toolPath; - }); -} -// os.arch does not always match the relative download url, e.g. -// os.arch == 'arm' != node-v12.13.1-linux-armv7l.tar.gz -// All other currently supported architectures match, e.g.: -// os.arch = arm64 => https://nodejs.org/dist/v{VERSION}/node-v{VERSION}-{OS}-arm64.tar.gz -// os.arch = x64 => https://nodejs.org/dist/v{VERSION}/node-v{VERSION}-{OS}-x64.tar.gz -function translateArchToDistUrl(arch) { - switch (arch) { - case 'arm': - return 'armv7l'; - default: - return arch; - } -} -function parseNodeVersionFile(contents) { - var _a, _b, _c; - let nodeVersion; - // Try parsing the file as an NPM `package.json` file. - try { - nodeVersion = (_a = JSON.parse(contents).volta) === null || _a === void 0 ? void 0 : _a.node; - if (!nodeVersion) - nodeVersion = (_b = JSON.parse(contents).engines) === null || _b === void 0 ? void 0 : _b.node; - } - catch (_d) { - core.info('Node version file is not JSON file'); - } - if (!nodeVersion) { - const found = contents.match(/^(?:nodejs\s+)?v?(?[^\s]+)$/m); - nodeVersion = (_c = found === null || found === void 0 ? void 0 : found.groups) === null || _c === void 0 ? void 0 : _c.version; - } - // In the case of an unknown format, - // return as is and evaluate the version separately. - if (!nodeVersion) - nodeVersion = contents.trim(); - return nodeVersion; -} -exports.parseNodeVersionFile = parseNodeVersionFile; -function isLatestSyntax(versionSpec) { - return ['current', 'latest', 'node'].includes(versionSpec); -} -exports.isLatestSyntax = isLatestSyntax; - - /***/ }), /***/ 399: @@ -74391,13 +73912,12 @@ var __importDefault = (this && this.__importDefault) || function (mod) { Object.defineProperty(exports, "__esModule", ({ value: true })); const core = __importStar(__nccwpck_require__(2186)); const exec = __importStar(__nccwpck_require__(1514)); -const installer = __importStar(__nccwpck_require__(2574)); const fs_1 = __importDefault(__nccwpck_require__(7147)); +const os_1 = __importDefault(__nccwpck_require__(2037)); const auth = __importStar(__nccwpck_require__(7573)); const path = __importStar(__nccwpck_require__(1017)); const cache_restore_1 = __nccwpck_require__(9517); const cache_utils_1 = __nccwpck_require__(1678); -const os_1 = __importDefault(__nccwpck_require__(2037)); const installer_factory_1 = __nccwpck_require__(1260); function run() { return __awaiter(this, void 0, void 0, function* () { @@ -74434,7 +73954,6 @@ function run() { else { throw new Error(`Could not resolve version: ${version} for build`); } - // await installer.getNode(version, stable, checkLatest, auth, arch); } yield printEnvDetailsAndSetOutput(); const registryUrl = core.getInput('registry-url'); @@ -74471,11 +73990,34 @@ function resolveVersionInput() { if (!fs_1.default.existsSync(versionFilePath)) { throw new Error(`The specified node version file at: ${versionFilePath} does not exist`); } - version = installer.parseNodeVersionFile(fs_1.default.readFileSync(versionFilePath, 'utf8')); + version = parseNodeVersionFile(fs_1.default.readFileSync(versionFilePath, 'utf8')); core.info(`Resolved ${versionFileInput} as ${version}`); } return version; } +function parseNodeVersionFile(contents) { + var _a, _b, _c; + let nodeVersion; + // Try parsing the file as an NPM `package.json` file. + try { + nodeVersion = (_a = JSON.parse(contents).volta) === null || _a === void 0 ? void 0 : _a.node; + if (!nodeVersion) + nodeVersion = (_b = JSON.parse(contents).engines) === null || _b === void 0 ? void 0 : _b.node; + } + catch (_d) { + core.info('Node version file is not JSON file'); + } + if (!nodeVersion) { + const found = contents.match(/^(?:nodejs\s+)?v?(?[^\s]+)$/m); + nodeVersion = (_c = found === null || found === void 0 ? void 0 : found.groups) === null || _c === void 0 ? void 0 : _c.version; + } + // In the case of an unknown format, + // return as is and evaluate the version separately. + if (!nodeVersion) + nodeVersion = contents.trim(); + return nodeVersion; +} +exports.parseNodeVersionFile = parseNodeVersionFile; function printEnvDetailsAndSetOutput() { return __awaiter(this, void 0, void 0, function* () { core.startGroup('Environment details'); diff --git a/src/distibutions/base-distribution.ts b/src/distibutions/base-distribution.ts index 5d488954..74edb47f 100644 --- a/src/distibutions/base-distribution.ts +++ b/src/distibutions/base-distribution.ts @@ -28,7 +28,9 @@ export default abstract class BaseDistribution { public async getNodeJsInfo() { let toolPath = this.findVersionInHoostedToolCacheDirectory(); - if (!toolPath) { + if (toolPath) { + core.info(`Found in cache @ ${toolPath}`); + } else { const nodeVersions = await this.getNodejsVersions(); const versions = this.filterVersions(nodeVersions); const evaluatedVersion = this.evaluateVersions(versions); @@ -76,7 +78,6 @@ export default abstract class BaseDistribution { } protected async downloadNodejs(info: INodeVersionInfo) { - let osPlat: string = os.platform(); let downloadPath = ''; try { downloadPath = await tc.downloadTool(info.downloadUrl); @@ -136,7 +137,9 @@ export default abstract class BaseDistribution { throw err; } } + const toolPath = await tc.cacheDir(tempDir, 'node', version, arch); + return toolPath; } @@ -151,10 +154,13 @@ export default abstract class BaseDistribution { let extPath: string; info = info || ({} as INodeVersionInfo); // satisfy compiler, never null when reaches here if (this.osPlat == 'win32') { - let _7zPath = path.join(__dirname, '../..', 'externals', '7zr.exe'); + const _7zPath = path.join(__dirname, '../..', 'externals', '7zr.exe'); extPath = await tc.extract7z(downloadPath, undefined, _7zPath); // 7z extracts to folder matching file name - let nestedPath = path.join(extPath, path.basename(info.fileName, '.7z')); + const nestedPath = path.join( + extPath, + path.basename(info.fileName, '.7z') + ); if (fs.existsSync(nestedPath)) { extPath = nestedPath; } @@ -180,13 +186,12 @@ export default abstract class BaseDistribution { return toolPath; } - protected getDistFileName(arch: string = os.arch()): string { - let osPlat: string = os.platform(); + protected getDistFileName(arch: string): string { let osArch: string = this.translateArchToDistUrl(arch); // node offers a json list of versions let dataFileName: string; - switch (osPlat) { + switch (this.osPlat) { case 'linux': dataFileName = `linux-${osArch}`; break; @@ -197,14 +202,14 @@ export default abstract class BaseDistribution { dataFileName = `win-${osArch}-exe`; break; default: - throw new Error(`Unexpected OS '${osPlat}'`); + throw new Error(`Unexpected OS '${this.osPlat}'`); } return dataFileName; } protected filterVersions(nodeVersions: INodeVersion[]) { - let versions: string[] = []; + const versions: string[] = []; const dataFileName = this.getDistFileName(this.nodeInfo.arch); diff --git a/src/distibutions/official_builds/official_builds.ts b/src/distibutions/official_builds/official_builds.ts index cb2c2b46..f518ee1f 100644 --- a/src/distibutions/official_builds/official_builds.ts +++ b/src/distibutions/official_builds/official_builds.ts @@ -4,9 +4,8 @@ import * as semver from 'semver'; import os from 'os'; import path from 'path'; -import {INodeVersion} from '../../installer'; import BaseDistribution from '../base-distribution'; -import {INodejs, INodeVersionInfo} from '../base-models'; +import {INodejs, INodeVersion, INodeVersionInfo} from '../base-models'; interface INodeRelease extends tc.IToolRelease { lts?: string; @@ -18,8 +17,8 @@ export default class OfficialBuilds extends BaseDistribution { } public async getNodeJsInfo() { - let manifest: tc.IToolRelease[] = []; - let nodeVersions: INodeVersion[] = []; + let manifest: tc.IToolRelease[] | undefined; + let nodeVersions: INodeVersion[] | undefined; if (this.isLtsAlias(this.nodeInfo.versionSpec)) { core.info('Attempt to resolve LTS alias from manifest...'); @@ -41,16 +40,35 @@ export default class OfficialBuilds extends BaseDistribution { core.info(`getting latest node version...`); } + if (this.nodeInfo.checkLatest) { + core.info('Attempt to resolve the latest version from manifest...'); + const osArch = this.translateArchToDistUrl(this.nodeInfo.arch); + const resolvedVersion = await this.resolveVersionFromManifest( + this.nodeInfo.versionSpec, + osArch, + manifest + ); + if (resolvedVersion) { + this.nodeInfo.versionSpec = resolvedVersion; + core.info(`Resolved as '${resolvedVersion}'`); + } else { + core.info( + `Failed to resolve version ${this.nodeInfo.versionSpec} from manifest` + ); + } + } + let toolPath = this.findVersionInHoostedToolCacheDirectory(); - if (!toolPath) { + if (toolPath) { + core.info(`Found in cache @ ${toolPath}`); + } else { try { + core.info(`Attempting to download ${this.nodeInfo.versionSpec}...`); const versionInfo = await this.getInfoFromManifest( this.nodeInfo.versionSpec, - true, - this.nodeInfo.auth, this.nodeInfo.arch, - undefined + manifest ); if (versionInfo) { core.info( @@ -106,8 +124,7 @@ export default class OfficialBuilds extends BaseDistribution { core.debug(`evaluating ${versions.length} versions`); - for (let i = 0; i < versions.length; i++) { - const potential: string = versions[i]; + for (let potential of versions) { const satisfied: boolean = semver.satisfies( potential, this.nodeInfo.versionSpec @@ -185,13 +202,30 @@ export default class OfficialBuilds extends BaseDistribution { return release.version.split('.')[0]; } + private async resolveVersionFromManifest( + versionSpec: string, + osArch: string = this.translateArchToDistUrl(os.arch()), + manifest: tc.IToolRelease[] | undefined + ): Promise { + try { + const info = await this.getInfoFromManifest( + versionSpec, + osArch, + manifest + ); + return info?.resolvedVersion; + } catch (err) { + core.info('Unable to resolve version from manifest...'); + core.debug(err.message); + } + } + private async getInfoFromManifest( versionSpec: string, - stable: boolean, - auth: string | undefined, osArch: string = this.translateArchToDistUrl(os.arch()), manifest: tc.IToolRelease[] | undefined ): Promise { + const stable = true; let info: INodeVersionInfo | null = null; if (!manifest) { core.debug('No manifest cached'); diff --git a/src/installer.ts b/src/installer.ts deleted file mode 100644 index ffee7dd5..00000000 --- a/src/installer.ts +++ /dev/null @@ -1,650 +0,0 @@ -import os from 'os'; -import * as assert from 'assert'; -import * as core from '@actions/core'; -import * as hc from '@actions/http-client'; -import * as io from '@actions/io'; -import * as tc from '@actions/tool-cache'; -import * as path from 'path'; -import * as semver from 'semver'; -import fs from 'fs'; - -// -// Node versions interface -// see https://nodejs.org/dist/index.json -// for nightly https://nodejs.org/download/nightly/index.json -// for rc https://nodejs.org/download/rc/index.json -// for canary https://nodejs.org/download/v8-canary/index.json -// -export interface INodeVersion { - version: string; - files: string[]; -} - -interface INodeVersionInfo { - downloadUrl: string; - resolvedVersion: string; - arch: string; - fileName: string; -} - -interface INodeRelease extends tc.IToolRelease { - lts?: string; -} - -export enum Distributions { - DEFAULT = '', - CANARY = '-v8-canary', - NIGHTLY = '-nightly', - RC = '-rc' -} - -export const distributionOf = (versionSpec: string): Distributions => { - if (versionSpec.includes(Distributions.CANARY)) return Distributions.CANARY; - if (versionSpec.includes(Distributions.NIGHTLY)) return Distributions.NIGHTLY; - if (semver.prerelease(versionSpec)) return Distributions.RC; - return Distributions.DEFAULT; -}; - -interface VersionMatcher { - (potential: string): boolean; - - // memoize the factory for testing and debug purposes - factory: - | ((ver: string, suffix: string) => VersionMatcher) - | ((semverRanger: string) => VersionMatcher) - | (() => VersionMatcher); -} - -export const semverVersionMatcherFactory = (range: string): VersionMatcher => { - const matcher = (potential: string): boolean => - semver.satisfies(potential, range); - - matcher.factory = semverVersionMatcherFactory; - return matcher; -}; - -export const nightlyV8MatcherFactory = ( - version: string, - distribution: string -): VersionMatcher => { - const {range, includePrerelease} = createRangePreRelease( - version, - distribution - )!; - const matcher = (potential: string): boolean => - distributionOf(potential) === distribution && - semver.satisfies( - potential.replace(distribution, `${distribution}.`), - range!, - {includePrerelease: includePrerelease} - ); - matcher.factory = nightlyV8MatcherFactory; - return matcher; -}; - -export const splitVersionSpec = (versionSpec: string): string[] => - versionSpec.split(/-(.*)/s); - -const createRangePreRelease = ( - versionSpec: string, - distribution: string = '' -) => { - let range: string | undefined; - const [raw, prerelease] = splitVersionSpec(versionSpec); - const isValidVersion = semver.valid(raw); - const rawVersion = (isValidVersion ? raw : semver.coerce(raw))!; - - if (`-${prerelease}` !== distribution) { - range = `${rawVersion}${`-${prerelease}`.replace( - distribution, - `${distribution}.` - )}`; - } else { - range = `${semver.validRange(`^${rawVersion}${distribution}`)}-0`; - } - - return {range, includePrerelease: !isValidVersion}; -}; - -export function versionMatcherFactory(versionSpec: string): VersionMatcher { - const raw = splitVersionSpec(versionSpec)[0]; - const validVersion = semver.valid(raw) ? raw : semver.coerce(raw)?.version; - const distribution = distributionOf(versionSpec); - - if (validVersion) { - switch (distribution) { - case Distributions.CANARY: - case Distributions.NIGHTLY: - return nightlyV8MatcherFactory(versionSpec, distribution); - case Distributions.RC: - case Distributions.DEFAULT: - return semverVersionMatcherFactory(versionSpec); - } - } else { - throw Error(`Invalid version input "${versionSpec}"`); - } -} - -export async function getNode( - versionSpec: string, - stable: boolean, - checkLatest: boolean, - auth: string | undefined, - arch: string = os.arch() -) { - // Store manifest data to avoid multiple calls - let manifest: INodeRelease[] | undefined; - let nodeVersions: INodeVersion[] | undefined; - const osPlat: string = os.platform(); - const osArch: string = translateArchToDistUrl(arch); - const distribution = distributionOf(versionSpec); - - if (isLtsAlias(versionSpec)) { - core.info('Attempt to resolve LTS alias from manifest...'); - - // No try-catch since it's not possible to resolve LTS alias without manifest - manifest = await getManifest(auth); - - versionSpec = resolveLtsAliasFromManifest(versionSpec, stable, manifest); - } - - if (isLatestSyntax(versionSpec)) { - nodeVersions = await getVersionsFromDist(versionSpec); - versionSpec = await queryDistForMatch(versionSpec, arch, nodeVersions); - core.info(`getting latest node version ${versionSpec}...`); - } - - if ( - (distribution === Distributions.NIGHTLY || - distribution === Distributions.CANARY) && - checkLatest - ) { - nodeVersions = await getVersionsFromDist(versionSpec); - versionSpec = await queryDistForMatch(versionSpec, arch, nodeVersions); - } - - if ( - checkLatest && - distribution !== Distributions.NIGHTLY && - distribution !== Distributions.CANARY - ) { - core.info('Attempt to resolve the latest version from manifest...'); - const resolvedVersion = await resolveVersionFromManifest( - versionSpec, - stable, - auth, - osArch, - manifest - ); - if (resolvedVersion) { - versionSpec = resolvedVersion; - core.info(`Resolved as '${versionSpec}'`); - } else { - core.info(`Failed to resolve version ${versionSpec} from manifest`); - } - } - - // check cache - let toolPath: string; - if (distribution === Distributions.DEFAULT) { - toolPath = tc.find('node', versionSpec, osArch); - } else { - const localVersionPaths = tc.findAllVersions('node', osArch); - const localVersion = evaluateVersions(localVersionPaths, versionSpec); - toolPath = localVersion && tc.find('node', localVersion, osArch); - } - - // If not found in cache, download - if (toolPath) { - core.info(`Found in cache @ ${toolPath}`); - } else { - core.info(`Attempting to download ${versionSpec}...`); - let downloadPath = ''; - let info: INodeVersionInfo | null = null; - - // - // Try download from internal distribution (popular versions only) - // - try { - info = await getInfoFromManifest( - versionSpec, - stable, - auth, - osArch, - manifest - ); - if (info) { - core.info( - `Acquiring ${info.resolvedVersion} - ${info.arch} from ${info.downloadUrl}` - ); - downloadPath = await tc.downloadTool(info.downloadUrl, undefined, auth); - } else { - core.info( - 'Not found in manifest. Falling back to download directly from Node' - ); - } - } catch (err) { - // Rate limit? - if ( - err instanceof tc.HTTPError && - (err.httpStatusCode === 403 || err.httpStatusCode === 429) - ) { - core.info( - `Received HTTP status code ${err.httpStatusCode}. This usually indicates the rate limit has been exceeded` - ); - } else { - core.info(err.message); - } - core.debug(err.stack); - core.info('Falling back to download directly from Node'); - } - - // - // Download from nodejs.org - // - if (!downloadPath) { - info = await getInfoFromDist(versionSpec, arch, nodeVersions); - if (!info) { - throw new Error( - `Unable to find Node version '${versionSpec}' for platform ${osPlat} and architecture ${osArch}.` - ); - } - - core.info( - `Acquiring ${info.resolvedVersion} - ${info.arch} from ${info.downloadUrl}` - ); - try { - downloadPath = await tc.downloadTool(info.downloadUrl); - } catch (err) { - if (err instanceof tc.HTTPError && err.httpStatusCode == 404) { - return await acquireNodeFromFallbackLocation( - info.resolvedVersion, - info.arch - ); - } - - throw err; - } - } - - // - // Extract - // - core.info('Extracting ...'); - let extPath: string; - info = info || ({} as INodeVersionInfo); // satisfy compiler, never null when reaches here - if (osPlat == 'win32') { - let _7zPath = path.join(__dirname, '../..', 'externals', '7zr.exe'); - extPath = await tc.extract7z(downloadPath, undefined, _7zPath); - // 7z extracts to folder matching file name - let nestedPath = path.join(extPath, path.basename(info.fileName, '.7z')); - if (fs.existsSync(nestedPath)) { - extPath = nestedPath; - } - } else { - extPath = await tc.extractTar(downloadPath, undefined, [ - 'xz', - '--strip', - '1' - ]); - } - - // - // Install into the local tool cache - node extracts with a root folder that matches the fileName downloaded - // - core.info('Adding to the cache ...'); - toolPath = await tc.cacheDir( - extPath, - 'node', - info.resolvedVersion, - info.arch - ); - core.info('Done'); - } - - // - // a tool installer initimately knows details about the layout of that tool - // for example, node binary is in the bin folder after the extract on Mac/Linux. - // layouts could change by version, by platform etc... but that's the tool installers job - // - if (osPlat != 'win32') { - toolPath = path.join(toolPath, 'bin'); - } - - // - // prepend the tools path. instructs the agent to prepend for future tasks - core.addPath(toolPath); -} - -export function isLtsAlias(versionSpec: string): boolean { - return versionSpec.startsWith('lts/'); -} - -function getManifest(auth: string | undefined): Promise { - core.debug('Getting manifest from actions/node-versions@main'); - return tc.getManifestFromRepo('actions', 'node-versions', auth, 'main'); -} - -export function resolveLtsAliasFromManifest( - versionSpec: string, - stable: boolean, - manifest: INodeRelease[] -): string { - const alias = versionSpec.split('lts/')[1]?.toLowerCase(); - - if (!alias) { - throw new Error( - `Unable to parse LTS alias for Node version '${versionSpec}'` - ); - } - - core.debug(`LTS alias '${alias}' for Node version '${versionSpec}'`); - - // Supported formats are `lts/`, `lts/*`, and `lts/-n`. Where asterisk means highest possible LTS and -n means the nth-highest. - const n = Number(alias); - const aliases = Object.fromEntries( - manifest - .filter(x => x.lts && x.stable === stable) - .map(x => [x.lts!.toLowerCase(), x]) - .reverse() - ); - const numbered = Object.values(aliases); - const release = - alias === '*' - ? numbered[numbered.length - 1] - : n < 0 - ? numbered[numbered.length - 1 + n] - : aliases[alias]; - - if (!release) { - throw new Error( - `Unable to find LTS release '${alias}' for Node version '${versionSpec}'.` - ); - } - - core.debug( - `Found LTS release '${release.version}' for Node version '${versionSpec}'` - ); - - return release.version.split('.')[0]; -} - -async function getInfoFromManifest( - versionSpec: string, - stable: boolean, - auth: string | undefined, - osArch: string = translateArchToDistUrl(os.arch()), - manifest: tc.IToolRelease[] | undefined -): Promise { - let info: INodeVersionInfo | null = null; - if (!manifest) { - core.debug('No manifest cached'); - manifest = await getManifest(auth); - } - - const rel = await tc.findFromManifest(versionSpec, stable, manifest, osArch); - - if (rel && rel.files.length > 0) { - info = {}; - info.resolvedVersion = rel.version; - info.arch = rel.files[0].arch; - info.downloadUrl = rel.files[0].download_url; - info.fileName = rel.files[0].filename; - } - - return info; -} - -async function getInfoFromDist( - versionSpec: string, - arch: string = os.arch(), - nodeVersions?: INodeVersion[] -): Promise { - let osPlat: string = os.platform(); - let osArch: string = translateArchToDistUrl(arch); - - let version: string = await queryDistForMatch( - versionSpec, - arch, - nodeVersions - ); - - if (!version) { - return null; - } - - // - // Download - a tool installer intimately knows how to get the tool (and construct urls) - // - version = semver.clean(version) || ''; - let fileName: string = - osPlat == 'win32' - ? `node-v${version}-win-${osArch}` - : `node-v${version}-${osPlat}-${osArch}`; - let urlFileName: string = - osPlat == 'win32' ? `${fileName}.7z` : `${fileName}.tar.gz`; - const initialUrl = getNodejsDistUrl(versionSpec); - const url = `${initialUrl}/v${version}/${urlFileName}`; - - return { - downloadUrl: url, - resolvedVersion: version, - arch: arch, - fileName: fileName - }; -} - -async function resolveVersionFromManifest( - versionSpec: string, - stable: boolean, - auth: string | undefined, - osArch: string = translateArchToDistUrl(os.arch()), - manifest: tc.IToolRelease[] | undefined -): Promise { - try { - const info = await getInfoFromManifest( - versionSpec, - stable, - auth, - osArch, - manifest - ); - return info?.resolvedVersion; - } catch (err) { - core.info('Unable to resolve version from manifest...'); - core.debug(err.message); - } -} - -// TODO - should we just export this from @actions/tool-cache? Lifted directly from there -// - the answer from dsame@github.com - we have customized matcher and can not -// export `evaluateVersions` from tc. But it would be possible to modify tc to accept -// the matcher as an optional parameter to `evaluateVersions` -export function evaluateVersions( - versions: string[], - versionSpec: string -): string { - core.debug(`evaluating ${versions.length} versions`); - - const matcher = versionMatcherFactory(versionSpec); - const version = versions.sort(semver.rcompare).find(matcher) || ''; - - if (version) { - core.debug(`matched: ${version}`); - } else { - core.debug('match not found'); - } - - return version; -} - -export function getNodejsDistUrl(version: string) { - switch (distributionOf(version)) { - case Distributions.CANARY: - return 'https://nodejs.org/download/v8-canary'; - case Distributions.NIGHTLY: - return 'https://nodejs.org/download/nightly'; - case Distributions.RC: - return 'https://nodejs.org/download/rc'; - case Distributions.DEFAULT: - return 'https://nodejs.org/dist'; - } -} - -export async function queryDistForMatch( - versionSpec: string, - arch: string = os.arch(), - nodeVersions?: INodeVersion[] -): Promise { - let osPlat: string = os.platform(); - let osArch: string = translateArchToDistUrl(arch); - - // node offers a json list of versions - let dataFileName: string; - switch (osPlat) { - case 'linux': - dataFileName = `linux-${osArch}`; - break; - case 'darwin': - dataFileName = `osx-${osArch}-tar`; - break; - case 'win32': - dataFileName = `win-${osArch}-exe`; - break; - default: - throw new Error(`Unexpected OS '${osPlat}'`); - } - - if (!nodeVersions) { - core.debug('No dist manifest cached'); - nodeVersions = await getVersionsFromDist(versionSpec); - } - - if (isLatestSyntax(versionSpec)) { - core.info(`getting latest node version...`); - return nodeVersions[0].version; - } - - const versions: string[] = []; - nodeVersions.forEach((nodeVersion: INodeVersion) => { - // ensure this version supports your os and platform - if (nodeVersion.files.indexOf(dataFileName) >= 0) { - versions.push(nodeVersion.version); - } - }); - - // get the latest version that matches the version spec - const version = evaluateVersions(versions, versionSpec); - return version; -} - -export async function getVersionsFromDist( - versionSpec: string -): Promise { - const distUrl = getNodejsDistUrl(versionSpec); - const dataUrl = `${distUrl}/index.json`; - let httpClient = new hc.HttpClient('setup-node', [], { - allowRetries: true, - maxRetries: 3 - }); - let response = await httpClient.getJson(dataUrl); - return response.result || []; -} - -// For non LTS versions of Node, the files we need (for Windows) are sometimes located -// in a different folder than they normally are for other versions. -// Normally the format is similar to: https://nodejs.org/dist/v5.10.1/node-v5.10.1-win-x64.7z -// In this case, there will be two files located at: -// /dist/v5.10.1/win-x64/node.exe -// /dist/v5.10.1/win-x64/node.lib -// If this is not the structure, there may also be two files located at: -// /dist/v0.12.18/node.exe -// /dist/v0.12.18/node.lib -// This method attempts to download and cache the resources from these alternative locations. -// Note also that the files are normally zipped but in this case they are just an exe -// and lib file in a folder, not zipped. -async function acquireNodeFromFallbackLocation( - version: string, - arch: string = os.arch() -): Promise { - const initialUrl = getNodejsDistUrl(version); - let osPlat: string = os.platform(); - let osArch: string = translateArchToDistUrl(arch); - - // Create temporary folder to download in to - const tempDownloadFolder: string = - 'temp_' + Math.floor(Math.random() * 2000000000); - const tempDirectory = process.env['RUNNER_TEMP'] || ''; - assert.ok(tempDirectory, 'Expected RUNNER_TEMP to be defined'); - const tempDir: string = path.join(tempDirectory, tempDownloadFolder); - await io.mkdirP(tempDir); - let exeUrl: string; - let libUrl: string; - try { - exeUrl = `${initialUrl}/v${version}/win-${osArch}/node.exe`; - libUrl = `${initialUrl}/v${version}/win-${osArch}/node.lib`; - - core.info(`Downloading only node binary from ${exeUrl}`); - - const exePath = await tc.downloadTool(exeUrl); - await io.cp(exePath, path.join(tempDir, 'node.exe')); - const libPath = await tc.downloadTool(libUrl); - await io.cp(libPath, path.join(tempDir, 'node.lib')); - } catch (err) { - if (err instanceof tc.HTTPError && err.httpStatusCode == 404) { - exeUrl = `${initialUrl}/v${version}/node.exe`; - libUrl = `${initialUrl}/v${version}/node.lib`; - - const exePath = await tc.downloadTool(exeUrl); - await io.cp(exePath, path.join(tempDir, 'node.exe')); - const libPath = await tc.downloadTool(libUrl); - await io.cp(libPath, path.join(tempDir, 'node.lib')); - } else { - throw err; - } - } - let toolPath = await tc.cacheDir(tempDir, 'node', version, arch); - core.addPath(toolPath); - return toolPath; -} - -// os.arch does not always match the relative download url, e.g. -// os.arch == 'arm' != node-v12.13.1-linux-armv7l.tar.gz -// All other currently supported architectures match, e.g.: -// os.arch = arm64 => https://nodejs.org/dist/v{VERSION}/node-v{VERSION}-{OS}-arm64.tar.gz -// os.arch = x64 => https://nodejs.org/dist/v{VERSION}/node-v{VERSION}-{OS}-x64.tar.gz -function translateArchToDistUrl(arch: string): string { - switch (arch) { - case 'arm': - return 'armv7l'; - default: - return arch; - } -} - -export function parseNodeVersionFile(contents: string): string { - let nodeVersion: string | undefined; - - // Try parsing the file as an NPM `package.json` file. - try { - nodeVersion = JSON.parse(contents).volta?.node; - if (!nodeVersion) nodeVersion = JSON.parse(contents).engines?.node; - } catch { - core.info('Node version file is not JSON file'); - } - - if (!nodeVersion) { - const found = contents.match(/^(?:nodejs\s+)?v?(?[^\s]+)$/m); - nodeVersion = found?.groups?.version; - } - - // In the case of an unknown format, - // return as is and evaluate the version separately. - if (!nodeVersion) nodeVersion = contents.trim(); - - return nodeVersion as string; -} - -export function isLatestSyntax(versionSpec): boolean { - return ['current', 'latest', 'node'].includes(versionSpec); -} diff --git a/src/main.ts b/src/main.ts index fb7e9286..e2c98579 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,12 +1,13 @@ import * as core from '@actions/core'; import * as exec from '@actions/exec'; -import * as installer from './installer'; + import fs from 'fs'; +import os from 'os'; + import * as auth from './authutil'; import * as path from 'path'; import {restoreCache} from './cache-restore'; -import {isGhes, isCacheFeatureAvailable} from './cache-utils'; -import os from 'os'; +import {isCacheFeatureAvailable} from './cache-utils'; import {getNodejsDistribution} from './distibutions/installer-factory'; export async function run() { @@ -49,8 +50,6 @@ export async function run() { } else { throw new Error(`Could not resolve version: ${version} for build`); } - - // await installer.getNode(version, stable, checkLatest, auth, arch); } await printEnvDetailsAndSetOutput(); @@ -105,9 +104,7 @@ function resolveVersionInput(): string { ); } - version = installer.parseNodeVersionFile( - fs.readFileSync(versionFilePath, 'utf8') - ); + version = parseNodeVersionFile(fs.readFileSync(versionFilePath, 'utf8')); core.info(`Resolved ${versionFileInput} as ${version}`); } @@ -115,6 +112,29 @@ function resolveVersionInput(): string { return version; } +export function parseNodeVersionFile(contents: string): string { + let nodeVersion: string | undefined; + + // Try parsing the file as an NPM `package.json` file. + try { + nodeVersion = JSON.parse(contents).volta?.node; + if (!nodeVersion) nodeVersion = JSON.parse(contents).engines?.node; + } catch { + core.info('Node version file is not JSON file'); + } + + if (!nodeVersion) { + const found = contents.match(/^(?:nodejs\s+)?v?(?[^\s]+)$/m); + nodeVersion = found?.groups?.version; + } + + // In the case of an unknown format, + // return as is and evaluate the version separately. + if (!nodeVersion) nodeVersion = contents.trim(); + + return nodeVersion as string; +} + export async function printEnvDetailsAndSetOutput() { core.startGroup('Environment details');