Merge branch 'master' into restyle
Conflicts: app/ContainerDetails.react.js app/ContainerModal.react.js app/styles/main.less
10
README.md
|
@ -25,6 +25,16 @@ To run the app in development:
|
||||||
|
|
||||||
- `npm run release`
|
- `npm run release`
|
||||||
|
|
||||||
|
### Unit Tests
|
||||||
|
|
||||||
|
- `npm test`
|
||||||
|
|
||||||
|
### Integration Tests
|
||||||
|
|
||||||
|
Note that integration tests need to be run a Mac and _will_ remove your existing Boot2Docker VM, containers etc.
|
||||||
|
|
||||||
|
- `npm run test:integration`
|
||||||
|
|
||||||
## Uninstalling
|
## Uninstalling
|
||||||
|
|
||||||
- Remove Kitematic.app
|
- Remove Kitematic.app
|
||||||
|
|
|
@ -1,218 +0,0 @@
|
||||||
var React = require('react/addons');
|
|
||||||
var Router = require('react-router');
|
|
||||||
var Radial = require('./Radial.react.js');
|
|
||||||
var async = require('async');
|
|
||||||
var assign = require('object-assign');
|
|
||||||
var fs = require('fs');
|
|
||||||
var path = require('path');
|
|
||||||
var boot2docker = require('./boot2docker.js');
|
|
||||||
var virtualbox = require('./virtualbox.js');
|
|
||||||
var util = require('./util.js');
|
|
||||||
var docker = require('./docker.js');
|
|
||||||
var ContainerStore = require('./ContainerStore.js');
|
|
||||||
|
|
||||||
var setupSteps = [
|
|
||||||
{
|
|
||||||
run: function (callback, progressCallback) {
|
|
||||||
console.log(util.supportDir());
|
|
||||||
var installed = virtualbox.installed();
|
|
||||||
if (!installed) {
|
|
||||||
util.download('https://s3.amazonaws.com/kite-installer/' + virtualbox.INSTALLER_FILENAME, path.join(util.supportDir(), virtualbox.INSTALLER_FILENAME), virtualbox.INSTALLER_CHECKSUM, function (err) {
|
|
||||||
if (err) {callback(err); return;}
|
|
||||||
virtualbox.install(function (err) {
|
|
||||||
if (!virtualbox.installed()) {
|
|
||||||
callback('VirtualBox could not be installed. The installation either failed or was cancelled. Please try closing all VirtualBox instances and try again.');
|
|
||||||
} else {
|
|
||||||
callback(err);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}, function (progress) {
|
|
||||||
progressCallback(progress);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
virtualbox.version(function (err, installedVersion) {
|
|
||||||
if (err) {callback(err); return;}
|
|
||||||
if (util.compareVersions(installedVersion, virtualbox.REQUIRED_VERSION) < 0) {
|
|
||||||
// Download a newer version of Virtualbox
|
|
||||||
util.downloadFile(Setup.BASE_URL + virtualbox.INSTALLER_FILENAME, path.join(util.getResourceDir(), virtualbox.INSTALLER_FILENAME), virtualbox.INSTALLER_CHECKSUM, function (err) {
|
|
||||||
if (err) {callback(err); return;}
|
|
||||||
virtualbox.kill(function (err) {
|
|
||||||
if (err) {callback(err); return;}
|
|
||||||
virtualbox.install(function (err) {
|
|
||||||
if (err) {callback(err); return;}
|
|
||||||
virtualbox.version(function (err, installedVersion) {
|
|
||||||
if (err) {callback(err); return;}
|
|
||||||
if (util.compareVersions(installedVersion, virtualbox.REQUIRED_VERSION) < 0) {
|
|
||||||
callback('VirtualBox could not be installed. The installation either failed or was cancelled. Please try closing all VirtualBox instances and try again.');
|
|
||||||
} else {
|
|
||||||
callback(err);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}, function (progress) {
|
|
||||||
progressCallback(progress);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
callback();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
message: 'Downloading VirtualBox...'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
run: function (callback) {
|
|
||||||
virtualbox.deleteVM('kitematic-vm', function (err, removed) {
|
|
||||||
if (err) {
|
|
||||||
console.log(err);
|
|
||||||
}
|
|
||||||
callback();
|
|
||||||
});
|
|
||||||
},
|
|
||||||
message: 'Cleaning up existing Docker VM...'
|
|
||||||
},
|
|
||||||
|
|
||||||
// Initialize Boot2Docker if necessary.
|
|
||||||
{
|
|
||||||
run: function (callback) {
|
|
||||||
boot2docker.exists(function (err, exists) {
|
|
||||||
if (err) { callback(err); return; }
|
|
||||||
if (!exists) {
|
|
||||||
boot2docker.init(function (err) {
|
|
||||||
callback(err);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
if (!boot2docker.sshKeyExists()) {
|
|
||||||
callback('Boot2Docker SSH key doesn\'t exist. Fix by removing the existing Boot2Docker VM and re-run the installer. This usually occurs because an old version of Boot2Docker is installed.');
|
|
||||||
} else {
|
|
||||||
boot2docker.isoVersion(function (err, version) {
|
|
||||||
if (err || util.compareVersions(version, boot2docker.version()) < 0) {
|
|
||||||
boot2docker.stop(function(err) {
|
|
||||||
boot2docker.upgrade(function (err) {
|
|
||||||
callback(err);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
callback();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
message: 'Setting up the Docker VM...'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
run: function (callback) {
|
|
||||||
boot2docker.waitWhileStatus('saving', function (err) {
|
|
||||||
boot2docker.status(function (err, status) {
|
|
||||||
if (err) {callback(err); return;}
|
|
||||||
if (status !== 'running') {
|
|
||||||
boot2docker.start(function (err) {
|
|
||||||
callback(err);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
callback();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
},
|
|
||||||
message: 'Starting the Docker VM...'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
run: function (callback) {
|
|
||||||
boot2docker.ip(function (err, ip) {
|
|
||||||
if (err) {callback(err); return;}
|
|
||||||
console.log('Setting host IP to: ' + ip);
|
|
||||||
docker.setHost(ip);
|
|
||||||
callback(err);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
message: 'Detecting Docker VM...'
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
var Setup = React.createClass({
|
|
||||||
mixins: [ Router.Navigation ],
|
|
||||||
getInitialState: function () {
|
|
||||||
return {
|
|
||||||
message: '',
|
|
||||||
progress: 0
|
|
||||||
};
|
|
||||||
},
|
|
||||||
render: function () {
|
|
||||||
var radial;
|
|
||||||
if (this.state.progress) {
|
|
||||||
radial = <Radial progress={this.state.progress}/>;
|
|
||||||
} else if (this.state.error) {
|
|
||||||
radial = <Radial error={true} spin="true" progress="100"/>;
|
|
||||||
} else {
|
|
||||||
radial = <Radial spin="true" progress="100"/>;
|
|
||||||
}
|
|
||||||
if (this.state.error) {
|
|
||||||
return (
|
|
||||||
<div className="setup">
|
|
||||||
{radial}
|
|
||||||
<p className="error">Error: {this.state.error}</p>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return (
|
|
||||||
<div className="setup">
|
|
||||||
{radial}
|
|
||||||
<p>{this.state.message}</p>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
componentWillMount: function () {
|
|
||||||
this.setState({});
|
|
||||||
},
|
|
||||||
componentDidMount: function () {
|
|
||||||
var self = this;
|
|
||||||
this.setup(function (err) {
|
|
||||||
if (!err) {
|
|
||||||
boot2docker.ip(function (err, ip) {
|
|
||||||
docker.setHost(ip);
|
|
||||||
ContainerStore.init(function () {
|
|
||||||
self.transitionTo('containers');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
setup: function (callback) {
|
|
||||||
var self = this;
|
|
||||||
var currentStep = 0;
|
|
||||||
async.eachSeries(setupSteps, function (step, callback) {
|
|
||||||
console.log('Performing step ' + currentStep);
|
|
||||||
self.setState({progress: 0});
|
|
||||||
self.setState({message: step.message});
|
|
||||||
step.run(function (err) {
|
|
||||||
if (err) {
|
|
||||||
callback(err);
|
|
||||||
} else {
|
|
||||||
currentStep += 1;
|
|
||||||
callback();
|
|
||||||
}
|
|
||||||
}, function (progress) {
|
|
||||||
self.setState({progress: progress});
|
|
||||||
});
|
|
||||||
}, function (err) {
|
|
||||||
if (err) {
|
|
||||||
// if any of the steps fail
|
|
||||||
console.log('Kitematic setup failed at step ' + currentStep);
|
|
||||||
console.log(err);
|
|
||||||
self.setState({error: err.message});
|
|
||||||
callback(err);
|
|
||||||
} else {
|
|
||||||
// Setup Finished
|
|
||||||
console.log('Setup finished.');
|
|
||||||
callback();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
module.exports = Setup;
|
|
101
browser/main.js
|
@ -11,14 +11,18 @@ 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 saveVMOnQuit = false;
|
||||||
if (argv.test) {
|
|
||||||
console.log('Running tests');
|
|
||||||
}
|
|
||||||
|
|
||||||
process.env.NODE_PATH = __dirname + '/../node_modules';
|
process.env.NODE_PATH = __dirname + '/../node_modules';
|
||||||
|
process.env.RESOURCES_PATH = __dirname + '/../resources';
|
||||||
process.chdir(path.join(__dirname, '..'));
|
process.chdir(path.join(__dirname, '..'));
|
||||||
|
|
||||||
|
if (argv.integration) {
|
||||||
|
process.env.TEST_TYPE = 'integration';
|
||||||
|
} else {
|
||||||
|
process.env.TEST_TYPE = 'test';
|
||||||
|
}
|
||||||
|
|
||||||
app.on('activate-with-no-open-windows', function () {
|
app.on('activate-with-no-open-windows', function () {
|
||||||
if (mainWindow) {
|
if (mainWindow) {
|
||||||
mainWindow.show();
|
mainWindow.show();
|
||||||
|
@ -27,81 +31,78 @@ app.on('activate-with-no-open-windows', function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
app.on('ready', function() {
|
app.on('ready', function() {
|
||||||
var windowOptions = {
|
mainWindow = new BrowserWindow({
|
||||||
width: 1000,
|
width: 1000,
|
||||||
height: 700,
|
height: 700,
|
||||||
'min-width': 1000,
|
'min-width': 1000,
|
||||||
'min-height': 700,
|
'min-height': 700,
|
||||||
resizable: true,
|
resizable: true,
|
||||||
frame: false
|
frame: false,
|
||||||
};
|
show: false
|
||||||
|
});
|
||||||
mainWindow = new BrowserWindow(windowOptions);
|
|
||||||
mainWindow.hide();
|
|
||||||
|
|
||||||
if (argv.test) {
|
if (argv.test) {
|
||||||
mainWindow.loadUrl('file://' + __dirname + '/../build/specs.html');
|
mainWindow.loadUrl('file://' + __dirname + '/../tests/tests.html');
|
||||||
} else {
|
} else {
|
||||||
mainWindow.loadUrl('file://' + __dirname + '/../build/index.html');
|
mainWindow.loadUrl('file://' + __dirname + '/../build/index.html');
|
||||||
|
app.on('will-quit', function (e) {
|
||||||
|
if (saveVMOnQuit) {
|
||||||
|
exec('VBoxManage controlvm boot2docker-vm savestate', function (stderr, stdout, code) {});
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
process.on('uncaughtException', app.quit);
|
|
||||||
|
|
||||||
var saveVMOnQuit = false;
|
|
||||||
app.on('will-quit', function (e) {
|
|
||||||
if (saveVMOnQuit) {
|
|
||||||
exec('VBoxManage controlvm boot2docker-vm savestate', function (stderr, stdout, code) {});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
mainWindow.webContents.on('new-window', function (e) {
|
mainWindow.webContents.on('new-window', function (e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
});
|
});
|
||||||
|
|
||||||
mainWindow.webContents.on('did-finish-load', function() {
|
mainWindow.webContents.on('did-finish-load', function() {
|
||||||
mainWindow.show();
|
if (!argv.test) {
|
||||||
|
mainWindow.show();
|
||||||
|
}
|
||||||
mainWindow.focus();
|
mainWindow.focus();
|
||||||
|
|
||||||
mainWindow.setTitle('');
|
mainWindow.setTitle('');
|
||||||
|
|
||||||
// Auto Updates
|
// Auto Updates
|
||||||
autoUpdater.setFeedUrl('https://updates.kitematic.com/releases/latest?version=' + app.getVersion());
|
if (process.env.NODE_ENV !== 'development' && !argv.test) {
|
||||||
|
autoUpdater.setFeedUrl('https://updates.kitematic.com/releases/latest?version=' + app.getVersion());
|
||||||
|
|
||||||
autoUpdater.on('checking-for-update', function (e) {
|
autoUpdater.on('checking-for-update', function (e) {
|
||||||
console.log('Checking for update...');
|
console.log('Checking for update...');
|
||||||
});
|
});
|
||||||
|
|
||||||
autoUpdater.on('update-available', function (e) {
|
autoUpdater.on('update-available', function (e) {
|
||||||
console.log('Update available.');
|
console.log('Update available.');
|
||||||
console.log(e);
|
console.log(e);
|
||||||
});
|
});
|
||||||
|
|
||||||
autoUpdater.on('update-not-available', function (e) {
|
autoUpdater.on('update-not-available', function (e) {
|
||||||
console.log('Update not available.');
|
console.log('Update not available.');
|
||||||
});
|
});
|
||||||
|
|
||||||
autoUpdater.on('update-downloaded', function (e, releaseNotes, releaseName, releaseDate, updateURL) {
|
autoUpdater.on('update-downloaded', function (e, releaseNotes, releaseName, releaseDate, updateURL) {
|
||||||
console.log('Update downloaded.');
|
console.log('Update downloaded.');
|
||||||
mainWindow.webContents.send('notify', 'window:update-available');
|
mainWindow.webContents.send('notify', 'window:update-available');
|
||||||
});
|
});
|
||||||
|
|
||||||
autoUpdater.on('error', function (e) {
|
autoUpdater.on('error', function (e) {
|
||||||
console.log('An error occured while checking for updates.');
|
console.log('An error occured while checking for updates.');
|
||||||
console.log(e);
|
console.log(e);
|
||||||
});
|
});
|
||||||
|
|
||||||
ipc.on('command', function (event, arg) {
|
ipc.on('command', function (event, arg) {
|
||||||
console.log('Command: ' + arg);
|
console.log('Command: ' + arg);
|
||||||
if (arg === 'application:quit-install') {
|
if (arg === 'application:quit-install') {
|
||||||
saveVMOnQuit = false;
|
saveVMOnQuit = false;
|
||||||
autoUpdater.quitAndInstall();
|
autoUpdater.quitAndInstall();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
autoUpdater.checkForUpdates();
|
||||||
|
}
|
||||||
|
|
||||||
ipc.on('vm', function (event, arg) {
|
ipc.on('vm', function (event, arg) {
|
||||||
saveVMOnQuit = arg;
|
saveVMOnQuit = arg;
|
||||||
});
|
});
|
||||||
|
|
||||||
autoUpdater.checkForUpdates();
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
13
deps
|
@ -1,18 +1,15 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
BASE="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
BASE="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||||
export 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)")
|
||||||
export BOOT2DOCKER_CLI_VERSION_FILE=boot2docker-$BOOT2DOCKER_CLI_VERSION
|
BOOT2DOCKER_CLI_FILE=boot2docker-$BOOT2DOCKER_CLI_VERSION
|
||||||
|
|
||||||
mkdir -p $BASE/cache
|
|
||||||
|
|
||||||
pushd $BASE/resources > /dev/null
|
pushd $BASE/resources > /dev/null
|
||||||
|
|
||||||
if [ ! -f $BOOT2DOCKER_CLI_VERSION_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_VERSION_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_VERSION_FILE
|
chmod +x $BOOT2DOCKER_CLI_FILE
|
||||||
fi
|
fi
|
||||||
|
|
||||||
popd > /dev/null
|
popd > /dev/null
|
||||||
|
|
Before Width: | Height: | Size: 1.1 MiB After Width: | Height: | Size: 1.1 MiB |
102
gulpfile.js
|
@ -1,90 +1,50 @@
|
||||||
var gulp = require('gulp');
|
|
||||||
var source = require('vinyl-source-stream'); // Used to stream bundle for further handling
|
|
||||||
var browserify = require('browserify');
|
|
||||||
var watchify = require('watchify');
|
|
||||||
var reactify = require('reactify');
|
|
||||||
var gulpif = require('gulp-if');
|
|
||||||
var uglify = require('gulp-uglifyjs');
|
|
||||||
var notify = require('gulp-notify');
|
|
||||||
var concat = require('gulp-concat');
|
var concat = require('gulp-concat');
|
||||||
|
var cssmin = require('gulp-cssmin');
|
||||||
|
var downloadatomshell = require('gulp-download-atom-shell');
|
||||||
|
var fs = require('fs');
|
||||||
|
var gulp = require('gulp');
|
||||||
|
var gulpif = require('gulp-if');
|
||||||
|
var gutil = require('gulp-util');
|
||||||
|
var http = require('http');
|
||||||
var less = require('gulp-less');
|
var less = require('gulp-less');
|
||||||
var livereload = require('gulp-livereload');
|
var livereload = require('gulp-livereload');
|
||||||
var cssmin = require('gulp-cssmin');
|
|
||||||
var imagemin = require('gulp-imagemin');
|
|
||||||
var gutil = require('gulp-util');
|
|
||||||
var shell = require('gulp-shell');
|
|
||||||
var plumber = require('gulp-plumber');
|
var plumber = require('gulp-plumber');
|
||||||
var sourcemaps = require('gulp-sourcemaps');
|
|
||||||
var glob = require('glob');
|
|
||||||
var runSequence = require('run-sequence');
|
|
||||||
var ecstatic = require('ecstatic');
|
|
||||||
var downloadatomshell = require('gulp-download-atom-shell');
|
|
||||||
var packagejson = require('./package.json');
|
|
||||||
var http = require('http');
|
|
||||||
var react = require('gulp-react');
|
var react = require('gulp-react');
|
||||||
var fs = require('fs');
|
var runSequence = require('run-sequence');
|
||||||
|
var shell = require('gulp-shell');
|
||||||
|
var sourcemaps = require('gulp-sourcemaps');
|
||||||
|
var packagejson = require('./package.json');
|
||||||
|
|
||||||
var dependencies = Object.keys(packagejson.dependencies);
|
var dependencies = Object.keys(packagejson.dependencies);
|
||||||
var devDependencies = Object.keys(packagejson.devDependencies);
|
var devDependencies = Object.keys(packagejson.devDependencies);
|
||||||
var options = {
|
var options = {
|
||||||
dev: process.argv.indexOf('release') === -1 && process.argv.indexOf('test') === -1,
|
dev: process.argv.indexOf('release') === -1 && process.argv.indexOf('test') === -1,
|
||||||
test: process.argv.indexOf('test') !== -1,
|
test: process.argv.indexOf('test') !== -1,
|
||||||
|
integration: process.argv.indexOf('--integration') !== -1,
|
||||||
filename: 'Kitematic.app',
|
filename: 'Kitematic.app',
|
||||||
name: 'Kitematic'
|
name: 'Kitematic'
|
||||||
};
|
};
|
||||||
|
|
||||||
gulp.task('js', function () {
|
gulp.task('js', function () {
|
||||||
gulp.src('./app/**/*.js')
|
gulp.src('src/**/*.js')
|
||||||
.pipe(plumber(function(error) {
|
.pipe(plumber(function(error) {
|
||||||
gutil.log(gutil.colors.red('Error (' + error.plugin + '): ' + error.message));
|
gutil.log(gutil.colors.red('Error (' + error.plugin + '): ' + error.message));
|
||||||
// emit the end event, to properly end the task
|
// emit the end event, to properly end the task
|
||||||
this.emit('end');
|
this.emit('end');
|
||||||
}))
|
}))
|
||||||
.pipe(react())
|
.pipe(react())
|
||||||
.pipe(gulp.dest(options.dev ? './build' : './dist/osx/' + options.filename + '/Contents/Resources/app/build'))
|
.pipe(gulp.dest((options.dev || options.test) ? './build' : './dist/osx/' + options.filename + '/Contents/Resources/app/build'))
|
||||||
.pipe(gulpif(options.dev, livereload()));
|
.pipe(gulpif(options.dev, livereload()));
|
||||||
});
|
});
|
||||||
|
|
||||||
gulp.task('specs', function () {
|
|
||||||
var bundler = browserify({
|
|
||||||
entries: glob.sync('./specs/**/*-spec.js'),
|
|
||||||
debug: true, // Gives us sourcemapping
|
|
||||||
transform: [reactify],
|
|
||||||
cache: {}, packageCache: {}, fullPaths: true // Requirement of watchify
|
|
||||||
});
|
|
||||||
|
|
||||||
dependencies.forEach(function (dep) {
|
|
||||||
bundler.external(dep);
|
|
||||||
});
|
|
||||||
|
|
||||||
devDependencies.forEach(function (dep) {
|
|
||||||
bundler.external(dep);
|
|
||||||
});
|
|
||||||
|
|
||||||
bundler.external('./app');
|
|
||||||
|
|
||||||
bundler.bundle()
|
|
||||||
.on('error', gutil.log)
|
|
||||||
.pipe(source('specs.js'))
|
|
||||||
.pipe(gulp.dest('./build'));
|
|
||||||
|
|
||||||
gulp.src('./specs/specs.html')
|
|
||||||
.pipe(gulp.dest('./build'));
|
|
||||||
});
|
|
||||||
|
|
||||||
gulp.task('images', function() {
|
gulp.task('images', function() {
|
||||||
return gulp.src('./app/images/*')
|
return gulp.src('images/*')
|
||||||
.pipe(imagemin({
|
|
||||||
progressive: true,
|
|
||||||
interlaced: true,
|
|
||||||
svgoPlugins: [{removeViewBox: false}]
|
|
||||||
}))
|
|
||||||
.pipe(gulp.dest(options.dev ? './build' : './dist/osx/' + options.filename + '/Contents/Resources/app/build'))
|
.pipe(gulp.dest(options.dev ? './build' : './dist/osx/' + options.filename + '/Contents/Resources/app/build'))
|
||||||
.pipe(gulpif(options.dev, livereload()));
|
.pipe(gulpif(options.dev, livereload()));
|
||||||
});
|
});
|
||||||
|
|
||||||
gulp.task('styles', function () {
|
gulp.task('styles', function () {
|
||||||
return gulp.src('app/styles/main.less')
|
return gulp.src('styles/main.less')
|
||||||
.pipe(plumber(function(error) {
|
.pipe(plumber(function(error) {
|
||||||
gutil.log(gutil.colors.red('Error (' + error.plugin + '): ' + error.message));
|
gutil.log(gutil.colors.red('Error (' + error.plugin + '): ' + error.message));
|
||||||
// emit the end event, to properly end the task
|
// emit the end event, to properly end the task
|
||||||
|
@ -96,7 +56,7 @@ gulp.task('styles', function () {
|
||||||
.pipe(gulp.dest(options.dev ? './build' : './dist/osx/' + options.filename + '/Contents/Resources/app/build'))
|
.pipe(gulp.dest(options.dev ? './build' : './dist/osx/' + options.filename + '/Contents/Resources/app/build'))
|
||||||
.pipe(gulpif(!options.dev, cssmin()))
|
.pipe(gulpif(!options.dev, cssmin()))
|
||||||
.pipe(concat('main.css'))
|
.pipe(concat('main.css'))
|
||||||
.pipe(gulpif(options.dev && !options.test, livereload()));
|
.pipe(gulpif(options.dev, livereload()));
|
||||||
});
|
});
|
||||||
|
|
||||||
gulp.task('download', function (cb) {
|
gulp.task('download', function (cb) {
|
||||||
|
@ -107,11 +67,11 @@ gulp.task('download', function (cb) {
|
||||||
});
|
});
|
||||||
|
|
||||||
gulp.task('copy', function () {
|
gulp.task('copy', function () {
|
||||||
gulp.src('./app/index.html')
|
gulp.src('index.html')
|
||||||
.pipe(gulp.dest(options.dev ? './build' : './dist/osx/' + options.filename + '/Contents/Resources/app/build'))
|
.pipe(gulp.dest(options.dev ? './build' : './dist/osx/' + options.filename + '/Contents/Resources/app/build'))
|
||||||
.pipe(gulpif(options.dev, livereload()));
|
.pipe(gulpif(options.dev, livereload()));
|
||||||
|
|
||||||
gulp.src('./app/fonts/**')
|
gulp.src('fonts/**')
|
||||||
.pipe(gulp.dest(options.dev ? './build' : './dist/osx/' + options.filename + '/Contents/Resources/app/build'))
|
.pipe(gulp.dest(options.dev ? './build' : './dist/osx/' + options.filename + '/Contents/Resources/app/build'))
|
||||||
.pipe(gulpif(options.dev, livereload()));
|
.pipe(gulpif(options.dev, livereload()));
|
||||||
});
|
});
|
||||||
|
@ -180,19 +140,25 @@ gulp.task('release', function () {
|
||||||
runSequence('download', 'dist', ['copy', 'images', 'js', 'styles'], 'sign', 'zip');
|
runSequence('download', 'dist', ['copy', 'images', 'js', 'styles'], 'sign', 'zip');
|
||||||
});
|
});
|
||||||
|
|
||||||
gulp.task('test', ['download', 'copy', 'js', 'images', 'styles', 'specs'], function () {
|
gulp.task('test', ['download', 'copy', 'js'], function () {
|
||||||
var env = process.env;
|
var env = process.env;
|
||||||
env.NODE_ENV = 'development';
|
env.NODE_ENV = 'test';
|
||||||
gulp.src('').pipe(shell(['./cache/Atom.app/Contents/MacOS/Atom . --test'], {
|
if (options.integration) {
|
||||||
env: env
|
gulp.src('').pipe(shell(['./cache/Atom.app/Contents/MacOS/Atom . --test --integration'], {
|
||||||
}));
|
env: env
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
|
gulp.src('').pipe(shell(['./cache/Atom.app/Contents/MacOS/Atom . --test'], {
|
||||||
|
env: env
|
||||||
|
}));
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
gulp.task('default', ['download', 'copy', 'js', 'images', 'styles'], function () {
|
gulp.task('default', ['download', 'copy', 'js', 'images', 'styles'], function () {
|
||||||
gulp.watch('./app/**/*.js', ['js']);
|
gulp.watch('src/**/*.js', ['js']);
|
||||||
gulp.watch('./app/**/*.html', ['copy']);
|
gulp.watch('index.html', ['copy']);
|
||||||
gulp.watch('./app/styles/**/*.less', ['styles']);
|
gulp.watch('styles/**/*.less', ['styles']);
|
||||||
gulp.watch('./app/images/**', ['images']);
|
gulp.watch('images/**', ['images']);
|
||||||
|
|
||||||
livereload.listen();
|
livereload.listen();
|
||||||
|
|
||||||
|
|
Before Width: | Height: | Size: 243 B After Width: | Height: | Size: 243 B |
Before Width: | Height: | Size: 355 B After Width: | Height: | Size: 355 B |
Before Width: | Height: | Size: 208 B After Width: | Height: | Size: 208 B |
Before Width: | Height: | Size: 412 B After Width: | Height: | Size: 412 B |
Before Width: | Height: | Size: 272 B After Width: | Height: | Size: 272 B |
Before Width: | Height: | Size: 563 B After Width: | Height: | Size: 563 B |
Before Width: | Height: | Size: 410 B After Width: | Height: | Size: 410 B |
Before Width: | Height: | Size: 852 B After Width: | Height: | Size: 852 B |
Before Width: | Height: | Size: 618 B After Width: | Height: | Size: 618 B |
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 584 B After Width: | Height: | Size: 584 B |
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 240 B After Width: | Height: | Size: 240 B |
Before Width: | Height: | Size: 321 B After Width: | Height: | Size: 321 B |
Before Width: | Height: | Size: 238 B After Width: | Height: | Size: 238 B |
Before Width: | Height: | Size: 572 B After Width: | Height: | Size: 572 B |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 106 B After Width: | Height: | Size: 106 B |
Before Width: | Height: | Size: 115 B After Width: | Height: | Size: 115 B |
Before Width: | Height: | Size: 609 B After Width: | Height: | Size: 609 B |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 705 B After Width: | Height: | Size: 705 B |
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 641 B After Width: | Height: | Size: 641 B |
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 2.5 KiB |
Before Width: | Height: | Size: 5.6 KiB After Width: | Height: | Size: 5.6 KiB |
Before Width: | Height: | Size: 359 B After Width: | Height: | Size: 359 B |
Before Width: | Height: | Size: 731 B After Width: | Height: | Size: 731 B |
Before Width: | Height: | Size: 527 B After Width: | Height: | Size: 527 B |
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 252 B After Width: | Height: | Size: 252 B |
Before Width: | Height: | Size: 461 B After Width: | Height: | Size: 461 B |
Before Width: | Height: | Size: 347 B After Width: | Height: | Size: 347 B |
Before Width: | Height: | Size: 638 B After Width: | Height: | Size: 638 B |
Before Width: | Height: | Size: 349 B After Width: | Height: | Size: 349 B |
Before Width: | Height: | Size: 729 B After Width: | Height: | Size: 729 B |
Before Width: | Height: | Size: 349 B After Width: | Height: | Size: 349 B |
Before Width: | Height: | Size: 729 B After Width: | Height: | Size: 729 B |
Before Width: | Height: | Size: 535 B After Width: | Height: | Size: 535 B |
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 466 B After Width: | Height: | Size: 466 B |
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.0 KiB |
|
@ -6,6 +6,6 @@
|
||||||
<title>Kitematic</title>
|
<title>Kitematic</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<script src="main.js"></script>
|
<script src="Main.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
35
package.json
|
@ -12,9 +12,11 @@
|
||||||
"bugs": "https://github.com/kitematic/kitematic/issues",
|
"bugs": "https://github.com/kitematic/kitematic/issues",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "gulp",
|
"start": "gulp",
|
||||||
"preinstall": "./deps",
|
"test": "gulp test --silent",
|
||||||
"test": "gulp test",
|
"test:integration": "gulp test --silent --integration",
|
||||||
"release": "gulp release"
|
"all-tests": "npm test && npm run integration-tests",
|
||||||
|
"release": "gulp run release",
|
||||||
|
"preinstall": "./deps"
|
||||||
},
|
},
|
||||||
"licenses": [
|
"licenses": [
|
||||||
{
|
{
|
||||||
|
@ -22,18 +24,27 @@
|
||||||
"url": "https://raw.githubusercontent.com/kitematic/kitematic/master/LICENSE"
|
"url": "https://raw.githubusercontent.com/kitematic/kitematic/master/LICENSE"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"boot2docker-version": "1.3.2",
|
"jest": {
|
||||||
"atom-shell-version": "0.20.6",
|
"unmockedModulePathPatterns": [
|
||||||
|
"dockerode",
|
||||||
|
"react",
|
||||||
|
"debug"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"boot2docker-version": "1.4.1",
|
||||||
|
"atom-shell-version": "0.21.0",
|
||||||
|
"virtualbox-version": "4.3.20",
|
||||||
|
"virtualbox-filename": "VirtualBox-4.3.20-96996-OSX.dmg",
|
||||||
|
"virtualbox-required-version": "4.3.18",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ansi-to-html": "0.2.0",
|
"ansi-to-html": "0.2.0",
|
||||||
"async": "^0.9.0",
|
"async": "^0.9.0",
|
||||||
"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",
|
||||||
"exec": "0.1.2",
|
"exec": "0.1.2",
|
||||||
"gulp-react": "^2.0.0",
|
"jest-cli": "^0.2.1",
|
||||||
"jquery": "^2.1.3",
|
"jquery": "^2.1.3",
|
||||||
"minimist": "^1.1.0",
|
"minimist": "^1.1.0",
|
||||||
"moment": "2.8.1",
|
|
||||||
"node-uuid": "1.4.1",
|
"node-uuid": "1.4.1",
|
||||||
"object-assign": "^2.0.0",
|
"object-assign": "^2.0.0",
|
||||||
"open": "0.0.5",
|
"open": "0.0.5",
|
||||||
|
@ -41,7 +52,7 @@
|
||||||
"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.42.0",
|
"request": "^2.51.0",
|
||||||
"request-progress": "0.3.1",
|
"request-progress": "0.3.1",
|
||||||
"retina.js": "^1.1.0",
|
"retina.js": "^1.1.0",
|
||||||
"underscore": "^1.7.0"
|
"underscore": "^1.7.0"
|
||||||
|
@ -61,17 +72,19 @@
|
||||||
"gulp-livereload": "^2.1.1",
|
"gulp-livereload": "^2.1.1",
|
||||||
"gulp-notify": "^1.4.2",
|
"gulp-notify": "^1.4.2",
|
||||||
"gulp-plumber": "^0.6.6",
|
"gulp-plumber": "^0.6.6",
|
||||||
|
"gulp-react": "^2.0.0",
|
||||||
"gulp-shell": "^0.2.11",
|
"gulp-shell": "^0.2.11",
|
||||||
"gulp-sourcemaps": "^1.2.8",
|
"gulp-sourcemaps": "^1.2.8",
|
||||||
"gulp-streamify": "0.0.5",
|
"gulp-streamify": "0.0.5",
|
||||||
"gulp-uglify": "^0.3.1",
|
"gulp-uglify": "^0.3.1",
|
||||||
"gulp-uglifyjs": "^0.5.0",
|
"gulp-uglifyjs": "^0.5.0",
|
||||||
"gulp-util": "^3.0.0",
|
"gulp-util": "^3.0.0",
|
||||||
"jasmine-tagged": "^1.1.2",
|
|
||||||
"livereload-js": "^2.2.1",
|
|
||||||
"reactify": "^0.15.2",
|
"reactify": "^0.15.2",
|
||||||
|
"rimraf": "^2.2.8",
|
||||||
"run-sequence": "^1.0.2",
|
"run-sequence": "^1.0.2",
|
||||||
|
"time-require": "^0.1.2",
|
||||||
"vinyl-source-stream": "^0.1.1",
|
"vinyl-source-stream": "^0.1.1",
|
||||||
"watchify": "^2.1.1"
|
"watchify": "^2.1.1",
|
||||||
|
"zombie": "^2.5.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +0,0 @@
|
||||||
var Containers = require('./../app/Containers.react.js');
|
|
||||||
var TestUtils = require('react/addons').TestUtils;
|
|
||||||
var jasmine = require('jasmine-node');
|
|
||||||
|
|
||||||
describe('Containers', function() {
|
|
||||||
it('should be wrapped with a div', function() {
|
|
||||||
// var app = TestUtils.renderIntoDocument(App());
|
|
||||||
expect(true).toEqual(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
|
@ -1,9 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<link rel="stylesheet" href="main.css"/>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<script src="specs.js"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
|
@ -84,7 +84,14 @@ var Boot2Docker = {
|
||||||
cmdExec([Boot2Docker.command(), 'upgrade'], callback);
|
cmdExec([Boot2Docker.command(), 'upgrade'], callback);
|
||||||
},
|
},
|
||||||
ip: function (callback) {
|
ip: function (callback) {
|
||||||
cmdExec([Boot2Docker.command(), 'ip'], callback);
|
exec([Boot2Docker.command(), 'ip'], function (stderr, stdout, code) {
|
||||||
|
if (code) {
|
||||||
|
callback(stderr);
|
||||||
|
} else {
|
||||||
|
console.log(stderr, stdout, code);
|
||||||
|
callback(null, stdout.trim().replace('\n', ''));
|
||||||
|
}
|
||||||
|
});
|
||||||
},
|
},
|
||||||
erase: function (callback) {
|
erase: function (callback) {
|
||||||
var VMFileLocation = path.join(homeDir(), 'VirtualBox\\ VMs/boot2docker-vm');
|
var VMFileLocation = path.join(homeDir(), 'VirtualBox\\ VMs/boot2docker-vm');
|
|
@ -9,8 +9,8 @@ var remote = require('remote');
|
||||||
var dialog = remote.require('dialog');
|
var dialog = remote.require('dialog');
|
||||||
var ContainerStore = require('./ContainerStore');
|
var ContainerStore = require('./ContainerStore');
|
||||||
var ContainerUtil = require('./ContainerUtil');
|
var ContainerUtil = require('./ContainerUtil');
|
||||||
var docker = require('./docker');
|
var docker = require('./Docker');
|
||||||
var boot2docker = require('./boot2docker');
|
var boot2docker = require('./Boot2Docker');
|
||||||
var ProgressBar = require('react-bootstrap/ProgressBar');
|
var ProgressBar = require('react-bootstrap/ProgressBar');
|
||||||
var Popover = require('react-bootstrap/Popover');
|
var Popover = require('react-bootstrap/Popover');
|
||||||
|
|
||||||
|
@ -37,8 +37,6 @@ var ContainerDetails = React.createClass({
|
||||||
componentWillReceiveProps: function () {
|
componentWillReceiveProps: function () {
|
||||||
this.init();
|
this.init();
|
||||||
},
|
},
|
||||||
componentWillMount: function () {
|
|
||||||
},
|
|
||||||
componentDidMount: function () {
|
componentDidMount: function () {
|
||||||
this.init();
|
this.init();
|
||||||
ContainerStore.on(ContainerStore.SERVER_PROGRESS_EVENT, this.updateProgress);
|
ContainerStore.on(ContainerStore.SERVER_PROGRESS_EVENT, this.updateProgress);
|
||||||
|
@ -80,7 +78,7 @@ var ContainerDetails = React.createClass({
|
||||||
var $viewPopover = $(this.getDOMNode()).find('.popover-view');
|
var $viewPopover = $(this.getDOMNode()).find('.popover-view');
|
||||||
var $volumePopover = $(this.getDOMNode()).find('.popover-volume');
|
var $volumePopover = $(this.getDOMNode()).find('.popover-volume');
|
||||||
|
|
||||||
/*if ($viewDropdown && $volumeDropdown && $viewPopover && $volumePopover) {
|
/*if ($viewDropdown.offset() && $volumeDropdown.offset()) {
|
||||||
$viewPopover.offset({
|
$viewPopover.offset({
|
||||||
top: $viewDropdown.offset().top + 32,
|
top: $viewDropdown.offset().top + 32,
|
||||||
left: $viewDropdown.offset().left - ($viewPopover.outerWidth() / 2) + 14
|
left: $viewDropdown.offset().left - ($viewPopover.outerWidth() / 2) + 14
|
||||||
|
@ -98,16 +96,19 @@ var ContainerDetails = React.createClass({
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.setState({
|
this.setState({
|
||||||
|
progress: ContainerStore.progress(this.getParams().name),
|
||||||
env: ContainerUtil.env(container),
|
env: ContainerUtil.env(container),
|
||||||
});
|
});
|
||||||
var ports = ContainerUtil.ports(container);
|
var ports = ContainerUtil.ports(container);
|
||||||
var webPorts = ['80', '8000', '8080', '3000', '5000', '2368'];
|
var webPorts = ['80', '8000', '8080', '3000', '5000', '2368'];
|
||||||
|
console.log(ports);
|
||||||
this.setState({
|
this.setState({
|
||||||
ports: ports,
|
ports: ports,
|
||||||
defaultPort: _.find(_.keys(ports), function (port) {
|
defaultPort: _.find(_.keys(ports), function (port) {
|
||||||
return webPorts.indexOf(port) !== -1;
|
return webPorts.indexOf(port) !== -1;
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
console.log(this.state);
|
||||||
this.updateLogs();
|
this.updateLogs();
|
||||||
},
|
},
|
||||||
updateLogs: function (name) {
|
updateLogs: function (name) {
|
||||||
|
@ -149,6 +150,7 @@ var ContainerDetails = React.createClass({
|
||||||
console.log('CLICKED');
|
console.log('CLICKED');
|
||||||
if (this.state.defaultPort) {
|
if (this.state.defaultPort) {
|
||||||
console.log(this.state.defaultPort);
|
console.log(this.state.defaultPort);
|
||||||
|
console.log(this.state.ports[this.state.defaultPort].url);
|
||||||
exec(['open', this.state.ports[this.state.defaultPort].url], function (err) {
|
exec(['open', this.state.ports[this.state.defaultPort].url], function (err) {
|
||||||
if (err) { throw err; }
|
if (err) { throw err; }
|
||||||
});
|
});
|
||||||
|
@ -322,6 +324,13 @@ var ContainerDetails = React.createClass({
|
||||||
disabled: !this.props.container.State.Running
|
disabled: !this.props.container.State.Running
|
||||||
});
|
});
|
||||||
|
|
||||||
|
var restartButtonClass = React.addons.classSet({
|
||||||
|
btn: true,
|
||||||
|
'btn-action': true,
|
||||||
|
'with-icon': true,
|
||||||
|
disabled: this.props.container.State.Restarting
|
||||||
|
});
|
||||||
|
|
||||||
var viewButtonClass = React.addons.classSet({
|
var viewButtonClass = React.addons.classSet({
|
||||||
btn: true,
|
btn: true,
|
||||||
'btn-action': true,
|
'btn-action': true,
|
|
@ -9,12 +9,13 @@ var ModalTrigger = require('react-bootstrap/ModalTrigger');
|
||||||
var ContainerModal = require('./ContainerModal.react');
|
var ContainerModal = require('./ContainerModal.react');
|
||||||
var ContainerListItem = require('./ContainerListItem.react');
|
var ContainerListItem = require('./ContainerListItem.react');
|
||||||
var Header = require('./Header.react');
|
var Header = require('./Header.react');
|
||||||
var docker = require('./docker');
|
var docker = require('./Docker');
|
||||||
|
|
||||||
var ContainerList = React.createClass({
|
var ContainerList = React.createClass({
|
||||||
render: function () {
|
render: function () {
|
||||||
var self = this;
|
var self = this;
|
||||||
var containers = this.props.containers.map(function (container) {
|
var containers = this.props.containers.map(function (container) {
|
||||||
|
console.log(container);
|
||||||
return (
|
return (
|
||||||
<ContainerListItem container={container} />
|
<ContainerListItem container={container} />
|
||||||
);
|
);
|
|
@ -83,6 +83,9 @@ var ContainerModal = React.createClass({
|
||||||
},
|
},
|
||||||
handleClick: function (name, event) {
|
handleClick: function (name, event) {
|
||||||
ContainerStore.create(name, 'latest', function (err, containerName) {
|
ContainerStore.create(name, 'latest', function (err, containerName) {
|
||||||
|
if (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
this.props.onRequestHide();
|
this.props.onRequestHide();
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
},
|
},
|
|
@ -1,14 +1,14 @@
|
||||||
|
var $ = require('jquery');
|
||||||
|
var _ = require('underscore');
|
||||||
var EventEmitter = require('events').EventEmitter;
|
var EventEmitter = require('events').EventEmitter;
|
||||||
var async = require('async');
|
var async = require('async');
|
||||||
var path = require('path');
|
var path = require('path');
|
||||||
var assign = require('object-assign');
|
var assign = require('object-assign');
|
||||||
var Stream = require('stream');
|
var Stream = require('stream');
|
||||||
var Convert = require('ansi-to-html');
|
var Convert = require('ansi-to-html');
|
||||||
var docker = require('./docker');
|
var docker = require('./Docker');
|
||||||
var registry = require('./registry');
|
var registry = require('./Registry');
|
||||||
var ContainerUtil = require('./ContainerUtil');
|
var ContainerUtil = require('./ContainerUtil');
|
||||||
var $ = require('jquery');
|
|
||||||
var _ = require('underscore');
|
|
||||||
|
|
||||||
var convert = new Convert();
|
var convert = new Convert();
|
||||||
|
|
||||||
|
@ -18,7 +18,6 @@ var _progress = {};
|
||||||
var _logs = {};
|
var _logs = {};
|
||||||
var _streams = {};
|
var _streams = {};
|
||||||
var _muted = {};
|
var _muted = {};
|
||||||
var _config = {};
|
|
||||||
|
|
||||||
var ContainerStore = assign(EventEmitter.prototype, {
|
var ContainerStore = assign(EventEmitter.prototype, {
|
||||||
CLIENT_CONTAINER_EVENT: 'client_container',
|
CLIENT_CONTAINER_EVENT: 'client_container',
|
||||||
|
@ -226,14 +225,14 @@ var ContainerStore = assign(EventEmitter.prototype, {
|
||||||
// If the event is delete, remove the container
|
// If the event is delete, remove the container
|
||||||
if (data.status === 'destroy') {
|
if (data.status === 'destroy') {
|
||||||
var container = _.findWhere(_.values(_containers), {Id: data.id});
|
var container = _.findWhere(_.values(_containers), {Id: data.id});
|
||||||
if (!container) {
|
if (container) {
|
||||||
return;
|
delete _containers[container.Name];
|
||||||
|
if (!_muted[container.Name]) {
|
||||||
|
this.emit(this.SERVER_CONTAINER_EVENT, container.Name, data.status);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.emit(this.SERVER_CONTAINER_EVENT, data.status);
|
||||||
}
|
}
|
||||||
delete _containers[container.Name];
|
|
||||||
if (_muted[container.Name]) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.emit(this.SERVER_CONTAINER_EVENT, container.Name, data.status);
|
|
||||||
} else {
|
} else {
|
||||||
this.fetchContainer(data.id, function (err) {
|
this.fetchContainer(data.id, function (err) {
|
||||||
if (err) {
|
if (err) {
|
||||||
|
@ -250,7 +249,12 @@ var ContainerStore = assign(EventEmitter.prototype, {
|
||||||
init: function (callback) {
|
init: function (callback) {
|
||||||
// TODO: Load cached data from db on loading
|
// TODO: Load cached data from db on loading
|
||||||
this.fetchAllContainers(function (err) {
|
this.fetchAllContainers(function (err) {
|
||||||
callback();
|
if (err) {
|
||||||
|
callback(err);
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
callback();
|
||||||
|
}
|
||||||
this.emit(this.CLIENT_CONTAINER_EVENT);
|
this.emit(this.CLIENT_CONTAINER_EVENT);
|
||||||
this._resumePulling();
|
this._resumePulling();
|
||||||
this._startListeningToEvents();
|
this._startListeningToEvents();
|
||||||
|
@ -366,7 +370,6 @@ var ContainerStore = assign(EventEmitter.prototype, {
|
||||||
});
|
});
|
||||||
stream.on('end', function () {
|
stream.on('end', function () {
|
||||||
delete _streams[name];
|
delete _streams[name];
|
||||||
console.log('end', name);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
@ -381,6 +384,10 @@ var ContainerStore = assign(EventEmitter.prototype, {
|
||||||
if (!data) {
|
if (!data) {
|
||||||
// Pull image
|
// Pull image
|
||||||
self._createPlaceholderContainer(imageName, containerName, function (err, container) {
|
self._createPlaceholderContainer(imageName, containerName, function (err, container) {
|
||||||
|
if (err) {
|
||||||
|
callback(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
_containers[containerName] = container;
|
_containers[containerName] = container;
|
||||||
self.emit(self.CLIENT_CONTAINER_EVENT, containerName, 'create');
|
self.emit(self.CLIENT_CONTAINER_EVENT, containerName, 'create');
|
||||||
_muted[containerName] = true;
|
_muted[containerName] = true;
|
|
@ -1,5 +1,5 @@
|
||||||
var _ = require('underscore');
|
var _ = require('underscore');
|
||||||
var docker = require('./docker');
|
var docker = require('./Docker');
|
||||||
|
|
||||||
var ContainerUtil = {
|
var ContainerUtil = {
|
||||||
env: function (container) {
|
env: function (container) {
|
||||||
|
@ -15,7 +15,6 @@ var ContainerUtil = {
|
||||||
ports: function (container, callback) {
|
ports: function (container, callback) {
|
||||||
var res = {};
|
var res = {};
|
||||||
var ip = docker.host;
|
var ip = docker.host;
|
||||||
console.log(container);
|
|
||||||
_.each(container.NetworkSettings.Ports, function (value, key) {
|
_.each(container.NetworkSettings.Ports, function (value, key) {
|
||||||
var dockerPort = key.split('/')[0];
|
var dockerPort = key.split('/')[0];
|
||||||
var localUrl = null;
|
var localUrl = null;
|
|
@ -1,3 +1,6 @@
|
||||||
|
var async = require('async');
|
||||||
|
var _ = require('underscore');
|
||||||
|
var $ = require('jquery');
|
||||||
var React = require('react/addons');
|
var React = require('react/addons');
|
||||||
var Router = require('react-router');
|
var Router = require('react-router');
|
||||||
var RetinaImage = require('react-retina-image');
|
var RetinaImage = require('react-retina-image');
|
||||||
|
@ -6,11 +9,7 @@ var ContainerModal = require('./ContainerModal.react');
|
||||||
var ContainerStore = require('./ContainerStore');
|
var ContainerStore = require('./ContainerStore');
|
||||||
var ContainerList = require('./ContainerList.react');
|
var ContainerList = require('./ContainerList.react');
|
||||||
var Header = require('./Header.react');
|
var Header = require('./Header.react');
|
||||||
var async = require('async');
|
var docker = require('./Docker');
|
||||||
var _ = require('underscore');
|
|
||||||
var docker = require('./docker');
|
|
||||||
var $ = require('jquery');
|
|
||||||
|
|
||||||
var Containers = React.createClass({
|
var Containers = React.createClass({
|
||||||
mixins: [Router.Navigation, Router.State],
|
mixins: [Router.Navigation, Router.State],
|
||||||
getInitialState: function () {
|
getInitialState: function () {
|
|
@ -12,8 +12,7 @@ var Docker = {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this._client = new dockerode({
|
this._client = new dockerode({
|
||||||
protocol: 'https',
|
host: '192.168.59.103',
|
||||||
host: this.host,
|
|
||||||
port: 2376,
|
port: 2376,
|
||||||
ca: fs.readFileSync(path.join(certDir, 'ca.pem')),
|
ca: fs.readFileSync(path.join(certDir, 'ca.pem')),
|
||||||
cert: fs.readFileSync(path.join(certDir, 'cert.pem')),
|
cert: fs.readFileSync(path.join(certDir, 'cert.pem')),
|
|
@ -1,33 +1,23 @@
|
||||||
var module = require('module');
|
|
||||||
require.main.paths.splice(0, 0, process.env.NODE_PATH);
|
|
||||||
|
|
||||||
var Bugsnag = require('bugsnag-js');
|
|
||||||
var React = require('react');
|
var React = require('react');
|
||||||
var Router = require('react-router');
|
var Router = require('react-router');
|
||||||
var RetinaImage = require('react-retina-image');
|
var RetinaImage = require('react-retina-image');
|
||||||
var async = require('async');
|
var async = require('async');
|
||||||
var docker = require('./docker');
|
var docker = require('./Docker');
|
||||||
var router = require('./router');
|
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 Menu = require('./Menu');
|
var Menu = require('./Menu');
|
||||||
var remote = require('remote');
|
var remote = require('remote');
|
||||||
var app = remote.require('app');
|
var app = remote.require('app');
|
||||||
var ipc = require('ipc');
|
var ipc = require('ipc');
|
||||||
|
|
||||||
|
|
||||||
var Route = Router.Route;
|
var Route = Router.Route;
|
||||||
var NotFoundRoute = Router.NotFoundRoute;
|
var NotFoundRoute = Router.NotFoundRoute;
|
||||||
var DefaultRoute = Router.DefaultRoute;
|
var DefaultRoute = Router.DefaultRoute;
|
||||||
var Link = Router.Link;
|
var Link = Router.Link;
|
||||||
var RouteHandler = Router.RouteHandler;
|
var RouteHandler = Router.RouteHandler;
|
||||||
|
|
||||||
Bugsnag.apiKey = 'fc51aab02ce9dd1bb6ebc9fe2f4d43d7';
|
|
||||||
Bugsnag.autoNotify = true;
|
|
||||||
Bugsnag.releaseStage = process.env.NODE_ENV === 'development' ? 'development' : 'production';
|
|
||||||
Bugsnag.notifyReleaseStages = [];
|
|
||||||
Bugsnag.appVersion = app.getVersion();
|
|
||||||
|
|
||||||
if (process.env.NODE_ENV === 'development') {
|
if (process.env.NODE_ENV === 'development') {
|
||||||
var script = document.createElement('script');
|
var script = document.createElement('script');
|
||||||
script.type = 'text/javascript';
|
script.type = 'text/javascript';
|
||||||
|
@ -37,13 +27,25 @@ if (process.env.NODE_ENV === 'development') {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!window.location.hash.length || window.location.hash === '#/') {
|
if (!window.location.hash.length || window.location.hash === '#/') {
|
||||||
router.run(function (Handler) {
|
SetupStore.run(function (err) {
|
||||||
React.render(<Handler/>, document.body);
|
boot2docker.ip(function (err, ip) {
|
||||||
|
if (err) console.log(err);
|
||||||
|
docker.setHost(ip);
|
||||||
|
router.transitionTo('containers');
|
||||||
|
ContainerStore.init(function (err) {
|
||||||
|
if (err) console.log(err);
|
||||||
|
router.run(function (Handler) {
|
||||||
|
React.render(<Handler/>, document.body);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
boot2docker.ip(function (err, ip) {
|
boot2docker.ip(function (err, ip) {
|
||||||
|
if (err) console.log(err);
|
||||||
docker.setHost(ip);
|
docker.setHost(ip);
|
||||||
ContainerStore.init(function () {
|
ContainerStore.init(function (err) {
|
||||||
|
if (err) console.log(err);
|
||||||
router.run(function (Handler) {
|
router.run(function (Handler) {
|
||||||
React.render(<Handler/>, document.body);
|
React.render(<Handler/>, document.body);
|
||||||
});
|
});
|
|
@ -3,7 +3,7 @@ var app = remote.require('app');
|
||||||
var Menu = remote.require('menu');
|
var Menu = remote.require('menu');
|
||||||
var MenuItem = remote.require('menu-item');
|
var MenuItem = remote.require('menu-item');
|
||||||
var BrowserWindow = remote.require('browser-window');
|
var BrowserWindow = remote.require('browser-window');
|
||||||
var router = require('./router');
|
var router = require('./Router');
|
||||||
|
|
||||||
// main.js
|
// main.js
|
||||||
var template = [
|
var template = [
|
|
@ -1,5 +1,5 @@
|
||||||
var Router = require('react-router');
|
var Router = require('react-router');
|
||||||
var routes = require('./routes');
|
var routes = require('./Routes');
|
||||||
|
|
||||||
var router = Router.create({
|
var router = Router.create({
|
||||||
routes: routes
|
routes: routes
|
|
@ -0,0 +1,55 @@
|
||||||
|
var React = require('react/addons');
|
||||||
|
var Router = require('react-router');
|
||||||
|
var Radial = require('./Radial.react.js');
|
||||||
|
var async = require('async');
|
||||||
|
var assign = require('object-assign');
|
||||||
|
var fs = require('fs');
|
||||||
|
var path = require('path');
|
||||||
|
var virtualbox = require('./Virtualbox');
|
||||||
|
var util = require('./Util');
|
||||||
|
var SetupStore = require('./SetupStore');
|
||||||
|
|
||||||
|
var Setup = React.createClass({
|
||||||
|
mixins: [ Router.Navigation ],
|
||||||
|
getInitialState: function () {
|
||||||
|
return {
|
||||||
|
message: '',
|
||||||
|
progress: 0
|
||||||
|
};
|
||||||
|
},
|
||||||
|
componentWillMount: function () {
|
||||||
|
SetupStore.on(SetupStore.PROGRESS_EVENT, this.update);
|
||||||
|
},
|
||||||
|
componentDidMount: function () {
|
||||||
|
},
|
||||||
|
update: function () {
|
||||||
|
|
||||||
|
},
|
||||||
|
render: function () {
|
||||||
|
var radial;
|
||||||
|
if (this.state.progress) {
|
||||||
|
radial = <Radial progress={this.state.progress}/>;
|
||||||
|
} else if (this.state.error) {
|
||||||
|
radial = <Radial error={true} spin="true" progress="100"/>;
|
||||||
|
} else {
|
||||||
|
radial = <Radial spin="true" progress="100"/>;
|
||||||
|
}
|
||||||
|
if (this.state.error) {
|
||||||
|
return (
|
||||||
|
<div className="setup">
|
||||||
|
{radial}
|
||||||
|
<p className="error">Error: {this.state.error}</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return (
|
||||||
|
<div className="setup">
|
||||||
|
{radial}
|
||||||
|
<p>{this.state.message}</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = Setup;
|
|
@ -0,0 +1,201 @@
|
||||||
|
var EventEmitter = require('events').EventEmitter;
|
||||||
|
var assign = require('object-assign');
|
||||||
|
var async = require('async');
|
||||||
|
var fs = require('fs');
|
||||||
|
var path = require('path');
|
||||||
|
var exec = require('exec');
|
||||||
|
var boot2docker = require('./Boot2Docker');
|
||||||
|
var virtualbox = require('./Virtualbox');
|
||||||
|
var setupUtil = require('./SetupUtil');
|
||||||
|
var packagejson = JSON.parse(fs.readFileSync(path.join(__dirname, '..', 'package.json'), 'utf8'));
|
||||||
|
|
||||||
|
var _currentStep = null;
|
||||||
|
var _error = null;
|
||||||
|
var _progress = null;
|
||||||
|
|
||||||
|
var SetupStore = assign(EventEmitter.prototype, {
|
||||||
|
PROGRESS_EVENT: 'setup_progress',
|
||||||
|
STEP_EVENT: 'setup_step',
|
||||||
|
ERROR_EVENT: 'setup_error',
|
||||||
|
downloadVirtualboxStep: {
|
||||||
|
_download: function (callback, progressCallback) {
|
||||||
|
setupUtil.virtualboxSHA256(packagejson['virtualbox-version'], packagejson['virtualbox-filename'], function (err, checksum) {
|
||||||
|
if (err) {
|
||||||
|
callback(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var url = 'http://download.virtualbox.org/virtualbox/' + packagejson['virtualbox-version'] + '/' + packagejson['virtualbox-filename'];
|
||||||
|
var downloadPath = path.join(setupUtil.supportDir(), packagejson['virtualbox-filename']);
|
||||||
|
setupUtil.download(url, downloadPath, checksum, function (err) {
|
||||||
|
callback(err);
|
||||||
|
}, function (progress) {
|
||||||
|
progressCallback(progress);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
run: function (callback, progressCallback) {
|
||||||
|
if (virtualbox.installed()) {
|
||||||
|
virtualbox.version(function (err, version) {
|
||||||
|
if (err) {callback(err); return;}
|
||||||
|
if (setupUtil.compareVersions(version, packagejson['virtualbox-required-version']) < 0) {
|
||||||
|
this._download(callback, progressCallback);
|
||||||
|
} else {
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this._download(callback, progressCallback);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
name: 'downloading_virtualbox',
|
||||||
|
message: 'Downloading Virtualbox',
|
||||||
|
},
|
||||||
|
installVirtualboxStep: {
|
||||||
|
_install: function (callback) {
|
||||||
|
exec(['hdiutil', 'attach', path.join(setupUtil.supportDir(), 'VirtualBox-4.3.20-96996-OSX.dmg')], function (stderr, stdout, code) {
|
||||||
|
if (code) {
|
||||||
|
callback(stderr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var iconPath = path.join(setupUtil.resourceDir(), 'kitematic.icns');
|
||||||
|
setupUtil.isSudo(function (err, isSudo) {
|
||||||
|
sudoCmd = isSudo ? ['sudo'] : [path.join(setupUtil.resourceDir(), 'cocoasudo'), '--icon=' + iconPath, '--prompt=Kitematic requires administrative privileges to install VirtualBox and copy itself to the Applications folder.'];
|
||||||
|
sudoCmd.push.apply(sudoCmd, ['installer', '-pkg', '/Volumes/VirtualBox/VirtualBox.pkg', '-target', '/']);
|
||||||
|
exec(sudoCmd, function (stderr, stdout, code) {
|
||||||
|
if (code) {
|
||||||
|
console.log(stderr);
|
||||||
|
console.log(stdout);
|
||||||
|
callback('Could not install virtualbox.');
|
||||||
|
} else {
|
||||||
|
exec(['hdiutil', 'detach', '/Volumes/VirtualBox'], function(stderr, stdout, code) {
|
||||||
|
if (code) {
|
||||||
|
callback(stderr);
|
||||||
|
} else {
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
run: function (callback) {
|
||||||
|
var self = this;
|
||||||
|
if (virtualbox.installed()) {
|
||||||
|
virtualbox.version(function (err, version) {
|
||||||
|
if (setupUtil.compareVersions(version, packagejson['virtualbox-required-version']) < 0) {
|
||||||
|
virtualbox.kill(function (err) {
|
||||||
|
if (err) {callback(err); return;}
|
||||||
|
self._install(function(err) {
|
||||||
|
callback(err);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
self._install(function(err) {
|
||||||
|
callback(err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
name: 'installing_virtualbox',
|
||||||
|
message: 'Installing VirtualBox',
|
||||||
|
},
|
||||||
|
cleanupKitematicStep: {
|
||||||
|
run: function (callback) {
|
||||||
|
virtualbox.vmdestroy('kitematic-vm', function (err, removed) {
|
||||||
|
if (err) {
|
||||||
|
console.log(err);
|
||||||
|
}
|
||||||
|
callback();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
name: 'cleanup_kitematic',
|
||||||
|
message: 'Cleaning up existing Kitematic install...'
|
||||||
|
},
|
||||||
|
initBoot2DockerStep: {
|
||||||
|
run: function (callback) {
|
||||||
|
boot2docker.exists(function (err, exists) {
|
||||||
|
if (err) { callback(err); return; }
|
||||||
|
if (!exists) {
|
||||||
|
boot2docker.init(function (err) {
|
||||||
|
callback(err);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
if (!boot2docker.sshKeyExists()) {
|
||||||
|
callback('Boot2Docker SSH key doesn\'t exist. Fix by removing the existing Boot2Docker VM and re-run the installer. This usually occurs because an old version of Boot2Docker is installed.');
|
||||||
|
} else {
|
||||||
|
boot2docker.isoVersion(function (err, version) {
|
||||||
|
if (err || setupUtil.compareVersions(version, boot2docker.version()) < 0) {
|
||||||
|
boot2docker.stop(function(err) {
|
||||||
|
boot2docker.upgrade(function (err) {
|
||||||
|
callback(err);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
name: 'init_boot2docker',
|
||||||
|
message: 'Setting up the Docker VM...'
|
||||||
|
},
|
||||||
|
startBoot2DockerStep: {
|
||||||
|
run: function (callback) {
|
||||||
|
boot2docker.waitWhileStatus('saving', function (err) {
|
||||||
|
boot2docker.status(function (err, status) {
|
||||||
|
if (err) {callback(err); return;}
|
||||||
|
if (status !== 'running') {
|
||||||
|
boot2docker.start(function (err) {
|
||||||
|
callback(err);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
name: 'start_boot2docker',
|
||||||
|
message: 'Starting the Docker VM...'
|
||||||
|
},
|
||||||
|
step: function () {
|
||||||
|
return _currentStep;
|
||||||
|
},
|
||||||
|
progress: function () {
|
||||||
|
return _progress;
|
||||||
|
},
|
||||||
|
run: function (callback) {
|
||||||
|
var self = this;
|
||||||
|
var steps = [this.downloadVirtualboxStep, this.installVirtualboxStep, this.cleanupKitematicStep, this.initBoot2DockerStep, this.startBoot2DockerStep];
|
||||||
|
async.eachSeries(steps, function (step, callback) {
|
||||||
|
console.log(step.name);
|
||||||
|
_currentStep = step;
|
||||||
|
_progress = null;
|
||||||
|
step.run(function (err) {
|
||||||
|
if (err) {
|
||||||
|
callback(err);
|
||||||
|
} else {
|
||||||
|
self.emit(self.STEP_EVENT);
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
}, function (progress) {
|
||||||
|
self.emit(self.PROGRESS_EVENT, progress);
|
||||||
|
_progress = progress;
|
||||||
|
});
|
||||||
|
}, function (err) {
|
||||||
|
if (err) {
|
||||||
|
self.emit(self.ERROR_EVENT, _error);
|
||||||
|
callback(err);
|
||||||
|
} else {
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = SetupStore;
|
|
@ -1,13 +1,13 @@
|
||||||
var path = require('path');
|
|
||||||
var fs = require('fs');
|
|
||||||
var nodeCrypto = require('crypto');
|
|
||||||
var request = require('request');
|
var request = require('request');
|
||||||
var progress = require('request-progress');
|
var progress = require('request-progress');
|
||||||
|
var path = require('path');
|
||||||
|
var crypto = require('crypto');
|
||||||
|
var fs = require('fs');
|
||||||
var exec = require('exec');
|
var exec = require('exec');
|
||||||
|
|
||||||
var Util = {
|
var SetupUtil = {
|
||||||
supportDir: function (callback) {
|
supportDir: function (callback) {
|
||||||
var dirs = ['Application\ Support', 'Kitematic'];
|
var dirs = ['Library', 'Application\ Support', 'Kitematic'];
|
||||||
var acc = process.env.HOME;
|
var acc = process.env.HOME;
|
||||||
dirs.forEach(function (d) {
|
dirs.forEach(function (d) {
|
||||||
acc = path.join(acc, d);
|
acc = path.join(acc, d);
|
||||||
|
@ -17,6 +17,19 @@ var Util = {
|
||||||
});
|
});
|
||||||
return acc;
|
return acc;
|
||||||
},
|
},
|
||||||
|
resourceDir: function (callback) {
|
||||||
|
return process.env.RESOURCES_PATH;
|
||||||
|
},
|
||||||
|
isSudo: function (callback) {
|
||||||
|
exec(['sudo', '-n', '-u', 'root', 'true'], function (stderr, stdout, code) {
|
||||||
|
if (code) {
|
||||||
|
callback(stderr);
|
||||||
|
} else {
|
||||||
|
var isSudo = stderr.indexOf('a password is required') === -1;
|
||||||
|
callback(null, isSudo);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
download: function (url, filename, checksum, callback, progressCallback) {
|
download: function (url, filename, checksum, callback, progressCallback) {
|
||||||
var doDownload = function () {
|
var doDownload = function () {
|
||||||
progress(request({
|
progress(request({
|
||||||
|
@ -37,8 +50,7 @@ var Util = {
|
||||||
|
|
||||||
// Compare checksum to see if it already exists first
|
// Compare checksum to see if it already exists first
|
||||||
if (fs.existsSync(filename)) {
|
if (fs.existsSync(filename)) {
|
||||||
var existingChecksum = nodeCrypto.createHash('sha256').update(fs.readFileSync(filename), 'utf8').digest('hex');
|
var existingChecksum = crypto.createHash('sha256').update(fs.readFileSync(filename), 'utf8').digest('hex');
|
||||||
console.log(existingChecksum);
|
|
||||||
if (existingChecksum !== checksum) {
|
if (existingChecksum !== checksum) {
|
||||||
fs.unlinkSync(filename);
|
fs.unlinkSync(filename);
|
||||||
doDownload();
|
doDownload();
|
||||||
|
@ -49,6 +61,22 @@ var Util = {
|
||||||
doDownload();
|
doDownload();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
virtualboxSHA256: function (version, filename, callback) {
|
||||||
|
var checksumUrl = 'http://dlc-cdn.sun.com/virtualbox/' + version + '/SHA256SUMS';
|
||||||
|
request(checksumUrl, function (error, response, body) {
|
||||||
|
if (error) {
|
||||||
|
callback(error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var checksums = body.split('\n').map(function (line) {
|
||||||
|
return line.split(' *');
|
||||||
|
}).reduce(function (obj, pair) {
|
||||||
|
obj[pair[1]] = pair[0];
|
||||||
|
return obj;
|
||||||
|
}, {});
|
||||||
|
callback(null, checksums[filename]);
|
||||||
|
});
|
||||||
|
},
|
||||||
compareVersions: function (v1, v2, options) {
|
compareVersions: function (v1, v2, options) {
|
||||||
var lexicographical = options && options.lexicographical,
|
var lexicographical = options && options.lexicographical,
|
||||||
zeroExtend = options && options.zeroExtend,
|
zeroExtend = options && options.zeroExtend,
|
||||||
|
@ -100,4 +128,4 @@ var Util = {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = Util;
|
module.exports = SetupUtil;
|
|
@ -2,35 +2,21 @@ var fs = require('fs');
|
||||||
var exec = require('exec');
|
var exec = require('exec');
|
||||||
var path = require('path');
|
var path = require('path');
|
||||||
var async = require('async');
|
var async = require('async');
|
||||||
var util = require('./util');
|
var util = require('./Util');
|
||||||
|
|
||||||
var VirtualBox = {
|
var VirtualBox = {
|
||||||
REQUIRED_VERSION: '4.3.18',
|
|
||||||
INCLUDED_VERSION: '4.3.18',
|
|
||||||
INSTALLER_FILENAME: 'virtualbox-4.3.18.pkg',
|
|
||||||
INSTALLER_CHECKSUM: '5836c94481c460c648b9216386591a2915293ac86b9bb6c57746637796af6af2',
|
|
||||||
command: function () {
|
command: function () {
|
||||||
return '/usr/bin/VBoxManage';
|
return '/usr/bin/VBoxManage';
|
||||||
},
|
},
|
||||||
installed: function () {
|
installed: function () {
|
||||||
return fs.existsSync('/usr/bin/VBoxManage') && fs.existsSync('/Applications/VirtualBox.app/Contents/MacOS/VirtualBox');
|
return fs.existsSync('/usr/bin/VBoxManage') && fs.existsSync('/Applications/VirtualBox.app/Contents/MacOS/VirtualBox');
|
||||||
},
|
},
|
||||||
install: function (callback) {
|
|
||||||
// -W waits for the process to close before finishing.
|
|
||||||
exec('open -W ' + path.join(util.supportDir(), this.INSTALLER_FILENAME).replace(' ', '\\ '), function (stderr, stdout, code) {
|
|
||||||
if (code) {
|
|
||||||
callback(stderr);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
callback(null);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
version: function (callback) {
|
version: function (callback) {
|
||||||
if (!this.installed()) {
|
if (!this.installed()) {
|
||||||
callback('VirtualBox not installed.');
|
callback('VirtualBox not installed.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
exec('/usr/bin/VBoxManage -v', function (stderr, stdout, code) {
|
exec([this.command(), '-v'], function (stderr, stdout, code) {
|
||||||
if (code) {
|
if (code) {
|
||||||
callback(stderr);
|
callback(stderr);
|
||||||
return;
|
return;
|
||||||
|
@ -44,12 +30,12 @@ var VirtualBox = {
|
||||||
callback(null, match[1]);
|
callback(null, match[1]);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
saveVMs: function (callback) {
|
poweroff: function (callback) {
|
||||||
if (!this.installed()) {
|
if (!this.installed()) {
|
||||||
callback('VirtualBox not installed.');
|
callback('VirtualBox not installed.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
exec('list runningvms | sed -E \'s/.*\\{(.*)\\}/\\1/\' | xargs -L1 -I {} VBoxManage controlvm {} savestate', function (stderr, stdout, code) {
|
exec(this.command() + ' list runningvms | sed -E \'s/.*\\{(.*)\\}/\\1/\' | xargs -L1 -I {} ' + this.command() + ' controlvm {} acpipowerbutton', function (stderr, stdout, code) {
|
||||||
if (code) {
|
if (code) {
|
||||||
callback(stderr);
|
callback(stderr);
|
||||||
} else {
|
} else {
|
||||||
|
@ -58,7 +44,7 @@ var VirtualBox = {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
kill: function (callback) {
|
kill: function (callback) {
|
||||||
this.saveRunningVMs(function (err) {
|
this.poweroff(function (err) {
|
||||||
if (err) {callback(err); return;}
|
if (err) {callback(err); return;}
|
||||||
exec('pkill VirtualBox', function (stderr, stdout, code) {
|
exec('pkill VirtualBox', function (stderr, stdout, code) {
|
||||||
if (code) {callback(stderr); return;}
|
if (code) {callback(stderr); return;}
|
||||||
|
@ -69,7 +55,7 @@ var VirtualBox = {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
vmState: function (name, callback) {
|
vmstate: function (name, callback) {
|
||||||
exec(this.command() + ' showvminfo ' + name + ' --machinereadable', function (stderr, stdout, code) {
|
exec(this.command() + ' showvminfo ' + name + ' --machinereadable', function (stderr, stdout, code) {
|
||||||
if (code) { callback(stderr); return; }
|
if (code) { callback(stderr); return; }
|
||||||
var match = stdout.match(/VMState="(\w+)"/);
|
var match = stdout.match(/VMState="(\w+)"/);
|
||||||
|
@ -80,24 +66,25 @@ var VirtualBox = {
|
||||||
callback(null, match[1]);
|
callback(null, match[1]);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
deleteVM:function (name, callback) {
|
vmdestroy: function (name, callback) {
|
||||||
VirtualBox.vmState(name, function (err, state) {
|
var self = this;
|
||||||
|
this.vmstate(name, function (err, state) {
|
||||||
// No VM found
|
// No VM found
|
||||||
if (err) { callback(null, false); return; }
|
if (err) { callback(null, false); return; }
|
||||||
VirtualBox.exec('controlvm ' + name + ' acpipowerbutton', function (stderr, stdout, code) {
|
exec('/usr/bin/VBoxManage controlvm ' + name + ' acpipowerbutton', function (stderr, stdout, code) {
|
||||||
if (code) { callback(stderr, false); return; }
|
if (code) { callback(stderr, false); return; }
|
||||||
var state = null;
|
var state = null;
|
||||||
|
|
||||||
async.until(function () {
|
async.until(function () {
|
||||||
return state === 'poweroff';
|
return state === 'poweroff';
|
||||||
}, function (callback) {
|
}, function (callback) {
|
||||||
VirtualBox.vmState(name, function (err, newState) {
|
self.vmstate(name, function (err, newState) {
|
||||||
if (err) { callback(err); return; }
|
if (err) { callback(err); return; }
|
||||||
state = newState;
|
state = newState;
|
||||||
setTimeout(callback, 250);
|
setTimeout(callback, 250);
|
||||||
});
|
});
|
||||||
}, function (err) {
|
}, function (err) {
|
||||||
VirtualBox.exec('unregistervm ' + name + ' --delete', function (stderr, stdout, code) {
|
exec('/usr/bin/VBoxManage unregistervm ' + name + ' --delete', function (stderr, stdout, code) {
|
||||||
if (code) { callback(err); return; }
|
if (code) { callback(err); return; }
|
||||||
callback();
|
callback();
|
||||||
});
|
});
|