2024-08-15 09:22:33 +08:00
|
|
|
import { promises } from "fs"
|
|
|
|
import { delimiter } from "path"
|
|
|
|
import { addPath as ghAddPath } from "@actions/core"
|
|
|
|
import { GITHUB_ACTIONS } from "ci-info"
|
|
|
|
import { error, info } from "ci-log"
|
|
|
|
import { execPowershell } from "exec-powershell"
|
2024-08-15 09:43:53 +08:00
|
|
|
import { defaultRcPath, sourceRCInRc } from "./rc-file.js"
|
2024-08-15 09:22:33 +08:00
|
|
|
const { appendFile } = promises
|
|
|
|
|
2024-08-16 17:37:02 +08:00
|
|
|
/**
|
|
|
|
* The options for adding a PATH variable
|
|
|
|
*/
|
2024-08-15 09:22:33 +08:00
|
|
|
type AddPathOptions = {
|
|
|
|
/**
|
|
|
|
* The path to the RC file that the PATH variables should be added to.
|
|
|
|
*/
|
|
|
|
rcPath: string
|
2024-08-15 09:43:53 +08:00
|
|
|
/** Provide a name (your tool) to add a variable guard for sourcing your rc file */
|
|
|
|
guard?: string
|
2024-08-15 09:22:33 +08:00
|
|
|
}
|
|
|
|
/**
|
|
|
|
* Add a path to the PATH environment variable.
|
|
|
|
*
|
|
|
|
* This function is cross-platforms and works in all the local or CI systems.
|
|
|
|
*/
|
|
|
|
|
|
|
|
export async function addPath(path: string, givenOptions: Partial<AddPathOptions> = {}) {
|
2024-08-15 09:43:53 +08:00
|
|
|
const options = { rcPath: defaultRcPath, ...givenOptions }
|
2024-08-15 09:22:33 +08:00
|
|
|
|
|
|
|
if (isIgnoredPath(path)) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
process.env.PATH = `${path}${delimiter}${process.env.PATH}`
|
|
|
|
try {
|
|
|
|
if (GITHUB_ACTIONS) {
|
|
|
|
try {
|
|
|
|
ghAddPath(path)
|
|
|
|
} catch (err) {
|
|
|
|
error(err as Error)
|
2024-08-15 09:43:53 +08:00
|
|
|
await addPathSystem(path, options)
|
2024-08-15 09:22:33 +08:00
|
|
|
}
|
|
|
|
} else {
|
2024-08-15 09:43:53 +08:00
|
|
|
await addPathSystem(path, options)
|
2024-08-15 09:22:33 +08:00
|
|
|
}
|
|
|
|
} catch (err) {
|
|
|
|
error(`${err}\nFailed to add ${path} to the percistent PATH. You should add it manually.`)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-08-15 09:43:53 +08:00
|
|
|
async function addPathSystem(path: string, options: AddPathOptions) {
|
2024-08-15 09:22:33 +08:00
|
|
|
switch (process.platform) {
|
|
|
|
case "win32": {
|
|
|
|
// We do not use `execaSync(`setx PATH "${path};%PATH%"`)` because of its character limit and also because %PATH% is different for user and system
|
|
|
|
await execPowershell(
|
|
|
|
`$USER_PATH=([Environment]::GetEnvironmentVariable("PATH", "User")); [Environment]::SetEnvironmentVariable("PATH", "${path};$USER_PATH", "User")`,
|
|
|
|
)
|
|
|
|
info(`"${path}" was added to the PATH.`)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
case "linux":
|
|
|
|
case "darwin": {
|
2024-08-15 09:43:53 +08:00
|
|
|
await sourceRCInRc(options)
|
|
|
|
await appendFile(options.rcPath, `\nexport PATH="${path}:$PATH"\n`)
|
|
|
|
info(`"${path}" was added to "${options.rcPath}"`)
|
2024-08-15 09:22:33 +08:00
|
|
|
return
|
|
|
|
}
|
|
|
|
default: {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const ignoredPaths = [/\/usr\/bin\/?/, /\/usr\/local\/bin\/?/]
|
|
|
|
|
|
|
|
/** Skip adding /usr/bin to PATH if it is already there */
|
|
|
|
function isIgnoredPath(path: string) {
|
|
|
|
if (ignoredPaths.some((checkedPath) => checkedPath.test(path))) {
|
|
|
|
const paths = process.env.PATH?.split(delimiter) ?? []
|
|
|
|
return paths.includes(path)
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|