Merge pull request #269 from aminya/apt

This commit is contained in:
Amin Yahyaabadi 2024-08-29 16:38:30 -07:00 committed by GitHub
commit 56a945c190
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
36 changed files with 783 additions and 425 deletions

View File

@ -12,24 +12,20 @@ ignorePaths:
- .vscode/extensions.json
words:
- aarch
- hadolint
- aminya
- envosman
- applellvm
- biome
- biomejs
- bazel
- bazelisk
- biome
- biomejs
- buildtools
- caxa
- ccache
- choco
- clangd
- cmake
- iarna
- cobertura
- copr
- pnpx
- CPATH
- Cppcheck
- CPPFLAGS
@ -43,6 +39,7 @@ words:
- DVCPKG
- dyld
- eabi
- envosman
- esmodule
- execa
- ftxui
@ -50,6 +47,8 @@ words:
- gcovr
- ghes
- Graphviz
- hadolint
- iarna
- inja
- isci
- isroot
@ -61,6 +60,7 @@ words:
- libdw
- libstdc
- libtinfo
- memoizee
- msbuild
- msvc
- msvcrt
@ -79,6 +79,7 @@ words:
- patha
- pipx
- pnpm
- pnpx
- pwsh
- pygments
- pypy

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

View File

@ -73,6 +73,7 @@
"@types/eslint": "^8.56.11",
"@types/iarna__toml": "~2.0.5",
"@types/jest": "^29.5.12",
"@types/memoizee": "^0.4.11",
"@types/node": "^12.20.55",
"@types/semver": "^7.5.8",
"@types/which": "^3.0.4",
@ -86,10 +87,10 @@
"cspell": "^8.13.3",
"diagnostics_channel": "^1.1.0",
"dprint": "^0.47.2",
"envosman": "workspace:*",
"escape-path-with-spaces": "^1.0.2",
"escape-quotes": "^1.0.2",
"escape-string-regexp": "^5.0.0",
"setup-apt": "workspace:*",
"eslint": "^8.57.0",
"eslint-config-atomic": "^1.22.1",
"exec-powershell": "workspace:*",
@ -98,14 +99,14 @@
"jest": "^29.7.0",
"lefthook": "^1.7.12",
"macos-release": "^3.3.0",
"micro-memoize": "^4.1.2",
"memoizee": "^0.4.17",
"mkdirp": "^3.0.1",
"mri": "^1.2.0",
"msvc-dev-cmd": "github:aminya/msvc-dev-cmd#c01f519bd995460228ed3dec4df51df92dc290fd",
"node-downloader-helper": "2.1.9",
"npm-check-updates": "^17.0.6",
"npm-run-all2": "^6.2.2",
"numerous": "1.0.3",
"envosman": "workspace:*",
"p-timeout": "^6.1.2",
"parcel": "2.0.0-canary.1717",
"path-exists": "^5.0.0",
@ -115,8 +116,9 @@
"readme-md-generator": "^1.0.0",
"retry-as-promised": "^7.0.4",
"semver": "7.6.3",
"setup-python": "github:aminya/setup-python#a783db655c6e40317e2c0c96f9d162d9c9f4a751",
"setup-apt": "workspace:*",
"setup-brew": "workspace:*",
"setup-python": "github:aminya/setup-python#a783db655c6e40317e2c0c96f9d162d9c9f4a751",
"shx": "0.3.4",
"simple-update-notifier": "^2.0.0",
"terser-config-atomic": "^1.0.0",
@ -129,8 +131,7 @@
"untildify-user": "workspace:*",
"util.types": "^0.0.2",
"web-streams-polyfill": "^4.0.0",
"which": "^4.0.0",
"node-downloader-helper": "2.1.9"
"which": "^4.0.0"
},
"productionDependencies": [
"@actions/core",
@ -149,7 +150,7 @@
"exec-powershell",
"execa",
"is-url-online",
"micro-memoize",
"memoizee",
"mri",
"msvc-dev-cmd",
"node-downloader-helper",

View File

@ -23,7 +23,7 @@
"ci-info": "^4.0.0",
"escape-path-with-spaces": "^1.0.2",
"escape-quotes": "^1.0.2",
"micro-memoize": "^4.1.2",
"memoizee": "^0.4.17",
"path-exists": "^5.0.0",
"ci-log": "workspace:*",
"exec-powershell": "workspace:*",
@ -45,5 +45,7 @@
"unix",
"macos"
],
"devDependencies": {}
"devDependencies": {
"@types/memoizee": "0.4.11"
}
}

View File

