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
resources/cache resources/cache
resources/base-images.tar.gz resources/base-images.tar.gz
resources/virtualbox-4.3.12.pkg resources/virtualbox-*.pkg
resources/boot2docker resources/boot2docker*
resources/node-webkit resources/node-webkit
resources/mongod resources/mongod
resources/MONGOD_LICENSE.txt 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 fs = require('fs');
var path = require('path'); var path = require('path');
var app = require('app');
var BrowserWindow = require('browser-window');
var freeport = function (callback) { var freeport = function (callback) {
var server = net.createServer(); var server = net.createServer();
var port = 0; var port = 0;
@ -44,7 +47,7 @@ var start = function (callback) {
console.log('MongoDB: ' + mongoPort); console.log('MongoDB: ' + mongoPort);
console.log('webPort: ' + webPort); 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) { 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: { env: {
PURPOSE: 'KITEMATIC' PURPOSE: 'KITEMATIC'
} }
@ -68,8 +71,11 @@ var start = function (callback) {
user_env.BIND_IP = '127.0.0.1'; user_env.BIND_IP = '127.0.0.1';
user_env.DB_PATH = dataPath; user_env.DB_PATH = dataPath;
user_env.MONGO_URL = 'mongodb://localhost:' + mongoPort + '/meteor'; user_env.MONGO_URL = 'mongodb://localhost:' + mongoPort + '/meteor';
console.log(path.join(process.cwd(), 'resources', 'node')); user_env.METEOR_SETTINGS = fs.readFileSync(path.join(__dirname, 'resources', 'settings.json'), 'utf8');
var nodeChild = child_process.spawn(path.join(process.cwd(), 'resources', 'node'), ['./bundle/main.js'], { 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 env: user_env
}); });
@ -94,40 +100,47 @@ var start = function (callback) {
} }
}; };
start(function (url, nodeChild, mongoChild) { mainWindow = null;
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);
}
var gui = require('nw.gui'); app.on('activate-with-no-open-windows', function () {
var mainWindow = gui.Window.get(); if (mainWindow) {
gui.App.on('reopen', function () {
mainWindow.show(); mainWindow.show();
}); }
setTimeout(function () { return false;
mainWindow.window.location = url; });
mainWindow.on('loaded', function () {
mainWindow.show(); app.on('ready', function() {
}); start(function (url, nodeChild, mongoChild) {
}, 400); var cleanUpChildren = function () {
mainWindow.on('close', function (type) { console.log('Cleaning up children.')
this.hide(); mongoChild.kill();
console.log('closed'); nodeChild.kill();
if (type === 'quit') { app.quit();
console.log('here'); process.exit();
if (nodeChild && mongoChild) { };
cleanUpChildren();
} if (nodeChild && mongoChild) {
this.close(true); 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, "Images": true,
"Apps": true, "Apps": true,
"Installs": true, "Installs": true,
"Settings": true,
"Docker": true, "Docker": true,
"Util": true, "Util": true,
"Sync": 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 standard-app-packages
less less
bootstrap3-less bootstrap3-less
iron-router
handlebar-helpers handlebar-helpers
underscore-string-latest underscore-string-latest
collection-helpers collection-helpers
fast-render iron:router
iron-router-ga 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.1
application-configuration@1.0.0 autoupdate@1.0.6
autoupdate@1.0.4
binary-heap@1.0.0 binary-heap@1.0.0
blaze-tools@1.0.0 blaze-tools@1.0.0
blaze@1.0.3 blaze@2.0.0
bootstrap3-less@0.0.0 bootstrap3-less@0.0.0
callback-hook@1.0.0 callback-hook@1.0.0
check@1.0.0 check@1.0.0
collection-helpers@0.0.0 collection-helpers@0.0.0
collection-hooks@0.0.0 ctl-helper@1.0.3
collection2@0.0.0 ctl@1.0.1
ctl-helper@1.0.2 ddp@1.0.8
ctl@1.0.0 deps@1.0.3
deps@1.0.0 ejson@1.0.1
ejson@1.0.0 follower-livedata@1.0.1
fast-render@0.0.0
follower-livedata@1.0.0
geojson-utils@1.0.0 geojson-utils@1.0.0
handlebar-helpers@0.0.0 handlebar-helpers@0.0.0
headers@0.0.0
html-tools@1.0.0 html-tools@1.0.0
htmljs@1.0.0 htmljs@1.0.0
id-map@1.0.0 id-map@1.0.0
inject-initial@0.0.0 iron:core@0.3.4
iron-core@0.2.0 iron:dynamic-template@0.4.1
iron-dynamic-template@0.2.1 iron:layout@0.4.1
iron-layout@0.2.0 iron:router@0.9.3
iron-router@0.8.2
jquery@1.0.0 jquery@1.0.0
json@1.0.0 json@1.0.0
less@1.0.4 less@1.0.7
livedata@1.0.5 livedata@1.0.9
localstorage@1.0.0
logging@1.0.2 logging@1.0.2
meteor@1.0.2 meteor-platform@1.0.2
meteor@1.0.3
minifiers@1.0.2 minifiers@1.0.2
minimongo@1.0.1 minimongo@1.0.2
moment@0.0.0 mongo-livedata@1.0.4
mongo-livedata@1.0.3 mongo@1.0.4
npm@0.0.0 observe-sequence@1.0.2
observe-sequence@1.0.1
octicons@0.0.0
ordered-dict@1.0.0 ordered-dict@1.0.0
random@1.0.0 random@1.0.0
reactive-dict@1.0.0 reactive-dict@1.0.2
reload@1.0.0 reactive-var@1.0.1
reload@1.0.1
retry@1.0.0 retry@1.0.0
reywood:iron-router-ga@0.3.2
routepolicy@1.0.0 routepolicy@1.0.0
service-configuration@1.0.0 session@1.0.1
session@1.0.0 spacebars-compiler@1.0.2
simple-schema@0.0.0 spacebars@1.0.1
spacebars-compiler@1.0.1 standard-app-packages@1.0.1
spacebars@1.0.0 templating@1.0.5
standard-app-packages@1.0.0 tracker@1.0.2
templating@1.0.4 ui@1.0.2
ui@1.0.0
underscore-string-latest@0.0.0 underscore-string-latest@0.0.0
underscore@1.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: { Apps.update(app._id, {$set: {
status: 'STARTING' status: 'STARTING'
}}); }});
Docker.getContainerData(app.docker.Id, function (err, data) { Docker.startContainer(app.docker.Id, function (err) {
if (err) { console.error(err); } if (err) { console.error(err); }
// Use dig to refresh the DNS Docker.getContainerData(app.docker.Id, function (err, data) {
exec('/usr/bin/dig ' + app.name + '.kite @172.17.42.1', function(err, stdout, stderr) { if (err) { console.error(err); }
console.log(err); // Use dig to refresh the DNS
console.log(stdout); exec('/usr/bin/dig ' + app.name + '.kite @172.17.42.1', function(err, stdout, stderr) {
console.log(stderr); console.log(err);
Apps.update(app._id, {$set: { console.log(stdout);
status: 'READY', console.log(stderr);
docker: data Apps.update(app._id, {$set: {
}}); status: 'READY',
docker: data
}});
});
}); });
}); });
} }
@ -98,8 +101,8 @@ AppUtil.restart = function (appId) {
AppUtil.remove = function (appId) { AppUtil.remove = function (appId) {
var app = Apps.findOne(appId); var app = Apps.findOne(appId);
Apps.remove({_id: appId});
if (app.docker) { if (app.docker) {
Apps.remove({_id: appId});
Docker.removeContainer(app.docker.Id, function (err) { Docker.removeContainer(app.docker.Id, function (err) {
if (err) { console.error(err); } if (err) { console.error(err); }
var appPath = path.join(Util.KITE_PATH, app.name); var appPath = path.join(Util.KITE_PATH, app.name);
@ -131,7 +134,6 @@ AppUtil.logs = function (appId) {
logs: [] logs: []
} }
}); });
var logs = [];
response.setEncoding('utf8'); response.setEncoding('utf8');
response.on('data', function (line) { response.on('data', function (line) {
Apps.update(app._id, { Apps.update(app._id, {

View File

@ -6,8 +6,12 @@ Boot2Docker = {};
Boot2Docker.REQUIRED_IP = '192.168.60.103'; 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) { 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); callback(err, stdout, stderr);
}); });
}; };
@ -30,7 +34,7 @@ Boot2Docker.stop = function (callback) {
}; };
Boot2Docker.erase = 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) { exec('rm -rf ' + VMFileLocation, function (err) {
callback(err); callback(err);
}); });
@ -235,7 +239,7 @@ Boot2Docker.version = function (callback) {
}; };
Boot2Docker.injectUtilities = 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); callback(err);
}); });
}; };

