UNPKG

nsolid-manager

Version:

A manager that sets up and runs N|Solid for you

359 lines (319 loc) 10.4 kB
'use strict'; /* ---------------------------------- This file will install all necessary dependencies TODO (Alex): Checksum download files TODO (Alex): put extracted files into versioned sub folders TODO (Alex): Add Tests TODO (Alex): Provide more sanity checking of Filesystem changes -----------------------------------*/ /* Until this bug is fixed: https://github.com/eslint/eslint/issues/1801 We will not be enforcing indentation in eslint */ /* eslint indent: [0] */ const FileDownloader = require('./lib/file-downloader.js'); const binaryUrls = require('./binary-urls.json'); const os = require('os'); const fs = require('fs'); const chalk = require('chalk'); const ncp = require('ncp'); const debug = require('debug')('install'); const targz = require('tar.gz'); const unzip = require('unzip'); const _ = require('lodash'); const utils = require('./lib/utils'); const multiProgress = require('multi-progress'); // Windows is not currently supported const isWin = /^win/.test(process.platform); if (isWin) { console.error('Windows Not Currently Supported'); process.exit(1); } // 'darwin', 'freebsd', 'linux', 'sunos' or 'win32' let platform = os.platform(); debug('Raw Platform %s', platform); // map platforms to 2 switch (platform) { case 'sunos': case 'freebsd': platform = 'linux'; break; default: break; } debug('Mapped Platform %s', platform); /* Test if Dependency Directory Exists */ const dependencyDir = `${__dirname}/dependencies`; if (!fs.existsSync(dependencyDir)) { debug('Creating Dependency Directory'); fs.mkdirSync(dependencyDir); } else { debug('Dependency Directory Already Exists'); } /* Promise Workflow */ debug('Begin Loading Package Metadata'); loadAllMetaData(binaryUrls).then(metaData => { debug('Finish loading all meta data about 3rd party packages'); // TODO (Alex): Perform some check for existing libraries return downloadAndExtractAll(metaData); }).then(result => { process.stdout.write('\n\n\n\n'); console.log('Done Loading Dependencies %s', result); }) .catch(err => { console.error(err, err.stack); }); /* *************************************************************** Functions to download and extract libraries **************************************************************** */ function downloadAndExtractAll(metaData) { debug('Beginning download of all packages'); const multi = multiProgress(process.stdout); const downloadQueue = [ downloadNsolidPackage('nsolid-hub', metaData, multi), downloadNsolidPackage('nsolid-console', metaData, multi), downloadNsolidPackage('nsolid', metaData, multi), downloadEtcd(metaData.etcd, multi) ]; return Promise.all(downloadQueue); } // Universal function for downloading and extracting // a Nsolid package (hub, console, nsolid) function downloadNsolidPackage(name, allMetaData, multi) { return new Promise((resolve, reject) => { let transferred = 0; const metaData = allMetaData[name]; const version = metaData.meta.version; const filename = utils.templateString(metaData.file, { version, platform }); // create a multi line terminal progress bar const bar = multi.newBar(' :stage [:file] :size B ', { complete: '=', incomplete: ' ', width: 30, total: 100 }); bar.tick({ stage: 'Downloading', file: chalk.cyan(filename), size: transferred }); const url = `${metaData.dir}${version}/${filename}`; debug('Making request for %s', url); new FileDownloader({ url, location: `${dependencyDir}/${filename}` }) .on('progress', state => { transferred = state.size.transferred; bar.tick({ stage: 'Downloading', file: chalk.cyan(filename), size: transferred }); debug('Download Progress for %s: %s', name, state.size.transferred); }) .on('error', reject) .on('done', file => { debug('File download completed for %s. Starting extract.', name); bar.tick({ stage: 'Extracting', file: chalk.cyan(filename), size: transferred }); // untar file fs.createReadStream(file) .pipe(targz({}, { strip: 1 }).createWriteStream(`${dependencyDir}/${name}`)) .on('end', () => { bar.tick({ stage: chalk.green('Completed'), file: chalk.cyan(filename), size: transferred }); debug('Extract Complete for %s', filename); resolve(); }); }) .start(); }); } // Download and extract ETCD function downloadEtcd(metaData, multi) { return new Promise((resolve, reject) => { let transferred = 0; const version = metaData.meta.tag_name; const filename = utils.templateString(metaData.file, { version, platform, extension: (platform === 'linux') ? '.tar.gz' : '.zip' }); // without extension const rawfilename = utils.templateString(metaData.file, { version, platform, extension: '' }); // find the relevant asset from a list of them const asset = _.find(metaData.meta.assets, { name: filename }); if (!asset) { console.error('unable to find etcd version asset'); process.exit(1); } // create a multi line terminal progress bar const bar = multi.newBar(' :stage [:file] :size B ', { complete: '=', incomplete: ' ', width: 30, total: 100 }); bar.tick({ stage: 'Downloading', file: chalk.cyan(filename), size: transferred }); const url = asset.browser_download_url; debug('Making request for %s. Filename: %s', url, filename); new FileDownloader({ url, location: `${dependencyDir}/${filename}` }) .on('progress', state => { transferred = state.size.transferred; bar.tick({ stage: 'Downloading', file: chalk.cyan(filename), size: transferred }); debug('Download Progress for %s: %s', 'etcd', state.size.transferred); }) .on('error', reject) .on('done', file => { debug('File download completed for etcd. Starting extract.'); bar.tick({ stage: 'Extracting', file: chalk.cyan(filename), size: transferred }); let outputStream; // we need to use unzip on darwin if (platform === 'linux') { debug('Linux detected. Using targz for etcd extract'); outputStream = targz({}, { strip: 1 }).createWriteStream(`${dependencyDir}/etcd`); } else { debug('Mac detected. Using unzip for etcd extract'); outputStream = unzip.Extract({ //eslint-disable-line path: `${dependencyDir}/etcd` }); } // untar file fs.createReadStream(file) .pipe(outputStream) .on('close', streamCleanup); function streamCleanup() { bar.tick({ stage: chalk.green('Completed'), file: chalk.cyan(filename), size: transferred }); debug('Inside fs.createReadStream end block.'); debug(`Platform: ${platform}`); // if we are on mac we need to move the location of the // unzipped folder up one level. So we will just the NCP library if (platform === 'darwin') { debug('Detected Mac'); debug(`Copying From: ${dependencyDir}/etcd/${rawfilename}`); debug(`To: ${dependencyDir}/etcd/`); ncp( `${dependencyDir}/etcd/${rawfilename}`, `${dependencyDir}/etcd/`, (err) => { if (err) return reject(err); // chmod file debug('Chmodding etcd file'); fs.chmodSync(`${dependencyDir}/etcd/etcd`, '0740'); return resolve(); }); } else { resolve(); } } }) .start(); }); } /* *************************************************************** Functions to load meta data Version example: `v3.4.2` **************************************************************** */ function loadAllMetaData(metaData) { const metaQueue = [ loadNsolidMetaData('nsolid-hub'), loadNsolidMetaData('nsolid-console'), loadNsolidMetaData('nsolid'), loadEtcdMetaData() ]; return Promise.all(metaQueue).then(results => new Promise(resolve => { debug('Mapping Meta data response back onto original metadata object'); // map results to existing meta data object // TODO (Alex): Be more elegant than this const newObj = _.extend({}, metaData); newObj['nsolid-hub'].meta = results[0]; newObj['nsolid-console'].meta = results[1]; newObj.nsolid.meta = results[2]; newObj.etcd.meta = results[3]; return resolve(newObj); })); } // Universal function for returning Nsolid meta data function loadNsolidMetaData(name, version) { return new Promise((resolve, reject) => { utils.requestAsync(binaryUrls[name].metaUrl).then(metaData => { debug('Received meta data response for %s', name); // find specific version from meta data if (version) { debug('Searching for version %s for %s', version, name); const vData = _.find(metaData, { version }); if (vData) resolve(vData); return reject(new Error(`Unable to find version data for ${name}`)); } debug('Returning newest version for %s', name); // return newest version return resolve(metaData[0]); }, reject); }); } // load etc release data from github function loadEtcdMetaData(version) { return new Promise((resolve, reject) => { utils.requestAsync(binaryUrls.etcd.metaUrl).then(metaData => { debug('Received meta data response for etcd'); if (version) { debug('Searching for version %s for %s', version, 'etcd'); const vData = _.findWhere(metaData, { tag_name: version }); if (vData) return resolve(vData); return reject(new Error('Unable to find version data for Etcd')); } debug('Returning newest version for etcd'); return resolve(metaData[0]); }, reject); }); }