diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml index 945b054..5f4a24e 100644 --- a/.github/workflows/workflow.yml +++ b/.github/workflows/workflow.yml @@ -40,6 +40,20 @@ jobs: - name: Verify node and npm run: __tests__/verify-node.sh 10 + test-fallback: + runs-on: windows-latest + steps: + - uses: actions/checkout@v2 + - name: Clear tool cache + run: mv "${{ runner.tool_cache }}" "${{ runner.tool_cache }}.old" + - name: Setup node 0.12.18 # For non LTS versions of Node, the zip is not always available + uses: ./ # and falls back to downloading node.exe and node.lib + with: + node-version: 0.12.18 + - name: Verify node + shell: bash + run: __tests__/verify-node.sh 0.12.18 SKIP_NPM + test-proxy: runs-on: ubuntu-latest container: diff --git a/__tests__/authutil.test.ts b/__tests__/authutil.test.ts index c75d5b6..9f46b51 100644 --- a/__tests__/authutil.test.ts +++ b/__tests__/authutil.test.ts @@ -1,28 +1,27 @@ -import io = require('@actions/io'); -import fs = require('fs'); -import path = require('path'); - -const tempDir = path.join( - __dirname, - 'runner', - path.join( - Math.random() - .toString(36) - .substring(7) - ), - 'temp' -); - -const rcFile = path.join(tempDir, '.npmrc'); - -process.env['GITHUB_REPOSITORY'] = 'OwnerName/repo'; -process.env['RUNNER_TEMP'] = tempDir; +import * as io from '@actions/io'; +import * as fs from 'fs'; +import * as path from 'path'; import * as auth from '../src/authutil'; +let rcFile: string; + describe('installer tests', () => { beforeAll(async () => { + const tempDir = path.join( + __dirname, + 'runner', + path.join( + Math.random() + .toString(36) + .substring(7) + ), + 'temp' + ); await io.rmRF(tempDir); await io.mkdirP(tempDir); + process.env['GITHUB_REPOSITORY'] = 'OwnerName/repo'; + process.env['RUNNER_TEMP'] = tempDir; + rcFile = path.join(tempDir, '.npmrc'); }, 100000); beforeEach(() => { diff --git a/__tests__/installer.test.ts b/__tests__/installer.test.ts index e0ada32..7f76aeb 100644 --- a/__tests__/installer.test.ts +++ b/__tests__/installer.test.ts @@ -1,39 +1,39 @@ -import io = require('@actions/io'); -import fs = require('fs'); -import os = require('os'); -import path = require('path'); - -const toolDir = path.join( - __dirname, - 'runner', - path.join( - Math.random() - .toString(36) - .substring(7) - ), - 'tools' -); -const tempDir = path.join( - __dirname, - 'runner', - path.join( - Math.random() - .toString(36) - .substring(7) - ), - 'temp' -); - -process.env['RUNNER_TOOL_CACHE'] = toolDir; -process.env['RUNNER_TEMP'] = tempDir; +import * as io from '@actions/io'; +import * as tc from '@actions/tool-cache'; +import * as fs from 'fs'; +import * as os from 'os'; +import * as path from 'path'; import * as installer from '../src/installer'; -const IS_WINDOWS = process.platform === 'win32'; +const isWindows = process.platform === 'win32'; +let toolDir: string; describe('installer tests', () => { beforeAll(async () => { + toolDir = path.join( + __dirname, + 'runner', + path.join( + Math.random() + .toString(36) + .substring(7) + ), + 'tools' + ); + const tempDir = path.join( + __dirname, + 'runner', + path.join( + Math.random() + .toString(36) + .substring(7) + ), + 'temp' + ); await io.rmRF(toolDir); await io.rmRF(tempDir); + process.env['RUNNER_TOOL_CACHE'] = toolDir; + process.env['RUNNER_TEMP'] = tempDir; }, 100000); it('Acquires version of node if no matching version is installed', async () => { @@ -41,14 +41,14 @@ describe('installer tests', () => { const nodeDir = path.join(toolDir, 'node', '10.16.0', os.arch()); expect(fs.existsSync(`${nodeDir}.complete`)).toBe(true); - if (IS_WINDOWS) { + if (isWindows) { expect(fs.existsSync(path.join(nodeDir, 'node.exe'))).toBe(true); } else { expect(fs.existsSync(path.join(nodeDir, 'bin', 'node'))).toBe(true); } }, 100000); - if (IS_WINDOWS) { + if (isWindows) { it('Falls back to backup location if first one doesnt contain correct version', async () => { await installer.getNode('5.10.1'); const nodeDir = path.join(toolDir, 'node', '5.10.1', os.arch()); @@ -81,7 +81,7 @@ describe('installer tests', () => { const nodeDir = path.join(toolDir, 'node', '8.8.1', os.arch()); expect(fs.existsSync(`${nodeDir}.complete`)).toBe(true); - if (IS_WINDOWS) { + if (isWindows) { expect(fs.existsSync(path.join(nodeDir, 'node.exe'))).toBe(true); } else { expect(fs.existsSync(path.join(nodeDir, 'bin', 'node'))).toBe(true); diff --git a/__tests__/verify-node.sh b/__tests__/verify-node.sh index 738eb46..797aa78 100755 --- a/__tests__/verify-node.sh +++ b/__tests__/verify-node.sh @@ -7,13 +7,17 @@ fi node_version="$(node --version)" echo "Found node version '$node_version'" -if [ -z "$(echo $node_version | grep v$1)" ]; then +if [ -z "$(echo $node_version | grep --fixed-strings v$1)" ]; then echo "Unexpected version" exit 1 fi -echo "Testing npm install" -mkdir -p test-npm-install -cd test-npm-install -npm init -y || exit 1 -npm install @actions/core || exit 1 \ No newline at end of file +if [ -z "$2" ]; then + echo "Testing npm install" + mkdir -p test-npm-install + cd test-npm-install + npm init -y || exit 1 + npm install @actions/core || exit 1 +else + echo "Skip testing npm" +fi diff --git a/dist/index.js b/dist/index.js index 65782bd..214ec57 100644 --- a/dist/index.js +++ b/dist/index.js @@ -10096,9 +10096,12 @@ 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}`); @@ -10109,31 +10112,6 @@ class HTTPError extends Error { exports.HTTPError = HTTPError; const IS_WINDOWS = process.platform === 'win32'; 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 * @@ -10143,52 +10121,71 @@ if (!tempDirectory || !cacheRoot) { */ function downloadTool(url, dest) { 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 - }); - dest = dest || path.join(tempDirectory, v4_1.default()); - yield io.mkdirP(path.dirname(dest)); - core.debug(`Downloading ${url}`); - core.debug(`Downloading ${dest}`); - if (fs.existsSync(dest)) { - throw new Error(`Destination file path ${dest} already exists`); + 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 || ''); + }), (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 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; - } - const file = fs.createWriteStream(dest); - 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(dest); - }); - } - 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) { + 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 + }); + 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; + } + // 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 * @@ -10278,14 +10275,17 @@ function extractTar(file, dest, flags = 'xz') { // 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 const args = [flags]; @@ -10446,7 +10446,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}`); @@ -10468,7 +10468,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) { @@ -10487,7 +10487,7 @@ function _createExtractFolder(dest) { return __awaiter(this, void 0, void 0, function* () { if (!dest) { // create a temp dir - dest = path.join(tempDirectory, v4_1.default()); + dest = path.join(_getTempDirectory(), v4_1.default()); } yield io.mkdirP(dest); return dest; @@ -10495,7 +10495,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); @@ -10505,7 +10505,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'); @@ -10542,6 +10542,31 @@ 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; +} //# sourceMappingURL=tool-cache.js.map /***/ }), @@ -12091,8 +12116,7 @@ var __importStar = (this && this.__importStar) || function (mod) { return result; }; Object.defineProperty(exports, "__esModule", { value: true }); -// Load tempDirectory before it gets wiped by tool-cache -let tempDirectory = process.env['RUNNER_TEMPDIRECTORY'] || ''; +const assert = __importStar(__webpack_require__(357)); const core = __importStar(__webpack_require__(470)); const hc = __importStar(__webpack_require__(539)); const io = __importStar(__webpack_require__(1)); @@ -12102,22 +12126,6 @@ const path = __importStar(__webpack_require__(622)); const semver = __importStar(__webpack_require__(280)); let osPlat = os.platform(); let osArch = translateArchToDistUrl(os.arch()); -if (!tempDirectory) { - let baseLocation; - if (process.platform === 'win32') { - // On windows use the USERPROFILE env variable - baseLocation = process.env['USERPROFILE'] || 'C:\\'; - } - else { - if (process.platform === 'darwin') { - baseLocation = '/Users'; - } - else { - baseLocation = '/home'; - } - } - tempDirectory = path.join(baseLocation, 'actions', 'temp'); -} function getNode(versionSpec) { return __awaiter(this, void 0, void 0, function* () { // check cache @@ -12276,8 +12284,10 @@ function acquireNode(version) { function acquireNodeFromFallbackLocation(version) { return __awaiter(this, void 0, void 0, function* () { // Create temporary folder to download in to - let tempDownloadFolder = 'temp_' + Math.floor(Math.random() * 2000000000); - let tempDir = path.join(tempDirectory, tempDownloadFolder); + const tempDownloadFolder = 'temp_' + Math.floor(Math.random() * 2000000000); + const tempDirectory = process.env['RUNNER_TEMP'] || ''; + assert.ok(tempDirectory, 'Expected RUNNER_TEMP to be defined'); + const tempDir = path.join(tempDirectory, tempDownloadFolder); yield io.mkdirP(tempDir); let exeUrl; let libUrl; @@ -15815,6 +15825,83 @@ function onceStrict (fn) { } +/***/ }), + +/***/ 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__(470)); +/** + * 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'); + } + 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'); + } + } + 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++; + } + // Last attempt + return yield action(); + }); + } + getSleepAmount() { + return (Math.floor(Math.random() * (this.maxSeconds - this.minSeconds + 1)) + + this.minSeconds); + } + sleep(seconds) { + return __awaiter(this, void 0, void 0, function* () { + return new Promise(resolve => setTimeout(resolve, seconds * 1000)); + }); + } +} +exports.RetryHelper = RetryHelper; +//# sourceMappingURL=retry-helper.js.map + /***/ }), /***/ 986: diff --git a/package-lock.json b/package-lock.json index 29fbac4..a4b0fc8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -40,9 +40,9 @@ "integrity": "sha512-J8KuFqVPr3p6U8W93DOXlXW6zFvrQAJANdS+vw0YhusLIq+bszW8zmK2Fh1C2kDPX8FMvwIl1OUcFgvJoXLbAg==" }, "@actions/tool-cache": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@actions/tool-cache/-/tool-cache-1.3.1.tgz", - "integrity": "sha512-sKoEJv0/c7WzjPEq2PO12Sc8QdEp58XIBHMm3c4lUn/iZWgLz9HBeCuFGpLQjDvXJNfLZ4g+WD+rMjgOmpH4Ag==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@actions/tool-cache/-/tool-cache-1.3.3.tgz", + "integrity": "sha512-AFVyTLcIxusDVI1gMhbZW8m/On7YNJG+xYaxorV+qic+f7lO7h37aT2mfzxqAq7mwHxtP1YlVFNrXe9QDf/bPg==", "requires": { "@actions/core": "^1.2.0", "@actions/exec": "^1.0.0", @@ -738,9 +738,9 @@ }, "dependencies": { "acorn": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.1.1.tgz", - "integrity": "sha512-jPTiwtOxaHNaAPg/dmrJ/beuzLRnXtB0kQPQ8JpotKJgTB6rX6c8mlf315941pyjBSaPg8NHXS9fhP4u17DpGA==", + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.1.tgz", + "integrity": "sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA==", "dev": true } } @@ -3484,9 +3484,9 @@ } }, "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", "dev": true }, "kleur": { diff --git a/package.json b/package.json index fd0541f..0321d49 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,7 @@ "@actions/github": "^1.1.0", "@actions/http-client": "^1.0.6", "@actions/io": "^1.0.2", - "@actions/tool-cache": "^1.3.1", + "@actions/tool-cache": "^1.3.3", "semver": "^6.1.1" }, "devDependencies": { diff --git a/src/installer.ts b/src/installer.ts index 25c908a..1265ed8 100644 --- a/src/installer.ts +++ b/src/installer.ts @@ -1,5 +1,3 @@ -// Load tempDirectory before it gets wiped by tool-cache -let tempDirectory = process.env['RUNNER_TEMPDIRECTORY'] || ''; import * as assert from 'assert'; import * as core from '@actions/core'; import * as hc from '@actions/http-client'; @@ -12,21 +10,6 @@ import * as semver from 'semver'; let osPlat: string = os.platform(); let osArch: string = translateArchToDistUrl(os.arch()); -if (!tempDirectory) { - let baseLocation; - if (process.platform === 'win32') { - // On windows use the USERPROFILE env variable - baseLocation = process.env['USERPROFILE'] || 'C:\\'; - } else { - if (process.platform === 'darwin') { - baseLocation = '/Users'; - } else { - baseLocation = '/home'; - } - } - tempDirectory = path.join(baseLocation, 'actions', 'temp'); -} - // // Node versions interface // see https://nodejs.org/dist/index.json @@ -206,9 +189,11 @@ async function acquireNodeFromFallbackLocation( version: string ): Promise { // Create temporary folder to download in to - let tempDownloadFolder: string = + const tempDownloadFolder: string = 'temp_' + Math.floor(Math.random() * 2000000000); - let tempDir: string = path.join(tempDirectory, tempDownloadFolder); + const tempDirectory = process.env['RUNNER_TEMP'] || ''; + assert.ok(tempDirectory, 'Expected RUNNER_TEMP to be defined'); + const tempDir: string = path.join(tempDirectory, tempDownloadFolder); await io.mkdirP(tempDir); let exeUrl: string; let libUrl: string;