mirror of https://github.com/actions/setup-node
wip
This commit is contained in:
parent
339aa41a8d
commit
ecf0ce62f9
|
@ -2,6 +2,9 @@ node_modules/
|
||||||
lib/
|
lib/
|
||||||
__tests__/runner/*
|
__tests__/runner/*
|
||||||
|
|
||||||
|
validate/temp
|
||||||
|
validate/node
|
||||||
|
|
||||||
# Rest of the file pulled from https://github.com/github/gitignore/blob/master/Node.gitignore
|
# Rest of the file pulled from https://github.com/github/gitignore/blob/master/Node.gitignore
|
||||||
# Logs
|
# Logs
|
||||||
logs
|
logs
|
||||||
|
|
10
action.yml
10
action.yml
|
@ -1,17 +1,21 @@
|
||||||
name: 'Setup Node.js environment'
|
name: 'Setup Node.js environment'
|
||||||
description: 'Setup a Node.js environment and add it to the PATH, additionally providing proxy support'
|
description: 'Setup a Node.js environment by adding problem matchers and optionally downloading and adding it to the PATH'
|
||||||
author: 'GitHub'
|
author: 'GitHub'
|
||||||
inputs:
|
inputs:
|
||||||
always-auth:
|
always-auth:
|
||||||
description: 'Set always-auth in npmrc'
|
description: 'Set always-auth in npmrc'
|
||||||
default: 'false'
|
default: 'false'
|
||||||
node-version:
|
node-version:
|
||||||
description: 'Version Spec of the version to use. Examples: 10.x, 10.15.1, >=10.15.0'
|
description: 'Version Spec of the version to use. Examples: 12.x, 10.15.1, >=10.15.0'
|
||||||
default: '10.x'
|
|
||||||
registry-url:
|
registry-url:
|
||||||
description: 'Optional registry to set up for auth. Will set the registry in a project level .npmrc and .yarnrc file, and set up auth to read in from env.NODE_AUTH_TOKEN'
|
description: 'Optional registry to set up for auth. Will set the registry in a project level .npmrc and .yarnrc file, and set up auth to read in from env.NODE_AUTH_TOKEN'
|
||||||
scope:
|
scope:
|
||||||
description: 'Optional scope for authenticating against scoped registries'
|
description: 'Optional scope for authenticating against scoped registries'
|
||||||
|
token:
|
||||||
|
description: Used to pull node distributions from node-versions. Since there's a default, this is typically not supplied by the user.
|
||||||
|
default: ${{ github.token }}
|
||||||
|
# TODO: add input to control forcing to pull from cloud or dist.
|
||||||
|
# escape valve for someone having issues or needing the absolute latest which isn't cached yet
|
||||||
# Deprecated option, do not use. Will not be supported after October 1, 2019
|
# Deprecated option, do not use. Will not be supported after October 1, 2019
|
||||||
version:
|
version:
|
||||||
description: 'Deprecated. Use node-version instead. Will not be supported after October 1, 2019'
|
description: 'Deprecated. Use node-version instead. Will not be supported after October 1, 2019'
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "setup-node",
|
"name": "setup-node",
|
||||||
"version": "1.0.0",
|
"version": "2.0.0",
|
||||||
"lockfileVersion": 1,
|
"lockfileVersion": 1,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
@ -40,16 +40,26 @@
|
||||||
"integrity": "sha512-J8KuFqVPr3p6U8W93DOXlXW6zFvrQAJANdS+vw0YhusLIq+bszW8zmK2Fh1C2kDPX8FMvwIl1OUcFgvJoXLbAg=="
|
"integrity": "sha512-J8KuFqVPr3p6U8W93DOXlXW6zFvrQAJANdS+vw0YhusLIq+bszW8zmK2Fh1C2kDPX8FMvwIl1OUcFgvJoXLbAg=="
|
||||||
},
|
},
|
||||||
"@actions/tool-cache": {
|
"@actions/tool-cache": {
|
||||||
"version": "1.3.3",
|
"version": "1.5.2",
|
||||||
"resolved": "https://registry.npmjs.org/@actions/tool-cache/-/tool-cache-1.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/@actions/tool-cache/-/tool-cache-1.5.2.tgz",
|
||||||
"integrity": "sha512-AFVyTLcIxusDVI1gMhbZW8m/On7YNJG+xYaxorV+qic+f7lO7h37aT2mfzxqAq7mwHxtP1YlVFNrXe9QDf/bPg==",
|
"integrity": "sha512-40A1St0GEo+QvHV1YRjStEoQcKKMaip+zNXPgGHcjYXCdZ7cl1LGlwOpsVVqwk+6ue/shFTS76KC1A02mVVCQA==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@actions/core": "^1.2.0",
|
"@actions/core": "^1.2.0",
|
||||||
"@actions/exec": "^1.0.0",
|
"@actions/exec": "^1.0.0",
|
||||||
"@actions/http-client": "^1.0.3",
|
"@actions/http-client": "^1.0.8",
|
||||||
"@actions/io": "^1.0.1",
|
"@actions/io": "^1.0.1",
|
||||||
"semver": "^6.1.0",
|
"semver": "^6.1.0",
|
||||||
"uuid": "^3.3.2"
|
"uuid": "^3.3.2"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@actions/http-client": {
|
||||||
|
"version": "1.0.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-1.0.8.tgz",
|
||||||
|
"integrity": "sha512-G4JjJ6f9Hb3Zvejj+ewLLKLf99ZC+9v+yCxoYf9vSyH+WkzPLB2LuUtRMGNkooMqdugGBFStIKXOuvH1W+EctA==",
|
||||||
|
"requires": {
|
||||||
|
"tunnel": "0.0.6"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@babel/code-frame": {
|
"@babel/code-frame": {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "setup-node",
|
"name": "setup-node",
|
||||||
"version": "1.0.0",
|
"version": "2.0.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"description": "setup node action",
|
"description": "setup node action",
|
||||||
"main": "lib/setup-node.js",
|
"main": "lib/setup-node.js",
|
||||||
|
@ -27,7 +27,7 @@
|
||||||
"@actions/github": "^1.1.0",
|
"@actions/github": "^1.1.0",
|
||||||
"@actions/http-client": "^1.0.6",
|
"@actions/http-client": "^1.0.6",
|
||||||
"@actions/io": "^1.0.2",
|
"@actions/io": "^1.0.2",
|
||||||
"@actions/tool-cache": "^1.3.3",
|
"@actions/tool-cache": "^1.5.2",
|
||||||
"semver": "^6.1.1"
|
"semver": "^6.1.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|
222
src/installer.ts
222
src/installer.ts
|
@ -6,6 +6,7 @@ import * as tc from '@actions/tool-cache';
|
||||||
import * as os from 'os';
|
import * as os from 'os';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import * as semver from 'semver';
|
import * as semver from 'semver';
|
||||||
|
import { Url } from 'url';
|
||||||
|
|
||||||
let osPlat: string = os.platform();
|
let osPlat: string = os.platform();
|
||||||
let osArch: string = translateArchToDistUrl(os.arch());
|
let osArch: string = translateArchToDistUrl(os.arch());
|
||||||
|
@ -19,36 +20,64 @@ interface INodeVersion {
|
||||||
files: string[];
|
files: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getNode(versionSpec: string) {
|
interface INodeVersionInfo {
|
||||||
|
downloadUrl: string;
|
||||||
|
token: string | null;
|
||||||
|
resolvedVersion: string;
|
||||||
|
fileName: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getNode(versionSpec: string, stable: boolean, token: string) {
|
||||||
// check cache
|
// check cache
|
||||||
|
let info: INodeVersionInfo | null = null;
|
||||||
let toolPath: string;
|
let toolPath: string;
|
||||||
toolPath = tc.find('node', versionSpec);
|
toolPath = tc.find('node', versionSpec);
|
||||||
|
|
||||||
// If not found in cache, download
|
// If not found in cache, download
|
||||||
if (!toolPath) {
|
if (toolPath) {
|
||||||
let version: string;
|
console.log(`Found in cache @ ${toolPath}`);
|
||||||
const c = semver.clean(versionSpec) || '';
|
} else {
|
||||||
// If explicit version
|
console.log(`Attempting to download ${versionSpec}...`)
|
||||||
if (semver.valid(c) != null) {
|
let info = await getInfoFromManifest(versionSpec, stable, token);
|
||||||
// version to download
|
if (!info) {
|
||||||
version = versionSpec;
|
console.log('Not found in manifest. Falling back to download directly from Node')
|
||||||
} else {
|
info = await getInfoFromDist(versionSpec);
|
||||||
// query nodejs.org for a matching version
|
}
|
||||||
version = await queryLatestMatch(versionSpec);
|
|
||||||
if (!version) {
|
if (!info) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Unable to find Node version '${versionSpec}' for platform ${osPlat} and architecture ${osArch}.`
|
`Unable to find Node version '${versionSpec}' for platform ${osPlat} and architecture ${osArch}.`
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`Acquiring ${info.resolvedVersion} from ${info.downloadUrl}`);
|
||||||
|
|
||||||
|
let downloadPath = ""
|
||||||
|
try {
|
||||||
|
downloadPath = await tc.downloadTool(info.downloadUrl, undefined, token);
|
||||||
|
} catch (err) {
|
||||||
|
if (err instanceof tc.HTTPError && err.httpStatusCode == 404) {
|
||||||
|
return await acquireNodeFromFallbackLocation(info.resolvedVersion);
|
||||||
}
|
}
|
||||||
|
|
||||||
// check cache
|
throw err;
|
||||||
toolPath = tc.find('node', version);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!toolPath) {
|
//
|
||||||
// download, extract, cache
|
// Extract
|
||||||
toolPath = await acquireNode(version);
|
//
|
||||||
|
let extPath: string;
|
||||||
|
if (osPlat == 'win32') {
|
||||||
|
let _7zPath = path.join(__dirname, '..', 'externals', '7zr.exe');
|
||||||
|
extPath = await tc.extract7z(downloadPath, undefined, _7zPath);
|
||||||
|
} else {
|
||||||
|
extPath = await tc.extractTar(downloadPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Install into the local tool cache - node extracts with a root folder that matches the fileName downloaded
|
||||||
|
//
|
||||||
|
toolPath = await tc.cacheDir(extPath, 'node', info.resolvedVersion);
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
@ -65,7 +94,87 @@ export async function getNode(versionSpec: string) {
|
||||||
core.addPath(toolPath);
|
core.addPath(toolPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function queryLatestMatch(versionSpec: string): Promise<string> {
|
async function getInfoFromManifest(versionSpec: string, stable: boolean, token: string): Promise<INodeVersionInfo | null> {
|
||||||
|
let info: INodeVersionInfo | null = null;
|
||||||
|
const releases = await tc.getManifestFromRepo("actions", "node-versions", token)
|
||||||
|
console.log(`matching ${versionSpec}...`)
|
||||||
|
const rel = await tc.findFromManifest(versionSpec, stable, releases);
|
||||||
|
|
||||||
|
if (rel && rel.files.length > 0) {
|
||||||
|
info = <INodeVersionInfo>{};
|
||||||
|
info.resolvedVersion = rel.version;
|
||||||
|
info.downloadUrl = rel.files[0].download_url;
|
||||||
|
info.fileName = rel.files[0].filename;
|
||||||
|
info.token = token;
|
||||||
|
}
|
||||||
|
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getInfoFromDist(versionSpec: string): Promise<INodeVersionInfo | null> {
|
||||||
|
let info: INodeVersionInfo | null = null;
|
||||||
|
let version: string;
|
||||||
|
|
||||||
|
// If explicit version don't query
|
||||||
|
if (semver.clean(versionSpec) != null) {
|
||||||
|
// version to download
|
||||||
|
version = versionSpec;
|
||||||
|
} else {
|
||||||
|
// query nodejs.org for a matching version
|
||||||
|
version = await queryDistForMatch(versionSpec);
|
||||||
|
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`;
|
||||||
|
let url = `https://nodejs.org/dist/v${version}/${urlFileName}`;
|
||||||
|
|
||||||
|
return <INodeVersionInfo>{
|
||||||
|
downloadUrl: url,
|
||||||
|
resolvedVersion: version,
|
||||||
|
fileName: fileName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO - should we just export this from @actions/tool-cache? Lifted directly from there
|
||||||
|
function evaluateVersions(versions: string[], versionSpec: string): string {
|
||||||
|
let version = '';
|
||||||
|
core.debug(`evaluating ${versions.length} versions`);
|
||||||
|
versions = versions.sort((a, b) => {
|
||||||
|
if (semver.gt(a, b)) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
});
|
||||||
|
for (let i = versions.length - 1; i >= 0; i--) {
|
||||||
|
const potential: string = versions[i];
|
||||||
|
const satisfied: boolean = semver.satisfies(potential, versionSpec);
|
||||||
|
if (satisfied) {
|
||||||
|
version = potential;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (version) {
|
||||||
|
core.debug(`matched: ${version}`);
|
||||||
|
} else {
|
||||||
|
core.debug('match not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
return version;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function queryDistForMatch(versionSpec: string): Promise<string> {
|
||||||
// node offers a json list of versions
|
// node offers a json list of versions
|
||||||
let dataFileName: string;
|
let dataFileName: string;
|
||||||
switch (osPlat) {
|
switch (osPlat) {
|
||||||
|
@ -102,77 +211,6 @@ async function queryLatestMatch(versionSpec: string): Promise<string> {
|
||||||
return version;
|
return version;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO - should we just export this from @actions/tool-cache? Lifted directly from there
|
|
||||||
function evaluateVersions(versions: string[], versionSpec: string): string {
|
|
||||||
let version = '';
|
|
||||||
core.debug(`evaluating ${versions.length} versions`);
|
|
||||||
versions = versions.sort((a, b) => {
|
|
||||||
if (semver.gt(a, b)) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
});
|
|
||||||
for (let i = versions.length - 1; i >= 0; i--) {
|
|
||||||
const potential: string = versions[i];
|
|
||||||
const satisfied: boolean = semver.satisfies(potential, versionSpec);
|
|
||||||
if (satisfied) {
|
|
||||||
version = potential;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (version) {
|
|
||||||
core.debug(`matched: ${version}`);
|
|
||||||
} else {
|
|
||||||
core.debug('match not found');
|
|
||||||
}
|
|
||||||
|
|
||||||
return version;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function acquireNode(version: string): Promise<string> {
|
|
||||||
//
|
|
||||||
// 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`;
|
|
||||||
let downloadUrl = `https://nodejs.org/dist/v${version}/${urlFileName}`;
|
|
||||||
|
|
||||||
let downloadPath: string;
|
|
||||||
|
|
||||||
try {
|
|
||||||
downloadPath = await tc.downloadTool(downloadUrl);
|
|
||||||
} catch (err) {
|
|
||||||
if (err instanceof tc.HTTPError && err.httpStatusCode == 404) {
|
|
||||||
return await acquireNodeFromFallbackLocation(version);
|
|
||||||
}
|
|
||||||
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Extract
|
|
||||||
//
|
|
||||||
let extPath: string;
|
|
||||||
if (osPlat == 'win32') {
|
|
||||||
let _7zPath = path.join(__dirname, '..', 'externals', '7zr.exe');
|
|
||||||
extPath = await tc.extract7z(downloadPath, undefined, _7zPath);
|
|
||||||
} else {
|
|
||||||
extPath = await tc.extractTar(downloadPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Install into the local tool cache - node extracts with a root folder that matches the fileName downloaded
|
|
||||||
//
|
|
||||||
let toolRoot = path.join(extPath, fileName);
|
|
||||||
return await tc.cacheDir(toolRoot, 'node', version);
|
|
||||||
}
|
|
||||||
|
|
||||||
// For non LTS versions of Node, the files we need (for Windows) are sometimes located
|
// 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.
|
// 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
|
// Normally the format is similar to: https://nodejs.org/dist/v5.10.1/node-v5.10.1-win-x64.7z
|
||||||
|
|
|
@ -9,12 +9,16 @@ async function run() {
|
||||||
// Version is optional. If supplied, install / use from the tool cache
|
// Version is optional. If supplied, install / use from the tool cache
|
||||||
// If not supplied then task is still used to setup proxy, auth, etc...
|
// If not supplied then task is still used to setup proxy, auth, etc...
|
||||||
//
|
//
|
||||||
let version = core.getInput('version');
|
let version = core.getInput('node-version');
|
||||||
if (!version) {
|
if (!version) {
|
||||||
version = core.getInput('node-version');
|
version = core.getInput('version');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log(`version: ${version}`);
|
||||||
if (version) {
|
if (version) {
|
||||||
await installer.getNode(version);
|
let token = core.getInput('token');
|
||||||
|
let stable = (core.getInput('stable') || 'true').toUpperCase() === 'TRUE';
|
||||||
|
await installer.getNode(version, stable, token);
|
||||||
}
|
}
|
||||||
|
|
||||||
const registryUrl: string = core.getInput('registry-url');
|
const registryUrl: string = core.getInput('registry-url');
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
|
||||||
|
#/bin/bash
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
rm -rf ./temp
|
||||||
|
rm -rf ./node
|
||||||
|
|
||||||
|
# uncomment to use charles proxy or other debugging proxy
|
||||||
|
# export NODE_TLS_REJECT_UNAUTHORIZED=0
|
||||||
|
# export https_proxy=http://127.0.0.1:8888
|
||||||
|
|
||||||
|
export RUNNER_TOOL_CACHE=$(pwd)
|
||||||
|
export RUNNER_TEMP="${RUNNER_TOOL_CACHE}/temp"
|
||||||
|
export INPUT_STABLE=true
|
||||||
|
export INPUT_VERSION="12.x"
|
||||||
|
# export your PAT with repo scope before running
|
||||||
|
export INPUT_TOKEN=$GITHUB_TOKEN
|
||||||
|
|
||||||
|
echo "Getting ${INPUT_VERSION} ($INPUT_STABLE) with ${INPUT_TOKEN}..."
|
||||||
|
|
||||||
|
node ../dist/index.js
|
Loading…
Reference in New Issue