зеркало из https://github.com/docker/kitematic.git
Adding Setup tests and settings fix
This commit is contained in:
Родитель
67569bead1
Коммит
621b0503ab
|
@ -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
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
|
||||
|
|
19
package.json
19
package.json
|
@ -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'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'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;
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче