Compare commits

..

22 Commits

Author SHA1 Message Date
Konrad Pabjan 60c1140889
Clarifications to the README (#167)
* Update README.md

* Update README.md

Co-authored-by: Bryan MacFarlane <bryanmacfarlane@github.com>
2021-01-20 11:27:33 -05:00
Maxim Lobanov 321b6ccb03
Switch from "master" to "main" branch (#171)
* Switch from "master" to "main" branch

* Update README.md
2020-07-20 12:50:59 -04:00
Maxim Lobanov 1ae8f4b1fd
Implement "check-latest" flag to check if pre-cached version is latest one (#165) 2020-06-29 14:56:37 -04:00
Bryan MacFarlane 0e2f9cde8b announce v2-beta 2020-05-19 09:57:20 -04:00
Bryan MacFarlane e434342e4e
download from node-versions and fallback to node dist (#147)
* download LTS versions from releases

* support upcoiming ghes 

Co-authored-by: eric sciple <ericsciple@users.noreply.github.com>
2020-05-19 09:25:54 -04:00
Bryan MacFarlane 7c6182c745
Update README.md 2020-05-06 09:58:52 -04:00
Bryan MacFarlane a47b2f66c6
Merge pull request #108 from nschonni/patch-2
chore: Swap EOL 8 in example for 12
2020-05-02 11:47:58 -04:00
Bryan MacFarlane 89ea387bde
Merge pull request #141 from fishcharlie/patch-1
Changing node-version default to be latest LTS (12.x)
2020-05-02 11:45:15 -04:00
Josh Gross 44c9c18728
Merge pull request #142 from actions/joshmgross/run-tests-on-windows
Run test job on windows-latest
2020-04-21 16:16:40 -04:00
Josh Gross e715d9a456
Merge pull request #137 from actions/joshmgross/log-node-version
Log node and npm versions
2020-04-21 15:45:36 -04:00
Josh Gross b1f2e78536
Use matrix for workflow 2020-04-21 14:43:21 -04:00
Josh Gross 55897e37f3
Run test job on windows-latest 2020-04-21 14:39:51 -04:00
Josh Gross 46903d1fb1
Await exec calls to ensure proper version ordering 2020-04-21 14:17:29 -04:00
Josh Gross 28505ad4d3
Move version to args 2020-04-21 14:13:03 -04:00
Josh Gross 2d53d29868
Use actions/exec for getting version 2020-04-21 14:03:26 -04:00
Josh Gross 1e163ded31
Quote exec parameters 2020-04-21 11:40:45 -04:00
Josh Gross ffde538781
Only include npm version if npm exists 2020-04-21 11:23:19 -04:00
Charlie Fish 0cc027b656
Changing node-version default to be latest LTS (12.x) 2020-04-20 17:36:56 -06:00
Josh Gross e99a7e62b2
Add dist changes 2020-04-16 11:47:45 -04:00
Josh Gross 0dc69b3a71
Remove extra version logging 2020-04-16 10:56:07 -04:00
Josh Gross 52eb8a7524
Log node and npm version 2020-04-16 10:45:55 -04:00
Nick Schonning 6b7f6c9fa6
chore: Swap EOL 8 in example for 12 2020-01-31 02:12:22 -05:00
13 changed files with 1000 additions and 193 deletions

32
.github/workflows/build-test.yml vendored Normal file
View File

@ -0,0 +1,32 @@
name: build-test
on:
pull_request:
paths-ignore:
- '**.md'
push:
branches:
- master
- releases/*
paths-ignore:
- '**.md'
jobs:
build:
runs-on: ${{ matrix.operating-system }}
strategy:
matrix:
operating-system: [ubuntu-latest, windows-latest, macos-latest]
steps:
- uses: actions/checkout@v2
- name: Setup node 12
uses: actions/setup-node@v1
with:
node-version: 12.x
- run: npm ci
- run: npm run build
- run: npm run format-check
- run: npm test
- name: Verify no unstaged changes
if: runner.os != 'windows'
run: __tests__/verify-no-unstaged-changes.sh

56
.github/workflows/proxy.yml vendored Normal file
View File

@ -0,0 +1,56 @@
name: proxy
on:
pull_request:
paths-ignore:
- '**.md'
push:
branches:
- master
- releases/*
paths-ignore:
- '**.md'
jobs:
test-proxy:
runs-on: ubuntu-latest
strategy:
fail-fast: false
container:
image: ubuntu:latest
options: --dns 127.0.0.1
services:
squid-proxy:
image: datadog/squid:latest
ports:
- 3128:3128
env:
https_proxy: http://squid-proxy:3128
steps:
- uses: actions/checkout@v2
- name: Clear tool cache
run: rm -rf $RUNNER_TOOL_CACHE/*
- name: Setup node 10
uses: ./
with:
node-version: 10.x
- name: Verify node and npm
run: __tests__/verify-node.sh 10
test-bypass-proxy:
runs-on: ubuntu-latest
strategy:
fail-fast: false
env:
https_proxy: http://no-such-proxy:3128
no_proxy: api.github.com,github.com,nodejs.org,registry.npmjs.org,*.s3.amazonaws.com,s3.amazonaws.com
steps:
- uses: actions/checkout@v2
- name: Clear tool cache
run: rm -rf $RUNNER_TOOL_CACHE/*
- name: Setup node 11
uses: ./
with:
node-version: 11
- name: Verify node and npm
run: __tests__/verify-node.sh 11

99
.github/workflows/versions.yml vendored Normal file
View File

@ -0,0 +1,99 @@
name: versions
on:
pull_request:
paths-ignore:
- '**.md'
push:
branches:
- master
- releases/*
paths-ignore:
- '**.md'
jobs:
local-cache:
runs-on: ${{ matrix.operating-system }}
strategy:
fail-fast: false
matrix:
operating-system: [ubuntu-latest, windows-latest, macos-latest]
node-version: [10, 12, 14]
steps:
- uses: actions/checkout@v2
- name: Setup Node
uses: ./
with:
node-version: ${{ matrix.node-version }}
- name: Verify node and npm
run: __tests__/verify-node.sh "${{ matrix.node-version }}"
shell: bash
manifest:
runs-on: ${{ matrix.operating-system }}
strategy:
fail-fast: false
matrix:
operating-system: [ubuntu-latest, windows-latest, macos-latest]
node-version: [10.15, 12.16.0, 14.2.0]
steps:
- uses: actions/checkout@v2
- name: Setup Node
uses: ./
with:
node-version: ${{ matrix.node-version }}
- name: Verify node and npm
run: __tests__/verify-node.sh "${{ matrix.node-version }}"
shell: bash
check-latest:
runs-on: ${{ matrix.operating-system }}
strategy:
fail-fast: false
matrix:
operating-system: [ubuntu-latest, windows-latest, macos-latest]
node-version: [10, 11, 12, 14]
steps:
- uses: actions/checkout@v2
- name: Setup Node and check latest
uses: ./
with:
node-version: ${{ matrix.node-version }}
check-latest: true
- name: Verify node and npm
run: __tests__/verify-node.sh "${{ matrix.node-version }}"
shell: bash
node-dist:
runs-on: ${{ matrix.operating-system }}
strategy:
fail-fast: false
matrix:
operating-system: [ubuntu-latest, windows-latest, macos-latest]
node-version: [11, 13]
steps:
- uses: actions/checkout@v2
- name: Setup Node from dist
uses: ./
with:
node-version: ${{ matrix.node-version }}
- name: Verify node and npm
run: __tests__/verify-node.sh "${{ matrix.node-version }}"
shell: bash
old-versions:
runs-on: ${{ matrix.operating-system }}
strategy:
fail-fast: false
matrix:
operating-system: [ubuntu-latest, windows-latest, macos-latest]
steps:
- uses: actions/checkout@v2
# test old versions which didn't have npm and layout different
- name: Setup node 0.12.18 from dist
uses: ./
with:
node-version: 0.12.18
- name: Verify node
run: __tests__/verify-node.sh 0.12.18 SKIP_NPM
shell: bash

View File

@ -1,94 +0,0 @@
name: Main workflow
on:
pull_request:
push:
branches:
- master
- releases/*
jobs:
build:
runs-on: ${{ matrix.operating-system }}
strategy:
matrix:
operating-system: [ubuntu-latest, windows-latest]
steps:
- uses: actions/checkout@v2
- name: Setup node 12
uses: actions/setup-node@v1
with:
node-version: 12.x
- run: npm ci
- run: npm run build
- run: npm run format-check
- run: npm test
- name: Verify no unstaged changes
if: runner.os != 'windows'
run: __tests__/verify-no-unstaged-changes.sh
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Clear tool cache
run: rm -rf $RUNNER_TOOL_CACHE/*
- name: Setup node 10
uses: ./
with:
node-version: 10.x
- name: Verify node and npm
run: __tests__/verify-node.sh 10
test-fallback:
runs-on: windows-latest
steps:
- uses: actions/checkout@v2
- name: Clear tool cache
run: mv "${{ runner.tool_cache }}" "${{ runner.tool_cache }}.old"
- name: Setup node 0.12.18 # For non LTS versions of Node, the zip is not always available
uses: ./ # and falls back to downloading node.exe and node.lib
with:
node-version: 0.12.18
- name: Verify node
shell: bash
run: __tests__/verify-node.sh 0.12.18 SKIP_NPM
test-proxy:
runs-on: ubuntu-latest
container:
image: ubuntu:latest
options: --dns 127.0.0.1
services:
squid-proxy:
image: datadog/squid:latest
ports:
- 3128:3128
env:
https_proxy: http://squid-proxy:3128
steps:
- uses: actions/checkout@v2
- name: Clear tool cache
run: rm -rf $RUNNER_TOOL_CACHE/*
- name: Setup node 10
uses: ./
with:
node-version: 10.x
- name: Verify node and npm
run: __tests__/verify-node.sh 10
test-bypass-proxy:
runs-on: ubuntu-latest
env:
https_proxy: http://no-such-proxy:3128
no_proxy: github.com,nodejs.org,registry.npmjs.org
steps:
- uses: actions/checkout@v2
- name: Clear tool cache
run: rm -rf $RUNNER_TOOL_CACHE/*
- name: Setup node 10
uses: ./
with:
node-version: 10.x
- name: Verify node and npm
run: __tests__/verify-node.sh 10

View File

@ -1,13 +1,30 @@
# setup-node
<p align="left">
<a href="https://github.com/actions/setup-node"><img alt="GitHub Actions status" src="https://github.com/actions/setup-node/workflows/Main%20workflow/badge.svg"></a>
<a href="https://github.com/actions/setup-node/actions?query=workflow%3Abuild-test"><img alt="build-test status" src="https://github.com/actions/setup-node/workflows/build-test/badge.svg"></a> <a href="https://github.com/actions/setup-node/actions?query=workflow%3Aversions"><img alt="versions status" src="https://github.com/actions/setup-node/workflows/versions/badge.svg"></a> <a href="https://github.com/actions/setup-node/actions?query=workflow%3Aproxy"><img alt="proxy status" src="https://github.com/actions/setup-node/workflows/proxy/badge.svg"></a>
</p>
This action sets by node environment for use in actions by:
- optionally downloading and caching a version of node - npm by version spec and add to PATH
- registering problem matchers for error output
- configuring authentication for GPR or npm
# v2-beta
A beta release which adds reliability for pulling node distributions from a cache of node releases is available by referencing the `v2-beta` tag.
```yaml
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2-beta
with:
node-version: '12'
```
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/).
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.
# Usage
@ -19,7 +36,26 @@ steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v1
with:
node-version: '10.x'
node-version: '12'
- run: npm install
- run: npm test
```
Check latest version:
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.
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.
> Setting `check-latest` to `true` has performance implications as downloading versions of Node is slower than using cached versions
```yaml
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
with:
node-version: '12'
check-latest: true
- run: npm install
- run: npm test
```
@ -31,7 +67,7 @@ jobs:
runs-on: ubuntu-16.04
strategy:
matrix:
node: [ '10', '8' ]
node: [ '10', '12' ]
name: Node ${{ matrix.node }} sample
steps:
- uses: actions/checkout@v2

View File

@ -8,6 +8,7 @@ import path from 'path';
import * as main from '../src/main';
import * as im from '../src/installer';
import * as auth from '../src/authutil';
import {context} from '@actions/github';
let nodeTestManifest = require('./data/versions-manifest.json');
let nodeTestDist = require('./data/node-dist-index.json');
@ -24,6 +25,7 @@ describe('setup-node', () => {
let findSpy: jest.SpyInstance;
let cnSpy: jest.SpyInstance;
let logSpy: jest.SpyInstance;
let warningSpy: jest.SpyInstance;
let getManifestSpy: jest.SpyInstance;
let getDistSpy: jest.SpyInstance;
let platSpy: jest.SpyInstance;
@ -77,8 +79,9 @@ describe('setup-node', () => {
// writes
cnSpy = jest.spyOn(process.stdout, 'write');
logSpy = jest.spyOn(console, 'log');
logSpy = jest.spyOn(core, 'info');
dbgSpy = jest.spyOn(core, 'debug');
warningSpy = jest.spyOn(core, 'warning');
cnSpy.mockImplementation(line => {
// uncomment to debug
// process.stderr.write('write:' + line + '\n');
@ -333,4 +336,154 @@ describe('setup-node', () => {
expect(cnSpy).toHaveBeenCalledWith(`::error::${errMsg}${osm.EOL}`);
});
describe('check-latest flag', () => {
it('use local version and dont check manifest if check-latest is not specified', async () => {
os.platform = 'linux';
os.arch = 'x64';
inputs['node-version'] = '12';
inputs['check-latest'] = 'false';
const toolPath = path.normalize('/cache/node/12.16.1/x64');
findSpy.mockReturnValue(toolPath);
await main.run();
expect(logSpy).toHaveBeenCalledWith(`Found in cache @ ${toolPath}`);
expect(logSpy).not.toHaveBeenCalledWith(
'Attempt to resolve the latest version from manifest...'
);
});
it('check latest version and resolve it from local cache', async () => {
os.platform = 'linux';
os.arch = 'x64';
inputs['node-version'] = '12';
inputs['check-latest'] = 'true';
const toolPath = path.normalize('/cache/node/12.16.2/x64');
findSpy.mockReturnValue(toolPath);
dlSpy.mockImplementation(async () => '/some/temp/path');
exSpy.mockImplementation(async () => '/some/other/temp/path');
cacheSpy.mockImplementation(async () => toolPath);
await main.run();
expect(logSpy).toHaveBeenCalledWith(
'Attempt to resolve the latest version from manifest...'
);
expect(logSpy).toHaveBeenCalledWith("Resolved as '12.16.2'");
expect(logSpy).toHaveBeenCalledWith(`Found in cache @ ${toolPath}`);
});
it('check latest version and install it from manifest', async () => {
os.platform = 'linux';
os.arch = 'x64';
inputs['node-version'] = '12';
inputs['check-latest'] = 'true';
findSpy.mockImplementation(() => '');
dlSpy.mockImplementation(async () => '/some/temp/path');
const toolPath = path.normalize('/cache/node/12.16.2/x64');
exSpy.mockImplementation(async () => '/some/other/temp/path');
cacheSpy.mockImplementation(async () => toolPath);
const expectedUrl =
'https://github.com/actions/node-versions/releases/download/12.16.2-20200423.28/node-12.16.2-linux-x64.tar.gz';
await main.run();
expect(logSpy).toHaveBeenCalledWith(
'Attempt to resolve the latest version from manifest...'
);
expect(logSpy).toHaveBeenCalledWith("Resolved as '12.16.2'");
expect(logSpy).toHaveBeenCalledWith(
`Acquiring 12.16.2 from ${expectedUrl}`
);
expect(logSpy).toHaveBeenCalledWith('Extracting ...');
});
it('fallback to dist if version if not found in manifest', async () => {
os.platform = 'linux';
os.arch = 'x64';
// a version which is not in the manifest but is in node dist
let versionSpec = '11';
inputs['node-version'] = versionSpec;
inputs['check-latest'] = 'true';
inputs['always-auth'] = false;
inputs['token'] = 'faketoken';
// ... but not in the local cache
findSpy.mockImplementation(() => '');
dlSpy.mockImplementation(async () => '/some/temp/path');
let toolPath = path.normalize('/cache/node/11.11.0/x64');
exSpy.mockImplementation(async () => '/some/other/temp/path');
cacheSpy.mockImplementation(async () => toolPath);
await main.run();
let expPath = path.join(toolPath, 'bin');
expect(dlSpy).toHaveBeenCalled();
expect(exSpy).toHaveBeenCalled();
expect(logSpy).toHaveBeenCalledWith(
'Attempt to resolve the latest version from manifest...'
);
expect(logSpy).toHaveBeenCalledWith(
`Failed to resolve version ${versionSpec} from manifest`
);
expect(logSpy).toHaveBeenCalledWith(
`Attempting to download ${versionSpec}...`
);
expect(cnSpy).toHaveBeenCalledWith(`::add-path::${expPath}${osm.EOL}`);
});
it('fallback to dist if manifest is not available', async () => {
os.platform = 'linux';
os.arch = 'x64';
// a version which is not in the manifest but is in node dist
let versionSpec = '12';
inputs['node-version'] = versionSpec;
inputs['check-latest'] = 'true';
inputs['always-auth'] = false;
inputs['token'] = 'faketoken';
// ... but not in the local cache
findSpy.mockImplementation(() => '');
getManifestSpy.mockImplementation(() => {
throw new Error('Unable to download manifest');
});
dlSpy.mockImplementation(async () => '/some/temp/path');
let toolPath = path.normalize('/cache/node/12.11.0/x64');
exSpy.mockImplementation(async () => '/some/other/temp/path');
cacheSpy.mockImplementation(async () => toolPath);
await main.run();
let expPath = path.join(toolPath, 'bin');
expect(dlSpy).toHaveBeenCalled();
expect(exSpy).toHaveBeenCalled();
expect(logSpy).toHaveBeenCalledWith(
'Attempt to resolve the latest version from manifest...'
);
expect(logSpy).toHaveBeenCalledWith(
'Unable to resolve version from manifest...'
);
expect(logSpy).toHaveBeenCalledWith(
`Failed to resolve version ${versionSpec} from manifest`
);
expect(logSpy).toHaveBeenCalledWith(
`Attempting to download ${versionSpec}...`
);
expect(cnSpy).toHaveBeenCalledWith(`::add-path::${expPath}${osm.EOL}`);
});
});
});

View File

@ -7,6 +7,9 @@ inputs:
default: 'false'
node-version:
description: 'Version Spec of the version to use. Examples: 12.x, 10.15.1, >=10.15.0'
check-latest:
description: 'Set this option if you want the action to check for the latest available version that satisfies the version spec'
default: false
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'
scope:

535
dist/index.js vendored
View File

@ -563,13 +563,20 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
result["default"] = mod;
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
const os = __webpack_require__(87);
const events = __webpack_require__(614);
const child = __webpack_require__(129);
const path = __webpack_require__(622);
const io = __webpack_require__(1);
const ioUtil = __webpack_require__(672);
const os = __importStar(__webpack_require__(87));
const events = __importStar(__webpack_require__(614));
const child = __importStar(__webpack_require__(129));
const path = __importStar(__webpack_require__(622));
const io = __importStar(__webpack_require__(1));
const ioUtil = __importStar(__webpack_require__(672));
/* eslint-disable @typescript-eslint/unbound-method */
const IS_WINDOWS = process.platform === 'win32';
/*
@ -1013,6 +1020,12 @@ class ToolRunner extends events.EventEmitter {
resolve(exitCode);
}
});
if (this.options.input) {
if (!cp.stdin) {
throw new Error('child process missing stdin');
}
cp.stdin.end(this.options.input);
}
});
});
}
@ -1325,7 +1338,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
};
Object.defineProperty(exports, "__esModule", { value: true });
const semver = __importStar(__webpack_require__(280));
const core_1 = __webpack_require__(470);
const core_1 = __webpack_require__(902);
// needs to be require for core node modules to be mocked
/* eslint @typescript-eslint/no-require-imports: 0 */
const os = __webpack_require__(87);
@ -4618,6 +4631,7 @@ const core = __importStar(__webpack_require__(470));
const installer = __importStar(__webpack_require__(749));
const auth = __importStar(__webpack_require__(202));
const path = __importStar(__webpack_require__(622));
const url_1 = __webpack_require__(835);
function run() {
return __awaiter(this, void 0, void 0, function* () {
try {
@ -4629,11 +4643,12 @@ function run() {
if (!version) {
version = core.getInput('version');
}
console.log(`version: ${version}`);
if (version) {
let token = core.getInput('token');
let auth = !token || isGhes() ? undefined : `token ${token}`;
let stable = (core.getInput('stable') || 'true').toUpperCase() === 'TRUE';
yield installer.getNode(version, stable, token);
const checkLatest = (core.getInput('check-latest') || 'false').toUpperCase() === 'TRUE';
yield installer.getNode(version, stable, checkLatest, auth);
}
const registryUrl = core.getInput('registry-url');
const alwaysAuth = core.getInput('always-auth');
@ -4651,6 +4666,10 @@ function run() {
});
}
exports.run = run;
function isGhes() {
const ghUrl = new url_1.URL(process.env['GITHUB_SERVER_URL'] || 'https://github.com');
return ghUrl.hostname.toUpperCase() !== 'GITHUB.COM';
}
//# sourceMappingURL=main.js.map
/***/ }),
@ -10870,7 +10889,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const core = __importStar(__webpack_require__(470));
const core = __importStar(__webpack_require__(902));
const io = __importStar(__webpack_require__(1));
const fs = __importStar(__webpack_require__(747));
const mm = __importStar(__webpack_require__(31));
@ -10899,9 +10918,10 @@ const userAgent = 'actions/tool-cache';
*
* @param url url of tool to download
* @param dest path to download tool
* @param auth authorization header
* @returns path to downloaded tool
*/
function downloadTool(url, dest, token) {
function downloadTool(url, dest, auth) {
return __awaiter(this, void 0, void 0, function* () {
dest = dest || path.join(_getTempDirectory(), v4_1.default());
yield io.mkdirP(path.dirname(dest));
@ -10912,7 +10932,7 @@ function downloadTool(url, dest, token) {
const maxSeconds = _getGlobal('TEST_DOWNLOAD_TOOL_RETRY_MAX_SECONDS', 20);
const retryHelper = new retry_helper_1.RetryHelper(maxAttempts, minSeconds, maxSeconds);
return yield retryHelper.execute(() => __awaiter(this, void 0, void 0, function* () {
return yield downloadToolAttempt(url, dest || '', token);
return yield downloadToolAttempt(url, dest || '', auth);
}), (err) => {
if (err instanceof HTTPError && err.httpStatusCode) {
// Don't retry anything less than 500, except 408 Request Timeout and 429 Too Many Requests
@ -10928,7 +10948,7 @@ function downloadTool(url, dest, token) {
});
}
exports.downloadTool = downloadTool;
function downloadToolAttempt(url, dest, token) {
function downloadToolAttempt(url, dest, auth) {
return __awaiter(this, void 0, void 0, function* () {
if (fs.existsSync(dest)) {
throw new Error(`Destination file path ${dest} already exists`);
@ -10938,9 +10958,10 @@ function downloadToolAttempt(url, dest, token) {
allowRetries: false
});
let headers;
if (token) {
if (auth) {
core.debug('set auth');
headers = {
authorization: `token ${token}`
authorization: auth
};
}
const response = yield http.get(url, headers);
@ -10998,9 +11019,10 @@ function extract7z(file, dest, _7zPath) {
process.chdir(dest);
if (_7zPath) {
try {
const logLevel = core.isDebug() ? '-bb1' : '-bb0';
const args = [
'x',
'-bb1',
logLevel,
'-bd',
'-sccUTF-8',
file
@ -11076,7 +11098,16 @@ function extractTar(file, dest, flags = 'xz') {
core.debug(versionOutput.trim());
const isGnuTar = versionOutput.toUpperCase().includes('GNU TAR');
// Initialize args
const args = [flags];
let args;
if (flags instanceof Array) {
args = flags;
}
else {
args = [flags];
}
if (core.isDebug() && !flags.includes('v')) {
args.push('-v');
}
let destArg = dest;
let fileArg = file;
if (IS_WINDOWS && isGnuTar) {
@ -11126,7 +11157,7 @@ function extractZipWin(file, dest) {
const escapedDest = dest.replace(/'/g, "''").replace(/"|\n|\r/g, '');
const command = `$ErrorActionPreference = 'Stop' ; try { Add-Type -AssemblyName System.IO.Compression.FileSystem } catch { } ; [System.IO.Compression.ZipFile]::ExtractToDirectory('${escapedFile}', '${escapedDest}')`;
// run powershell
const powershellPath = yield io.which('powershell');
const powershellPath = yield io.which('powershell', true);
const args = [
'-NoLogo',
'-Sta',
@ -11142,8 +11173,12 @@ function extractZipWin(file, dest) {
}
function extractZipNix(file, dest) {
return __awaiter(this, void 0, void 0, function* () {
const unzipPath = yield io.which('unzip');
yield exec_1.exec(`"${unzipPath}"`, [file], { cwd: dest });
const unzipPath = yield io.which('unzip', true);
const args = [file];
if (!core.isDebug()) {
args.unshift('-q');
}
yield exec_1.exec(`"${unzipPath}"`, args, { cwd: dest });
});
}
/**
@ -11271,14 +11306,16 @@ function findAllVersions(toolName, arch) {
return versions;
}
exports.findAllVersions = findAllVersions;
function getManifestFromRepo(owner, repo, token, branch = 'master') {
function getManifestFromRepo(owner, repo, auth, branch = 'master') {
return __awaiter(this, void 0, void 0, function* () {
let releases = [];
const treeUrl = `https://api.github.com/repos/${owner}/${repo}/git/trees/${branch}`;
const http = new httpm.HttpClient('tool-cache');
const headers = {
authorization: `token ${token}`
};
const headers = {};
if (auth) {
core.debug('set auth');
headers.authorization = auth;
}
const response = yield http.getJson(treeUrl, headers);
if (!response.result) {
return releases;
@ -12956,54 +12993,105 @@ const io = __importStar(__webpack_require__(1));
const tc = __importStar(__webpack_require__(533));
const path = __importStar(__webpack_require__(622));
const semver = __importStar(__webpack_require__(280));
function getNode(versionSpec, stable, token) {
const fs = __webpack_require__(747);
function getNode(versionSpec, stable, checkLatest, auth) {
return __awaiter(this, void 0, void 0, function* () {
let osPlat = os.platform();
let osArch = translateArchToDistUrl(os.arch());
if (checkLatest) {
core.info('Attempt to resolve the latest version from manifest...');
const resolvedVersion = yield resolveVersionFromManifest(versionSpec, stable, auth);
if (resolvedVersion) {
versionSpec = resolvedVersion;
core.info(`Resolved as '${versionSpec}'`);
}
else {
core.info(`Failed to resolve version ${versionSpec} from manifest`);
}
}
// check cache
let info = null;
let toolPath;
toolPath = tc.find('node', versionSpec);
// If not found in cache, download
if (toolPath) {
console.log(`Found in cache @ ${toolPath}`);
core.info(`Found in cache @ ${toolPath}`);
}
else {
console.log(`Attempting to download ${versionSpec}...`);
let info = yield getInfoFromManifest(versionSpec, stable, token);
if (!info) {
console.log('Not found in manifest. Falling back to download directly from Node');
info = yield getInfoFromDist(versionSpec);
}
if (!info) {
throw new Error(`Unable to find Node version '${versionSpec}' for platform ${osPlat} and architecture ${osArch}.`);
}
console.log(`Acquiring ${info.resolvedVersion} from ${info.downloadUrl}`);
core.info(`Attempting to download ${versionSpec}...`);
let downloadPath = '';
let info = null;
//
// Try download from internal distribution (popular versions only)
//
try {
downloadPath = yield tc.downloadTool(info.downloadUrl, undefined, token);
info = yield getInfoFromManifest(versionSpec, stable, auth);
if (info) {
core.info(`Acquiring ${info.resolvedVersion} from ${info.downloadUrl}`);
downloadPath = yield tc.downloadTool(info.downloadUrl, undefined, auth);
}
else {
core.info('Not found in manifest. Falling back to download directly from Node');
}
}
catch (err) {
if (err instanceof tc.HTTPError && err.httpStatusCode == 404) {
return yield acquireNodeFromFallbackLocation(info.resolvedVersion);
// Rate limit?
if (err instanceof tc.HTTPError &&
(err.httpStatusCode === 403 || err.httpStatusCode === 429)) {
core.info(`Received HTTP status code ${err.httpStatusCode}. This usually indicates the rate limit has been exceeded`);
}
else {
core.info(err.message);
}
core.debug(err.stack);
core.info('Falling back to download directly from Node');
}
//
// Download from nodejs.org
//
if (!downloadPath) {
info = yield getInfoFromDist(versionSpec);
if (!info) {
throw new Error(`Unable to find Node version '${versionSpec}' for platform ${osPlat} and architecture ${osArch}.`);
}
core.info(`Acquiring ${info.resolvedVersion} from ${info.downloadUrl}`);
try {
downloadPath = yield tc.downloadTool(info.downloadUrl);
}
catch (err) {
if (err instanceof tc.HTTPError && err.httpStatusCode == 404) {
return yield acquireNodeFromFallbackLocation(info.resolvedVersion);
}
throw err;
}
throw err;
}
//
// Extract
//
core.info('Extracting ...');
let extPath;
info = info || {}; // satisfy compiler, never null when reaches here
if (osPlat == 'win32') {
let _7zPath = path.join(__dirname, '..', 'externals', '7zr.exe');
extPath = yield tc.extract7z(downloadPath, undefined, _7zPath);
// 7z extracts to folder matching file name
let nestedPath = path.join(extPath, path.basename(info.fileName, '.7z'));
if (fs.existsSync(nestedPath)) {
extPath = nestedPath;
}
}
else {
extPath = yield tc.extractTar(downloadPath);
extPath = yield tc.extractTar(downloadPath, undefined, [
'xz',
'--strip',
'1'
]);
}
//
// Install into the local tool cache - node extracts with a root folder that matches the fileName downloaded
//
core.info('Adding to the cache ...');
toolPath = yield tc.cacheDir(extPath, 'node', info.resolvedVersion);
core.info('Done');
}
//
// a tool installer initimately knows details about the layout of that tool
@ -13019,18 +13107,16 @@ function getNode(versionSpec, stable, token) {
});
}
exports.getNode = getNode;
function getInfoFromManifest(versionSpec, stable, token) {
function getInfoFromManifest(versionSpec, stable, auth) {
return __awaiter(this, void 0, void 0, function* () {
let info = null;
const releases = yield tc.getManifestFromRepo('actions', 'node-versions', token);
console.log(`matching ${versionSpec}...`);
const releases = yield tc.getManifestFromRepo('actions', 'node-versions', auth, 'main');
const rel = yield tc.findFromManifest(versionSpec, stable, releases);
if (rel && rel.files.length > 0) {
info = {};
info.resolvedVersion = rel.version;
info.downloadUrl = rel.files[0].download_url;
info.fileName = rel.files[0].filename;
info.token = token;
}
return info;
});
@ -13039,7 +13125,6 @@ function getInfoFromDist(versionSpec) {
return __awaiter(this, void 0, void 0, function* () {
let osPlat = os.platform();
let osArch = translateArchToDistUrl(os.arch());
let info = null;
let version;
version = yield queryDistForMatch(versionSpec);
if (!version) {
@ -13061,6 +13146,18 @@ function getInfoFromDist(versionSpec) {
};
});
}
function resolveVersionFromManifest(versionSpec, stable, auth) {
return __awaiter(this, void 0, void 0, function* () {
try {
const info = yield getInfoFromManifest(versionSpec, stable, auth);
return info === null || info === void 0 ? void 0 : info.resolvedVersion;
}
catch (err) {
core.info('Unable to resolve version from manifest...');
core.debug(err.message);
}
});
}
// TODO - should we just export this from @actions/tool-cache? Lifted directly from there
function evaluateVersions(versions, versionSpec) {
let version = '';
@ -13158,6 +13255,7 @@ function acquireNodeFromFallbackLocation(version) {
try {
exeUrl = `https://nodejs.org/dist/v${version}/win-${osArch}/node.exe`;
libUrl = `https://nodejs.org/dist/v${version}/win-${osArch}/node.lib`;
core.info(`Downloading only node binary from ${exeUrl}`);
const exePath = yield tc.downloadTool(exeUrl);
yield io.cp(exePath, path.join(tempDir, 'node.exe'));
const libPath = yield tc.downloadTool(libUrl);
@ -13176,7 +13274,9 @@ function acquireNodeFromFallbackLocation(version) {
throw err;
}
}
return yield tc.cacheDir(tempDir, 'node', version);
let toolPath = yield tc.cacheDir(tempDir, 'node', version);
core.addPath(toolPath);
return toolPath;
});
}
// os.arch does not always match the relative download url, e.g.
@ -15920,6 +16020,105 @@ function set(object, path, value) {
module.exports = set;
/***/ }),
/***/ 888:
/***/ (function(__unusedmodule, exports, __webpack_require__) {
"use strict";
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
result["default"] = mod;
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
const os = __importStar(__webpack_require__(87));
/**
* Commands
*
* Command Format:
* ::name key=value,key=value::message
*
* Examples:
* ::warning::This is the message
* ::set-env name=MY_VAR::some value
*/
function issueCommand(command, properties, message) {
const cmd = new Command(command, properties, message);
process.stdout.write(cmd.toString() + os.EOL);
}
exports.issueCommand = issueCommand;
function issue(name, message = '') {
issueCommand(name, {}, message);
}
exports.issue = issue;
const CMD_STRING = '::';
class Command {
constructor(command, properties, message) {
if (!command) {
command = 'missing.command';
}
this.command = command;
this.properties = properties;
this.message = message;
}
toString() {
let cmdStr = CMD_STRING + this.command;
if (this.properties && Object.keys(this.properties).length > 0) {
cmdStr += ' ';
let first = true;
for (const key in this.properties) {
if (this.properties.hasOwnProperty(key)) {
const val = this.properties[key];
if (val) {
if (first) {
first = false;
}
else {
cmdStr += ',';
}
cmdStr += `${key}=${escapeProperty(val)}`;
}
}
}
}
cmdStr += `${CMD_STRING}${escapeData(this.message)}`;
return cmdStr;
}
}
/**
* Sanitizes an input into a string so it can be passed into issueCommand safely
* @param input input to sanitize into a string
*/
function toCommandValue(input) {
if (input === null || input === undefined) {
return '';
}
else if (typeof input === 'string' || input instanceof String) {
return input;
}
return JSON.stringify(input);
}
exports.toCommandValue = toCommandValue;
function escapeData(s) {
return toCommandValue(s)
.replace(/%/g, '%25')
.replace(/\r/g, '%0D')
.replace(/\n/g, '%0A');
}
function escapeProperty(s) {
return toCommandValue(s)
.replace(/%/g, '%25')
.replace(/\r/g, '%0D')
.replace(/\n/g, '%0A')
.replace(/:/g, '%3A')
.replace(/,/g, '%2C');
}
//# sourceMappingURL=command.js.map
/***/ }),
/***/ 899:
@ -16025,6 +16224,235 @@ function patchForDeprecation(octokit, apiOptions, method, methodName) {
}
/***/ }),
/***/ 902:
/***/ (function(__unusedmodule, exports, __webpack_require__) {
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
result["default"] = mod;
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
const command_1 = __webpack_require__(888);
const os = __importStar(__webpack_require__(87));
const path = __importStar(__webpack_require__(622));
/**
* The code to exit an action
*/
var ExitCode;
(function (ExitCode) {
/**
* A code indicating that the action was successful
*/
ExitCode[ExitCode["Success"] = 0] = "Success";
/**
* A code indicating that the action was a failure
*/
ExitCode[ExitCode["Failure"] = 1] = "Failure";
})(ExitCode = exports.ExitCode || (exports.ExitCode = {}));
//-----------------------------------------------------------------------
// Variables
//-----------------------------------------------------------------------
/**
* Sets env variable for this action and future actions in the job
* @param name the name of the variable to set
* @param val the value of the variable. Non-string values will be converted to a string via JSON.stringify
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function exportVariable(name, val) {
const convertedVal = command_1.toCommandValue(val);
process.env[name] = convertedVal;
command_1.issueCommand('set-env', { name }, convertedVal);
}
exports.exportVariable = exportVariable;
/**
* Registers a secret which will get masked from logs
* @param secret value of the secret
*/
function setSecret(secret) {
command_1.issueCommand('add-mask', {}, secret);
}
exports.setSecret = setSecret;
/**
* Prepends inputPath to the PATH (for this action and future actions)
* @param inputPath
*/
function addPath(inputPath) {
command_1.issueCommand('add-path', {}, inputPath);
process.env['PATH'] = `${inputPath}${path.delimiter}${process.env['PATH']}`;
}
exports.addPath = addPath;
/**
* Gets the value of an input. The value is also trimmed.
*
* @param name name of the input to get
* @param options optional. See InputOptions.
* @returns string
*/
function getInput(name, options) {
const val = process.env[`INPUT_${name.replace(/ /g, '_').toUpperCase()}`] || '';
if (options && options.required && !val) {
throw new Error(`Input required and not supplied: ${name}`);
}
return val.trim();
}
exports.getInput = getInput;
/**
* Sets the value of an output.
*
* @param name name of the output to set
* @param value value to store. Non-string values will be converted to a string via JSON.stringify
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function setOutput(name, value) {
command_1.issueCommand('set-output', { name }, value);
}
exports.setOutput = setOutput;
/**
* Enables or disables the echoing of commands into stdout for the rest of the step.
* Echoing is disabled by default if ACTIONS_STEP_DEBUG is not set.
*
*/
function setCommandEcho(enabled) {
command_1.issue('echo', enabled ? 'on' : 'off');
}
exports.setCommandEcho = setCommandEcho;
//-----------------------------------------------------------------------
// Results
//-----------------------------------------------------------------------
/**
* Sets the action status to failed.
* When the action exits it will be with an exit code of 1
* @param message add error issue message
*/
function setFailed(message) {
process.exitCode = ExitCode.Failure;
error(message);
}
exports.setFailed = setFailed;
//-----------------------------------------------------------------------
// Logging Commands
//-----------------------------------------------------------------------
/**
* Gets whether Actions Step Debug is on or not
*/
function isDebug() {
return process.env['RUNNER_DEBUG'] === '1';
}
exports.isDebug = isDebug;
/**
* Writes debug message to user log
* @param message debug message
*/
function debug(message) {
command_1.issueCommand('debug', {}, message);
}
exports.debug = debug;
/**
* Adds an error issue
* @param message error issue message. Errors will be converted to string via toString()
*/
function error(message) {
command_1.issue('error', message instanceof Error ? message.toString() : message);
}
exports.error = error;
/**
* Adds an warning issue
* @param message warning issue message. Errors will be converted to string via toString()
*/
function warning(message) {
command_1.issue('warning', message instanceof Error ? message.toString() : message);
}
exports.warning = warning;
/**
* Writes info to log with console.log.
* @param message info message
*/
function info(message) {
process.stdout.write(message + os.EOL);
}
exports.info = info;
/**
* Begin an output group.
*
* Output until the next `groupEnd` will be foldable in this group
*
* @param name The name of the output group
*/
function startGroup(name) {
command_1.issue('group', name);
}
exports.startGroup = startGroup;
/**
* End an output group.
*/
function endGroup() {
command_1.issue('endgroup');
}
exports.endGroup = endGroup;
/**
* Wrap an asynchronous function call in a group.
*
* Returns the same type as the function itself.
*
* @param name The name of the group
* @param fn The function to wrap in the group
*/
function group(name, fn) {
return __awaiter(this, void 0, void 0, function* () {
startGroup(name);
let result;
try {
result = yield fn();
}
finally {
endGroup();
}
return result;
});
}
exports.group = group;
//-----------------------------------------------------------------------
// Wrapper action state
//-----------------------------------------------------------------------
/**
* Saves state for current action, the state can only be retrieved by this action's post job execution.
*
* @param name name of the state to store
* @param value value to store. Non-string values will be converted to a string via JSON.stringify
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function saveState(name, value) {
command_1.issueCommand('save-state', { name }, value);
}
exports.saveState = saveState;
/**
* Gets the value of an state set by this action's main execution.
*
* @param name name of the state to get
* @returns string
*/
function getState(name) {
return process.env[`STATE_${name}`] || '';
}
exports.getState = getState;
//# sourceMappingURL=core.js.map
/***/ }),
/***/ 929:
@ -16666,7 +17094,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
const core = __importStar(__webpack_require__(470));
const core = __importStar(__webpack_require__(902));
/**
* Internal class for retries
*/
@ -16735,8 +17163,15 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
result["default"] = mod;
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
const tr = __webpack_require__(9);
const tr = __importStar(__webpack_require__(9));
/**
* Exec a command.
* Output will be streamed to the live console.

19
package-lock.json generated
View File

@ -10,9 +10,9 @@
"integrity": "sha512-IbCx7oefq+Gi6FWbSs2Fnw8VkEI6Y4gvjrYprY3RV//ksq/KPMlClOerJ4jRosyal6zkUIc8R9fS/cpRMlGClg=="
},
"@actions/exec": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/@actions/exec/-/exec-1.0.3.tgz",
"integrity": "sha512-TogJGnueOmM7ntCi0ASTUj4LapRRtDfj57Ja4IhPmg2fls28uVOPbAn8N+JifaOumN2UG3oEO/Ixek2A4NcYSA==",
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/@actions/exec/-/exec-1.0.4.tgz",
"integrity": "sha512-4DPChWow9yc9W3WqEbUj8Nr86xkpyE29ZzWjXucHItclLbEW6jr80Zx4nqv18QL6KK65+cifiQZXvnqgTV6oHw==",
"requires": {
"@actions/io": "^1.0.1"
}
@ -40,11 +40,11 @@
"integrity": "sha512-J8KuFqVPr3p6U8W93DOXlXW6zFvrQAJANdS+vw0YhusLIq+bszW8zmK2Fh1C2kDPX8FMvwIl1OUcFgvJoXLbAg=="
},
"@actions/tool-cache": {
"version": "1.5.2",
"resolved": "https://registry.npmjs.org/@actions/tool-cache/-/tool-cache-1.5.2.tgz",
"integrity": "sha512-40A1St0GEo+QvHV1YRjStEoQcKKMaip+zNXPgGHcjYXCdZ7cl1LGlwOpsVVqwk+6ue/shFTS76KC1A02mVVCQA==",
"version": "1.5.4",
"resolved": "https://registry.npmjs.org/@actions/tool-cache/-/tool-cache-1.5.4.tgz",
"integrity": "sha512-72ijIBM0s/dx2D0eYYxaxaeKWeVatOK8OHPNctJ5cyKjZp1j12egX+nW/N+tnQRNMVxTp9WjudZO5wizUBxC/w==",
"requires": {
"@actions/core": "^1.2.0",
"@actions/core": "^1.2.3",
"@actions/exec": "^1.0.0",
"@actions/http-client": "^1.0.8",
"@actions/io": "^1.0.1",
@ -52,6 +52,11 @@
"uuid": "^3.3.2"
},
"dependencies": {
"@actions/core": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/@actions/core/-/core-1.2.4.tgz",
"integrity": "sha512-YJCEq8BE3CdN8+7HPZ/4DxJjk/OkZV2FFIf+DlZTC/4iBlzYCD5yjRR6eiOS5llO11zbRltIRuKAjMKaWTE6cg=="
},
"@actions/http-client": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-1.0.8.tgz",

View File

@ -24,10 +24,11 @@
"license": "MIT",
"dependencies": {
"@actions/core": "^1.2.2",
"@actions/exec": "^1.0.3",
"@actions/github": "^1.1.0",
"@actions/http-client": "^1.0.6",
"@actions/io": "^1.0.2",
"@actions/tool-cache": "^1.5.2",
"@actions/tool-cache": "^1.5.4",
"semver": "^6.1.1"
},
"devDependencies": {

View File

@ -6,7 +6,7 @@ import * as io from '@actions/io';
import * as tc from '@actions/tool-cache';
import * as path from 'path';
import * as semver from 'semver';
import {Url} from 'url';
import fs = require('fs');
//
// Node versions interface
@ -19,7 +19,6 @@ export interface INodeVersion {
interface INodeVersionInfo {
downloadUrl: string;
token: string | null;
resolvedVersion: string;
fileName: string;
}
@ -27,63 +26,119 @@ interface INodeVersionInfo {
export async function getNode(
versionSpec: string,
stable: boolean,
token: string
checkLatest: boolean,
auth: string | undefined
) {
let osPlat: string = os.platform();
let osArch: string = translateArchToDistUrl(os.arch());
if (checkLatest) {
core.info('Attempt to resolve the latest version from manifest...');
const resolvedVersion = await resolveVersionFromManifest(
versionSpec,
stable,
auth
);
if (resolvedVersion) {
versionSpec = resolvedVersion;
core.info(`Resolved as '${versionSpec}'`);
} else {
core.info(`Failed to resolve version ${versionSpec} from manifest`);
}
}
// check cache
let info: INodeVersionInfo | null = null;
let toolPath: string;
toolPath = tc.find('node', versionSpec);
// If not found in cache, download
if (toolPath) {
console.log(`Found in cache @ ${toolPath}`);
core.info(`Found in cache @ ${toolPath}`);
} else {
console.log(`Attempting to download ${versionSpec}...`);
let info = await getInfoFromManifest(versionSpec, stable, token);
if (!info) {
console.log(
'Not found in manifest. Falling back to download directly from Node'
);
info = await getInfoFromDist(versionSpec);
}
if (!info) {
throw new Error(
`Unable to find Node version '${versionSpec}' for platform ${osPlat} and architecture ${osArch}.`
);
}
console.log(`Acquiring ${info.resolvedVersion} from ${info.downloadUrl}`);
core.info(`Attempting to download ${versionSpec}...`);
let downloadPath = '';
let info: INodeVersionInfo | null = null;
//
// Try download from internal distribution (popular versions only)
//
try {
downloadPath = await tc.downloadTool(info.downloadUrl, undefined, token);
info = await getInfoFromManifest(versionSpec, stable, auth);
if (info) {
core.info(`Acquiring ${info.resolvedVersion} from ${info.downloadUrl}`);
downloadPath = await tc.downloadTool(info.downloadUrl, undefined, auth);
} else {
core.info(
'Not found in manifest. Falling back to download directly from Node'
);
}
} catch (err) {
if (err instanceof tc.HTTPError && err.httpStatusCode == 404) {
return await acquireNodeFromFallbackLocation(info.resolvedVersion);
// Rate limit?
if (
err instanceof tc.HTTPError &&
(err.httpStatusCode === 403 || err.httpStatusCode === 429)
) {
core.info(
`Received HTTP status code ${err.httpStatusCode}. This usually indicates the rate limit has been exceeded`
);
} else {
core.info(err.message);
}
core.debug(err.stack);
core.info('Falling back to download directly from Node');
}
//
// Download from nodejs.org
//
if (!downloadPath) {
info = await getInfoFromDist(versionSpec);
if (!info) {
throw new Error(
`Unable to find Node version '${versionSpec}' for platform ${osPlat} and architecture ${osArch}.`
);
}
throw err;
core.info(`Acquiring ${info.resolvedVersion} from ${info.downloadUrl}`);
try {
downloadPath = await tc.downloadTool(info.downloadUrl);
} catch (err) {
if (err instanceof tc.HTTPError && err.httpStatusCode == 404) {
return await acquireNodeFromFallbackLocation(info.resolvedVersion);
}
throw err;
}
}
//
// Extract
//
core.info('Extracting ...');
let extPath: string;
info = info || ({} as INodeVersionInfo); // satisfy compiler, never null when reaches here
if (osPlat == 'win32') {
let _7zPath = path.join(__dirname, '..', 'externals', '7zr.exe');
extPath = await tc.extract7z(downloadPath, undefined, _7zPath);
// 7z extracts to folder matching file name
let nestedPath = path.join(extPath, path.basename(info.fileName, '.7z'));
if (fs.existsSync(nestedPath)) {
extPath = nestedPath;
}
} else {
extPath = await tc.extractTar(downloadPath);
extPath = await tc.extractTar(downloadPath, undefined, [
'xz',
'--strip',
'1'
]);
}
//
// Install into the local tool cache - node extracts with a root folder that matches the fileName downloaded
//
core.info('Adding to the cache ...');
toolPath = await tc.cacheDir(extPath, 'node', info.resolvedVersion);
core.info('Done');
}
//
@ -103,15 +158,15 @@ export async function getNode(
async function getInfoFromManifest(
versionSpec: string,
stable: boolean,
token: string
auth: string | undefined
): Promise<INodeVersionInfo | null> {
let info: INodeVersionInfo | null = null;
const releases = await tc.getManifestFromRepo(
'actions',
'node-versions',
token
auth,
'main'
);
console.log(`matching ${versionSpec}...`);
const rel = await tc.findFromManifest(versionSpec, stable, releases);
if (rel && rel.files.length > 0) {
@ -119,7 +174,6 @@ async function getInfoFromManifest(
info.resolvedVersion = rel.version;
info.downloadUrl = rel.files[0].download_url;
info.fileName = rel.files[0].filename;
info.token = token;
}
return info;
@ -131,7 +185,6 @@ async function getInfoFromDist(
let osPlat: string = os.platform();
let osArch: string = translateArchToDistUrl(os.arch());
let info: INodeVersionInfo | null = null;
let version: string;
version = await queryDistForMatch(versionSpec);
@ -158,6 +211,20 @@ async function getInfoFromDist(
};
}
async function resolveVersionFromManifest(
versionSpec: string,
stable: boolean,
auth: string | undefined
): Promise<string | undefined> {
try {
const info = await getInfoFromManifest(versionSpec, stable, auth);
return info?.resolvedVersion;
} catch (err) {
core.info('Unable to resolve version from manifest...');
core.debug(err.message);
}
}
// TODO - should we just export this from @actions/tool-cache? Lifted directly from there
function evaluateVersions(versions: string[], versionSpec: string): string {
let version = '';
@ -262,6 +329,8 @@ async function acquireNodeFromFallbackLocation(
exeUrl = `https://nodejs.org/dist/v${version}/win-${osArch}/node.exe`;
libUrl = `https://nodejs.org/dist/v${version}/win-${osArch}/node.lib`;
core.info(`Downloading only node binary from ${exeUrl}`);
const exePath = await tc.downloadTool(exeUrl);
await io.cp(exePath, path.join(tempDir, 'node.exe'));
const libPath = await tc.downloadTool(libUrl);
@ -279,7 +348,9 @@ async function acquireNodeFromFallbackLocation(
throw err;
}
}
return await tc.cacheDir(tempDir, 'node', version);
let toolPath = await tc.cacheDir(tempDir, 'node', version);
core.addPath(toolPath);
return toolPath;
}
// os.arch does not always match the relative download url, e.g.

View File

@ -2,6 +2,7 @@ import * as core from '@actions/core';
import * as installer from './installer';
import * as auth from './authutil';
import * as path from 'path';
import {URL} from 'url';
export async function run() {
try {
@ -14,11 +15,13 @@ export async function run() {
version = core.getInput('version');
}
console.log(`version: ${version}`);
if (version) {
let token = core.getInput('token');
let auth = !token || isGhes() ? undefined : `token ${token}`;
let stable = (core.getInput('stable') || 'true').toUpperCase() === 'TRUE';
await installer.getNode(version, stable, token);
const checkLatest =
(core.getInput('check-latest') || 'false').toUpperCase() === 'TRUE';
await installer.getNode(version, stable, checkLatest, auth);
}
const registryUrl: string = core.getInput('registry-url');
@ -39,3 +42,10 @@ export async function run() {
core.setFailed(error.message);
}
}
function isGhes(): boolean {
const ghUrl = new URL(
process.env['GITHUB_SERVER_URL'] || 'https://github.com'
);
return ghUrl.hostname.toUpperCase() !== 'GITHUB.COM';
}

View File

@ -13,7 +13,7 @@ rm -rf ./node
export RUNNER_TOOL_CACHE=$(pwd)
export RUNNER_TEMP="${RUNNER_TOOL_CACHE}/temp"
export INPUT_STABLE=true
export INPUT_VERSION="12.x"
export INPUT_VERSION="12" #"0.12.7" #"12" #"11.15.0"
# export your PAT with repo scope before running
export INPUT_TOKEN=$GITHUB_TOKEN