2024-08-18 16:53:22 +08:00
|
|
|
import { tmpdir } from "os"
|
2024-08-29 04:54:48 +08:00
|
|
|
import { join } from "path"
|
2024-08-16 17:13:47 +08:00
|
|
|
import { execRoot, execRootSync } from "admina"
|
|
|
|
import { warning } from "ci-log"
|
2024-08-18 16:53:22 +08:00
|
|
|
import { DownloaderHelper } from "node-downloader-helper"
|
2024-08-16 17:13:47 +08:00
|
|
|
import { pathExists } from "path-exists"
|
2024-08-19 16:18:00 +08:00
|
|
|
import { installAptPack } from "./install.js"
|
2024-08-16 17:13:47 +08:00
|
|
|
|
2024-08-29 04:54:48 +08:00
|
|
|
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
|
2024-08-29 05:23:40 +08:00
|
|
|
* await addAptKey({ key: "3B4FE6ACC0B21F32" fileName: "bazel-archive-keyring.gpg"})
|
2024-08-29 04:54:48 +08:00
|
|
|
* ```
|
|
|
|
*
|
|
|
|
* @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)
|
|
|
|
}
|
2024-08-16 17:13:47 +08:00
|
|
|
}
|
|
|
|
|
2024-08-29 04:54:48 +08:00
|
|
|
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 = {
|
|
|
|
/**
|
2024-08-29 05:23:40 +08:00
|
|
|
* The key to add
|
2024-08-29 04:54:48 +08:00
|
|
|
*
|
|
|
|
* @example
|
|
|
|
* ```ts
|
2024-08-29 05:23:40 +08:00
|
|
|
* "3B4FE6ACC0B21F32"
|
2024-08-29 04:54:48 +08:00
|
|
|
* ```
|
|
|
|
*/
|
2024-08-29 05:23:40 +08:00
|
|
|
key: string
|
2024-08-29 04:54:48 +08:00
|
|
|
/**
|
|
|
|
* The keyserver to use (Defaults to `keyserver.ubuntu.com`)
|
|
|
|
*/
|
|
|
|
keyServer?: string
|
|
|
|
} & GpgKeyOptions
|
|
|
|
|
|
|
|
export const defaultKeyServer = "keyserver.ubuntu.com"
|
|
|
|
|
2024-08-16 17:13:47 +08:00
|
|
|
/**
|
|
|
|
* Add an apt key via a keyserver
|
|
|
|
* @returns The file name of the key that was added or `undefined` if it failed
|
|
|
|
*/
|
2024-08-29 04:54:48 +08:00
|
|
|
export async function addAptKeyViaServer(
|
2024-08-29 05:23:40 +08:00
|
|
|
{ key, keyServer = defaultKeyServer, fileName, keyStorePath = defaultKeyServer }: KeyServerOptions,
|
2024-08-29 04:54:48 +08:00
|
|
|
) {
|
2024-08-16 17:13:47 +08:00
|
|
|
try {
|
2024-08-29 04:54:48 +08:00
|
|
|
assertGpgFileName(fileName)
|
|
|
|
const filePath = join(keyStorePath, fileName)
|
|
|
|
if (!(await pathExists(filePath))) {
|
2024-08-16 17:13:47 +08:00
|
|
|
initGpg()
|
|
|
|
|
2024-08-29 05:23:40 +08:00
|
|
|
await execRoot("gpg", [
|
|
|
|
"--no-default-keyring",
|
|
|
|
"--keyring",
|
|
|
|
`gnupg-ring:${filePath}`,
|
|
|
|
"--keyserver",
|
|
|
|
keyServer,
|
|
|
|
"--recv-keys",
|
|
|
|
key,
|
|
|
|
])
|
|
|
|
await execRoot("chmod", ["644", filePath])
|
2024-08-16 17:13:47 +08:00
|
|
|
}
|
2024-08-29 04:54:48 +08:00
|
|
|
return filePath
|
2024-08-16 17:13:47 +08:00
|
|
|
} catch (err) {
|
2024-08-29 04:54:48 +08:00
|
|
|
warning(`Failed to add apt key via server ${keyServer}: ${err}`)
|
2024-08-16 17:13:47 +08:00
|
|
|
return undefined
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-08-29 04:54:48 +08:00
|
|
|
export type KeyUrl = {
|
|
|
|
/**
|
|
|
|
* The URL to download the key from
|
|
|
|
*/
|
|
|
|
keyUrl: string
|
|
|
|
} & GpgKeyOptions
|
|
|
|
|
2024-08-16 17:13:47 +08:00
|
|
|
/**
|
|
|
|
* Add an apt key via a download
|
2024-08-29 04:54:48 +08:00
|
|
|
* @param options The options for adding the key
|
2024-08-16 17:13:47 +08:00
|
|
|
* @returns The file name of the key that was added
|
|
|
|
*/
|
2024-08-29 04:54:48 +08:00
|
|
|
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 dlPath = join(tmpdir(), fileName)
|
|
|
|
const dl = new DownloaderHelper(keyUrl, tmpdir(), { fileName })
|
|
|
|
dl.on("error", (err) => {
|
|
|
|
throw new Error(`Failed to download ${keyUrl}: ${err}`)
|
|
|
|
})
|
|
|
|
await dl.start()
|
2024-08-18 16:53:22 +08:00
|
|
|
|
2024-08-29 04:54:48 +08:00
|
|
|
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"])
|
|
|
|
}
|
2024-08-18 16:53:22 +08:00
|
|
|
|
2024-08-29 04:54:48 +08:00
|
|
|
function assertGpgFileName(fileName: string) {
|
|
|
|
if (!fileName.endsWith(".gpg")) {
|
|
|
|
throw new Error(`Key file name must end with .gpg: ${fileName}`)
|
2024-08-16 17:13:47 +08:00
|
|
|
}
|
|
|
|
}
|