Merge pull request #68 from kitematic/rc0.2.0

Release candidate 0.2.0 changes.
This commit is contained in:
Sean Li 2014-09-11 00:02:09 -07:00
commit cf4917e8a0
61 changed files with 832 additions and 4476 deletions

4
.gitignore vendored
View File

@ -11,8 +11,8 @@ bin
# Resources
resources/cache
resources/base-images.tar.gz
resources/virtualbox-4.3.12.pkg
resources/boot2docker
resources/virtualbox-*.pkg
resources/boot2docker*
resources/node-webkit
resources/mongod
resources/MONGOD_LICENSE.txt

View File

@ -1,9 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title>Welcome to Kitematic</title>
</head>
<body>
</body>
<script src="index.js" type="text/javascript"></script>
</html>

View File

@ -4,6 +4,9 @@ var os = require('os');
var fs = require('fs');
var path = require('path');
var app = require('app');
var BrowserWindow = require('browser-window');
var freeport = function (callback) {
var server = net.createServer();
var port = 0;
@ -44,7 +47,7 @@ var start = function (callback) {
console.log('MongoDB: ' + mongoPort);
console.log('webPort: ' + webPort);
child_process.exec('kill $(ps aux -e | grep PURPOSE=KITEMATIC | awk \'{print $2}\') && rm ' + path.join(dataPath, 'mongod.lock'), function (error, stdout, stderr) {
var mongoChild = child_process.spawn(path.join(process.cwd(), 'resources', 'mongod'), ['--bind_ip', '127.0.0.1', '--dbpath', dataPath, '--port', mongoPort, '--unixSocketPrefix', dataPath], {
var mongoChild = child_process.spawn(path.join(__dirname, 'resources', 'mongod'), ['--bind_ip', '127.0.0.1', '--dbpath', dataPath, '--port', mongoPort, '--unixSocketPrefix', dataPath], {
env: {
PURPOSE: 'KITEMATIC'
}
@ -68,8 +71,11 @@ var start = function (callback) {
user_env.BIND_IP = '127.0.0.1';
user_env.DB_PATH = dataPath;
user_env.MONGO_URL = 'mongodb://localhost:' + mongoPort + '/meteor';
console.log(path.join(process.cwd(), 'resources', 'node'));
var nodeChild = child_process.spawn(path.join(process.cwd(), 'resources', 'node'), ['./bundle/main.js'], {
user_env.METEOR_SETTINGS = fs.readFileSync(path.join(__dirname, 'resources', 'settings.json'), 'utf8');
user_env.DIR = __dirname;
user_env.NODE_ENV = 'production';
user_env.NODE_PATH = path.join(__dirname, 'node_modules');
var nodeChild = child_process.spawn(path.join(__dirname, 'resources', 'node'), [path.join(__dirname, 'bundle', 'main.js')], {
env: user_env
});
@ -94,40 +100,47 @@ var start = function (callback) {
}
};
start(function (url, nodeChild, mongoChild) {
var cleanUpChildren = function () {
console.log('Cleaning up children.')
mongoChild.kill();
nodeChild.kill();
};
if (nodeChild && mongoChild) {
process.on('exit', cleanUpChildren);
process.on('uncaughtException', cleanUpChildren);
process.on('SIGINT', cleanUpChildren);
process.on('SIGTERM', cleanUpChildren);
}
mainWindow = null;
var gui = require('nw.gui');
var mainWindow = gui.Window.get();
gui.App.on('reopen', function () {
app.on('activate-with-no-open-windows', function () {
if (mainWindow) {
mainWindow.show();
});
setTimeout(function () {
mainWindow.window.location = url;
mainWindow.on('loaded', function () {
mainWindow.show();
});
}, 400);
mainWindow.on('close', function (type) {
this.hide();
console.log('closed');
if (type === 'quit') {
console.log('here');
if (nodeChild && mongoChild) {
cleanUpChildren();
}
this.close(true);
}
return false;
});
app.on('ready', function() {
start(function (url, nodeChild, mongoChild) {
var cleanUpChildren = function () {
console.log('Cleaning up children.')
mongoChild.kill();
nodeChild.kill();
app.quit();
process.exit();
};
if (nodeChild && mongoChild) {
process.on('exit', cleanUpChildren);
process.on('uncaughtException', cleanUpChildren);
process.on('SIGINT', cleanUpChildren);
process.on('SIGTERM', cleanUpChildren);
}
console.log('Window Closed.');
// Create the browser window.
var windowOptions = {
width: 800,
height: 578,
frame: false,
resizable: false,
'web-preferences': {
'web-security': false
}
};
mainWindow = new BrowserWindow(windowOptions);
// and load the index.html of the app.
mainWindow.loadUrl(url);
mainWindow.show();
mainWindow.focus();
});
});

BIN
kitematic.icns Normal file

Binary file not shown.

View File

@ -157,6 +157,7 @@
"Images": true,
"Apps": true,
"Installs": true,
"Settings": true,
"Docker": true,
"Util": true,
"Sync": true,

View File

@ -0,0 +1,6 @@
# This file contains information which helps Meteor properly upgrade your
# app when you run 'meteor update'. You should check it into version control
# with your project.
notices-for-0.9.0
notices-for-0.9.1

View File

@ -6,9 +6,8 @@
standard-app-packages
less
bootstrap3-less
iron-router
handlebar-helpers
underscore-string-latest
collection-helpers
fast-render
iron-router-ga
iron:router
reywood:iron-router-ga

View File

@ -1 +1 @@
0.8.3
METEOR@0.9.1.1

View File

@ -1,60 +1,54 @@
accounts-base@1.0.0
application-configuration@1.0.0
autoupdate@1.0.4
application-configuration@1.0.1
autoupdate@1.0.6
binary-heap@1.0.0
blaze-tools@1.0.0
blaze@1.0.3
blaze@2.0.0
bootstrap3-less@0.0.0
callback-hook@1.0.0
check@1.0.0
collection-helpers@0.0.0
collection-hooks@0.0.0
collection2@0.0.0
ctl-helper@1.0.2
ctl@1.0.0
deps@1.0.0
ejson@1.0.0
fast-render@0.0.0
follower-livedata@1.0.0
ctl-helper@1.0.3
ctl@1.0.1
ddp@1.0.8
deps@1.0.3
ejson@1.0.1
follower-livedata@1.0.1
geojson-utils@1.0.0
handlebar-helpers@0.0.0
headers@0.0.0
html-tools@1.0.0
htmljs@1.0.0
id-map@1.0.0
inject-initial@0.0.0
iron-core@0.2.0
iron-dynamic-template@0.2.1
iron-layout@0.2.0
iron-router@0.8.2
iron:core@0.3.4
iron:dynamic-template@0.4.1
iron:layout@0.4.1
iron:router@0.9.3
jquery@1.0.0
json@1.0.0
less@1.0.4
livedata@1.0.5
localstorage@1.0.0
less@1.0.7
livedata@1.0.9
logging@1.0.2
meteor@1.0.2
meteor-platform@1.0.2
meteor@1.0.3
minifiers@1.0.2
minimongo@1.0.1
moment@0.0.0
mongo-livedata@1.0.3
npm@0.0.0
observe-sequence@1.0.1
octicons@0.0.0
minimongo@1.0.2
mongo-livedata@1.0.4
mongo@1.0.4
observe-sequence@1.0.2
ordered-dict@1.0.0
random@1.0.0
reactive-dict@1.0.0
reload@1.0.0
reactive-dict@1.0.2
reactive-var@1.0.1
reload@1.0.1
retry@1.0.0
reywood:iron-router-ga@0.3.2
routepolicy@1.0.0
service-configuration@1.0.0
session@1.0.0
simple-schema@0.0.0
spacebars-compiler@1.0.1
spacebars@1.0.0
standard-app-packages@1.0.0
templating@1.0.4
ui@1.0.0
session@1.0.1
spacebars-compiler@1.0.2
spacebars@1.0.1
standard-app-packages@1.0.1
templating@1.0.5
tracker@1.0.2
ui@1.0.2
underscore-string-latest@0.0.0
underscore@1.0.0
webapp@1.0.2
webapp@1.0.3

View File

@ -53,17 +53,20 @@ AppUtil.start = function (appId) {
Apps.update(app._id, {$set: {
status: 'STARTING'
}});
Docker.getContainerData(app.docker.Id, function (err, data) {
Docker.startContainer(app.docker.Id, function (err) {
if (err) { console.error(err); }
// Use dig to refresh the DNS
exec('/usr/bin/dig ' + app.name + '.kite @172.17.42.1', function(err, stdout, stderr) {
console.log(err);
console.log(stdout);
console.log(stderr);
Apps.update(app._id, {$set: {
status: 'READY',
docker: data
}});
Docker.getContainerData(app.docker.Id, function (err, data) {
if (err) { console.error(err); }
// Use dig to refresh the DNS
exec('/usr/bin/dig ' + app.name + '.kite @172.17.42.1', function(err, stdout, stderr) {
console.log(err);
console.log(stdout);
console.log(stderr);
Apps.update(app._id, {$set: {
status: 'READY',
docker: data
}});
});
});
});
}
@ -98,8 +101,8 @@ AppUtil.restart = function (appId) {
AppUtil.remove = function (appId) {
var app = Apps.findOne(appId);
Apps.remove({_id: appId});
if (app.docker) {
Apps.remove({_id: appId});
Docker.removeContainer(app.docker.Id, function (err) {
if (err) { console.error(err); }
var appPath = path.join(Util.KITE_PATH, app.name);
@ -131,7 +134,6 @@ AppUtil.logs = function (appId) {
logs: []
}
});
var logs = [];
response.setEncoding('utf8');
response.on('data', function (line) {
Apps.update(app._id, {

View File

@ -6,8 +6,12 @@ Boot2Docker = {};
Boot2Docker.REQUIRED_IP = '192.168.60.103';
Boot2Docker.command = function () {
return path.join(Util.getBinDir(), 'boot2docker-1.2.0') + ' --vm="kitematic-vm"';
};
Boot2Docker.exec = function (command, callback) {
exec(path.join(Util.getBinDir(), 'boot2docker') + ' ' + command, function(err, stdout, stderr) {
exec(Boot2Docker.command() + ' ' + command, function(err, stdout, stderr) {
callback(err, stdout, stderr);
});
};
@ -30,7 +34,7 @@ Boot2Docker.stop = function (callback) {
};
Boot2Docker.erase = function (callback) {
var VMFileLocation = path.join(Util.getHomePath(), 'VirtualBox\\ VMs/boot2docker-vm');
var VMFileLocation = path.join(Util.getHomePath(), 'VirtualBox\\ VMs/kitematic-vm');
exec('rm -rf ' + VMFileLocation, function (err) {
callback(err);
});
@ -235,7 +239,7 @@ Boot2Docker.version = function (callback) {
};
Boot2Docker.injectUtilities = function (callback) {
exec('/bin/cat ' + path.join(Util.getBinDir(), 'kite-binaries.tar.gz') + ' | ' + path.join(Util.getBinDir(), 'boot2docker') + ' ssh "tar zx -C /usr/local/bin"', function (err, stdout) {
exec('/bin/cat ' + path.join(Util.getBinDir(), 'kite-binaries.tar.gz') + ' | ' + Boot2Docker.command() + ' ssh "tar zx -C /usr/local/bin"', function (err, stdout) {
callback(err);
});
};

View File

@ -6,6 +6,9 @@ var path = require('path');
Docker = {};
Docker.DOCKER_HOST = '192.168.60.103';
Docker.DEFAULT_IMAGES_FILENAME = 'base-images-0.0.2.tar.gz';
Docker.DEFAULT_IMAGES_CHECKSUM = 'a3517ac21034a1969d9ff15e3c41b1e2f1aa83c67b16a8bd0bc378ffefaf573b'; // Sha256 Checksum
Docker.client = function () {
return new Dockerode({host: Docker.DOCKER_HOST, port: '2375'});
};
@ -83,7 +86,7 @@ Docker.runContainer = function (app, image, callback) {
Docker.startContainer = function (containerId, callback) {
var container = docker.getContainer(containerId);
container.stop(function (err) {
container.start(function (err) {
if (err) {
console.log(err);
callback(err);
@ -158,7 +161,7 @@ Docker.removeImage = function (imageId, callback) {
};
Docker.removeBindFolder = function (name, callback) {
exec(path.join(Util.getBinDir(), 'boot2docker') + ' ssh "sudo rm -rf /var/lib/docker/binds/' + name + '"', function (err, stdout) {
exec(Boot2Docker.command() + ' ssh "sudo rm -rf /var/lib/docker/binds/' + name + '"', function (err, stdout) {
callback(err, stdout);
});
};
@ -267,13 +270,13 @@ Docker.reloadDefaultContainers = function (callback) {
async.until(function () {
return ready;
}, function (callback) {
docker.listContainers(function (err, containers) {
docker.listContainers(function (err) {
if (!err) {
ready = true;
}
callback();
});
}, function (err) {
}, function () {
console.log('Removing old Kitematic default containers.');
Docker.killAndRemoveContainers(Docker.defaultContainerNames, function (err) {
console.log('Removed old Kitematic default containers.');
@ -283,7 +286,7 @@ Docker.reloadDefaultContainers = function (callback) {
return;
}
console.log('Loading new Kitematic default images.');
docker.loadImage(path.join(Util.getBinDir(), 'base-images.tar.gz'), {}, function (err) {
docker.loadImage(path.join(Util.getResourceDir(), Docker.DEFAULT_IMAGES_FILENAME), {}, function (err) {
if (err) {
callback(err);
return;

View File

@ -1,10 +1,13 @@
var async = require('async');
var fs = require('fs');
var path = require('path');
var remote = require('remote');
var app = remote.require('app');
Installer = {};
Installer.CURRENT_VERSION = '0.0.2';
Installer.CURRENT_VERSION = app.getVersion();
Installer.baseURL = 'https://s3.amazonaws.com/kite-installer/';
Installer.isUpToDate = function () {
return !!Installs.findOne({version: Installer.CURRENT_VERSION});
@ -19,26 +22,42 @@ Installer.isUpToDate = function () {
*/
Installer.steps = [
{
run: function (callback) {
run: function (callback, progressCallback) {
var installed = VirtualBox.installed();
if (!installed) {
VirtualBox.install(function (err) {
callback(err);
Util.downloadFile(Installer.baseURL + VirtualBox.INSTALLER_FILENAME, path.join(Util.getResourceDir(), 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 {
// Version 4.3.12 is required.
VirtualBox.version(function (err, installedVersion) {
if (err) {
callback(err);
return;
}
if (err) {callback(err); return;}
if (Util.compareVersions(installedVersion, VirtualBox.REQUIRED_VERSION) < 0) {
VirtualBox.install(function (err) {
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);
}
// Download a newer version of Virtualbox
Util.downloadFile(Installer.baseURL + VirtualBox.INSTALLER_FILENAME, path.join(Util.getResourceDir(), VirtualBox.INSTALLER_FILENAME), VirtualBox.INSTALLER_CHECKSUM, 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();
@ -47,8 +66,8 @@ Installer.steps = [
}
},
pastMessage: 'VirtualBox installed',
message: 'Installing VirtualBox',
futureMessage: 'Install VirtualBox if necessary'
message: 'Downloading & Installing VirtualBox',
futureMessage: 'Download & Install VirtualBox if necessary'
},
// Initialize Boot2Docker if necessary.
@ -57,7 +76,7 @@ Installer.steps = [
Boot2Docker.exists(function (err, exists) {
if (err) { callback(err); return; }
if (!exists) {
var vmFilesPath = path.join(Util.getHomePath(), 'VirtualBox\ VMs', 'boot2docker-vm');
var vmFilesPath = path.join(Util.getHomePath(), 'VirtualBox\ VMs', 'kitematic-vm');
if (fs.existsSync(vmFilesPath)) {
Util.deleteFolder(vmFilesPath);
}
@ -67,30 +86,30 @@ Installer.steps = [
} else {
if (!Boot2Docker.sshKeyExists()) {
callback('Boot2Docker SSH key doesn\'t exist. Fix by deleting the existing Boot2Docker VM and re-run the installer. This usually occurs because an old version of Boot2Docker is installed.');
}
Boot2Docker.stop(function (err) {
if (err) { callback(err); return; }
Boot2Docker.upgrade(function (err) {
callback(err);
} else {
Boot2Docker.stop(function(err) {
Boot2Docker.upgrade(function (err) {
callback(err);
});
});
});
}
}
});
},
pastMessage: 'Setup the Boot2Docker VM (if required)',
message: 'Setting up the Boot2Docker VM',
futureMessage: 'Set up the Boot2Docker VM(if required)'
pastMessage: 'Setup the Kitematic VM (if required)',
message: 'Setting up the Kitematic VM',
futureMessage: 'Set up the Kitematic VM(if required)'
},
{
run: function (callback) {
VirtualBox.addCustomHostAdapter('boot2docker-vm', function (err, ifname) {
VirtualBox.addCustomHostAdapter('kitematic-vm', function (err, ifname) {
callback(err);
});
},
pastMessage: 'Added custom host adapter to the Boot2Docker VM',
message: 'Adding custom host adapter to the Boot2Docker VM',
futureMessage: 'Add custom host adapter to the Boot2Docker VM'
pastMessage: 'Added custom host adapter to the Kitematic VM',
message: 'Adding custom host adapter to the Kitematic VM',
futureMessage: 'Add custom host adapter to the Kitematic VM'
},
// Start the Kitematic VM
@ -110,14 +129,14 @@ Installer.steps = [
}
});
},
pastMessage: 'Started the Boot2Docker VM',
message: 'Starting the Boot2Docker VM',
pastMessage: 'Started the Kitematic VM',
message: 'Starting the Kitematic VM',
futureMessage: 'Start the Kitematic VM'
},
{
run: function (callback) {
VirtualBox.setupRouting('boot2docker-vm', function (err, ifname) {
VirtualBox.setupRouting('kitematic-vm', function (err, ifname) {
callback(err);
});
},
@ -128,12 +147,16 @@ Installer.steps = [
// Set up the default Kitematic images
{
run: function (callback) {
Docker.reloadDefaultContainers(function (err) {
callback(err);
run: function (callback, progressCallback) {
Util.downloadFile(Installer.baseURL + Docker.DEFAULT_IMAGES_FILENAME, path.join(Util.getResourceDir(), Docker.DEFAULT_IMAGES_FILENAME), Docker.DEFAULT_IMAGES_CHECKSUM, function (err) {
Docker.reloadDefaultContainers(function (err) {
callback(err);
});
}, function (progress) {
progressCallback(progress);
});
},
pastMessage: 'Started the Boot2Docker VM',
pastMessage: 'Set up the default Kitematic images.',
message: 'Setting up the default Kitematic images...',
subMessage: '(This may take a few minutes)',
futureMessage: 'Set up the default Kitematic images'
@ -146,6 +169,7 @@ Installer.run = function (callback) {
Session.set('numberOfInstallSteps', this.steps.length);
async.eachSeries(this.steps, function (step, callback) {
console.log('Performing step ' + currentStep);
Session.set('currentInstallStepProgress', 0);
step.run(function (err) {
if (err) {
callback(err);
@ -154,6 +178,8 @@ Installer.run = function (callback) {
Session.set('currentInstallStep', currentStep);
callback();
}
}, function (progress) {
Session.set('currentInstallStepProgress', progress);
});
}, function (err) {
if (err) {

130
meteor/client/lib/menu.js Normal file
View File

@ -0,0 +1,130 @@
var remote = require('remote');
var app = remote.require('app');
var Menu = remote.require('menu');
// main.js
var template = [
{
label: 'Kitematic',
submenu: [
{
label: 'About Kitematic',
selector: 'orderFrontStandardAboutPanel:'
},
{
type: 'separator'
},
{
label: 'Services',
submenu: []
},
{
type: 'separator'
},
{
label: 'Hide Kitematic',
accelerator: 'Command+H',
selector: 'hide:'
},
{
label: 'Hide Others',
accelerator: 'Command+Shift+H',
selector: 'hideOtherApplications:'
},
{
label: 'Show All',
selector: 'unhideAllApplications:'
},
{
type: 'separator'
},
{
label: 'Quit',
accelerator: 'Command+Q',
click: function() { app.quit(); }
}
]
},
{
label: 'Edit',
submenu: [
{
label: 'Undo',
accelerator: 'Command+Z',
selector: 'undo:'
},
{
label: 'Redo',
accelerator: 'Shift+Command+Z',
selector: 'redo:'
},
{
type: 'separator'
},
{
label: 'Cut',
accelerator: 'Command+X',
selector: 'cut:'
},
{
label: 'Copy',
accelerator: 'Command+C',
selector: 'copy:'
},
{
label: 'Paste',
accelerator: 'Command+V',
selector: 'paste:'
},
{
label: 'Select All',
accelerator: 'Command+A',
selector: 'selectAll:'
}
]
},
{
label: 'View',
submenu: [
{
label: 'Reload',
accelerator: 'Command+R',
click: function() { remote.getCurrentWindow().reloadIgnoringCache(); }
},
{
label: 'Toggle DevTools',
accelerator: 'Alt+Command+I',
click: function() { remote.getCurrentWindow().toggleDevTools(); }
}
]
},
{
label: 'Window',
submenu: [
{
label: 'Minimize',
accelerator: 'Command+M',
selector: 'performMiniaturize:'
},
{
label: 'Close',
accelerator: 'Command+W',
click: function () { remote.getCurrentWindow().hide(); }
},
{
type: 'separator'
},
{
label: 'Bring All to Front',
selector: 'arrangeInFront:'
}
]
},
{
label: 'Help',
submenu: []
}
];
var menu = Menu.buildFromTemplate(template);
Menu.setApplicationMenu(menu);

View File

@ -1,19 +1,26 @@
Router.configure({
layoutTemplate: 'layout',
trackPageView: true
onBeforeAction: function () {
var install = Installs.findOne({});
if (install && install.tracking) {
var currentPath = Router.current().path;
console.log(currentPath);
ga('send', 'pageview', currentPath);
}
}
});
SetupController = RouteController.extend({
layoutTemplate: 'setup_layout',
waitOn: function () {
return [Meteor.subscribe('installs')];
return [Meteor.subscribe('installs'), Meteor.subscribe('settings')];
}
});
DashboardController = RouteController.extend({
layoutTemplate: 'dashboard_layout',
waitOn: function () {
return [Meteor.subscribe('apps'), Meteor.subscribe('images'), Meteor.subscribe('installs')];
return [Meteor.subscribe('apps'), Meteor.subscribe('images'), Meteor.subscribe('installs'), Meteor.subscribe('settings')];
}
});
@ -43,6 +50,11 @@ Router.map(function () {
controller: 'SetupController'
});
this.route('setup_finish', {
path: '/setup/finish',
controller: 'SetupController'
});
this.route('setup', {
path: '/',
controller: 'SetupController',
@ -51,12 +63,11 @@ Router.map(function () {
if (!Installer.isUpToDate()) {
if (!Installs.findOne()) {
console.log('No installs detected, running installer again.');
this.redirect('/setup/intro');
} else {
// There's an install but it's lower than the current version, re-run as an 'update'.
Session.set('isUpdating', true);
this.redirect('/setup/intro');
}
this.redirect('/setup/intro');
} else {
this.redirect('/apps');
}

View File

@ -13,4 +13,7 @@ Meteor.startup(function () {
console.log('Created Kitematic .images directory.');
fs.mkdirSync(Util.KITE_IMAGES_PATH);
}
if (!fs.existsSync(Util.getResourceDir())) {
fs.mkdirSync(Util.getResourceDir());
}
});

View File

@ -59,18 +59,20 @@ Sync.addAppWatcher = function (app) {
}
exec(args, function (err, out, code) {
var results = null;
var location = null;
try {
if (err.indexOf('the archives are locked.') !== -1) {
var results = errorPattern.exec(err);
var location = results[1].replace(' ', '\\ ');
results = errorPattern.exec(err);
location = results[1].replace(' ', '\\ ');
exec('/bin/rm -rf ' + location, function () {
console.log('Removed unison file.');
console.log(location);
});
}
if (err.indexOf('The archive file is missing on some hosts') !== -1) {
var results = archiveErrorPattern.exec(err);
var location = results[1].replace(' ', '\\ ');
results = archiveErrorPattern.exec(err);
location = results[1].replace(' ', '\\ ');
var fullLocation = path.join(Util.getHomePath(), 'Library/Application\\ Support/Unison', location);
var cmd = '/bin/rm -rf ' + fullLocation;
exec(cmd, function () {});

View File

@ -1,5 +1,8 @@
var path = require('path');
var fs = require('fs');
var nodeCrypto = require('crypto');
var request = require('request');
var progress = require('request-progress');
Util = {};
@ -8,15 +11,11 @@ Util.getHomePath = function () {
};
Util.getBinDir = function () {
if (process.env.NODE_ENV === 'development') {
return path.join(path.join(process.env.PWD, '..'), 'resources');
} else {
if (Meteor.isClient) {
return path.join(process.cwd(), 'resources');
} else {
return path.join(process.cwd(), '../../../resources');
}
}
return path.join(process.env.DIR, 'resources');
};
Util.getResourceDir = function () {
return path.join(Util.getHomePath(), 'Library/Application\ Support/Kitematic/Resources');
};
Util.KITE_PATH = path.join(Util.getHomePath(), 'Kitematic');
@ -78,6 +77,47 @@ Util.hasDockerfile = function (directory) {
return fs.existsSync(path.join(directory, 'Dockerfile'));
};
Util.openTerminal = function (command) {
var terminalCmd = path.join(Util.getBinDir(), 'terminal') + ' ' + command;
var exec = require('child_process').exec;
exec(terminalCmd, function (err, stdout) {
console.log(stdout);
if (err) {
console.log(err);
}
});
};
Util.downloadFile = function (url, filename, checksum, callback, progressCallback) {
var doDownload = function () {
progress(request(url), {
throttle: 250
}).on('progress', function (state) {
progressCallback(state.percent);
}).on('error', function (err) {
callback(err);
}).pipe(fs.createWriteStream(filename)).on('error', function (err) {
callback(err);
}).on('close', function (err) {
callback(err);
});
};
// Compare checksum to see if it already exists first
if (fs.existsSync(filename)) {
var existingChecksum = nodeCrypto.createHash('sha256').update(fs.readFileSync(filename), 'utf8').digest('hex');
console.log(existingChecksum);
if (existingChecksum !== checksum) {
fs.unlinkSync(filename);
doDownload();
} else {
callback();
}
} else {
doDownload();
}
};
/**
* Compares two software version numbers (e.g. "1.7.1" or "1.2b").
*
@ -156,8 +196,11 @@ Util.compareVersions = function (v1, v2, options) {
};
trackLink = function (trackLabel) {
if (trackLabel) {
console.log(trackLabel);
ga('send', 'event', 'link', 'click', trackLabel);
var install = Installs.findOne({});
if (install && install.tracking) {
if (trackLabel) {
console.log(trackLabel);
ga('send', 'event', 'link', 'click', trackLabel);
}
}
};

View File

@ -7,6 +7,7 @@ VirtualBox = {};
VirtualBox.REQUIRED_VERSION = '4.3.14';
VirtualBox.INCLUDED_VERSION = '4.3.14';
VirtualBox.INSTALLER_FILENAME = 'virtualbox-4.3.14.pkg';
VirtualBox.INSTALLER_CHECKSUM = '486348a5336539728ca20dcd9674cf3d37e5c7f32255d90f1edc7391b54bd5dd'; // Sha256 Checksum
// Info for the hostonly interface we add to the VM.
VirtualBox.HOSTONLY_HOSTIP = '192.168.60.3';
@ -24,7 +25,7 @@ VirtualBox.exec = function (command, callback) {
VirtualBox.install = function (callback) {
// -W waits for the process to close before finishing.
exec('open -W ' + path.join(Util.getBinDir(), this.INSTALLER_FILENAME), function (error, stdout, stderr) {
exec('open -W ' + path.join(Util.getResourceDir(), this.INSTALLER_FILENAME).replace(' ', '\\ '), function (error, stdout, stderr) {
console.log(stdout);
console.log(stderr);
if (error) {

View File

@ -1,11 +1,11 @@
try {
moment = require('moment');
gui = require('nw.gui');
gui.App.clearCache();
win = gui.Window.get();
var nativeMenuBar = new gui.Menu({type: 'menubar'});
nativeMenuBar.createMacBuiltin('Kitematic');
win.menu = nativeMenuBar;
// gui = require('nw.gui');
// gui.App.clearCache();
// win = gui.Window.get();
// var nativeMenuBar = new gui.Menu({type: 'menubar'});
// nativeMenuBar.createMacBuiltin('Kitematic');
// win.menu = nativeMenuBar;
} catch (e) {
console.error(e);
}

View File

@ -9,3 +9,4 @@
@import "stylesheets/dashboard.import.less";
@import "stylesheets/setup.import.less";
@import "stylesheets/spinner.import.less";
@import "stylesheets/radial-progress.import.less";

View File

@ -0,0 +1,91 @@
.radial-progress {
@circle-size: 34px;
@circle-background: #d6dadc;
@circle-color: #3FD899;
@inset-size: 28px;
@inset-color: #fbfbfb;
@transition-length: 1s;
@shadow: 0px 1px 3px rgba(0,0,0,0.2);
@percentage-color: #3FD899;
@percentage-font-size: 11px;
@percentage-text-width: 57px;
width: @circle-size;
height: @circle-size;
background-color: @circle-background;
border-radius: 50%;
.circle {
.mask, .fill, .shadow {
width: @circle-size;
height: @circle-size;
position: absolute;
border-radius: 50%;
}
.shadow {
box-shadow: @shadow inset;
}
.mask, .fill {
-webkit-backface-visibility: hidden;
transition: -webkit-transform @transition-length;
transition: -ms-transform @transition-length;
transition: transform @transition-length;
border-radius: 50%;
}
.mask {
clip: rect(0px, @circle-size, @circle-size, @circle-size/2);
.fill {
clip: rect(0px, @circle-size/2, @circle-size, 0px);
background-color: @circle-color;
}
}
}
.inset {
width: @inset-size;
height: @inset-size;
position: absolute;
margin-left: (@circle-size - @inset-size)/2;
margin-top: (@circle-size - @inset-size)/2;
background-color: @inset-color;
border-radius: 50%;
box-shadow: @shadow;
.percentage {
width: @percentage-text-width;
position: absolute;
top: (@inset-size - @percentage-font-size) / 2;
left: (@inset-size - @percentage-text-width) / 2;
line-height: 1;
text-align: center;
color: @percentage-color;
font-weight: 800;
font-size: @percentage-font-size;
}
}
@i: 0;
@increment: 180deg / 100;
.loop (@i) when (@i <= 100) {
&[data-progress="@{i}"] {
.circle {
.mask.full, .fill {
-webkit-transform: rotate(@increment * @i);
-ms-transform: rotate(@increment * @i);
transform: rotate(@increment * @i);
}
.fill.fix {
-webkit-transform: rotate(@increment * @i * 2);
-ms-transform: rotate(@increment * @i * 2);
transform: rotate(@increment * @i * 2);
}
}
.inset .percentage:before {
content: "@{i}%"
}
}
.loop(@i + 1);
}
.loop(@i);
}

View File

@ -40,7 +40,27 @@
margin-top: 60px;
}
.install-finish {
-webkit-app-region: no-drag;
margin-top: 80px;
p {
margin-top: 0;
font-size: 13px;
}
}
.install-diagonistics {
-webkit-app-region: no-drag;
label {
text-align: left;
}
margin: 0 auto;
max-width: 300px;
margin-top: 50px;
}
.install-continue {
-webkit-app-region: no-drag;
margin-top: 140px;
p {
margin-top: 0;

View File

@ -10,6 +10,7 @@ html, body {
font-size: @font-size-base;
height: @window-height;
.no-select();
overflow: hidden;
}
html, body, div, span, object,

View File

@ -1,3 +1,6 @@
var remote = require('remote');
var dialog = remote.require('dialog');
var getConfigVars = function ($form) {
var configVars = {};
$form.find('.env-var-pair').each(function () {
@ -39,10 +42,15 @@ Template.dashboard_apps_settings.events({
return false;
},
'click .btn-delete-app': function () {
var result = confirm("Are you sure you want to delete this app?");
if (result === true) {
AppUtil.remove(this._id);
Router.go('dashboard_apps');
}
var appId = this._id;
dialog.showMessageBox({
message: 'Are you sure you want to delete this app?',
buttons: ['Delete', 'Cancel']
}, function (index) {
if (index === 0) {
AppUtil.remove(appId);
Router.go('dashboard_apps');
}
});
}
});

View File

@ -22,17 +22,19 @@
<small><a onclick="trackLink('app image detail')" href="/images/{{image._id}}">{{image.meta.name}}</a></small>
</h5>
<div class="options">
{{#if $eq status 'STOPPED'}}
<a href="#" onclick="trackLink('start app')" class="btn-icon btn-start" target="_blank" data-toggle="tooltip" data-placement="bottom" title="Start App"><span class="typcn typcn-media-play-outline"></span></a>
{{/if}}
{{#if $eq status 'READY'}}
<a href="#" onclick="trackLink('stop app')" class="btn-icon btn-stop" target="_blank" data-toggle="tooltip" data-placement="bottom" title="Stop App"><span class="typcn typcn-media-stop"></span></a>
{{#if url}}
<a href="{{url}}" onclick="trackLink('view app')" class="btn-icon btn-view" target="_blank" data-toggle="tooltip" data-placement="bottom" title="View App"><span class="typcn typcn-eye-outline"></span></a>
{{/if}}
<a href="#" onclick="trackLink('terminal into app')" class="btn-icon btn-terminal" data-toggle="tooltip" data-placement="bottom" title="Terminal"><span class="typcn typcn-device-laptop"></span></a>
{{/if}}
<a href="#" onclick="trackLink('open app folder')" class="btn-icon btn-folder" target="_blank" data-toggle="tooltip" data-placement="bottom" title="Folder"><span class="typcn typcn-folder-open"></span></a>
{{#if $eq status 'READY'}}
<a href="#" onclick="trackLink('stop app')" class="btn-icon btn-stop" target="_blank" data-toggle="tooltip" data-placement="bottom" title="Stop App"><span class="typcn typcn-media-pause-outline"></span></a>
{{/if}}
{{#if $eq status 'STOPPED'}}
<a href="#" onclick="trackLink('start app')" class="btn-icon btn-start" target="_blank" data-toggle="tooltip" data-placement="bottom" title="Start App"><span class="typcn typcn-media-play-outline"></span></a>
{{/if}}
<a href="#" onclick="trackLink('restart app')" class="btn-icon btn-restart" target="_blank" data-toggle="tooltip" data-placement="bottom" title="Restart"><span class="typcn typcn-refresh-outline"></span></a>
<a href="/apps/{{name}}/logs" onclick="trackLink('app logs')" class="btn-icon btn-logs" data-toggle="tooltip" data-placement="bottom" title="Logs"><span class="typcn typcn-document-text"></span></a>
<a href="/apps/{{name}}/settings" onclick="trackLink('app settings')" class="btn-icon" data-toggle="tooltip" data-placement="bottom" title="Settings"><span class="typcn typcn-cog-outline"></span></a>

View File

@ -1,5 +1,3 @@
var path = require('path');
Template.dashboard_single_app.rendered = function () {
Meteor.setInterval(function () {
$('.btn-icon').tooltip();
@ -21,16 +19,8 @@ Template.dashboard_single_app.events({
},
'click .btn-terminal': function () {
var app = this;
var cmd = path.join(Util.getBinDir(), 'boot2docker') + ' ssh -t "sudo docker-enter ' + app.docker.Id + '"';
var terminalCmd = path.join(Util.getBinDir(), 'terminal') + ' ' + cmd;
var exec = require('child_process').exec;
console.log(terminalCmd);
exec(terminalCmd, function (err, stdout) {
console.log(stdout);
if (err) {
console.log(err);
}
});
var cmd = Boot2Docker.command() + ' ssh -t "sudo docker-enter ' + app.docker.Id + '"';
Util.openTerminal(cmd);
},
'click .btn-start': function () {
AppUtil.start(this._id);

View File

@ -1,9 +1,11 @@
var remote = require('remote');
Template.dashboard_menu.events({
'click .mac-close': function () {
win.close();
remote.getCurrentWindow().hide();
},
'click .mac-minimize': function () {
win.minimize();
remote.getCurrentWindow().minimize();
},
'mouseover .mac-window-options': function () {
$('.mac-close i').show();

View File

@ -14,7 +14,7 @@ Template.modal_create_app.events({
var validationResult = formValidate(formData, FormSchema.formCreateApp);
if (validationResult.errors) {
clearFormErrors($form);
showFormErrors($form, validationResult.errors.details);
showFormErrors($form, validationResult.errors);
} else {
clearFormErrors($form);
var cleaned = validationResult.cleaned;

View File

@ -12,7 +12,6 @@
<h5 id="picked-directory"></h5>
<h6 id="picked-directory-error" class="error"></h6>
<a onclick="trackLink('pick image folder')" href="#" id="btn-pick-directory" class="btn btn-positive btn-sm"><span class="typcn typcn-folder-open"></span> Select Folder</a>
<input id="directory-picker" type="file" style="display: none;" nwdirectory />
</div>
</div>
<div class="modal-footer">

View File

@ -1,5 +1,7 @@
var path = require('path');
var fs = require('fs');
var remote = require('remote');
var dialog = remote.require('dialog');
Template.modal_create_image.rendered = function () {
$('#modal-create-image').bind('hidden.bs.modal', function () {
@ -9,27 +11,31 @@ Template.modal_create_image.rendered = function () {
Template.modal_create_image.events({
'click #btn-pick-directory': function () {
$('#directory-picker').click();
},
'change #directory-picker': function (e) {
var $picker = $(e.currentTarget);
var pickedDirectory = $picker.val();
$('#picked-directory-error').html('');
if (pickedDirectory) {
$('#picked-directory').html('<strong>' + pickedDirectory + '<strong>');
if (!Util.hasDockerfile(pickedDirectory)) {
$('#picked-directory-error').html('Only directories with Dockerfiles are supported now.');
$('#btn-create-image').attr('disabled', 'disabled');
} else {
$('#btn-create-image').removeAttr('disabled');
dialog.showOpenDialog({properties: ['openDirectory']}, function (filenames) {
if (!filenames) {
return;
}
} else {
$('#picked-directory').html('');
$('#btn-create-image').attr('disabled', 'disabled');
}
var directory = filenames[0];
if (directory) {
$('#picked-directory').html('<strong>' + directory + '<strong>');
if (!Util.hasDockerfile(directory)) {
$('#picked-directory-error').html('Only directories with Dockerfiles are supported now.');
$('#btn-create-image').attr('disabled', 'disabled');
} else {
Session.set('createImagePickedDirectory', directory);
$('#btn-create-image').removeAttr('disabled');
}
} else {
$('#picked-directory').html('');
$('#btn-create-image').attr('disabled', 'disabled');
}
});
},
'click #btn-create-image': function () {
var directory = $('#directory-picker').val();
var directory = Session.get('createImagePickedDirectory');
if (!directory) {
return;
}
$('#directory-picker').val('');
$('#picked-directory-error').html('');
$('#picked-directory').html('');

View File

@ -62,7 +62,6 @@
<h5><strong>{{this.originPath}}</strong></h5>
<h6 id="picked-directory-error" class="error"></h6>
<a onclick="trackLink('change build directory')" href="#" id="btn-pick-directory" class="btn btn-positive btn-sm"><span class="typcn typcn-folder-open"></span> Change Directory</a>
<input id="directory-picker" type="file" style="display: none;" nwdirectory />
</div>
</div>
<div class="section">

View File

@ -1,8 +1,16 @@
var remote = require('remote');
var dialog = remote.require('dialog');
Template.dashboard_images_settings.events({
'click .btn-delete-image': function () {
var result = confirm("Are you sure you want to delete this image?");
if (result === true) {
var imageId = this._id;
var imageId = this._id;
dialog.showMessageBox({
message: 'Are you sure you want to delete this image?',
buttons: ['Delete', 'Cancel']
}, function (index) {
if (index !== 0) {
return;
}
var image = Images.findOne(imageId);
var app = Apps.findOne({imageId: imageId});
if (!app) {
@ -23,26 +31,27 @@ Template.dashboard_images_settings.events({
$('#error-delete-image').html('<small class="error">This image is currently being used by <a href="/apps/' + app.name + '">' + app.name + '</a>.</small>');
$('#error-delete-image').fadeIn();
}
}
});
},
'click #btn-pick-directory': function () {
$('#directory-picker').click();
},
'change #directory-picker': function (e) {
var imageId = this._id;
var $picker = $(e.currentTarget);
var pickedDirectory = $picker.val();
$('#picked-directory-error').html('');
if (pickedDirectory) {
if (!Util.hasDockerfile(pickedDirectory)) {
$('#picked-directory-error').html('Only directories with Dockerfiles are supported now.');
} else {
Images.update(imageId, {
$set: {
originPath: pickedDirectory
}
});
dialog.showOpenDialog({properties: ['openDirectory']}, function (filenames) {
if (!filenames) {
return;
}
}
var directory = filenames[0];
if (directory) {
if (!Util.hasDockerfile(directory)) {
$('#picked-directory-error').html('Only directories with Dockerfiles are supported now.');
} else {
Images.update(imageId, {
$set: {
originPath: directory
}
});
}
}
});
}
});

View File

@ -19,6 +19,12 @@
<a onclick="trackLink('go to app image')" href="/images/{{image._id}}" class="btn-image" data-toggle="tooltip" data-placement="bottom" title="Image" data-container="body"><span class="typcn typcn-camera-outline"></span></a>
{{/if}}
<a onclick="trackLink('open app folder')" href="#" class="btn-folder" target="_blank" data-toggle="tooltip" data-placement="bottom" title="Folder" data-container="body"><span class="typcn typcn-folder-open"></span></a>
{{#if $eq status 'READY'}}
<a href="#" onclick="trackLink('stop app')" class="btn-icon btn-stop" target="_blank" data-toggle="tooltip" data-placement="bottom" title="Stop App"><span class="typcn typcn-media-pause-outline"></span></a>
{{/if}}
{{#if $eq status 'STOPPED'}}
<a href="#" onclick="trackLink('start app')" class="btn-icon btn-start" target="_blank" data-toggle="tooltip" data-placement="bottom" title="Start App"><span class="typcn typcn-media-play-outline"></span></a>
{{/if}}
<a onclick="trackLink('restart app')" href="#" class="btn-restart" target="_blank" data-toggle="tooltip" data-placement="bottom" title="Restart" data-container="body"><span class="typcn typcn-refresh-outline"></span></a>
</span>
</h3>

View File

@ -24,20 +24,8 @@ Template.dashboard_apps_layout.events({
AppUtil.logs(this._id);
},
'click .btn-terminal': function () {
var buildCmd = function (dockerId, termApp) {
return "echo 'boot2docker --vm=\"boot2docker-vm\" ssh -t \"sudo docker-enter " + dockerId + "\"' > /tmp/nsenter-start && chmod +x /tmp/nsenter-start && open -a " + termApp + " /tmp/nsenter-start";
};
var app = this;
var nsenterCmd = buildCmd(app.docker.Id, 'iTerm.app');
var exec = require('child_process').exec;
exec(nsenterCmd, function (err) {
if (err) {
nsenterCmd = buildCmd(app.docker.Id, 'Terminal.app');
exec(nsenterCmd, function (err) {
if (err) { throw err; }
});
}
});
var cmd = Boot2Docker.command() + ' ssh -t "sudo docker-enter ' + this.docker.Id + '"';
Util.openTerminal(cmd);
},
'click .btn-restart': function () {
AppUtil.restart(this._id);
@ -47,5 +35,13 @@ Template.dashboard_apps_layout.events({
exec('open ' + this.path, function (err) {
if (err) { throw err; }
});
},
'click .btn-start': function () {
AppUtil.start(this._id);
$('.btn-icon').tooltip('hide');
},
'click .btn-stop': function () {
AppUtil.stop(this._id);
$('.btn-icon').tooltip('hide');
}
});

View File

@ -39,4 +39,17 @@
{{/if}}
</div>
</div>
<div class="section dashboard-settings">
<div class="left-section">
<h4>Usage Diagnostics</h4>
<p class="help-block">Send anonymized usage diagnostics to help us improve Kitematic.</p>
</div>
<div class="right-section">
{{#if settings.tracking}}
<a onclick="trackLink('turn off usage analytics')" class="btn btn-negative btn-usage-analytics-off">Turn Off Usage Diagnostics</a>
{{else}}
<a onclick="trackLink('turn on usage analytics')" class="btn btn-positive btn-usage-analytics-on">Turn On Usage Diagnostics</a>
{{/if}}
</div>
</div>
</template>

View File

@ -20,6 +20,28 @@ Template.dashboard_settings.events({
console.log(err);
}
});
},
'click .btn-usage-analytics-on': function () {
var settings = Settings.findOne();
Settings.update(settings._id, {
$set: {
tracking: true
}
});
},
'click .btn-usage-analytics-off': function () {
var settings = Settings.findOne();
Settings.update(settings._id, {
$set: {
tracking: false
}
});
}
});
Template.dashboard_settings.helpers({
settings: function () {
return Settings.findOne({});
}
});

View File

@ -0,0 +1,21 @@
<template name="setup_finish">
<h2>Installation Complete</h2>
<div class="container text-center">
<div class="install_logo">
<img src="/install_finished.png">
</div>
<div class="install-diagonistics">
<div class="checkbox">
<label>
<input type="checkbox" checked> Enable anonymous usage diagnostics to help us improve Kitematic.
</label>
</div>
</div>
<div class="install-finish">
<a onclick="trackLink('finish install')" class="finish-button">
<img src="/continue.png">
<p>Finish</p>
</a>
</div>
</div>
</template>

View File

@ -0,0 +1,20 @@
Template.setup_finish.events({
'click .finish-button': function (e) {
var enableDiagnostics = $('.install-diagonistics input').attr('checked') ? true : false;
Installs.insert({version: Installer.CURRENT_VERSION});
var settings = Settings.findOne();
if (!settings) {
Settings.insert({tracking: enableDiagnostics});
} else {
settings.update(settings._id, {
$set: {
tracking: enableDiagnostics
}
});
}
Router.go('dashboard_apps');
e.preventDefault();
e.stopPropagation();
return false;
}
});

View File

@ -1,4 +1,11 @@
<template name="setup_install">
{{#if isUpdating}}
<h2>Updating</h2>
<p>This may take a few minutes.</p>
{{else}}
<h2>Installing</h2>
<p>This may take a few minutes.</p>
{{/if}}
<div class="container text-left">
<div class="steps">
{{#each steps}}
@ -24,7 +31,11 @@
{{#if $eq this.index failedStep}}
<img src="/step_failed.png">
{{else}}
{{> spinner}}
{{#if currentInstallStepProgress}}
{{> radial_progress progress=currentInstallStepProgress}}
{{else}}
{{> spinner}}
{{/if}}
{{/if}}
</span>
<div class="media-body">

View File

@ -7,8 +7,7 @@ Template.setup_install.rendered = function() {
console.log('Setup failed.');
console.log(err);
} else {
Installs.insert({version: Installer.CURRENT_VERSION});
Router.go('dashboard_apps');
Router.go('setup_finish');
}
});
}
@ -25,6 +24,9 @@ Template.setup_install.helpers({
currentInstallStep: function () {
return Session.get('currentInstallStep');
},
currentInstallStepProgress: function () {
return Session.get('currentInstallStepProgress');
},
installComplete: function () {
return Session.get('currentInstallStep') === Installer.steps.length;
},

View File

@ -1,4 +1,11 @@
<template name="setup_intro">
{{#if isUpdating}}
<h2>Welcome Back</h2>
<p>Kitematic needs to update itself to continue.</p>
{{else}}
<h2>Welcome to Kitematic</h2>
<p>This will set up everything needed to run Kitematic on your Mac.</p>
{{/if}}
<div class="container text-center">
<div class="install_logo">
<img src="/install_logo.png">

View File

@ -1,13 +1,6 @@
<template name="setup_layout">
{{setTitle}}
<div class="setup content text-center">
{{#if isUpdating}}
<h2>Welcome Back</h2>
<p>Kitematic needs to update itself to continue.</p>
{{else}}
<h2>Welcome to Kitematic</h2>
<p>This will set up everything needed to run Kitematic on your Mac.</p>
{{/if}}
{{> yield}}
</div>
</template>

View File

@ -0,0 +1,17 @@
<template name="radial_progress">
<div class="radial-progress" data-progress="{{progress}}">
<div class="circle">
<div class="mask full">
<div class="fill"></div>
</div>
<div class="mask half">
<div class="fill"></div>
<div class="fill fix"></div>
</div>
<div class="shadow"></div>
</div>
<div class="inset">
<div class="percentage"></div>
</div>
</div>
</template>

View File

@ -0,0 +1,13 @@
Settings = new Meteor.Collection('settings');
Settings.allow({
'update': function () {
return true;
},
'insert': function () {
return true;
},
'remove': function () {
return true;
}
});

View File

@ -1,19 +1,15 @@
/bootstrap3-less
/iron-router
/npm
/blaze-layout
/headers
/inject-initial
/handlebar-helpers
/server-deps
/collection2
/simple-schema
/collection-hooks
/moment
/underscore-string-latest
/blocking
/collection-helpers
/octicons
/fast-render
/iron-layout
/iron-core

Binary file not shown.

Before

Width:  |  Height:  |  Size: 857 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

View File

@ -7,5 +7,9 @@ Meteor.publish('images', function () {
});
Meteor.publish('installs', function () {
return Installs.find({});
return Installs.find();
});
Meteor.publish('settings', function () {
return Settings.find();
});

View File

@ -1,7 +1,7 @@
{
"public": {
"ga": {
"id": "UA-53012639-2",
"id": "UA-54515442-1",
"create": {
"cookieDomain": "none"
}

View File

@ -1,11 +0,0 @@
{
"packages": {
"bootstrap3-less": {},
"iron-router": {},
"handlebar-helpers": {},
"underscore-string-latest": {},
"collection-helpers": {},
"fast-render": {},
"iron-router-ga": {}
}
}

View File

@ -1,71 +0,0 @@
{
"meteor": {},
"dependencies": {
"basePackages": {
"bootstrap3-less": {},
"iron-router": {},
"handlebar-helpers": {},
"underscore-string-latest": {},
"collection-helpers": {},
"fast-render": {},
"iron-router-ga": {}
},
"packages": {
"bootstrap3-less": {
"git": "https://github.com/simison/bootstrap3-less",
"tag": "v0.2.1",
"commit": "dc94a51ed00d7de6d6f2b6d65107a876d849ad61"
},
"iron-router": {
"git": "https://github.com/EventedMind/iron-router.git",
"tag": "v0.8.2",
"commit": "05415a8891ea87a00fb1e2388585f2ca5a38e0da"
},
"handlebar-helpers": {
"git": "https://github.com/raix/Meteor-handlebar-helpers.git",
"tag": "v0.1.1",
"commit": "0b407ab65e7c1ebd53d71aef0de2e2c1d21a597c"
},
"underscore-string-latest": {
"git": "https://github.com/TimHeckel/meteor-underscore-string.git",
"tag": "v2.3.3",
"commit": "4a5d70eee48fbd90a6e6fc78747250d704a0b3bb"
},
"collection-helpers": {
"git": "https://github.com/dburles/meteor-collection-helpers.git",
"tag": "v0.3.1",
"commit": "eff6c859cd91eae324f6c99ab755992d0f271d91"
},
"fast-render": {
"git": "https://github.com/arunoda/meteor-fast-render.git",
"tag": "v1.0.0",
"commit": "acbc04982025fe78cebb8865b5a04689741d4b0b"
},
"iron-router-ga": {
"git": "https://github.com/reywood/meteor-iron-router-ga.git",
"tag": "v0.2.5",
"commit": "ede54c4633f9a54fddd5c431202a88284df2a932"
},
"iron-layout": {
"git": "https://github.com/EventedMind/iron-layout.git",
"tag": "v0.2.0",
"commit": "4a2d53e35ba036b0c189c7ceca34be494d4c6c97"
},
"blaze-layout": {
"git": "https://github.com/EventedMind/blaze-layout.git",
"tag": "v0.2.5",
"commit": "273e3ab7d005d91a1a59c71bd224533b4dae2fbd"
},
"iron-core": {
"git": "https://github.com/EventedMind/iron-core.git",
"tag": "v0.2.0",
"commit": "0e48b5dc50d03f01025b7b900fb5ce2f13d52cad"
},
"iron-dynamic-template": {
"git": "https://github.com/EventedMind/iron-dynamic-template.git",
"tag": "v0.2.1",
"commit": "4dd1185c4d9d616c9abdb3f33e4a7d5a88db7e18"
}
}
}
}

View File

@ -1,19 +1,7 @@
{
"name": "Kitematic",
"main": "index.html",
"node-remote": "<local>",
"version": "0.1.0",
"window": {
"show": false,
"toolbar": false,
"frame": false,
"width": 800,
"height": 600,
"resizable": false
},
"engines": {
"node": "0.11.13"
},
"main": "index.js",
"version": "0.2.0",
"dependencies": {
"async": "^0.9.0",
"chokidar": "git+https://github.com/usekite/chokidar.git",
@ -22,6 +10,8 @@
"open": "0.0.5",
"dockerode": "2.0.3",
"tar": "0.1.20",
"ansi-to-html": "0.2.0"
"ansi-to-html": "0.2.0",
"request": "2.42.0",
"request-progress": "0.3.1"
}
}

View File

@ -12,7 +12,7 @@ echo "nameserver 172.17.42.1" > /etc/resolver/kite
DIR=$(dirname "$0")
USER=`w -h | sort -u -t' ' -k1,1 | awk '{print $1}'`
/bin/rm -rf /Library/LaunchAgents/com.kitematic.route.plist
/bin/rm -rf /Library/LaunchDaemons/com.kitematic.route.plist
echo "<?xml version=\"1.0\" encoding=\"UTF-8\"?>
<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">
<plist version=\"1.0\">
@ -23,7 +23,7 @@ echo "<?xml version=\"1.0\" encoding=\"UTF-8\"?>
<array>
<string>bash</string>
<string>-c</string>
<string>/usr/sbin/scutil -w State:/Network/Interface/$IFNAME/IPv4;/sbin/route -n add -net 172.17.0.0 -netmask 255.255.0.0 -gateway $GATEWAY</string>
<string>/usr/sbin/scutil -w State:/Network/Interface/$IFNAME/IPv4 -t 0;sudo /sbin/route -n add -net 172.17.0.0 -netmask 255.255.0.0 -gateway $GATEWAY</string>
</array>
<key>KeepAlive</key>
<false/>
@ -34,6 +34,6 @@ echo "<?xml version=\"1.0\" encoding=\"UTF-8\"?>
</dict>
</plist>" > /Library/LaunchAgents/com.kitematic.route.plist
# Add entries to routing table for Kitematic VM
# Add entries to routing table for Kitematic boot2docker-vms
/sbin/route delete -net 172.17.0.0 -netmask 255.255.0.0 -gateway $GATEWAY > /dev/null 2>&1 || true
/sbin/route -n add -net 172.17.0.0 -netmask 255.255.0.0 -gateway $GATEWAY

File diff suppressed because it is too large Load Diff

View File

@ -1,35 +0,0 @@
#!/bin/bash
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
source $DIR/colors.sh
BASE=$DIR/..
pushd $BASE/meteor
$BASE/script/setup.sh
NPM="$BASE/cache/node/bin/npm"
if ! type "mrt" > /dev/null 2>&1; then
cecho "meteorite not found, install using npm install meteorite -g" $red
exit 1
fi
rm -rf ../bundle
$NPM install demeteorizer -g
cecho "-----> Building bundle from Meteor app, this may take a few minutes..." $blue
$BASE/cache/node/bin/demeteorizer -o ../bundle
cd ../bundle
cecho "-----> Installing bundle npm packages." $blue
$NPM install
cecho "-----> Removing unnecessary node_modules" $blue
rm -rf ./programs/ctl/node_modules
cecho "Bundle created." $green
popd

View File

@ -3,61 +3,82 @@ set -e # Auto exit on error
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
source $DIR/colors.sh
source $DIR/versions.sh
BASE=$DIR/..
NPM="$BASE/cache/node/bin/npm"
NODE="$BASE/cache/node/bin/node"
VERSION=$($NODE -pe 'JSON.parse(process.argv[1]).version' "$(cat package.json)")
pushd $BASE/meteor
$BASE/script/setup.sh
rm -rf ../bundle
cecho "-----> Building bundle from Meteor app, this may take a few minutes..." $blue
meteor bundle --directory ../bundle
cd ../bundle
cecho "-----> Installing bundle npm packages." $blue
pushd programs/server
$NPM install
popd
cecho "Bundle created." $green
popd
pushd $BASE
if [ ! -d bundle ]; then
cecho "No bundle, run script/bundle.sh first." $red
exit 1
fi
rm -rf dist/osx/Kitematic.app
rm -rf dist/osx/Kitematic.zip
rm -rf dist/osx
mkdir -p dist/osx/
cecho "-----> Creating Kitematic.app..." $blue
find cache/node-webkit -name "debug\.log" -print0 | xargs -0 rm -rf
cp -R cache/node-webkit/node-webkit.app dist/osx/
mv dist/osx/node-webkit.app dist/osx/Kitematic.app
mkdir -p dist/osx/Kitematic.app/Contents/Resources/app.nw
DIST_APP=Kitematic.app
cecho "-----> Copying meteor bundle into Kitematic.app..." $blue
cp -R bundle dist/osx/Kitematic.app/Contents/Resources/app.nw/
cecho "-----> Creating $DIST_APP..." $blue
find cache/atom-shell -name "debug\.log" -print0 | xargs -0 rm -rf
cp -R cache/atom-shell/Atom.app dist/osx/
mv dist/osx/Atom.app dist/osx/$DIST_APP
mkdir -p dist/osx/$DIST_APP/Contents/Resources/app
cecho "-----> Copying node-webkit app into Kitematic.app..." $blue
cp index.html dist/osx/Kitematic.app/Contents/Resources/app.nw/
cp index.js dist/osx/Kitematic.app/Contents/Resources/app.nw/
cp package.json dist/osx/Kitematic.app/Contents/Resources/app.nw/
cp -R node_modules dist/osx/Kitematic.app/Contents/Resources/app.nw/
cecho "-----> Copying meteor bundle into $DIST_APP..." $blue
mv bundle dist/osx/$DIST_APP/Contents/Resources/app/
$DIR/setup.sh
cecho "-----> Copying node-webkit app into $DIST_APP..." $blue
cp index.js dist/osx/$DIST_APP/Contents/Resources/app/
cp package.json dist/osx/$DIST_APP/Contents/Resources/app/
cp -R node_modules dist/osx/$DIST_APP/Contents/Resources/app/
cecho "-----> Copying binary files to Kitematic.app" $blue
mkdir -p dist/osx/Kitematic.app/Contents/Resources/app.nw/resources
cp -v resources/* dist/osx/Kitematic.app/Contents/Resources/app.nw/resources/ || :
cecho "-----> Copying binary files to $DIST_APP" $blue
mkdir -p dist/osx/$DIST_APP/Contents/Resources/app/resources
cp -v resources/* dist/osx/$DIST_APP/Contents/Resources/app/resources/ || :
chmod +x dist/osx/Kitematic.app/Contents/Resources/app.nw/resources/$BOOT2DOCKER_CLI_FILE
chmod +x dist/osx/Kitematic.app/Contents/Resources/app.nw/resources/$COCOASUDO_FILE
chmod +x dist/osx/Kitematic.app/Contents/Resources/app.nw/resources/install
chmod +x dist/osx/Kitematic.app/Contents/Resources/app.nw/resources/terminal
chmod +x dist/osx/Kitematic.app/Contents/Resources/app.nw/resources/unison
chmod +x dist/osx/Kitematic.app/Contents/Resources/app.nw/resources/node
cecho "-----> Copying icon to $DIST_APP" $blue
cp kitematic.icns dist/osx/$DIST_APP/Contents/Resources/atom.icns
chmod +x dist/osx/$DIST_APP/Contents/Resources/app/resources/$BOOT2DOCKER_CLI_FILE
chmod +x dist/osx/$DIST_APP/Contents/Resources/app/resources/$COCOASUDO_FILE
chmod +x dist/osx/$DIST_APP/Contents/Resources/app/resources/install
chmod +x dist/osx/$DIST_APP/Contents/Resources/app/resources/terminal
chmod +x dist/osx/$DIST_APP/Contents/Resources/app/resources/unison
chmod +x dist/osx/$DIST_APP/Contents/Resources/app/resources/node
cecho "-----> Updating Info.plist version to $VERSION" $blue
/usr/libexec/PlistBuddy -c "Set :CFBundleVersion $VERSION" $BASE/dist/osx/$DIST_APP/Contents/Info.plist
/usr/libexec/PlistBuddy -c "Set :CFBundleDisplayName Kitematic" $BASE/dist/osx/$DIST_APP/Contents/Info.plist
if [ -f $DIR/sign.sh ]; then
cecho "-----> Signing app file...." $blue
$DIR/sign.sh $BASE/dist/osx/Kitematic.app
$DIR/sign.sh $BASE/dist/osx/$DIST_APP
fi
pushd dist/osx
cecho "-----> Creating disributable zip file...." $blue
ditto -c -k --sequesterRsrc --keepParent Kitematic.app Kitematic.zip
ditto -c -k --sequesterRsrc --keepParent $DIST_APP Kitematic-$VERSION.zip
popd
cecho "Done." $green
cecho "Kitematic app available at dist/osx/Kitematic.app" $green
cecho "Kitematic app available at dist/osx/$DIST_APP" $green
cecho "Kitematic zip distribution available at dist/osx/Kitematic.zip" $green
popd

View File

@ -3,14 +3,14 @@
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
BASE=$DIR/..
source $BASE/script/setup.sh
export ROOT_URL=https://localhost:3000
export DOCKER_HOST=http://192.168.59.103
export DOCKER_PORT=2375
#export METEOR_SETTINGS=`cat $BASE/meteor/settings_dev.json`
#echo $METEOR_SETTINGS
export DIR=$BASE
cd $BASE/meteor
exec 3< <(mrt --settings $BASE/meteor/settings_dev.json)
exec 3< <(meteor --settings $BASE/meteor/settings_dev.json)
sed '/App running at/q' <&3 ; cat <&3 &
NODE_ENV=development $BASE/cache/node-webkit/node-webkit.app/Contents/MacOS/node-webkit $BASE
NODE_ENV=development $BASE/cache/atom-shell/Atom.app/Contents/MacOS/Atom $BASE

View File

@ -3,7 +3,6 @@ set -e # Auto exit on error
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
source $DIR/colors.sh
source $DIR/versions.sh
BASE=$DIR/..
pushd $BASE
@ -12,15 +11,18 @@ mkdir -p cache
pushd cache
if [ ! -f $BASE_IMAGE_VERSION_FILE ]; then
cecho "-----> Downloading Kitematic base images..." $purple
curl -L --progress-bar -o $BASE_IMAGE_VERSION_FILE https://s3.amazonaws.com/kite-installer/$BASE_IMAGE_VERSION_FILE
cp $BASE_IMAGE_VERSION_FILE ../resources/$BASE_IMAGE_FILE
fi
BOOT2DOCKER_CLI_VERSION=1.2.0
BOOT2DOCKER_CLI_VERSION_FILE=boot2docker-$BOOT2DOCKER_CLI_VERSION
BOOT2DOCKER_CLI_FILE=boot2docker
if [ ! -f $BOOT2DOCKER_CLI_VERSION_FILE ]; then
cecho "-----> Downloading Boot2docker CLI..." $purple
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
ATOM_SHELL_VERSION=0.16.2
ATOM_SHELL_FILE=atom-shell-v$ATOM_SHELL_VERSION-darwin-x64.zip
if [ ! -f $ATOM_SHELL_FILE ]; then
cecho "-----> Downloading Atom Shell..." $purple
curl -L -o $ATOM_SHELL_FILE https://github.com/atom/atom-shell/releases/download/v$ATOM_SHELL_VERSION/$ATOM_SHELL_FILE
mkdir -p atom-shell
unzip -d atom-shell $ATOM_SHELL_FILE
fi
if [ ! -f kite-node-webkit.tar.gz ]; then
@ -50,23 +52,20 @@ popd
pushd resources
if [ ! -f $VIRTUALBOX_FILE ]; then
cecho "-----> Downloading virtualbox installer..." $purple
curl -L --progress-bar -o $VIRTUALBOX_FILE https://s3.amazonaws.com/kite-installer/$VIRTUALBOX_FILE
if [ ! -f $BOOT2DOCKER_CLI_VERSION_FILE ]; then
cecho "-----> Downloading Boot2docker CLI..." $purple
curl -L -o $BOOT2DOCKER_CLI_VERSION_FILE https://s3.amazonaws.com/kite-installer/boot2docker-v$BOOT2DOCKER_CLI_VERSION
fi
if [ ! -f $COCOASUDO_FILE ]; then
cecho "-----> Downloading Cocoasudo binary..." $purple
curl -L -o $COCOASUDO_FILE https://github.com/performantdesign/cocoasudo/blob/master/build/Release/cocoasudo
chmod +x $COCOASUDO_FILE
fi
cp ../cache/$BOOT2DOCKER_CLI_VERSION_FILE $BOOT2DOCKER_CLI_FILE
chmod +x $BOOT2DOCKER_CLI_FILE
chmod +x $BOOT2DOCKER_CLI_VERSION_FILE
popd
NPM="$BASE/cache/node/bin/npm"
$NPM install
export npm_config_disturl=https://gh-contractor-zcbenz.s3.amazonaws.com/atom-shell/dist
export npm_config_target=0.16.2
export npm_config_arch=ia32
HOME=~/.atom-shell-gyp $NPM install
popd

View File

@ -1,11 +0,0 @@
BASE_IMAGE_VERSION=0.0.2
BASE_IMAGE_VERSION_FILE=base-images-$BASE_IMAGE_VERSION.tar.gz
BASE_IMAGE_FILE=base-images.tar.gz
VIRTUALBOX_FILE=virtualbox-4.3.14.pkg
BOOT2DOCKER_CLI_VERSION=1.2.0
BOOT2DOCKER_CLI_VERSION_FILE=boot2docker-$BOOT2DOCKER_CLI_VERSION
BOOT2DOCKER_CLI_FILE=boot2docker
COCOASUDO_FILE=cocoasudo