// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. import { spawn } from 'child_process'; import { i } from './i18n'; import { DownloadEvents } from './interfaces/events'; import { Session } from './session'; import { Uri } from './util/uri'; function streamVcpkg(vcpkgCommand: string | undefined, args: Array, listener: (chunk: any) => void): Promise { return new Promise((accept, reject) => { if (!vcpkgCommand) { reject(i`VCPKG_COMMAND was not set`); return; } const subproc = spawn(vcpkgCommand, args, { stdio: ['ignore', 'pipe', 'pipe'] }); subproc.stdout.on('data', listener); subproc.stderr.pipe(process.stdout); subproc.on('error', (err) => { reject(err); }); subproc.on('close', (code: number, signal) => { if (code === 0) { accept(); return; } reject(i`Running vcpkg internally returned a nonzero exit code: ${code}`); }); }); } async function runVcpkg(vcpkgCommand: string | undefined, args: Array): Promise { let result = ''; await streamVcpkg(vcpkgCommand, args, (chunk) => { result += chunk; }); return result.trimEnd(); } export function vcpkgFetch(session: Session, fetchKey: string): Promise { return runVcpkg(session.vcpkgCommand, ['fetch', fetchKey, '--x-stderr-status']).then((output) => { return output; }, (error) => { if (fetchKey === 'git') { session.channels.warning('failed to fetch git, falling back to attempting to use git from the PATH'); return Promise.resolve('git'); } return Promise.reject(error); }); } export async function vcpkgExtract(session: Session, archive: string, target:string, strip?:number|string): Promise { const args: Array = ['z-extract', archive, target]; if (strip) { args.push(`--strip=${strip}`); } return runVcpkg(session.vcpkgCommand, args);} export async function vcpkgDownload(session: Session, destination: string, sha512: string | undefined, uris: Array, events: Partial) : Promise { const args = ['x-download', destination, '--z-machine-readable-progress']; if (sha512) { args.push(`--sha512=${sha512}`); } else { args.push('--skip-sha512'); } for (const uri of uris) { events.downloadProgress?.(uri, destination, 0); const uriArgs = [...args, `--url=${uri.toString()}`]; try { await streamVcpkg(session.vcpkgCommand, uriArgs, (chunk) => { const match = /(\d+)(\.\d+)?%\s*$/.exec(chunk); if (!match) { return; } const number = parseFloat(match[1]); // throwing out 100s avoids displaying temporarily full progress bars resulting from redirects getting resolved if (number && number < 100) { events.downloadProgress?.(uri, destination, number); } }); return; } catch { session.channels.warning(i`failed to download from ${uri.toString()}`); } } throw new Error(i`failed to download ${destination} from any source`); }