mirror of https://github.com/docker/docs.git
Merge pull request #174 from kitematic/jmorgan-settings-fix
Fixing settings, testing SetupStore
This commit is contained in:
commit
54e105e3fd
|
@ -8,13 +8,12 @@ npm-debug.log
|
||||||
identity
|
identity
|
||||||
|
|
||||||
# Resources
|
# Resources
|
||||||
resources/boot2docker*
|
resources/boot2docker-*
|
||||||
|
resources/docker-*
|
||||||
|
|
||||||
# Cache
|
# Cache
|
||||||
cache
|
cache
|
||||||
|
|
||||||
resources/settings*
|
|
||||||
|
|
||||||
# Tests
|
# Tests
|
||||||
.test
|
.test
|
||||||
settings.json
|
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');
|
jest.dontMock('../src/VirtualBox');
|
||||||
var virtualbox = require('../src/Virtualbox');
|
var virtualBox = require('../src/VirtualBox');
|
||||||
var util = require('../src/Util');
|
var util = require('../src/Util');
|
||||||
var Promise = require('bluebird');
|
var Promise = require('bluebird');
|
||||||
|
|
||||||
describe('Virtualbox', function () {
|
describe('VirtualBox', function () {
|
||||||
it('returns the right command', 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 () {
|
describe('version 4.3.20r96996', function () {
|
||||||
|
@ -13,7 +13,7 @@ describe('Virtualbox', function () {
|
||||||
util.exec.mockImplementation(function () {
|
util.exec.mockImplementation(function () {
|
||||||
return Promise.resolve('4.3.20r96996');
|
return Promise.resolve('4.3.20r96996');
|
||||||
});
|
});
|
||||||
return virtualbox.version().then(function (version) {
|
return virtualBox.version().then(function (version) {
|
||||||
expect(version).toBe('4.3.20');
|
expect(version).toBe('4.3.20');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -6,7 +6,12 @@ var autoUpdater = require('auto-updater');
|
||||||
var BrowserWindow = require('browser-window');
|
var BrowserWindow = require('browser-window');
|
||||||
var ipc = require('ipc');
|
var ipc = require('ipc');
|
||||||
var argv = require('minimist')(process.argv);
|
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.NODE_PATH = __dirname + '/../node_modules';
|
||||||
process.env.RESOURCES_PATH = __dirname + '/../resources';
|
process.env.RESOURCES_PATH = __dirname + '/../resources';
|
||||||
|
@ -65,7 +70,7 @@ app.on('ready', function() {
|
||||||
|
|
||||||
// Auto Updates
|
// Auto Updates
|
||||||
if (process.env.NODE_ENV !== 'development' && !argv.test) {
|
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 () {
|
autoUpdater.on('checking-for-update', function () {
|
||||||
console.log('Checking for update...');
|
console.log('Checking for update...');
|
||||||
|
|
14
deps
14
deps
|
@ -2,14 +2,26 @@
|
||||||
BASE="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
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_VERSION=$(node -pe "JSON.parse(process.argv[1])['boot2docker-version']" "$(cat $BASE/package.json)")
|
||||||
BOOT2DOCKER_CLI_FILE=boot2docker-$BOOT2DOCKER_CLI_VERSION
|
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
|
pushd $BASE/resources > /dev/null
|
||||||
|
|
||||||
if [ ! -f $BOOT2DOCKER_CLI_FILE ]; then
|
if [ ! -f $BOOT2DOCKER_CLI_FILE ]; then
|
||||||
echo "-----> Downloading Boot2docker CLI..."
|
echo "-----> Downloading Boot2docker CLI..."
|
||||||
rm -rf boot2docker-*
|
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
|
chmod +x $BOOT2DOCKER_CLI_FILE
|
||||||
fi
|
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
|
popd > /dev/null
|
||||||
|
|
17
package.json
17
package.json
|
@ -27,12 +27,18 @@
|
||||||
"jest": {
|
"jest": {
|
||||||
"scriptPreprocessor": "preprocessor.js",
|
"scriptPreprocessor": "preprocessor.js",
|
||||||
"unmockedModulePathPatterns": [
|
"unmockedModulePathPatterns": [
|
||||||
"node_modules/request",
|
"tty",
|
||||||
|
"net",
|
||||||
|
"crypto",
|
||||||
|
"stream",
|
||||||
|
"object-assign",
|
||||||
|
"underscore",
|
||||||
"node_modules/react",
|
"node_modules/react",
|
||||||
"node_modules/bluebird",
|
"node_modules/bluebird",
|
||||||
"node_modules/6to5"
|
"node_modules/6to5"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"docker-version": "1.4.1",
|
||||||
"boot2docker-version": "1.4.1",
|
"boot2docker-version": "1.4.1",
|
||||||
"atom-shell-version": "0.21.1",
|
"atom-shell-version": "0.21.1",
|
||||||
"virtualbox-version": "4.3.20",
|
"virtualbox-version": "4.3.20",
|
||||||
|
@ -45,20 +51,17 @@
|
||||||
"bluebird": "^2.9.6",
|
"bluebird": "^2.9.6",
|
||||||
"bugsnag-js": "git+https://git@github.com/bugsnag/bugsnag-js",
|
"bugsnag-js": "git+https://git@github.com/bugsnag/bugsnag-js",
|
||||||
"dockerode": "2.0.4",
|
"dockerode": "2.0.4",
|
||||||
|
"download": "^4.0.0",
|
||||||
"exec": "0.1.2",
|
"exec": "0.1.2",
|
||||||
"html2canvas": "^0.5.0-alpha2",
|
|
||||||
"jquery": "^2.1.3",
|
"jquery": "^2.1.3",
|
||||||
"minimist": "^1.1.0",
|
"minimist": "^1.1.0",
|
||||||
"node-uuid": "1.4.1",
|
|
||||||
"object-assign": "^2.0.0",
|
"object-assign": "^2.0.0",
|
||||||
"open": "0.0.5",
|
|
||||||
"react": "^0.12.2",
|
"react": "^0.12.2",
|
||||||
"react-bootstrap": "^0.13.2",
|
"react-bootstrap": "^0.13.2",
|
||||||
"react-retina-image": "^1.1.2",
|
"react-retina-image": "^1.1.2",
|
||||||
"react-router": "^0.11.6",
|
"react-router": "^0.11.6",
|
||||||
"request": "^2.51.0",
|
"request": "^2.53.0",
|
||||||
"request-progress": "0.3.1",
|
"request-progress": "^0.3.1",
|
||||||
"request-promise": "^0.3.3",
|
|
||||||
"retina.js": "^1.1.0",
|
"retina.js": "^1.1.0",
|
||||||
"rimraf": "^2.2.8",
|
"rimraf": "^2.2.8",
|
||||||
"underscore": "^1.7.0"
|
"underscore": "^1.7.0"
|
||||||
|
|
|
@ -9,7 +9,12 @@ var router = require('./router');
|
||||||
var boot2docker = require('./boot2docker');
|
var boot2docker = require('./boot2docker');
|
||||||
var ContainerStore = require('./ContainerStore');
|
var ContainerStore = require('./ContainerStore');
|
||||||
var SetupStore = 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') {
|
if (process.env.NODE_ENV === 'development') {
|
||||||
var head = document.getElementsByTagName('head')[0];
|
var head = document.getElementsByTagName('head')[0];
|
||||||
|
|
|
@ -194,7 +194,7 @@ var NewContainer = React.createClass({
|
||||||
<div className="new-container">
|
<div className="new-container">
|
||||||
<div className="new-container-header">
|
<div className="new-container-header">
|
||||||
<div className="text">
|
<div className="text">
|
||||||
Pick an image to create new container.
|
Select an image to create a new container.
|
||||||
</div>
|
</div>
|
||||||
<div className="search">
|
<div className="search">
|
||||||
<div className="search-bar">
|
<div className="search-bar">
|
||||||
|
|
|
@ -13,7 +13,9 @@ var Radial = React.createClass({
|
||||||
var classes = React.addons.classSet({
|
var classes = React.addons.classSet({
|
||||||
'radial-progress': true,
|
'radial-progress': true,
|
||||||
'radial-spinner': this.props.spin,
|
'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 (
|
return (
|
||||||
<div className={classes} data-progress={this.props.progress}>
|
<div className={classes} data-progress={this.props.progress}>
|
||||||
|
|
|
@ -21,6 +21,11 @@ var Setup = React.createClass({
|
||||||
componentDidMount: function () {
|
componentDidMount: function () {
|
||||||
this.update();
|
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 () {
|
update: function () {
|
||||||
this.setState({
|
this.setState({
|
||||||
progress: SetupStore.percent(),
|
progress: SetupStore.percent(),
|
||||||
|
@ -28,117 +33,38 @@ var Setup = React.createClass({
|
||||||
error: SetupStore.error()
|
error: SetupStore.error()
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
renderDownloadingVirtualboxStep: function () {
|
renderContents: function () {
|
||||||
var message = "VirtualBox is being downloaded from Oracle's servers. Kitematic requires VirtualBox to run.";
|
var img = 'virtualbox.png';
|
||||||
|
if (SetupStore.step().name.indexOf('Boot2Docker') !== -1) {
|
||||||
|
img = 'boot2docker.png';
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<div className="setup">
|
|
||||||
<Header />
|
|
||||||
<div className="image">
|
|
||||||
<div className="contents">
|
<div className="contents">
|
||||||
<RetinaImage img src="virtualbox.png"/>
|
<RetinaImage src={img}/>
|
||||||
<div className="detail">
|
<div className="detail">
|
||||||
<Radial progress={this.state.progress}/>
|
<Radial progress={SetupStore.percent()} thick={true} gray={true}/>
|
||||||
</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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
renderStep: function () {
|
renderStep: function () {
|
||||||
switch(this.state.step) {
|
return (
|
||||||
case 'download_virtualbox':
|
<div className="setup">
|
||||||
return this.renderDownloadingVirtualboxStep();
|
<Header />
|
||||||
case 'install_virtualbox':
|
<div className="image">
|
||||||
return this.renderInstallingVirtualboxStep();
|
{this.renderContents()}
|
||||||
case 'cleanup_kitematic':
|
</div>
|
||||||
return this.renderInitBoot2DockerStep();
|
<div className="desc">
|
||||||
case 'init_boot2docker':
|
<div className="content">
|
||||||
return this.renderInitBoot2DockerStep();
|
<h4>Step {SetupStore.number()} out of {SetupStore.stepCount()}</h4>
|
||||||
case 'start_boot2docker':
|
<h1>{SetupStore.step().title}</h1>
|
||||||
return this.renderStartBoot2DockerStep();
|
<p>{SetupStore.step().message}</p>
|
||||||
default:
|
</div>
|
||||||
return false;
|
</div>
|
||||||
}
|
</div>
|
||||||
|
);
|
||||||
},
|
},
|
||||||
render: function () {
|
renderError: function () {
|
||||||
var step = this.renderStep();
|
|
||||||
if (this.state.error) {
|
|
||||||
return (
|
return (
|
||||||
<div className="setup">
|
<div className="setup">
|
||||||
<Header />
|
<Header />
|
||||||
|
@ -153,14 +79,21 @@ var Setup = React.createClass({
|
||||||
<div className="content">
|
<div className="content">
|
||||||
<h4>Installation Error</h4>
|
<h4>Installation Error</h4>
|
||||||
<h1>We're Sorry!</h1>
|
<h1>We're Sorry!</h1>
|
||||||
<p>There seem to be an unexpected error with Kitematic:</p>
|
<p>There seems to have been an unexpected error with Kitematic:</p>
|
||||||
<p className="error">{this.state.error}</p>
|
<p className="error">{this.state.error.message}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
},
|
||||||
|
render: function () {
|
||||||
|
if (!SetupStore.step()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (this.state.error) {
|
||||||
|
return this.renderError();
|
||||||
} else {
|
} else {
|
||||||
return step;
|
return this.renderStep();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,57 +1,74 @@
|
||||||
var EventEmitter = require('events').EventEmitter;
|
var EventEmitter = require('events').EventEmitter;
|
||||||
var assign = require('object-assign');
|
var assign = require('object-assign');
|
||||||
var fs = require('fs');
|
var _ = require('underscore');
|
||||||
var path = require('path');
|
var path = require('path');
|
||||||
var Promise = require('bluebird');
|
var Promise = require('bluebird');
|
||||||
var boot2docker = require('./Boot2Docker');
|
var boot2docker = require('./Boot2Docker');
|
||||||
var virtualbox = require('./Virtualbox');
|
var virtualBox = require('./VirtualBox');
|
||||||
var setupUtil = require('./SetupUtil');
|
var setupUtil = require('./SetupUtil');
|
||||||
var util = require('./Util');
|
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 _currentStep = null;
|
||||||
var _error = null;
|
var _error = null;
|
||||||
|
|
||||||
var VIRTUALBOX_FILE = `https://github.com/kitematic/virtualbox/releases/download/${packagejson['virtualbox-version']}/${packagejson['virtualbox-filename']}`;
|
var _steps = [{
|
||||||
var SUDO_PROMPT = 'Kitematic requires administrative privileges to install VirtualBox.';
|
name: 'downloadVirtualBox',
|
||||||
|
title: 'Downloading VirtualBox',
|
||||||
var SetupStore = assign(EventEmitter.prototype, {
|
message: 'VirtualBox is being downloaded. Kitematic requires VirtualBox to run containers.',
|
||||||
PROGRESS_EVENT: 'setup_progress',
|
totalPercent: 35,
|
||||||
STEP_EVENT: 'setup_step',
|
percent: 0,
|
||||||
ERROR_EVENT: 'setup_error',
|
run: Promise.coroutine(function* (progressCallback) {
|
||||||
downloadVirtualboxStep: Promise.coroutine(function* () {
|
var packagejson = util.packagejson();
|
||||||
if (virtualbox.installed()) {
|
if (virtualBox.installed()) {
|
||||||
var version = yield virtualbox.version();
|
var version = yield virtualBox.version();
|
||||||
if (setupUtil.compareVersions(version, packagejson['virtualbox-required-version']) >= 0) {
|
if (setupUtil.compareVersions(version, packagejson['virtualbox-required-version']) >= 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
yield setupUtil.download(VIRTUALBOX_FILE, path.join(setupUtil.supportDir(), packagejson['virtualbox-filename']), packagejson.checksum, percent => {
|
var virtualBoxFile = `https://github.com/kitematic/virtualbox/releases/download/${packagejson['virtualbox-version']}/${packagejson['virtualbox-filename']}`;
|
||||||
_percent = percent;
|
yield setupUtil.download(virtualBoxFile, path.join(setupUtil.supportDir(), packagejson['virtualbox-filename']), packagejson['virtualbox-checksum'], percent => {
|
||||||
SetupStore.emit(SetupStore.PROGRESS_EVENT);
|
progressCallback(percent);
|
||||||
});
|
});
|
||||||
}),
|
})
|
||||||
installVirtualboxStep: Promise.coroutine(function* () {
|
}, {
|
||||||
if (virtualbox.installed()) {
|
name: 'installVirtualBox',
|
||||||
var version = yield virtualbox.version();
|
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) {
|
if (setupUtil.compareVersions(version, packagejson['virtualbox-required-version']) >= 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
yield virtualbox.killall();
|
yield virtualBox.killall();
|
||||||
}
|
}
|
||||||
var isSudo = yield setupUtil.isSudo();
|
var isSudo = yield setupUtil.isSudo();
|
||||||
var iconPath = path.join(setupUtil.resourceDir(), 'kitematic.icns');
|
var iconPath = path.join(setupUtil.resourceDir(), 'kitematic.icns');
|
||||||
var sudoCmd = isSudo ? ['sudo'] : [path.join(setupUtil.resourceDir(), 'cocoasudo'), '--icon=' + iconPath, `--prompt=${SUDO_PROMPT}`];
|
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', '/']);
|
sudoCmd.push.apply(sudoCmd, ['installer', '-pkg', path.join(setupUtil.supportDir(), packagejson['virtualbox-filename']), '-target', '/']);
|
||||||
|
try {
|
||||||
yield util.exec(sudoCmd);
|
yield util.exec(sudoCmd);
|
||||||
}),
|
} catch (err) {
|
||||||
cleanupKitematicStep: function () {
|
console.log('Could not install virtualbox...');
|
||||||
return virtualbox.vmdestroy('kitematic-vm');
|
}
|
||||||
},
|
})
|
||||||
initBoot2DockerStep: Promise.coroutine(function* () {
|
}, {
|
||||||
|
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();
|
var exists = yield boot2docker.exists();
|
||||||
if (!exists) {
|
if (!exists) {
|
||||||
|
setupUtil.simulateProgress(this.seconds, progressCallback);
|
||||||
yield boot2docker.init();
|
yield boot2docker.init();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -62,63 +79,77 @@ var SetupStore = assign(EventEmitter.prototype, {
|
||||||
|
|
||||||
var isoversion = boot2docker.isoversion();
|
var isoversion = boot2docker.isoversion();
|
||||||
if (!isoversion || setupUtil.compareVersions(isoversion, boot2docker.version()) < 0) {
|
if (!isoversion || setupUtil.compareVersions(isoversion, boot2docker.version()) < 0) {
|
||||||
|
setupUtil.simulateProgress(this.seconds, progressCallback);
|
||||||
yield boot2docker.stop();
|
yield boot2docker.stop();
|
||||||
yield boot2docker.upgrade();
|
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 => {
|
return boot2docker.waitstatus('saving').then(boot2docker.status).then(status => {
|
||||||
|
setupUtil.simulateProgress(this.seconds, progressCallback);
|
||||||
if (status !== 'running') {
|
if (status !== 'running') {
|
||||||
return boot2docker.start();
|
return boot2docker.start();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
|
||||||
step: function () {
|
|
||||||
if (_currentStep) {
|
|
||||||
return _currentStep;
|
|
||||||
} else {
|
|
||||||
return '';
|
|
||||||
}
|
}
|
||||||
|
}];
|
||||||
|
|
||||||
|
var SetupStore = assign(EventEmitter.prototype, {
|
||||||
|
PROGRESS_EVENT: 'setup_progress',
|
||||||
|
STEP_EVENT: 'setup_step',
|
||||||
|
ERROR_EVENT: 'setup_error',
|
||||||
|
step: function () {
|
||||||
|
return _currentStep || _steps[0];
|
||||||
|
},
|
||||||
|
steps: function () {
|
||||||
|
return _.indexBy(_steps, 'name');
|
||||||
|
},
|
||||||
|
stepCount: function () {
|
||||||
|
return _steps.length;
|
||||||
|
},
|
||||||
|
number: function () {
|
||||||
|
return _.indexOf(_steps, _currentStep) + 1;
|
||||||
},
|
},
|
||||||
percent: function () {
|
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 () {
|
error: function () {
|
||||||
return _error;
|
return _error;
|
||||||
},
|
},
|
||||||
run: Promise.coroutine(function* () {
|
run: Promise.coroutine(function* (startAt) {
|
||||||
var steps = [{
|
startAt = startAt || 0;
|
||||||
name: 'download_virtualbox',
|
for (let step of _steps.slice(startAt)) {
|
||||||
run: this.downloadVirtualboxStep
|
_currentStep = step;
|
||||||
}, {
|
step.percent = 0;
|
||||||
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);
|
|
||||||
try {
|
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) {
|
} catch (err) {
|
||||||
console.log(err);
|
console.log(err.stack);
|
||||||
_error = err;
|
_error = err;
|
||||||
this.emit(this.ERROR_EVENT);
|
this.emit(this.ERROR_EVENT);
|
||||||
throw err;
|
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 request = require('request');
|
||||||
var progress = require('request-progress');
|
var progress = require('request-progress');
|
||||||
var path = require('path');
|
var path = require('path');
|
||||||
var crypto = require('crypto');
|
|
||||||
var fs = require('fs');
|
|
||||||
var exec = require('exec');
|
|
||||||
var Promise = require('bluebird');
|
var Promise = require('bluebird');
|
||||||
|
|
||||||
var SetupUtil = {
|
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) {
|
download: function (url, filename, checksum, percentCallback) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
if (fs.existsSync(filename)) {
|
if (fs.existsSync(filename)) {
|
||||||
var existingChecksum = crypto.createHash('sha256').update(fs.readFileSync(filename), 'utf8').digest('hex');
|
var existingChecksum = crypto.createHash('sha256').update(fs.readFileSync(filename), 'utf8').digest('hex');
|
||||||
if (existingChecksum === checksum) {
|
if (existingChecksum === checksum) {
|
||||||
resolve();
|
resolve();
|
||||||
|
return;
|
||||||
} else {
|
} else {
|
||||||
fs.unlinkSync(filename);
|
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) {
|
if (percentCallback) {
|
||||||
percentCallback(state.percent);
|
percentCallback(state.percent);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
var exec = require('exec');
|
var exec = require('exec');
|
||||||
var Promise = require('bluebird');
|
var Promise = require('bluebird');
|
||||||
|
var fs = require('fs');
|
||||||
|
var path = require('path');
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
exec: function (args) {
|
exec: function (args) {
|
||||||
|
@ -14,5 +16,8 @@ module.exports = {
|
||||||
},
|
},
|
||||||
home: function () {
|
home: function () {
|
||||||
return process.env[(process.platform === 'win32') ? 'USERPROFILE' : 'HOME'];
|
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-size: 140px;
|
||||||
@circle-background: #F2F2F2;
|
@circle-background: red;
|
||||||
@inset-size: @circle-size - 4;
|
@inset-size: @circle-size - 4;
|
||||||
@inset-color: white;
|
@inset-color: white;
|
||||||
@transition-length: 1s;
|
@transition-length: 1s;
|
||||||
// @percentage-color: #3FD899;
|
// @percentage-color: #3FD899;
|
||||||
@percentage-font-size: 24px;
|
@percentage-font-size: 24px;
|
||||||
@percentage-text-width: 57px;
|
@percentage-text-width: 57px;
|
||||||
|
background: white;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
|
|
||||||
width: @circle-size;
|
width: @circle-size;
|
||||||
|
@ -84,6 +85,37 @@
|
||||||
background-color: @brand-positive;
|
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;
|
@i: 0;
|
||||||
@increment: 180deg / 100;
|
@increment: 180deg / 100;
|
||||||
.loop (@i) when (@i <= 100) {
|
.loop (@i) when (@i <= 100) {
|
||||||
|
|
|
@ -63,6 +63,7 @@
|
||||||
p {
|
p {
|
||||||
&.error {
|
&.error {
|
||||||
color: @brand-danger;
|
color: @brand-danger;
|
||||||
|
word-wrap: break-word;
|
||||||
}
|
}
|
||||||
margin-top: 20px;
|
margin-top: 20px;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue