Merge pull request #21 from aminya/make-task

This commit is contained in:
Amin Yahyaabadi 2022-01-30 18:44:02 -08:00 committed by GitHub
commit 454f319514
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
31 changed files with 260 additions and 96 deletions

View File

@ -23,6 +23,8 @@ The package can be used locally or from CI services like GitHub Actions.
- vcpkg - vcpkg
- meson - meson
- conan - conan
- make
- task
- ccache - ccache
- cppcheck - cppcheck
- clangtidy - clangtidy

View File

@ -30,6 +30,12 @@ inputs:
conan: conan:
description: "The conan version to install." description: "The conan version to install."
required: false required: false
make:
description: "The make version to install."
required: false
task:
description: "The task version to install."
required: false
vcpkg: vcpkg:
description: "The vcpkg version to install." description: "The vcpkg version to install."
required: false required: false

2
dist/setup_cpp.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -56,7 +56,7 @@ export function setupChocolatey(
if (maybeChoco !== null) { if (maybeChoco !== null) {
binDir = dirname(maybeChoco) binDir = dirname(maybeChoco)
} else { } else {
binDir = "C:/ProgramData/Chocolatey/bin/" binDir = `${process.env.ChocolateyInstall ?? "C:/ProgramData/chocolatey"}/bin`
} }
if (existsSync(binDir)) { if (existsSync(binDir)) {

View File

@ -1,5 +1,6 @@
import { setupCmake } from "../cmake" import { setupCmake } from "../cmake"
import { setupTmpDir, cleanupTmpDir, testBin } from "../../utils/tests/test-helpers" import { setupTmpDir, cleanupTmpDir, testBin } from "../../utils/tests/test-helpers"
import { isGitHubCI } from "../../utils/env/isci"
jest.setTimeout(300000) jest.setTimeout(300000)
@ -10,14 +11,16 @@ describe("setup-cmake", () => {
}) })
it("should setup CMake", async () => { it("should setup CMake", async () => {
const { binDir } = await setupCmake("3.20.2", directory, "") const { binDir } = await setupCmake("3.20.2", directory, process.arch)
await testBin("cmake", ["--version"], binDir) await testBin("cmake", ["--version"], binDir)
}) })
it("should find CMake in the cache", async () => { it("should find CMake in the cache", async () => {
const { binDir } = await setupCmake("3.20.2", directory, "") const { binDir } = await setupCmake("3.20.2", directory, process.arch)
await testBin("cmake", ["--version"], binDir) await testBin("cmake", ["--version"], binDir)
expect(binDir.includes("ToolCache")).toBeTruthy() if (isGitHubCI()) {
expect(binDir).toMatch(process.env.RUNNER_TOOL_CACHE ?? "hostedtoolcache")
}
}) })
afterAll(async () => { afterAll(async () => {

View File

@ -1,16 +1,13 @@
import { extractZip, extractTar } from "@actions/tool-cache" import { extractZip, extractTar } from "@actions/tool-cache"
import { getInput } from "@actions/core"
import semverLte from "semver/functions/lte" import semverLte from "semver/functions/lte"
import semverCoerce from "semver/functions/coerce" import semverCoerce from "semver/functions/coerce"
import { setupBin, PackageInfo, InstallationInfo } from "../utils/setup/setupBin" import { setupBin, PackageInfo, InstallationInfo } from "../utils/setup/setupBin"
import { addBinExtension } from "../utils/extension/extension" import { addBinExtension } from "../utils/extension/extension"
/** Get the platform data for cmake */ /** Get the platform data for cmake */
function getCmakePackageInfo(version: string, platform?: NodeJS.Platform): PackageInfo { function getCmakePackageInfo(version: string, platform: NodeJS.Platform, arch: string): PackageInfo {
const semVersion = semverCoerce(version) ?? version const semVersion = semverCoerce(version) ?? version
const platformStr = platform ?? process.platform switch (platform) {
const arch = getInput("architecture") || process.arch
switch (platformStr) {
case "win32": { case "win32": {
const isOld = semverLte(semVersion, "v3.19.6") const isOld = semverLte(semVersion, "v3.19.6")
let osArchStr: string let osArchStr: string
@ -58,12 +55,11 @@ function getCmakePackageInfo(version: string, platform?: NodeJS.Platform): Packa
} }
} }
default: default:
throw new Error(`Unsupported platform '${platformStr}'`) throw new Error(`Unsupported platform '${platform}'`)
} }
} }
/** Setup cmake */ /** Setup cmake */
// eslint-disable-next-line @typescript-eslint/no-unused-vars export function setupCmake(version: string, setupDir: string, arch: string): Promise<InstallationInfo> {
export function setupCmake(version: string, setupDir: string, _arch: string): Promise<InstallationInfo> { return setupBin("cmake", version, getCmakePackageInfo, setupDir, arch)
return setupBin("cmake", version, getCmakePackageInfo, setupDir)
} }

View File

@ -11,6 +11,7 @@ const DefaultVersions: Record<string, string> = {
meson: "0.61.1", meson: "0.61.1",
python: "3.8.10", python: "3.8.10",
kcov: "v39", kcov: "v39",
task: "3.10.0",
gcc: process.platform === "win32" ? "11.2.0.07112021" : "11", gcc: process.platform === "win32" ? "11.2.0.07112021" : "11",
} }

View File

@ -24,8 +24,8 @@ export async function setupGcc(version: string, _setupDir: string, arch: string)
} else if (arch === "ia32" && existsSync("C:/tools/mingw32/bin")) { } else if (arch === "ia32" && existsSync("C:/tools/mingw32/bin")) {
binDir = "C:/tools/mingw32/bin" binDir = "C:/tools/mingw32/bin"
addPath(binDir) addPath(binDir)
} else if (existsSync("C:/ProgramData/Chocolatey/bin/g++.exe")) { } else if (existsSync(`${process.env.ChocolateyInstall ?? "C:/ProgramData/chocolatey"}/bin/g++.exe`)) {
binDir = "C:/ProgramData/Chocolatey/bin/" binDir = `${process.env.ChocolateyInstall ?? "C:/ProgramData/chocolatey"}/bin`
} }
break break
} }

