All files apt-key.ts

0% Statements 0/34
0% Branches 0/8
0% Functions 0/6
0% Lines 0/34

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149                                                                                                                                                                                                                                                                                                         
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"
 
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
 * @returns The file name of the key that was added or `undefined` if it failed
 */
export async function addAptKeyViaServer(
  { key, keyServer = defaultKeyServer, fileName, keyStorePath = defaultKeyServer }: KeyServerOptions,
) {
  try {
    assertGpgFileName(fileName)
    const filePath = join(keyStorePath, fileName)
    Iif (!(await pathExists(filePath))) {
      initGpg()
 
      await execRoot("gpg", [
        "--no-default-keyring",
        "--keyring",
        `gnupg-ring:${filePath}`,
        "--keyserver",
        keyServer,
        "--recv-keys",
        key,
      ])
      await execRoot("chmod", ["644", filePath])
    }
    return filePath
  } catch (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 options The options for adding the key
 * @returns The file name of the key that was added
 */
export async function addAptKeyViaURL({ keyUrl, fileName, keyStorePath = defaultKeyStorePath }: KeyUrl) {
  try {
    assertGpgFileName(fileName)
    const filePath = join(keyStorePath, fileName)
    Iif (!(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()
 
      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) {
  Iif (!fileName.endsWith(".gpg")) {
    throw new Error(`Key file name must end with .gpg: ${fileName}`)
  }
}