View File

@ -6,6 +6,9 @@ var path = require('path');
Docker = {}; Docker = {};
Docker.DOCKER_HOST = '192.168.60.103'; 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 () { Docker.client = function () {
return new Dockerode({host: Docker.DOCKER_HOST, port: '2375'}); return new Dockerode({host: Docker.DOCKER_HOST, port: '2375'});
}; };
@ -83,7 +86,7 @@ Docker.runContainer = function (app, image, callback) {
Docker.startContainer = function (containerId, callback) { Docker.startContainer = function (containerId, callback) {
var container = docker.getContainer(containerId); var container = docker.getContainer(containerId);
container.stop(function (err) { container.start(function (err) {
if (err) { if (err) {
console.log(err); console.log(err);
callback(err); callback(err);
@ -158,7 +161,7 @@ Docker.removeImage = function (imageId, callback) {
}; };
Docker.removeBindFolder = function (name, 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); callback(err, stdout);
}); });
}; };
@ -267,13 +270,13 @@ Docker.reloadDefaultContainers = function (callback) {
async.until(function () { async.until(function () {
return ready; return ready;
}, function (callback) { }, function (callback) {
docker.listContainers(function (err, containers) { docker.listContainers(function (err) {
if (!err) { if (!err) {
ready = true; ready = true;
} }
callback(); callback();
}); });
}, function (err) { }, function () {
console.log('Removing old Kitematic default containers.'); console.log('Removing old Kitematic default containers.');
Docker.killAndRemoveContainers(Docker.defaultContainerNames, function (err) { Docker.killAndRemoveContainers(Docker.defaultContainerNames, function (err) {
console.log('Removed old Kitematic default containers.'); console.log('Removed old Kitematic default containers.');
@ -283,7 +286,7 @@ Docker.reloadDefaultContainers = function (callback) {
return; return;
} }
console.log('Loading new Kitematic default images.'); 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) { if (err) {
callback(err); callback(err);
return; return;

View File

@ -1,10 +1,13 @@
var async = require('async'); var async = require('async');
var fs = require('fs'); var fs = require('fs');
var path = require('path'); var path = require('path');
var remote = require('remote');
var app = remote.require('app');
Installer = {}; Installer = {};
Installer.CURRENT_VERSION = '0.0.2'; Installer.CURRENT_VERSION = app.getVersion();
Installer.baseURL = 'https://s3.amazonaws.com/kite-installer/';
Installer.isUpToDate = function () { Installer.isUpToDate = function () {
return !!Installs.findOne({version: Installer.CURRENT_VERSION}); return !!Installs.findOne({version: Installer.CURRENT_VERSION});
@ -19,26 +22,42 @@ Installer.isUpToDate = function () {
*/ */
Installer.steps = [ Installer.steps = [
{ {
run: function (callback) { run: function (callback, progressCallback) {
var installed = VirtualBox.installed(); var installed = VirtualBox.installed();
if (!installed) { if (!installed) {
VirtualBox.install(function (err) { Util.downloadFile(Installer.baseURL + VirtualBox.INSTALLER_FILENAME, path.join(Util.getResourceDir(), VirtualBox.INSTALLER_FILENAME), VirtualBox.INSTALLER_CHECKSUM, function (err) {
callback(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 { } else {
// Version 4.3.12 is required. // Version 4.3.12 is required.
VirtualBox.version(function (err, installedVersion) { VirtualBox.version(function (err, installedVersion) {
if (err) { if (err) {callback(err); return;}
callback(err);
return;
}
if (Util.compareVersions(installedVersion, VirtualBox.REQUIRED_VERSION) < 0) { if (Util.compareVersions(installedVersion, VirtualBox.REQUIRED_VERSION) < 0) {
VirtualBox.install(function (err) { // Download a newer version of Virtualbox
if (Util.compareVersions(installedVersion, VirtualBox.REQUIRED_VERSION) < 0) { Util.downloadFile(Installer.baseURL + VirtualBox.INSTALLER_FILENAME, path.join(Util.getResourceDir(), VirtualBox.INSTALLER_FILENAME), VirtualBox.INSTALLER_CHECKSUM, function (err) {
callback('VirtualBox could not be installed. The installation either failed or was cancelled. Please try closing all VirtualBox instances and try again.'); if (err) {callback(err); return;}
} else { VirtualBox.install(function (err) {
callback(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 { } else {
callback(); callback();
@ -47,8 +66,8 @@ Installer.steps = [
} }
}, },
pastMessage: 'VirtualBox installed', pastMessage: 'VirtualBox installed',
message: 'Installing VirtualBox', message: 'Downloading & Installing VirtualBox',
futureMessage: 'Install VirtualBox if necessary' futureMessage: 'Download & Install VirtualBox if necessary'
}, },
// Initialize Boot2Docker if necessary. // Initialize Boot2Docker if necessary.
@ -57,7 +76,7 @@ Installer.steps = [
Boot2Docker.exists(function (err, exists) { Boot2Docker.exists(function (err, exists) {
if (err) { callback(err); return; } if (err) { callback(err); return; }
if (!exists) { 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)) { if (fs.existsSync(vmFilesPath)) {
Util.deleteFolder(vmFilesPath); Util.deleteFolder(vmFilesPath);
} }
@ -67,30 +86,30 @@ Installer.steps = [
} else { } else {
if (!Boot2Docker.sshKeyExists()) { 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.'); 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.');
} } else {
Boot2Docker.stop(function (err) { Boot2Docker.stop(function(err) {
if (err) { callback(err); return; } Boot2Docker.upgrade(function (err) {
Boot2Docker.upgrade(function (err) { callback(err);
callback(err); });
}); });
}); }
} }
}); });
}, },
pastMessage: 'Setup the Boot2Docker VM (if required)', pastMessage: 'Setup the Kitematic VM (if required)',
message: 'Setting up the Boot2Docker VM', message: 'Setting up the Kitematic VM',
futureMessage: 'Set up the Boot2Docker VM(if required)' futureMessage: 'Set up the Kitematic VM(if required)'
}, },
{ {
run: function (callback) { run: function (callback) {
VirtualBox.addCustomHostAdapter('boot2docker-vm', function (err, ifname) { VirtualBox.addCustomHostAdapter('kitematic-vm', function (err, ifname) {
callback(err); callback(err);
}); });
}, },
pastMessage: 'Added custom host adapter to the Boot2Docker VM', pastMessage: 'Added custom host adapter to the Kitematic VM',
message: 'Adding custom host adapter to the Boot2Docker VM', message: 'Adding custom host adapter to the Kitematic VM',
futureMessage: 'Add custom host adapter to the Boot2Docker VM' futureMessage: 'Add custom host adapter to the Kitematic VM'
}, },
// Start the Kitematic VM // Start the Kitematic VM
@ -110,14 +129,14 @@ Installer.steps = [
} }
}); });
}, },
pastMessage: 'Started the Boot2Docker VM', pastMessage: 'Started the Kitematic VM',
message: 'Starting the Boot2Docker VM', message: 'Starting the Kitematic VM',
futureMessage: 'Start the Kitematic VM' futureMessage: 'Start the Kitematic VM'
}, },
{ {
run: function (callback) { run: function (callback) {
VirtualBox.setupRouting('boot2docker-vm', function (err, ifname) { VirtualBox.setupRouting('kitematic-vm', function (err, ifname) {
callback(err); callback(err);
}); });
}, },
@ -128,12 +147,16 @@ Installer.steps = [
// Set up the default Kitematic images // Set up the default Kitematic images
{ {
run: function (callback) { run: function (callback, progressCallback) {
Docker.reloadDefaultContainers(function (err) { Util.downloadFile(Installer.baseURL + Docker.DEFAULT_IMAGES_FILENAME, path.join(Util.getResourceDir(), Docker.DEFAULT_IMAGES_FILENAME), Docker.DEFAULT_IMAGES_CHECKSUM, function (err) {
callback(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...', message: 'Setting up the default Kitematic images...',
subMessage: '(This may take a few minutes)', subMessage: '(This may take a few minutes)',
futureMessage: 'Set up the default Kitematic images' futureMessage: 'Set up the default Kitematic images'
@ -146,6 +169,7 @@ Installer.run = function (callback) {
Session.set('numberOfInstallSteps', this.steps.length); Session.set('numberOfInstallSteps', this.steps.length);
async.eachSeries(this.steps, function (step, callback) { async.eachSeries(this.steps, function (step, callback) {
console.log('Performing step ' + currentStep); console.log('Performing step ' + currentStep);
Session.set('currentInstallStepProgress', 0);
step.run(function (err) { step.run(function (err) {
if (err) { if (err) {
callback(err); callback(err);
@ -154,6 +178,8 @@ Installer.run = function (callback) {
Session.set('currentInstallStep', currentStep); Session.set('currentInstallStep', currentStep);
callback(); callback();
} }
}, function (progress) {
Session.set('currentInstallStepProgress', progress);
}); });
}, function (err) { }, function (err) {
if (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({ Router.configure({
layoutTemplate: 'layout', 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({ SetupController = RouteController.extend({
layoutTemplate: 'setup_layout', layoutTemplate: 'setup_layout',
waitOn: function () { waitOn: function () {
return [Meteor.subscribe('installs')]; return [Meteor.subscribe('installs'), Meteor.subscribe('settings')];
} }
}); });
DashboardController = RouteController.extend({ DashboardController = RouteController.extend({
layoutTemplate: 'dashboard_layout', layoutTemplate: 'dashboard_layout',
waitOn: function () { 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' controller: 'SetupController'
}); });
this.route('setup_finish', {
path: '/setup/finish',
controller: 'SetupController'
});
this.route('setup', { this.route('setup', {
path: '/', path: '/',
controller: 'SetupController', controller: 'SetupController',
@ -51,12 +63,11 @@ Router.map(function () {
if (!Installer.isUpToDate()) { if (!Installer.isUpToDate()) {
if (!Installs.findOne()) { if (!Installs.findOne()) {
console.log('No installs detected, running installer again.'); console.log('No installs detected, running installer again.');
this.redirect('/setup/intro');
} else { } else {
// There's an install but it's lower than the current version, re-run as an 'update'. // There's an install but it's lower than the current version, re-run as an 'update'.
Session.set('isUpdating', true); Session.set('isUpdating', true);
this.redirect('/setup/intro');
} }
this.redirect('/setup/intro');
} else { } else {
this.redirect('/apps'); this.redirect('/apps');
} }

View File

@ -13,4 +13,7 @@ Meteor.startup(function () {
console.log('Created Kitematic .images directory.'); console.log('Created Kitematic .images directory.');
fs.mkdirSync(Util.KITE_IMAGES_PATH); 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) { exec(args, function (err, out, code) {
var results = null;
var location = null;
try { try {
if (err.indexOf('the archives are locked.') !== -1) { if (err.indexOf('the archives are locked.') !== -1) {
var results = errorPattern.exec(err); results = errorPattern.exec(err);
var location = results[1].replace(' ', '\\ '); location = results[1].replace(' ', '\\ ');
exec('/bin/rm -rf ' + location, function () { exec('/bin/rm -rf ' + location, function () {
console.log('Removed unison file.'); console.log('Removed unison file.');
console.log(location); console.log(location);
}); });
} }
if (err.indexOf('The archive file is missing on some hosts') !== -1) { if (err.indexOf('The archive file is missing on some hosts') !== -1) {
var results = archiveErrorPattern.exec(err); results = archiveErrorPattern.exec(err);
var location = results[1].replace(' ', '\\ '); location = results[1].replace(' ', '\\ ');
var fullLocation = path.join(Util.getHomePath(), 'Library/Application\\ Support/Unison', location); var fullLocation = path.join(Util.getHomePath(), 'Library/Application\\ Support/Unison', location);
var cmd = '/bin/rm -rf ' + fullLocation; var cmd = '/bin/rm -rf ' + fullLocation;
exec(cmd, function () {}); exec(cmd, function () {});

View File

@ -1,5 +1,8 @@
var path = require('path'); var path = require('path');
var fs = require('fs'); var fs = require('fs');
var nodeCrypto = require('crypto');
var request = require('request');
var progress = require('request-progress');
Util = {}; Util = {};
@ -8,15 +11,11 @@ Util.getHomePath = function () {
}; };
Util.getBinDir = function () { Util.getBinDir = function () {
if (process.env.NODE_ENV === 'development') { return path.join(process.env.DIR, 'resources');
return path.join(path.join(process.env.PWD, '..'), 'resources'); };
} else {
if (Meteor.isClient) { Util.getResourceDir = function () {
return path.join(process.cwd(), 'resources'); return path.join(Util.getHomePath(), 'Library/Application\ Support/Kitematic/Resources');
} else {
return path.join(process.cwd(), '../../../resources');
}
}
}; };
Util.KITE_PATH = path.join(Util.getHomePath(), 'Kitematic'); Util.KITE_PATH = path.join(Util.getHomePath(), 'Kitematic');
@ -78,6 +77,47 @@ Util.hasDockerfile = function (directory) {
return fs.existsSync(path.join(directory, 'Dockerfile')); 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"). * 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) { trackLink = function (trackLabel) {
if (trackLabel) { var install = Installs.findOne({});
console.log(trackLabel); if (install && install.tracking) {
ga('send', 'event', 'link', 'click', trackLabel); 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.REQUIRED_VERSION = '4.3.14';
VirtualBox.INCLUDED_VERSION = '4.3.14'; VirtualBox.INCLUDED_VERSION = '4.3.14';
VirtualBox.INSTALLER_FILENAME = 'virtualbox-4.3.14.pkg'; VirtualBox.INSTALLER_FILENAME = 'virtualbox-4.3.14.pkg';
VirtualBox.INSTALLER_CHECKSUM = '486348a5336539728ca20dcd9674cf3d37e5c7f32255d90f1edc7391b54bd5dd'; // Sha256 Checksum
// Info for the hostonly interface we add to the VM. // Info for the hostonly interface we add to the VM.
VirtualBox.HOSTONLY_HOSTIP = '192.168.60.3'; VirtualBox.HOSTONLY_HOSTIP = '192.168.60.3';
@ -24,7 +25,7 @@ VirtualBox.exec = function (command, callback) {
VirtualBox.install = function (callback) { VirtualBox.install = function (callback) {
// -W waits for the process to close before finishing. // -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(stdout);
console.log(stderr); console.log(stderr);
if (error) { if (error) {

View File

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

View File

@ -9,3 +9,4 @@
@import "stylesheets/dashboard.import.less"; @import "stylesheets/dashboard.import.less";
@import "stylesheets/setup.import.less"; @import "stylesheets/setup.import.less";
@import "stylesheets/spinner.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; 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 { .install-continue {
-webkit-app-region: no-drag;
margin-top: 140px; margin-top: 140px;
p { p {
margin-top: 0; margin-top: 0;

View File

@ -10,6 +10,7 @@ html, body {
font-size: @font-size-base; font-size: @font-size-base;
height: @window-height; height: @window-height;
.no-select(); .no-select();
overflow: hidden;
} }
html, body, div, span, object, 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 getConfigVars = function ($form) {
var configVars = {}; var configVars = {};
$form.find('.env-var-pair').each(function () { $form.find('.env-var-pair').each(function () {
@ -39,10 +42,15 @@ Template.dashboard_apps_settings.events({
return false; return false;
}, },
'click .btn-delete-app': function () { 'click .btn-delete-app': function () {
var result = confirm("Are you sure you want to delete this app?"); var appId = this._id;
if (result === true) { dialog.showMessageBox({
AppUtil.remove(this._id); message: 'Are you sure you want to delete this app?',
Router.go('dashboard_apps'); 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> <small><a onclick="trackLink('app image detail')" href="/images/{{image._id}}">{{image.meta.name}}</a></small>
</h5> </h5>
<div class="options"> <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'}} {{#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}} {{#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> <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}} {{/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> <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}} {{/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> <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="#" 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}}/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> <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 () { Template.dashboard_single_app.rendered = function () {
Meteor.setInterval(function () { Meteor.setInterval(function () {
$('.btn-icon').tooltip(); $('.btn-icon').tooltip();
@ -21,16 +19,8 @@ Template.dashboard_single_app.events({
}, },
'click .btn-terminal': function () { 'click .btn-terminal': function () {
var app = this; var app = this;
var cmd = path.join(Util.getBinDir(), 'boot2docker') + ' ssh -t "sudo docker-enter ' + app.docker.Id + '"'; var cmd = Boot2Docker.command() + ' ssh -t "sudo docker-enter ' + app.docker.Id + '"';
var terminalCmd = path.join(Util.getBinDir(), 'terminal') + ' ' + cmd; Util.openTerminal(cmd);
var exec = require('child_process').exec;
console.log(terminalCmd);
exec(terminalCmd, function (err, stdout) {
console.log(stdout);
if (err) {
console.log(err);
}
});
}, },
'click .btn-start': function () { 'click .btn-start': function () {
AppUtil.start(this._id); AppUtil.start(this._id);

View File

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

View File

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

View File

@ -12,7 +12,6 @@
<h5 id="picked-directory"></h5> <h5 id="picked-directory"></h5>
<h6 id="picked-directory-error" class="error"></h6> <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> <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> </div>
<div class="modal-footer"> <div class="modal-footer">

View File

@ -1,5 +1,7 @@
var path = require('path'); var path = require('path');
var fs = require('fs'); var fs = require('fs');
var remote = require('remote');
var dialog = remote.require('dialog');
Template.modal_create_image.rendered = function () { Template.modal_create_image.rendered = function () {
$('#modal-create-image').bind('hidden.bs.modal', function () { $('#modal-create-image').bind('hidden.bs.modal', function () {
@ -9,27 +11,31 @@ Template.modal_create_image.rendered = function () {
Template.modal_create_image.events({ Template.modal_create_image.events({
'click #btn-pick-directory': function () { 'click #btn-pick-directory': function () {
$('#directory-picker').click(); dialog.showOpenDialog({properties: ['openDirectory']}, function (filenames) {
}, if (!filenames) {
'change #directory-picker': function (e) { return;
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');
} }
} else { var directory = filenames[0];
$('#picked-directory').html(''); if (directory) {
$('#btn-create-image').attr('disabled', 'disabled'); $('#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 () { 'click #btn-create-image': function () {
var directory = $('#directory-picker').val(); var directory = Session.get('createImagePickedDirectory');
if (!directory) {
return;
}
$('#directory-picker').val(''); $('#directory-picker').val('');
$('#picked-directory-error').html(''); $('#picked-directory-error').html('');
$('#picked-directory').html(''); $('#picked-directory').html('');

View File

@ -62,7 +62,6 @@
<h5><strong>{{this.originPath}}</strong></h5> <h5><strong>{{this.originPath}}</strong></h5>
<h6 id="picked-directory-error" class="error"></h6> <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> <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> </div>
<div class="section"> <div class="section">

View File

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

View File

@ -24,20 +24,8 @@ Template.dashboard_apps_layout.events({
AppUtil.logs(this._id); AppUtil.logs(this._id);
}, },
'click .btn-terminal': function () { 'click .btn-terminal': function () {
var buildCmd = function (dockerId, termApp) { var cmd = Boot2Docker.command() + ' ssh -t "sudo docker-enter ' + this.docker.Id + '"';
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"; Util.openTerminal(cmd);
};
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; }
});
}
});
}, },
'click .btn-restart': function () { 'click .btn-restart': function () {
AppUtil.restart(this._id); AppUtil.restart(this._id);
@ -47,5 +35,13 @@ Template.dashboard_apps_layout.events({
exec('open ' + this.path, function (err) { exec('open ' + this.path, function (err) {
if (err) { throw 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}} {{/if}}
</div> </div>
</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> </template>

View File

@ -20,6 +20,28 @@ Template.dashboard_settings.events({
console.log(err); 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"> <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="container text-left">
<div class="steps"> <div class="steps">
{{#each steps}} {{#each steps}}
@ -24,7 +31,11 @@
{{#if $eq this.index failedStep}} {{#if $eq this.index failedStep}}
<img src="/step_failed.png"> <img src="/step_failed.png">
{{else}} {{else}}
{{> spinner}} {{#if currentInstallStepProgress}}
{{> radial_progress progress=currentInstallStepProgress}}
{{else}}
{{> spinner}}
{{/if}}
{{/if}} {{/if}}
</span> </span>
<div class="media-body"> <div class="media-body">

View File

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

View File

@ -1,4 +1,11 @@
<template name="setup_intro"> <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="container text-center">
<div class="install_logo"> <div class="install_logo">
<img src="/install_logo.png"> <img src="/install_logo.png">

View File

@ -1,13 +1,6 @@
<template name="setup_layout"> <template name="setup_layout">
{{setTitle}} {{setTitle}}
<div class="setup content text-center"> <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}} {{> yield}}
</div> </div>
</template> </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 /bootstrap3-less
/iron-router /iron-router
/npm
/blaze-layout /blaze-layout
/headers /headers
/inject-initial /inject-initial
/handlebar-helpers /handlebar-helpers
/server-deps /server-deps
/collection2
/simple-schema
/collection-hooks /collection-hooks
/moment /moment
/underscore-string-latest /underscore-string-latest
/blocking /blocking
/collection-helpers /collection-helpers
/octicons
/fast-render /fast-render
/iron-layout /iron-layout
/iron-core /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 () { Meteor.publish('installs', function () {
return Installs.find({}); return Installs.find();
});
Meteor.publish('settings', function () {
return Settings.find();
}); });

View File

@ -1,7 +1,7 @@
{ {
"public": { "public": {
"ga": { "ga": {
"id": "UA-53012639-2", "id": "UA-54515442-1",
"create": { "create": {
"cookieDomain": "none" "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", "name": "Kitematic",
"main": "index.html", "main": "index.js",
"node-remote": "<local>", "version": "0.2.0",
"version": "0.1.0",
"window": {
"show": false,
"toolbar": false,
"frame": false,
"width": 800,
"height": 600,
"resizable": false
},
"engines": {
"node": "0.11.13"
},
"dependencies": { "dependencies": {
"async": "^0.9.0", "async": "^0.9.0",
"chokidar": "git+https://github.com/usekite/chokidar.git", "chokidar": "git+https://github.com/usekite/chokidar.git",
@ -22,6 +10,8 @@
"open": "0.0.5", "open": "0.0.5",
"dockerode": "2.0.3", "dockerode": "2.0.3",
"tar": "0.1.20", "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") DIR=$(dirname "$0")
USER=`w -h | sort -u -t' ' -k1,1 | awk '{print $1}'` 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\"?> 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\"> <!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">
<plist version=\"1.0\"> <plist version=\"1.0\">
@ -23,7 +23,7 @@ echo "<?xml version=\"1.0\" encoding=\"UTF-8\"?>
<array> <array>
<string>bash</string> <string>bash</string>
<string>-c</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> </array>
<key>KeepAlive</key> <key>KeepAlive</key>
<false/> <false/>
@ -34,6 +34,6 @@ echo "<?xml version=\"1.0\" encoding=\"UTF-8\"?>
</dict> </dict>
</plist>" > /Library/LaunchAgents/com.kitematic.route.plist </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 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 /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 )" DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
source $DIR/colors.sh source $DIR/colors.sh
source $DIR/versions.sh
BASE=$DIR/.. 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 pushd $BASE
if [ ! -d bundle ]; then rm -rf dist/osx
cecho "No bundle, run script/bundle.sh first." $red
exit 1
fi
rm -rf dist/osx/Kitematic.app
rm -rf dist/osx/Kitematic.zip
mkdir -p dist/osx/ mkdir -p dist/osx/
cecho "-----> Creating Kitematic.app..." $blue DIST_APP=Kitematic.app
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
cecho "-----> Copying meteor bundle into Kitematic.app..." $blue cecho "-----> Creating $DIST_APP..." $blue
cp -R bundle dist/osx/Kitematic.app/Contents/Resources/app.nw/ 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 cecho "-----> Copying meteor bundle into $DIST_APP..." $blue
cp index.html dist/osx/Kitematic.app/Contents/Resources/app.nw/ mv bundle dist/osx/$DIST_APP/Contents/Resources/app/
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/
$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 cecho "-----> Copying binary files to $DIST_APP" $blue
mkdir -p dist/osx/Kitematic.app/Contents/Resources/app.nw/resources mkdir -p dist/osx/$DIST_APP/Contents/Resources/app/resources
cp -v resources/* dist/osx/Kitematic.app/Contents/Resources/app.nw/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 cecho "-----> Copying icon to $DIST_APP" $blue
chmod +x dist/osx/Kitematic.app/Contents/Resources/app.nw/resources/$COCOASUDO_FILE cp kitematic.icns dist/osx/$DIST_APP/Contents/Resources/atom.icns
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/$DIST_APP/Contents/Resources/app/resources/$BOOT2DOCKER_CLI_FILE
chmod +x dist/osx/Kitematic.app/Contents/Resources/app.nw/resources/unison chmod +x dist/osx/$DIST_APP/Contents/Resources/app/resources/$COCOASUDO_FILE
chmod +x dist/osx/Kitematic.app/Contents/Resources/app.nw/resources/node 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 if [ -f $DIR/sign.sh ]; then
cecho "-----> Signing app file...." $blue cecho "-----> Signing app file...." $blue
$DIR/sign.sh $BASE/dist/osx/Kitematic.app $DIR/sign.sh $BASE/dist/osx/$DIST_APP
fi fi
pushd dist/osx pushd dist/osx
cecho "-----> Creating disributable zip file...." $blue 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 popd
cecho "Done." $green 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 cecho "Kitematic zip distribution available at dist/osx/Kitematic.zip" $green
popd popd

View File

@ -3,14 +3,14 @@
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
BASE=$DIR/.. BASE=$DIR/..
source $BASE/script/setup.sh
export ROOT_URL=https://localhost:3000 export ROOT_URL=https://localhost:3000
export DOCKER_HOST=http://192.168.59.103 export DOCKER_HOST=http://192.168.59.103
export DOCKER_PORT=2375 export DOCKER_PORT=2375
export DIR=$BASE
#export METEOR_SETTINGS=`cat $BASE/meteor/settings_dev.json`
#echo $METEOR_SETTINGS
cd $BASE/meteor 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 & 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 )" DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
source $DIR/colors.sh source $DIR/colors.sh
source $DIR/versions.sh
BASE=$DIR/.. BASE=$DIR/..
pushd $BASE pushd $BASE
@ -12,15 +11,18 @@ mkdir -p cache
pushd cache pushd cache
if [ ! -f $BASE_IMAGE_VERSION_FILE ]; then BOOT2DOCKER_CLI_VERSION=1.2.0
cecho "-----> Downloading Kitematic base images..." $purple BOOT2DOCKER_CLI_VERSION_FILE=boot2docker-$BOOT2DOCKER_CLI_VERSION
curl -L --progress-bar -o $BASE_IMAGE_VERSION_FILE https://s3.amazonaws.com/kite-installer/$BASE_IMAGE_VERSION_FILE BOOT2DOCKER_CLI_FILE=boot2docker
cp $BASE_IMAGE_VERSION_FILE ../resources/$BASE_IMAGE_FILE
fi
if [ ! -f $BOOT2DOCKER_CLI_VERSION_FILE ]; then ATOM_SHELL_VERSION=0.16.2
cecho "-----> Downloading Boot2docker CLI..." $purple ATOM_SHELL_FILE=atom-shell-v$ATOM_SHELL_VERSION-darwin-x64.zip
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
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 fi
if [ ! -f kite-node-webkit.tar.gz ]; then if [ ! -f kite-node-webkit.tar.gz ]; then
@ -50,23 +52,20 @@ popd
pushd resources pushd resources
if [ ! -f $VIRTUALBOX_FILE ]; then if [ ! -f $BOOT2DOCKER_CLI_VERSION_FILE ]; then
cecho "-----> Downloading virtualbox installer..." $purple cecho "-----> Downloading Boot2docker CLI..." $purple
curl -L --progress-bar -o $VIRTUALBOX_FILE https://s3.amazonaws.com/kite-installer/$VIRTUALBOX_FILE curl -L -o $BOOT2DOCKER_CLI_VERSION_FILE https://s3.amazonaws.com/kite-installer/boot2docker-v$BOOT2DOCKER_CLI_VERSION
fi fi
if [ ! -f $COCOASUDO_FILE ]; then chmod +x $BOOT2DOCKER_CLI_VERSION_FILE
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
popd popd
NPM="$BASE/cache/node/bin/npm" 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 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