View File

@ -31,7 +31,7 @@ describe("setup-Kcov", () => {
// it("should find Kcov in the cache", async () => { // it("should find Kcov in the cache", async () => {
// const directory = await setupTmpDir("kcov-v39") // const directory = await setupTmpDir("kcov-v39")
// const binDir = await testKcov("v39", directory) // const binDir = await testKcov("v39", directory)
// expect(binDir.includes("ToolCache")).toBeTruthy() // expect(binDir.includes("hostedtoolcache")).toBeTruthy()
// await cleanupTmpDir("kcov-v39") // await cleanupTmpDir("kcov-v39")
// }) // })
}) })

View File

@ -2,7 +2,7 @@ import execa from "execa"
// import { join } from "path" // import { join } from "path"
// import { untildify_user as untildify } from "./utils/path/untildify" // import { untildify_user as untildify } from "./utils/path/untildify"
// import { setupCmake } from "../cmake/cmake" // import { setupCmake } from "../cmake/cmake"
import { execaSudo } from "../utils/env/sudo" import { execSudo } from "../utils/exec/sudo"
import { addBinExtension } from "../utils/extension/extension" import { addBinExtension } from "../utils/extension/extension"
import { extractTarByExe } from "../utils/setup/extract" import { extractTarByExe } from "../utils/setup/extract"
import { setupAptPack } from "../utils/setup/setupAptPack" import { setupAptPack } from "../utils/setup/setupAptPack"
@ -20,9 +20,7 @@ function getKcovPackageInfo(version: string): PackageInfo {
extractedFolderName: "", extractedFolderName: "",
binRelativeDir: "usr/local/bin", binRelativeDir: "usr/local/bin",
binFileName: addBinExtension("kcov"), binFileName: addBinExtension("kcov"),
extractFunction: (file: string, dest: string) => { extractFunction: extractTarByExe,
return extractTarByExe(file, dest, ["--strip-components=0"])
},
} }
} else { } else {
return { return {
@ -38,7 +36,7 @@ function getKcovPackageInfo(version: string): PackageInfo {
await setupAptPack("libcurl4-openssl-dev") await setupAptPack("libcurl4-openssl-dev")
await execa("cmake", ["-S", "./", "-B", "./build"], { cwd: out }) await execa("cmake", ["-S", "./", "-B", "./build"], { cwd: out })
await execa("cmake", ["--build", "./build", "--config", "Release"], { cwd: out }) await execa("cmake", ["--build", "./build", "--config", "Release"], { cwd: out })
await execaSudo("cmake", ["--install", "./build"], out) await execSudo("cmake", ["--install", "./build"], out)
return out return out
}, },
} }
@ -48,7 +46,7 @@ function getKcovPackageInfo(version: string): PackageInfo {
export async function setupKcov(version: string, setupDir: string, arch: string) { export async function setupKcov(version: string, setupDir: string, arch: string) {
switch (process.platform) { switch (process.platform) {
case "linux": { case "linux": {
const installationInfo = await setupBin("kcov", version, getKcovPackageInfo, setupDir) const installationInfo = await setupBin("kcov", version, getKcovPackageInfo, setupDir, arch)
return installationInfo return installationInfo
} }
default: { default: {

View File

@ -2,6 +2,7 @@ import { setupLLVM, VERSIONS, getUrl, setupClangTools } from "../llvm"
import { getSpecificVersionAndUrl } from "../../utils/setup/version" import { getSpecificVersionAndUrl } from "../../utils/setup/version"
import { isValidUrl } from "../../utils/http/validate_url" import { isValidUrl } from "../../utils/http/validate_url"
import { setupTmpDir, cleanupTmpDir, testBin } from "../../utils/tests/test-helpers" import { setupTmpDir, cleanupTmpDir, testBin } from "../../utils/tests/test-helpers"
import { isGitHubCI } from "../../utils/env/isci"
jest.setTimeout(300000) jest.setTimeout(300000)
async function testUrl(version: string) { async function testUrl(version: string) {
@ -42,7 +43,7 @@ describe("setup-llvm", () => {
}) })
it("should setup LLVM", async () => { it("should setup LLVM", async () => {
const { binDir } = await setupLLVM("11.0.0", directory, "") const { binDir } = await setupLLVM("11.0.0", directory, process.arch)
await testBin("clang++", ["--version"], binDir) await testBin("clang++", ["--version"], binDir)
expect(process.env.CC?.includes("clang")).toBeTruthy() expect(process.env.CC?.includes("clang")).toBeTruthy()
@ -50,24 +51,29 @@ describe("setup-llvm", () => {
}) })
it("should find llvm in the cache", async () => { it("should find llvm in the cache", async () => {
const { binDir } = await setupLLVM("11.0.0", directory, "") const { binDir } = await setupLLVM("11.0.0", directory, process.arch)
await testBin("clang++", ["--version"], binDir) await testBin("clang++", ["--version"], binDir)
expect(binDir.includes("ToolCache")).toBeTruthy() if (isGitHubCI()) {
expect(binDir).toMatch(process.env.RUNNER_TOOL_CACHE ?? "hostedtoolcache")
}
expect(process.env.CC?.includes("clang")).toBeTruthy() expect(process.env.CC?.includes("clang")).toBeTruthy()
expect(process.env.CXX?.includes("clang++")).toBeTruthy() expect(process.env.CXX?.includes("clang++")).toBeTruthy()
expect(process.env.CC?.includes("ToolCache")).toBeTruthy()
expect(process.env.CXX?.includes("ToolCache")).toBeTruthy() if (isGitHubCI()) {
expect(process.env.CC).toMatch("hostedtoolcache")
expect(process.env.CXX).toMatch("hostedtoolcache")
}
}) })
it("should setup clang-tidy and clang-format", async () => { it("should setup clang-tidy and clang-format", async () => {
const { binDir } = await setupClangTools("11.0.0", directory, "") const { binDir } = await setupClangTools("11.0.0", directory, process.arch)
await testBin("clang-tidy", ["--version"], binDir) await testBin("clang-tidy", ["--version"], binDir)
await testBin("clang-format", ["--version"], binDir) await testBin("clang-format", ["--version"], binDir)
}) })
afterAll(async () => { afterAll(async () => {
await cleanupTmpDir("setup-llvm") await cleanupTmpDir("llvm")
}, 100000) }, 100000)
}) })

View File

@ -222,8 +222,8 @@ export function getUrl(platform: string, version: string): string | null | Promi
//================================================ //================================================
// Exports // Exports
//================================================ //================================================
// eslint-disable-next-line @typescript-eslint/no-unused-vars
async function getLLVMPackageInfo(version: string, platform: NodeJS.Platform): Promise<PackageInfo> { async function getLLVMPackageInfo(version: string, platform: NodeJS.Platform, _arch: string): Promise<PackageInfo> {
const [specificVersion, url] = await getSpecificVersionAndUrl(VERSIONS, platform, version, getUrl) const [specificVersion, url] = await getSpecificVersionAndUrl(VERSIONS, platform, version, getUrl)
setOutput("version", specificVersion) setOutput("version", specificVersion)
return { return {
@ -231,17 +231,17 @@ async function getLLVMPackageInfo(version: string, platform: NodeJS.Platform): P
extractedFolderName: "", extractedFolderName: "",
binRelativeDir: "bin", binRelativeDir: "bin",
binFileName: addBinExtension("clang"), binFileName: addBinExtension("clang"),
extractFunction: platform === "win32" ? extractExe : extractTarByExe, extractFunction:
platform === "win32"
? extractExe
: (file: string, dest: string) => {
return extractTarByExe(file, dest, ["--strip-components=1"])
},
} }
} }
export async function setupLLVM( export async function setupLLVM(version: string, setupDir: string, arch: string): Promise<InstallationInfo> {
version: string, const installationInfo = await setupBin("llvm", version, getLLVMPackageInfo, setupDir, arch)
setupDir: string,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
_arch: string
): Promise<InstallationInfo> {
const installationInfo = await setupBin("llvm", version, getLLVMPackageInfo, setupDir)
await activateLLVM(installationInfo.installDir ?? setupDir, version) await activateLLVM(installationInfo.installDir ?? setupDir, version)
return installationInfo return installationInfo
} }
@ -284,11 +284,6 @@ export async function activateLLVM(directory: string, versionGiven: string) {
} }
/** Setup llvm tools (clang tidy, clang format, etc) without activating llvm and using it as the compiler */ /** Setup llvm tools (clang tidy, clang format, etc) without activating llvm and using it as the compiler */
export function setupClangTools( export function setupClangTools(version: string, setupDir: string, arch: string): Promise<InstallationInfo> {
version: string, return setupBin("llvm", version, getLLVMPackageInfo, setupDir, arch)
setupDir: string,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
_arch: string
): Promise<InstallationInfo> {
return setupBin("llvm", version, getLLVMPackageInfo, setupDir)
} }

View File

@ -1,6 +1,8 @@
import * as core from "@actions/core" import * as core from "@actions/core"
import { setupBrew } from "./brew/brew" import { setupBrew } from "./brew/brew"
import { setupCcache } from "./ccache/ccache" import { setupCcache } from "./ccache/ccache"
import { setupMake } from "./make/make"
import { setupTask } from "./task/task"
import { setupChocolatey } from "./chocolatey/chocolatey" import { setupChocolatey } from "./chocolatey/chocolatey"
import { setupCmake } from "./cmake/cmake" import { setupCmake } from "./cmake/cmake"
import { setupConan } from "./conan/conan" import { setupConan } from "./conan/conan"
@ -50,6 +52,8 @@ const setups = {
msvc: setupMSVC, msvc: setupMSVC,
vcvarsall: setupVCVarsall, vcvarsall: setupVCVarsall,
kcov: setupKcov, kcov: setupKcov,
make: setupMake,
task: setupTask,
} }
/** The tools that can be installed */ /** The tools that can be installed */
@ -74,6 +78,8 @@ const tools: Array<keyof typeof setups> = [
"msvc", "msvc",
"vcvarsall", "vcvarsall",
"kcov", "kcov",
"make",
"task",
] ]
/** The possible inputs to the program */ /** The possible inputs to the program */
@ -282,6 +288,8 @@ All the available tools:
--vcpkg --vcpkg
--meson --meson
--conan --conan
--make
--task
--ccache --ccache
--cppcheck --cppcheck
--clangformat --clangformat

View File

@ -0,0 +1,12 @@
import { setupMake } from "../make"
import { testBin } from "../../utils/tests/test-helpers"
import { InstallationInfo } from "../../utils/setup/setupBin"
jest.setTimeout(300000)
describe("setup-make", () => {
it("should setup make", async () => {
const installInfo = await setupMake("", "", process.arch)
await testBin("make", ["--version"], (installInfo as InstallationInfo | undefined)?.binDir)
})
})

24
src/make/make.ts Normal file
View File

@ -0,0 +1,24 @@
import { addPath } from "@actions/core"
import { setupAptPack } from "../utils/setup/setupAptPack"
import { setupBrewPack } from "../utils/setup/setupBrewPack"
import { setupChocoPack } from "../utils/setup/setupChocoPack"
// eslint-disable-next-line @typescript-eslint/no-unused-vars
export function setupMake(version: string, _setupDir: string, _arch: string) {
switch (process.platform) {
case "win32": {
return setupChocoPack("make", version)
}
case "darwin": {
setupBrewPack("make", version)
addPath("/usr/local/opt/make/libexec/gnubin")
return { binDir: "/usr/local/opt/make/libexec/gnubin" }
}
case "linux": {
return setupAptPack("make", version)
}
default: {
throw new Error(`Unsupported platform`)
}
}
}

View File

@ -1,9 +1,10 @@
import { setupNinja } from "../ninja" import { setupNinja } from "../ninja"
import { setupTmpDir, cleanupTmpDir, testBin } from "../../utils/tests/test-helpers" import { setupTmpDir, cleanupTmpDir, testBin } from "../../utils/tests/test-helpers"
import { isGitHubCI } from "../../utils/env/isci"
jest.setTimeout(300000) jest.setTimeout(300000)
async function testNinja(directory: string) { async function testNinja(directory: string) {
const { binDir } = await setupNinja("1.10.2", directory, "") const { binDir } = await setupNinja("1.10.2", directory, process.arch)
await testBin("ninja", ["--version"], binDir) await testBin("ninja", ["--version"], binDir)
return binDir return binDir
} }
@ -20,10 +21,12 @@ describe("setup-ninja", () => {
it("should find Ninja in the cache", async () => { it("should find Ninja in the cache", async () => {
const binDir = await testNinja(directory) const binDir = await testNinja(directory)
expect(binDir.includes("ToolCache")).toBeTruthy() if (isGitHubCI()) {
expect(binDir).toMatch(process.env.RUNNER_TOOL_CACHE ?? "hostedtoolcache")
}
}) })
afterEach(async () => { afterEach(async () => {
await cleanupTmpDir("setup-ninja") await cleanupTmpDir("ninja")
}, 100000) }, 100000)
}) })

