antongolub/action-setup-bun

View on GitHub
src/main/ts/install.ts

Summary

Maintainability
A
1 hr
Test Coverage
A
92%
import path from 'path'
import semver from 'semver'
import fs from 'fs/promises'
import * as core from '@actions/core'
import * as glob from '@actions/glob'
import * as tc from '@actions/tool-cache'
import * as cache from '@actions/cache'
import { HttpClient } from '@actions/http-client'
import { rename } from './util'

const http = new HttpClient('@actions/http-client')

export function getArch(arch: string = process.arch) {
  if (!['arm64', 'x64', 'x86_64', 'aarch64'].includes(arch))
    throw new Error(`Unsupported arch: ${arch}`)

  return arch
}

export function getPlatform(platform: string = process.platform) {
  if (!['linux', 'darwin'].includes(platform))
    throw new Error(`Unsupported platform: ${platform}`)

  return platform
}

export async function install(
  repo: string,
  version: string,
  platform: string,
  arch: string,
  token?: string,
  withCache?: string,
) {
  const HOME = process.env['HOME']
  const BUN_INSTALL = `${HOME}/.bun`
  const bunBinDir = `${BUN_INSTALL}/bin`
  const binId = `bun-${version}-${platform}-${arch}`
  const restored = withCache && (await cache.restoreCache([bunBinDir], binId))

  if (restored) {
    core.info(`${binId} binary restored from action cache`)
  } else {
    const bunDist = await getBunDist(repo, version, platform, arch, token)
    const temp = await tc.extractZip(bunDist)
    const bun = (await (await glob.create(`${temp}/**/bun`)).glob())[0]

    if (bun) {
      await fs.mkdir(bunBinDir, { recursive: true })
      await rename(bun, `${bunBinDir}/bun`)
    }
  }

  if (withCache && restored !== binId) {
    await cache.saveCache([bunBinDir], binId)
    core.info(`${binId} binary added to action cache`)
  }

  core.exportVariable('BUN_INSTALL', BUN_INSTALL)
  core.addPath(bunBinDir)

  return BUN_INSTALL
}

export async function pickVersion(repo: string, range: string, token?: string) {
  type Tag = { name: string }
  const headers = token ? { Authorization: `token ${token}` } : undefined
  const url = `https://api.github.com/repos/${repo}/tags?per_page=1000&page=1`
  const tags = (await http.getJson(url, headers)).result as Tag[]
  const _range = range === 'latest' ? '*' : range

  if (semver.validRange(_range) === null) {
    throw new Error(`Invalid version range: ${_range}`)
  }

  const version = tags.find(({ name }) =>
    semver.satisfies(name.replace('bun-', ''), _range),
  )
  if (!version) throw new Error(`Version ${range} not found in ${repo}`)

  return version.name
}

export async function getBunDist(
  repo: string,
  version: string,
  platform: string,
  arch: string,
  token?: string,
): Promise<string> {
  const _version = version.replace('bun-', '')
  const file = `bun-${_version}-${platform}-${arch}.zip`
  const cachedBunPath = tc.find('bun', _version, arch)
  if (cachedBunPath) {
    core.info(`bun ${file} found in tool-cache`)
    return path.join(cachedBunPath, file)
  }

  const auth = token ? `token ${token}` : undefined
  const bunDistUri = getBunDistUri(repo, version, platform, arch)
  core.info(`Downloading bun from ${bunDistUri}`)
  const bunDist = await tc.downloadTool(bunDistUri, undefined, auth)

  await tc.cacheFile(bunDist, file, 'bun', _version, arch)
  core.info(`bun dist tool-cached: ${tc.find('bun', _version, arch)}`)

  return bunDist
}

export function getBunDistUri(
  repo: string,
  version: string,
  platform: string,
  arch: string,
) {
  // prettier-ignore
  const _arch =
      arch === 'x86_64' ? 'x64' :
      arch === 'arm64' ? 'aarch64' :  arch

  return `https://github.com/${repo}/releases/download/${version}/bun-${platform}-${_arch}.zip`
}