Merge pull request #276 from aminya/nala [skip ci]

This commit is contained in:
Amin Yahyaabadi 2024-09-03 03:43:09 -07:00 committed by GitHub
commit 324effb605
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 225 additions and 117 deletions

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

@ -4,4 +4,4 @@ pre-commit:
test.lint: test.lint:
run: pnpm run test.lint run: pnpm run test.lint
build: build:
run: pnpm run clean && pnpm i && pnpm run build -- --no-color && git add ./dist run: pnpm run clean && pnpm run build -- --no-color && git add ./dist

View File

@ -46,6 +46,34 @@ Add the update-alternatives command to the rc file
**returns:** Promise<void> **returns:** Promise<void>
### `getAptEnv` (function)
Get the environment variables to use for the apt command
**Parameters:**
- apt (`string`) - The apt command to use
**returns:** ProcessEnv
### `aptTimeout` (variable)
The timeout to use for apt commands
Wait up to 300 seconds if the apt-get lock is held
### `hasNala` (function)
Check if nala is installed
**returns:** boolean
### `getApt` (function)
Get the apt command to use
If nala is installed, use that, otherwise use apt-get
**returns:** string
### `isAptPackInstalled` (function) ### `isAptPackInstalled` (function)
Check if a package is installed Check if a package is installed
@ -66,6 +94,8 @@ Check if a package matching a regexp is installed
**returns:** Promise<boolean> **returns:** Promise<boolean>
### `updatedRepos` (variable)
### `updateAptRepos` (function) ### `updateAptRepos` (function)
Update the apt repositories Update the apt repositories
@ -76,15 +106,73 @@ Update the apt repositories
**returns:** void **returns:** void
### `updateAptReposMemoized` (variable)
Update the apt repositories (memoized)
**Parameters:**
- apt - The apt command to use (optional)
### `filterAndQualifyAptPackages` (function)
Filter out the packages that are already installed and qualify the packages into a full package name/version
**Parameters:**
- packages (`AptPackage[]`)
- apt (`string`)
**returns:** Promise<string[]>
### `qualifiedNeededAptPackage` (function)
Qualify the package into full package name/version.
If the package is not installed, return the full package name/version.
If the package is already installed, return undefined
**Parameters:**
- pack (`AptPackage`)
- apt (`string`)
**returns:** Promise<string>
### `initApt` (function)
Install gnupg and certificates (usually missing from docker containers)
**Parameters:**
- apt (`string`)
**returns:** Promise<void>
### `initAptMemoized` (variable)
Install gnupg and certificates (usually missing from docker containers) (memoized)
### `addAptRepository` (function)
**Parameters:**
- repo (`string`)
- apt (`string`)
**returns:** Promise<void>
### `installAddAptRepo` (function)
**Parameters:**
- apt (`string`)
**returns:** Promise<void>
### `InstallationInfo` (type) ### `InstallationInfo` (type)
The information about an installation result The information about an installation result
### `aptTimeout` (variable)
The timeout to use for apt commands
Wait up to 300 seconds if the apt-get lock is held
### `AptPackage` (type) ### `AptPackage` (type)
The information about an apt package The information about an apt package
@ -115,29 +203,6 @@ await installAptPack([
]) ])
``` ```
### `hasNala` (function)
Check if nala is installed
**returns:** boolean
### `getApt` (function)
Get the apt command to use
If nala is installed, use that, otherwise use apt-get
**returns:** string
### `getAptEnv` (function)
Get the environment variables to use for the apt command
**Parameters:**
- apt (`string`) - The apt command to use
**returns:** ProcessEnv
### `AddAptKeyOptions` (type) ### `AddAptKeyOptions` (type)
### `addAptKey` (function) ### `addAptKey` (function)
@ -151,10 +216,7 @@ Add an apt key
**returns:** Promise<string> **returns:** Promise<string>
```ts ```ts
await addAptKey({ await addAptKey({ key: "3B4FE6ACC0B21F32" fileName: "bazel-archive-keyring.gpg"})
key: "3B4FE6ACC0B21F32"
fileName: "bazel-archive-keyring.gpg",
})
``` ```
```ts ```ts

View File

@ -6,4 +6,5 @@ export * from "./get-apt.js"
export * from "./init-apt.js" export * from "./init-apt.js"
export * from "./install.js" export * from "./install.js"
export * from "./is-installed.js" export * from "./is-installed.js"
export * from "./qualify-install.js"
export * from "./update.js" export * from "./update.js"

View File