View File

@ -17,7 +17,8 @@ function getNinjaPlatform(platform: NodeJS.Platform) {
} }
/** Get the platform data for ninja */ /** Get the platform data for ninja */
function getNinjaPackageInfo(version: string, platform: NodeJS.Platform): PackageInfo { // eslint-disable-next-line @typescript-eslint/no-unused-vars
function getNinjaPackageInfo(version: string, platform: NodeJS.Platform, _arch: string): PackageInfo {
const ninjaPlatform = getNinjaPlatform(platform) const ninjaPlatform = getNinjaPlatform(platform)
return { return {
binRelativeDir: "", binRelativeDir: "",
@ -28,7 +29,6 @@ function getNinjaPackageInfo(version: string, platform: NodeJS.Platform): Packag
} }
} }
// eslint-disable-next-line @typescript-eslint/no-unused-vars export function setupNinja(version: string, setupDir: string, arch: string): Promise<InstallationInfo> {
export function setupNinja(version: string, setupDir: string, _arch: string): Promise<InstallationInfo> { return setupBin("ninja", version, getNinjaPackageInfo, setupDir, arch)
return setupBin("ninja", version, getNinjaPackageInfo, setupDir)
} }

View File

@ -0,0 +1,28 @@
import { setupTask } from "../task"
import { cleanupTmpDir, setupTmpDir, testBin } from "../../utils/tests/test-helpers"
import { isGitHubCI } from "../../utils/env/isci"
jest.setTimeout(300000)
describe("setup-task", () => {
let directory: string
beforeAll(async () => {
directory = await setupTmpDir("task")
})
it("should setup task", async () => {
const { binDir } = await setupTask("3.10.0", directory, process.arch)
await testBin("task", ["--version"], binDir)
})
it("should find task in the cache", async () => {
const { binDir } = await setupTask("3.10.0", directory, process.arch)
if (isGitHubCI()) {
expect(binDir).toMatch(process.env.RUNNER_TOOL_CACHE ?? "hostedtoolcache")
}
})
afterEach(async () => {
await cleanupTmpDir("task")
}, 100000)
})

