├── src
├── setup-node.ts
├── main.ts
├── authutil.ts
└── installer.ts
├── externals
└── 7zr.exe
├── .prettierrc.json
├── jest.config.js
├── .github
├── tsc.json
├── eslint-compact.json
├── eslint-stylish.json
└── workflows
│ ├── build-test.yml
│ ├── proxy.yml
│ └── versions.yml
├── __tests__
├── verify-node.sh
├── verify-no-unstaged-changes.sh
├── authutil.test.ts
├── data
│ ├── versions-manifest.json
│ └── node-dist-index.json
└── installer.test.ts
├── validate
└── test.sh
├── .vscode
└── launch.json
├── LICENSE
├── tsconfig.json
├── docs
└── contributors.md
├── package.json
├── action.yml
├── .gitignore
├── CONDUCT
└── README.md
/src/setup-node.ts:
--------------------------------------------------------------------------------
1 | import {run} from './main';
2 |
3 | run();
4 |
--------------------------------------------------------------------------------
/externals/7zr.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oyyd/setup-node/master/externals/7zr.exe
--------------------------------------------------------------------------------
/.prettierrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "printWidth": 80,
3 | "tabWidth": 2,
4 | "useTabs": false,
5 | "semi": true,
6 | "singleQuote": true,
7 | "trailingComma": "none",
8 | "bracketSpacing": false,
9 | "arrowParens": "avoid",
10 | "parser": "typescript"
11 | }
--------------------------------------------------------------------------------
/jest.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | clearMocks: true,
3 | moduleFileExtensions: ['js', 'ts'],
4 | testEnvironment: 'node',
5 | testMatch: ['**/*.test.ts'],
6 | testRunner: 'jest-circus/runner',
7 | transform: {
8 | '^.+\\.ts$': 'ts-jest'
9 | },
10 | verbose: true
11 | }
--------------------------------------------------------------------------------
/.github/tsc.json:
--------------------------------------------------------------------------------
1 | {
2 | "problemMatcher": [
3 | {
4 | "owner": "tsc",
5 | "pattern": [
6 | {
7 | "regexp": "^(?:\\s+\\d+\\>)?([^\\s].*)\\((\\d+|\\d+,\\d+|\\d+,\\d+,\\d+,\\d+)\\)\\s*:\\s+(error|warning|info)\\s+(\\w{1,2}\\d+)\\s*:\\s*(.*)$",
8 | "file": 1,
9 | "location": 2,
10 | "severity": 3,
11 | "code": 4,
12 | "message": 5
13 | }
14 | ]
15 | }
16 | ]
17 | }
--------------------------------------------------------------------------------
/.github/eslint-compact.json:
--------------------------------------------------------------------------------
1 | {
2 | "problemMatcher": [
3 | {
4 | "owner": "eslint-compact",
5 | "pattern": [
6 | {
7 | "regexp": "^(.+):\\sline\\s(\\d+),\\scol\\s(\\d+),\\s(Error|Warning|Info)\\s-\\s(.+)\\s\\((.+)\\)$",
8 | "file": 1,
9 | "line": 2,
10 | "column": 3,
11 | "severity": 4,
12 | "message": 5,
13 | "code": 6
14 | }
15 | ]
16 | }
17 | ]
18 | }
19 |
--------------------------------------------------------------------------------
/__tests__/verify-node.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | if [ -z "$1" ]; then
4 | echo "Must supply node version argument"
5 | exit 1
6 | fi
7 |
8 | node_version="$(node --version)"
9 | echo "Found node version '$node_version'"
10 | if [ -z "$(echo $node_version | grep --fixed-strings v$1)" ]; then
11 | echo "Unexpected version"
12 | exit 1
13 | fi
14 |
15 | if [ -z "$2" ]; then
16 | echo "Testing npm install"
17 | mkdir -p test-npm-install
18 | cd test-npm-install
19 | npm init -y || exit 1
20 | npm install @actions/core || exit 1
21 | else
22 | echo "Skip testing npm"
23 | fi
24 |
--------------------------------------------------------------------------------
/validate/test.sh:
--------------------------------------------------------------------------------
1 |
2 | #/bin/bash
3 |
4 | set -e
5 |
6 | rm -rf ./temp
7 | rm -rf ./node
8 |
9 | # uncomment to use charles proxy or other debugging proxy
10 | # export NODE_TLS_REJECT_UNAUTHORIZED=0
11 | # export https_proxy=http://127.0.0.1:8888
12 |
13 | export RUNNER_TOOL_CACHE=$(pwd)
14 | export RUNNER_TEMP="${RUNNER_TOOL_CACHE}/temp"
15 | export INPUT_STABLE=true
16 | export INPUT_VERSION="12" #"0.12.7" #"12" #"11.15.0"
17 | # export your PAT with repo scope before running
18 | export INPUT_TOKEN=$GITHUB_TOKEN
19 |
20 | echo "Getting ${INPUT_VERSION} ($INPUT_STABLE) with ${INPUT_TOKEN}..."
21 |
22 | node ../dist/index.js
23 |
--------------------------------------------------------------------------------
/.github/eslint-stylish.json:
--------------------------------------------------------------------------------
1 | {
2 | "problemMatcher": [
3 | {
4 | "owner": "eslint-stylish",
5 | "pattern": [
6 | {
7 | "regexp": "^([^\\s].*)$",
8 | "file": 1
9 | },
10 | {
11 | "regexp": "^\\s+(\\d+):(\\d+)\\s+(error|warning|info)\\s+(.*)\\s\\s+(.*)$",
12 | "line": 1,
13 | "column": 2,
14 | "severity": 3,
15 | "message": 4,
16 | "code": 5,
17 | "loop": true
18 | }
19 | ]
20 | }
21 | ]
22 | }
23 |
--------------------------------------------------------------------------------
/__tests__/verify-no-unstaged-changes.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | if [[ "$(git status --porcelain)" != "" ]]; then
4 | echo ----------------------------------------
5 | echo git status
6 | echo ----------------------------------------
7 | git status
8 | echo ----------------------------------------
9 | echo git diff
10 | echo ----------------------------------------
11 | git diff
12 | echo ----------------------------------------
13 | echo Troubleshooting
14 | echo ----------------------------------------
15 | echo "::error::Unstaged changes detected. Locally try running: git clean -ffdx && npm ci && npm run pre-checkin"
16 | exit 1
17 | fi
18 |
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | // Use IntelliSense to learn about possible attributes.
3 | // Hover to view descriptions of existing attributes.
4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5 | "version": "0.2.0",
6 | "configurations": [
7 | {
8 | "name": "Debug Jest Tests on Nix",
9 | "type": "node",
10 | "request": "launch",
11 | "runtimeArgs": [
12 | "--inspect-brk",
13 | "${workspaceRoot}/node_modules/.bin/jest",
14 | "--runInBand"
15 | ],
16 | "console": "integratedTerminal",
17 | "internalConsoleOptions": "neverOpen",
18 | "port": 9229
19 | }
20 | ]
21 | }
--------------------------------------------------------------------------------
/.github/workflows/build-test.yml:
--------------------------------------------------------------------------------
1 | name: build-test
2 |
3 | on:
4 | pull_request:
5 | paths-ignore:
6 | - '**.md'
7 | push:
8 | branches:
9 | - master
10 | - releases/*
11 | paths-ignore:
12 | - '**.md'
13 |
14 | jobs:
15 | build:
16 | runs-on: ${{ matrix.operating-system }}
17 | strategy:
18 | matrix:
19 | operating-system: [ubuntu-latest, windows-latest, macos-latest]
20 | steps:
21 | - uses: actions/checkout@v2
22 | - name: Setup node 12
23 | uses: actions/setup-node@v1
24 | with:
25 | node-version: 12.x
26 | - run: npm ci
27 | - run: npm run build
28 | - run: npm run format-check
29 | - run: npm test
30 | - name: Verify no unstaged changes
31 | if: runner.os != 'windows'
32 | run: __tests__/verify-no-unstaged-changes.sh
33 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 |
2 | The MIT License (MIT)
3 |
4 | Copyright (c) 2018 GitHub, Inc. and contributors
5 |
6 | Permission is hereby granted, free of charge, to any person obtaining a copy
7 | of this software and associated documentation files (the "Software"), to deal
8 | in the Software without restriction, including without limitation the rights
9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | copies of the Software, and to permit persons to whom the Software is
11 | furnished to do so, subject to the following conditions:
12 |
13 | The above copyright notice and this permission notice shall be included in
14 | all copies or substantial portions of the Software.
15 |
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | THE SOFTWARE.
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es6", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */
4 | "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
5 | "lib": [
6 | "es6"
7 | ],
8 | "outDir": "./lib", /* Redirect output structure to the directory. */
9 | "rootDir": "./src", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
10 | "sourceMap": true,
11 | "strict": true, /* Enable all strict type-checking options. */
12 | "noImplicitAny": false, /* Raise error on expressions and declarations with an implied 'any' type. */
13 | "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
14 | },
15 | "exclude": ["__tests__", "lib", "node_modules"]
16 | }
17 |
--------------------------------------------------------------------------------
/docs/contributors.md:
--------------------------------------------------------------------------------
1 | # Contributors
2 |
3 | ### Checkin
4 |
5 | - Do checkin source (src)
6 | - Do checkin build output (lib)
7 | - Do checkin runtime node_modules
8 | - Do not checkin devDependency node_modules (husky can help see below)
9 |
10 | ### devDependencies
11 |
12 | In order to handle correctly checking in node_modules without devDependencies, we run [Husky](https://github.com/typicode/husky) before each commit.
13 | This step ensures that formatting and checkin rules are followed and that devDependencies are excluded. To make sure Husky runs correctly, please use the following workflow:
14 |
15 | ```
16 | npm install # installs all devDependencies including Husky
17 | git add abc.ext # Add the files you've changed. This should include files in src, lib, and node_modules (see above)
18 | git commit -m "Informative commit message" # Commit. This will run Husky
19 | ```
20 |
21 | During the commit step, Husky will take care of formatting all files with [Prettier](https://github.com/prettier/prettier) as well as pruning out devDependencies using `npm prune --production`.
22 | It will also make sure these changes are appropriately included in your commit (no further work is needed)
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "setup-node",
3 | "version": "2.0.0",
4 | "private": true,
5 | "description": "setup node action",
6 | "main": "lib/setup-node.js",
7 | "scripts": {
8 | "build": "tsc && ncc build",
9 | "format": "prettier --write **/*.ts",
10 | "format-check": "prettier --check **/*.ts",
11 | "test": "jest",
12 | "pre-checkin": "npm run format && npm run build && npm test"
13 | },
14 | "repository": {
15 | "type": "git",
16 | "url": "git+https://github.com/actions/setup-node.git"
17 | },
18 | "keywords": [
19 | "actions",
20 | "node",
21 | "setup"
22 | ],
23 | "author": "GitHub",
24 | "license": "MIT",
25 | "dependencies": {
26 | "@actions/core": "^1.2.2",
27 | "@actions/exec": "^1.0.3",
28 | "@actions/github": "^1.1.0",
29 | "@actions/http-client": "^1.0.6",
30 | "@actions/io": "^1.0.2",
31 | "@actions/tool-cache": "^1.5.4",
32 | "semver": "^6.1.1"
33 | },
34 | "devDependencies": {
35 | "@types/jest": "^24.0.13",
36 | "@types/node": "^12.0.4",
37 | "@types/semver": "^6.0.0",
38 | "@zeit/ncc": "^0.21.0",
39 | "jest": "^24.9.0",
40 | "jest-circus": "^24.7.1",
41 | "prettier": "^1.19.1",
42 | "ts-jest": "^24.3.0",
43 | "typescript": "^3.8.3"
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/.github/workflows/proxy.yml:
--------------------------------------------------------------------------------
1 | name: proxy
2 |
3 | on:
4 | pull_request:
5 | paths-ignore:
6 | - '**.md'
7 | push:
8 | branches:
9 | - master
10 | - releases/*
11 | paths-ignore:
12 | - '**.md'
13 |
14 | jobs:
15 | test-proxy:
16 | runs-on: ubuntu-latest
17 | strategy:
18 | fail-fast: false
19 | container:
20 | image: ubuntu:latest
21 | options: --dns 127.0.0.1
22 | services:
23 | squid-proxy:
24 | image: datadog/squid:latest
25 | ports:
26 | - 3128:3128
27 | env:
28 | https_proxy: http://squid-proxy:3128
29 | steps:
30 | - uses: actions/checkout@v2
31 | - name: Clear tool cache
32 | run: rm -rf $RUNNER_TOOL_CACHE/*
33 | - name: Setup node 10
34 | uses: ./
35 | with:
36 | node-version: 10.x
37 | - name: Verify node and npm
38 | run: __tests__/verify-node.sh 10
39 |
40 | test-bypass-proxy:
41 | runs-on: ubuntu-latest
42 | strategy:
43 | fail-fast: false
44 | env:
45 | https_proxy: http://no-such-proxy:3128
46 | no_proxy: api.github.com,github.com,nodejs.org,registry.npmjs.org,*.s3.amazonaws.com,s3.amazonaws.com
47 | steps:
48 | - uses: actions/checkout@v2
49 | - name: Clear tool cache
50 | run: rm -rf $RUNNER_TOOL_CACHE/*
51 | - name: Setup node 11
52 | uses: ./
53 | with:
54 | node-version: 11
55 | - name: Verify node and npm
56 | run: __tests__/verify-node.sh 11
57 |
--------------------------------------------------------------------------------
/action.yml:
--------------------------------------------------------------------------------
1 | name: 'Setup Node.js environment'
2 | description: 'Setup a Node.js environment by adding problem matchers and optionally downloading and adding it to the PATH'
3 | author: 'GitHub'
4 | inputs:
5 | always-auth:
6 | description: 'Set always-auth in npmrc'
7 | default: 'false'
8 | node-version:
9 | description: 'Version Spec of the version to use. Examples: 12.x, 10.15.1, >=10.15.0'
10 | check-latest:
11 | description: 'Set this option if you want the action to check for the latest available version that satisfies the version spec'
12 | default: false
13 | registry-url:
14 | 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'
15 | scope:
16 | description: 'Optional scope for authenticating against scoped registries'
17 | token:
18 | description: Used to pull node distributions from node-versions. Since there's a default, this is typically not supplied by the user.
19 | default: ${{ github.token }}
20 | # TODO: add input to control forcing to pull from cloud or dist.
21 | # escape valve for someone having issues or needing the absolute latest which isn't cached yet
22 | # Deprecated option, do not use. Will not be supported after October 1, 2019
23 | version:
24 | description: 'Deprecated. Use node-version instead. Will not be supported after October 1, 2019'
25 | deprecationMessage: 'The version property will not be supported after October 1, 2019. Use node-version instead'
26 | runs:
27 | using: 'node12'
28 | main: 'dist/index.js'
29 |
--------------------------------------------------------------------------------
/src/main.ts:
--------------------------------------------------------------------------------
1 | import * as core from '@actions/core';
2 | import * as installer from './installer';
3 | import * as auth from './authutil';
4 | import * as path from 'path';
5 | import {URL} from 'url';
6 |
7 | export async function run() {
8 | try {
9 | //
10 | // Version is optional. If supplied, install / use from the tool cache
11 | // If not supplied then task is still used to setup proxy, auth, etc...
12 | //
13 | let version = core.getInput('node-version');
14 | if (!version) {
15 | version = core.getInput('version');
16 | }
17 |
18 | if (version) {
19 | let token = core.getInput('token');
20 | let auth = !token || isGhes() ? undefined : `token ${token}`;
21 | let stable = (core.getInput('stable') || 'true').toUpperCase() === 'TRUE';
22 | const checkLatest =
23 | (core.getInput('check-latest') || 'false').toUpperCase() === 'TRUE';
24 | await installer.getNode(version, stable, checkLatest, auth);
25 | }
26 |
27 | const registryUrl: string = core.getInput('registry-url');
28 | const alwaysAuth: string = core.getInput('always-auth');
29 | if (registryUrl) {
30 | auth.configAuthentication(registryUrl, alwaysAuth);
31 | }
32 |
33 | const matchersPath = path.join(__dirname, '..', '.github');
34 | console.log(`##[add-matcher]${path.join(matchersPath, 'tsc.json')}`);
35 | console.log(
36 | `##[add-matcher]${path.join(matchersPath, 'eslint-stylish.json')}`
37 | );
38 | console.log(
39 | `##[add-matcher]${path.join(matchersPath, 'eslint-compact.json')}`
40 | );
41 | } catch (error) {
42 | core.setFailed(error.message);
43 | }
44 | }
45 |
46 | function isGhes(): boolean {
47 | const ghUrl = new URL(
48 | process.env['GITHUB_SERVER_URL'] || 'https://github.com'
49 | );
50 | return ghUrl.hostname.toUpperCase() !== 'GITHUB.COM';
51 | }
52 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | lib/
3 | __tests__/runner/*
4 |
5 | validate/temp
6 | validate/node
7 |
8 | # Rest of the file pulled from https://github.com/github/gitignore/blob/master/Node.gitignore
9 | # Logs
10 | logs
11 | *.log
12 | npm-debug.log*
13 | yarn-debug.log*
14 | yarn-error.log*
15 | lerna-debug.log*
16 |
17 | # Diagnostic reports (https://nodejs.org/api/report.html)
18 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
19 |
20 | # Runtime data
21 | pids
22 | *.pid
23 | *.seed
24 | *.pid.lock
25 |
26 | # Directory for instrumented libs generated by jscoverage/JSCover
27 | lib-cov
28 |
29 | # Coverage directory used by tools like istanbul
30 | coverage
31 | *.lcov
32 |
33 | # nyc test coverage
34 | .nyc_output
35 |
36 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
37 | .grunt
38 |
39 | # Bower dependency directory (https://bower.io/)
40 | bower_components
41 |
42 | # node-waf configuration
43 | .lock-wscript
44 |
45 | # Compiled binary addons (https://nodejs.org/api/addons.html)
46 | build/Release
47 |
48 | # Dependency directories
49 | jspm_packages/
50 |
51 | # TypeScript v1 declaration files
52 | typings/
53 |
54 | # TypeScript cache
55 | *.tsbuildinfo
56 |
57 | # Optional npm cache directory
58 | .npm
59 |
60 | # Optional eslint cache
61 | .eslintcache
62 |
63 | # Optional REPL history
64 | .node_repl_history
65 |
66 | # Output of 'npm pack'
67 | *.tgz
68 |
69 | # Yarn Integrity file
70 | .yarn-integrity
71 |
72 | # dotenv environment variables file
73 | .env
74 | .env.test
75 |
76 | # parcel-bundler cache (https://parceljs.org/)
77 | .cache
78 |
79 | # next.js build output
80 | .next
81 |
82 | # nuxt.js build output
83 | .nuxt
84 |
85 | # vuepress build output
86 | .vuepress/dist
87 |
88 | # Serverless directories
89 | .serverless/
90 |
91 | # FuseBox cache
92 | .fusebox/
93 |
94 | # DynamoDB Local files
95 | .dynamodb/
96 |
--------------------------------------------------------------------------------
/src/authutil.ts:
--------------------------------------------------------------------------------
1 | import * as fs from 'fs';
2 | import * as os from 'os';
3 | import * as path from 'path';
4 | import * as core from '@actions/core';
5 | import * as github from '@actions/github';
6 |
7 | export function configAuthentication(registryUrl: string, alwaysAuth: string) {
8 | const npmrc: string = path.resolve(
9 | process.env['RUNNER_TEMP'] || process.cwd(),
10 | '.npmrc'
11 | );
12 | if (!registryUrl.endsWith('/')) {
13 | registryUrl += '/';
14 | }
15 |
16 | writeRegistryToFile(registryUrl, npmrc, alwaysAuth);
17 | }
18 |
19 | function writeRegistryToFile(
20 | registryUrl: string,
21 | fileLocation: string,
22 | alwaysAuth: string
23 | ) {
24 | let scope: string = core.getInput('scope');
25 | if (!scope && registryUrl.indexOf('npm.pkg.github.com') > -1) {
26 | scope = github.context.repo.owner;
27 | }
28 | if (scope && scope[0] != '@') {
29 | scope = '@' + scope;
30 | }
31 | if (scope) {
32 | scope = scope.toLowerCase();
33 | }
34 |
35 | core.debug(`Setting auth in ${fileLocation}`);
36 | let newContents: string = '';
37 | if (fs.existsSync(fileLocation)) {
38 | const curContents: string = fs.readFileSync(fileLocation, 'utf8');
39 | curContents.split(os.EOL).forEach((line: string) => {
40 | // Add current contents unless they are setting the registry
41 | if (!line.toLowerCase().startsWith('registry')) {
42 | newContents += line + os.EOL;
43 | }
44 | });
45 | }
46 | // Remove http: or https: from front of registry.
47 | const authString: string =
48 | registryUrl.replace(/(^\w+:|^)/, '') + ':_authToken=${NODE_AUTH_TOKEN}';
49 | const registryString: string = scope
50 | ? `${scope}:registry=${registryUrl}`
51 | : `registry=${registryUrl}`;
52 | const alwaysAuthString: string = `always-auth=${alwaysAuth}`;
53 | newContents += `${authString}${os.EOL}${registryString}${os.EOL}${alwaysAuthString}`;
54 | fs.writeFileSync(fileLocation, newContents);
55 | core.exportVariable('NPM_CONFIG_USERCONFIG', fileLocation);
56 | // Export empty node_auth_token so npm doesn't complain about not being able to find it
57 | core.exportVariable('NODE_AUTH_TOKEN', 'XXXXX-XXXXX-XXXXX-XXXXX');
58 | }
59 |
--------------------------------------------------------------------------------
/.github/workflows/versions.yml:
--------------------------------------------------------------------------------
1 | name: versions
2 |
3 | on:
4 | pull_request:
5 | paths-ignore:
6 | - '**.md'
7 | push:
8 | branches:
9 | - master
10 | - releases/*
11 | paths-ignore:
12 | - '**.md'
13 |
14 | jobs:
15 | local-cache:
16 | runs-on: ${{ matrix.operating-system }}
17 | strategy:
18 | fail-fast: false
19 | matrix:
20 | operating-system: [ubuntu-latest, windows-latest, macos-latest]
21 | node-version: [10, 12, 14]
22 | steps:
23 | - uses: actions/checkout@v2
24 | - name: Setup Node
25 | uses: ./
26 | with:
27 | node-version: ${{ matrix.node-version }}
28 | - name: Verify node and npm
29 | run: __tests__/verify-node.sh "${{ matrix.node-version }}"
30 | shell: bash
31 |
32 | manifest:
33 | runs-on: ${{ matrix.operating-system }}
34 | strategy:
35 | fail-fast: false
36 | matrix:
37 | operating-system: [ubuntu-latest, windows-latest, macos-latest]
38 | node-version: [10.15, 12.16.0, 14.2.0]
39 | steps:
40 | - uses: actions/checkout@v2
41 | - name: Setup Node
42 | uses: ./
43 | with:
44 | node-version: ${{ matrix.node-version }}
45 | - name: Verify node and npm
46 | run: __tests__/verify-node.sh "${{ matrix.node-version }}"
47 | shell: bash
48 |
49 | check-latest:
50 | runs-on: ${{ matrix.operating-system }}
51 | strategy:
52 | fail-fast: false
53 | matrix:
54 | operating-system: [ubuntu-latest, windows-latest, macos-latest]
55 | node-version: [10, 11, 12, 14]
56 | steps:
57 | - uses: actions/checkout@v2
58 | - name: Setup Node and check latest
59 | uses: ./
60 | with:
61 | node-version: ${{ matrix.node-version }}
62 | check-latest: true
63 | - name: Verify node and npm
64 | run: __tests__/verify-node.sh "${{ matrix.node-version }}"
65 | shell: bash
66 |
67 | node-dist:
68 | runs-on: ${{ matrix.operating-system }}
69 | strategy:
70 | fail-fast: false
71 | matrix:
72 | operating-system: [ubuntu-latest, windows-latest, macos-latest]
73 | node-version: [11, 13]
74 | steps:
75 | - uses: actions/checkout@v2
76 | - name: Setup Node from dist
77 | uses: ./
78 | with:
79 | node-version: ${{ matrix.node-version }}
80 | - name: Verify node and npm
81 | run: __tests__/verify-node.sh "${{ matrix.node-version }}"
82 | shell: bash
83 |
84 | old-versions:
85 | runs-on: ${{ matrix.operating-system }}
86 | strategy:
87 | fail-fast: false
88 | matrix:
89 | operating-system: [ubuntu-latest, windows-latest, macos-latest]
90 | steps:
91 | - uses: actions/checkout@v2
92 | # test old versions which didn't have npm and layout different
93 | - name: Setup node 0.12.18 from dist
94 | uses: ./
95 | with:
96 | node-version: 0.12.18
97 | - name: Verify node
98 | run: __tests__/verify-node.sh 0.12.18 SKIP_NPM
99 | shell: bash
--------------------------------------------------------------------------------
/CONDUCT:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | In the interest of fostering an open and welcoming environment, we as
6 | contributors and maintainers pledge to make participation in our project and
7 | our community a harassment-free experience for everyone, regardless of age, body
8 | size, disability, ethnicity, sex characteristics, gender identity and expression,
9 | level of experience, education, socio-economic status, nationality, personal
10 | appearance, race, religion, or sexual identity and orientation.
11 |
12 | ## Our Standards
13 |
14 | Examples of behavior that contributes to creating a positive environment
15 | include:
16 |
17 | * Using welcoming and inclusive language
18 | * Being respectful of differing viewpoints and experiences
19 | * Gracefully accepting constructive criticism
20 | * Focusing on what is best for the community
21 | * Showing empathy towards other community members
22 |
23 | Examples of unacceptable behavior by participants include:
24 |
25 | * The use of sexualized language or imagery and unwelcome sexual attention or
26 | advances
27 | * Trolling, insulting/derogatory comments, and personal or political attacks
28 | * Public or private harassment
29 | * Publishing others' private information, such as a physical or electronic
30 | address, without explicit permission
31 | * Other conduct which could reasonably be considered inappropriate in a
32 | professional setting
33 |
34 | ## Our Responsibilities
35 |
36 | Project maintainers are responsible for clarifying the standards of acceptable
37 | behavior and are expected to take appropriate and fair corrective action in
38 | response to any instances of unacceptable behavior.
39 |
40 | Project maintainers have the right and responsibility to remove, edit, or
41 | reject comments, commits, code, wiki edits, issues, and other contributions
42 | that are not aligned to this Code of Conduct, or to ban temporarily or
43 | permanently any contributor for other behaviors that they deem inappropriate,
44 | threatening, offensive, or harmful.
45 |
46 | ## Scope
47 |
48 | This Code of Conduct applies within all project spaces, and it also applies when
49 | an individual is representing the project or its community in public spaces.
50 | Examples of representing a project or community include using an official
51 | project e-mail address, posting via an official social media account, or acting
52 | as an appointed representative at an online or offline event. Representation of
53 | a project may be further defined and clarified by project maintainers.
54 |
55 | ## Enforcement
56 |
57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
58 | reported by contacting the project team at opensource@github.com. All
59 | complaints will be reviewed and investigated and will result in a response that
60 | is deemed necessary and appropriate to the circumstances. The project team is
61 | obligated to maintain confidentiality with regard to the reporter of an incident.
62 | Further details of specific enforcement policies may be posted separately.
63 |
64 | Project maintainers who do not follow or enforce the Code of Conduct in good
65 | faith may face temporary or permanent repercussions as determined by other
66 | members of the project's leadership.
67 |
68 | ## Attribution
69 |
70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
72 |
73 | [homepage]: https://www.contributor-covenant.org
74 |
75 | For answers to common questions about this code of conduct, see
76 | https://www.contributor-covenant.org/faq
--------------------------------------------------------------------------------
/__tests__/authutil.test.ts:
--------------------------------------------------------------------------------
1 | import os = require('os');
2 | import * as fs from 'fs';
3 | import * as path from 'path';
4 | import * as core from '@actions/core';
5 | import * as io from '@actions/io';
6 | import * as auth from '../src/authutil';
7 |
8 | let rcFile: string;
9 |
10 | describe('authutil tests', () => {
11 | const _runnerDir = path.join(__dirname, 'runner');
12 |
13 | let cnSpy: jest.SpyInstance;
14 | let logSpy: jest.SpyInstance;
15 | let dbgSpy: jest.SpyInstance;
16 |
17 | beforeAll(async () => {
18 | const randPath = path.join(
19 | Math.random()
20 | .toString(36)
21 | .substring(7)
22 | );
23 | const tempDir = path.join(_runnerDir, randPath, 'temp');
24 | await io.rmRF(tempDir);
25 | await io.mkdirP(tempDir);
26 | process.env['GITHUB_REPOSITORY'] = 'OwnerName/repo';
27 | process.env['RUNNER_TEMP'] = tempDir;
28 | rcFile = path.join(tempDir, '.npmrc');
29 | }, 100000);
30 |
31 | beforeEach(async () => {
32 | await io.rmRF(rcFile);
33 | // if (fs.existsSync(rcFile)) {
34 | // fs.unlinkSync(rcFile);
35 | // }
36 | process.env['INPUT_SCOPE'] = '';
37 |
38 | // writes
39 | cnSpy = jest.spyOn(process.stdout, 'write');
40 | logSpy = jest.spyOn(console, 'log');
41 | dbgSpy = jest.spyOn(core, 'debug');
42 | cnSpy.mockImplementation(line => {
43 | // uncomment to debug
44 | // process.stderr.write('write:' + line + '\n');
45 | });
46 | logSpy.mockImplementation(line => {
47 | // uncomment to debug
48 | // process.stderr.write('log:' + line + '\n');
49 | });
50 | dbgSpy.mockImplementation(msg => {
51 | // uncomment to see debug output
52 | // process.stderr.write(msg + '\n');
53 | });
54 | }, 100000);
55 |
56 | function dbg(message: string) {
57 | process.stderr.write('dbg::' + message + '::\n');
58 | }
59 |
60 | afterAll(async () => {
61 | if (_runnerDir) {
62 | await io.rmRF(_runnerDir);
63 | }
64 | }, 100000);
65 |
66 | function readRcFile(rcFile: string) {
67 | let rc = {};
68 | let contents = fs.readFileSync(rcFile, {encoding: 'utf8'});
69 | for (const line of contents.split(os.EOL)) {
70 | let parts = line.split('=');
71 | if (parts.length == 2) {
72 | rc[parts[0].trim()] = parts[1].trim();
73 | }
74 | }
75 | return rc;
76 | }
77 |
78 | it('Sets up npmrc for npmjs', async () => {
79 | await auth.configAuthentication('https://registry.npmjs.org/', 'false');
80 |
81 | expect(fs.statSync(rcFile)).toBeDefined();
82 | let contents = fs.readFileSync(rcFile, {encoding: 'utf8'});
83 | let rc = readRcFile(rcFile);
84 | expect(rc['registry']).toBe('https://registry.npmjs.org/');
85 | expect(rc['always-auth']).toBe('false');
86 | });
87 |
88 | it('Appends trailing slash to registry', async () => {
89 | await auth.configAuthentication('https://registry.npmjs.org', 'false');
90 |
91 | expect(fs.statSync(rcFile)).toBeDefined();
92 | let rc = readRcFile(rcFile);
93 | expect(rc['registry']).toBe('https://registry.npmjs.org/');
94 | expect(rc['always-auth']).toBe('false');
95 | });
96 |
97 | it('Configures scoped npm registries', async () => {
98 | process.env['INPUT_SCOPE'] = 'myScope';
99 | await auth.configAuthentication('https://registry.npmjs.org', 'false');
100 |
101 | expect(fs.statSync(rcFile)).toBeDefined();
102 | let rc = readRcFile(rcFile);
103 | expect(rc['@myscope:registry']).toBe('https://registry.npmjs.org/');
104 | expect(rc['always-auth']).toBe('false');
105 | });
106 |
107 | it('Automatically configures GPR scope', async () => {
108 | await auth.configAuthentication('npm.pkg.github.com', 'false');
109 |
110 | expect(fs.statSync(rcFile)).toBeDefined();
111 | let rc = readRcFile(rcFile);
112 | expect(rc['@ownername:registry']).toBe('npm.pkg.github.com/');
113 | expect(rc['always-auth']).toBe('false');
114 | });
115 |
116 | it('Sets up npmrc for always-auth true', async () => {
117 | await auth.configAuthentication('https://registry.npmjs.org/', 'true');
118 | expect(fs.statSync(rcFile)).toBeDefined();
119 | let rc = readRcFile(rcFile);
120 | expect(rc['registry']).toBe('https://registry.npmjs.org/');
121 | expect(rc['always-auth']).toBe('true');
122 | });
123 | });
124 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # setup-node
2 |
3 |
4 |
5 |
6 |
7 | This action sets by node environment for use in actions by:
8 |
9 | - optionally downloading and caching a version of node - npm by version spec and add to PATH
10 | - registering problem matchers for error output
11 | - configuring authentication for GPR or npm
12 |
13 | # v2-beta
14 |
15 | A beta release which adds reliability for pulling node distributions from a cache of node releases is available by referencing the `v2-beta` tag.
16 |
17 | ```yaml
18 | steps:
19 | - uses: actions/checkout@v2
20 | - uses: actions/setup-node@v2-beta
21 | with:
22 | node-version: '12'
23 | ```
24 |
25 | The action will first check the local cache for a semver match. The hosted images have been updated with the latest of each LTS from v8, v10, v12, and v14. `self-hosted` machines will benefit from the cache as well only downloading once. The action will pull LTS versions from [node-versions releases](https://github.com/actions/node-versions/releases) and on miss or failure will fall back to the previous behavior of downloading directly from [node dist](https://nodejs.org/dist/).
26 |
27 | The `node-version` input is optional. If not supplied, the node version that is PATH will be used. However, this action will still register problem matchers and support auth features. So setting up the node environment is still a valid scenario without downloading and caching versions.
28 |
29 | # Usage
30 |
31 | See [action.yml](action.yml)
32 |
33 | Basic:
34 | ```yaml
35 | steps:
36 | - uses: actions/checkout@v2
37 | - uses: actions/setup-node@v1
38 | with:
39 | node-version: '12'
40 | - run: npm install
41 | - run: npm test
42 | ```
43 |
44 | Check latest version:
45 |
46 | In the basic example above, the `check-latest` flag defaults to `false`. When set to `false`, the action tries to first resolve a version of node from the local cache. For information regarding locally cached versions of Node on GitHub hosted runners, check out [GitHub Actions Virtual Environments](https://github.com/actions/virtual-environments). The local version of Node in cache gets updated every couple of weeks. If unable to find a specific version in the cache, the action will then attempt to download a version of Node. Use the default or set `check-latest` to `false` if you prefer stability and if you want to ensure a specific version of Node is always used.
47 |
48 | If `check-latest` is set to `true`, the action first checks if the cached version is the latest one. If the locally cached version is not the most up-to-date, a version of Node will then be downloaded. Set `check-latest` to `true` it you want the most up-to-date version of Node to always be used.
49 |
50 | > Setting `check-latest` to `true` has performance implications as downloading versions of Node is slower than using cached versions
51 |
52 | ```yaml
53 | steps:
54 | - uses: actions/checkout@v2
55 | - uses: actions/setup-node@v2
56 | with:
57 | node-version: '12'
58 | check-latest: true
59 | - run: npm install
60 | - run: npm test
61 | ```
62 |
63 | Matrix Testing:
64 | ```yaml
65 | jobs:
66 | build:
67 | runs-on: ubuntu-16.04
68 | strategy:
69 | matrix:
70 | node: [ '10', '12' ]
71 | name: Node ${{ matrix.node }} sample
72 | steps:
73 | - uses: actions/checkout@v2
74 | - name: Setup node
75 | uses: actions/setup-node@v1
76 | with:
77 | node-version: ${{ matrix.node }}
78 | - run: npm install
79 | - run: npm test
80 | ```
81 |
82 | Publish to npmjs and GPR with npm:
83 | ```yaml
84 | steps:
85 | - uses: actions/checkout@v2
86 | - uses: actions/setup-node@v1
87 | with:
88 | node-version: '10.x'
89 | registry-url: 'https://registry.npmjs.org'
90 | - run: npm install
91 | - run: npm publish
92 | env:
93 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
94 | - uses: actions/setup-node@v1
95 | with:
96 | registry-url: 'https://npm.pkg.github.com'
97 | - run: npm publish
98 | env:
99 | NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
100 | ```
101 |
102 | Publish to npmjs and GPR with yarn:
103 | ```yaml
104 | steps:
105 | - uses: actions/checkout@v2
106 | - uses: actions/setup-node@v1
107 | with:
108 | node-version: '10.x'
109 | registry-url:
110 | - run: yarn install
111 | - run: yarn publish
112 | env:
113 | NODE_AUTH_TOKEN: ${{ secrets.YARN_TOKEN }}
114 | - uses: actions/setup-node@v1
115 | with:
116 | registry-url: 'https://npm.pkg.github.com'
117 | - run: yarn publish
118 | env:
119 | NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
120 | ```
121 |
122 | Use private packages:
123 | ```yaml
124 | steps:
125 | - uses: actions/checkout@v2
126 | - uses: actions/setup-node@v1
127 | with:
128 | node-version: '10.x'
129 | registry-url: 'https://registry.npmjs.org'
130 | # Skip post-install scripts here, as a malicious
131 | # script could steal NODE_AUTH_TOKEN.
132 | - run: npm install --ignore-scripts
133 | env:
134 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
135 | # `npm rebuild` will run all those post-install scripts for us.
136 | - run: npm rebuild && npm run prepare --if-present
137 | ```
138 |
139 |
140 | # License
141 |
142 | The scripts and documentation in this project are released under the [MIT License](LICENSE)
143 |
144 | # Contributions
145 |
146 | Contributions are welcome! See [Contributor's Guide](docs/contributors.md)
147 |
148 | ## Code of Conduct
149 |
150 | :wave: Be nice. See [our code of conduct](CONDUCT)
151 |
--------------------------------------------------------------------------------
/__tests__/data/versions-manifest.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "version": "14.0.0",
4 | "stable": true,
5 | "release_url": "https://github.com/actions/node-versions/releases/tag/14.0.0-20200423.30",
6 | "files": [
7 | {
8 | "filename": "node-14.0.0-darwin-x64.tar.gz",
9 | "arch": "x64",
10 | "platform": "darwin",
11 | "download_url": "https://github.com/actions/node-versions/releases/download/14.0.0-20200423.30/node-14.0.0-darwin-x64.tar.gz"
12 | },
13 | {
14 | "filename": "node-14.0.0-linux-x64.tar.gz",
15 | "arch": "x64",
16 | "platform": "linux",
17 | "download_url": "https://github.com/actions/node-versions/releases/download/14.0.0-20200423.30/node-14.0.0-linux-x64.tar.gz"
18 | },
19 | {
20 | "filename": "node-14.0.0-win32-x64.zip",
21 | "arch": "x64",
22 | "platform": "win32",
23 | "download_url": "https://github.com/actions/node-versions/releases/download/14.0.0-20200423.30/node-14.0.0-win32-x64.zip"
24 | }
25 | ]
26 | },
27 | {
28 | "version": "13.13.0",
29 | "stable": true,
30 | "release_url": "https://github.com/actions/node-versions/releases/tag/13.13.0-20200423.29",
31 | "files": [
32 | {
33 | "filename": "node-13.13.0-darwin-x64.tar.gz",
34 | "arch": "x64",
35 | "platform": "darwin",
36 | "download_url": "https://github.com/actions/node-versions/releases/download/13.13.0-20200423.29/node-13.13.0-darwin-x64.tar.gz"
37 | },
38 | {
39 | "filename": "node-13.13.0-linux-x64.tar.gz",
40 | "arch": "x64",
41 | "platform": "linux",
42 | "download_url": "https://github.com/actions/node-versions/releases/download/13.13.0-20200423.29/node-13.13.0-linux-x64.tar.gz"
43 | },
44 | {
45 | "filename": "node-13.13.0-win32-x64.zip",
46 | "arch": "x64",
47 | "platform": "win32",
48 | "download_url": "https://github.com/actions/node-versions/releases/download/13.13.0-20200423.29/node-13.13.0-win32-x64.zip"
49 | }
50 | ]
51 | },
52 | {
53 | "version": "12.16.2",
54 | "stable": true,
55 | "release_url": "https://github.com/actions/node-versions/releases/tag/12.16.2-20200423.28",
56 | "files": [
57 | {
58 | "filename": "node-12.16.2-darwin-x64.tar.gz",
59 | "arch": "x64",
60 | "platform": "darwin",
61 | "download_url": "https://github.com/actions/node-versions/releases/download/12.16.2-20200423.28/node-12.16.2-darwin-x64.tar.gz"
62 | },
63 | {
64 | "filename": "node-12.16.2-linux-x64.tar.gz",
65 | "arch": "x64",
66 | "platform": "linux",
67 | "download_url": "https://github.com/actions/node-versions/releases/download/12.16.2-20200423.28/node-12.16.2-linux-x64.tar.gz"
68 | },
69 | {
70 | "filename": "node-12.16.2-win32-x64.zip",
71 | "arch": "x64",
72 | "platform": "win32",
73 | "download_url": "https://github.com/actions/node-versions/releases/download/12.16.2-20200423.28/node-12.16.2-win32-x64.zip"
74 | }
75 | ]
76 | },
77 | {
78 | "version": "10.20.1",
79 | "stable": true,
80 | "release_url": "https://github.com/actions/node-versions/releases/tag/10.20.1-20200423.27",
81 | "files": [
82 | {
83 | "filename": "node-10.20.1-darwin-x64.tar.gz",
84 | "arch": "x64",
85 | "platform": "darwin",
86 | "download_url": "https://github.com/actions/node-versions/releases/download/10.20.1-20200423.27/node-10.20.1-darwin-x64.tar.gz"
87 | },
88 | {
89 | "filename": "node-10.20.1-linux-x64.tar.gz",
90 | "arch": "x64",
91 | "platform": "linux",
92 | "download_url": "https://github.com/actions/node-versions/releases/download/10.20.1-20200423.27/node-10.20.1-linux-x64.tar.gz"
93 | },
94 | {
95 | "filename": "node-10.20.1-win32-x64.zip",
96 | "arch": "x64",
97 | "platform": "win32",
98 | "download_url": "https://github.com/actions/node-versions/releases/download/10.20.1-20200423.27/node-10.20.1-win32-x64.zip"
99 | }
100 | ]
101 | },
102 | {
103 | "version": "8.17.0",
104 | "stable": true,
105 | "release_url": "https://github.com/actions/node-versions/releases/tag/8.17.0-20200423.26",
106 | "files": [
107 | {
108 | "filename": "node-8.17.0-darwin-x64.tar.gz",
109 | "arch": "x64",
110 | "platform": "darwin",
111 | "download_url": "https://github.com/actions/node-versions/releases/download/8.17.0-20200423.26/node-8.17.0-darwin-x64.tar.gz"
112 | },
113 | {
114 | "filename": "node-8.17.0-linux-x64.tar.gz",
115 | "arch": "x64",
116 | "platform": "linux",
117 | "download_url": "https://github.com/actions/node-versions/releases/download/8.17.0-20200423.26/node-8.17.0-linux-x64.tar.gz"
118 | },
119 | {
120 | "filename": "node-8.17.0-win32-x64.zip",
121 | "arch": "x64",
122 | "platform": "win32",
123 | "download_url": "https://github.com/actions/node-versions/releases/download/8.17.0-20200423.26/node-8.17.0-win32-x64.zip"
124 | }
125 | ]
126 | },
127 | {
128 | "version": "6.17.1",
129 | "stable": true,
130 | "release_url": "https://github.com/actions/node-versions/releases/tag/6.17.1-20200423.25",
131 | "files": [
132 | {
133 | "filename": "node-6.17.1-darwin-x64.tar.gz",
134 | "arch": "x64",
135 | "platform": "darwin",
136 | "download_url": "https://github.com/actions/node-versions/releases/download/6.17.1-20200423.25/node-6.17.1-darwin-x64.tar.gz"
137 | },
138 | {
139 | "filename": "node-6.17.1-linux-x64.tar.gz",
140 | "arch": "x64",
141 | "platform": "linux",
142 | "download_url": "https://github.com/actions/node-versions/releases/download/6.17.1-20200423.25/node-6.17.1-linux-x64.tar.gz"
143 | },
144 | {
145 | "filename": "node-6.17.1-win32-x64.zip",
146 | "arch": "x64",
147 | "platform": "win32",
148 | "download_url": "https://github.com/actions/node-versions/releases/download/6.17.1-20200423.25/node-6.17.1-win32-x64.zip"
149 | }
150 | ]
151 | }
152 | ]
--------------------------------------------------------------------------------
/src/installer.ts:
--------------------------------------------------------------------------------
1 | import os = require('os');
2 | import * as assert from 'assert';
3 | import * as core from '@actions/core';
4 | import * as hc from '@actions/http-client';
5 | import * as io from '@actions/io';
6 | import * as tc from '@actions/tool-cache';
7 | import * as path from 'path';
8 | import * as semver from 'semver';
9 | import fs = require('fs');
10 |
11 | //
12 | // Node versions interface
13 | // see https://nodejs.org/dist/index.json
14 | //
15 | export interface INodeVersion {
16 | version: string;
17 | files: string[];
18 | }
19 |
20 | interface INodeVersionInfo {
21 | downloadUrl: string;
22 | resolvedVersion: string;
23 | fileName: string;
24 | }
25 |
26 | export async function getNode(
27 | versionSpec: string,
28 | stable: boolean,
29 | checkLatest: boolean,
30 | auth: string | undefined
31 | ) {
32 | let osPlat: string = os.platform();
33 | let osArch: string = translateArchToDistUrl(os.arch());
34 |
35 | if (checkLatest) {
36 | core.info('Attempt to resolve the latest version from manifest...');
37 | const resolvedVersion = await resolveVersionFromManifest(
38 | versionSpec,
39 | stable,
40 | auth
41 | );
42 | if (resolvedVersion) {
43 | versionSpec = resolvedVersion;
44 | core.info(`Resolved as '${versionSpec}'`);
45 | } else {
46 | core.info(`Failed to resolve version ${versionSpec} from manifest`);
47 | }
48 | }
49 |
50 | // check cache
51 | let toolPath: string;
52 | toolPath = tc.find('node', versionSpec);
53 |
54 | // If not found in cache, download
55 | if (toolPath) {
56 | core.info(`Found in cache @ ${toolPath}`);
57 | } else {
58 | core.info(`Attempting to download ${versionSpec}...`);
59 | let downloadPath = '';
60 | let info: INodeVersionInfo | null = null;
61 |
62 | //
63 | // Try download from internal distribution (popular versions only)
64 | //
65 | try {
66 | info = await getInfoFromManifest(versionSpec, stable, auth);
67 | if (info) {
68 | core.info(`Acquiring ${info.resolvedVersion} from ${info.downloadUrl}`);
69 | downloadPath = await tc.downloadTool(info.downloadUrl, undefined, auth);
70 | } else {
71 | core.info(
72 | 'Not found in manifest. Falling back to download directly from Node'
73 | );
74 | }
75 | } catch (err) {
76 | // Rate limit?
77 | if (
78 | err instanceof tc.HTTPError &&
79 | (err.httpStatusCode === 403 || err.httpStatusCode === 429)
80 | ) {
81 | core.info(
82 | `Received HTTP status code ${err.httpStatusCode}. This usually indicates the rate limit has been exceeded`
83 | );
84 | } else {
85 | core.info(err.message);
86 | }
87 | core.debug(err.stack);
88 | core.info('Falling back to download directly from Node');
89 | }
90 |
91 | //
92 | // Download from nodejs.org
93 | //
94 | if (!downloadPath) {
95 | info = await getInfoFromDist(versionSpec);
96 | if (!info) {
97 | throw new Error(
98 | `Unable to find Node version '${versionSpec}' for platform ${osPlat} and architecture ${osArch}.`
99 | );
100 | }
101 |
102 | core.info(`Acquiring ${info.resolvedVersion} from ${info.downloadUrl}`);
103 | try {
104 | downloadPath = await tc.downloadTool(info.downloadUrl);
105 | } catch (err) {
106 | if (err instanceof tc.HTTPError && err.httpStatusCode == 404) {
107 | return await acquireNodeFromFallbackLocation(info.resolvedVersion);
108 | }
109 |
110 | throw err;
111 | }
112 | }
113 |
114 | //
115 | // Extract
116 | //
117 | core.info('Extracting ...');
118 | let extPath: string;
119 | info = info || ({} as INodeVersionInfo); // satisfy compiler, never null when reaches here
120 | if (osPlat == 'win32') {
121 | let _7zPath = path.join(__dirname, '..', 'externals', '7zr.exe');
122 | extPath = await tc.extract7z(downloadPath, undefined, _7zPath);
123 | // 7z extracts to folder matching file name
124 | let nestedPath = path.join(extPath, path.basename(info.fileName, '.7z'));
125 | if (fs.existsSync(nestedPath)) {
126 | extPath = nestedPath;
127 | }
128 | } else {
129 | extPath = await tc.extractTar(downloadPath, undefined, [
130 | 'xz',
131 | '--strip',
132 | '1'
133 | ]);
134 | }
135 |
136 | //
137 | // Install into the local tool cache - node extracts with a root folder that matches the fileName downloaded
138 | //
139 | core.info('Adding to the cache ...');
140 | toolPath = await tc.cacheDir(extPath, 'node', info.resolvedVersion);
141 | core.info('Done');
142 | }
143 |
144 | //
145 | // a tool installer initimately knows details about the layout of that tool
146 | // for example, node binary is in the bin folder after the extract on Mac/Linux.
147 | // layouts could change by version, by platform etc... but that's the tool installers job
148 | //
149 | if (osPlat != 'win32') {
150 | toolPath = path.join(toolPath, 'bin');
151 | }
152 |
153 | //
154 | // prepend the tools path. instructs the agent to prepend for future tasks
155 | core.addPath(toolPath);
156 | }
157 |
158 | async function getInfoFromManifest(
159 | versionSpec: string,
160 | stable: boolean,
161 | auth: string | undefined
162 | ): Promise {
163 | let info: INodeVersionInfo | null = null;
164 | const releases = await tc.getManifestFromRepo(
165 | 'actions',
166 | 'node-versions',
167 | auth,
168 | 'main'
169 | );
170 | const rel = await tc.findFromManifest(versionSpec, stable, releases);
171 |
172 | if (rel && rel.files.length > 0) {
173 | info = {};
174 | info.resolvedVersion = rel.version;
175 | info.downloadUrl = rel.files[0].download_url;
176 | info.fileName = rel.files[0].filename;
177 | }
178 |
179 | return info;
180 | }
181 |
182 | async function getInfoFromDist(
183 | versionSpec: string
184 | ): Promise {
185 | let osPlat: string = os.platform();
186 | let osArch: string = translateArchToDistUrl(os.arch());
187 |
188 | let version: string;
189 |
190 | version = await queryDistForMatch(versionSpec);
191 | if (!version) {
192 | return null;
193 | }
194 |
195 | //
196 | // Download - a tool installer intimately knows how to get the tool (and construct urls)
197 | //
198 | version = semver.clean(version) || '';
199 | let fileName: string =
200 | osPlat == 'win32'
201 | ? `node-v${version}-win-${osArch}`
202 | : `node-v${version}-${osPlat}-${osArch}`;
203 | let urlFileName: string =
204 | osPlat == 'win32' ? `${fileName}.7z` : `${fileName}.tar.gz`;
205 | let url = `https://nodejs.org/dist/v${version}/${urlFileName}`;
206 |
207 | return {
208 | downloadUrl: url,
209 | resolvedVersion: version,
210 | fileName: fileName
211 | };
212 | }
213 |
214 | async function resolveVersionFromManifest(
215 | versionSpec: string,
216 | stable: boolean,
217 | auth: string | undefined
218 | ): Promise {
219 | try {
220 | const info = await getInfoFromManifest(versionSpec, stable, auth);
221 | return info?.resolvedVersion;
222 | } catch (err) {
223 | core.info('Unable to resolve version from manifest...');
224 | core.debug(err.message);
225 | }
226 | }
227 |
228 | // TODO - should we just export this from @actions/tool-cache? Lifted directly from there
229 | function evaluateVersions(versions: string[], versionSpec: string): string {
230 | let version = '';
231 | core.debug(`evaluating ${versions.length} versions`);
232 | versions = versions.sort((a, b) => {
233 | if (semver.gt(a, b)) {
234 | return 1;
235 | }
236 | return -1;
237 | });
238 | for (let i = versions.length - 1; i >= 0; i--) {
239 | const potential: string = versions[i];
240 | const satisfied: boolean = semver.satisfies(potential, versionSpec);
241 | if (satisfied) {
242 | version = potential;
243 | break;
244 | }
245 | }
246 |
247 | if (version) {
248 | core.debug(`matched: ${version}`);
249 | } else {
250 | core.debug('match not found');
251 | }
252 |
253 | return version;
254 | }
255 |
256 | async function queryDistForMatch(versionSpec: string): Promise {
257 | let osPlat: string = os.platform();
258 | let osArch: string = translateArchToDistUrl(os.arch());
259 |
260 | // node offers a json list of versions
261 | let dataFileName: string;
262 | switch (osPlat) {
263 | case 'linux':
264 | dataFileName = `linux-${osArch}`;
265 | break;
266 | case 'darwin':
267 | dataFileName = `osx-${osArch}-tar`;
268 | break;
269 | case 'win32':
270 | dataFileName = `win-${osArch}-exe`;
271 | break;
272 | default:
273 | throw new Error(`Unexpected OS '${osPlat}'`);
274 | }
275 |
276 | let versions: string[] = [];
277 | let nodeVersions = await module.exports.getVersionsFromDist();
278 |
279 | nodeVersions.forEach((nodeVersion: INodeVersion) => {
280 | // ensure this version supports your os and platform
281 | if (nodeVersion.files.indexOf(dataFileName) >= 0) {
282 | versions.push(nodeVersion.version);
283 | }
284 | });
285 |
286 | // get the latest version that matches the version spec
287 | let version: string = evaluateVersions(versions, versionSpec);
288 | return version;
289 | }
290 |
291 | export async function getVersionsFromDist(): Promise {
292 | let dataUrl = 'https://nodejs.org/dist/index.json';
293 | let httpClient = new hc.HttpClient('setup-node', [], {
294 | allowRetries: true,
295 | maxRetries: 3
296 | });
297 | let response = await httpClient.getJson(dataUrl);
298 | return response.result || [];
299 | }
300 |
301 | // For non LTS versions of Node, the files we need (for Windows) are sometimes located
302 | // in a different folder than they normally are for other versions.
303 | // Normally the format is similar to: https://nodejs.org/dist/v5.10.1/node-v5.10.1-win-x64.7z
304 | // In this case, there will be two files located at:
305 | // /dist/v5.10.1/win-x64/node.exe
306 | // /dist/v5.10.1/win-x64/node.lib
307 | // If this is not the structure, there may also be two files located at:
308 | // /dist/v0.12.18/node.exe
309 | // /dist/v0.12.18/node.lib
310 | // This method attempts to download and cache the resources from these alternative locations.
311 | // Note also that the files are normally zipped but in this case they are just an exe
312 | // and lib file in a folder, not zipped.
313 | async function acquireNodeFromFallbackLocation(
314 | version: string
315 | ): Promise {
316 | let osPlat: string = os.platform();
317 | let osArch: string = translateArchToDistUrl(os.arch());
318 |
319 | // Create temporary folder to download in to
320 | const tempDownloadFolder: string =
321 | 'temp_' + Math.floor(Math.random() * 2000000000);
322 | const tempDirectory = process.env['RUNNER_TEMP'] || '';
323 | assert.ok(tempDirectory, 'Expected RUNNER_TEMP to be defined');
324 | const tempDir: string = path.join(tempDirectory, tempDownloadFolder);
325 | await io.mkdirP(tempDir);
326 | let exeUrl: string;
327 | let libUrl: string;
328 | try {
329 | exeUrl = `https://nodejs.org/dist/v${version}/win-${osArch}/node.exe`;
330 | libUrl = `https://nodejs.org/dist/v${version}/win-${osArch}/node.lib`;
331 |
332 | core.info(`Downloading only node binary from ${exeUrl}`);
333 |
334 | const exePath = await tc.downloadTool(exeUrl);
335 | await io.cp(exePath, path.join(tempDir, 'node.exe'));
336 | const libPath = await tc.downloadTool(libUrl);
337 | await io.cp(libPath, path.join(tempDir, 'node.lib'));
338 | } catch (err) {
339 | if (err instanceof tc.HTTPError && err.httpStatusCode == 404) {
340 | exeUrl = `https://nodejs.org/dist/v${version}/node.exe`;
341 | libUrl = `https://nodejs.org/dist/v${version}/node.lib`;
342 |
343 | const exePath = await tc.downloadTool(exeUrl);
344 | await io.cp(exePath, path.join(tempDir, 'node.exe'));
345 | const libPath = await tc.downloadTool(libUrl);
346 | await io.cp(libPath, path.join(tempDir, 'node.lib'));
347 | } else {
348 | throw err;
349 | }
350 | }
351 | let toolPath = await tc.cacheDir(tempDir, 'node', version);
352 | core.addPath(toolPath);
353 | return toolPath;
354 | }
355 |
356 | // os.arch does not always match the relative download url, e.g.
357 | // os.arch == 'arm' != node-v12.13.1-linux-armv7l.tar.gz
358 | // All other currently supported architectures match, e.g.:
359 | // os.arch = arm64 => https://nodejs.org/dist/v{VERSION}/node-v{VERSION}-{OS}-arm64.tar.gz
360 | // os.arch = x64 => https://nodejs.org/dist/v{VERSION}/node-v{VERSION}-{OS}-x64.tar.gz
361 | function translateArchToDistUrl(arch: string): string {
362 | switch (arch) {
363 | case 'arm':
364 | return 'armv7l';
365 | default:
366 | return arch;
367 | }
368 | }
369 |
--------------------------------------------------------------------------------
/__tests__/installer.test.ts:
--------------------------------------------------------------------------------
1 | import * as core from '@actions/core';
2 | import * as io from '@actions/io';
3 | import * as tc from '@actions/tool-cache';
4 | import fs from 'fs';
5 | import cp from 'child_process';
6 | import osm = require('os');
7 | import path from 'path';
8 | import * as main from '../src/main';
9 | import * as im from '../src/installer';
10 | import * as auth from '../src/authutil';
11 | import {context} from '@actions/github';
12 |
13 | let nodeTestManifest = require('./data/versions-manifest.json');
14 | let nodeTestDist = require('./data/node-dist-index.json');
15 |
16 | // let matchers = require('../matchers.json');
17 | // let matcherPattern = matchers.problemMatcher[0].pattern[0];
18 | // let matcherRegExp = new RegExp(matcherPattern.regexp);
19 |
20 | describe('setup-node', () => {
21 | let inputs = {} as any;
22 | let os = {} as any;
23 |
24 | let inSpy: jest.SpyInstance;
25 | let findSpy: jest.SpyInstance;
26 | let cnSpy: jest.SpyInstance;
27 | let logSpy: jest.SpyInstance;
28 | let warningSpy: jest.SpyInstance;
29 | let getManifestSpy: jest.SpyInstance;
30 | let getDistSpy: jest.SpyInstance;
31 | let platSpy: jest.SpyInstance;
32 | let archSpy: jest.SpyInstance;
33 | let dlSpy: jest.SpyInstance;
34 | let exSpy: jest.SpyInstance;
35 | let cacheSpy: jest.SpyInstance;
36 | let dbgSpy: jest.SpyInstance;
37 | let whichSpy: jest.SpyInstance;
38 | let existsSpy: jest.SpyInstance;
39 | let mkdirpSpy: jest.SpyInstance;
40 | let execSpy: jest.SpyInstance;
41 | let authSpy: jest.SpyInstance;
42 |
43 | beforeEach(() => {
44 | // @actions/core
45 | inputs = {};
46 | inSpy = jest.spyOn(core, 'getInput');
47 | inSpy.mockImplementation(name => inputs[name]);
48 |
49 | // node
50 | os = {};
51 | platSpy = jest.spyOn(osm, 'platform');
52 | platSpy.mockImplementation(() => os['platform']);
53 | archSpy = jest.spyOn(osm, 'arch');
54 | archSpy.mockImplementation(() => os['arch']);
55 | execSpy = jest.spyOn(cp, 'execSync');
56 |
57 | // @actions/tool-cache
58 | findSpy = jest.spyOn(tc, 'find');
59 | dlSpy = jest.spyOn(tc, 'downloadTool');
60 | exSpy = jest.spyOn(tc, 'extractTar');
61 | cacheSpy = jest.spyOn(tc, 'cacheDir');
62 | getManifestSpy = jest.spyOn(tc, 'getManifestFromRepo');
63 | getDistSpy = jest.spyOn(im, 'getVersionsFromDist');
64 |
65 | // io
66 | whichSpy = jest.spyOn(io, 'which');
67 | existsSpy = jest.spyOn(fs, 'existsSync');
68 | mkdirpSpy = jest.spyOn(io, 'mkdirP');
69 |
70 | // disable authentication portion for installer tests
71 | authSpy = jest.spyOn(auth, 'configAuthentication');
72 | authSpy.mockImplementation(() => {});
73 |
74 | // gets
75 | getManifestSpy.mockImplementation(
76 | () => nodeTestManifest
77 | );
78 | getDistSpy.mockImplementation(() => nodeTestDist);
79 |
80 | // writes
81 | cnSpy = jest.spyOn(process.stdout, 'write');
82 | logSpy = jest.spyOn(core, 'info');
83 | dbgSpy = jest.spyOn(core, 'debug');
84 | warningSpy = jest.spyOn(core, 'warning');
85 | cnSpy.mockImplementation(line => {
86 | // uncomment to debug
87 | // process.stderr.write('write:' + line + '\n');
88 | });
89 | logSpy.mockImplementation(line => {
90 | // uncomment to debug
91 | // process.stderr.write('log:' + line + '\n');
92 | });
93 | dbgSpy.mockImplementation(msg => {
94 | // uncomment to see debug output
95 | // process.stderr.write(msg + '\n');
96 | });
97 | });
98 |
99 | afterEach(() => {
100 | jest.resetAllMocks();
101 | jest.clearAllMocks();
102 | //jest.restoreAllMocks();
103 | });
104 |
105 | afterAll(async () => {}, 100000);
106 |
107 | //--------------------------------------------------
108 | // Manifest find tests
109 | //--------------------------------------------------
110 | it('can mock manifest versions', async () => {
111 | let versions: tc.IToolRelease[] | null = await tc.getManifestFromRepo(
112 | 'actions',
113 | 'node-versions',
114 | 'mocktoken'
115 | );
116 | expect(versions).toBeDefined();
117 | expect(versions?.length).toBe(6);
118 | });
119 |
120 | it('can mock dist versions', async () => {
121 | let versions: im.INodeVersion[] = await im.getVersionsFromDist();
122 | expect(versions).toBeDefined();
123 | expect(versions?.length).toBe(23);
124 | });
125 |
126 | it('can find 12.16.2 from manifest on osx', async () => {
127 | os.platform = 'darwin';
128 | os.arch = 'x64';
129 | let versions: tc.IToolRelease[] | null = await tc.getManifestFromRepo(
130 | 'actions',
131 | 'node-versions',
132 | 'mocktoken'
133 | );
134 | expect(versions).toBeDefined();
135 | let match = await tc.findFromManifest('12.16.2', true, versions);
136 | expect(match).toBeDefined();
137 | expect(match?.version).toBe('12.16.2');
138 | });
139 |
140 | it('can find 12 from manifest on linux', async () => {
141 | os.platform = 'linux';
142 | os.arch = 'x64';
143 | let versions: tc.IToolRelease[] | null = await tc.getManifestFromRepo(
144 | 'actions',
145 | 'node-versions',
146 | 'mocktoken'
147 | );
148 | expect(versions).toBeDefined();
149 | let match = await tc.findFromManifest('12.16.2', true, versions);
150 | expect(match).toBeDefined();
151 | expect(match?.version).toBe('12.16.2');
152 | });
153 |
154 | it('can find 10 from manifest on windows', async () => {
155 | os.platform = 'win32';
156 | os.arch = 'x64';
157 | let versions: tc.IToolRelease[] | null = await tc.getManifestFromRepo(
158 | 'actions',
159 | 'node-versions',
160 | 'mocktoken'
161 | );
162 | expect(versions).toBeDefined();
163 | let match = await tc.findFromManifest('10', true, versions);
164 | expect(match).toBeDefined();
165 | expect(match?.version).toBe('10.20.1');
166 | });
167 |
168 | //--------------------------------------------------
169 | // Found in cache tests
170 | //--------------------------------------------------
171 |
172 | it('finds version in cache with stable true', async () => {
173 | inputs['node-version'] = '12';
174 | inputs.stable = 'true';
175 |
176 | let toolPath = path.normalize('/cache/node/12.16.1/x64');
177 | findSpy.mockImplementation(() => toolPath);
178 | await main.run();
179 |
180 | expect(logSpy).toHaveBeenCalledWith(`Found in cache @ ${toolPath}`);
181 | });
182 |
183 | it('finds version in cache with stable not supplied', async () => {
184 | inputs['node-version'] = '12';
185 |
186 | inSpy.mockImplementation(name => inputs[name]);
187 |
188 | let toolPath = path.normalize('/cache/node/12.16.1/x64');
189 | findSpy.mockImplementation(() => toolPath);
190 | await main.run();
191 |
192 | expect(logSpy).toHaveBeenCalledWith(`Found in cache @ ${toolPath}`);
193 | });
194 |
195 | it('finds version in cache and adds it to the path', async () => {
196 | inputs['node-version'] = '12';
197 |
198 | inSpy.mockImplementation(name => inputs[name]);
199 |
200 | let toolPath = path.normalize('/cache/node/12.16.1/x64');
201 | findSpy.mockImplementation(() => toolPath);
202 | await main.run();
203 |
204 | let expPath = path.join(toolPath, 'bin');
205 | expect(cnSpy).toHaveBeenCalledWith(`::add-path::${expPath}${osm.EOL}`);
206 | });
207 |
208 | it('handles unhandled find error and reports error', async () => {
209 | let errMsg = 'unhandled error message';
210 | inputs['node-version'] = '12';
211 |
212 | findSpy.mockImplementation(() => {
213 | throw new Error(errMsg);
214 | });
215 |
216 | await main.run();
217 |
218 | expect(cnSpy).toHaveBeenCalledWith('::error::' + errMsg + osm.EOL);
219 | });
220 |
221 | it('downloads a version from a manifest match', async () => {
222 | os.platform = 'linux';
223 | os.arch = 'x64';
224 |
225 | // a version which is in the manifest
226 | let versionSpec = '12.16.2';
227 | let resolvedVersion = versionSpec;
228 |
229 | inputs['node-version'] = versionSpec;
230 | inputs['always-auth'] = false;
231 | inputs['token'] = 'faketoken';
232 |
233 | let expectedUrl =
234 | 'https://github.com/actions/node-versions/releases/download/12.16.2-20200423.28/node-12.16.2-linux-x64.tar.gz';
235 |
236 | // ... but not in the local cache
237 | findSpy.mockImplementation(() => '');
238 |
239 | dlSpy.mockImplementation(async () => '/some/temp/path');
240 | let toolPath = path.normalize('/cache/node/12.16.2/x64');
241 | exSpy.mockImplementation(async () => '/some/other/temp/path');
242 | cacheSpy.mockImplementation(async () => toolPath);
243 |
244 | await main.run();
245 |
246 | let expPath = path.join(toolPath, 'bin');
247 |
248 | expect(dlSpy).toHaveBeenCalled();
249 | expect(exSpy).toHaveBeenCalled();
250 | expect(logSpy).toHaveBeenCalledWith(
251 | `Acquiring ${resolvedVersion} from ${expectedUrl}`
252 | );
253 | expect(logSpy).toHaveBeenCalledWith(
254 | `Attempting to download ${versionSpec}...`
255 | );
256 | expect(cnSpy).toHaveBeenCalledWith(`::add-path::${expPath}${osm.EOL}`);
257 | });
258 |
259 | it('falls back to a version from node dist', async () => {
260 | os.platform = 'linux';
261 | os.arch = 'x64';
262 |
263 | // a version which is not in the manifest but is in node dist
264 | let versionSpec = '11.15.0';
265 | let resolvedVersion = versionSpec;
266 |
267 | inputs['node-version'] = versionSpec;
268 | inputs['always-auth'] = false;
269 | inputs['token'] = 'faketoken';
270 |
271 | let expectedUrl =
272 | 'https://github.com/actions/node-versions/releases/download/12.16.2-20200423.28/node-12.16.2-linux-x64.tar.gz';
273 |
274 | // ... but not in the local cache
275 | findSpy.mockImplementation(() => '');
276 |
277 | dlSpy.mockImplementation(async () => '/some/temp/path');
278 | let toolPath = path.normalize('/cache/node/11.11.0/x64');
279 | exSpy.mockImplementation(async () => '/some/other/temp/path');
280 | cacheSpy.mockImplementation(async () => toolPath);
281 |
282 | await main.run();
283 |
284 | let expPath = path.join(toolPath, 'bin');
285 |
286 | expect(dlSpy).toHaveBeenCalled();
287 | expect(exSpy).toHaveBeenCalled();
288 | expect(logSpy).toHaveBeenCalledWith(
289 | 'Not found in manifest. Falling back to download directly from Node'
290 | );
291 | expect(logSpy).toHaveBeenCalledWith(
292 | `Attempting to download ${versionSpec}...`
293 | );
294 | expect(cnSpy).toHaveBeenCalledWith(`::add-path::${expPath}${osm.EOL}`);
295 | });
296 |
297 | it('does not find a version that does not exist', async () => {
298 | os.platform = 'linux';
299 | os.arch = 'x64';
300 |
301 | let versionSpec = '9.99.9';
302 | inputs['node-version'] = versionSpec;
303 |
304 | findSpy.mockImplementation(() => '');
305 | await main.run();
306 |
307 | expect(logSpy).toHaveBeenCalledWith(
308 | 'Not found in manifest. Falling back to download directly from Node'
309 | );
310 | expect(logSpy).toHaveBeenCalledWith(
311 | `Attempting to download ${versionSpec}...`
312 | );
313 | expect(cnSpy).toHaveBeenCalledWith(
314 | `::error::Unable to find Node version '${versionSpec}' for platform ${os.platform} and architecture ${os.arch}.${osm.EOL}`
315 | );
316 | });
317 |
318 | it('reports a failed download', async () => {
319 | let errMsg = 'unhandled download message';
320 | os.platform = 'linux';
321 | os.arch = 'x64';
322 |
323 | // a version which is in the manifest
324 | let versionSpec = '12.16.2';
325 | let resolvedVersion = versionSpec;
326 |
327 | inputs['node-version'] = versionSpec;
328 | inputs['always-auth'] = false;
329 | inputs['token'] = 'faketoken';
330 |
331 | findSpy.mockImplementation(() => '');
332 | dlSpy.mockImplementation(() => {
333 | throw new Error(errMsg);
334 | });
335 | await main.run();
336 |
337 | expect(cnSpy).toHaveBeenCalledWith(`::error::${errMsg}${osm.EOL}`);
338 | });
339 |
340 | describe('check-latest flag', () => {
341 | it('use local version and dont check manifest if check-latest is not specified', async () => {
342 | os.platform = 'linux';
343 | os.arch = 'x64';
344 |
345 | inputs['node-version'] = '12';
346 | inputs['check-latest'] = 'false';
347 |
348 | const toolPath = path.normalize('/cache/node/12.16.1/x64');
349 | findSpy.mockReturnValue(toolPath);
350 | await main.run();
351 |
352 | expect(logSpy).toHaveBeenCalledWith(`Found in cache @ ${toolPath}`);
353 | expect(logSpy).not.toHaveBeenCalledWith(
354 | 'Attempt to resolve the latest version from manifest...'
355 | );
356 | });
357 |
358 | it('check latest version and resolve it from local cache', async () => {
359 | os.platform = 'linux';
360 | os.arch = 'x64';
361 |
362 | inputs['node-version'] = '12';
363 | inputs['check-latest'] = 'true';
364 |
365 | const toolPath = path.normalize('/cache/node/12.16.2/x64');
366 | findSpy.mockReturnValue(toolPath);
367 | dlSpy.mockImplementation(async () => '/some/temp/path');
368 | exSpy.mockImplementation(async () => '/some/other/temp/path');
369 | cacheSpy.mockImplementation(async () => toolPath);
370 |
371 | await main.run();
372 |
373 | expect(logSpy).toHaveBeenCalledWith(
374 | 'Attempt to resolve the latest version from manifest...'
375 | );
376 | expect(logSpy).toHaveBeenCalledWith("Resolved as '12.16.2'");
377 | expect(logSpy).toHaveBeenCalledWith(`Found in cache @ ${toolPath}`);
378 | });
379 |
380 | it('check latest version and install it from manifest', async () => {
381 | os.platform = 'linux';
382 | os.arch = 'x64';
383 |
384 | inputs['node-version'] = '12';
385 | inputs['check-latest'] = 'true';
386 |
387 | findSpy.mockImplementation(() => '');
388 | dlSpy.mockImplementation(async () => '/some/temp/path');
389 | const toolPath = path.normalize('/cache/node/12.16.2/x64');
390 | exSpy.mockImplementation(async () => '/some/other/temp/path');
391 | cacheSpy.mockImplementation(async () => toolPath);
392 | const expectedUrl =
393 | 'https://github.com/actions/node-versions/releases/download/12.16.2-20200423.28/node-12.16.2-linux-x64.tar.gz';
394 |
395 | await main.run();
396 |
397 | expect(logSpy).toHaveBeenCalledWith(
398 | 'Attempt to resolve the latest version from manifest...'
399 | );
400 | expect(logSpy).toHaveBeenCalledWith("Resolved as '12.16.2'");
401 | expect(logSpy).toHaveBeenCalledWith(
402 | `Acquiring 12.16.2 from ${expectedUrl}`
403 | );
404 | expect(logSpy).toHaveBeenCalledWith('Extracting ...');
405 | });
406 |
407 | it('fallback to dist if version if not found in manifest', async () => {
408 | os.platform = 'linux';
409 | os.arch = 'x64';
410 |
411 | // a version which is not in the manifest but is in node dist
412 | let versionSpec = '11';
413 |
414 | inputs['node-version'] = versionSpec;
415 | inputs['check-latest'] = 'true';
416 | inputs['always-auth'] = false;
417 | inputs['token'] = 'faketoken';
418 |
419 | // ... but not in the local cache
420 | findSpy.mockImplementation(() => '');
421 |
422 | dlSpy.mockImplementation(async () => '/some/temp/path');
423 | let toolPath = path.normalize('/cache/node/11.11.0/x64');
424 | exSpy.mockImplementation(async () => '/some/other/temp/path');
425 | cacheSpy.mockImplementation(async () => toolPath);
426 |
427 | await main.run();
428 |
429 | let expPath = path.join(toolPath, 'bin');
430 |
431 | expect(dlSpy).toHaveBeenCalled();
432 | expect(exSpy).toHaveBeenCalled();
433 | expect(logSpy).toHaveBeenCalledWith(
434 | 'Attempt to resolve the latest version from manifest...'
435 | );
436 | expect(logSpy).toHaveBeenCalledWith(
437 | `Failed to resolve version ${versionSpec} from manifest`
438 | );
439 | expect(logSpy).toHaveBeenCalledWith(
440 | `Attempting to download ${versionSpec}...`
441 | );
442 | expect(cnSpy).toHaveBeenCalledWith(`::add-path::${expPath}${osm.EOL}`);
443 | });
444 |
445 | it('fallback to dist if manifest is not available', async () => {
446 | os.platform = 'linux';
447 | os.arch = 'x64';
448 |
449 | // a version which is not in the manifest but is in node dist
450 | let versionSpec = '12';
451 |
452 | inputs['node-version'] = versionSpec;
453 | inputs['check-latest'] = 'true';
454 | inputs['always-auth'] = false;
455 | inputs['token'] = 'faketoken';
456 |
457 | // ... but not in the local cache
458 | findSpy.mockImplementation(() => '');
459 | getManifestSpy.mockImplementation(() => {
460 | throw new Error('Unable to download manifest');
461 | });
462 |
463 | dlSpy.mockImplementation(async () => '/some/temp/path');
464 | let toolPath = path.normalize('/cache/node/12.11.0/x64');
465 | exSpy.mockImplementation(async () => '/some/other/temp/path');
466 | cacheSpy.mockImplementation(async () => toolPath);
467 |
468 | await main.run();
469 |
470 | let expPath = path.join(toolPath, 'bin');
471 |
472 | expect(dlSpy).toHaveBeenCalled();
473 | expect(exSpy).toHaveBeenCalled();
474 | expect(logSpy).toHaveBeenCalledWith(
475 | 'Attempt to resolve the latest version from manifest...'
476 | );
477 | expect(logSpy).toHaveBeenCalledWith(
478 | 'Unable to resolve version from manifest...'
479 | );
480 | expect(logSpy).toHaveBeenCalledWith(
481 | `Failed to resolve version ${versionSpec} from manifest`
482 | );
483 | expect(logSpy).toHaveBeenCalledWith(
484 | `Attempting to download ${versionSpec}...`
485 | );
486 | expect(cnSpy).toHaveBeenCalledWith(`::add-path::${expPath}${osm.EOL}`);
487 | });
488 | });
489 | });
490 |
--------------------------------------------------------------------------------
/__tests__/data/node-dist-index.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "version": "v14.1.0",
4 | "date": "2020-04-29",
5 | "files": [
6 | "aix-ppc64",
7 | "headers",
8 | "linux-arm64",
9 | "linux-armv7l",
10 | "linux-ppc64le",
11 | "linux-s390x",
12 | "linux-x64",
13 | "osx-x64-pkg",
14 | "osx-x64-tar",
15 | "src",
16 | "win-x64-7z",
17 | "win-x64-exe",
18 | "win-x64-msi",
19 | "win-x64-zip",
20 | "win-x86-7z",
21 | "win-x86-exe",
22 | "win-x86-msi",
23 | "win-x86-zip"
24 | ],
25 | "npm": "6.14.4",
26 | "v8": "8.1.307.31",
27 | "uv": "1.37.0",
28 | "zlib": "1.2.11",
29 | "openssl": "1.1.1g",
30 | "modules": "83",
31 | "lts": false,
32 | "security": false
33 | },
34 | {
35 | "version": "v14.0.0",
36 | "date": "2020-04-21",
37 | "files": [
38 | "aix-ppc64",
39 | "headers",
40 | "linux-arm64",
41 | "linux-armv7l",
42 | "linux-ppc64le",
43 | "linux-s390x",
44 | "linux-x64",
45 | "osx-x64-pkg",
46 | "osx-x64-tar",
47 | "src",
48 | "win-x64-7z",
49 | "win-x64-exe",
50 | "win-x64-msi",
51 | "win-x64-zip",
52 | "win-x86-7z",
53 | "win-x86-exe",
54 | "win-x86-msi",
55 | "win-x86-zip"
56 | ],
57 | "npm": "6.14.4",
58 | "v8": "8.1.307.30",
59 | "uv": "1.37.0",
60 | "zlib": "1.2.11",
61 | "openssl": "1.1.1f",
62 | "modules": "83",
63 | "lts": false,
64 | "security": false
65 | },
66 | {
67 | "version": "v13.14.0",
68 | "date": "2020-04-28",
69 | "files": [
70 | "aix-ppc64",
71 | "headers",
72 | "linux-arm64",
73 | "linux-armv7l",
74 | "linux-ppc64le",
75 | "linux-s390x",
76 | "linux-x64",
77 | "osx-x64-pkg",
78 | "osx-x64-tar",
79 | "src",
80 | "sunos-x64",
81 | "win-x64-7z",
82 | "win-x64-exe",
83 | "win-x64-msi",
84 | "win-x64-zip",
85 | "win-x86-7z",
86 | "win-x86-exe",
87 | "win-x86-msi",
88 | "win-x86-zip"
89 | ],
90 | "npm": "6.14.4",
91 | "v8": "7.9.317.25",
92 | "uv": "1.37.0",
93 | "zlib": "1.2.11",
94 | "openssl": "1.1.1g",
95 | "modules": "79",
96 | "lts": false,
97 | "security": false
98 | },
99 | {
100 | "version": "v13.13.0",
101 | "date": "2020-04-14",
102 | "files": [
103 | "aix-ppc64",
104 | "headers",
105 | "linux-arm64",
106 | "linux-armv7l",
107 | "linux-ppc64le",
108 | "linux-s390x",
109 | "linux-x64",
110 | "osx-x64-pkg",
111 | "osx-x64-tar",
112 | "src",
113 | "sunos-x64",
114 | "win-x64-7z",
115 | "win-x64-exe",
116 | "win-x64-msi",
117 | "win-x64-zip",
118 | "win-x86-7z",
119 | "win-x86-exe",
120 | "win-x86-msi",
121 | "win-x86-zip"
122 | ],
123 | "npm": "6.14.4",
124 | "v8": "7.9.317.25",
125 | "uv": "1.35.0",
126 | "zlib": "1.2.11",
127 | "openssl": "1.1.1f",
128 | "modules": "79",
129 | "lts": false,
130 | "security": false
131 | },
132 | {
133 | "version": "v12.16.3",
134 | "date": "2020-04-28",
135 | "files": [
136 | "aix-ppc64",
137 | "headers",
138 | "linux-arm64",
139 | "linux-armv7l",
140 | "linux-ppc64le",
141 | "linux-s390x",
142 | "linux-x64",
143 | "osx-x64-pkg",
144 | "osx-x64-tar",
145 | "src",
146 | "sunos-x64",
147 | "win-x64-7z",
148 | "win-x64-exe",
149 | "win-x64-msi",
150 | "win-x64-zip",
151 | "win-x86-7z",
152 | "win-x86-exe",
153 | "win-x86-msi",
154 | "win-x86-zip"
155 | ],
156 | "npm": "6.14.4",
157 | "v8": "7.8.279.23",
158 | "uv": "1.34.2",
159 | "zlib": "1.2.11",
160 | "openssl": "1.1.1g",
161 | "modules": "72",
162 | "lts": "Erbium",
163 | "security": false
164 | },
165 | {
166 | "version": "v12.16.2",
167 | "date": "2020-04-08",
168 | "files": [
169 | "aix-ppc64",
170 | "headers",
171 | "linux-arm64",
172 | "linux-armv7l",
173 | "linux-ppc64le",
174 | "linux-s390x",
175 | "linux-x64",
176 | "osx-x64-pkg",
177 | "osx-x64-tar",
178 | "src",
179 | "sunos-x64",
180 | "win-x64-7z",
181 | "win-x64-exe",
182 | "win-x64-msi",
183 | "win-x64-zip",
184 | "win-x86-7z",
185 | "win-x86-exe",
186 | "win-x86-msi",
187 | "win-x86-zip"
188 | ],
189 | "npm": "6.14.4",
190 | "v8": "7.8.279.23",
191 | "uv": "1.34.2",
192 | "zlib": "1.2.11",
193 | "openssl": "1.1.1e",
194 | "modules": "72",
195 | "lts": "Erbium",
196 | "security": false
197 | },
198 | {
199 | "version": "v12.1.0",
200 | "date": "2019-04-29",
201 | "files": [
202 | "aix-ppc64",
203 | "headers",
204 | "linux-arm64",
205 | "linux-armv7l",
206 | "linux-ppc64le",
207 | "linux-s390x",
208 | "linux-x64",
209 | "osx-x64-pkg",
210 | "osx-x64-tar",
211 | "src",
212 | "sunos-x64",
213 | "win-x64-7z",
214 | "win-x64-exe",
215 | "win-x64-msi",
216 | "win-x64-zip",
217 | "win-x86-7z",
218 | "win-x86-exe",
219 | "win-x86-msi",
220 | "win-x86-zip"
221 | ],
222 | "npm": "6.9.0",
223 | "v8": "7.4.288.21",
224 | "uv": "1.28.0",
225 | "zlib": "1.2.11",
226 | "openssl": "1.1.1b",
227 | "modules": "72",
228 | "lts": false,
229 | "security": false
230 | },
231 | {
232 | "version": "v11.15.0",
233 | "date": "2019-04-30",
234 | "files": [
235 | "aix-ppc64",
236 | "headers",
237 | "linux-arm64",
238 | "linux-armv6l",
239 | "linux-armv7l",
240 | "linux-ppc64le",
241 | "linux-s390x",
242 | "linux-x64",
243 | "osx-x64-pkg",
244 | "osx-x64-tar",
245 | "src",
246 | "sunos-x64",
247 | "win-x64-7z",
248 | "win-x64-exe",
249 | "win-x64-msi",
250 | "win-x64-zip",
251 | "win-x86-7z",
252 | "win-x86-exe",
253 | "win-x86-msi",
254 | "win-x86-zip"
255 | ],
256 | "npm": "6.7.0",
257 | "v8": "7.0.276.38",
258 | "uv": "1.27.0",
259 | "zlib": "1.2.11",
260 | "openssl": "1.1.1b",
261 | "modules": "67",
262 | "lts": false,
263 | "security": false
264 | },
265 | {
266 | "version": "v10.20.1",
267 | "date": "2020-04-10",
268 | "files": [
269 | "aix-ppc64",
270 | "headers",
271 | "linux-arm64",
272 | "linux-armv6l",
273 | "linux-armv7l",
274 | "linux-ppc64le",
275 | "linux-s390x",
276 | "linux-x64",
277 | "osx-x64-pkg",
278 | "osx-x64-tar",
279 | "src",
280 | "sunos-x64",
281 | "win-x64-7z",
282 | "win-x64-exe",
283 | "win-x64-msi",
284 | "win-x64-zip",
285 | "win-x86-7z",
286 | "win-x86-exe",
287 | "win-x86-msi",
288 | "win-x86-zip"
289 | ],
290 | "npm": "6.14.4",
291 | "v8": "6.8.275.32",
292 | "uv": "1.34.2",
293 | "zlib": "1.2.11",
294 | "openssl": "1.1.1e",
295 | "modules": "64",
296 | "lts": "Dubnium",
297 | "security": false
298 | },
299 | {
300 | "version": "v10.20.0",
301 | "date": "2020-03-24",
302 | "files": [
303 | "aix-ppc64",
304 | "headers",
305 | "linux-arm64",
306 | "linux-armv6l",
307 | "linux-armv7l",
308 | "linux-ppc64le",
309 | "linux-s390x",
310 | "linux-x64",
311 | "osx-x64-pkg",
312 | "osx-x64-tar",
313 | "src",
314 | "sunos-x64",
315 | "win-x64-7z",
316 | "win-x64-exe",
317 | "win-x64-msi",
318 | "win-x64-zip",
319 | "win-x86-7z",
320 | "win-x86-exe",
321 | "win-x86-msi",
322 | "win-x86-zip"
323 | ],
324 | "npm": "6.14.4",
325 | "v8": "6.8.275.32",
326 | "uv": "1.34.2",
327 | "zlib": "1.2.11",
328 | "openssl": "1.1.1e",
329 | "modules": "64",
330 | "lts": "Dubnium",
331 | "security": false
332 | },
333 | {
334 | "version": "v9.11.2",
335 | "date": "2018-06-12",
336 | "files": [
337 | "aix-ppc64",
338 | "headers",
339 | "linux-arm64",
340 | "linux-armv6l",
341 | "linux-armv7l",
342 | "linux-ppc64le",
343 | "linux-s390x",
344 | "linux-x64",
345 | "linux-x86",
346 | "osx-x64-pkg",
347 | "osx-x64-tar",
348 | "src",
349 | "sunos-x64",
350 | "sunos-x86",
351 | "win-x64-7z",
352 | "win-x64-exe",
353 | "win-x64-msi",
354 | "win-x64-zip",
355 | "win-x86-7z",
356 | "win-x86-exe",
357 | "win-x86-msi",
358 | "win-x86-zip"
359 | ],
360 | "npm": "5.6.0",
361 | "v8": "6.2.414.46",
362 | "uv": "1.19.2",
363 | "zlib": "1.2.11",
364 | "openssl": "1.0.2o",
365 | "modules": "59",
366 | "lts": false,
367 | "security": false
368 | },
369 | {
370 | "version": "v9.11.1",
371 | "date": "2018-04-05",
372 | "files": [
373 | "aix-ppc64",
374 | "headers",
375 | "linux-arm64",
376 | "linux-armv6l",
377 | "linux-armv7l",
378 | "linux-ppc64le",
379 | "linux-s390x",
380 | "linux-x64",
381 | "linux-x86",
382 | "osx-x64-pkg",
383 | "osx-x64-tar",
384 | "src",
385 | "sunos-x64",
386 | "sunos-x86",
387 | "win-x64-7z",
388 | "win-x64-exe",
389 | "win-x64-msi",
390 | "win-x64-zip",
391 | "win-x86-7z",
392 | "win-x86-exe",
393 | "win-x86-msi",
394 | "win-x86-zip"
395 | ],
396 | "npm": "5.6.0",
397 | "v8": "6.2.414.46",
398 | "uv": "1.19.2",
399 | "zlib": "1.2.11",
400 | "openssl": "1.0.2o",
401 | "modules": "59",
402 | "lts": false,
403 | "security": false
404 | },
405 | {
406 | "version": "v8.17.0",
407 | "date": "2019-12-17",
408 | "files": [
409 | "aix-ppc64",
410 | "headers",
411 | "linux-arm64",
412 | "linux-armv6l",
413 | "linux-armv7l",
414 | "linux-ppc64le",
415 | "linux-s390x",
416 | "linux-x64",
417 | "linux-x86",
418 | "osx-x64-pkg",
419 | "osx-x64-tar",
420 | "src",
421 | "sunos-x64",
422 | "sunos-x86",
423 | "win-x64-7z",
424 | "win-x64-exe",
425 | "win-x64-msi",
426 | "win-x64-zip",
427 | "win-x86-7z",
428 | "win-x86-exe",
429 | "win-x86-msi",
430 | "win-x86-zip"
431 | ],
432 | "npm": "6.13.4",
433 | "v8": "6.2.414.78",
434 | "uv": "1.23.2",
435 | "zlib": "1.2.11",
436 | "openssl": "1.0.2s",
437 | "modules": "57",
438 | "lts": "Carbon",
439 | "security": true
440 | },
441 | {
442 | "version": "v8.16.2",
443 | "date": "2019-10-08",
444 | "files": [
445 | "aix-ppc64",
446 | "headers",
447 | "linux-arm64",
448 | "linux-armv6l",
449 | "linux-armv7l",
450 | "linux-ppc64le",
451 | "linux-s390x",
452 | "linux-x64",
453 | "linux-x86",
454 | "osx-x64-pkg",
455 | "osx-x64-tar",
456 | "src",
457 | "sunos-x64",
458 | "sunos-x86",
459 | "win-x64-7z",
460 | "win-x64-exe",
461 | "win-x64-msi",
462 | "win-x64-zip",
463 | "win-x86-7z",
464 | "win-x86-exe",
465 | "win-x86-msi",
466 | "win-x86-zip"
467 | ],
468 | "npm": "6.4.1",
469 | "v8": "6.2.414.78",
470 | "uv": "1.23.2",
471 | "zlib": "1.2.11",
472 | "openssl": "1.0.2s",
473 | "modules": "57",
474 | "lts": "Carbon",
475 | "security": false
476 | },
477 | {
478 | "version": "v7.10.1",
479 | "date": "2017-07-11",
480 | "files": [
481 | "aix-ppc64",
482 | "headers",
483 | "linux-arm64",
484 | "linux-armv6l",
485 | "linux-armv7l",
486 | "linux-ppc64le",
487 | "linux-s390x",
488 | "linux-x64",
489 | "linux-x86",
490 | "osx-x64-pkg",
491 | "osx-x64-tar",
492 | "src",
493 | "sunos-x64",
494 | "sunos-x86",
495 | "win-x64-7z",
496 | "win-x64-exe",
497 | "win-x64-msi",
498 | "win-x64-zip",
499 | "win-x86-7z",
500 | "win-x86-exe",
501 | "win-x86-msi",
502 | "win-x86-zip"
503 | ],
504 | "npm": "4.2.0",
505 | "v8": "5.5.372.43",
506 | "uv": "1.11.0",
507 | "zlib": "1.2.11",
508 | "openssl": "1.0.2k",
509 | "modules": "51",
510 | "lts": false,
511 | "security": true
512 | },
513 | {
514 | "version": "v7.10.0",
515 | "date": "2017-05-02",
516 | "files": [
517 | "aix-ppc64",
518 | "headers",
519 | "linux-arm64",
520 | "linux-armv6l",
521 | "linux-armv7l",
522 | "linux-ppc64le",
523 | "linux-s390x",
524 | "linux-x64",
525 | "linux-x86",
526 | "osx-x64-pkg",
527 | "osx-x64-tar",
528 | "src",
529 | "sunos-x64",
530 | "sunos-x86",
531 | "win-x64-7z",
532 | "win-x64-exe",
533 | "win-x64-msi",
534 | "win-x64-zip",
535 | "win-x86-7z",
536 | "win-x86-exe",
537 | "win-x86-msi",
538 | "win-x86-zip"
539 | ],
540 | "npm": "4.2.0",
541 | "v8": "5.5.372.43",
542 | "uv": "1.11.0",
543 | "zlib": "1.2.11",
544 | "openssl": "1.0.2k",
545 | "modules": "51",
546 | "lts": false,
547 | "security": false
548 | },
549 | {
550 | "version": "v6.17.1",
551 | "date": "2019-04-03",
552 | "files": [
553 | "aix-ppc64",
554 | "headers",
555 | "linux-arm64",
556 | "linux-armv6l",
557 | "linux-armv7l",
558 | "linux-ppc64le",
559 | "linux-s390x",
560 | "linux-x64",
561 | "linux-x86",
562 | "osx-x64-pkg",
563 | "osx-x64-tar",
564 | "src",
565 | "sunos-x64",
566 | "sunos-x86",
567 | "win-x64-7z",
568 | "win-x64-exe",
569 | "win-x64-msi",
570 | "win-x64-zip",
571 | "win-x86-7z",
572 | "win-x86-exe",
573 | "win-x86-msi",
574 | "win-x86-zip"
575 | ],
576 | "npm": "3.10.10",
577 | "v8": "5.1.281.111",
578 | "uv": "1.16.1",
579 | "zlib": "1.2.11",
580 | "openssl": "1.0.2r",
581 | "modules": "48",
582 | "lts": "Boron",
583 | "security": false
584 | },
585 | {
586 | "version": "v6.17.0",
587 | "date": "2019-02-28",
588 | "files": [
589 | "aix-ppc64",
590 | "headers",
591 | "linux-arm64",
592 | "linux-armv6l",
593 | "linux-armv7l",
594 | "linux-ppc64le",
595 | "linux-s390x",
596 | "linux-x64",
597 | "linux-x86",
598 | "osx-x64-pkg",
599 | "osx-x64-tar",
600 | "src",
601 | "sunos-x64",
602 | "sunos-x86",
603 | "win-x64-7z",
604 | "win-x64-exe",
605 | "win-x64-msi",
606 | "win-x64-zip",
607 | "win-x86-7z",
608 | "win-x86-exe",
609 | "win-x86-msi",
610 | "win-x86-zip"
611 | ],
612 | "npm": "3.10.10",
613 | "v8": "5.1.281.111",
614 | "uv": "1.16.1",
615 | "zlib": "1.2.11",
616 | "openssl": "1.0.2r",
617 | "modules": "48",
618 | "lts": "Boron",
619 | "security": true
620 | },
621 | {
622 | "version": "v5.12.0",
623 | "date": "2016-06-23",
624 | "files": [
625 | "headers",
626 | "linux-arm64",
627 | "linux-armv6l",
628 | "linux-armv7l",
629 | "linux-ppc64le",
630 | "linux-x64",
631 | "linux-x86",
632 | "osx-x64-pkg",
633 | "osx-x64-tar",
634 | "src",
635 | "sunos-x64",
636 | "sunos-x86",
637 | "win-x64-exe",
638 | "win-x64-msi",
639 | "win-x86-exe",
640 | "win-x86-msi"
641 | ],
642 | "npm": "3.8.6",
643 | "v8": "4.6.85.32",
644 | "uv": "1.8.0",
645 | "zlib": "1.2.8",
646 | "openssl": "1.0.2h",
647 | "modules": "47",
648 | "lts": false,
649 | "security": false
650 | },
651 | {
652 | "version": "v4.9.1",
653 | "date": "2018-03-29",
654 | "files": [
655 | "headers",
656 | "linux-arm64",
657 | "linux-armv6l",
658 | "linux-armv7l",
659 | "linux-ppc64le",
660 | "linux-x64",
661 | "linux-x86",
662 | "osx-x64-pkg",
663 | "osx-x64-tar",
664 | "src",
665 | "sunos-x64",
666 | "sunos-x86",
667 | "win-x64-7z",
668 | "win-x64-exe",
669 | "win-x64-msi",
670 | "win-x64-zip",
671 | "win-x86-7z",
672 | "win-x86-exe",
673 | "win-x86-msi",
674 | "win-x86-zip"
675 | ],
676 | "npm": "2.15.11",
677 | "v8": "4.5.103.53",
678 | "uv": "1.9.1",
679 | "zlib": "1.2.11",
680 | "openssl": "1.0.2o",
681 | "modules": "46",
682 | "lts": "Argon",
683 | "security": false
684 | },
685 | {
686 | "version": "v4.9.0",
687 | "date": "2018-03-28",
688 | "files": [
689 | "headers",
690 | "linux-arm64",
691 | "linux-armv6l",
692 | "linux-armv7l",
693 | "linux-ppc64le",
694 | "linux-x64",
695 | "linux-x86",
696 | "osx-x64-pkg",
697 | "osx-x64-tar",
698 | "src",
699 | "sunos-x64",
700 | "sunos-x86",
701 | "win-x64-7z",
702 | "win-x64-exe",
703 | "win-x64-msi",
704 | "win-x64-zip",
705 | "win-x86-7z",
706 | "win-x86-exe",
707 | "win-x86-msi",
708 | "win-x86-zip"
709 | ],
710 | "npm": "2.15.11",
711 | "v8": "4.5.103.53",
712 | "uv": "1.9.1",
713 | "zlib": "1.2.11",
714 | "openssl": "1.0.2o",
715 | "modules": "46",
716 | "lts": "Argon",
717 | "security": true
718 | },
719 | {
720 | "version": "v0.12.18",
721 | "date": "2017-02-22",
722 | "files": [
723 | "headers",
724 | "linux-x64",
725 | "linux-x86",
726 | "osx-x64-pkg",
727 | "osx-x64-tar",
728 | "osx-x86-tar",
729 | "src",
730 | "sunos-x86",
731 | "win-x64-exe",
732 | "win-x86-exe",
733 | "win-x86-msi"
734 | ],
735 | "npm": "2.15.11",
736 | "v8": "3.28.71.20",
737 | "uv": "1.6.1",
738 | "zlib": "1.2.8",
739 | "openssl": "1.0.1u",
740 | "modules": "14",
741 | "lts": false,
742 | "security": false
743 | },
744 | {
745 | "version": "v0.12.17",
746 | "date": "2016-10-18",
747 | "files": [
748 | "headers",
749 | "linux-x64",
750 | "linux-x86",
751 | "osx-x64-pkg",
752 | "osx-x64-tar",
753 | "osx-x86-tar",
754 | "src",
755 | "sunos-x64",
756 | "sunos-x86",
757 | "win-x64-exe",
758 | "win-x86-exe",
759 | "win-x86-msi"
760 | ],
761 | "npm": "2.15.1",
762 | "v8": "3.28.71.19",
763 | "uv": "1.6.1",
764 | "zlib": "1.2.8",
765 | "openssl": "1.0.1u",
766 | "modules": "14",
767 | "lts": false,
768 | "security": true
769 | }
770 | ]
--------------------------------------------------------------------------------