UNPKG

ipsambeatae

Version:

Shared dependencies of Compass, the MongoDB extension for VSCode and MongoSH

126 lines (115 loc) 3.88 kB
/* eslint-disable no-console */ import fetch from 'node-fetch'; import tar from 'tar'; import { promisify } from 'util'; import { promises as fs } from 'fs'; import path from 'path'; import download from 'download'; import { pipeline } from 'stream'; import getDownloadURL from 'mongodb-download-url'; import type { Options as DownloadOptions } from 'mongodb-download-url'; import createDebug from 'debug'; const debug = createDebug('mongodb-downloader'); export type { DownloadOptions }; // Download mongod + mongos and return the path to a directory containing them. export async function downloadMongoDb( tmpdir: string, targetVersionSemverSpecifier = '*', options: DownloadOptions = {} ): Promise<string> { let wantsEnterprise = options.enterprise ?? false; async function lookupDownloadUrl(): Promise<string> { return ( await getDownloadURL({ version: targetVersionSemverSpecifier, enterprise: wantsEnterprise, ...options, }) ).url; } await fs.mkdir(tmpdir, { recursive: true }); if (targetVersionSemverSpecifier === 'latest-alpha') { return await doDownload( tmpdir, !!options.crypt_shared, 'latest-alpha', lookupDownloadUrl ); } if (/-enterprise$/.test(targetVersionSemverSpecifier)) { wantsEnterprise = true; targetVersionSemverSpecifier = targetVersionSemverSpecifier.replace( /-enterprise$/, '' ); } return await doDownload( tmpdir, !!options.crypt_shared, targetVersionSemverSpecifier + (wantsEnterprise ? '-enterprise' : '-community'), () => lookupDownloadUrl() ); } const downloadPromises: Record<string, Promise<string>> = {}; async function doDownload( tmpdir: string, isCryptLibrary: boolean, version: string, lookupDownloadUrl: () => Promise<string> ) { const downloadTarget = path.resolve( tmpdir, `mongodb-${process.platform}-${process.env.DISTRO_ID || 'none'}-${ process.arch }-${version}`.replace(/[^a-zA-Z0-9_-]/g, '') ); return (downloadPromises[downloadTarget] ??= (async () => { const bindir = path.resolve( downloadTarget, isCryptLibrary && process.platform !== 'win32' ? 'lib' : 'bin' ); try { await fs.stat(bindir); debug(`Skipping download because ${downloadTarget} exists`); return bindir; } catch { /* ignore */ } await fs.mkdir(downloadTarget, { recursive: true }); const url = await lookupDownloadUrl(); debug(`Downloading ${url} into ${downloadTarget}...`); async function downloadAndExtract(withExtraStripDepth = 0): Promise<void> { if (/\.tgz$|\.tar(\.[^.]+)?$/.exec(url)) { // the server's tarballs can contain hard links, which the (unmaintained?) // `download` package is unable to handle (https://github.com/kevva/decompress/issues/93) const response = await fetch(url); await promisify(pipeline)( response.body, tar.x({ cwd: downloadTarget, strip: isCryptLibrary ? 0 : 1 }) ); } else { await download(url, downloadTarget, { extract: true, strip: isCryptLibrary ? 0 : 1, }); } try { await fs.stat(bindir); // Make sure it exists. } catch (err) { if (withExtraStripDepth === 0 && url.includes('macos')) { // The server team changed how macos release artifacts are packed // and added a `./` prefix to paths in the tarball, // which seems like it shouldn't change anything but does // in fact require an increased path strip depth. console.info('Retry due to miscalculated --strip-components depth'); return await downloadAndExtract(1); } throw err; } } await downloadAndExtract(); debug(`Download complete`, bindir); return bindir; })()); }