All files qualify-install.ts

60.46% Statements 26/43
64.7% Branches 11/17
28.57% Functions 2/7
61.9% Lines 26/42

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  2x 2x 2x 2x   2x 2x                                                                                   3x         3x 3x 2x     1x   1x           1x   1x   1x 1x     1x                                           3x         7x 7x             7x   7x 2x         5x   5x 5x                          
import { warning } from "ci-log"
import escapeRegex from "escape-string-regexp"
import { execa } from "execa"
import { getAptEnv } from "./apt-env.js"
import { getApt } from "./get-apt.js"
import type { AptPackage } from "./install.js"
import { isAptPackInstalled } from "./is-installed.js"
import { updateAptReposMemoized, updatedRepos } from "./update.js"
 
/**
 * Filter out the packages that are already installed and qualify the packages into a full package name/version
 */
export async function filterAndQualifyAptPackages(packages: AptPackage[], apt: string = getApt()) {
  return (await Promise.all(packages.map((pack) => qualifiedNeededAptPackage(pack, apt))))
    .filter((pack) => pack !== undefined)
}
 
/**
 * 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()) {
  const info = await findAptPackageInfo(pack.name, pack.version, apt)
  Iif (info === undefined) {
    throw new Error(`Could not find package ${pack.name} ${pack.version}`)
  }
 
  // Qualify the package into full package name/version
  const qualified = getAptArg(info)
 
  // filter out the package that are already installed
  return (await isAptPackInstalled(qualified)) ? undefined : qualified
}
 
function getAptArg(info: AptPackageInfo) {
  return `${info.name}=${info.version}`
}
 
export type AptPackageInfo = {
  name: string
  version: string
}
 
/**
 * Get the version of the package from apt-cache show
 * If the version is not found, check if apt-cache search can find the version
 * If the version is still not found, update the repos and try again
 */
export async function findAptPackageInfo(
  name: string,
  version: string | undefined,
  apt: string = getApt(),
): Promise<AptPackageInfo | undefined> {
  const info = await aptCacheShow(name, version, apt)
  if (info !== undefined) {
    return info
  }
 
  if (version !== undefined) {
    // check if apt-cache search can find the version
    const { stdout } = await execa("apt-cache", [
      "search",
      "--names-only",
      `^${escapeRegex(name)}-${escapeRegex(version)}$`,
    ], { env: getAptEnv(apt), stdio: "pipe" })
 
    const stdOutTrim = (stdout as string).trim()
 
    if (stdOutTrim !== "") {
      // get the package name by splitting the first line by " - "
      const packages = stdOutTrim.split("\n")
      const actualName = packages[0].split(" - ")[0]
 
      // get the version from apt-cache show
      return await aptCacheShow(actualName, undefined, apt)
    }
  }
 
  // If apt-cache fails, update the repos and try again
  Iif (!updatedRepos) {
    updateAptReposMemoized(apt)
    return findAptPackageInfo(apt, name, version)
  }
 
  return undefined
}
 
/**
 * Get the info about a package from apt-cache show
 *
 * @param name The name of the package
 * @param version The version of the package
 * @param apt The apt command to use
 *
 * @returns The package info or undefined if the package is not found
 */
export async function aptCacheShow(
  name: string,
  version: string | undefined,
  apt: string = getApt(),
): Promise<AptPackageInfo | undefined> {
  try {
    const { stdout } = await execa("apt-cache", [
      "show",
      version !== undefined && version !== ""
        ? `${name}=${version}`
        : name,
    ], { env: getAptEnv(apt), stdout: "pipe" })
 
    const stdoutTrim = (stdout as string).trim()
 
    if (stdoutTrim === "") {
      return undefined
    }
 
    // parse the version from the output
    // Version: 4:11.2.0-1ubuntu1
    const versionMatch = stdoutTrim.match(/^Version: (.*)$/m)
 
    if (versionMatch !== null) {
      return {
        name,
        version: versionMatch[1],
      }
    }
 
    return undefined
  } catch {
    // ignore
  }
 
  return undefined
}