forked from lix-project/lix-website
209 lines
3.6 KiB
JavaScript
209 lines
3.6 KiB
JavaScript
|
'use strict';
|
||
|
const fs = require('fs');
|
||
|
const path = require('path');
|
||
|
const url = require('url');
|
||
|
const pify = require('pify');
|
||
|
const importLazy = require('import-lazy')(require);
|
||
|
|
||
|
const binCheck = importLazy('bin-check');
|
||
|
const binVersionCheck = importLazy('bin-version-check');
|
||
|
const download = importLazy('download');
|
||
|
const osFilterObj = importLazy('os-filter-obj');
|
||
|
|
||
|
const statAsync = pify(fs.stat);
|
||
|
const chmodAsync = pify(fs.chmod);
|
||
|
|
||
|
/**
|
||
|
* Initialize a new `BinWrapper`
|
||
|
*
|
||
|
* @param {Object} options
|
||
|
* @api public
|
||
|
*/
|
||
|
module.exports = class BinWrapper {
|
||
|
constructor(options = {}) {
|
||
|
this.options = options;
|
||
|
|
||
|
if (this.options.strip <= 0) {
|
||
|
this.options.strip = 0;
|
||
|
} else if (!this.options.strip) {
|
||
|
this.options.strip = 1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get or set files to download
|
||
|
*
|
||
|
* @param {String} src
|
||
|
* @param {String} os
|
||
|
* @param {String} arch
|
||
|
* @api public
|
||
|
*/
|
||
|
src(src, os, arch) {
|
||
|
if (arguments.length === 0) {
|
||
|
return this._src;
|
||
|
}
|
||
|
|
||
|
this._src = this._src || [];
|
||
|
this._src.push({
|
||
|
url: src,
|
||
|
os,
|
||
|
arch
|
||
|
});
|
||
|
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get or set the destination
|
||
|
*
|
||
|
* @param {String} dest
|
||
|
* @api public
|
||
|
*/
|
||
|
dest(dest) {
|
||
|
if (arguments.length === 0) {
|
||
|
return this._dest;
|
||
|
}
|
||
|
|
||
|
this._dest = dest;
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get or set the binary
|
||
|
*
|
||
|
* @param {String} bin
|
||
|
* @api public
|
||
|
*/
|
||
|
use(bin) {
|
||
|
if (arguments.length === 0) {
|
||
|
return this._use;
|
||
|
}
|
||
|
|
||
|
this._use = bin;
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get or set a semver range to test the binary against
|
||
|
*
|
||
|
* @param {String} range
|
||
|
* @api public
|
||
|
*/
|
||
|
version(range) {
|
||
|
if (arguments.length === 0) {
|
||
|
return this._version;
|
||
|
}
|
||
|
|
||
|
this._version = range;
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get path to the binary
|
||
|
*
|
||
|
* @api public
|
||
|
*/
|
||
|
path() {
|
||
|
return path.join(this.dest(), this.use());
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Run
|
||
|
*
|
||
|
* @param {Array} cmd
|
||
|
* @api public
|
||
|
*/
|
||
|
run(cmd = ['--version']) {
|
||
|
return this.findExisting().then(() => {
|
||
|
if (this.options.skipCheck) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
return this.runCheck(cmd);
|
||
|
});
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Run binary check
|
||
|
*
|
||
|
* @param {Array} cmd
|
||
|
* @api private
|
||
|
*/
|
||
|
runCheck(cmd) {
|
||
|
return binCheck(this.path(), cmd).then(works => {
|
||
|
if (!works) {
|
||
|
throw new Error(`The \`${this.path()}\` binary doesn't seem to work correctly`);
|
||
|
}
|
||
|
|
||
|
if (this.version()) {
|
||
|
return binVersionCheck(this.path(), this.version());
|
||
|
}
|
||
|
|
||
|
return Promise.resolve();
|
||
|
});
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Find existing files
|
||
|
*
|
||
|
* @api private
|
||
|
*/
|
||
|
findExisting() {
|
||
|
return statAsync(this.path()).catch(error => {
|
||
|
if (error && error.code === 'ENOENT') {
|
||
|
return this.download();
|
||
|
}
|
||
|
|
||
|
return Promise.reject(error);
|
||
|
});
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Download files
|
||
|
*
|
||
|
* @api private
|
||
|
*/
|
||
|
download() {
|
||
|
const files = osFilterObj(this.src() || []);
|
||
|
const urls = [];
|
||
|
|
||
|
if (files.length === 0) {
|
||
|
return Promise.reject(new Error('No binary found matching your system. It\'s probably not supported.'));
|
||
|
}
|
||
|
|
||
|
files.forEach(file => urls.push(file.url));
|
||
|
|
||
|
return Promise.all(urls.map(url => download(url, this.dest(), {
|
||
|
extract: true,
|
||
|
strip: this.options.strip
|
||
|
}))).then(result => {
|
||
|
const resultingFiles = flatten(result.map((item, index) => {
|
||
|
if (Array.isArray(item)) {
|
||
|
return item.map(file => file.path);
|
||
|
}
|
||
|
|
||
|
const parsedUrl = url.parse(files[index].url);
|
||
|
const parsedPath = path.parse(parsedUrl.pathname);
|
||
|
|
||
|
return parsedPath.base;
|
||
|
}));
|
||
|
|
||
|
return Promise.all(resultingFiles.map(fileName => {
|
||
|
return chmodAsync(path.join(this.dest(), fileName), 0o755);
|
||
|
}));
|
||
|
});
|
||
|
}
|
||
|
};
|
||
|
|
||
|
function flatten(arr) {
|
||
|
return arr.reduce((acc, elem) => {
|
||
|
if (Array.isArray(elem)) {
|
||
|
acc.push(...elem);
|
||
|
} else {
|
||
|
acc.push(elem);
|
||
|
}
|
||
|
|
||
|
return acc;
|
||
|
}, []);
|
||
|
}
|