| "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; |
| }; |
| var __importDefault = (this && this.__importDefault) || function (mod) { |
| return (mod && mod.__esModule) ? mod : { "default": mod }; |
| }; |
| Object.defineProperty(exports, "__esModule", { value: true }); |
| const core = __importStar(require("@actions/core")); |
| const io = __importStar(require("@actions/io")); |
| const fs = __importStar(require("fs")); |
| const mm = __importStar(require("./manifest")); |
| const os = __importStar(require("os")); |
| const path = __importStar(require("path")); |
| const httpm = __importStar(require("@actions/http-client")); |
| const semver = __importStar(require("semver")); |
| const stream = __importStar(require("stream")); |
| const util = __importStar(require("util")); |
| const v4_1 = __importDefault(require("uuid/v4")); |
| const exec_1 = require("@actions/exec/lib/exec"); |
| const assert_1 = require("assert"); |
| const retry_helper_1 = require("./retry-helper"); |
| class HTTPError extends Error { |
| constructor(httpStatusCode) { |
| super(`Unexpected HTTP response: ${httpStatusCode}`); |
| this.httpStatusCode = httpStatusCode; |
| Object.setPrototypeOf(this, new.target.prototype); |
| } |
| } |
| exports.HTTPError = HTTPError; |
| const IS_WINDOWS = process.platform === 'win32'; |
| const IS_MAC = process.platform === 'darwin'; |
| const userAgent = 'actions/tool-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, dest, auth) { |
| return __awaiter(this, void 0, void 0, function* () { |
| 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; |
| } |
| } |
| // 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 |
| * |
| * @param file path to the .7z file |
| * @param dest destination directory. Optional. |
| * @param _7zPath path to 7zr.exe. Optional, for long path support. Most .7z archives do not have this |
| * problem. If your .7z archive contains very long paths, you can pass the path to 7zr.exe which will |
| * gracefully handle long paths. By default 7zdec.exe is used because it is a very small program and is |
| * bundled with the tool lib. However it does not support long paths. 7zr.exe is the reduced command line |
| * interface, it is smaller than the full command line interface, and it does support long paths. At the |
| * time of this writing, it is freely available from the LZMA SDK that is available on the 7zip website. |
| * Be sure to check the current license agreement. If 7zr.exe is bundled with your action, then the path |
| * to 7zr.exe can be pass to this function. |
| * @returns path to the destination directory |
| */ |
| 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 = yield _createExtractFolder(dest); |
| const originalCwd = process.cwd(); |
| process.chdir(dest); |
| if (_7zPath) { |
| try { |
| const logLevel = core.isDebug() ? '-bb1' : '-bb0'; |
| const args = [ |
| 'x', |
| logLevel, |
| '-bd', |
| '-sccUTF-8', |
| file |
| ]; |
| const options = { |
| silent: true |
| }; |
| yield exec_1.exec(`"${_7zPath}"`, args, options); |
| } |
| finally { |
| process.chdir(originalCwd); |
| } |
| } |
| else { |
| const escapedScript = path |
| .join(__dirname, '..', 'scripts', 'Invoke-7zdec.ps1') |
| .replace(/'/g, "''") |
| .replace(/"|\n|\r/g, ''); // double-up single quotes, remove double quotes and newlines |
| const escapedFile = file.replace(/'/g, "''").replace(/"|\n|\r/g, ''); |
| const escapedTarget = dest.replace(/'/g, "''").replace(/"|\n|\r/g, ''); |
| const command = `& '${escapedScript}' -Source '${escapedFile}' -Target '${escapedTarget}'`; |
| const args = [ |
| '-NoLogo', |
| '-Sta', |
| '-NoProfile', |
| '-NonInteractive', |
| '-ExecutionPolicy', |
| 'Unrestricted', |
| '-Command', |
| command |
| ]; |
| const options = { |
| silent: true |
| }; |
| try { |
| const powershellPath = yield io.which('powershell', true); |
| yield exec_1.exec(`"${powershellPath}"`, args, options); |
| } |
| finally { |
| process.chdir(originalCwd); |
| } |
| } |
| return dest; |
| }); |
| } |
| exports.extract7z = extract7z; |
| /** |
| * Extract a compressed tar archive |
| * |
| * @param file path to the tar |
| * @param dest destination directory. 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') { |
| return __awaiter(this, void 0, void 0, function* () { |
| if (!file) { |
| throw new Error("parameter 'file' is required"); |
| } |
| // 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 |
| * |
| * @param file path to the zip |
| * @param dest destination directory. Optional. |
| * @returns path to the destination directory |
| */ |
| function extractZip(file, dest) { |
| return __awaiter(this, void 0, void 0, function* () { |
| if (!file) { |
| throw new Error("parameter 'file' is required"); |
| } |
| dest = yield _createExtractFolder(dest); |
| if (IS_WINDOWS) { |
| yield extractZipWin(file, dest); |
| } |
| else { |
| yield extractZipNix(file, dest); |
| } |
| return dest; |
| }); |
| } |
| exports.extractZip = extractZip; |
| function extractZipWin(file, dest) { |
| return __awaiter(this, void 0, void 0, function* () { |
| // build the powershell command |
| const escapedFile = file.replace(/'/g, "''").replace(/"|\n|\r/g, ''); // double-up single quotes, remove double quotes and newlines |
| 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', true); |
| const args = [ |
| '-NoLogo', |
| '-Sta', |
| '-NoProfile', |
| '-NonInteractive', |
| '-ExecutionPolicy', |
| 'Unrestricted', |
| '-Command', |
| command |
| ]; |
| yield exec_1.exec(`"${powershellPath}"`, args); |
| }); |
| } |
| function extractZipNix(file, dest) { |
| return __awaiter(this, void 0, void 0, function* () { |
| const unzipPath = yield io.which('unzip', true); |
| const args = [file]; |
| if (!core.isDebug()) { |
| args.unshift('-q'); |
| } |
| yield exec_1.exec(`"${unzipPath}"`, args, { cwd: dest }); |
| }); |
| } |
| /** |
| * Caches a directory and installs it into the tool cacheDir |
| * |
| * @param sourceDir the directory to cache into tools |
| * @param tool tool name |
| * @param version version of the tool. semver format |
| * @param arch architecture of the tool. Optional. Defaults to machine architecture |
| */ |
| function cacheDir(sourceDir, tool, version, arch) { |
| return __awaiter(this, void 0, void 0, function* () { |
| version = semver.clean(version) || version; |
| arch = arch || os.arch(); |
| core.debug(`Caching tool ${tool} ${version} ${arch}`); |
| core.debug(`source dir: ${sourceDir}`); |
| if (!fs.statSync(sourceDir).isDirectory()) { |
| throw new Error('sourceDir is not a directory'); |
| } |
| // Create the tool dir |
| const destPath = yield _createToolPath(tool, version, arch); |
| // copy each child item. do not move. move can fail on Windows |
| // due to anti-virus software having an open handle on a file. |
| for (const itemName of fs.readdirSync(sourceDir)) { |
| const s = path.join(sourceDir, itemName); |
| yield io.cp(s, destPath, { recursive: true }); |
| } |
| // write .complete |
| _completeToolPath(tool, version, arch); |
| return destPath; |
| }); |
| } |
| exports.cacheDir = cacheDir; |
| /** |
| * Caches a downloaded file (GUID) and installs it |
| * into the tool cache with a given targetName |
| * |
| * @param sourceFile the file to cache into tools. Typically a result of downloadTool which is a guid. |
| * @param targetFile the name of the file name in the tools directory |
| * @param tool tool name |
| * @param version version of the tool. semver format |
| * @param arch architecture of the tool. Optional. Defaults to machine architecture |
| */ |
| function cacheFile(sourceFile, targetFile, tool, version, arch) { |
| return __awaiter(this, void 0, void 0, function* () { |
| version = semver.clean(version) || version; |
| arch = arch || os.arch(); |
| core.debug(`Caching tool ${tool} ${version} ${arch}`); |
| core.debug(`source file: ${sourceFile}`); |
| if (!fs.statSync(sourceFile).isFile()) { |
| throw new Error('sourceFile is not a file'); |
| } |
| // create the tool dir |
| const destFolder = yield _createToolPath(tool, version, arch); |
| // copy instead of move. move can fail on Windows due to |
| // anti-virus software having an open handle on a file. |
| const destPath = path.join(destFolder, targetFile); |
| core.debug(`destination file ${destPath}`); |
| yield io.cp(sourceFile, destPath); |
| // write .complete |
| _completeToolPath(tool, version, arch); |
| return destFolder; |
| }); |
| } |
| exports.cacheFile = cacheFile; |
| /** |
| * Finds the path to a tool version in the local installed tool cache |
| * |
| * @param toolName name of the tool |
| * @param versionSpec version of the tool |
| * @param arch optional arch. defaults to arch of computer |
| */ |
| function find(toolName, versionSpec, arch) { |
| if (!toolName) { |
| throw new Error('toolName parameter is required'); |
| } |
| if (!versionSpec) { |
| throw new Error('versionSpec parameter is required'); |
| } |
| arch = arch || os.arch(); |
| // attempt to resolve an explicit version |
| if (!_isExplicitVersion(versionSpec)) { |
| const localVersions = findAllVersions(toolName, arch); |
| const match = _evaluateVersions(localVersions, versionSpec); |
| versionSpec = match; |
| } |
| // check for the explicit version in the cache |
| let toolPath = ''; |
| if (versionSpec) { |
| versionSpec = semver.clean(versionSpec) || ''; |
| 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}`); |
| toolPath = cachePath; |
| } |
| else { |
| core.debug('not found'); |
| } |
| } |
| return toolPath; |
| } |
| exports.find = find; |
| /** |
| * Finds the paths to all versions of a tool that are installed in the local tool cache |
| * |
| * @param toolName name of the tool |
| * @param arch optional arch. defaults to arch of computer |
| */ |
| function findAllVersions(toolName, arch) { |
| const versions = []; |
| arch = arch || os.arch(); |
| const toolPath = path.join(_getCacheDirectory(), toolName); |
| if (fs.existsSync(toolPath)) { |
| const children = fs.readdirSync(toolPath); |
| for (const child of children) { |
| if (_isExplicitVersion(child)) { |
| const fullPath = path.join(toolPath, child, arch || ''); |
| if (fs.existsSync(fullPath) && fs.existsSync(`${fullPath}.complete`)) { |
| versions.push(child); |
| } |
| } |
| } |
| } |
| 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(_getTempDirectory(), v4_1.default()); |
| } |
| yield io.mkdirP(dest); |
| return dest; |
| }); |
| } |
| function _createToolPath(tool, version, arch) { |
| return __awaiter(this, void 0, void 0, function* () { |
| const folderPath = path.join(_getCacheDirectory(), tool, semver.clean(version) || version, arch || ''); |
| core.debug(`destination ${folderPath}`); |
| const markerPath = `${folderPath}.complete`; |
| yield io.rmRF(folderPath); |
| yield io.rmRF(markerPath); |
| yield io.mkdirP(folderPath); |
| return folderPath; |
| }); |
| } |
| function _completeToolPath(tool, 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'); |
| } |
| function _isExplicitVersion(versionSpec) { |
| const c = semver.clean(versionSpec) || ''; |
| core.debug(`isExplicit: ${c}`); |
| const valid = semver.valid(c) != null; |
| core.debug(`explicit? ${valid}`); |
| return valid; |
| } |
| function _evaluateVersions(versions, versionSpec) { |
| let version = ''; |
| core.debug(`evaluating ${versions.length} versions`); |
| versions = versions.sort((a, b) => { |
| if (semver.gt(a, b)) { |
| return 1; |
| } |
| return -1; |
| }); |
| for (let i = versions.length - 1; i >= 0; i--) { |
| const potential = versions[i]; |
| const satisfied = semver.satisfies(potential, versionSpec); |
| if (satisfied) { |
| version = potential; |
| break; |
| } |
| } |
| if (version) { |
| core.debug(`matched: ${version}`); |
| } |
| else { |
| core.debug('match not found'); |
| } |
| 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 |