fix: install nala via default repo or installer

This commit is contained in:
Amin Yahyaabadi 2024-09-03 00:16:55 -07:00
parent 61855cf251
commit 4e7c4bb64b
No known key found for this signature in database
GPG Key ID: F52AF77F636088F0
12 changed files with 206 additions and 116 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

@ -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,9 @@
import { tmpdir } from "os"
import { execRootSync } from "admina" import { execRootSync } from "admina"
import { dirname } from "patha" import { readFile, writeFile } from "fs/promises"
import { addAptKeyViaURL, hasNala, installAptPack } from "setup-apt" 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,31 +26,49 @@ 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
const nalaPack = await qualifiedNeededAptPackage({ name: "nala", version })
if (nalaPack !== undefined) {
await installAptPack([{ name: nalaPack }])
return { binDir } return { binDir }
} }
// Nala is not available in the default repositories
// Check if the legacy version is available
const nalaLegacyPack = await qualifiedNeededAptPackage({ name: "nala-legacy" })
if (nalaLegacyPack !== undefined) {
await installAptPack([{ name: nalaLegacyPack }], true)
return { binDir }
}
// 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, ""))
execRootSync("bash", [installerPath])
}
export function bashWithNala(script: string) { export function bashWithNala(script: string) {
if (hasNala()) { if (hasNala()) {
return `apt-get() { nala $@; }; export -f apt-get; ${script}; unset -f apt-get` return `apt-get() { nala $@; }; export -f apt-get; ${script}; unset -f apt-get`