@ -1,7 +1,7 @@
import { promises } from "fs"
import { grantUserWriteAccess } from "admina"
import { info, warning } from "ci-log"
import memoize from "micro-memoize"
import memoize from "memoizee"
import { pathExists } from "path-exists"
import { untildifyUser } from "untildify-user"
const { appendFile, readFile, writeFile } = promises
@ -38,7 +38,7 @@ async function sourceRCInRc_(options: RcOptions) {
/**
* handles adding conditions to source rc file from .bashrc and .profile
*/
export const sourceRCInRc = memoize(sourceRCInRc_, { isPromise: true })
export const sourceRCInRc = memoize(sourceRCInRc_, { promise: true })
async function addRCHeader(options: RcOptions) {
// a variable that prevents source rc from being called from .bashrc and .profile

View File

@ -100,6 +100,21 @@ Install a package using apt
**returns:** Promise<InstallationInfo>
```ts
await installAptPack([{ name: "ca-certificates" }, { name: "gnupg" }])
```
```ts
await installAptPack([
{
name: "gcc",
version,
repository: "ppa:ubuntu-toolchain-r/test",
key: { key: "1E9377A2BA9EF27F", fileName: "ubuntu-toolchain-r-test.gpg" },
},
])
```
### `hasNala` (function)
Check if nala is installed
@ -123,26 +138,58 @@ Get the environment variables to use for the apt command
**returns:** ProcessEnv
### `AddAptKeyOptions` (type)
### `addAptKey` (function)
Add an apt key
**Parameters:**
- options (`AddAptKeyOptions`) - The options for adding the key
**returns:** Promise<string>
```ts
await addAptKey({
key: "3B4FE6ACC0B21F32"
fileName: "bazel-archive-keyring.gpg",
})
```
```ts
await addAptKey({
keyUrl: "https://bazel.build/bazel-release.pub.gpg",
fileName: "bazel-archive-keyring.gpg",
})
```
### `defaultKeyStorePath` (variable)
### `KeyServerOptions` (type)
### `defaultKeyServer` (variable)
### `addAptKeyViaServer` (function)
Add an apt key via a keyserver
**Parameters:**
- keys (`string[]`) - The keys to add
- name (`string`) - The name of the key
- server (`string`) - The keyserver to use (Defaults to `keyserver.ubuntu.com`)
- { key, keyServer = defaultKeyServer, fileName, keyStorePath = defaultKeyServer } (`KeyServerOptions`)
**returns:** Promise<string>
### `addAptKeyViaDownload` (function)
### `KeyUrl` (type)
### `addAptKeyViaURL` (function)
Add an apt key via a download
**Parameters:**
- name (`string`) - The name of the key
- url (`string`) - The URL of the key
- options - The options for adding the key
- { keyUrl, fileName, keyStorePath = defaultKeyStorePath } (`KeyUrl`)
**returns:** Promise<string>

View File

@ -1,6 +1,6 @@
{
"name": "setup-apt",
"version": "1.0.1",
"version": "2.0.0",
"description": "Setup apt packages and repositories in Debian/Ubuntu-based distributions",
"repository": "https://github.com/aminya/setup-cpp",
"homepage": "https://github.com/aminya/setup-cpp/tree/master/packages/setup-apt",
@ -26,7 +26,8 @@
"which": "4.0.0",
"execa": "^7.2.0",
"escape-string-regexp": "^5.0.0",
"node-downloader-helper": "2.1.9"
"node-downloader-helper": "2.1.9",
"memoizee": "^0.4.17"
},
"engines": {
"node": ">=12"
@ -48,5 +49,7 @@
"package",
"apt-key"
],
"devDependencies": {}
"devDependencies": {
"@types/memoizee": "0.4.11"
}
}

View File

@ -0,0 +1,21 @@
/**
* Get the environment variables to use for the apt command
* @param apt The apt command to use
* @private Used internally
*/
export function getAptEnv(apt: string) {
const env: NodeJS.ProcessEnv = { ...process.env, DEBIAN_FRONTEND: "noninteractive" }
if (apt === "nala") {
// if LANG/LC_ALL is not set, enable utf8 otherwise nala fails because of ASCII encoding
if (env.LANG === undefined) {
env.LANG = "C.UTF-8"
}
if (env.LC_ALL === undefined) {
env.LC_ALL = "C.UTF-8"
}
}
return env
}

View File

@ -1,69 +1,148 @@
import { tmpdir } from "os"
import { join } from "path"
import { execRoot, execRootSync } from "admina"
import { warning } from "ci-log"
import { DownloaderHelper } from "node-downloader-helper"
import { pathExists } from "path-exists"
import { installAptPack } from "./install.js"
function initGpg() {
execRootSync("gpg", ["-k"])
export type AddAptKeyOptions = KeyServerOptions | KeyUrl
/**
* 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({ key: "3B4FE6ACC0B21F32" 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 key to add
*
* @example
* ```ts
* "3B4FE6ACC0B21F32"
* ```
*/
key: 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
* @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
*/
export async function addAptKeyViaServer(keys: string[], name: string, server = "keyserver.ubuntu.com") {
export async function addAptKeyViaServer(
{ key, keyServer = defaultKeyServer, fileName, keyStorePath = defaultKeyServer }: KeyServerOptions,
) {
try {
const fileName = `/etc/apt/trusted.gpg.d/${name}`
if (!(await pathExists(fileName))) {
assertGpgFileName(fileName)
const filePath = join(keyStorePath, fileName)
if (!(await pathExists(filePath))) {
initGpg()
await Promise.all(
keys.map(async (key) => {
await execRoot("gpg", [
"--no-default-keyring",
"--keyring",
`gnupg-ring:${fileName}`,
`gnupg-ring:${filePath}`,
"--keyserver",
server,
keyServer,
"--recv-keys",
key,
])
await execRoot("chmod", ["644", fileName])
}),
)
await execRoot("chmod", ["644", filePath])
}
return fileName
return filePath
} catch (err) {
warning(`Failed to add apt key via server ${server}: ${err}`)
warning(`Failed to add apt key via server ${keyServer}: ${err}`)
return undefined
}
}
export type KeyUrl = {
/**
* The URL to download the key from
*/
keyUrl: string
} & GpgKeyOptions
/**
* Add an apt key via a download
* @param name The name of the key
* @param url The URL of the key
* @param options The options for adding the key
* @returns The file name of the key that was added
*/
export async function addAptKeyViaDownload(name: string, url: string) {
const fileName = `/etc/apt/trusted.gpg.d/${name}`
if (!(await pathExists(fileName))) {
export async function addAptKeyViaURL({ keyUrl, fileName, keyStorePath = defaultKeyStorePath }: KeyUrl) {
try {
assertGpgFileName(fileName)
const filePath = join(keyStorePath, fileName)
if (!(await pathExists(filePath))) {
initGpg()
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) => {
throw new Error(`Failed to download ${url}: ${err}`)
throw new Error(`Failed to download ${keyUrl}: ${err}`)
})
await dl.start()
execRootSync("gpg", ["--no-default-keyring", "--keyring", `gnupg-ring:${fileName}`, "--import", `/tmp/${name}`])
execRootSync("chmod", ["644", fileName])
execRootSync("gpg", [
"--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
}

View File

@ -0,0 +1,28 @@
import { defaultExecOptions, execRootSync } from "admina"
import { getAptEnv } from "./apt-env.js"
import { aptTimeout } from "./apt-timeout.js"
import { getApt } from "./get-apt.js"
import { initAptMemoized } from "./init-apt.js"
import { isAptPackInstalled } from "./is-installed.js"
import { updateAptReposMemoized } from "./update.js"
export async function addAptRepository(repo: string, apt = getApt()) {
await initAptMemoized(apt)
await installAddAptRepo(apt)
execRootSync("add-apt-repository", ["-y", "--no-update", repo], { ...defaultExecOptions, env: getAptEnv(apt) })
// Update the repos
updateAptReposMemoized.clear() // ensure update is called
updateAptReposMemoized(apt)
}
export async function installAddAptRepo(apt: string) {
if (await isAptPackInstalled("software-properties-common")) {
return
}
execRootSync(
apt,
["install", "-y", "--fix-broken", "-o", aptTimeout, "software-properties-common"],
{ ...defaultExecOptions, env: getAptEnv(apt) },
)
}

View File

@ -0,0 +1,7 @@
/**
* The timeout to use for apt commands
* Wait up to 300 seconds if the apt-get lock is held
* @private Used internally
*/
export const aptTimeout = "Dpkg::Lock::Timeout=300"

View File

@ -0,0 +1,22 @@
import which from "which"
/**
* Check if nala is installed
*/
export function hasNala() {
return which.sync("nala", { nothrow: true }) !== null
}
/**
* Get the apt command to use
* If nala is installed, use that, otherwise use apt-get
*/
export function getApt() {
let apt: string
if (hasNala()) {
apt = "nala"
} else {
apt = "apt-get"
}
return apt
}

View File

@ -1,5 +1,9 @@
export * from "./alternatives.js"
export * from "./apt-key.js"
export * from "./apt-repository.js"
export * from "./apt-timeout.js"
export * from "./get-apt.js"
export * from "./init-apt.js"
export * from "./install.js"
export * from "./is-installed.js"
export * from "./update.js"

View File

@ -0,0 +1,28 @@
import { defaultExecOptions, execRootSync } from "admina"
import memoize from "memoizee"
import { getAptEnv } from "./apt-env.js"
import { aptTimeout } from "./apt-timeout.js"
import { filterAndQualifyAptPackages } from "./qualify-install.js"
import { updateAptReposMemoized } from "./update.js"
/** Install gnupg and certificates (usually missing from docker containers) */
export async function initApt(apt: string) {
// Update the repos
updateAptReposMemoized(apt)
const toInstall = await filterAndQualifyAptPackages(apt, [
{ name: "ca-certificates" },
{ name: "gnupg" },
{ name: "apt-utils" },
])
if (toInstall.length !== 0) {
execRootSync(apt, ["install", "-y", "--fix-broken", "-o", aptTimeout, ...toInstall], {
...defaultExecOptions,
env: getAptEnv(apt),
})
}
}
/** Install gnupg and certificates (usually missing from docker containers) (memoized) */
export const initAptMemoized = memoize(initApt, { promise: true })

View File

@ -1,11 +1,14 @@
import { defaultExecOptions, execRootSync } from "admina"
import { info, warning } from "ci-log"
import escapeRegex from "escape-string-regexp"
import { type ExecaError, execa } from "execa"
import which from "which"
import { addAptKeyViaServer } from "./apt-key.js"
import { isAptPackInstalled } from "./is-installed.js"
import { updateAptRepos } from "./update.js"
import type { ExecaError } from "execa"
import { getAptEnv } from "./apt-env.js"
import { type AddAptKeyOptions, addAptKey } from "./apt-key.js"
import { addAptRepository } from "./apt-repository.js"
import { aptTimeout } from "./apt-timeout.js"
import { getApt } from "./get-apt.js"
import { initAptMemoized } from "./init-apt.js"
import { filterAndQualifyAptPackages } from "./qualify-install.js"
import { updateAptReposMemoized } from "./update.js"
/**
* The information about an installation result
@ -19,17 +22,6 @@ export type InstallationInfo = {
bin?: string
}
/* eslint-disable require-atomic-updates */
let didUpdate: boolean = false
let didInit: boolean = false
/**
* The timeout to use for apt commands
* Wait up to 300 seconds if the apt-get lock is held
* @private Used internally
*/
export const aptTimeout = "Dpkg::Lock::Timeout=300"
/**
* The information about an apt package
*/
@ -38,8 +30,10 @@ export type AptPackage = {
name: string
/** The version of the package (optional) */
version?: string
/** The repositories to add before installing the package (optional) */
repositories?: string[]
/** The repository to add before installing the package (optional) */
repository?: string
/** The key to add before installing the package (optional) */
key?: AddAptKeyOptions
}
const retryErrors = [
@ -53,6 +47,25 @@ const retryErrors = [
*
* @param packages The packages to install (name, and optional info like version and repositories)
* @param update Whether to update the package list before installing (Defaults to `false`)
*
* @returns The installation information
*
* @example
* ```ts
* await installAptPack([{ name: "ca-certificates" }, { name: "gnupg" }])
* ```
*
* @example
* ```ts
await installAptPack([
{
name: "gcc",
version,
repository: "ppa:ubuntu-toolchain-r/test",
key: { key: "1E9377A2BA9EF27F", fileName: "ubuntu-toolchain-r-test.gpg" },
},
])
* ```
*/
export async function installAptPack(packages: AptPackage[], update = false): Promise<InstallationInfo> {
const apt: string = getApt()
@ -63,8 +76,7 @@ export async function installAptPack(packages: AptPackage[], update = false): Pr
// Update the repos if needed
if (update) {
updateAptRepos(apt)
didUpdate = true
updateAptReposMemoized(apt)
}
// Add the repos if needed
@ -78,13 +90,13 @@ export async function installAptPack(packages: AptPackage[], update = false): Pr
}
// Initialize apt if needed
if (!didInit) {
await initApt(apt)
didInit = true
}
await initAptMemoized(apt)
try {
// Add the keys if needed
await addAptKeys(packages)
// Install
try {
execRootSync(apt, ["install", "--fix-broken", "-y", ...needToInstall], {
...defaultExecOptions,
env: getAptEnv(apt),
@ -107,187 +119,19 @@ export async function installAptPack(packages: AptPackage[], update = false): Pr
return { binDir: "/usr/bin/" }
}
async function addRepositories(apt: string, packages: AptPackage[]) {
const allRepositories = [...new Set(packages.flatMap((pack) => pack.repository ?? []))]
await Promise.all(allRepositories.map((repo) => addAptRepository(repo, apt)))
}
async function addAptKeys(packages: AptPackage[]) {
await Promise.all(packages.map(async (pack) => {
if (pack.key !== undefined) {
await addAptKey(pack.key)
}
}))
}
function isExecaError(err: unknown): err is ExecaError {
return typeof (err as ExecaError).stderr === "string"
}
/**
* Check if nala is installed
*/
export function hasNala() {
return which.sync("nala", { nothrow: true }) !== null
}
/**
* Get the apt command to use
* If nala is installed, use that, otherwise use apt-get
*/
export function getApt() {
let apt: string
if (hasNala()) {
apt = "nala"
} else {
apt = "apt-get"
}
return apt
}
/**
* Get the environment variables to use for the apt command
* @param apt The apt command to use
* @private Used internally
*/
export function getAptEnv(apt: string) {
const env: NodeJS.ProcessEnv = { ...process.env, DEBIAN_FRONTEND: "noninteractive" }
if (apt === "nala") {
// if LANG/LC_ALL is not set, enable utf8 otherwise nala fails because of ASCII encoding
if (env.LANG === undefined) {
env.LANG = "C.UTF-8"
}
if (env.LC_ALL === undefined) {
env.LC_ALL = "C.UTF-8"
}
}
return env
}
/**
* The type of apt package to install
*/
export enum AptPackageType {
NameDashVersion = 0,
NameEqualsVersion = 1,
Name = 2,
None = 3,
}
/**
* Filter out the packages that are already installed and qualify the packages into a full package name/version
*/
async function filterAndQualifyAptPackages(apt: string, packages: AptPackage[]) {
return (await Promise.all(packages.map((pack) => qualifiedNeededAptPackage(apt, pack))))
.filter((pack) => pack !== undefined)
}
async function qualifiedNeededAptPackage(apt: string, pack: AptPackage) {
// Qualify the packages into full package name/version
const qualified = await getAptArg(apt, pack.name, pack.version)
// filter out the packages that are already installed
return (await isAptPackInstalled(qualified)) ? undefined : qualified
}
async function addRepositories(apt: string, packages: AptPackage[]) {
const allRepositories = [...new Set(packages.flatMap((pack) => pack.repositories ?? []))]
if (allRepositories.length !== 0) {
if (!didInit) {
await initApt(apt)
didInit = true
}
await installAddAptRepo(apt)
for (const repo of allRepositories) {
// eslint-disable-next-line no-await-in-loop
execRootSync("add-apt-repository", ["-y", "--no-update", repo], { ...defaultExecOptions, env: getAptEnv(apt) })
}
updateAptRepos(apt)
didUpdate = true
}
}
async function aptPackageType(apt: string, name: string, version: string | undefined): Promise<AptPackageType> {
if (version !== undefined && version !== "") {
const { stdout } = await execa("apt-cache", [
"search",
"--names-only",
`^${escapeRegex(name)}-${escapeRegex(version)}$`,
], { env: getAptEnv(apt), stdio: "pipe" })
if (stdout.trim() !== "") {
return AptPackageType.NameDashVersion
}
try {
// check if apt-get show can find the version
// eslint-disable-next-line @typescript-eslint/no-shadow
const { stdout } = await execa("apt-cache", ["show", `${name}=${version}`], { env: getAptEnv(apt) })
if (stdout.trim() === "") {
return AptPackageType.NameEqualsVersion
}
} catch {
// ignore
}
}
try {
const { stdout: showStdout } = await execa("apt-cache", ["show", name], { env: getAptEnv(apt), stdio: "pipe" })
if (showStdout.trim() !== "") {
return AptPackageType.Name
}
} catch {
// ignore
}
// If apt-cache fails, update the repos and try again
if (!didUpdate) {
updateAptRepos(getApt())
didUpdate = true
return aptPackageType(apt, name, version)
}
return AptPackageType.None
}
async function getAptArg(apt: string, name: string, version: string | undefined) {
const package_type = await aptPackageType(apt, name, version)
switch (package_type) {
case AptPackageType.NameDashVersion:
return `${name}-${version}`
case AptPackageType.NameEqualsVersion:
return `${name}=${version}`
case AptPackageType.Name:
if (version !== undefined && version !== "") {
warning(`Could not find package ${name} with version ${version}. Installing the latest version.`)
}
return name
default:
throw new Error(`Could not find package ${name} ${version ?? ""}`)
}
}
async function installAddAptRepo(apt: string) {
if (await isAptPackInstalled("software-properties-common")) {
return
}
execRootSync(
apt,
["install", "-y", "--fix-broken", "-o", aptTimeout, "software-properties-common"],
{ ...defaultExecOptions, env: getAptEnv(apt) },
)
}
/** Install gnupg and certificates (usually missing from docker containers) */
async function initApt(apt: string) {
// Update the repos if needed
if (!didUpdate) {
updateAptRepos(apt)
didUpdate = true
}
const toInstall = await filterAndQualifyAptPackages(apt, [
{ name: "ca-certificates" },
{ name: "gnupg" },
{ name: "apt-utils" },
])
if (toInstall.length !== 0) {
execRootSync(apt, ["install", "-y", "--fix-broken", "-o", aptTimeout, ...toInstall], {
...defaultExecOptions,
env: getAptEnv(apt),
})
}
await Promise.all([
addAptKeyViaServer(["3B4FE6ACC0B21F32", "40976EAF437D05B5"], "setup-cpp-ubuntu-archive.gpg"),
addAptKeyViaServer(["1E9377A2BA9EF27F"], "launchpad-toolchain.gpg"),
])
}

View File

@ -1,5 +1,5 @@
import { execa } from "execa"
import { getAptEnv } from "./install.js"
import { getAptEnv } from "./apt-env.js"
/**
* Check if a package is installed

View File

@ -0,0 +1,90 @@
import { warning } from "ci-log"
import escapeRegex from "escape-string-regexp"
import { execa } from "execa"
import { getAptEnv } from "./apt-env.js"
import type { AptPackage } from "./install.js"
import { isAptPackInstalled } from "./is-installed.js"
import { updateAptReposMemoized, updatedRepos } from "./update.js"
/**
* The type of apt package to install
*/
export enum AptPackageType {
NameDashVersion = 0,
NameEqualsVersion = 1,
Name = 2,
None = 3,
}
/**
* Filter out the packages that are already installed and qualify the packages into a full package name/version
*/
export async function filterAndQualifyAptPackages(apt: string, packages: AptPackage[]) {
return (await Promise.all(packages.map((pack) => qualifiedNeededAptPackage(apt, pack))))
.filter((pack) => pack !== undefined)
}
async function qualifiedNeededAptPackage(apt: string, pack: AptPackage) {
// Qualify the packages into full package name/version
const qualified = await getAptArg(apt, pack.name, pack.version)
// filter out the packages that are already installed
return (await isAptPackInstalled(qualified)) ? undefined : qualified
}
async function aptPackageType(apt: string, name: string, version: string | undefined): Promise<AptPackageType> {
if (version !== undefined && version !== "") {
const { stdout } = await execa("apt-cache", [
"search",
"--names-only",
`^${escapeRegex(name)}-${escapeRegex(version)}$`,
], { env: getAptEnv(apt), stdio: "pipe" })
if (stdout.trim() !== "") {
return AptPackageType.NameDashVersion
}
try {
// check if apt-get show can find the version
// eslint-disable-next-line @typescript-eslint/no-shadow
const { stdout } = await execa("apt-cache", ["show", `${name}=${version}`], { env: getAptEnv(apt) })
if (stdout.trim() === "") {
return AptPackageType.NameEqualsVersion
}
} catch {
// ignore
}
}
try {
const { stdout: showStdout } = await execa("apt-cache", ["show", name], { env: getAptEnv(apt), stdio: "pipe" })
if (showStdout.trim() !== "") {
return AptPackageType.Name
}
} catch {
// ignore
}
// If apt-cache fails, update the repos and try again
if (!updatedRepos) {
updateAptReposMemoized(apt)
return aptPackageType(apt, name, version)
}
return AptPackageType.None
}
async function getAptArg(apt: string, name: string, version: string | undefined) {
const package_type = await aptPackageType(apt, name, version)
switch (package_type) {
case AptPackageType.NameDashVersion:
return `${name}-${version}`
case AptPackageType.NameEqualsVersion:
return `${name}=${version}`
case AptPackageType.Name:
if (version !== undefined && version !== "") {
warning(`Could not find package ${name} with version ${version}. Installing the latest version.`)
}
return name
default:
throw new Error(`Could not find package ${name} ${version ?? ""}`)
}
}

View File

@ -1,5 +1,10 @@
import { defaultExecOptions, execRootSync } from "admina"
import { aptTimeout, getApt, getAptEnv } from "./install.js"
import memoize from "memoizee"
import { getAptEnv } from "./apt-env.js"
import { aptTimeout } from "./apt-timeout.js"
import { getApt } from "./get-apt.js"
export let updatedRepos = false // eslint-disable-line import/no-mutable-exports
/**
* Update the apt repositories
@ -11,4 +16,12 @@ export function updateAptRepos(apt: string = getApt()) {
apt !== "nala" ? ["update", "-y", "-o", aptTimeout] : ["update", "-o", aptTimeout],
{ ...defaultExecOptions, env: getAptEnv(apt) },
)
updatedRepos = true
}
/**
* Update the apt repositories (memoized)
* @param apt The apt command to use (optional)
*/
export const updateAptReposMemoized = memoize(updateAptRepos)

View File

@ -1,6 +1,8 @@
<h1 align="center">setup-brew</h1>
<p>
<img alt="Version" src="https://img.shields.io/badge/version-1.0.0-blue.svg?cacheSeconds=2592000" />
<a href="https://www.npmjs.com/package/setup-brew" target="_blank">
<img alt="Version" src="https://img.shields.io/npm/v/setup-brew.svg">
</a>
<img src="https://img.shields.io/badge/node-%3E%3D12-blue.svg" />
<a href="#" target="_blank">
<img alt="License: Apache--2.0" src="https://img.shields.io/badge/License-Apache--2.0-yellow.svg" />
@ -47,8 +49,9 @@ A function that installs a package using brew
**Parameters:**
- name (`string`)
- version (`string`)
- name (`string`) - The name of the package
- version (`string`) - The version of the package (optional)
- options - The options for installing the package
- givenOptions (`BrewPackOptions`)
**returns:** Promise<InstallationInfo>

View File

@ -28,7 +28,8 @@
},
"devDependencies": {
"path-exists": "5.0.0",
"patha": "0.4.1"
"patha": "0.4.1",
"cross-spawn": "7.0.3"
},
"engines": {
"node": ">=12"

View File

@ -17,7 +17,14 @@ export type BrewPackOptions = {
args?: string[]
}
/** A function that installs a package using brew */
/** A function that installs a package using brew
*
* @param name The name of the package
* @param version The version of the package (optional)
* @param options The options for installing the package
*
* @returns The installation information
*/
export async function installBrewPack(
name: string,
version?: string,

View File

@ -53,6 +53,9 @@ importers:
'@types/jest':
specifier: ^29.5.12
version: 29.5.12
'@types/memoizee':
specifier: ^0.4.11
version: 0.4.11
'@types/node':
specifier: ^12.20.55
version: 12.20.55
@ -128,9 +131,9 @@ importers:
macos-release:
specifier: ^3.3.0
version: 3.3.0
micro-memoize:
specifier: ^4.1.2
version: 4.1.2
memoizee:
specifier: ^0.4.17
version: 0.4.17
mkdirp:
specifier: ^3.0.1
version: 3.0.1
@ -263,15 +266,19 @@ importers:
exec-powershell:
specifier: workspace:*
version: link:../exec-powershell
micro-memoize:
specifier: ^4.1.2
version: 4.1.2
memoizee:
specifier: ^0.4.17
version: 0.4.17
path-exists:
specifier: ^5.0.0
version: 5.0.0
untildify-user:
specifier: workspace:*
version: link:../untildify-user
devDependencies:
'@types/memoizee':
specifier: 0.4.11
version: 0.4.11
packages/exec-powershell:
dependencies:
@ -312,6 +319,9 @@ importers:
execa:
specifier: ^7.2.0
version: 7.2.0
memoizee:
specifier: ^0.4.17
version: 0.4.17
node-downloader-helper:
specifier: 2.1.9
version: 2.1.9
@ -321,6 +331,10 @@ importers:
which:
specifier: 4.0.0
version: 4.0.0
devDependencies:
'@types/memoizee':
specifier: 0.4.11
version: 0.4.11
packages/setup-brew:
dependencies:
@ -346,6 +360,9 @@ importers:
specifier: 4.0.0
version: 4.0.0
devDependencies:
cross-spawn:
specifier: 7.0.3
version: 7.0.3
path-exists:
specifier: 5.0.0
version: 5.0.0
@ -1692,6 +1709,9 @@ packages:
'@types/mdast@4.0.4':
resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==}
'@types/memoizee@0.4.11':
resolution: {integrity: sha512-2gyorIBZu8GoDr9pYjROkxWWcFtHCquF7TVbN2I+/OvgZhnIGQS0vX5KJz4lXNKb8XOSfxFOSG5OLru1ESqLUg==}
'@types/minimatch@5.1.2':
resolution: {integrity: sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==}
@ -2453,6 +2473,10 @@ packages:
resolution: {integrity: sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==}
engines: {node: '>=8.0.0'}
d@1.0.2:
resolution: {integrity: sha512-MOqHvMWF9/9MX6nza0KgvFH4HpMU0EF5uUDXqX/BtxtU8NfB0QzRtJ8Oe/6SuS4kbhyzVJwjd97EA4PKrzJ8bw==}
engines: {node: '>=0.12'}
damerau-levenshtein@1.0.8:
resolution: {integrity: sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==}
@ -2733,6 +2757,20 @@ packages:
resolution: {integrity: sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==}
engines: {node: '>= 0.4'}
es5-ext@0.10.64:
resolution: {integrity: sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg==}
engines: {node: '>=0.10'}
es6-iterator@2.0.3:
resolution: {integrity: sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==}
es6-symbol@3.1.4:
resolution: {integrity: sha512-U9bFFjX8tFiATgtkJ1zg25+KviIXpgRvRHS8sau3GfhVzThRQrOeksPeT0BWW2MNZs1OEWJ1DPXOQMn0KKRkvg==}
engines: {node: '>=0.12'}
es6-weak-map@2.0.3:
resolution: {integrity: sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA==}
escalade@3.1.2:
resolution: {integrity: sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==}
engines: {node: '>=6'}
@ -2940,6 +2978,10 @@ packages:
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
hasBin: true
esniff@2.0.1:
resolution: {integrity: sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==}
engines: {node: '>=0.10'}
espree@9.6.1:
resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
@ -2969,6 +3011,9 @@ packages:
resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==}
engines: {node: '>=0.10.0'}
event-emitter@0.3.5:
resolution: {integrity: sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==}
event-target-shim@5.0.1:
resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==}
engines: {node: '>=6'}
@ -2997,6 +3042,9 @@ packages:
resolution: {integrity: sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
ext@1.7.0:
resolution: {integrity: sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==}
external-editor@3.1.0:
resolution: {integrity: sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==}
engines: {node: '>=4'}
@ -3514,6 +3562,9 @@ packages:
resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==}
engines: {node: '>=12'}
is-promise@2.2.2:
resolution: {integrity: sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==}
is-regex@1.1.4:
resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==}
engines: {node: '>= 0.4'}
@ -4016,6 +4067,9 @@ packages:
lru-cache@5.1.1:
resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==}
lru-queue@0.1.0:
resolution: {integrity: sha512-BpdYkt9EvGl8OfWHDQPISVpcl5xZthb+XPsbELj5AQXxIC8IriDZIQYjBJPEm5rS420sjZ0TLEzRcq5KdBhYrQ==}
macos-release@3.3.0:
resolution: {integrity: sha512-tPJQ1HeyiU2vRruNGhZ+VleWuMQRro8iFtJxYgnS4NQe+EukKF6aGiIT+7flZhISAt2iaXBCfFGvAyif7/f8nQ==}
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
@ -4057,6 +4111,10 @@ packages:
meant@1.0.3:
resolution: {integrity: sha512-88ZRGcNxAq4EH38cQ4D85PM57pikCwS8Z99EWHODxN7KBY+UuPiqzRTtZzS8KTXO/ywSWbdjjJST2Hly/EQxLw==}
memoizee@0.4.17:
resolution: {integrity: sha512-DGqD7Hjpi/1or4F/aYAspXKNm5Yili0QDAFAY4QYvpqpgiY6+1jOfqpmByzjxbWd/T9mChbCArXAbDAsTm5oXA==}
engines: {node: '>=0.12'}
memorystream@0.3.1:
resolution: {integrity: sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==}
engines: {node: '>= 0.10.0'}
@ -4068,9 +4126,6 @@ packages:
resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
engines: {node: '>= 8'}
micro-memoize@4.1.2:
resolution: {integrity: sha512-+HzcV2H+rbSJzApgkj0NdTakkC+bnyeiUxgT6/m7mjcz1CmM22KYFKp+EVj1sWe4UYcnriJr5uqHQD/gMHLD+g==}
micromark-core-commonmark@2.0.1:
resolution: {integrity: sha512-CUQyKr1e///ZODyD1U3xit6zXwy1a8q2a1S1HKtIlmgvurrEpaw/Y9y6KSIbF8P59cn/NjzHyO+Q2fAyYLQrAA==}
@ -4214,6 +4269,9 @@ packages:
natural-compare@1.4.0:
resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
next-tick@1.1.0:
resolution: {integrity: sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==}
node-addon-api@6.1.0:
resolution: {integrity: sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA==}
@ -5012,6 +5070,10 @@ packages:
version: 1.0.0
engines: {node: '>=12'}
timers-ext@0.1.8:
resolution: {integrity: sha512-wFH7+SEAcKfJpfLPkrgMPvvwnEtj8W4IurvEyrKsDleXnKLCDw71w8jltvfLa8Rm4qQxxT4jmDBYbJG/z7qoww==}
engines: {node: '>=0.12'}
timsort@0.3.0:
resolution: {integrity: sha512-qsdtZH+vMoCARQtyod4imc2nIJwg9Cc7lPRrw9CzF8ZKR0khdr8+2nX80PBhET3tcyTtJDxAffGh2rXH4tyU8A==}
@ -5158,6 +5220,9 @@ packages:
resolution: {integrity: sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==}
engines: {node: '>=12.20'}
type@2.7.3:
resolution: {integrity: sha512-8j+1QmAbPvLZow5Qpi6NCaN8FB60p/6x8/vfNqOk/hC+HuvFZhL4+WfekuhQLiqFZXOgQdrs3B+XxEmCc6b3FQ==}
typed-array-buffer@1.0.2:
resolution: {integrity: sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==}
engines: {node: '>= 0.4'}
@ -7189,6 +7254,8 @@ snapshots:
dependencies:
'@types/unist': 3.0.3
'@types/memoizee@0.4.11': {}
'@types/minimatch@5.1.2': {}
'@types/ms@0.7.34': {}
@ -8165,6 +8232,11 @@ snapshots:
dependencies:
css-tree: 1.1.3
d@1.0.2:
dependencies:
es5-ext: 0.10.64
type: 2.7.3
damerau-levenshtein@1.0.8:
optional: true
@ -8505,6 +8577,31 @@ snapshots:
is-date-object: 1.0.5
is-symbol: 1.0.4
es5-ext@0.10.64:
dependencies:
es6-iterator: 2.0.3
es6-symbol: 3.1.4
esniff: 2.0.1
next-tick: 1.1.0
es6-iterator@2.0.3:
dependencies:
d: 1.0.2
es5-ext: 0.10.64
es6-symbol: 3.1.4
es6-symbol@3.1.4:
dependencies:
d: 1.0.2
ext: 1.7.0
es6-weak-map@2.0.3:
dependencies:
d: 1.0.2
es5-ext: 0.10.64
es6-iterator: 2.0.3
es6-symbol: 3.1.4
escalade@3.1.2: {}
escape-path-with-spaces@1.0.2: {}
@ -8872,6 +8969,13 @@ snapshots:
transitivePeerDependencies:
- supports-color
esniff@2.0.1:
dependencies:
d: 1.0.2
es5-ext: 0.10.64
event-emitter: 0.3.5
type: 2.7.3
espree@9.6.1:
dependencies:
acorn: 8.12.1
@ -8894,6 +8998,11 @@ snapshots:
esutils@2.0.3: {}
event-emitter@0.3.5:
dependencies:
d: 1.0.2
es5-ext: 0.10.64
event-target-shim@5.0.1: {}
events@3.3.0: {}
@ -8944,6 +9053,10 @@ snapshots:
jest-message-util: 29.7.0
jest-util: 29.7.0
ext@1.7.0:
dependencies:
type: 2.7.3
external-editor@3.1.0:
dependencies:
chardet: 0.7.0
@ -9448,6 +9561,8 @@ snapshots:
is-plain-obj@4.1.0: {}
is-promise@2.2.2: {}
is-regex@1.1.4:
dependencies:
call-bind: 1.0.7
@ -10122,6 +10237,10 @@ snapshots:
dependencies:
yallist: 3.1.1
lru-queue@0.1.0:
dependencies:
es5-ext: 0.10.64
macos-release@3.3.0: {}
make-dir@2.1.0:
@ -10176,14 +10295,23 @@ snapshots:
meant@1.0.3: {}
memoizee@0.4.17:
dependencies:
d: 1.0.2
es5-ext: 0.10.64
es6-weak-map: 2.0.3
event-emitter: 0.3.5
is-promise: 2.2.2
lru-queue: 0.1.0
next-tick: 1.1.0
timers-ext: 0.1.8
memorystream@0.3.1: {}
merge-stream@2.0.0: {}
merge2@1.4.1: {}
micro-memoize@4.1.2: {}
micromark-core-commonmark@2.0.1:
dependencies:
decode-named-character-reference: 1.0.2
@ -10389,6 +10517,8 @@ snapshots:
natural-compare@1.4.0: {}
next-tick@1.1.0: {}
node-addon-api@6.1.0: {}
node-addon-api@7.1.1: {}
@ -11248,6 +11378,11 @@ snapshots:
is-node: 1.0.2
numerous: 1.0.3
timers-ext@0.1.8:
dependencies:
es5-ext: 0.10.64
next-tick: 1.1.0
timsort@0.3.0: {}
tmp@0.0.33:
@ -11371,6 +11506,8 @@ snapshots:
type-fest@2.19.0: {}
type@2.7.3: {}
typed-array-buffer@1.0.2:
dependencies:
call-bind: 1.0.7

View File

@ -1,5 +1,5 @@
import { execRootSync } from "admina"
import { addAptKeyViaDownload, installAptPack } from "setup-apt"
import { addAptKeyViaURL, installAptPack } from "setup-apt"
import { installBrewPack } from "setup-brew"
import { hasDnf } from "../utils/env/hasDnf.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" }])
} else if (isUbuntu()) {
// https://bazel.build/install/ubuntu
const keyFileName = await addAptKeyViaDownload(
"bazel-archive-keyring.gpg",
"https://bazel.build/bazel-release.pub.gpg",
)
const keyFileName = await addAptKeyViaURL({
fileName: "bazel-archive-keyring.gpg",
keyUrl: "https://bazel.build/bazel-release.pub.gpg",
})
execRootSync("bash", [
"-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`,

View File

@ -111,8 +111,18 @@ export async function setupGcc(version: string, setupDir: string, arch: string,
])
} else if (isUbuntu()) {
installationInfo = await installAptPack([
{ name: "gcc", version, repositories: ["ppa:ubuntu-toolchain-r/test"] },
{ name: "g++", version, repositories: ["ppa:ubuntu-toolchain-r/test"] },
{
name: "gcc",
version,
repository: "ppa:ubuntu-toolchain-r/test",
key: { key: "1E9377A2BA9EF27F", fileName: "ubuntu-toolchain-r-test.gpg" },
},
{
name: "g++",
version,
repository: "ppa:ubuntu-toolchain-r/test",
key: { key: "1E9377A2BA9EF27F", fileName: "ubuntu-toolchain-r-test.gpg" },
},
])
}
} else {
@ -120,7 +130,12 @@ export async function setupGcc(version: string, setupDir: string, arch: string,
if (isArch()) {
await setupPacmanPack("gcc-multilib", version)
} else if (isUbuntu()) {
await installAptPack([{ name: "gcc-multilib", version, repositories: ["ppa:ubuntu-toolchain-r/test"] }])
await installAptPack([{
name: "gcc-multilib",
version,
repository: "ppa:ubuntu-toolchain-r/test",
key: { key: "1E9377A2BA9EF27F", fileName: "ubuntu-toolchain-r-test.gpg" },
}])
}
}
break
@ -162,7 +177,12 @@ export async function setupMingw(version: string, setupDir: string, arch: string
installationInfo = await setupDnfPack([{ name: "mingw64-gcc", version }])
} else if (isUbuntu()) {
installationInfo = await installAptPack([
{ name: "mingw-w64", version, repositories: ["ppa:ubuntu-toolchain-r/test"] },
{
name: "mingw-w64",
version,
repository: "ppa:ubuntu-toolchain-r/test",
key: { key: "1E9377A2BA9EF27F", fileName: "ubuntu-toolchain-r-test.gpg" },
},
])
}
break

View File

@ -2,7 +2,7 @@ import { delimiter } from "path"
import { GITHUB_ACTIONS } from "ci-info"
import { info, warning } from "ci-log"
import { addEnv } from "envosman"
import memoize from "micro-memoize"
import memoize from "memoizee"
import { pathExists } from "path-exists"
import { addExeExt, join } from "patha"
import { addUpdateAlternativesToRc, installAptPack } from "setup-apt"
@ -35,7 +35,7 @@ async function setupLLVMWithoutActivation_raw(version: string, setupDir: string,
return installationInfo
}
const setupLLVMWithoutActivation = memoize(setupLLVMWithoutActivation_raw, { isPromise: true })
const setupLLVMWithoutActivation = memoize(setupLLVMWithoutActivation_raw, { promise: true })
/**
* Setup clang-format
@ -81,7 +81,7 @@ async function llvmBinaryDeps_raw(majorVersion: number) {
}
}
}
const llvmBinaryDeps = memoize(llvmBinaryDeps_raw, { isPromise: true })
const llvmBinaryDeps = memoize(llvmBinaryDeps_raw, { promise: true })
async function setupLLVMDeps_raw(arch: string) {
if (process.platform === "linux") {
@ -90,7 +90,7 @@ async function setupLLVMDeps_raw(arch: string) {
await setupGcc(getVersion("gcc", undefined, await ubuntuVersion()), "", arch, 40)
}
}
const setupLLVMDeps = memoize(setupLLVMDeps_raw, { isPromise: true })
const setupLLVMDeps = memoize(setupLLVMDeps_raw, { promise: true })
export async function activateLLVM(directory: string) {
const ld = process.env.LD_LIBRARY_PATH ?? ""

View File

@ -1,6 +1,6 @@
import { execRootSync } from "admina"
import { dirname } from "patha"
import { addAptKeyViaDownload, hasNala, installAptPack } from "setup-apt"
import { addAptKeyViaURL, hasNala, installAptPack } from "setup-apt"
import which from "which"
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" }])
// https://gitlab.com/volian/nala/-/wikis/Installation
const keyFileName = await addAptKeyViaDownload(
"volian-archive-nala.gpg",
"https://deb.volian.org/volian/nala.key",
)
const keyFileName = await addAptKeyViaURL({
fileName: "volian-archive-nala.gpg",
keyUrl: "https://deb.volian.org/volian/nala.key",
})
execRootSync("/bin/bash", [
"-c",
`echo "deb [signed-by=${keyFileName}] http://deb.volian.org/volian/ nala main" | tee /etc/apt/sources.list.d/volian-archive-nala.list`,

View File

@ -103,7 +103,7 @@ export async function setupPowershellSystem(version: string | undefined, _setupD
execRootSync("dpkg", ["-i", "packages-microsoft-prod.deb"])
// TODO Debian
// const keyFileName = await addAptKeyViaDownload(
// const keyFileName = await addAptKeyViaURL(
// "microsoft.asc",
// "https://packages.microsoft.com/keys/microsoft.asc"
// )

View File

@ -7,7 +7,7 @@ import { info, warning } from "ci-log"
import { addPath } from "envosman"
import { execa } from "execa"
import { readdir } from "fs/promises"
import memoize from "micro-memoize"
import memoize from "memoizee"
import { pathExists } from "path-exists"
import { addExeExt, dirname, join } from "patha"
import { installAptPack } from "setup-apt"
@ -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 foundPython = await findPython(setupDir)
@ -113,7 +113,7 @@ async function findOrSetupPython(version: string, setupDir: string, arch: string
if (foundPython === undefined) {
throw new Error("Python binary could not be found")
}
installInfo.bin = foundPython
installInfo = { bin: foundPython, installDir: dirname(foundPython), binDir: dirname(foundPython) }
}
return installInfo
@ -317,4 +317,4 @@ async function addPythonBaseExecPrefix_raw(python: string) {
*
* The answer is cached for subsequent calls
*/
export const addPythonBaseExecPrefix = memoize(addPythonBaseExecPrefix_raw, { isPromise: true })
export const addPythonBaseExecPrefix = memoize(addPythonBaseExecPrefix_raw, { promise: true })

View File

@ -1,5 +1,5 @@
import macosRelease from "macos-release"
import memoize from "micro-memoize"
import memoize from "memoizee"
/**
* Get macOS version

View File

@ -1,6 +1,6 @@
import os from "os"
import { warning } from "ci-log"
import memoize from "micro-memoize"
import memoize from "memoizee"
import { installAptPack } from "setup-apt"
import { getUbuntuVersion } from "ubuntu-version"
import which from "which"
@ -34,7 +34,7 @@ async function ubuntuVersion_raw(): Promise<number[] | null> {
}
/** Detect Ubuntu version */
export const ubuntuVersion = memoize(ubuntuVersion_raw, { isPromise: true })
export const ubuntuVersion = memoize(ubuntuVersion_raw, { promise: true })
/** Detect Ubuntu version using os.version() for Ubuntu based distros */
function detectUsingOsVersion() {

View File

@ -1,7 +1,7 @@
import { info } from "@actions/core"
import { addPath } from "envosman"
import { execa, execaSync } from "execa"
import memoize from "micro-memoize"
import memoize from "memoizee"
import { mkdirp } from "mkdirp"
import { pathExists } from "path-exists"
import { addExeExt, dirname, join } from "patha"
@ -128,7 +128,7 @@ async function getPipxHome_raw() {
await mkdirp(join(pipxHome, "venv"))
return pipxHome
}
const getPipxHome = memoize(getPipxHome_raw, { isPromise: true })
const getPipxHome = memoize(getPipxHome_raw, { promise: true })
async function getPipxBinDir_raw() {
if (process.env.PIPX_BIN_DIR !== undefined) {
@ -140,7 +140,7 @@ async function getPipxBinDir_raw() {
await mkdirp(pipxBinDir)
return pipxBinDir
}
const getPipxBinDir = memoize(getPipxBinDir_raw, { isPromise: true })
const getPipxBinDir = memoize(getPipxBinDir_raw, { promise: true })
async function getPython_raw(): Promise<string> {
const pythonBin = (await setupPython(getVersion("python", undefined, await ubuntuVersion()), "", process.arch)).bin
@ -149,7 +149,7 @@ async function getPython_raw(): Promise<string> {
}
return pythonBin
}
const getPython = memoize(getPython_raw, { isPromise: true })
const getPython = memoize(getPython_raw, { promise: true })
async function pipHasPackage(python: string, name: string) {
const result = await execa(python, ["-m", "pip", "-qq", "index", "versions", name], {