mirror of
https://github.com/actions/setup-node.git
synced 2025-04-22 09:21:00 +00:00
Expand node version file support to include lts based codenames
This commit is contained in:
parent
c3812bd36a
commit
a0d376d3fa
7 changed files with 311 additions and 43 deletions
|
@ -6,7 +6,8 @@ import cp from 'child_process';
|
|||
import osm = require('os');
|
||||
import path from 'path';
|
||||
import * as main from '../src/main';
|
||||
import * as im from '../src/installer';
|
||||
import * as nv from '../src/node-version';
|
||||
import * as nvf from '../src/node-version-file';
|
||||
import * as auth from '../src/authutil';
|
||||
import {context} from '@actions/github';
|
||||
|
||||
|
@ -40,6 +41,7 @@ describe('setup-node', () => {
|
|||
let mkdirpSpy: jest.SpyInstance;
|
||||
let execSpy: jest.SpyInstance;
|
||||
let authSpy: jest.SpyInstance;
|
||||
let parseNodeVersionSpy: jest.SpyInstance;
|
||||
|
||||
beforeEach(() => {
|
||||
// @actions/core
|
||||
|
@ -63,7 +65,8 @@ describe('setup-node', () => {
|
|||
exSpy = jest.spyOn(tc, 'extractTar');
|
||||
cacheSpy = jest.spyOn(tc, 'cacheDir');
|
||||
getManifestSpy = jest.spyOn(tc, 'getManifestFromRepo');
|
||||
getDistSpy = jest.spyOn(im, 'getVersionsFromDist');
|
||||
getDistSpy = jest.spyOn(nv, 'getVersionsFromDist');
|
||||
parseNodeVersionSpy = jest.spyOn(nvf, 'parseNodeVersionFile');
|
||||
|
||||
// io
|
||||
whichSpy = jest.spyOn(io, 'which');
|
||||
|
@ -79,7 +82,7 @@ describe('setup-node', () => {
|
|||
getManifestSpy.mockImplementation(
|
||||
() => <tc.IToolRelease[]>nodeTestManifest
|
||||
);
|
||||
getDistSpy.mockImplementation(() => <im.INodeVersion>nodeTestDist);
|
||||
getDistSpy.mockImplementation(() => <nv.INodeVersion>nodeTestDist);
|
||||
|
||||
// writes
|
||||
cnSpy = jest.spyOn(process.stdout, 'write');
|
||||
|
@ -124,7 +127,7 @@ describe('setup-node', () => {
|
|||
});
|
||||
|
||||
it('can mock dist versions', async () => {
|
||||
let versions: im.INodeVersion[] = await im.getVersionsFromDist();
|
||||
let versions: nv.INodeVersion[] = await nv.getVersionsFromDist();
|
||||
expect(versions).toBeDefined();
|
||||
expect(versions?.length).toBe(23);
|
||||
});
|
||||
|
@ -517,10 +520,12 @@ describe('setup-node', () => {
|
|||
// Arrange
|
||||
const versionSpec = 'v12';
|
||||
const versionFile = '.nvmrc';
|
||||
const expectedVersionSpec = '12';
|
||||
|
||||
inputs['node-version-file'] = versionFile;
|
||||
|
||||
readFileSyncSpy.mockImplementation(() => versionSpec);
|
||||
parseNodeVersionSpy.mockImplementation(() => expectedVersionSpec);
|
||||
|
||||
// Act
|
||||
await main.run();
|
||||
|
@ -531,8 +536,9 @@ describe('setup-node', () => {
|
|||
path.join(__dirname, '..', versionFile),
|
||||
'utf8'
|
||||
);
|
||||
expect(parseNodeVersionSpy).toHaveBeenCalledWith(versionSpec);
|
||||
expect(logSpy).toHaveBeenCalledWith(
|
||||
`Resolved ${versionFile} as ${versionSpec}`
|
||||
`Resolved ${versionFile} as ${expectedVersionSpec}`
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
88
__tests__/node-version-file.test.ts
Normal file
88
__tests__/node-version-file.test.ts
Normal file
|
@ -0,0 +1,88 @@
|
|||
import * as nv from '../src/node-version';
|
||||
import * as nvf from '../src/node-version-file';
|
||||
|
||||
let nodeTestDist = require('./data/node-dist-index.json');
|
||||
|
||||
describe('node-version-file', () => {
|
||||
let getVersionsFromDist: jest.SpyInstance;
|
||||
|
||||
beforeEach(() => {
|
||||
// @actions/core
|
||||
console.log('::stop-commands::stoptoken'); // Disable executing of runner commands when running tests in actions
|
||||
|
||||
getVersionsFromDist = jest.spyOn(nv, 'getVersionsFromDist');
|
||||
|
||||
// gets
|
||||
getVersionsFromDist.mockImplementation(() => <nv.INodeVersion>nodeTestDist);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.resetAllMocks();
|
||||
jest.clearAllMocks();
|
||||
//jest.restoreAllMocks();
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
console.log('::stoptoken::'); // Re-enable executing of runner commands when running tests in actions
|
||||
}, 100000);
|
||||
|
||||
//--------------------------------------------------
|
||||
// Manifest find tests
|
||||
//--------------------------------------------------
|
||||
describe('parseNodeVersionFile', () => {
|
||||
it('without `v` prefix', async () => {
|
||||
// Arrange
|
||||
const versionSpec = '12';
|
||||
|
||||
// Act
|
||||
const result = await nvf.parseNodeVersionFile(versionSpec);
|
||||
|
||||
// Assert
|
||||
expect(result).toBe(versionSpec);
|
||||
});
|
||||
|
||||
it('lts/*', async () => {
|
||||
// Arrange
|
||||
const versionSpec = 'lts/*';
|
||||
|
||||
// Act
|
||||
const result = await nvf.parseNodeVersionFile(versionSpec);
|
||||
|
||||
// Assert
|
||||
expect(result).toMatch(/^\d+\.\d+\.\d+$/);
|
||||
});
|
||||
|
||||
it('lts/erbium', async () => {
|
||||
// Arrange
|
||||
const versionSpec = 'lts/*';
|
||||
|
||||
// Act
|
||||
const result = await nvf.parseNodeVersionFile(versionSpec);
|
||||
|
||||
// Assert
|
||||
expect(result).toMatch(/\d\.\d\.\d/);
|
||||
});
|
||||
|
||||
it('partial syntax like 12', async () => {
|
||||
// Arrange
|
||||
const versionSpec = '12';
|
||||
|
||||
// Act
|
||||
const result = await nvf.parseNodeVersionFile(versionSpec);
|
||||
|
||||
// Assert
|
||||
expect(result).toBe(versionSpec);
|
||||
});
|
||||
|
||||
it('partial syntax like 12.16', async () => {
|
||||
// Arrange
|
||||
const versionSpec = '12.16';
|
||||
|
||||
// Act
|
||||
const result = await nvf.parseNodeVersionFile(versionSpec);
|
||||
|
||||
// Assert
|
||||
expect(result).toBe(versionSpec);
|
||||
});
|
||||
});
|
||||
});
|
136
dist/index.js
vendored
136
dist/index.js
vendored
|
@ -3028,6 +3028,82 @@ const windowsRelease = release => {
|
|||
module.exports = windowsRelease;
|
||||
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ 74:
|
||||
/***/ (function(__unusedmodule, exports, __webpack_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 __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 semvar = __importStar(__webpack_require__(280));
|
||||
const node_version_1 = __webpack_require__(708);
|
||||
function parseNodeVersionFile(contents) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
contents = contents.trim();
|
||||
if (/^v\d/.test(contents)) {
|
||||
contents = contents.substring(1);
|
||||
}
|
||||
const nodeVersions = yield node_version_1.getVersionsFromDist();
|
||||
let nodeVersion;
|
||||
if (contents.startsWith('lts/')) {
|
||||
nodeVersion = findLatestLts(nodeVersions, contents).version;
|
||||
}
|
||||
else if (semvar.valid(contents) || isPartialMatch(contents)) {
|
||||
nodeVersion = contents;
|
||||
}
|
||||
else {
|
||||
throw new Error(`Couldn't resolve node version: '${contents}'`);
|
||||
}
|
||||
return stripVPrefix(nodeVersion);
|
||||
});
|
||||
}
|
||||
exports.parseNodeVersionFile = parseNodeVersionFile;
|
||||
function findLatestLts(nodeVersions, codename) {
|
||||
let nodeVersion;
|
||||
if (codename === 'lts/*') {
|
||||
nodeVersion = nodeVersions.reduce((latest, nodeVersion) => {
|
||||
if (!nodeVersion.lts) {
|
||||
return latest;
|
||||
}
|
||||
return semvar.gt(nodeVersion.version, latest.version)
|
||||
? nodeVersion
|
||||
: latest;
|
||||
});
|
||||
}
|
||||
else {
|
||||
codename = codename.replace('lts/', '').toLowerCase();
|
||||
nodeVersion = nodeVersions.find(nodeVersion => `${nodeVersion.lts}`.toLowerCase() === codename);
|
||||
}
|
||||
if (!nodeVersion) {
|
||||
throw new Error(`Couldn't find matching release for codename: '${codename}'`);
|
||||
}
|
||||
return nodeVersion;
|
||||
}
|
||||
function isPartialMatch(version) {
|
||||
return /^\d+(\.\d+(\.\d+)?)?$/.test(version);
|
||||
}
|
||||
function stripVPrefix(version) {
|
||||
return /^v\d/.test(version) ? version.substring(1) : version;
|
||||
}
|
||||
//# sourceMappingURL=node-version-file.js.map
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ 82:
|
||||
|
@ -4695,6 +4771,7 @@ const auth = __importStar(__webpack_require__(202));
|
|||
const fs = __webpack_require__(747);
|
||||
const path = __importStar(__webpack_require__(622));
|
||||
const url_1 = __webpack_require__(835);
|
||||
const node_version_file_1 = __webpack_require__(74);
|
||||
function run() {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
try {
|
||||
|
@ -4710,7 +4787,7 @@ function run() {
|
|||
const versionFile = core.getInput('node-version-file');
|
||||
if (!!versionFile) {
|
||||
const versionFilePath = path.join(__dirname, '..', versionFile);
|
||||
version = fs.readFileSync(versionFilePath, 'utf8');
|
||||
version = yield node_version_file_1.parseNodeVersionFile(fs.readFileSync(versionFilePath, 'utf8'));
|
||||
core.info(`Resolved ${versionFile} as ${version}`);
|
||||
}
|
||||
}
|
||||
|
@ -12969,6 +13046,45 @@ module.exports = {"activity":{"checkStarringRepo":{"method":"GET","params":{"own
|
|||
|
||||
/***/ }),
|
||||
|
||||
/***/ 708:
|
||||
/***/ (function(__unusedmodule, exports, __webpack_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 __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 hc = __importStar(__webpack_require__(539));
|
||||
function getVersionsFromDist() {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
let dataUrl = 'https://nodejs.org/dist/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;
|
||||
//# sourceMappingURL=node-version.js.map
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ 722:
|
||||
/***/ (function(module) {
|
||||
|
||||
|
@ -13072,7 +13188,7 @@ module.exports = require("fs");
|
|||
/***/ }),
|
||||
|
||||
/***/ 749:
|
||||
/***/ (function(module, exports, __webpack_require__) {
|
||||
/***/ (function(__unusedmodule, exports, __webpack_require__) {
|
||||
|
||||
"use strict";
|
||||
|
||||
|
@ -13096,12 +13212,12 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|||
const os = __webpack_require__(87);
|
||||
const assert = __importStar(__webpack_require__(357));
|
||||
const core = __importStar(__webpack_require__(470));
|
||||
const hc = __importStar(__webpack_require__(539));
|
||||
const io = __importStar(__webpack_require__(1));
|
||||
const tc = __importStar(__webpack_require__(533));
|
||||
const path = __importStar(__webpack_require__(622));
|
||||
const semver = __importStar(__webpack_require__(280));
|
||||
const fs = __webpack_require__(747);
|
||||
const node_version_1 = __webpack_require__(708);
|
||||
function getNode(versionSpec, stable, checkLatest, auth) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
let osPlat = os.platform();
|
||||
|
@ -13312,7 +13428,7 @@ function queryDistForMatch(versionSpec) {
|
|||
throw new Error(`Unexpected OS '${osPlat}'`);
|
||||
}
|
||||
let versions = [];
|
||||
let nodeVersions = yield module.exports.getVersionsFromDist();
|
||||
let nodeVersions = yield node_version_1.getVersionsFromDist();
|
||||
nodeVersions.forEach((nodeVersion) => {
|
||||
// ensure this version supports your os and platform
|
||||
if (nodeVersion.files.indexOf(dataFileName) >= 0) {
|
||||
|
@ -13324,18 +13440,6 @@ function queryDistForMatch(versionSpec) {
|
|||
return version;
|
||||
});
|
||||
}
|
||||
function getVersionsFromDist() {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
let dataUrl = 'https://nodejs.org/dist/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
|
||||
|
|
|
@ -1,21 +1,12 @@
|
|||
import os = require('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 = require('fs');
|
||||
|
||||
//
|
||||
// Node versions interface
|
||||
// see https://nodejs.org/dist/index.json
|
||||
//
|
||||
export interface INodeVersion {
|
||||
version: string;
|
||||
files: string[];
|
||||
}
|
||||
import {INodeVersion, getVersionsFromDist} from './node-version';
|
||||
|
||||
interface INodeVersionInfo {
|
||||
downloadUrl: string;
|
||||
|
@ -274,7 +265,7 @@ async function queryDistForMatch(versionSpec: string): Promise<string> {
|
|||
}
|
||||
|
||||
let versions: string[] = [];
|
||||
let nodeVersions = await module.exports.getVersionsFromDist();
|
||||
let nodeVersions = await getVersionsFromDist();
|
||||
|
||||
nodeVersions.forEach((nodeVersion: INodeVersion) => {
|
||||
// ensure this version supports your os and platform
|
||||
|
@ -288,16 +279,6 @@ async function queryDistForMatch(versionSpec: string): Promise<string> {
|
|||
return version;
|
||||
}
|
||||
|
||||
export async function getVersionsFromDist(): Promise<INodeVersion[]> {
|
||||
let dataUrl = 'https://nodejs.org/dist/index.json';
|
||||
let httpClient = new hc.HttpClient('setup-node', [], {
|
||||
allowRetries: true,
|
||||
maxRetries: 3
|
||||
});
|
||||
let response = await httpClient.getJson<INodeVersion[]>(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
|
||||
|
|
|
@ -4,6 +4,7 @@ import * as auth from './authutil';
|
|||
import fs = require('fs');
|
||||
import * as path from 'path';
|
||||
import {URL} from 'url';
|
||||
import {parseNodeVersionFile} from './node-version-file';
|
||||
|
||||
export async function run() {
|
||||
try {
|
||||
|
@ -21,7 +22,9 @@ export async function run() {
|
|||
|
||||
if (!!versionFile) {
|
||||
const versionFilePath = path.join(__dirname, '..', versionFile);
|
||||
version = fs.readFileSync(versionFilePath, 'utf8');
|
||||
version = await parseNodeVersionFile(
|
||||
fs.readFileSync(versionFilePath, 'utf8')
|
||||
);
|
||||
core.info(`Resolved ${versionFile} as ${version}`);
|
||||
}
|
||||
}
|
||||
|
|
65
src/node-version-file.ts
Normal file
65
src/node-version-file.ts
Normal file
|
@ -0,0 +1,65 @@
|
|||
import * as semvar from 'semver';
|
||||
import {INodeVersion, getVersionsFromDist} from './node-version';
|
||||
|
||||
export async function parseNodeVersionFile(contents: string): Promise<string> {
|
||||
contents = contents.trim();
|
||||
|
||||
if (/^v\d/.test(contents)) {
|
||||
contents = contents.substring(1);
|
||||
}
|
||||
|
||||
const nodeVersions = await getVersionsFromDist();
|
||||
|
||||
let nodeVersion: string;
|
||||
|
||||
if (contents.startsWith('lts/')) {
|
||||
nodeVersion = findLatestLts(nodeVersions, contents).version;
|
||||
} else if (semvar.valid(contents) || isPartialMatch(contents)) {
|
||||
nodeVersion = contents;
|
||||
} else {
|
||||
throw new Error(`Couldn't resolve node version: '${contents}'`);
|
||||
}
|
||||
|
||||
return stripVPrefix(nodeVersion);
|
||||
}
|
||||
|
||||
function findLatestLts(
|
||||
nodeVersions: INodeVersion[],
|
||||
codename: string
|
||||
): INodeVersion {
|
||||
let nodeVersion: INodeVersion | undefined;
|
||||
|
||||
if (codename === 'lts/*') {
|
||||
nodeVersion = nodeVersions.reduce((latest, nodeVersion) => {
|
||||
if (!nodeVersion.lts) {
|
||||
return latest;
|
||||
}
|
||||
|
||||
return semvar.gt(nodeVersion.version, latest.version)
|
||||
? nodeVersion
|
||||
: latest;
|
||||
});
|
||||
} else {
|
||||
codename = codename.replace('lts/', '').toLowerCase();
|
||||
|
||||
nodeVersion = nodeVersions.find(
|
||||
nodeVersion => `${nodeVersion.lts}`.toLowerCase() === codename
|
||||
);
|
||||
}
|
||||
|
||||
if (!nodeVersion) {
|
||||
throw new Error(
|
||||
`Couldn't find matching release for codename: '${codename}'`
|
||||
);
|
||||
}
|
||||
|
||||
return nodeVersion;
|
||||
}
|
||||
|
||||
function isPartialMatch(version: string): boolean {
|
||||
return /^\d+(\.\d+(\.\d+)?)?$/.test(version);
|
||||
}
|
||||
|
||||
function stripVPrefix(version: string): string {
|
||||
return /^v\d/.test(version) ? version.substring(1) : version;
|
||||
}
|
21
src/node-version.ts
Normal file
21
src/node-version.ts
Normal file
|
@ -0,0 +1,21 @@
|
|||
import * as hc from '@actions/http-client';
|
||||
|
||||
//
|
||||
// Node versions interface
|
||||
// see https://nodejs.org/dist/index.json
|
||||
//
|
||||
export interface INodeVersion {
|
||||
version: string;
|
||||
files: string[];
|
||||
lts: boolean | string;
|
||||
}
|
||||
|
||||
export async function getVersionsFromDist(): Promise<INodeVersion[]> {
|
||||
let dataUrl = 'https://nodejs.org/dist/index.json';
|
||||
let httpClient = new hc.HttpClient('setup-node', [], {
|
||||
allowRetries: true,
|
||||
maxRetries: 3
|
||||
});
|
||||
let response = await httpClient.getJson<INodeVersion[]>(dataUrl);
|
||||
return response.result || [];
|
||||
}
|
Loading…
Add table
Reference in a new issue