48
src/task/task.ts Normal file
View File

@ -0,0 +1,48 @@
import { extractZip } from "@actions/tool-cache"
import { addBinExtension } from "../utils/extension/extension"
import { extractTarByExe } from "../utils/setup/extract"
import { setupBin, PackageInfo, InstallationInfo } from "../utils/setup/setupBin"
/** Get the platform name task uses in their download links */
function getTaskPlatform(platform: NodeJS.Platform) {
switch (platform) {
case "win32":
return "windows"
default:
return platform
}
}
/** Get the arch name task uses in their download links */
function getTaskArch(arch: string) {
switch (arch) {
case "x64":
return "amd64"
case "ia32":
case "x86":
case "i386":
case "x32":
return "386"
default:
return arch
}
}
/** Get the platform data for task */
function getTaskPackageInfo(version: string, platform: NodeJS.Platform, arch: string): PackageInfo {
const taskPlatform = getTaskPlatform(platform)
const taskArch = getTaskArch(arch)
const isZip = platform === "win32"
const extension = isZip ? "zip" : "tar.gz"
return {
binRelativeDir: "",
binFileName: addBinExtension("task"),
extractedFolderName: "",
extractFunction: isZip ? extractZip : extractTarByExe,
url: `https://github.com/go-task/task/releases/download/v${version}/task_${taskPlatform}_${taskArch}.${extension}`,
}
}
export function setupTask(version: string, setupDir: string, arch: string): Promise<InstallationInfo> {
return setupBin("task", version, getTaskPackageInfo, setupDir, arch)
}

