mirror of https://github.com/aminya/setup-cpp
feat!: support addAptKey options through installAptPack
BREAKING no default keys are added during apt installations anymore. Explicitly pass the keys needed. BREAKING the arguments to addAptKeyViaServer and addAptKeyViaDownload has changed. addAptKeyViaDownload renamed to addAptKeyViaURL
This commit is contained in:
parent
12442d6b61
commit
19bf09e888
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -135,7 +135,7 @@ Add an apt key via a keyserver
|
||||||
|
|
||||||
**returns:** Promise<string>
|
**returns:** Promise<string>
|
||||||
|
|
||||||
### `addAptKeyViaDownload` (function)
|
### `addAptKeyViaURL` (function)
|
||||||
|
|
||||||
Add an apt key via a download
|
Add an apt key via a download
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "setup-apt",
|
"name": "setup-apt",
|
||||||
"version": "1.0.1",
|
"version": "2.0.0",
|
||||||
"description": "Setup apt packages and repositories in Debian/Ubuntu-based distributions",
|
"description": "Setup apt packages and repositories in Debian/Ubuntu-based distributions",
|
||||||
"repository": "https://github.com/aminya/setup-cpp",
|
"repository": "https://github.com/aminya/setup-cpp",
|
||||||
"homepage": "https://github.com/aminya/setup-cpp/tree/master/packages/setup-apt",
|
"homepage": "https://github.com/aminya/setup-cpp/tree/master/packages/setup-apt",
|
||||||
|
|
|
@ -1,25 +1,78 @@
|
||||||
import { tmpdir } from "os"
|
import { tmpdir } from "os"
|
||||||
|
import { join } from "path"
|
||||||
import { execRoot, execRootSync } from "admina"
|
import { execRoot, execRootSync } from "admina"
|
||||||
import { warning } from "ci-log"
|
import { warning } from "ci-log"
|
||||||
import { DownloaderHelper } from "node-downloader-helper"
|
import { DownloaderHelper } from "node-downloader-helper"
|
||||||
import { pathExists } from "path-exists"
|
import { pathExists } from "path-exists"
|
||||||
import { installAptPack } from "./install.js"
|
import { installAptPack } from "./install.js"
|
||||||
|
|
||||||
function initGpg() {
|
export type AddAptKeyOptions = KeyServerOptions | KeyUrl
|
||||||
execRootSync("gpg", ["-k"])
|
|
||||||
|
/**
|
||||||
|
* Add an apt key
|
||||||
|
* @param options The options for adding the key
|
||||||
|
* @returns The file name of the key that was added or `undefined` if it failed
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* ```ts
|
||||||
|
* await addAptKey({ keys: ["3B4FE6ACC0B21F32", "40976EAF437D05B5"], fileName: "bazel-archive-keyring.gpg"})
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* ```ts
|
||||||
|
* await addAptKey({ keyUrl: "https://bazel.build/bazel-release.pub.gpg", fileName: "bazel-archive-keyring.gpg"})
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
export function addAptKey(options: AddAptKeyOptions) {
|
||||||
|
if ("keyUrl" in options) {
|
||||||
|
return addAptKeyViaURL(options)
|
||||||
|
} else {
|
||||||
|
return addAptKeyViaServer(options)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type GpgKeyOptions = {
|
||||||
|
/**
|
||||||
|
* The file name for the key (should end in `.gpg`)
|
||||||
|
*/
|
||||||
|
fileName: string
|
||||||
|
/**
|
||||||
|
* The key store path (Defaults to `/etc/apt/trusted.gpg.d`)
|
||||||
|
*/
|
||||||
|
keyStorePath?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export const defaultKeyStorePath = "/etc/apt/trusted.gpg.d"
|
||||||
|
|
||||||
|
export type KeyServerOptions = {
|
||||||
|
/**
|
||||||
|
* The keys to add
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* ```ts
|
||||||
|
* ["3B4FE6ACC0B21F32", "40976EAF437D05B5"]
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
keys: string[]
|
||||||
|
/**
|
||||||
|
* The keyserver to use (Defaults to `keyserver.ubuntu.com`)
|
||||||
|
*/
|
||||||
|
keyServer?: string
|
||||||
|
} & GpgKeyOptions
|
||||||
|
|
||||||
|
export const defaultKeyServer = "keyserver.ubuntu.com"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add an apt key via a keyserver
|
* Add an apt key via a keyserver
|
||||||
* @param keys The keys to add
|
|
||||||
* @param name The name of the key
|
|
||||||
* @param server The keyserver to use (Defaults to `keyserver.ubuntu.com`)
|
|
||||||
* @returns The file name of the key that was added or `undefined` if it failed
|
* @returns The file name of the key that was added or `undefined` if it failed
|
||||||
*/
|
*/
|
||||||
export async function addAptKeyViaServer(keys: string[], name: string, server = "keyserver.ubuntu.com") {
|
export async function addAptKeyViaServer(
|
||||||
|
{ keys, keyServer = defaultKeyServer, fileName, keyStorePath = defaultKeyServer }: KeyServerOptions,
|
||||||
|
) {
|
||||||
try {
|
try {
|
||||||
const fileName = `/etc/apt/trusted.gpg.d/${name}`
|
assertGpgFileName(fileName)
|
||||||
if (!(await pathExists(fileName))) {
|
const filePath = join(keyStorePath, fileName)
|
||||||
|
if (!(await pathExists(filePath))) {
|
||||||
initGpg()
|
initGpg()
|
||||||
|
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
|
@ -27,43 +80,73 @@ export async function addAptKeyViaServer(keys: string[], name: string, server =
|
||||||
await execRoot("gpg", [
|
await execRoot("gpg", [
|
||||||
"--no-default-keyring",
|
"--no-default-keyring",
|
||||||
"--keyring",
|
"--keyring",
|
||||||
`gnupg-ring:${fileName}`,
|
`gnupg-ring:${filePath}`,
|
||||||
"--keyserver",
|
"--keyserver",
|
||||||
server,
|
keyServer,
|
||||||
"--recv-keys",
|
"--recv-keys",
|
||||||
key,
|
key,
|
||||||
])
|
])
|
||||||
await execRoot("chmod", ["644", fileName])
|
await execRoot("chmod", ["644", filePath])
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
return fileName
|
return filePath
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
warning(`Failed to add apt key via server ${server}: ${err}`)
|
warning(`Failed to add apt key via server ${keyServer}: ${err}`)
|
||||||
return undefined
|
return undefined
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type KeyUrl = {
|
||||||
|
/**
|
||||||
|
* The URL to download the key from
|
||||||
|
*/
|
||||||
|
keyUrl: string
|
||||||
|
} & GpgKeyOptions
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add an apt key via a download
|
* Add an apt key via a download
|
||||||
* @param name The name of the key
|
* @param options The options for adding the key
|
||||||
* @param url The URL of the key
|
|
||||||
* @returns The file name of the key that was added
|
* @returns The file name of the key that was added
|
||||||
*/
|
*/
|
||||||
export async function addAptKeyViaDownload(name: string, url: string) {
|
export async function addAptKeyViaURL({ keyUrl, fileName, keyStorePath = defaultKeyStorePath }: KeyUrl) {
|
||||||
const fileName = `/etc/apt/trusted.gpg.d/${name}`
|
try {
|
||||||
if (!(await pathExists(fileName))) {
|
assertGpgFileName(fileName)
|
||||||
|
const filePath = join(keyStorePath, fileName)
|
||||||
|
if (!(await pathExists(filePath))) {
|
||||||
initGpg()
|
initGpg()
|
||||||
|
|
||||||
await installAptPack([{ name: "ca-certificates" }])
|
await installAptPack([{ name: "ca-certificates" }])
|
||||||
const dl = new DownloaderHelper(url, tmpdir(), { fileName: name })
|
|
||||||
|
const dlPath = join(tmpdir(), fileName)
|
||||||
|
const dl = new DownloaderHelper(keyUrl, tmpdir(), { fileName })
|
||||||
dl.on("error", (err) => {
|
dl.on("error", (err) => {
|
||||||
throw new Error(`Failed to download ${url}: ${err}`)
|
throw new Error(`Failed to download ${keyUrl}: ${err}`)
|
||||||
})
|
})
|
||||||
await dl.start()
|
await dl.start()
|
||||||
|
|
||||||
execRootSync("gpg", ["--no-default-keyring", "--keyring", `gnupg-ring:${fileName}`, "--import", `/tmp/${name}`])
|
execRootSync("gpg", [
|
||||||
execRootSync("chmod", ["644", fileName])
|
"--no-default-keyring",
|
||||||
|
"--keyring",
|
||||||
|
`gnupg-ring:${filePath}`,
|
||||||
|
"--import",
|
||||||
|
dlPath,
|
||||||
|
])
|
||||||
|
execRootSync("chmod", ["644", filePath])
|
||||||
|
}
|
||||||
|
return filePath
|
||||||
|
} catch (err) {
|
||||||
|
warning(`Failed to add apt key via download ${keyUrl}: ${err}`)
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function initGpg() {
|
||||||
|
execRootSync("gpg", ["-k"])
|
||||||
|
}
|
||||||
|
|
||||||
|
function assertGpgFileName(fileName: string) {
|
||||||
|
if (!fileName.endsWith(".gpg")) {
|
||||||
|
throw new Error(`Key file name must end with .gpg: ${fileName}`)
|
||||||
}
|
}
|
||||||
return fileName
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { info, warning } from "ci-log"
|
||||||
import escapeRegex from "escape-string-regexp"
|
import escapeRegex from "escape-string-regexp"
|
||||||
import { type ExecaError, execa } from "execa"
|
import { type ExecaError, execa } from "execa"
|
||||||
import which from "which"
|
import which from "which"
|
||||||
import { addAptKeyViaServer } from "./apt-key.js"
|
import { type AddAptKeyOptions, addAptKey } from "./apt-key.js"
|
||||||
import { isAptPackInstalled } from "./is-installed.js"
|
import { isAptPackInstalled } from "./is-installed.js"
|
||||||
import { updateAptRepos } from "./update.js"
|
import { updateAptRepos } from "./update.js"
|
||||||
|
|
||||||
|
@ -40,6 +40,8 @@ export type AptPackage = {
|
||||||
version?: string
|
version?: string
|
||||||
/** The repositories to add before installing the package (optional) */
|
/** The repositories to add before installing the package (optional) */
|
||||||
repositories?: string[]
|
repositories?: string[]
|
||||||
|
/** The keys to add before installing the package (optional) */
|
||||||
|
addAptKey?: AddAptKeyOptions[]
|
||||||
}
|
}
|
||||||
|
|
||||||
const retryErrors = [
|
const retryErrors = [
|
||||||
|
@ -83,8 +85,11 @@ export async function installAptPack(packages: AptPackage[], update = false): Pr
|
||||||
didInit = true
|
didInit = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Install
|
|
||||||
try {
|
try {
|
||||||
|
// Add the keys if needed
|
||||||
|
await addAptKeys(packages)
|
||||||
|
|
||||||
|
// Install
|
||||||
execRootSync(apt, ["install", "--fix-broken", "-y", ...needToInstall], {
|
execRootSync(apt, ["install", "--fix-broken", "-y", ...needToInstall], {
|
||||||
...defaultExecOptions,
|
...defaultExecOptions,
|
||||||
env: getAptEnv(apt),
|
env: getAptEnv(apt),
|
||||||
|
@ -107,6 +112,14 @@ export async function installAptPack(packages: AptPackage[], update = false): Pr
|
||||||
return { binDir: "/usr/bin/" }
|
return { binDir: "/usr/bin/" }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function addAptKeys(packages: AptPackage[]) {
|
||||||
|
await Promise.all(packages.map(async (pack) => {
|
||||||
|
if (pack.addAptKey !== undefined) {
|
||||||
|
await Promise.all(pack.addAptKey.map(addAptKey))
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
function isExecaError(err: unknown): err is ExecaError {
|
function isExecaError(err: unknown): err is ExecaError {
|
||||||
return typeof (err as ExecaError).stderr === "string"
|
return typeof (err as ExecaError).stderr === "string"
|
||||||
}
|
}
|
||||||
|
@ -285,9 +298,4 @@ async function initApt(apt: string) {
|
||||||
env: getAptEnv(apt),
|
env: getAptEnv(apt),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
await Promise.all([
|
|
||||||
addAptKeyViaServer(["3B4FE6ACC0B21F32", "40976EAF437D05B5"], "setup-cpp-ubuntu-archive.gpg"),
|
|
||||||
addAptKeyViaServer(["1E9377A2BA9EF27F"], "launchpad-toolchain.gpg"),
|
|
||||||
])
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,7 +28,8 @@
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"path-exists": "5.0.0",
|
"path-exists": "5.0.0",
|
||||||
"patha": "0.4.1"
|
"patha": "0.4.1",
|
||||||
|
"cross-spawn": "7.0.3"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
|
|
|
@ -346,6 +346,9 @@ importers:
|
||||||
specifier: 4.0.0
|
specifier: 4.0.0
|
||||||
version: 4.0.0
|
version: 4.0.0
|
||||||
devDependencies:
|
devDependencies:
|
||||||
|
cross-spawn:
|
||||||
|
specifier: 7.0.3
|
||||||
|
version: 7.0.3
|
||||||
path-exists:
|
path-exists:
|
||||||
specifier: 5.0.0
|
specifier: 5.0.0
|
||||||
version: 5.0.0
|
version: 5.0.0
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { execRootSync } from "admina"
|
import { execRootSync } from "admina"
|
||||||
import { addAptKeyViaDownload, installAptPack } from "setup-apt"
|
import { addAptKeyViaURL, installAptPack } from "setup-apt"
|
||||||
import { installBrewPack } from "setup-brew"
|
import { installBrewPack } from "setup-brew"
|
||||||
import { hasDnf } from "../utils/env/hasDnf.js"
|
import { hasDnf } from "../utils/env/hasDnf.js"
|
||||||
import { isArch } from "../utils/env/isArch.js"
|
import { isArch } from "../utils/env/isArch.js"
|
||||||
|
@ -28,10 +28,10 @@ export async function setupBazel(version: string, _setupDir: string, _arch: stri
|
||||||
return setupDnfPack([{ name: "bazel4" }])
|
return setupDnfPack([{ name: "bazel4" }])
|
||||||
} else if (isUbuntu()) {
|
} else if (isUbuntu()) {
|
||||||
// https://bazel.build/install/ubuntu
|
// https://bazel.build/install/ubuntu
|
||||||
const keyFileName = await addAptKeyViaDownload(
|
const keyFileName = await addAptKeyViaURL({
|
||||||
"bazel-archive-keyring.gpg",
|
fileName: "bazel-archive-keyring.gpg",
|
||||||
"https://bazel.build/bazel-release.pub.gpg",
|
keyUrl: "https://bazel.build/bazel-release.pub.gpg",
|
||||||
)
|
})
|
||||||
execRootSync("bash", [
|
execRootSync("bash", [
|
||||||
"-c",
|
"-c",
|
||||||
`echo "deb [arch=amd64 signed-by=${keyFileName}] https://storage.googleapis.com/bazel-apt stable jdk1.8" | tee /etc/apt/sources.list.d/bazel.list`,
|
`echo "deb [arch=amd64 signed-by=${keyFileName}] https://storage.googleapis.com/bazel-apt stable jdk1.8" | tee /etc/apt/sources.list.d/bazel.list`,
|
||||||
|
|
|
@ -111,8 +111,18 @@ export async function setupGcc(version: string, setupDir: string, arch: string,
|
||||||
])
|
])
|
||||||
} else if (isUbuntu()) {
|
} else if (isUbuntu()) {
|
||||||
installationInfo = await installAptPack([
|
installationInfo = await installAptPack([
|
||||||
{ name: "gcc", version, repositories: ["ppa:ubuntu-toolchain-r/test"] },
|
{
|
||||||
{ name: "g++", version, repositories: ["ppa:ubuntu-toolchain-r/test"] },
|
name: "gcc",
|
||||||
|
version,
|
||||||
|
repositories: ["ppa:ubuntu-toolchain-r/test"],
|
||||||
|
addAptKey: [{ keys: ["1E9377A2BA9EF27F"], fileName: "ubuntu-toolchain-r-test.gpg" }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "g++",
|
||||||
|
version,
|
||||||
|
repositories: ["ppa:ubuntu-toolchain-r/test"],
|
||||||
|
addAptKey: [{ keys: ["1E9377A2BA9EF27F"], fileName: "ubuntu-toolchain-r-test.gpg" }],
|
||||||
|
},
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -120,7 +130,12 @@ export async function setupGcc(version: string, setupDir: string, arch: string,
|
||||||
if (isArch()) {
|
if (isArch()) {
|
||||||
await setupPacmanPack("gcc-multilib", version)
|
await setupPacmanPack("gcc-multilib", version)
|
||||||
} else if (isUbuntu()) {
|
} else if (isUbuntu()) {
|
||||||
await installAptPack([{ name: "gcc-multilib", version, repositories: ["ppa:ubuntu-toolchain-r/test"] }])
|
await installAptPack([{
|
||||||
|
name: "gcc-multilib",
|
||||||
|
version,
|
||||||
|
repositories: ["ppa:ubuntu-toolchain-r/test"],
|
||||||
|
addAptKey: [{ keys: ["1E9377A2BA9EF27F"], fileName: "ubuntu-toolchain-r-test.gpg" }],
|
||||||
|
}])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
|
@ -162,7 +177,12 @@ export async function setupMingw(version: string, setupDir: string, arch: string
|
||||||
installationInfo = await setupDnfPack([{ name: "mingw64-gcc", version }])
|
installationInfo = await setupDnfPack([{ name: "mingw64-gcc", version }])
|
||||||
} else if (isUbuntu()) {
|
} else if (isUbuntu()) {
|
||||||
installationInfo = await installAptPack([
|
installationInfo = await installAptPack([
|
||||||
{ name: "mingw-w64", version, repositories: ["ppa:ubuntu-toolchain-r/test"] },
|
{
|
||||||
|
name: "mingw-w64",
|
||||||
|
version,
|
||||||
|
repositories: ["ppa:ubuntu-toolchain-r/test"],
|
||||||
|
addAptKey: [{ keys: ["1E9377A2BA9EF27F"], fileName: "ubuntu-toolchain-r-test.gpg" }],
|
||||||
|
},
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { execRootSync } from "admina"
|
import { execRootSync } from "admina"
|
||||||
import { dirname } from "patha"
|
import { dirname } from "patha"
|
||||||
import { addAptKeyViaDownload, hasNala, installAptPack } from "setup-apt"
|
import { addAptKeyViaURL, hasNala, installAptPack } from "setup-apt"
|
||||||
import which from "which"
|
import which from "which"
|
||||||
import { isUbuntu } from "../utils/env/isUbuntu.js"
|
import { isUbuntu } from "../utils/env/isUbuntu.js"
|
||||||
|
|
||||||
|
@ -24,10 +24,10 @@ export async function setupNala(version: string, _setupDir: string, _arch: strin
|
||||||
await installAptPack([{ name: "python3-apt" }])
|
await installAptPack([{ name: "python3-apt" }])
|
||||||
|
|
||||||
// https://gitlab.com/volian/nala/-/wikis/Installation
|
// https://gitlab.com/volian/nala/-/wikis/Installation
|
||||||
const keyFileName = await addAptKeyViaDownload(
|
const keyFileName = await addAptKeyViaURL({
|
||||||
"volian-archive-nala.gpg",
|
fileName: "volian-archive-nala.gpg",
|
||||||
"https://deb.volian.org/volian/nala.key",
|
keyUrl: "https://deb.volian.org/volian/nala.key",
|
||||||
)
|
})
|
||||||
execRootSync("/bin/bash", [
|
execRootSync("/bin/bash", [
|
||||||
"-c",
|
"-c",
|
||||||
`echo "deb [signed-by=${keyFileName}] http://deb.volian.org/volian/ nala main" | tee /etc/apt/sources.list.d/volian-archive-nala.list`,
|
`echo "deb [signed-by=${keyFileName}] http://deb.volian.org/volian/ nala main" | tee /etc/apt/sources.list.d/volian-archive-nala.list`,
|
||||||
|
|
|
@ -103,7 +103,7 @@ export async function setupPowershellSystem(version: string | undefined, _setupD
|
||||||
execRootSync("dpkg", ["-i", "packages-microsoft-prod.deb"])
|
execRootSync("dpkg", ["-i", "packages-microsoft-prod.deb"])
|
||||||
|
|
||||||
// TODO Debian
|
// TODO Debian
|
||||||
// const keyFileName = await addAptKeyViaDownload(
|
// const keyFileName = await addAptKeyViaURL(
|
||||||
// "microsoft.asc",
|
// "microsoft.asc",
|
||||||
// "https://packages.microsoft.com/keys/microsoft.asc"
|
// "https://packages.microsoft.com/keys/microsoft.asc"
|
||||||
// )
|
// )
|
||||||
|
|
|
@ -76,7 +76,7 @@ async function setupWheel(foundPython: string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function findOrSetupPython(version: string, setupDir: string, arch: string) {
|
async function findOrSetupPython(version: string, setupDir: string, arch: string): Promise<InstallationInfo> {
|
||||||
let installInfo: InstallationInfo | undefined
|
let installInfo: InstallationInfo | undefined
|
||||||
let foundPython = await findPython(setupDir)
|
let foundPython = await findPython(setupDir)
|
||||||
|
|
||||||
|
@ -108,12 +108,12 @@ async function findOrSetupPython(version: string, setupDir: string, arch: string
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (foundPython === undefined || installInfo.bin === undefined) {
|
if (foundPython === undefined || installInfo?.bin === undefined) {
|
||||||
foundPython = await findPython(setupDir)
|
foundPython = await findPython(setupDir)
|
||||||
if (foundPython === undefined) {
|
if (foundPython === undefined) {
|
||||||
throw new Error("Python binary could not be found")
|
throw new Error("Python binary could not be found")
|
||||||
}
|
}
|
||||||
installInfo.bin = foundPython
|
installInfo = { bin: foundPython, installDir: dirname(foundPython), binDir: dirname(foundPython) }
|
||||||
}
|
}
|
||||||
|
|
||||||
return installInfo
|
return installInfo
|
||||||
|
|
Loading…
Reference in New Issue