UNPKG

setup-cpp

Version:

Install all the tools required for building and testing C++/C projects.

158 lines (137 loc) 5.26 kB
import { tmpdir } from "os" import { basename, join } from "path" import { cacheDir, downloadTool, find } from "@actions/tool-cache" import ciInfo from "ci-info" const { GITHUB_ACTIONS } = ciInfo import { info, warning } from "ci-log" import { addPath } from "envosman" import { chmod } from "fs/promises" import { pathExists } from "path-exists" import retry from "retry-as-promised" import { maybeGetInput, rcOptions } from "../../options.js" import { getArchiveType, getExtractFunction } from "./extract.js" /** A type that describes a package */ export type PackageInfo = { /** Url to download the package */ url: string /** The top folder name once it is extracted. It can be `""` if there is no top folder */ extractedFolderName: string /** The relative directory in which the binary is located. It can be `""` if the exe is in the top folder */ binRelativeDir: string /** The main binary file. */ binFileName: string /** The function to extract the downloaded archive. It can be `undefined`, if the binary itself is downloaded directly. */ extractFunction?: (file: string, dest: string) => Promise<unknown> } export type InstallationInfo = { /** The top install dir */ installDir?: string binDir: string bin?: string } /** * A function that: * * - Downloads and extracts a package * - Adds the bin path of the package to PATH * - Caches the downloaded directory into tool cache for usage from other sessions * * @returns The installation directory */ export async function setupBin( name: string, version: string, getPackageInfo: (version: string, platform: NodeJS.Platform, arch: string) => PackageInfo | Promise<PackageInfo>, setupDir: string, arch: string, ): Promise<InstallationInfo> { info(`Installing ${name} ${version} ${arch} via direct downloading`) process.env.RUNNER_TEMP = process.env.RUNNER_TEMP ?? tmpdir() process.env.RUNNER_TOOL_CACHE = process.env.RUNNER_TOOL_CACHE ?? join(tmpdir(), "setup-cpp", "hostedtoolcache") const { url, binRelativeDir, binFileName, extractedFolderName, extractFunction } = await getPackageInfo( version, process.platform, arch, ) // Restore from cache (if found). if (GITHUB_ACTIONS) { try { const dir = find(name, version) if (dir) { const installDir = join(dir, extractedFolderName) const binDir = join(installDir, binRelativeDir) if (await pathExists(join(binDir, binFileName))) { info(`${name} ${version} was found in the cache at ${binDir}.`) await addPath(binDir, rcOptions) return { installDir, binDir } } } } catch { // fails on a local machine? } } const installDir = join(setupDir, extractedFolderName) const binDir = join(installDir, binRelativeDir) const binFile = join(binDir, binFileName) await downloadExtractInstall(binDir, binFile, name, version, url, setupDir, extractFunction, arch) await cacheInstallation(setupDir, name, version) return { installDir, binDir } } async function downloadExtractInstall( binDir: string, binFile: string, name: string, version: string, url: string, setupDir: string, givenExtractFunction: PackageInfo["extractFunction"], arch: string, ) { // download ane extract the package into the installation directory. if ((await Promise.all([pathExists(binDir), pathExists(binFile)])).includes(false)) { try { const downloaded = await tryDownload(name, version, url) info(`Extracting ${downloaded} to ${setupDir}`) const extractFunction = givenExtractFunction ?? getExtractFunction(getArchiveType(url)) await extractFunction(downloaded, setupDir) } catch (err) { throw new Error(`Failed to download ${name} ${version} ${arch} from ${url}: ${err}`) } } // Adding the bin dir to the path /** The directory which the tool is installed to */ info(`Add ${binDir} to PATH`) await addPath(binDir, rcOptions) // Check if the binary exists after extraction if (!(await pathExists(binFile))) { throw new Error(`Failed to find the binary ${binFile} after extracting ${name} ${version} ${arch}`) } // make the binary executable on non-windows platforms if (process.platform !== "win32") { try { await chmod(binFile, "755") } catch (err) { warning(`Failed to make ${binFile} executable: ${err}`) } } } async function tryDownload(name: string, version: string, url: string) { info(`Download ${name} ${version}`) // try to download the package 4 times with 2 seconds delay const downloaded = await retry( () => { const downloadedFilePath = join(process.env.RUNNER_TEMP ?? tmpdir(), `${Date.now()}-${basename(url)}`) return downloadTool(url, downloadedFilePath) }, { name: url, max: 4, backoffBase: 2000, report: (err) => info(err) }, ) return downloaded } async function cacheInstallation(setupDir: string, name: string, version: string) { // check if inside Github Actions. If so, cache the installation if (GITHUB_ACTIONS && typeof process.env.RUNNER_TOOL_CACHE === "string") { if (maybeGetInput("cache-tools") === "true" || process.env.CACHE_TOOLS === "true") { await cacheDir(setupDir, name, version) } } }