@ -10,11 +10,11 @@ export async function initApt(apt: string) {
// Update the repos // Update the repos
updateAptReposMemoized(apt) updateAptReposMemoized(apt)
const toInstall = await filterAndQualifyAptPackages(apt, [ const toInstall = await filterAndQualifyAptPackages([
{ name: "ca-certificates" }, { name: "ca-certificates" },
{ name: "gnupg" }, { name: "gnupg" },
{ name: "apt-utils" }, { name: "apt-utils" },
]) ], apt)
if (toInstall.length !== 0) { if (toInstall.length !== 0) {
execRootSync(apt, ["install", "-y", "--fix-broken", "-o", aptTimeout, ...toInstall], { execRootSync(apt, ["install", "-y", "--fix-broken", "-o", aptTimeout, ...toInstall], {

View File

@ -82,7 +82,7 @@ export async function installAptPack(packages: AptPackage[], update = false): Pr
// Add the repos if needed // Add the repos if needed
await addRepositories(apt, packages) await addRepositories(apt, packages)
const needToInstall = await filterAndQualifyAptPackages(apt, packages) const needToInstall = await filterAndQualifyAptPackages(packages, apt)
if (needToInstall.length === 0) { if (needToInstall.length === 0) {
info("All packages are already installed") info("All packages are already installed")

View File

@ -2,6 +2,7 @@ import { warning } from "ci-log"
import escapeRegex from "escape-string-regexp" import escapeRegex from "escape-string-regexp"
import { execa } from "execa" import { execa } from "execa"
import { getAptEnv } from "./apt-env.js" import { getAptEnv } from "./apt-env.js"
import { getApt } from "./get-apt.js"
import type { AptPackage } from "./install.js" import type { AptPackage } from "./install.js"
import { isAptPackInstalled } from "./is-installed.js" import { isAptPackInstalled } from "./is-installed.js"
import { updateAptReposMemoized, updatedRepos } from "./update.js" import { updateAptReposMemoized, updatedRepos } from "./update.js"
@ -19,15 +20,20 @@ export enum AptPackageType {
/** /**
* Filter out the packages that are already installed and qualify the packages into a full package name/version * 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[]) { export async function filterAndQualifyAptPackages(packages: AptPackage[], apt: string = getApt()) {
return (await Promise.all(packages.map((pack) => qualifiedNeededAptPackage(apt, pack)))) return (await Promise.all(packages.map((pack) => qualifiedNeededAptPackage(pack, apt))))
.filter((pack) => pack !== undefined) .filter((pack) => pack !== undefined)
} }
async function qualifiedNeededAptPackage(apt: string, pack: AptPackage) { /**
// Qualify the packages into full package name/version * Qualify the package into full package name/version.
* If the package is not installed, return the full package name/version.
* If the package is already installed, return undefined
*/
export async function qualifiedNeededAptPackage(pack: AptPackage, apt: string = getApt()) {
// Qualify the package into full package name/version
const qualified = await getAptArg(apt, pack.name, pack.version) const qualified = await getAptArg(apt, pack.name, pack.version)
// filter out the packages that are already installed // filter out the package that are already installed
return (await isAptPackInstalled(qualified)) ? undefined : qualified return (await isAptPackInstalled(qualified)) ? undefined : qualified
} }

View File

@ -1,6 +1,10 @@
import { tmpdir } from "os"
import { execRootSync } from "admina" import { execRootSync } from "admina"
import { dirname } from "patha" import { error, info } from "ci-log"
import { addAptKeyViaURL, hasNala, installAptPack } from "setup-apt" import { readFile, writeFile } from "fs/promises"
import { DownloaderHelper } from "node-downloader-helper"
import { dirname, join } from "patha"
import { hasNala, installAptPack, qualifiedNeededAptPackage } 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"
@ -23,30 +27,65 @@ 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
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`,
])
try {
if (version !== "legacy") {
await installAptPack([{ name: "nala" }], true)
} else {
await installAptPack([{ name: "nala-legacy" }], true)
}
} catch (err) {
await installAptPack([{ name: "nala-legacy" }], true)
}
binDir = "/usr/bin" // eslint-disable-line require-atomic-updates binDir = "/usr/bin" // eslint-disable-line require-atomic-updates
// If nala is available in the default repositories, install it
try {
const nalaPack = await qualifiedNeededAptPackage({ name: "nala", version })
if (nalaPack !== undefined) {
await installAptPack([{ name: nalaPack }])
return { binDir } return { binDir }
} }
} catch (err) {
// ignore
info(`Failed to install nala: ${err}`)
}
// Nala is not available in the default repositories
// Check if the legacy version is available
try {
const nalaLegacyPack = await qualifiedNeededAptPackage({ name: "nala-legacy" })
if (nalaLegacyPack !== undefined) {
await installAptPack([{ name: nalaLegacyPack }], true)
return { binDir }
}
} catch (err) {
// ignore
info(`Failed to install nala-legacy: ${err}`)
}
// Install via the installer script
await setupNalaViaInstaller()
return { binDir }
}
async function setupNalaViaInstaller() {
const installer = new DownloaderHelper(
"https://gitlab.com/volian/volian-archive/-/raw/main/install-nala.sh",
tmpdir(),
{ fileName: "install-nala.sh" },
)
installer.on("error", (err) => {
throw new Error(`Failed to download install-nala.sh: ${err}`)
})
await installer.start()
const installerPath = join(tmpdir(), "install-nala.sh")
// Patch the installer script to not use sudo explicitly
const script = await readFile(installerPath, "utf8")
await writeFile(installerPath, script.replace(/sudo/g, ""))
await installAptPack([{ name: "wget" }])
try {
execRootSync("bash", [installerPath])
} catch (err) {
error(`Failed to install nala via installer: ${err}`)
execRootSync("apt", ["install", "-y", "-t", "nala", "nala"])
}
}
export function bashWithNala(script: string) { export function bashWithNala(script: string) {
if (hasNala()) { if (hasNala()) {