Path: blob/main/package/src/common/archive-binary-dependencies.ts
6450 views
/*1* archive-binary-dependencies.ts2*3* Copyright (C) 2020-2022 Posit Software, PBC4*/5import { join } from "../../../src/deno_ral/path.ts";6import { info } from "../../../src/deno_ral/log.ts";7import { logPandoc } from "../../../src/core/log.ts";89import { execProcess } from "../../../src/core/process.ts";10import { Configuration, withWorkingDir } from "./config.ts";11import {12ArchitectureDependency,13Dependency,14kDependencies,15PlatformDependency,16} from "./dependencies/dependencies.ts";1718const kBucket = "s3://rstudio-buildtools/quarto";19const kBucketBaseUrl = "https://s3.amazonaws.com/rstudio-buildtools/quarto";2021// Provides a URL in the archive for a dependency22export function archiveUrl(23dependency: Dependency,24platformDependency: PlatformDependency,25) {26// TODO: Deal w/archive bin deps for this27if (dependency.bucket === "deno") {28return platformDependency.url + platformDependency.filename;29} else {30return `${kBucketBaseUrl}/${dependency.bucket}/${dependency.version}/${platformDependency.filename}`;31}32}3334// Archives dependencies (if they are not present in the archive already)35export async function archiveBinaryDependencies(_config: Configuration) {36await withWorkingDir(async (workingDir) => {37await logPandoc(`## Updating binary dependencies`);3839for (const dependency of kDependencies) {40await archiveBinaryDependency(dependency, workingDir);41}42});43}4445// Archives dependencies (if they are not present in the archive already)46export async function checkBinaryDependencies(_config: Configuration) {47await withWorkingDir(async (workingDir) => {48await logPandoc(`## Checking binary dependencies`);4950for (const dependency of kDependencies) {51await checkBinaryDependency(dependency, workingDir);52}53});54}5556export async function checkBinaryDependency(57dependency: Dependency,58workingDir: string,59) {60info(`** ${dependency.name} ${dependency.version} **`);6162const dependencyBucketPath = `${dependency.bucket}/${dependency.version}`;63info("Checking archive status...\n");6465const archive = async (66architectureDependency: ArchitectureDependency,67) => {68const platformDeps = [69architectureDependency.darwin,70architectureDependency.linux,71architectureDependency.windows,72];73for (const platformDep of platformDeps) {74if (platformDep) {75// This dependency doesn't exist, archive it76info(77`Checking ${dependencyBucketPath} - ${platformDep.filename}`,78);7980// Download the file81await download(82workingDir,83platformDep,84);85}86}87};8889for (const arch of Object.keys(dependency.architectureDependencies)) {90info(`Checking ${dependency.name}`);91const archDep = dependency.architectureDependencies[arch];92await archive(archDep);93}9495info("");96}9798export async function archiveBinaryDependency(99dependency: Dependency,100workingDir: string,101) {102await logPandoc(`## ${dependency.name} ${dependency.version}\n\nChecking archive status...`, "markdown");103104const dependencyBucketPath = `${dependency.bucket}/${dependency.version}`;105106const archive = async (107architectureDependency: ArchitectureDependency,108) => {109const platformDeps = [110architectureDependency.darwin,111architectureDependency.linux,112architectureDependency.windows,113];114for (const platformDep of platformDeps) {115if (platformDep) {116117const dependencyAwsPath =118`${kBucket}/${dependencyBucketPath}/${platformDep.filename}`;119await logPandoc(`Checking \`${dependencyAwsPath}\``, "markdown");120const response = await s3cmd("ls", [dependencyAwsPath]);121if (response?.includes('Unable to locate credentials')) {122throw new Error("Unable to locate S3 credentials, please try again.");123}124125126if (!response) {127// This dependency doesn't exist, archive it128await logPandoc(129`Archiving \`${dependencyBucketPath}\` - ${platformDep.filename}`,130);131132// Download the file133const localPath = await download(134workingDir,135platformDep,136);137138// Sync to S3139info(`Copying to ${dependencyAwsPath}\n`);140const result = await s3cmd("cp", [141localPath,142dependencyAwsPath,143"--acl",144"public-read",145]);146info(` (Response): ${result}`);147} else {148info(` ${dependencyAwsPath.split("/").slice(-1)[0]} already archived.\n`);149}150}151}152};153154for (const arch of Object.keys(dependency.architectureDependencies)) {155const archDep = dependency.architectureDependencies[arch];156await archive(archDep);157}158}159160async function s3cmd(cmd: string, args: string[]) {161const s3Args = ["s3", cmd, ...args];162const p = await execProcess({163cmd: "aws",164args: s3Args,165stdout: "piped",166});167168return p.stdout || p.stderr;169}170171async function download(172workingDir: string,173dependency: PlatformDependency,174) {175info("Downloading " + dependency.url);176const response = await fetch(dependency.url);177if (response.status === 200) {178const blob = await response.blob();179180const bytes = await blob.arrayBuffer();181const data = new Uint8Array(bytes);182183const targetPath = join(workingDir, dependency.filename);184Deno.writeFileSync(185targetPath,186data,187);188return targetPath;189} else {190throw new Error(191`Failed to fetch dependency ${dependency.filename}.\nThe url ${dependency.url} returned a non 200 status code:\n${response.status} - ${response.statusText}`,192);193}194}195196197