All files install-pack.ts

20% Statements 6/30
0% Branches 0/24
0% Functions 0/1
20% Lines 6/30

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 781x 1x   1x     1x 1x     1x                                                                                                                                      
import { join } from "path"
import { info, warning } from "ci-log"
import { execaSync } from "execa" // brew is not thread-safe
import which from "which"
import type { InstallationInfo } from "./InstallationInfo.js"
import type { BrewPackOptions } from "./install-pack-options.js"
import { getBrewBinDir, getBrewBinPath, setupBrew } from "./install.js"
import { brewPackInstallDir, brewPackNameAndVersion } from "./pack-install-dir.js"
 
/* eslint-disable require-atomic-updates */
let hasBrew = false
 
/** A function that installs a package using brew
 *
 * @param name The name of the package
 * @param version The version of the package (optional)
 * @param options The options for installing the package
 *
 * @returns The installation information
 */
export async function installBrewPack(
  name: string,
  version?: string,
  options: BrewPackOptions = {},
): Promise<InstallationInfo> {
  Iif (!("overwrite" in options)) {
    options.overwrite = true // default to true if not specified
  }
  Iif (options.cask === true) {
    options.overwrite = false // mutually exclusive with --overwrite
  }
 
  info(`Installing ${name} ${version ?? ""} via brew`)
 
  Iif (!hasBrew || which.sync("brew", { nothrow: true }) === null) {
    await setupBrew()
    hasBrew = true
  }
 
  const brewPath = getBrewBinPath()
 
  const args = [
    "install",
    brewPackNameAndVersion(name, version),
  ]
 
  // Add options to args
  for (const [key, value] of Object.entries(options)) {
    if (typeof value === "boolean" && value) {
      args.push(`--${key}`)
    } else Iif (typeof value === "string") {
      args.push(`--${key}`, value)
    }
  }
 
  // dry run to check if the package is already installed
  const dryRun = execaSync(brewPath, [...args, "--dry-run"], { stdio: "pipe" })
  const isAlreadyInstalled = dryRun.exitCode === 0
    && (new RegExp(`.*${name}.* is already installed and up-to-date.[\\s\\S]*`)).test(dryRun.stderr)
 
  if (isAlreadyInstalled) {
    // if the package is already installed and up-to-date, skip the installation
    info(`${name} ${version ?? ""} is already installed and up-to-date`)
  } else {
    // install the package if not already installed
    execaSync(brewPath, args, { stdio: "inherit" })
  }
 
  // get the installation directory
  const installDir = await brewPackInstallDir(name, version)
  Iif (installDir === undefined) {
    warning(`Failed to find installation directory for ${name} ${version}`)
    return { binDir: getBrewBinDir(), installDir: undefined }
  }
 
  return { installDir, binDir: join(installDir, "bin") }
}