View File

@ -1,12 +1,12 @@
import { exportVariable } from "@actions/core" import { exportVariable } from "@actions/core"
import * as core from "@actions/core" import * as core from "@actions/core"
import execa from "execa"
import { isGitHubCI } from "./isci" import { isGitHubCI } from "./isci"
import { untildify_user as untildify } from "../path/untildify" import { untildify_user as untildify } from "../path/untildify"
import { appendFileSync } from "fs" import { appendFileSync } from "fs"
import { join } from "path" import { join } from "path"
import { isRoot } from "./sudo" import { isRoot } from "./sudo"
import { error } from "../io/io" import { error } from "../io/io"
import { execPowershell } from "../exec/powershell"
/** An add path function that works locally or inside GitHub Actions */ /** An add path function that works locally or inside GitHub Actions */
export function addEnv(name: string, val: string | undefined) { export function addEnv(name: string, val: string | undefined) {
@ -31,11 +31,8 @@ function addEnvSystem(name: string, valGiven: string | undefined) {
const val = valGiven ?? "" const val = valGiven ?? ""
switch (process.platform) { switch (process.platform) {
case "win32": { case "win32": {
if (val.length <= 1024) { // We do not use `execa.sync(`setx PATH "${path};%PATH%"`)` because of its character limit
execa.sync(`setx "${name}" "${val}"`) execPowershell(`[Environment]::SetEnvironmentVariable('${name}', '${val}', 'User')`)
} else {
execa.sync(`powershell -C "[Environment]::SetEnvironmentVariable('${name}', '${val}', 'User')"`)
}
core.info(`${name}="${val} was set in the environment."`) core.info(`${name}="${val} was set in the environment."`)
return return
} }

View File

@ -1,4 +1,3 @@
import execa from "execa"
import which from "which" import which from "which"
let _issudo: boolean | undefined = undefined let _issudo: boolean | undefined = undefined
@ -18,11 +17,3 @@ export function mightSudo(command: string) {
} }
return command return command
} }
export function execaSudo(file: string, args: string[], cwd?: string) {
if (isRoot()) {
return execa.command(`sudo ${[file, ...args].map((arg) => `'${arg}'`).join(" ")}`, { shell: true, cwd })
} else {
return execa(file, args)
}
}

View File

@ -0,0 +1,22 @@
import execa from "execa"
import which from "which"
let powershell: string | undefined
export function execPowershell(command: string) {
if (powershell === undefined) {
const maybePwsh = which.sync("pwsh", { nothrow: true })
if (maybePwsh !== null) {
powershell = maybePwsh
}
const maybePowerShell = which.sync("powershell", { nothrow: true })
if (maybePowerShell !== null) {
powershell = maybePowerShell
}
}
if (powershell === undefined) {
throw new Error("Could not find powershell")
}
execa.sync(`${powershell} -C "${command}"`)
}

14
src/utils/exec/sudo.ts Normal file
View File

@ -0,0 +1,14 @@
import execa from "execa"
import { isRoot } from "../env/sudo"
export function execSudo(file: string, args: string[], cwd?: string) {
if (isRoot()) {
return execa.command(`sudo ${[file, ...args].map((arg) => `'${arg}'`).join(" ")}`, {
shell: true,
cwd,
stdio: "inherit",
})
} else {
return execa(file, args)
}
}

View File

@ -1,14 +1,15 @@
import { addPath as ghAddPath } from "@actions/core" import { addPath as ghAddPath } from "@actions/core"
import { delimiter } from "path" import { delimiter } from "path"
import * as core from "@actions/core" import * as core from "@actions/core"
import execa from "execa"
import { isGitHubCI } from "../env/isci" import { isGitHubCI } from "../env/isci"
import { untildify_user as untildify } from "./untildify" import { untildify_user as untildify } from "./untildify"
import { appendFileSync } from "fs" import { appendFileSync } from "fs"
import { error } from "../io/io" import { error } from "../io/io"
import { execPowershell } from "../exec/powershell"
/** An add path function that works locally or inside GitHub Actions */ /** An add path function that works locally or inside GitHub Actions */
export function addPath(path: string) { export function addPath(path: string) {
process.env.PATH = `${path}${delimiter}${process.env.PATH}`
try { try {
if (isGitHubCI()) { if (isGitHubCI()) {
ghAddPath(path) ghAddPath(path)
@ -29,11 +30,10 @@ export function addPath(path: string) {
function addPathSystem(path: string) { function addPathSystem(path: string) {
switch (process.platform) { switch (process.platform) {
case "win32": { case "win32": {
if (`${path};${process.env.PATH}`.length <= 1024) { // We do not use `execa.sync(`setx PATH "${path};%PATH%"`)` because of its character limit and also because %PATH% is different for user and system
execa.sync(`setx PATH "${path};%PATH%"`) execPowershell(
} else { `$USER_PATH=[Environment]::GetEnvironmentVariable('PATH', 'User'); [Environment]::SetEnvironmentVariable('PATH', \\"${path};$USER_PATH\\", 'User')`
execa.sync(`powershell -C "[Environment]::SetEnvironmentVariable('PATH', \\"${path};$env:PATH\\", 'User')"`) )
}
core.info(`${path} was added to the PATH.`) core.info(`${path} was added to the PATH.`)
return return
} }
@ -45,8 +45,7 @@ function addPathSystem(path: string) {
return return
} }
default: { default: {
// fall through shell path modification return
} }
} }
process.env.PATH = `${path}${delimiter}${process.env.PATH}`
} }

View File

@ -7,7 +7,7 @@ export async function extractExe(file: string, dest: string) {
return dest return dest
} }
export async function extractTarByExe(file: string, dest: string, flags = ["--strip-components=1"]) { export async function extractTarByExe(file: string, dest: string, flags = ["--strip-components=0"]) {
try { try {
await mkdirP(dest) await mkdirP(dest)
} catch { } catch {

View File

@ -1,6 +1,6 @@
/* eslint-disable require-atomic-updates */ /* eslint-disable require-atomic-updates */
import { InstallationInfo } from "./setupBin" import { InstallationInfo } from "./setupBin"
import { execaSudo } from "../env/sudo" import { execSudo } from "../exec/sudo"
let didUpdate: boolean = false let didUpdate: boolean = false
let didInit: boolean = false let didInit: boolean = false
@ -16,7 +16,7 @@ export async function setupAptPack(
process.env.DEBIAN_FRONTEND = "noninteractive" process.env.DEBIAN_FRONTEND = "noninteractive"
if (!didUpdate) { if (!didUpdate) {
await execaSudo(apt, ["update", "-y"]) await execSudo(apt, ["update", "-y"])
didUpdate = true didUpdate = true
} }
@ -25,7 +25,7 @@ export async function setupAptPack(
// set time - zone // set time - zone
// TZ = Canada / Pacific // TZ = Canada / Pacific
// ln - snf / usr / share / zoneinfo / $TZ / etc / localtime && echo $TZ > /etc/timezone // ln - snf / usr / share / zoneinfo / $TZ / etc / localtime && echo $TZ > /etc/timezone
await execaSudo(apt, [ await execSudo(apt, [
"install", "install",
"--fix-broken", "--fix-broken",
"-y", "-y",
@ -40,19 +40,19 @@ export async function setupAptPack(
if (Array.isArray(repositories)) { if (Array.isArray(repositories)) {
for (const repo of repositories) { for (const repo of repositories) {
// eslint-disable-next-line no-await-in-loop // eslint-disable-next-line no-await-in-loop
await execaSudo("add-apt-repository", ["--update", "-y", repo]) await execSudo("add-apt-repository", ["--update", "-y", repo])
} }
await execaSudo(apt, ["update", "-y"]) await execSudo(apt, ["update", "-y"])
} }
if (version !== undefined && version !== "") { if (version !== undefined && version !== "") {
try { try {
await execaSudo(apt, ["install", "--fix-broken", "-y", `${name}=${version}`]) await execSudo(apt, ["install", "--fix-broken", "-y", `${name}=${version}`])
} catch { } catch {
await execaSudo(apt, ["install", "--fix-broken", "-y", `${name}-${version}`]) await execSudo(apt, ["install", "--fix-broken", "-y", `${name}-${version}`])
} }
} else { } else {
await execaSudo(apt, ["install", "--fix-broken", "-y", name]) await execSudo(apt, ["install", "--fix-broken", "-y", name])
} }
return { binDir: "/usr/bin/" } return { binDir: "/usr/bin/" }

View File

@ -41,15 +41,17 @@ export type InstallationInfo = {
export async function setupBin( export async function setupBin(
name: string, name: string,
version: string, version: string,
getPackageInfo: (version: string, platform: NodeJS.Platform) => PackageInfo | Promise<PackageInfo>, getPackageInfo: (version: string, platform: NodeJS.Platform, arch: string) => PackageInfo | Promise<PackageInfo>,
setupDir: string setupDir: string,
arch: string
): Promise<InstallationInfo> { ): Promise<InstallationInfo> {
process.env.RUNNER_TEMP = process.env.RUNNER_TEMP ?? tmpdir() process.env.RUNNER_TEMP = process.env.RUNNER_TEMP ?? tmpdir()
process.env.RUNNER_TOOL_CACHE = process.env.RUNNER_TOOL_CACH ?? join(tmpdir(), "setup-cpp", "ToolCache") process.env.RUNNER_TOOL_CACHE = process.env.RUNNER_TOOL_CACHE ?? join(tmpdir(), "setup-cpp", "hostedtoolcache")
const { url, binRelativeDir, binFileName, extractedFolderName, extractFunction } = await getPackageInfo( const { url, binRelativeDir, binFileName, extractedFolderName, extractFunction } = await getPackageInfo(
version, version,
process.platform process.platform,
arch
) )
// Restore from cache (if found). // Restore from cache (if found).
@ -60,7 +62,7 @@ export async function setupBin(
const installDir = join(dir, extractedFolderName) const installDir = join(dir, extractedFolderName)
const binDir = join(installDir, binRelativeDir) const binDir = join(installDir, binRelativeDir)
if (existsSync(binDir) && existsSync(join(binDir, binFileName))) { if (existsSync(binDir) && existsSync(join(binDir, binFileName))) {
info(`${name} ${version} was found in the cache.`) info(`${name} ${version} was found in the cache at ${binDir}.`)
addPath(binDir) addPath(binDir)
return { installDir, binDir } return { installDir, binDir }
} }
@ -85,8 +87,12 @@ export async function setupBin(
await setupAptPack("xz-utils") await setupAptPack("xz-utils")
} }
const downloaded = await downloadTool(url) try {
await extractFunction?.(downloaded, setupDir) const downloaded = await downloadTool(url)
await extractFunction?.(downloaded, setupDir)
} catch (err) {
throw new Error(`Failed to download ${name} ${version} ${arch}: ${err}`)
}
} }
// Adding the bin dir to the path // Adding the bin dir to the path

View File

@ -14,7 +14,9 @@ export function setupBrewPack(name: string, version?: string): InstallationInfo
} }
// brew is not thread-safe // brew is not thread-safe
execa.sync("brew", ["install", version !== undefined && version !== "" ? `${name}@${version}` : name]) execa.sync("brew", ["install", version !== undefined && version !== "" ? `${name}@${version}` : name], {
stdio: "inherit",
})
return { binDir: "/usr/local/bin/" } return { binDir: "/usr/local/bin/" }
} }

View File

@ -28,7 +28,7 @@ export function setupChocoPack(name: string, version?: string, args: string[] =
execa.sync("choco", ["install", "-y", name, ...args], { env, extendEnv: false }) execa.sync("choco", ["install", "-y", name, ...args], { env, extendEnv: false })
} }
const binDir = "C:/ProgramData/Chocolatey/bin/" const binDir = `${process.env.ChocolateyInstall ?? "C:/ProgramData/chocolatey"}/bin`
addPath(binDir) addPath(binDir)
return { binDir } return { binDir }
} }

View File

@ -4,6 +4,7 @@ import * as path from "path"
import { addBinExtension } from "../extension/extension" import { addBinExtension } from "../extension/extension"
import { join } from "path" import { join } from "path"
import spawn from "cross-spawn" import spawn from "cross-spawn"
import { existsSync } from "fs"
export async function setupTmpDir(testName: string) { export async function setupTmpDir(testName: string) {
const tempDirectory = path.join(tmpdir(), "setup-cpp", testName) const tempDirectory = path.join(tmpdir(), "setup-cpp", testName)
@ -38,10 +39,12 @@ export async function testBin(
if (typeof binDir === "string") { if (typeof binDir === "string") {
expect(binDir).toBeDefined() expect(binDir).toBeDefined()
expect(binDir).not.toHaveLength(0) expect(binDir).not.toHaveLength(0)
expect(existsSync(binDir)).toBeTruthy()
bin = join(binDir, addBinExtension(name)) bin = join(binDir, addBinExtension(name))
} }
if (args !== null) { if (args !== null) {
console.log(`Running ${bin} ${args.join(" ")}`)
const { status } = spawn.sync(bin, args, { stdio: "inherit" }) const { status } = spawn.sync(bin, args, { stdio: "inherit" })
expect(status).toBe(0) expect(status).toBe(0)
} }