Adding Setup tests and settings fix

This commit is contained in:
Jeffrey Morgan 2015-02-11 15:09:17 -08:00
Родитель 67569bead1
Коммит 621b0503ab
15 изменённых файлов: 370 добавлений и 211 удалений

5
.gitignore поставляемый
Просмотреть файл

@ -8,13 +8,12 @@ npm-debug.log
identity
# Resources
resources/boot2docker*
resources/boot2docker-*
resources/docker-*
# Cache
cache
resources/settings*
# Tests
.test
settings.json

Просмотреть файл

@ -0,0 +1,119 @@
jest.dontMock('../src/SetupStore');
var setupStore = require('../src/SetupStore');
var virtualBox = require('../src/VirtualBox');
var util = require('../src/Util');
var boot2docker = require('../src/Boot2Docker');
var setupUtil = require('../src/SetupUtil');
var Promise = require('bluebird');
describe('SetupStore', function () {
describe('download step', function () {
util.packagejson.mockReturnValue({});
pit('downloads virtualbox if it is not installed', function () {
virtualBox.installed.mockReturnValue(false);
setupUtil.download.mockReturnValue(Promise.resolve());
return setupStore.steps().downloadVirtualBox.run().then(() => {
// TODO: make sure download was called with the right args
expect(setupUtil.download).toBeCalled();
});
});
pit('downloads virtualbox if it is installed but has an outdated version', function () {
virtualBox.installed.mockReturnValue(true);
virtualBox.version.mockReturnValue(Promise.resolve('4.3.16'));
setupUtil.compareVersions.mockReturnValue(-1);
setupUtil.download.mockReturnValue(Promise.resolve());
return setupStore.steps().downloadVirtualBox.run().then(() => {
expect(setupUtil.download).toBeCalled();
});
});
pit('skips download if virtualbox is already installed', function () {
virtualBox.installed.mockReturnValue(true);
virtualBox.version.mockReturnValue(Promise.resolve('4.3.20'));
setupUtil.download.mockClear();
setupUtil.download.mockReturnValue(Promise.resolve());
setupUtil.compareVersions.mockReturnValue(1);
return setupStore.steps().downloadVirtualBox.run().then(() => {
expect(setupUtil.download).not.toBeCalled();
});
});
});
describe('install step', function () {
pit('installs virtualbox if it is not installed', function () {
virtualBox.installed.mockReturnValue(false);
virtualBox.killall.mockReturnValue(Promise.resolve());
setupUtil.isSudo.mockReturnValue(Promise.resolve(false));
util.exec.mockReturnValue(Promise.resolve());
return setupStore.steps().installVirtualBox.run().then(() => {
// TODO: make sure that the right install command was executed
expect(util.exec).toBeCalled();
});
});
pit('installs virtualbox if it is installed but has an outdated version', function () {
virtualBox.installed.mockReturnValue(true);
virtualBox.version.mockReturnValue(Promise.resolve('4.3.16'));
virtualBox.killall.mockReturnValue(Promise.resolve());
setupUtil.isSudo.mockReturnValue(Promise.resolve(false));
setupUtil.compareVersions.mockReturnValue(-1);
util.exec.mockReturnValue(Promise.resolve());
return setupStore.steps().installVirtualBox.run().then(() => {
// TODO: make sure the right install command was executed
expect(virtualBox.killall).toBeCalled();
expect(util.exec).toBeCalled();
});
});
pit('skips install if virtualbox is already installed', function () {
virtualBox.installed.mockReturnValue(true);
virtualBox.version.mockReturnValue(Promise.resolve('4.3.20'));
setupUtil.isSudo.mockReturnValue(Promise.resolve(false));
setupUtil.compareVersions.mockReturnValue(-1);
util.exec.mockReturnValue(Promise.resolve());
return setupStore.steps().installVirtualBox.run().then(() => {
virtualBox.killall.mockClear();
util.exec.mockClear();
expect(virtualBox.killall).not.toBeCalled();
expect(util.exec).not.toBeCalled();
});
});
});
describe('init step', function () {
virtualBox.vmdestroy.mockReturnValue(Promise.resolve());
pit('inintializes the boot2docker vm if it does not exist', function () {
boot2docker.exists.mockReturnValue(Promise.resolve(false));
boot2docker.init.mockReturnValue(Promise.resolve());
return setupStore.steps().initBoot2Docker.run().then(() => {
expect(boot2docker.init).toBeCalled();
});
});
pit('upgrades the boot2docker vm if it exists and is out of date', function () {
boot2docker.exists.mockReturnValue(Promise.resolve(true));
boot2docker.isoversion.mockReturnValue('1.0');
boot2docker.haskeys.mockReturnValue(true);
boot2docker.stop.mockReturnValue(Promise.resolve());
boot2docker.upgrade.mockReturnValue(Promise.resolve());
setupUtil.compareVersions.mockReturnValue(-1);
return setupStore.steps().initBoot2Docker.run().then(() => {
boot2docker.init.mockClear();
expect(boot2docker.init).not.toBeCalled();
expect(boot2docker.upgrade).toBeCalled();
});
});
});
describe('start step', function () {
pit('starts the boot2docker vm if it is not running', function () {
boot2docker.status.mockReturnValue(false);
boot2docker.waitstatus.mockReturnValue(Promise.resolve());
boot2docker.start.mockReturnValue(Promise.resolve());
return setupStore.steps().startBoot2Docker.run().then(() => {
expect(boot2docker.start).toBeCalled();
});
});
});
});

