From e53c144a0d7927d83cd04398278ae2235bbc9d27 Mon Sep 17 00:00:00 2001 From: Silvano Cerza Date: Thu, 30 Jul 2020 17:28:21 +0200 Subject: [PATCH] Requests to Github API are now authenticated using the workflow token --- .github/workflows/test.yml | 3 + README.md | 6 +- __tests__/main.test.ts | 23 +- action.yml | 4 + dist/index.js | 2225 +++++++++++++++++++++++------------- package-lock.json | 68 +- package.json | 7 +- src/installer.ts | 15 +- 8 files changed, 1498 insertions(+), 853 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 6d20037..c0f0bdd 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -6,6 +6,9 @@ jobs: test: runs-on: ${{ matrix.operating-system }} + env: + GITHUB_TOKEN: ${{ github.token }} + strategy: matrix: operating-system: [ubuntu-latest, windows-latest] diff --git a/README.md b/README.md index 5b9b3f0..fe117e3 100644 --- a/README.md +++ b/README.md @@ -39,12 +39,14 @@ To work on the codebase you have to install all the dependencies: # npm install ``` -To run the tests: +To run tests set the environment variable `GITHUB_TOKEN` with a valid Personal Access Token and then: ```sh # npm run test ``` +See the [official Github documentation][pat-docs] to know more about Personal Access Tokens. + ## Release 1. `npm install` to add all the dependencies, included development. @@ -53,3 +55,5 @@ To run the tests: 4. `npm run pack` to package for distribution 5. `git add src dist` to check in the code that matters. 6. open a PR and request a review. + +[pat-docs]: https://docs.github.com/en/github/authenticating-to-github/creating-a-personal-access-token diff --git a/__tests__/main.test.ts b/__tests__/main.test.ts index 7a5cdce..57753f9 100644 --- a/__tests__/main.test.ts +++ b/__tests__/main.test.ts @@ -1,3 +1,4 @@ +import core = require("@actions/core"); import io = require("@actions/io"); import path = require("path"); import os = require("os"); @@ -13,8 +14,18 @@ process.env["RUNNER_TEMP"] = tempDir; process.env["RUNNER_TOOL_CACHE"] = toolDir; import * as installer from "../src/installer"; +// Inputs for mock @actions/core +let inputs = { + token: process.env.GITHUB_TOKEN || "" +} as any; + describe("installer tests", () => { beforeEach(async function() { + // Mock getInput + jest.spyOn(core, "getInput").mockImplementation((name: string) => { + return inputs[name]; + }); + await io.rmRF(toolDir); await io.rmRF(tempDir); await io.mkdirP(toolDir); @@ -28,6 +39,7 @@ describe("installer tests", () => { } catch { console.log("Failed to remove test directories"); } + jest.restoreAllMocks(); }); it("Downloads version of Arduino CLI if no matching version is installed", async () => { @@ -41,10 +53,14 @@ describe("installer tests", () => { } else { expect(fs.existsSync(path.join(bindir, "arduino-cli"))).toBe(true); } - }, 10000); + }, 20000); describe("Gets the latest release of Arduino CLI", () => { beforeEach(() => { + jest.spyOn(core, "getInput").mockImplementation((name: string) => { + return inputs[name]; + }); + nock("https://api.github.com") .get("/repos/Arduino/arduino-cli/git/refs/tags") .replyWithFile(200, path.join(dataDir, "tags.json")); @@ -53,6 +69,7 @@ describe("installer tests", () => { afterEach(() => { nock.cleanAll(); nock.enableNetConnect(); + jest.clearAllMocks(); }); it("Gets the latest version of Arduino CLI 0.4.0 using 0.4 and no matching version is installed", async () => { @@ -65,7 +82,7 @@ describe("installer tests", () => { } else { expect(fs.existsSync(path.join(bindir, "arduino-cli"))).toBe(true); } - }, 10000); + }, 20000); it("Gets latest version of Task using 0.x and no matching version is installed", async () => { await installer.getArduinoCli("0.x"); @@ -77,6 +94,6 @@ describe("installer tests", () => { } else { expect(fs.existsSync(path.join(bindir, "arduino-cli"))).toBe(true); } - }, 10000); + }, 20000); }); }); diff --git a/action.yml b/action.yml index 01bc1ad..707525d 100644 --- a/action.yml +++ b/action.yml @@ -5,6 +5,10 @@ inputs: version: description: "Version to use. Example: 0.5.0" default: "0.x" + token: + description: "Personal access token (PAT) used to call the Github API." + required: false + default: ${{ github.token }} runs: using: "node12" main: "dist/index.js" diff --git a/dist/index.js b/dist/index.js index 11702a7..231edef 100644 --- a/dist/index.js +++ b/dist/index.js @@ -360,10 +360,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 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'; /* @@ -709,6 +719,16 @@ class ToolRunner extends events.EventEmitter { */ exec() { return __awaiter(this, void 0, void 0, function* () { + // root the tool path if it is unrooted and contains relative pathing + if (!ioUtil.isRooted(this.toolPath) && + (this.toolPath.includes('/') || + (IS_WINDOWS && this.toolPath.includes('\\')))) { + // prefer options.cwd if it is specified, however options.cwd may also need to be rooted + this.toolPath = path.resolve(process.cwd(), this.options.cwd || process.cwd(), this.toolPath); + } + // if the tool is only a file name, then resolve it from the PATH + // otherwise verify it exists (add extension on Windows if necessary) + this.toolPath = yield io.which(this.toolPath, true); return new Promise((resolve, reject) => { this._debug(`exec tool: ${this.toolPath}`); this._debug('arguments:'); @@ -797,6 +817,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); + } }); }); } @@ -934,235 +960,123 @@ module.exports = require("tls"); /***/ }), -/***/ 87: -/***/ (function(module) { - -module.exports = require("os"); - -/***/ }), - -/***/ 105: -/***/ (function(__unusedmodule, exports, __webpack_require__) { +/***/ 31: +/***/ (function(module, exports, __webpack_require__) { "use strict"; -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. 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) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } + 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 httpm = __webpack_require__(874); -const util = __webpack_require__(729); -class RestClient { - /** - * Creates an instance of the RestClient - * @constructor - * @param {string} userAgent - userAgent for requests - * @param {string} baseUrl - (Optional) If not specified, use full urls per request. If supplied and a function passes a relative url, it will be appended to this - * @param {ifm.IRequestHandler[]} handlers - handlers are typically auth handlers (basic, bearer, ntlm supplied) - * @param {ifm.IRequestOptions} requestOptions - options for each http requests (http proxy setting, socket timeout) - */ - constructor(userAgent, baseUrl, handlers, requestOptions) { - this.client = new httpm.HttpClient(userAgent, handlers, requestOptions); - if (baseUrl) { - this._baseUrl = baseUrl; - } - } - /** - * Gets a resource from an endpoint - * Be aware that not found returns a null. Other error conditions reject the promise - * @param {string} requestUrl - fully qualified or relative url - * @param {IRequestOptions} requestOptions - (optional) requestOptions object - */ - options(requestUrl, options) { - return __awaiter(this, void 0, void 0, function* () { - let url = util.getUrl(requestUrl, this._baseUrl); - let res = yield this.client.options(url, this._headersFromOptions(options)); - return this._processResponse(res, options); - }); - } - /** - * Gets a resource from an endpoint - * Be aware that not found returns a null. Other error conditions reject the promise - * @param {string} resource - fully qualified url or relative path - * @param {IRequestOptions} requestOptions - (optional) requestOptions object - */ - get(resource, options) { - return __awaiter(this, void 0, void 0, function* () { - let url = util.getUrl(resource, this._baseUrl); - let res = yield this.client.get(url, this._headersFromOptions(options)); - return this._processResponse(res, options); - }); - } - /** - * Deletes a resource from an endpoint - * Be aware that not found returns a null. Other error conditions reject the promise - * @param {string} resource - fully qualified or relative url - * @param {IRequestOptions} requestOptions - (optional) requestOptions object - */ - del(resource, options) { - return __awaiter(this, void 0, void 0, function* () { - let url = util.getUrl(resource, this._baseUrl); - let res = yield this.client.del(url, this._headersFromOptions(options)); - return this._processResponse(res, options); - }); - } - /** - * Creates resource(s) from an endpoint - * T type of object returned. - * Be aware that not found returns a null. Other error conditions reject the promise - * @param {string} resource - fully qualified or relative url - * @param {IRequestOptions} requestOptions - (optional) requestOptions object - */ - create(resource, resources, options) { - return __awaiter(this, void 0, void 0, function* () { - let url = util.getUrl(resource, this._baseUrl); - let headers = this._headersFromOptions(options, true); - let data = JSON.stringify(resources, null, 2); - let res = yield this.client.post(url, data, headers); - return this._processResponse(res, options); - }); - } - /** - * Updates resource(s) from an endpoint - * T type of object returned. - * Be aware that not found returns a null. Other error conditions reject the promise - * @param {string} resource - fully qualified or relative url - * @param {IRequestOptions} requestOptions - (optional) requestOptions object - */ - update(resource, resources, options) { - return __awaiter(this, void 0, void 0, function* () { - let url = util.getUrl(resource, this._baseUrl); - let headers = this._headersFromOptions(options, true); - let data = JSON.stringify(resources, null, 2); - let res = yield this.client.patch(url, data, headers); - return this._processResponse(res, options); - }); - } - /** - * Replaces resource(s) from an endpoint - * T type of object returned. - * Be aware that not found returns a null. Other error conditions reject the promise - * @param {string} resource - fully qualified or relative url - * @param {IRequestOptions} requestOptions - (optional) requestOptions object - */ - replace(resource, resources, options) { - return __awaiter(this, void 0, void 0, function* () { - let url = util.getUrl(resource, this._baseUrl); - let headers = this._headersFromOptions(options, true); - let data = JSON.stringify(resources, null, 2); - let res = yield this.client.put(url, data, headers); - return this._processResponse(res, options); - }); - } - uploadStream(verb, requestUrl, stream, options) { - return __awaiter(this, void 0, void 0, function* () { - let url = util.getUrl(requestUrl, this._baseUrl); - let headers = this._headersFromOptions(options, true); - let res = yield this.client.sendStream(verb, url, stream, headers); - return this._processResponse(res, options); - }); - } - _headersFromOptions(options, contentType) { - options = options || {}; - let headers = options.additionalHeaders || {}; - headers["Accept"] = options.acceptHeader || "application/json"; - if (contentType) { - let found = false; - for (let header in headers) { - if (header.toLowerCase() == "content-type") { - found = true; +const semver = __importStar(__webpack_require__(280)); +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); +const cp = __webpack_require__(129); +const fs = __webpack_require__(747); +function _findMatch(versionSpec, stable, candidates, archFilter) { + return __awaiter(this, void 0, void 0, function* () { + const platFilter = os.platform(); + let result; + let match; + let file; + for (const candidate of candidates) { + const version = candidate.version; + core_1.debug(`check ${version} satisfies ${versionSpec}`); + if (semver.satisfies(version, versionSpec) && + (!stable || candidate.stable === stable)) { + file = candidate.files.find(item => { + core_1.debug(`${item.arch}===${archFilter} && ${item.platform}===${platFilter}`); + let chk = item.arch === archFilter && item.platform === platFilter; + if (chk && item.platform_version) { + const osVersion = module.exports._getOsVersion(); + if (osVersion === item.platform_version) { + chk = true; + } + else { + chk = semver.satisfies(osVersion, item.platform_version); + } + } + return chk; + }); + if (file) { + core_1.debug(`matched ${candidate.version}`); + match = candidate; + break; } } - if (!found) { - headers["Content-Type"] = 'application/json; charset=utf-8'; - } } - return headers; + if (match && file) { + // clone since we're mutating the file list to be only the file that matches + result = Object.assign({}, match); + result.files = [file]; + } + return result; + }); +} +exports._findMatch = _findMatch; +function _getOsVersion() { + // TODO: add windows and other linux, arm variants + // right now filtering on version is only an ubuntu and macos scenario for tools we build for hosted (python) + const plat = os.platform(); + let version = ''; + if (plat === 'darwin') { + version = cp.execSync('sw_vers -productVersion').toString(); } - static dateTimeDeserializer(key, value) { - if (typeof value === 'string') { - let a = new Date(value); - if (!isNaN(a.valueOf())) { - return a; + else if (plat === 'linux') { + // lsb_release process not in some containers, readfile + // Run cat /etc/lsb-release + // DISTRIB_ID=Ubuntu + // DISTRIB_RELEASE=18.04 + // DISTRIB_CODENAME=bionic + // DISTRIB_DESCRIPTION="Ubuntu 18.04.4 LTS" + const lsbContents = module.exports._readLinuxVersionFile(); + if (lsbContents) { + const lines = lsbContents.split('\n'); + for (const line of lines) { + const parts = line.split('='); + if (parts.length === 2 && parts[0].trim() === 'DISTRIB_RELEASE') { + version = parts[1].trim(); + break; + } } } - return value; } - _processResponse(res, options) { - return __awaiter(this, void 0, void 0, function* () { - return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () { - const statusCode = res.message.statusCode; - const response = { - statusCode: statusCode, - result: null, - headers: {} - }; - // not found leads to null obj returned - if (statusCode == httpm.HttpCodes.NotFound) { - resolve(response); - } - let obj; - let contents; - // get the result from the body - try { - contents = yield res.readBody(); - if (contents && contents.length > 0) { - if (options && options.deserializeDates) { - obj = JSON.parse(contents, RestClient.dateTimeDeserializer); - } - else { - obj = JSON.parse(contents); - } - if (options && options.responseProcessor) { - response.result = options.responseProcessor(obj); - } - else { - response.result = obj; - } - } - response.headers = res.message.headers; - } - catch (err) { - // Invalid resource (contents not json); leaving result obj null - } - // note that 3xx redirects are handled by the http layer. - if (statusCode > 299) { - let msg; - // if exception/error in body, attempt to get better error - if (obj && obj.message) { - msg = obj.message; - } - else if (contents && contents.length > 0) { - // it may be the case that the exception is in the body message as string - msg = contents; - } - else { - msg = "Failed request: (" + statusCode + ")"; - } - let err = new Error(msg); - // attach statusCode and body obj (if available) to the error object - err['statusCode'] = statusCode; - if (response.result) { - err['result'] = response.result; - } - reject(err); - } - else { - resolve(response); - } - })); - }); + return version; +} +exports._getOsVersion = _getOsVersion; +function _readLinuxVersionFile() { + const lsbFile = '/etc/lsb-release'; + let contents = ''; + if (fs.existsSync(lsbFile)) { + contents = fs.readFileSync(lsbFile).toString(); } + return contents; } -exports.RestClient = RestClient; +exports._readLinuxVersionFile = _readLinuxVersionFile; +//# sourceMappingURL=manifest.js.map + +/***/ }), + +/***/ 87: +/***/ (function(module) { +module.exports = require("os"); /***/ }), @@ -1219,6 +1133,7 @@ function httpsOverHttp(options) { var agent = new TunnelingAgent(options); agent.request = http.request; agent.createSocket = createSecureSocket; + agent.defaultPort = 443; return agent; } @@ -1232,6 +1147,7 @@ function httpsOverHttps(options) { var agent = new TunnelingAgent(options); agent.request = https.request; agent.createSocket = createSecureSocket; + agent.defaultPort = 443; return agent; } @@ -1300,8 +1216,14 @@ TunnelingAgent.prototype.createSocket = function createSocket(options, cb) { var connectOptions = mergeOptions({}, self.proxyOptions, { method: 'CONNECT', path: options.host + ':' + options.port, - agent: false + agent: false, + headers: { + host: options.host + ':' + options.port + } }); + if (options.localAddress) { + connectOptions.localAddress = options.localAddress; + } if (connectOptions.proxyAuth) { connectOptions.headers = connectOptions.headers || {}; connectOptions.headers['Proxy-Authorization'] = 'Basic ' + @@ -1333,20 +1255,29 @@ TunnelingAgent.prototype.createSocket = function createSocket(options, cb) { connectReq.removeAllListeners(); socket.removeAllListeners(); - if (res.statusCode === 200) { - assert.equal(head.length, 0); - debug('tunneling connection has established'); - self.sockets[self.sockets.indexOf(placeholder)] = socket; - cb(socket); - } else { + if (res.statusCode !== 200) { debug('tunneling socket could not be established, statusCode=%d', - res.statusCode); + res.statusCode); + socket.destroy(); var error = new Error('tunneling socket could not be established, ' + - 'statusCode=' + res.statusCode); + 'statusCode=' + res.statusCode); + error.code = 'ECONNRESET'; + options.request.emit('error', error); + self.removeSocket(placeholder); + return; + } + if (head.length > 0) { + debug('got illegal response body from proxy'); + socket.destroy(); + var error = new Error('got illegal response body from proxy'); error.code = 'ECONNRESET'; options.request.emit('error', error); self.removeSocket(placeholder); + return; } + debug('tunneling connection has established'); + self.sockets[self.sockets.indexOf(placeholder)] = socket; + return cb(socket); } function onError(cause) { @@ -3379,17 +3310,31 @@ 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; +}; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; Object.defineProperty(exports, "__esModule", { value: true }); -const core = __webpack_require__(470); -const io = __webpack_require__(1); -const fs = __webpack_require__(747); -const os = __webpack_require__(87); -const path = __webpack_require__(622); -const httpm = __webpack_require__(874); -const semver = __webpack_require__(280); -const uuidV4 = __webpack_require__(826); +const core = __importStar(__webpack_require__(902)); +const io = __importStar(__webpack_require__(1)); +const fs = __importStar(__webpack_require__(747)); +const mm = __importStar(__webpack_require__(31)); +const os = __importStar(__webpack_require__(87)); +const path = __importStar(__webpack_require__(622)); +const httpm = __importStar(__webpack_require__(539)); +const semver = __importStar(__webpack_require__(280)); +const stream = __importStar(__webpack_require__(794)); +const util = __importStar(__webpack_require__(669)); +const v4_1 = __importDefault(__webpack_require__(826)); const exec_1 = __webpack_require__(986); const assert_1 = __webpack_require__(357); +const retry_helper_1 = __webpack_require__(979); class HTTPError extends Error { constructor(httpStatusCode) { super(`Unexpected HTTP response: ${httpStatusCode}`); @@ -3399,86 +3344,90 @@ class HTTPError extends Error { } exports.HTTPError = HTTPError; const IS_WINDOWS = process.platform === 'win32'; +const IS_MAC = process.platform === 'darwin'; const userAgent = 'actions/tool-cache'; -// On load grab temp directory and cache directory and remove them from env (currently don't want to expose this) -let tempDirectory = process.env['RUNNER_TEMP'] || ''; -let cacheRoot = process.env['RUNNER_TOOL_CACHE'] || ''; -// If directories not found, place them in common temp locations -if (!tempDirectory || !cacheRoot) { - let baseLocation; - if (IS_WINDOWS) { - // On windows use the USERPROFILE env variable - baseLocation = process.env['USERPROFILE'] || 'C:\\'; - } - else { - if (process.platform === 'darwin') { - baseLocation = '/Users'; - } - else { - baseLocation = '/home'; - } - } - if (!tempDirectory) { - tempDirectory = path.join(baseLocation, 'actions', 'temp'); - } - if (!cacheRoot) { - cacheRoot = path.join(baseLocation, 'actions', 'cache'); - } -} /** * Download a tool from an url and stream it into a file * * @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) { +function downloadTool(url, dest, auth) { return __awaiter(this, void 0, void 0, function* () { - // Wrap in a promise so that we can resolve from within stream callbacks - return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () { - try { - const http = new httpm.HttpClient(userAgent, [], { - allowRetries: true, - maxRetries: 3 - }); - const destPath = path.join(tempDirectory, uuidV4()); - yield io.mkdirP(tempDirectory); - core.debug(`Downloading ${url}`); - core.debug(`Downloading ${destPath}`); - if (fs.existsSync(destPath)) { - throw new Error(`Destination file path ${destPath} already exists`); - } - const response = yield http.get(url); - if (response.message.statusCode !== 200) { - const err = new HTTPError(response.message.statusCode); - core.debug(`Failed to download from "${url}". Code(${response.message.statusCode}) Message(${response.message.statusMessage})`); - throw err; + dest = dest || path.join(_getTempDirectory(), v4_1.default()); + yield io.mkdirP(path.dirname(dest)); + core.debug(`Downloading ${url}`); + core.debug(`Destination ${dest}`); + const maxAttempts = 3; + const minSeconds = _getGlobal('TEST_DOWNLOAD_TOOL_RETRY_MIN_SECONDS', 10); + 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 || '', auth); + }), (err) => { + if (err instanceof HTTPError && err.httpStatusCode) { + // Don't retry anything less than 500, except 408 Request Timeout and 429 Too Many Requests + if (err.httpStatusCode < 500 && + err.httpStatusCode !== 408 && + err.httpStatusCode !== 429) { + return false; } - const file = fs.createWriteStream(destPath); - file.on('open', () => __awaiter(this, void 0, void 0, function* () { - try { - const stream = response.message.pipe(file); - stream.on('close', () => { - core.debug('download complete'); - resolve(destPath); - }); - } - catch (err) { - core.debug(`Failed to download from "${url}". Code(${response.message.statusCode}) Message(${response.message.statusMessage})`); - reject(err); - } - })); - file.on('error', err => { - file.end(); - reject(err); - }); - } - catch (err) { - reject(err); } - })); + // Otherwise retry + return true; + }); }); } exports.downloadTool = downloadTool; +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`); + } + // Get the response headers + const http = new httpm.HttpClient(userAgent, [], { + allowRetries: false + }); + let headers; + if (auth) { + core.debug('set auth'); + headers = { + authorization: auth + }; + } + const response = yield http.get(url, headers); + if (response.message.statusCode !== 200) { + const err = new HTTPError(response.message.statusCode); + core.debug(`Failed to download from "${url}". Code(${response.message.statusCode}) Message(${response.message.statusMessage})`); + throw err; + } + // Download the response body + const pipeline = util.promisify(stream.pipeline); + const responseMessageFactory = _getGlobal('TEST_DOWNLOAD_TOOL_RESPONSE_MESSAGE_FACTORY', () => response.message); + const readStream = responseMessageFactory(); + let succeeded = false; + try { + yield pipeline(readStream, fs.createWriteStream(dest)); + core.debug('download complete'); + succeeded = true; + return dest; + } + finally { + // Error, delete dest before retry + if (!succeeded) { + core.debug('download failed'); + try { + yield io.rmRF(dest); + } + catch (err) { + core.debug(`Failed to delete '${dest}'. ${err.message}`); + } + } + } + }); +} /** * Extract a .7z file * @@ -3498,14 +3447,15 @@ function extract7z(file, dest, _7zPath) { return __awaiter(this, void 0, void 0, function* () { assert_1.ok(IS_WINDOWS, 'extract7z() not supported on current OS'); assert_1.ok(file, 'parameter "file" is required'); - dest = dest || (yield _createExtractFolder(dest)); + dest = yield _createExtractFolder(dest); const originalCwd = process.cwd(); process.chdir(dest); if (_7zPath) { try { + const logLevel = core.isDebug() ? '-bb1' : '-bb0'; const args = [ 'x', - '-bb1', + logLevel, '-bd', '-sccUTF-8', file @@ -3553,11 +3503,11 @@ function extract7z(file, dest, _7zPath) { } exports.extract7z = extract7z; /** - * Extract a tar + * Extract a compressed tar archive * * @param file path to the tar * @param dest destination directory. Optional. - * @param flags flags for the tar. Optional. + * @param flags flags for the tar command to use for extraction. Defaults to 'xz' (extracting gzipped tars). Optional. * @returns path to the destination directory */ function extractTar(file, dest, flags = 'xz') { @@ -3565,13 +3515,81 @@ function extractTar(file, dest, flags = 'xz') { if (!file) { throw new Error("parameter 'file' is required"); } - dest = dest || (yield _createExtractFolder(dest)); - const tarPath = yield io.which('tar', true); - yield exec_1.exec(`"${tarPath}"`, [flags, '-C', dest, '-f', file]); + // Create dest + dest = yield _createExtractFolder(dest); + // Determine whether GNU tar + core.debug('Checking tar --version'); + let versionOutput = ''; + yield exec_1.exec('tar --version', [], { + ignoreReturnCode: true, + silent: true, + listeners: { + stdout: (data) => (versionOutput += data.toString()), + stderr: (data) => (versionOutput += data.toString()) + } + }); + core.debug(versionOutput.trim()); + const isGnuTar = versionOutput.toUpperCase().includes('GNU TAR'); + // Initialize args + 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) { + args.push('--force-local'); + destArg = dest.replace(/\\/g, '/'); + // Technically only the dest needs to have `/` but for aesthetic consistency + // convert slashes in the file arg too. + fileArg = file.replace(/\\/g, '/'); + } + if (isGnuTar) { + // Suppress warnings when using GNU tar to extract archives created by BSD tar + args.push('--warning=no-unknown-keyword'); + } + args.push('-C', destArg, '-f', fileArg); + yield exec_1.exec(`tar`, args); return dest; }); } exports.extractTar = extractTar; +/** + * Extract a xar compatible archive + * + * @param file path to the archive + * @param dest destination directory. Optional. + * @param flags flags for the xar. Optional. + * @returns path to the destination directory + */ +function extractXar(file, dest, flags = []) { + return __awaiter(this, void 0, void 0, function* () { + assert_1.ok(IS_MAC, 'extractXar() not supported on current OS'); + assert_1.ok(file, 'parameter "file" is required'); + dest = yield _createExtractFolder(dest); + let args; + if (flags instanceof Array) { + args = flags; + } + else { + args = [flags]; + } + args.push('-x', '-C', dest, '-f', file); + if (core.isDebug()) { + args.push('-v'); + } + const xarPath = yield io.which('xar', true); + yield exec_1.exec(`"${xarPath}"`, _unique(args)); + return dest; + }); +} +exports.extractXar = extractXar; /** * Extract a zip * @@ -3584,17 +3602,12 @@ function extractZip(file, dest) { if (!file) { throw new Error("parameter 'file' is required"); } - dest = dest || (yield _createExtractFolder(dest)); + dest = yield _createExtractFolder(dest); if (IS_WINDOWS) { yield extractZipWin(file, dest); } else { - if (process.platform === 'darwin') { - yield extractZipDarwin(file, dest); - } - else { - yield extractZipNix(file, dest); - } + yield extractZipNix(file, dest); } return dest; }); @@ -3607,7 +3620,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', @@ -3623,14 +3636,12 @@ function extractZipWin(file, dest) { } function extractZipNix(file, dest) { return __awaiter(this, void 0, void 0, function* () { - const unzipPath = __webpack_require__.ab + "unzip"; - yield exec_1.exec(`"${unzipPath}"`, [file], { cwd: dest }); - }); -} -function extractZipDarwin(file, dest) { - return __awaiter(this, void 0, void 0, function* () { - const unzipPath = __webpack_require__.ab + "unzip-darwin"; - 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 }); }); } /** @@ -3721,7 +3732,7 @@ function find(toolName, versionSpec, arch) { let toolPath = ''; if (versionSpec) { versionSpec = semver.clean(versionSpec) || ''; - const cachePath = path.join(cacheRoot, toolName, versionSpec, arch); + const cachePath = path.join(_getCacheDirectory(), toolName, versionSpec, arch); core.debug(`checking cache: ${cachePath}`); if (fs.existsSync(cachePath) && fs.existsSync(`${cachePath}.complete`)) { core.debug(`Found tool in cache ${toolName} ${versionSpec} ${arch}`); @@ -3743,7 +3754,7 @@ exports.find = find; function findAllVersions(toolName, arch) { const versions = []; arch = arch || os.arch(); - const toolPath = path.join(cacheRoot, toolName); + const toolPath = path.join(_getCacheDirectory(), toolName); if (fs.existsSync(toolPath)) { const children = fs.readdirSync(toolPath); for (const child of children) { @@ -3758,11 +3769,56 @@ function findAllVersions(toolName, arch) { return versions; } exports.findAllVersions = findAllVersions; +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 = {}; + if (auth) { + core.debug('set auth'); + headers.authorization = auth; + } + const response = yield http.getJson(treeUrl, headers); + if (!response.result) { + return releases; + } + let manifestUrl = ''; + for (const item of response.result.tree) { + if (item.path === 'versions-manifest.json') { + manifestUrl = item.url; + break; + } + } + headers['accept'] = 'application/vnd.github.VERSION.raw'; + let versionsRaw = yield (yield http.get(manifestUrl, headers)).readBody(); + if (versionsRaw) { + // shouldn't be needed but protects against invalid json saved with BOM + versionsRaw = versionsRaw.replace(/^\uFEFF/, ''); + try { + releases = JSON.parse(versionsRaw); + } + catch (_a) { + core.debug('Invalid json'); + } + } + return releases; + }); +} +exports.getManifestFromRepo = getManifestFromRepo; +function findFromManifest(versionSpec, stable, manifest, archFilter = os.arch()) { + return __awaiter(this, void 0, void 0, function* () { + // wrap the internal impl + const match = yield mm._findMatch(versionSpec, stable, manifest, archFilter); + return match; + }); +} +exports.findFromManifest = findFromManifest; function _createExtractFolder(dest) { return __awaiter(this, void 0, void 0, function* () { if (!dest) { // create a temp dir - dest = path.join(tempDirectory, uuidV4()); + dest = path.join(_getTempDirectory(), v4_1.default()); } yield io.mkdirP(dest); return dest; @@ -3770,7 +3826,7 @@ function _createExtractFolder(dest) { } function _createToolPath(tool, version, arch) { return __awaiter(this, void 0, void 0, function* () { - const folderPath = path.join(cacheRoot, tool, semver.clean(version) || version, arch || ''); + const folderPath = path.join(_getCacheDirectory(), tool, semver.clean(version) || version, arch || ''); core.debug(`destination ${folderPath}`); const markerPath = `${folderPath}.complete`; yield io.rmRF(folderPath); @@ -3780,7 +3836,7 @@ function _createToolPath(tool, version, arch) { }); } function _completeToolPath(tool, version, arch) { - const folderPath = path.join(cacheRoot, tool, semver.clean(version) || version, arch || ''); + const folderPath = path.join(_getCacheDirectory(), tool, semver.clean(version) || version, arch || ''); const markerPath = `${folderPath}.complete`; fs.writeFileSync(markerPath, ''); core.debug('finished caching tool'); @@ -3817,21 +3873,592 @@ function _evaluateVersions(versions, versionSpec) { } return version; } +/** + * Gets RUNNER_TOOL_CACHE + */ +function _getCacheDirectory() { + const cacheDirectory = process.env['RUNNER_TOOL_CACHE'] || ''; + assert_1.ok(cacheDirectory, 'Expected RUNNER_TOOL_CACHE to be defined'); + return cacheDirectory; +} +/** + * Gets RUNNER_TEMP + */ +function _getTempDirectory() { + const tempDirectory = process.env['RUNNER_TEMP'] || ''; + assert_1.ok(tempDirectory, 'Expected RUNNER_TEMP to be defined'); + return tempDirectory; +} +/** + * Gets a global variable + */ +function _getGlobal(key, defaultValue) { + /* eslint-disable @typescript-eslint/no-explicit-any */ + const value = global[key]; + /* eslint-enable @typescript-eslint/no-explicit-any */ + return value !== undefined ? value : defaultValue; +} +/** + * Returns an array of unique values. + * @param values Values to make unique. + */ +function _unique(values) { + return Array.from(new Set(values)); +} //# sourceMappingURL=tool-cache.js.map /***/ }), -/***/ 605: -/***/ (function(module) { - -module.exports = require("http"); - -/***/ }), +/***/ 539: +/***/ (function(__unusedmodule, exports, __webpack_require__) { -/***/ 614: -/***/ (function(module) { +"use strict"; -module.exports = require("events"); +Object.defineProperty(exports, "__esModule", { value: true }); +const url = __webpack_require__(835); +const http = __webpack_require__(605); +const https = __webpack_require__(211); +const pm = __webpack_require__(950); +let tunnel; +var HttpCodes; +(function (HttpCodes) { + HttpCodes[HttpCodes["OK"] = 200] = "OK"; + HttpCodes[HttpCodes["MultipleChoices"] = 300] = "MultipleChoices"; + HttpCodes[HttpCodes["MovedPermanently"] = 301] = "MovedPermanently"; + HttpCodes[HttpCodes["ResourceMoved"] = 302] = "ResourceMoved"; + HttpCodes[HttpCodes["SeeOther"] = 303] = "SeeOther"; + HttpCodes[HttpCodes["NotModified"] = 304] = "NotModified"; + HttpCodes[HttpCodes["UseProxy"] = 305] = "UseProxy"; + HttpCodes[HttpCodes["SwitchProxy"] = 306] = "SwitchProxy"; + HttpCodes[HttpCodes["TemporaryRedirect"] = 307] = "TemporaryRedirect"; + HttpCodes[HttpCodes["PermanentRedirect"] = 308] = "PermanentRedirect"; + HttpCodes[HttpCodes["BadRequest"] = 400] = "BadRequest"; + HttpCodes[HttpCodes["Unauthorized"] = 401] = "Unauthorized"; + HttpCodes[HttpCodes["PaymentRequired"] = 402] = "PaymentRequired"; + HttpCodes[HttpCodes["Forbidden"] = 403] = "Forbidden"; + HttpCodes[HttpCodes["NotFound"] = 404] = "NotFound"; + HttpCodes[HttpCodes["MethodNotAllowed"] = 405] = "MethodNotAllowed"; + HttpCodes[HttpCodes["NotAcceptable"] = 406] = "NotAcceptable"; + HttpCodes[HttpCodes["ProxyAuthenticationRequired"] = 407] = "ProxyAuthenticationRequired"; + HttpCodes[HttpCodes["RequestTimeout"] = 408] = "RequestTimeout"; + HttpCodes[HttpCodes["Conflict"] = 409] = "Conflict"; + HttpCodes[HttpCodes["Gone"] = 410] = "Gone"; + HttpCodes[HttpCodes["TooManyRequests"] = 429] = "TooManyRequests"; + HttpCodes[HttpCodes["InternalServerError"] = 500] = "InternalServerError"; + HttpCodes[HttpCodes["NotImplemented"] = 501] = "NotImplemented"; + HttpCodes[HttpCodes["BadGateway"] = 502] = "BadGateway"; + HttpCodes[HttpCodes["ServiceUnavailable"] = 503] = "ServiceUnavailable"; + HttpCodes[HttpCodes["GatewayTimeout"] = 504] = "GatewayTimeout"; +})(HttpCodes = exports.HttpCodes || (exports.HttpCodes = {})); +var Headers; +(function (Headers) { + Headers["Accept"] = "accept"; + Headers["ContentType"] = "content-type"; +})(Headers = exports.Headers || (exports.Headers = {})); +var MediaTypes; +(function (MediaTypes) { + MediaTypes["ApplicationJson"] = "application/json"; +})(MediaTypes = exports.MediaTypes || (exports.MediaTypes = {})); +/** + * Returns the proxy URL, depending upon the supplied url and proxy environment variables. + * @param serverUrl The server URL where the request will be sent. For example, https://api.github.com + */ +function getProxyUrl(serverUrl) { + let proxyUrl = pm.getProxyUrl(url.parse(serverUrl)); + return proxyUrl ? proxyUrl.href : ''; +} +exports.getProxyUrl = getProxyUrl; +const HttpRedirectCodes = [ + HttpCodes.MovedPermanently, + HttpCodes.ResourceMoved, + HttpCodes.SeeOther, + HttpCodes.TemporaryRedirect, + HttpCodes.PermanentRedirect +]; +const HttpResponseRetryCodes = [ + HttpCodes.BadGateway, + HttpCodes.ServiceUnavailable, + HttpCodes.GatewayTimeout +]; +const RetryableHttpVerbs = ['OPTIONS', 'GET', 'DELETE', 'HEAD']; +const ExponentialBackoffCeiling = 10; +const ExponentialBackoffTimeSlice = 5; +class HttpClientResponse { + constructor(message) { + this.message = message; + } + readBody() { + return new Promise(async (resolve, reject) => { + let output = Buffer.alloc(0); + this.message.on('data', (chunk) => { + output = Buffer.concat([output, chunk]); + }); + this.message.on('end', () => { + resolve(output.toString()); + }); + }); + } +} +exports.HttpClientResponse = HttpClientResponse; +function isHttps(requestUrl) { + let parsedUrl = url.parse(requestUrl); + return parsedUrl.protocol === 'https:'; +} +exports.isHttps = isHttps; +class HttpClient { + constructor(userAgent, handlers, requestOptions) { + this._ignoreSslError = false; + this._allowRedirects = true; + this._allowRedirectDowngrade = false; + this._maxRedirects = 50; + this._allowRetries = false; + this._maxRetries = 1; + this._keepAlive = false; + this._disposed = false; + this.userAgent = userAgent; + this.handlers = handlers || []; + this.requestOptions = requestOptions; + if (requestOptions) { + if (requestOptions.ignoreSslError != null) { + this._ignoreSslError = requestOptions.ignoreSslError; + } + this._socketTimeout = requestOptions.socketTimeout; + if (requestOptions.allowRedirects != null) { + this._allowRedirects = requestOptions.allowRedirects; + } + if (requestOptions.allowRedirectDowngrade != null) { + this._allowRedirectDowngrade = requestOptions.allowRedirectDowngrade; + } + if (requestOptions.maxRedirects != null) { + this._maxRedirects = Math.max(requestOptions.maxRedirects, 0); + } + if (requestOptions.keepAlive != null) { + this._keepAlive = requestOptions.keepAlive; + } + if (requestOptions.allowRetries != null) { + this._allowRetries = requestOptions.allowRetries; + } + if (requestOptions.maxRetries != null) { + this._maxRetries = requestOptions.maxRetries; + } + } + } + options(requestUrl, additionalHeaders) { + return this.request('OPTIONS', requestUrl, null, additionalHeaders || {}); + } + get(requestUrl, additionalHeaders) { + return this.request('GET', requestUrl, null, additionalHeaders || {}); + } + del(requestUrl, additionalHeaders) { + return this.request('DELETE', requestUrl, null, additionalHeaders || {}); + } + post(requestUrl, data, additionalHeaders) { + return this.request('POST', requestUrl, data, additionalHeaders || {}); + } + patch(requestUrl, data, additionalHeaders) { + return this.request('PATCH', requestUrl, data, additionalHeaders || {}); + } + put(requestUrl, data, additionalHeaders) { + return this.request('PUT', requestUrl, data, additionalHeaders || {}); + } + head(requestUrl, additionalHeaders) { + return this.request('HEAD', requestUrl, null, additionalHeaders || {}); + } + sendStream(verb, requestUrl, stream, additionalHeaders) { + return this.request(verb, requestUrl, stream, additionalHeaders); + } + /** + * Gets a typed object from an endpoint + * Be aware that not found returns a null. Other errors (4xx, 5xx) reject the promise + */ + async getJson(requestUrl, additionalHeaders = {}) { + additionalHeaders[Headers.Accept] = this._getExistingOrDefaultHeader(additionalHeaders, Headers.Accept, MediaTypes.ApplicationJson); + let res = await this.get(requestUrl, additionalHeaders); + return this._processResponse(res, this.requestOptions); + } + async postJson(requestUrl, obj, additionalHeaders = {}) { + let data = JSON.stringify(obj, null, 2); + additionalHeaders[Headers.Accept] = this._getExistingOrDefaultHeader(additionalHeaders, Headers.Accept, MediaTypes.ApplicationJson); + additionalHeaders[Headers.ContentType] = this._getExistingOrDefaultHeader(additionalHeaders, Headers.ContentType, MediaTypes.ApplicationJson); + let res = await this.post(requestUrl, data, additionalHeaders); + return this._processResponse(res, this.requestOptions); + } + async putJson(requestUrl, obj, additionalHeaders = {}) { + let data = JSON.stringify(obj, null, 2); + additionalHeaders[Headers.Accept] = this._getExistingOrDefaultHeader(additionalHeaders, Headers.Accept, MediaTypes.ApplicationJson); + additionalHeaders[Headers.ContentType] = this._getExistingOrDefaultHeader(additionalHeaders, Headers.ContentType, MediaTypes.ApplicationJson); + let res = await this.put(requestUrl, data, additionalHeaders); + return this._processResponse(res, this.requestOptions); + } + async patchJson(requestUrl, obj, additionalHeaders = {}) { + let data = JSON.stringify(obj, null, 2); + additionalHeaders[Headers.Accept] = this._getExistingOrDefaultHeader(additionalHeaders, Headers.Accept, MediaTypes.ApplicationJson); + additionalHeaders[Headers.ContentType] = this._getExistingOrDefaultHeader(additionalHeaders, Headers.ContentType, MediaTypes.ApplicationJson); + let res = await this.patch(requestUrl, data, additionalHeaders); + return this._processResponse(res, this.requestOptions); + } + /** + * Makes a raw http request. + * All other methods such as get, post, patch, and request ultimately call this. + * Prefer get, del, post and patch + */ + async request(verb, requestUrl, data, headers) { + if (this._disposed) { + throw new Error('Client has already been disposed.'); + } + let parsedUrl = url.parse(requestUrl); + let info = this._prepareRequest(verb, parsedUrl, headers); + // Only perform retries on reads since writes may not be idempotent. + let maxTries = this._allowRetries && RetryableHttpVerbs.indexOf(verb) != -1 + ? this._maxRetries + 1 + : 1; + let numTries = 0; + let response; + while (numTries < maxTries) { + response = await this.requestRaw(info, data); + // Check if it's an authentication challenge + if (response && + response.message && + response.message.statusCode === HttpCodes.Unauthorized) { + let authenticationHandler; + for (let i = 0; i < this.handlers.length; i++) { + if (this.handlers[i].canHandleAuthentication(response)) { + authenticationHandler = this.handlers[i]; + break; + } + } + if (authenticationHandler) { + return authenticationHandler.handleAuthentication(this, info, data); + } + else { + // We have received an unauthorized response but have no handlers to handle it. + // Let the response return to the caller. + return response; + } + } + let redirectsRemaining = this._maxRedirects; + while (HttpRedirectCodes.indexOf(response.message.statusCode) != -1 && + this._allowRedirects && + redirectsRemaining > 0) { + const redirectUrl = response.message.headers['location']; + if (!redirectUrl) { + // if there's no location to redirect to, we won't + break; + } + let parsedRedirectUrl = url.parse(redirectUrl); + if (parsedUrl.protocol == 'https:' && + parsedUrl.protocol != parsedRedirectUrl.protocol && + !this._allowRedirectDowngrade) { + throw new Error('Redirect from HTTPS to HTTP protocol. This downgrade is not allowed for security reasons. If you want to allow this behavior, set the allowRedirectDowngrade option to true.'); + } + // we need to finish reading the response before reassigning response + // which will leak the open socket. + await response.readBody(); + // strip authorization header if redirected to a different hostname + if (parsedRedirectUrl.hostname !== parsedUrl.hostname) { + for (let header in headers) { + // header names are case insensitive + if (header.toLowerCase() === 'authorization') { + delete headers[header]; + } + } + } + // let's make the request with the new redirectUrl + info = this._prepareRequest(verb, parsedRedirectUrl, headers); + response = await this.requestRaw(info, data); + redirectsRemaining--; + } + if (HttpResponseRetryCodes.indexOf(response.message.statusCode) == -1) { + // If not a retry code, return immediately instead of retrying + return response; + } + numTries += 1; + if (numTries < maxTries) { + await response.readBody(); + await this._performExponentialBackoff(numTries); + } + } + return response; + } + /** + * Needs to be called if keepAlive is set to true in request options. + */ + dispose() { + if (this._agent) { + this._agent.destroy(); + } + this._disposed = true; + } + /** + * Raw request. + * @param info + * @param data + */ + requestRaw(info, data) { + return new Promise((resolve, reject) => { + let callbackForResult = function (err, res) { + if (err) { + reject(err); + } + resolve(res); + }; + this.requestRawWithCallback(info, data, callbackForResult); + }); + } + /** + * Raw request with callback. + * @param info + * @param data + * @param onResult + */ + requestRawWithCallback(info, data, onResult) { + let socket; + if (typeof data === 'string') { + info.options.headers['Content-Length'] = Buffer.byteLength(data, 'utf8'); + } + let callbackCalled = false; + let handleResult = (err, res) => { + if (!callbackCalled) { + callbackCalled = true; + onResult(err, res); + } + }; + let req = info.httpModule.request(info.options, (msg) => { + let res = new HttpClientResponse(msg); + handleResult(null, res); + }); + req.on('socket', sock => { + socket = sock; + }); + // If we ever get disconnected, we want the socket to timeout eventually + req.setTimeout(this._socketTimeout || 3 * 60000, () => { + if (socket) { + socket.end(); + } + handleResult(new Error('Request timeout: ' + info.options.path), null); + }); + req.on('error', function (err) { + // err has statusCode property + // res should have headers + handleResult(err, null); + }); + if (data && typeof data === 'string') { + req.write(data, 'utf8'); + } + if (data && typeof data !== 'string') { + data.on('close', function () { + req.end(); + }); + data.pipe(req); + } + else { + req.end(); + } + } + /** + * Gets an http agent. This function is useful when you need an http agent that handles + * routing through a proxy server - depending upon the url and proxy environment variables. + * @param serverUrl The server URL where the request will be sent. For example, https://api.github.com + */ + getAgent(serverUrl) { + let parsedUrl = url.parse(serverUrl); + return this._getAgent(parsedUrl); + } + _prepareRequest(method, requestUrl, headers) { + const info = {}; + info.parsedUrl = requestUrl; + const usingSsl = info.parsedUrl.protocol === 'https:'; + info.httpModule = usingSsl ? https : http; + const defaultPort = usingSsl ? 443 : 80; + info.options = {}; + info.options.host = info.parsedUrl.hostname; + info.options.port = info.parsedUrl.port + ? parseInt(info.parsedUrl.port) + : defaultPort; + info.options.path = + (info.parsedUrl.pathname || '') + (info.parsedUrl.search || ''); + info.options.method = method; + info.options.headers = this._mergeHeaders(headers); + if (this.userAgent != null) { + info.options.headers['user-agent'] = this.userAgent; + } + info.options.agent = this._getAgent(info.parsedUrl); + // gives handlers an opportunity to participate + if (this.handlers) { + this.handlers.forEach(handler => { + handler.prepareRequest(info.options); + }); + } + return info; + } + _mergeHeaders(headers) { + const lowercaseKeys = obj => Object.keys(obj).reduce((c, k) => ((c[k.toLowerCase()] = obj[k]), c), {}); + if (this.requestOptions && this.requestOptions.headers) { + return Object.assign({}, lowercaseKeys(this.requestOptions.headers), lowercaseKeys(headers)); + } + return lowercaseKeys(headers || {}); + } + _getExistingOrDefaultHeader(additionalHeaders, header, _default) { + const lowercaseKeys = obj => Object.keys(obj).reduce((c, k) => ((c[k.toLowerCase()] = obj[k]), c), {}); + let clientHeader; + if (this.requestOptions && this.requestOptions.headers) { + clientHeader = lowercaseKeys(this.requestOptions.headers)[header]; + } + return additionalHeaders[header] || clientHeader || _default; + } + _getAgent(parsedUrl) { + let agent; + let proxyUrl = pm.getProxyUrl(parsedUrl); + let useProxy = proxyUrl && proxyUrl.hostname; + if (this._keepAlive && useProxy) { + agent = this._proxyAgent; + } + if (this._keepAlive && !useProxy) { + agent = this._agent; + } + // if agent is already assigned use that agent. + if (!!agent) { + return agent; + } + const usingSsl = parsedUrl.protocol === 'https:'; + let maxSockets = 100; + if (!!this.requestOptions) { + maxSockets = this.requestOptions.maxSockets || http.globalAgent.maxSockets; + } + if (useProxy) { + // If using proxy, need tunnel + if (!tunnel) { + tunnel = __webpack_require__(413); + } + const agentOptions = { + maxSockets: maxSockets, + keepAlive: this._keepAlive, + proxy: { + proxyAuth: proxyUrl.auth, + host: proxyUrl.hostname, + port: proxyUrl.port + } + }; + let tunnelAgent; + const overHttps = proxyUrl.protocol === 'https:'; + if (usingSsl) { + tunnelAgent = overHttps ? tunnel.httpsOverHttps : tunnel.httpsOverHttp; + } + else { + tunnelAgent = overHttps ? tunnel.httpOverHttps : tunnel.httpOverHttp; + } + agent = tunnelAgent(agentOptions); + this._proxyAgent = agent; + } + // if reusing agent across request and tunneling agent isn't assigned create a new agent + if (this._keepAlive && !agent) { + const options = { keepAlive: this._keepAlive, maxSockets: maxSockets }; + agent = usingSsl ? new https.Agent(options) : new http.Agent(options); + this._agent = agent; + } + // if not using private agent and tunnel agent isn't setup then use global agent + if (!agent) { + agent = usingSsl ? https.globalAgent : http.globalAgent; + } + if (usingSsl && this._ignoreSslError) { + // we don't want to set NODE_TLS_REJECT_UNAUTHORIZED=0 since that will affect request for entire process + // http.RequestOptions doesn't expose a way to modify RequestOptions.agent.options + // we have to cast it to any and change it directly + agent.options = Object.assign(agent.options || {}, { + rejectUnauthorized: false + }); + } + return agent; + } + _performExponentialBackoff(retryNumber) { + retryNumber = Math.min(ExponentialBackoffCeiling, retryNumber); + const ms = ExponentialBackoffTimeSlice * Math.pow(2, retryNumber); + return new Promise(resolve => setTimeout(() => resolve(), ms)); + } + static dateTimeDeserializer(key, value) { + if (typeof value === 'string') { + let a = new Date(value); + if (!isNaN(a.valueOf())) { + return a; + } + } + return value; + } + async _processResponse(res, options) { + return new Promise(async (resolve, reject) => { + const statusCode = res.message.statusCode; + const response = { + statusCode: statusCode, + result: null, + headers: {} + }; + // not found leads to null obj returned + if (statusCode == HttpCodes.NotFound) { + resolve(response); + } + let obj; + let contents; + // get the result from the body + try { + contents = await res.readBody(); + if (contents && contents.length > 0) { + if (options && options.deserializeDates) { + obj = JSON.parse(contents, HttpClient.dateTimeDeserializer); + } + else { + obj = JSON.parse(contents); + } + response.result = obj; + } + response.headers = res.message.headers; + } + catch (err) { + // Invalid resource (contents not json); leaving result obj null + } + // note that 3xx redirects are handled by the http layer. + if (statusCode > 299) { + let msg; + // if exception/error in body, attempt to get better error + if (obj && obj.message) { + msg = obj.message; + } + else if (contents && contents.length > 0) { + // it may be the case that the exception is in the body message as string + msg = contents; + } + else { + msg = 'Failed request: (' + statusCode + ')'; + } + let err = new Error(msg); + // attach statusCode and body obj (if available) to the error object + err['statusCode'] = statusCode; + if (response.result) { + err['result'] = response.result; + } + reject(err); + } + else { + resolve(response); + } + }); + } +} +exports.HttpClient = HttpClient; + + +/***/ }), + +/***/ 605: +/***/ (function(module) { + +module.exports = require("http"); + +/***/ }), + +/***/ 614: +/***/ (function(module) { + +module.exports = require("events"); /***/ }), @@ -4087,49 +4714,6 @@ function bytesToUuid(buf, offset) { module.exports = bytesToUuid; -/***/ }), - -/***/ 729: -/***/ (function(__unusedmodule, exports, __webpack_require__) { - -"use strict"; - -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. -Object.defineProperty(exports, "__esModule", { value: true }); -const url = __webpack_require__(835); -const path = __webpack_require__(622); -/** - * creates an url from a request url and optional base url (http://server:8080) - * @param {string} resource - a fully qualified url or relative path - * @param {string} baseUrl - an optional baseUrl (http://server:8080) - * @return {string} - resultant url - */ -function getUrl(resource, baseUrl) { - const pathApi = path.posix || path; - if (!baseUrl) { - return resource; - } - else if (!resource) { - return baseUrl; - } - else { - const base = url.parse(baseUrl); - const resultantUrl = url.parse(resource); - // resource (specific per request) elements take priority - resultantUrl.protocol = resultantUrl.protocol || base.protocol; - resultantUrl.auth = resultantUrl.auth || base.auth; - resultantUrl.host = resultantUrl.host || base.host; - resultantUrl.pathname = pathApi.resolve(base.pathname, resultantUrl.pathname); - if (!resultantUrl.pathname.endsWith('/') && resource.endsWith('/')) { - resultantUrl.pathname += '/'; - } - return url.format(resultantUrl); - } -} -exports.getUrl = getUrl; - - /***/ }), /***/ 747: @@ -4166,7 +4750,7 @@ let tempDirectory = process.env["RUNNER_TEMP"] || ""; const os = __importStar(__webpack_require__(87)); const path = __importStar(__webpack_require__(622)); const util = __importStar(__webpack_require__(669)); -const restm = __importStar(__webpack_require__(105)); +const httpm = __importStar(__webpack_require__(539)); const semver = __importStar(__webpack_require__(280)); if (!tempDirectory) { let baseLocation; @@ -4214,7 +4798,8 @@ function downloadRelease(version) { let downloadUrl = util.format("https://github.com/Arduino/arduino-cli/releases/download/%s/%s", version, fileName); let downloadPath = null; try { - downloadPath = yield tc.downloadTool(downloadUrl); + const token = core.getInput("token", { required: true }); + downloadPath = yield tc.downloadTool(downloadUrl, "", token); } catch (error) { core.debug(error); @@ -4255,11 +4840,11 @@ function getFileName(version) { // Retrieve a list of versions scraping tags from the Github API function fetchVersions() { return __awaiter(this, void 0, void 0, function* () { - let rest = new restm.RestClient("setup-arduino-cli"); - let tags = (yield rest.get("https://api.github.com/repos/Arduino/arduino-cli/git/refs/tags")).result || []; + let rest = new httpm.HttpClient("setup-arduino-cli"); + let tags = (yield rest.getJson("https://api.github.com/repos/Arduino/arduino-cli/git/refs/tags")).result || []; return tags - .filter(tag => tag.ref.match(/\d+\.[\w\.]+/g)) - .map(tag => tag.ref.replace("refs/tags/", "")); + .filter((tag) => tag.ref.match(/\d+\.[\w\.]+/g)) + .map((tag) => tag.ref.replace("refs/tags/", "")); }); } // Compute an actual version starting from the `version` configuration param. @@ -4270,12 +4855,12 @@ function computeVersion(version) { version = version.slice(0, version.length - 2); } const allVersions = yield fetchVersions(); - const possibleVersions = allVersions.filter(v => v.startsWith(version)); + const possibleVersions = allVersions.filter((v) => v.startsWith(version)); const versionMap = new Map(); - possibleVersions.forEach(v => versionMap.set(normalizeVersion(v), v)); + possibleVersions.forEach((v) => versionMap.set(normalizeVersion(v), v)); const versions = Array.from(versionMap.keys()) .sort(semver.rcompare) - .map(v => versionMap.get(v)); + .map((v) => versionMap.get(v)); core.debug(`evaluating ${versions.length} versions`); if (versions.length === 0) { throw new Error("unable to get latest version"); @@ -4296,7 +4881,7 @@ function normalizeVersion(version) { else { // handle beta and rc // e.g. 1.10beta1 -? 1.10.0-beta1, 1.10rc1 -> 1.10.0-rc1 - if (preStrings.some(el => versionPart[1].includes(el))) { + if (preStrings.some((el) => versionPart[1].includes(el))) { versionPart[1] = versionPart[1] .replace("beta", ".0-beta") .replace("rc", ".0-rc") @@ -4312,7 +4897,7 @@ function normalizeVersion(version) { else { // handle beta and rc // e.g. 1.8.5beta1 -> 1.8.5-beta1, 1.8.5rc1 -> 1.8.5-rc1 - if (preStrings.some(el => versionPart[2].includes(el))) { + if (preStrings.some((el) => versionPart[2].includes(el))) { versionPart[2] = versionPart[2] .replace("beta", "-beta") .replace("rc", "-rc") @@ -4324,6 +4909,13 @@ function normalizeVersion(version) { } +/***/ }), + +/***/ 794: +/***/ (function(module) { + +module.exports = require("stream"); + /***/ }), /***/ 826: @@ -4369,466 +4961,474 @@ module.exports = require("url"); /***/ }), -/***/ 874: +/***/ 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 + +/***/ }), + +/***/ 902: /***/ (function(__unusedmodule, exports, __webpack_require__) { "use strict"; -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. 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) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } + 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 url = __webpack_require__(835); -const http = __webpack_require__(605); -const https = __webpack_require__(211); -let fs; -let tunnel; -var HttpCodes; -(function (HttpCodes) { - HttpCodes[HttpCodes["OK"] = 200] = "OK"; - HttpCodes[HttpCodes["MultipleChoices"] = 300] = "MultipleChoices"; - HttpCodes[HttpCodes["MovedPermanently"] = 301] = "MovedPermanently"; - HttpCodes[HttpCodes["ResourceMoved"] = 302] = "ResourceMoved"; - HttpCodes[HttpCodes["SeeOther"] = 303] = "SeeOther"; - HttpCodes[HttpCodes["NotModified"] = 304] = "NotModified"; - HttpCodes[HttpCodes["UseProxy"] = 305] = "UseProxy"; - HttpCodes[HttpCodes["SwitchProxy"] = 306] = "SwitchProxy"; - HttpCodes[HttpCodes["TemporaryRedirect"] = 307] = "TemporaryRedirect"; - HttpCodes[HttpCodes["PermanentRedirect"] = 308] = "PermanentRedirect"; - HttpCodes[HttpCodes["BadRequest"] = 400] = "BadRequest"; - HttpCodes[HttpCodes["Unauthorized"] = 401] = "Unauthorized"; - HttpCodes[HttpCodes["PaymentRequired"] = 402] = "PaymentRequired"; - HttpCodes[HttpCodes["Forbidden"] = 403] = "Forbidden"; - HttpCodes[HttpCodes["NotFound"] = 404] = "NotFound"; - HttpCodes[HttpCodes["MethodNotAllowed"] = 405] = "MethodNotAllowed"; - HttpCodes[HttpCodes["NotAcceptable"] = 406] = "NotAcceptable"; - HttpCodes[HttpCodes["ProxyAuthenticationRequired"] = 407] = "ProxyAuthenticationRequired"; - HttpCodes[HttpCodes["RequestTimeout"] = 408] = "RequestTimeout"; - HttpCodes[HttpCodes["Conflict"] = 409] = "Conflict"; - HttpCodes[HttpCodes["Gone"] = 410] = "Gone"; - HttpCodes[HttpCodes["InternalServerError"] = 500] = "InternalServerError"; - HttpCodes[HttpCodes["NotImplemented"] = 501] = "NotImplemented"; - HttpCodes[HttpCodes["BadGateway"] = 502] = "BadGateway"; - HttpCodes[HttpCodes["ServiceUnavailable"] = 503] = "ServiceUnavailable"; - HttpCodes[HttpCodes["GatewayTimeout"] = 504] = "GatewayTimeout"; -})(HttpCodes = exports.HttpCodes || (exports.HttpCodes = {})); -const HttpRedirectCodes = [HttpCodes.MovedPermanently, HttpCodes.ResourceMoved, HttpCodes.SeeOther, HttpCodes.TemporaryRedirect, HttpCodes.PermanentRedirect]; -const HttpResponseRetryCodes = [HttpCodes.BadGateway, HttpCodes.ServiceUnavailable, HttpCodes.GatewayTimeout]; -const RetryableHttpVerbs = ['OPTIONS', 'GET', 'DELETE', 'HEAD']; -const ExponentialBackoffCeiling = 10; -const ExponentialBackoffTimeSlice = 5; -class HttpClientResponse { - constructor(message) { - this.message = message; - } - readBody() { - return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () { - let output = ''; - this.message.on('data', (chunk) => { - output += chunk; - }); - this.message.on('end', () => { - resolve(output); - }); - })); +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.HttpClientResponse = HttpClientResponse; -function isHttps(requestUrl) { - let parsedUrl = url.parse(requestUrl); - return parsedUrl.protocol === 'https:'; +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.isHttps = isHttps; -var EnvironmentVariables; -(function (EnvironmentVariables) { - EnvironmentVariables["HTTP_PROXY"] = "HTTP_PROXY"; - EnvironmentVariables["HTTPS_PROXY"] = "HTTPS_PROXY"; -})(EnvironmentVariables || (EnvironmentVariables = {})); -class HttpClient { - constructor(userAgent, handlers, requestOptions) { - this._ignoreSslError = false; - this._allowRedirects = true; - this._maxRedirects = 50; - this._allowRetries = false; - this._maxRetries = 1; - this._keepAlive = false; - this._disposed = false; - this.userAgent = userAgent; - this.handlers = handlers || []; - this.requestOptions = requestOptions; - if (requestOptions) { - if (requestOptions.ignoreSslError != null) { - this._ignoreSslError = requestOptions.ignoreSslError; - } - this._socketTimeout = requestOptions.socketTimeout; - this._httpProxy = requestOptions.proxy; - if (requestOptions.proxy && requestOptions.proxy.proxyBypassHosts) { - this._httpProxyBypassHosts = []; - requestOptions.proxy.proxyBypassHosts.forEach(bypass => { - this._httpProxyBypassHosts.push(new RegExp(bypass, 'i')); - }); - } - this._certConfig = requestOptions.cert; - if (this._certConfig) { - // If using cert, need fs - fs = __webpack_require__(747); - // cache the cert content into memory, so we don't have to read it from disk every time - if (this._certConfig.caFile && fs.existsSync(this._certConfig.caFile)) { - this._ca = fs.readFileSync(this._certConfig.caFile, 'utf8'); - } - if (this._certConfig.certFile && fs.existsSync(this._certConfig.certFile)) { - this._cert = fs.readFileSync(this._certConfig.certFile, 'utf8'); - } - if (this._certConfig.keyFile && fs.existsSync(this._certConfig.keyFile)) { - this._key = fs.readFileSync(this._certConfig.keyFile, 'utf8'); - } - } - if (requestOptions.allowRedirects != null) { - this._allowRedirects = requestOptions.allowRedirects; - } - if (requestOptions.maxRedirects != null) { - this._maxRedirects = Math.max(requestOptions.maxRedirects, 0); - } - if (requestOptions.keepAlive != null) { - this._keepAlive = requestOptions.keepAlive; - } - if (requestOptions.allowRetries != null) { - this._allowRetries = requestOptions.allowRetries; - } - if (requestOptions.maxRetries != null) { - this._maxRetries = requestOptions.maxRetries; - } +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 + +/***/ }), + +/***/ 950: +/***/ (function(__unusedmodule, exports, __webpack_require__) { + +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +const url = __webpack_require__(835); +function getProxyUrl(reqUrl) { + let usingSsl = reqUrl.protocol === 'https:'; + let proxyUrl; + if (checkBypass(reqUrl)) { + return proxyUrl; } - options(requestUrl, additionalHeaders) { - return this.request('OPTIONS', requestUrl, null, additionalHeaders || {}); + let proxyVar; + if (usingSsl) { + proxyVar = process.env['https_proxy'] || process.env['HTTPS_PROXY']; } - get(requestUrl, additionalHeaders) { - return this.request('GET', requestUrl, null, additionalHeaders || {}); + else { + proxyVar = process.env['http_proxy'] || process.env['HTTP_PROXY']; } - del(requestUrl, additionalHeaders) { - return this.request('DELETE', requestUrl, null, additionalHeaders || {}); + if (proxyVar) { + proxyUrl = url.parse(proxyVar); } - post(requestUrl, data, additionalHeaders) { - return this.request('POST', requestUrl, data, additionalHeaders || {}); + return proxyUrl; +} +exports.getProxyUrl = getProxyUrl; +function checkBypass(reqUrl) { + if (!reqUrl.hostname) { + return false; } - patch(requestUrl, data, additionalHeaders) { - return this.request('PATCH', requestUrl, data, additionalHeaders || {}); + let noProxy = process.env['no_proxy'] || process.env['NO_PROXY'] || ''; + if (!noProxy) { + return false; } - put(requestUrl, data, additionalHeaders) { - return this.request('PUT', requestUrl, data, additionalHeaders || {}); + // Determine the request port + let reqPort; + if (reqUrl.port) { + reqPort = Number(reqUrl.port); } - head(requestUrl, additionalHeaders) { - return this.request('HEAD', requestUrl, null, additionalHeaders || {}); + else if (reqUrl.protocol === 'http:') { + reqPort = 80; } - sendStream(verb, requestUrl, stream, additionalHeaders) { - return this.request(verb, requestUrl, stream, additionalHeaders); + else if (reqUrl.protocol === 'https:') { + reqPort = 443; } - /** - * Makes a raw http request. - * All other methods such as get, post, patch, and request ultimately call this. - * Prefer get, del, post and patch - */ - request(verb, requestUrl, data, headers) { - return __awaiter(this, void 0, void 0, function* () { - if (this._disposed) { - throw new Error("Client has already been disposed."); - } - let info = this._prepareRequest(verb, requestUrl, headers); - // Only perform retries on reads since writes may not be idempotent. - let maxTries = (this._allowRetries && RetryableHttpVerbs.indexOf(verb) != -1) ? this._maxRetries + 1 : 1; - let numTries = 0; - let response; - while (numTries < maxTries) { - response = yield this.requestRaw(info, data); - // Check if it's an authentication challenge - if (response && response.message && response.message.statusCode === HttpCodes.Unauthorized) { - let authenticationHandler; - for (let i = 0; i < this.handlers.length; i++) { - if (this.handlers[i].canHandleAuthentication(response)) { - authenticationHandler = this.handlers[i]; - break; - } - } - if (authenticationHandler) { - return authenticationHandler.handleAuthentication(this, info, data); - } - else { - // We have received an unauthorized response but have no handlers to handle it. - // Let the response return to the caller. - return response; - } - } - let redirectsRemaining = this._maxRedirects; - while (HttpRedirectCodes.indexOf(response.message.statusCode) != -1 - && this._allowRedirects - && redirectsRemaining > 0) { - const redirectUrl = response.message.headers["location"]; - if (!redirectUrl) { - // if there's no location to redirect to, we won't - break; - } - // we need to finish reading the response before reassigning response - // which will leak the open socket. - yield response.readBody(); - // let's make the request with the new redirectUrl - info = this._prepareRequest(verb, redirectUrl, headers); - response = yield this.requestRaw(info, data); - redirectsRemaining--; - } - if (HttpResponseRetryCodes.indexOf(response.message.statusCode) == -1) { - // If not a retry code, return immediately instead of retrying - return response; - } - numTries += 1; - if (numTries < maxTries) { - yield response.readBody(); - yield this._performExponentialBackoff(numTries); - } - } - return response; - }); + // Format the request hostname and hostname with port + let upperReqHosts = [reqUrl.hostname.toUpperCase()]; + if (typeof reqPort === 'number') { + upperReqHosts.push(`${upperReqHosts[0]}:${reqPort}`); } - /** - * Needs to be called if keepAlive is set to true in request options. - */ - dispose() { - if (this._agent) { - this._agent.destroy(); + // Compare request host against noproxy + for (let upperNoProxyItem of noProxy + .split(',') + .map(x => x.trim().toUpperCase()) + .filter(x => x)) { + if (upperReqHosts.some(x => x === upperNoProxyItem)) { + return true; } - this._disposed = true; - } - /** - * Raw request. - * @param info - * @param data - */ - requestRaw(info, data) { - return new Promise((resolve, reject) => { - let callbackForResult = function (err, res) { - if (err) { - reject(err); - } - resolve(res); - }; - this.requestRawWithCallback(info, data, callbackForResult); - }); } - /** - * Raw request with callback. - * @param info - * @param data - * @param onResult - */ - requestRawWithCallback(info, data, onResult) { - let socket; - let isDataString = typeof (data) === 'string'; - if (typeof (data) === 'string') { - info.options.headers["Content-Length"] = Buffer.byteLength(data, 'utf8'); - } - let callbackCalled = false; - let handleResult = (err, res) => { - if (!callbackCalled) { - callbackCalled = true; - onResult(err, res); - } - }; - let req = info.httpModule.request(info.options, (msg) => { - let res = new HttpClientResponse(msg); - handleResult(null, res); - }); - req.on('socket', (sock) => { - socket = sock; - }); - // If we ever get disconnected, we want the socket to timeout eventually - req.setTimeout(this._socketTimeout || 3 * 60000, () => { - if (socket) { - socket.end(); - } - handleResult(new Error('Request timeout: ' + info.options.path), null); - }); - req.on('error', function (err) { - // err has statusCode property - // res should have headers - handleResult(err, null); - }); - if (data && typeof (data) === 'string') { - req.write(data, 'utf8'); - } - if (data && typeof (data) !== 'string') { - data.on('close', function () { - req.end(); - }); - data.pipe(req); - } - else { - req.end(); + return false; +} +exports.checkBypass = checkBypass; + + +/***/ }), + +/***/ 979: +/***/ (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 core = __importStar(__webpack_require__(902)); +/** + * Internal class for retries + */ +class RetryHelper { + constructor(maxAttempts, minSeconds, maxSeconds) { + if (maxAttempts < 1) { + throw new Error('max attempts should be greater than or equal to 1'); } - } - _prepareRequest(method, requestUrl, headers) { - const info = {}; - info.parsedUrl = url.parse(requestUrl); - const usingSsl = info.parsedUrl.protocol === 'https:'; - info.httpModule = usingSsl ? https : http; - const defaultPort = usingSsl ? 443 : 80; - info.options = {}; - info.options.host = info.parsedUrl.hostname; - info.options.port = info.parsedUrl.port ? parseInt(info.parsedUrl.port) : defaultPort; - info.options.path = (info.parsedUrl.pathname || '') + (info.parsedUrl.search || ''); - info.options.method = method; - info.options.headers = this._mergeHeaders(headers); - info.options.headers["user-agent"] = this.userAgent; - info.options.agent = this._getAgent(requestUrl); - // gives handlers an opportunity to participate - if (this.handlers && !this._isPresigned(requestUrl)) { - this.handlers.forEach((handler) => { - handler.prepareRequest(info.options); - }); + this.maxAttempts = maxAttempts; + this.minSeconds = Math.floor(minSeconds); + this.maxSeconds = Math.floor(maxSeconds); + if (this.minSeconds > this.maxSeconds) { + throw new Error('min seconds should be less than or equal to max seconds'); } - return info; } - _isPresigned(requestUrl) { - if (this.requestOptions && this.requestOptions.presignedUrlPatterns) { - const patterns = this.requestOptions.presignedUrlPatterns; - for (let i = 0; i < patterns.length; i++) { - if (requestUrl.match(patterns[i])) { - return true; + execute(action, isRetryable) { + return __awaiter(this, void 0, void 0, function* () { + let attempt = 1; + while (attempt < this.maxAttempts) { + // Try + try { + return yield action(); } + catch (err) { + if (isRetryable && !isRetryable(err)) { + throw err; + } + core.info(err.message); + } + // Sleep + const seconds = this.getSleepAmount(); + core.info(`Waiting ${seconds} seconds before trying again`); + yield this.sleep(seconds); + attempt++; } - } - return false; - } - _mergeHeaders(headers) { - const lowercaseKeys = obj => Object.keys(obj).reduce((c, k) => (c[k.toLowerCase()] = obj[k], c), {}); - if (this.requestOptions && this.requestOptions.headers) { - return Object.assign({}, lowercaseKeys(this.requestOptions.headers), lowercaseKeys(headers)); - } - return lowercaseKeys(headers || {}); - } - _getAgent(requestUrl) { - let agent; - let proxy = this._getProxy(requestUrl); - let useProxy = proxy.proxyUrl && proxy.proxyUrl.hostname && !this._isBypassProxy(requestUrl); - if (this._keepAlive && useProxy) { - agent = this._proxyAgent; - } - if (this._keepAlive && !useProxy) { - agent = this._agent; - } - // if agent is already assigned use that agent. - if (!!agent) { - return agent; - } - let parsedUrl = url.parse(requestUrl); - const usingSsl = parsedUrl.protocol === 'https:'; - let maxSockets = 100; - if (!!this.requestOptions) { - maxSockets = this.requestOptions.maxSockets || http.globalAgent.maxSockets; - } - if (useProxy) { - // If using proxy, need tunnel - if (!tunnel) { - tunnel = __webpack_require__(413); - } - const agentOptions = { - maxSockets: maxSockets, - keepAlive: this._keepAlive, - proxy: { - proxyAuth: proxy.proxyAuth, - host: proxy.proxyUrl.hostname, - port: proxy.proxyUrl.port - }, - }; - let tunnelAgent; - const overHttps = proxy.proxyUrl.protocol === 'https:'; - if (usingSsl) { - tunnelAgent = overHttps ? tunnel.httpsOverHttps : tunnel.httpsOverHttp; - } - else { - tunnelAgent = overHttps ? tunnel.httpOverHttps : tunnel.httpOverHttp; - } - agent = tunnelAgent(agentOptions); - this._proxyAgent = agent; - } - // if reusing agent across request and tunneling agent isn't assigned create a new agent - if (this._keepAlive && !agent) { - const options = { keepAlive: this._keepAlive, maxSockets: maxSockets }; - agent = usingSsl ? new https.Agent(options) : new http.Agent(options); - this._agent = agent; - } - // if not using private agent and tunnel agent isn't setup then use global agent - if (!agent) { - agent = usingSsl ? https.globalAgent : http.globalAgent; - } - if (usingSsl && this._ignoreSslError) { - // we don't want to set NODE_TLS_REJECT_UNAUTHORIZED=0 since that will affect request for entire process - // http.RequestOptions doesn't expose a way to modify RequestOptions.agent.options - // we have to cast it to any and change it directly - agent.options = Object.assign(agent.options || {}, { rejectUnauthorized: false }); - } - if (usingSsl && this._certConfig) { - agent.options = Object.assign(agent.options || {}, { ca: this._ca, cert: this._cert, key: this._key, passphrase: this._certConfig.passphrase }); - } - return agent; + // Last attempt + return yield action(); + }); } - _getProxy(requestUrl) { - const parsedUrl = url.parse(requestUrl); - let usingSsl = parsedUrl.protocol === 'https:'; - let proxyConfig = this._httpProxy; - // fallback to http_proxy and https_proxy env - let https_proxy = process.env[EnvironmentVariables.HTTPS_PROXY]; - let http_proxy = process.env[EnvironmentVariables.HTTP_PROXY]; - if (!proxyConfig) { - if (https_proxy && usingSsl) { - proxyConfig = { - proxyUrl: https_proxy - }; - } - else if (http_proxy) { - proxyConfig = { - proxyUrl: http_proxy - }; - } - } - let proxyUrl; - let proxyAuth; - if (proxyConfig) { - if (proxyConfig.proxyUrl.length > 0) { - proxyUrl = url.parse(proxyConfig.proxyUrl); - } - if (proxyConfig.proxyUsername || proxyConfig.proxyPassword) { - proxyAuth = proxyConfig.proxyUsername + ":" + proxyConfig.proxyPassword; - } - } - return { proxyUrl: proxyUrl, proxyAuth: proxyAuth }; + getSleepAmount() { + return (Math.floor(Math.random() * (this.maxSeconds - this.minSeconds + 1)) + + this.minSeconds); } - _isBypassProxy(requestUrl) { - if (!this._httpProxyBypassHosts) { - return false; - } - let bypass = false; - this._httpProxyBypassHosts.forEach(bypassHost => { - if (bypassHost.test(requestUrl)) { - bypass = true; - } + sleep(seconds) { + return __awaiter(this, void 0, void 0, function* () { + return new Promise(resolve => setTimeout(resolve, seconds * 1000)); }); - return bypass; - } - _performExponentialBackoff(retryNumber) { - retryNumber = Math.min(ExponentialBackoffCeiling, retryNumber); - const ms = ExponentialBackoffTimeSlice * Math.pow(2, retryNumber); - return new Promise(resolve => setTimeout(() => resolve(), ms)); } } -exports.HttpClient = HttpClient; - +exports.RetryHelper = RetryHelper; +//# sourceMappingURL=retry-helper.js.map /***/ }), @@ -4846,8 +5446,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. diff --git a/package-lock.json b/package-lock.json index 18bbd06..a1b32af 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5,31 +5,49 @@ "requires": true, "dependencies": { "@actions/core": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@actions/core/-/core-1.1.0.tgz", - "integrity": "sha512-KKpo3xzo0Zsikni9tbOsEQkxZBGDsYSJZNkTvmo0gPSXrc98TBOcdTvKwwjitjkjHkreTggWdB1ACiAFVgsuzA==" + "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/exec": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@actions/exec/-/exec-1.0.1.tgz", - "integrity": "sha512-nvFkxwiicvpzNiCBF4wFBDfnBvi7xp/as7LE1hBxBxKG2L29+gkIPBiLKMVORL+Hg3JNf07AKRfl0V5djoypjQ==" + "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" + } + }, + "@actions/http-client": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-1.0.8.tgz", + "integrity": "sha512-G4JjJ6f9Hb3Zvejj+ewLLKLf99ZC+9v+yCxoYf9vSyH+WkzPLB2LuUtRMGNkooMqdugGBFStIKXOuvH1W+EctA==", + "requires": { + "tunnel": "0.0.6" + } }, "@actions/io": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@actions/io/-/io-1.0.1.tgz", - "integrity": "sha512-rhq+tfZukbtaus7xyUtwKfuiCRXd1hWSfmJNEpFgBQJ4woqPEpsBw04awicjwz9tyG2/MVhAEMfVn664Cri5zA==" + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@actions/io/-/io-1.0.2.tgz", + "integrity": "sha512-J8KuFqVPr3p6U8W93DOXlXW6zFvrQAJANdS+vw0YhusLIq+bszW8zmK2Fh1C2kDPX8FMvwIl1OUcFgvJoXLbAg==" }, "@actions/tool-cache": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@actions/tool-cache/-/tool-cache-1.1.1.tgz", - "integrity": "sha512-AILekrrj/L4N/5z5TGtUKVie4nKjxDioCgOEymyYxzPhGfjIxfE71tN2VTTpiICEWJ883rPRj2+WinTr1b6yVA==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@actions/tool-cache/-/tool-cache-1.6.0.tgz", + "integrity": "sha512-+fyEBImPD3m5I0o6DflCO0NHY180LPoX8Lo6y4Iez+V17kO8kfkH0VHxb8mUdmD6hn9dWA9Ch1JA20fXoIYUeQ==", "requires": { - "@actions/core": "^1.1.0", - "@actions/exec": "^1.0.1", + "@actions/core": "^1.2.3", + "@actions/exec": "^1.0.0", + "@actions/http-client": "^1.0.8", "@actions/io": "^1.0.1", "semver": "^6.1.0", - "typed-rest-client": "^1.4.0", "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==" + } } }, "@babel/code-frame": { @@ -10765,9 +10783,9 @@ } }, "tunnel": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.4.tgz", - "integrity": "sha1-LTeFoVjBdMmhbcLARuxfxfF0IhM=" + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", + "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==" }, "tunnel-agent": { "version": "0.6.0", @@ -10805,15 +10823,6 @@ "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", "dev": true }, - "typed-rest-client": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/typed-rest-client/-/typed-rest-client-1.5.0.tgz", - "integrity": "sha512-DVZRlmsfnTjp6ZJaatcdyvvwYwbWvR4YDNFDqb+qdTxpvaVP99YCpBkA8rxsLtAPjBVoDe4fNsnMIdZTiPuKWg==", - "requires": { - "tunnel": "0.0.4", - "underscore": "1.8.3" - } - }, "typedarray-to-buffer": { "version": "3.1.5", "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", @@ -10829,11 +10838,6 @@ "integrity": "sha512-MYlEfn5VrLNsgudQTVJeNaQFUAI7DkhnOjdpAp4T+ku1TfQClewlbSuTVHiA+8skNBgaf02TL/kLOvig4y3G8w==", "dev": true }, - "underscore": { - "version": "1.8.3", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.8.3.tgz", - "integrity": "sha1-Tz+1OxBuYJf8+ctBCfKl6b36UCI=" - }, "union-value": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", diff --git a/package.json b/package.json index 5e8b4c7..b1ffd58 100644 --- a/package.json +++ b/package.json @@ -24,12 +24,13 @@ "author": "Arduino", "license": "MIT", "dependencies": { - "@actions/core": "^1.0.0", - "@actions/tool-cache": "^1.1.0", + "@actions/core": "^1.2.4", + "@actions/http-client": "^1.0.8", + "@actions/tool-cache": "^1.6.0", "semver": "^6.1.1" }, "devDependencies": { - "@actions/io": "^1.0.0", + "@actions/io": "^1.0.2", "@types/jest": "^24.0.13", "@types/node": "^12.0.4", "@types/semver": "^6.0.0", diff --git a/src/installer.ts b/src/installer.ts index 0533161..6c3428d 100644 --- a/src/installer.ts +++ b/src/installer.ts @@ -4,7 +4,8 @@ let tempDirectory = process.env["RUNNER_TEMP"] || ""; import * as os from "os"; import * as path from "path"; import * as util from "util"; -import * as restm from "typed-rest-client/RestClient"; +import * as httpm from "@actions/http-client"; +import * as auth from "@actions/http-client/auth"; import * as semver from "semver"; if (!tempDirectory) { @@ -24,7 +25,6 @@ if (!tempDirectory) { import * as core from "@actions/core"; import * as tc from "@actions/tool-cache"; -import io = require("@actions/io"); let osPlat: string = os.platform(); let osArch: string = os.arch(); @@ -63,7 +63,8 @@ async function downloadRelease(version: string): Promise { ); let downloadPath: string | null = null; try { - downloadPath = await tc.downloadTool(downloadUrl); + const token: string = core.getInput("token", { required: true }); + downloadPath = await tc.downloadTool(downloadUrl, undefined, token); } catch (error) { core.debug(error); throw `Failed to download version ${version}: ${error}`; @@ -105,10 +106,14 @@ function getFileName(version: string): string { // Retrieve a list of versions scraping tags from the Github API async function fetchVersions(): Promise { - let rest: restm.RestClient = new restm.RestClient("setup-arduino-cli"); + const token: string = core.getInput("token", { required: true }); + const authHandler = new auth.PersonalAccessTokenCredentialHandler(token); + let rest: httpm.HttpClient = new httpm.HttpClient("setup-arduino-cli", [ + authHandler + ]); let tags: ITaskRef[] = ( - await rest.get( + await rest.getJson( "https://api.github.com/repos/Arduino/arduino-cli/git/refs/tags" ) ).result || [];