Просмотреть файл

@ -1,11 +1,11 @@
jest.dontMock('../src/Virtualbox');
var virtualbox = require('../src/Virtualbox');
var virtualBox = require('../src/Virtualbox');
var util = require('../src/Util');
var Promise = require('bluebird');
describe('Virtualbox', function () {
describe('VirtualBox', function () {
it('returns the right command', function () {
expect(virtualbox.command()).toBe('/usr/bin/VBoxManage');
expect(virtualBox.command()).toBe('/usr/bin/VBoxManage');
});
describe('version 4.3.20r96996', function () {
@ -13,7 +13,7 @@ describe('Virtualbox', function () {
util.exec.mockImplementation(function () {
return Promise.resolve('4.3.20r96996');
});
return virtualbox.version().then(function (version) {
return virtualBox.version().then(function (version) {
expect(version).toBe('4.3.20');
});
});

Просмотреть файл

@ -6,7 +6,12 @@ var autoUpdater = require('auto-updater');
var BrowserWindow = require('browser-window');
var ipc = require('ipc');
var argv = require('minimist')(process.argv);
var settingsjson = JSON.parse(fs.readFileSync(path.join(__dirname, '..', 'settings.json'), 'utf8'));
var settingsjson;
try {
settingsjson = JSON.parse(fs.readFileSync(path.join(__dirname, '..', 'settings.json'), 'utf8'));
} catch (err) {
settingsjson = {};
}
process.env.NODE_PATH = __dirname + '/../node_modules';
process.env.RESOURCES_PATH = __dirname + '/../resources';
@ -65,7 +70,7 @@ app.on('ready', function() {
// Auto Updates
if (process.env.NODE_ENV !== 'development' && !argv.test) {
autoUpdater.setFeedUrl('https://updates.kitematic.com/releases/latest?version=' + app.getVersion() + '&beta=' + settingsjson.beta);
autoUpdater.setFeedUrl('https://updates.kitematic.com/releases/latest?version=' + app.getVersion() + '&beta=' + !!settingsjson.beta);
autoUpdater.on('checking-for-update', function () {
console.log('Checking for update...');

14
deps
Просмотреть файл

@ -2,14 +2,26 @@
BASE="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
BOOT2DOCKER_CLI_VERSION=$(node -pe "JSON.parse(process.argv[1])['boot2docker-version']" "$(cat $BASE/package.json)")
BOOT2DOCKER_CLI_FILE=boot2docker-$BOOT2DOCKER_CLI_VERSION
DOCKER_CLI_VERSION=$(node -pe "JSON.parse(process.argv[1])['docker-version']" "$(cat $BASE/package.json)")
DOCKER_CLI_FILE=docker-$DOCKER_CLI_VERSION
pushd $BASE/resources > /dev/null
if [ ! -f $BOOT2DOCKER_CLI_FILE ]; then
echo "-----> Downloading Boot2docker CLI..."
rm -rf boot2docker-*
curl -L -o $BOOT2DOCKER_CLI_FILE https://github.com/boot2docker/boot2docker-cli/releases/download/v${BOOT2DOCKER_CLI_VERSION}/boot2docker-v${BOOT2DOCKER_CLI_VERSION}-darwin-amd64
curl -L -o $BOOT2DOCKER_CLI_FILE https://github.com/boot2docker/boot2docker-cli/releases/download/v$BOOT2DOCKER_CLI_VERSION/boot2docker-v$BOOT2DOCKER_CLI_VERSION-darwin-amd64
chmod +x $BOOT2DOCKER_CLI_FILE
fi
if [ ! -f $DOCKER_CLI_FILE ]; then
echo "-----> Downloading Docker CLI..."
rm -rf docker-*
curl -L -o docker-$DOCKER_CLI_VERSION.tgz https://get.docker.com/builds/Darwin/x86_64/docker-$DOCKER_CLI_VERSION.tgz
tar xvzf docker-$DOCKER_CLI_VERSION.tgz --strip=3
rm docker-$DOCKER_CLI_VERSION.tgz
mv docker docker-$DOCKER_CLI_VERSION
chmod +x $DOCKER_CLI_FILE
fi
popd > /dev/null

Просмотреть файл

@ -27,13 +27,19 @@
"jest": {
"scriptPreprocessor": "preprocessor.js",
"unmockedModulePathPatterns": [
"node_modules/request",
"tty",
"net",
"crypto",
"stream",
"object-assign",
"underscore",
"node_modules/react",
"node_modules/bluebird",
"node_modules/6to5"
]
},
"boot2docker-version": "1.4.1",
"docker-version": "1.5.0",
"boot2docker-version": "1.5.0",
"atom-shell-version": "0.21.1",
"virtualbox-version": "4.3.20",
"virtualbox-filename": "VirtualBox-4.3.20.pkg",
@ -45,20 +51,17 @@
"bluebird": "^2.9.6",
"bugsnag-js": "git+https://git@github.com/bugsnag/bugsnag-js",
"dockerode": "2.0.4",
"download": "^4.0.0",
"exec": "0.1.2",
"html2canvas": "^0.5.0-alpha2",
"jquery": "^2.1.3",
"minimist": "^1.1.0",
"node-uuid": "1.4.1",
"object-assign": "^2.0.0",
"open": "0.0.5",
"react": "^0.12.2",
"react-bootstrap": "^0.13.2",
"react-retina-image": "^1.1.2",
"react-router": "^0.11.6",
"request": "^2.51.0",
"request-progress": "0.3.1",
"request-promise": "^0.3.3",
"request": "^2.53.0",
"request-progress": "^0.3.1",
"retina.js": "^1.1.0",
"rimraf": "^2.2.8",
"underscore": "^1.7.0"

Просмотреть файл

@ -9,7 +9,12 @@ var router = require('./router');
var boot2docker = require('./boot2docker');
var ContainerStore = require('./ContainerStore');
var SetupStore = require('./ContainerStore');
var settingsjson = JSON.parse(fs.readFileSync(path.join(__dirname, '..', 'settings.json'), 'utf8'));
var settingsjson;
try {
settingsjson = JSON.parse(fs.readFileSync(path.join(__dirname, '..', 'settings.json'), 'utf8'));
} catch (err) {
settingsjson = {};
}
if (process.env.NODE_ENV === 'development') {
var head = document.getElementsByTagName('head')[0];

Просмотреть файл

@ -194,7 +194,7 @@ var NewContainer = React.createClass({
<div className="new-container">
<div className="new-container-header">
<div className="text">
Pick an image to create new container.
Select an image to create a new container.
</div>
<div className="search">
<div className="search-bar">

Просмотреть файл

@ -13,7 +13,9 @@ var Radial = React.createClass({
var classes = React.addons.classSet({
'radial-progress': true,
'radial-spinner': this.props.spin,
'radial-negative': this.props.error
'radial-negative': this.props.error,
'radial-thick': this.props.thick || false,
'radial-gray': this.props.gray || false
});
return (
<div className={classes} data-progress={this.props.progress}>

Просмотреть файл

@ -21,6 +21,11 @@ var Setup = React.createClass({
componentDidMount: function () {
this.update();
},
componentDidUnmount: function () {
SetupStore.removeListener(SetupStore.PROGRESS_EVENT, this.update);
SetupStore.removeListener(SetupStore.STEP_EVENT, this.update);
SetupStore.removeListener(SetupStore.ERROR_EVENT, this.update);
},
update: function () {
this.setState({
progress: SetupStore.percent(),
@ -28,139 +33,67 @@ var Setup = React.createClass({
error: SetupStore.error()
});
},
renderDownloadingVirtualboxStep: function () {
var message = "VirtualBox is being downloaded from Oracle's servers. Kitematic requires VirtualBox to run.";
renderContents: function () {
var img = 'virtualbox.png';
if (SetupStore.step().name.indexOf('Boot2Docker') !== -1) {
img = 'boot2docker.png';
}
return (
<div className="setup">
<Header />
<div className="image">
<div className="contents">
<RetinaImage img src="virtualbox.png"/>
<div className="detail">
<Radial progress={this.state.progress}/>
</div>
</div>
</div>
<div className="desc">
<div className="content">
<h4>Step 1 out of 4</h4>
<h1>Downloading VirtualBox</h1>
<p>{message}</p>
</div>
</div>
</div>
);
},
renderInstallingVirtualboxStep: function () {
var message = 'VirtualBox is being installed in the background. We may need you to type in your password to continue.';
return (
<div className="setup">
<Header />
<div className="image">
<div className="contents">
<RetinaImage img src="virtualbox.png"/>
<div className="detail">
<Radial progress="90" spin="true"/>
</div>
</div>
</div>
<div className="desc">
<div className="content">
<h4>Step 2 out of 4</h4>
<h1>Installing VirtualBox</h1>
<p>{message}</p>
</div>
</div>
</div>
);
},
renderInitBoot2DockerStep: function () {
var message = 'To run Docker containers on your computer, we are setting up a Linux virtual machine provided by boot2docker.';
return (
<div className="setup">
<Header />
<div className="image">
<div className="contents">
<RetinaImage img src="boot2docker.png"/>
<div className="detail">
<Radial progress="90" spin="true"/>
</div>
</div>
</div>
<div className="desc">
<div className="content">
<h4>Step 3 out of 4</h4>
<h1>Setting up Docker VM</h1>
<p>{message}</p>
</div>
</div>
</div>
);
},
renderStartBoot2DockerStep: function () {
var message = 'Kitematic is starting the boot2docker VM. This may take about a minute.';
return (
<div className="setup">
<Header />
<div className="image">
<div className="contents">
<RetinaImage img src="boot2docker.png"/>
<div className="detail">
<Radial progress="90" spin="true"/>
</div>
</div>
</div>
<div className="desc">
<div className="content">
<h4>Step 4 out of 4</h4>
<h1>Starting Docker VM</h1>
<p>{message}</p>
</div>
<div className="contents">
<RetinaImage src={img}/>
<div className="detail">
<Radial progress={SetupStore.percent()} thick={true} gray={true}/>
</div>
</div>
);
},
renderStep: function () {
switch(this.state.step) {
case 'download_virtualbox':
return this.renderDownloadingVirtualboxStep();
case 'install_virtualbox':
return this.renderInstallingVirtualboxStep();
case 'cleanup_kitematic':
return this.renderInitBoot2DockerStep();
case 'init_boot2docker':
return this.renderInitBoot2DockerStep();
case 'start_boot2docker':
return this.renderStartBoot2DockerStep();
default:
return false;
}
},
render: function () {
var step = this.renderStep();
if (this.state.error) {
return (
<div className="setup">
<Header />
<div className="image">
<div className="contents">
<RetinaImage img src="install-error.png"/>
<div className="detail">
</div>
</div>
return (
<div className="setup">
<Header />
<div className="image">
{this.renderContents()}
</div>
<div className="desc">
<div className="content">
<h4>Step {SetupStore.number()} out of {SetupStore.stepCount()}</h4>
<h1>{SetupStore.step().title}</h1>
<p>{SetupStore.step().message}</p>
</div>
<div className="desc">
<div className="content">
<h4>Installation Error</h4>
<h1>We&#39;re Sorry!</h1>
<p>There seem to be an unexpected error with Kitematic:</p>
<p className="error">{this.state.error}</p>
</div>
</div>
);
},
renderError: function () {
return (
<div className="setup">
<Header />
<div className="image">
<div className="contents">
<RetinaImage img src="install-error.png"/>
<div className="detail">
</div>
</div>
</div>
);
<div className="desc">
<div className="content">
<h4>Installation Error</h4>
<h1>We&#39;re Sorry!</h1>
<p>There seems to have been an unexpected error with Kitematic:</p>
<p className="error">{this.state.error.message}</p>
</div>
</div>
</div>
);
},
render: function () {
if (!SetupStore.step()) {
return false;
}
if (this.state.error) {
return this.renderError();
} else {
return step;
return this.renderStep();
}
}
});

Просмотреть файл

@ -1,57 +1,74 @@
var EventEmitter = require('events').EventEmitter;
var assign = require('object-assign');
var fs = require('fs');
var _ = require('underscore');
var path = require('path');
var Promise = require('bluebird');
var boot2docker = require('./Boot2Docker');
var virtualbox = require('./Virtualbox');
var virtualBox = require('./VirtualBox');
var setupUtil = require('./SetupUtil');
var util = require('./Util');
var packagejson = JSON.parse(fs.readFileSync(path.join(__dirname, '..', 'package.json'), 'utf8'));
var _percent = 0;
var SUDO_PROMPT = 'Kitematic requires administrative privileges to install VirtualBox.';
var _currentStep = null;
var _error = null;
var VIRTUALBOX_FILE = `https://github.com/kitematic/virtualbox/releases/download/${packagejson['virtualbox-version']}/${packagejson['virtualbox-filename']}`;
var SUDO_PROMPT = 'Kitematic requires administrative privileges to install VirtualBox.';
var SetupStore = assign(EventEmitter.prototype, {
PROGRESS_EVENT: 'setup_progress',
STEP_EVENT: 'setup_step',
ERROR_EVENT: 'setup_error',
downloadVirtualboxStep: Promise.coroutine(function* () {
if (virtualbox.installed()) {
var version = yield virtualbox.version();
var _steps = [{
name: 'downloadVirtualBox',
title: 'Downloading VirtualBox',
message: 'VirtualBox is being downloaded. Kitematic requires VirtualBox to run containers.',
totalPercent: 35,
percent: 0,
run: Promise.coroutine(function* (progressCallback) {
var packagejson = util.packagejson();
if (virtualBox.installed()) {
var version = yield virtualBox.version();
if (setupUtil.compareVersions(version, packagejson['virtualbox-required-version']) >= 0) {
return;
}
}
yield setupUtil.download(VIRTUALBOX_FILE, path.join(setupUtil.supportDir(), packagejson['virtualbox-filename']), packagejson.checksum, percent => {
_percent = percent;
SetupStore.emit(SetupStore.PROGRESS_EVENT);
var virtualBoxFile = `https://github.com/kitematic/virtualbox/releases/download/${packagejson['virtualbox-version']}/${packagejson['virtualbox-filename']}`;
yield setupUtil.download(virtualBoxFile, path.join(setupUtil.supportDir(), packagejson['virtualbox-filename']), packagejson['virtualbox-checksum'], percent => {
progressCallback(percent);
});
}),
installVirtualboxStep: Promise.coroutine(function* () {
if (virtualbox.installed()) {
var version = yield virtualbox.version();
})
}, {
name: 'installVirtualBox',
title: 'Installing VirtualBox',
message: "VirtualBox is being installed in the background. We may need you to type in your password to continue.",
totalPercent: 5,
percent: 0,
seconds: 5,
run: Promise.coroutine(function* () {
var packagejson = util.packagejson();
if (virtualBox.installed()) {
var version = yield virtualBox.version();
if (setupUtil.compareVersions(version, packagejson['virtualbox-required-version']) >= 0) {
return;
}
yield virtualbox.killall();
yield virtualBox.killall();
}
var isSudo = yield setupUtil.isSudo();
var iconPath = path.join(setupUtil.resourceDir(), 'kitematic.icns');
var sudoCmd = isSudo ? ['sudo'] : [path.join(setupUtil.resourceDir(), 'cocoasudo'), '--icon=' + iconPath, `--prompt=${SUDO_PROMPT}`];
sudoCmd.push.apply(sudoCmd, ['installer', '-pkg', path.join(setupUtil.supportDir(), packagejson['virtualbox-filename']), '-target', '/']);
yield util.exec(sudoCmd);
}),
cleanupKitematicStep: function () {
return virtualbox.vmdestroy('kitematic-vm');
},
initBoot2DockerStep: Promise.coroutine(function* () {
try {
yield util.exec(sudoCmd);
} catch (err) {
console.log('Could not install virtualbox...');
}
})
}, {
name: 'initBoot2Docker',
title: 'Setting up Docker VM',
message: "To run Docker containers on your computer, we are setting up a Linux virtual machine provided by boot2docker.",
totalPercent: 15,
percent: 0,
seconds: 11,
run: Promise.coroutine(function* (progressCallback) {
yield virtualBox.vmdestroy('kitematic-vm');
var exists = yield boot2docker.exists();
if (!exists) {
setupUtil.simulateProgress(this.seconds, progressCallback);
yield boot2docker.init();
return;
}
@ -62,63 +79,77 @@ var SetupStore = assign(EventEmitter.prototype, {
var isoversion = boot2docker.isoversion();
if (!isoversion || setupUtil.compareVersions(isoversion, boot2docker.version()) < 0) {
setupUtil.simulateProgress(this.seconds, progressCallback);
yield boot2docker.stop();
yield boot2docker.upgrade();
}
}),
startBoot2DockerStep: function () {
})
}, {
name: 'startBoot2Docker',
title: 'Starting Docker VM',
message: "Kitematic is starting the boot2docker VM. This may take about a minute.",
totalPercent: 45,
percent: 0,
seconds: 35,
run: function (progressCallback) {
return boot2docker.waitstatus('saving').then(boot2docker.status).then(status => {
setupUtil.simulateProgress(this.seconds, progressCallback);
if (status !== 'running') {
return boot2docker.start();
}
});
},
}
}];
var SetupStore = assign(EventEmitter.prototype, {
PROGRESS_EVENT: 'setup_progress',
STEP_EVENT: 'setup_step',
ERROR_EVENT: 'setup_error',
step: function () {
if (_currentStep) {
return _currentStep;
} else {
return '';
}
return _currentStep || _steps[0];
},
steps: function () {
return _.indexBy(_steps, 'name');
},
stepCount: function () {
return _steps.length;
},
number: function () {
return _.indexOf(_steps, _currentStep) + 1;
},
percent: function () {
return _percent;
var total = 0;
_.each(_steps, step => {
total += step.totalPercent * step.percent / 100;
});
return Math.min(Math.round(total), 99);
},
error: function () {
return _error;
},
run: Promise.coroutine(function* () {
var steps = [{
name: 'download_virtualbox',
run: this.downloadVirtualboxStep
}, {
name: 'install_virtualbox',
run: this.installVirtualboxStep
}, {
name: 'cleanup_kitematic',
run: this.cleanupKitematicStep
}, {
name: 'init_boot2docker',
run: this.initBoot2DockerStep
}, {
name: 'start_boot2docker',
run: this.startBoot2DockerStep
}];
_error = null;
for (let step of steps) {
console.log(step.name);
_currentStep = step.name;
_percent = 0;
this.emit(this.STEP_EVENT);
run: Promise.coroutine(function* (startAt) {
startAt = startAt || 0;
for (let step of _steps.slice(startAt)) {
_currentStep = step;
step.percent = 0;
try {
yield step.run();
console.log(step.name);
this.emit(this.STEP_EVENT);
yield step.run(percent => {
if (_currentStep) {
step.percent = percent;
this.emit(this.PROGRESS_EVENT);
}
});
step.percent = 100;
} catch (err) {
console.log(err);
console.log(err.stack);
_error = err;
this.emit(this.ERROR_EVENT);
throw err;
}
}
_currentStep = null;
})
});

Просмотреть файл

@ -1,9 +1,10 @@
var _ = require('underscore');
var crypto = require('crypto');
var exec = require('exec');
var fs = require('fs');
var request = require('request');
var progress = require('request-progress');
var path = require('path');
var crypto = require('crypto');
var fs = require('fs');
var exec = require('exec');
var Promise = require('bluebird');
var SetupUtil = {
@ -31,18 +32,29 @@ var SetupUtil = {
});
});
},
simulateProgress: function (estimateSeconds, progress) {
var times = _.range(0, estimateSeconds * 1000, 200);
var timers = [];
_.each(times, time => {
var timer = setTimeout(() => {
progress(100 * time / (estimateSeconds * 1000));
}, time);
timers.push(timer);
});
},
download: function (url, filename, checksum, percentCallback) {
return new Promise((resolve, reject) => {
if (fs.existsSync(filename)) {
var existingChecksum = crypto.createHash('sha256').update(fs.readFileSync(filename), 'utf8').digest('hex');
if (existingChecksum === checksum) {
resolve();
return;
} else {
fs.unlinkSync(filename);
}
}
progress(request({ uri: url, rejectUnauthorized: false }), { throttle: 250 }).on('progress', state => {
progress(request({ uri: url, rejectUnauthorized: false }), { throttle: 10 }).on('progress', state => {
if (percentCallback) {
percentCallback(state.percent);
}

Просмотреть файл

@ -1,5 +1,7 @@
var exec = require('exec');
var Promise = require('bluebird');
var fs = require('fs');
var path = require('path');
module.exports = {
exec: function (args) {
@ -14,5 +16,8 @@ module.exports = {
},
home: function () {
return process.env[(process.platform === 'win32') ? 'USERPROFILE' : 'HOME'];
},
packagejson: function () {
return JSON.parse(fs.readFileSync(path.join(__dirname, '..', 'package.json'), 'utf8'));
}
};

Просмотреть файл

@ -16,13 +16,14 @@
}
@circle-size: 140px;
@circle-background: #F2F2F2;
@circle-background: red;
@inset-size: @circle-size - 4;
@inset-color: white;
@transition-length: 1s;
// @percentage-color: #3FD899;
@percentage-font-size: 24px;
@percentage-text-width: 57px;
background: white;
margin: 0 auto;
width: @circle-size;
@ -84,6 +85,37 @@
background-color: @brand-positive;
}
&.radial-thick {
@inset-size: @circle-size - 10;
.inset {
width: @inset-size;
height: @inset-size;
position: absolute;
margin-left: (@circle-size - @inset-size) / 2.0;
margin-top: (@circle-size - @inset-size) / 2.0;
background-color: @inset-color;
border-radius: 100%;
.percentage {
width: @percentage-text-width;
position: absolute;
top: (@inset-size - @percentage-font-size) / 2.0;
left: (@inset-size - @percentage-text-width) / 2.0;
line-height: 1;
text-align: center;
color: @brand-primary;
font-weight: 500;
font-size: @percentage-font-size;
}
}
}
&.radial-gray {
background: #EEE;
}
@i: 0;
@increment: 180deg / 100;
.loop (@i) when (@i <= 100) {

Просмотреть файл

@ -63,6 +63,7 @@
p {
&.error {
color: @brand-danger;
word-wrap: break-word;
}
margin-top: 20px;
}