Merge branch 'windows' of github.com:kitematic/kitematic into windows

This commit is contained in:
Jeffrey Morgan 2015-06-17 10:29:29 -07:00
commit 81047a0df6
24 changed files with 474 additions and 115 deletions

View File

@ -76,11 +76,38 @@ module.exports = function (grunt) {
}
},
rcedit: {
exes: {
files: [{
expand: true,
cwd: 'dist/Kitematic-win32',
src: ['Kitematic.exe']
}],
options: {
icon: 'util/kitematic.ico',
'file-version': packagejson.version,
'product-version': packagejson.version,
'version-string': {
'CompanyName': 'Docker, Inc',
'ProductVersion': packagejson.version,
'ProductName': 'Kitematic',
'FileDescription': 'Kitematic',
'InternalName': 'Kitematic.exe',
'OriginalFilename': 'Kitematic.exe',
'LegalCopyright': 'Copyright 2015 Docker Inc. All rights reserved.'
}
}
}
},
'create-windows-installer': {
appDirectory: 'dist/Kitematic-win32/',
authors: 'Docker Inc.',
loadingGif: 'util/loading.gif',
setupIcon: 'util/kitematic.ico'
setupIcon: 'util/kitematic.ico',
description: 'Kitematic',
title: 'Kitematic',
version: packagejson.version
},
// docker binaries
@ -126,7 +153,7 @@ module.exports = function (grunt) {
files: [{
expand: true,
cwd: 'resources',
src: ['docker*'],
src: ['docker*', 'boot2docker.iso', 'ssh.exe', 'OPENSSH_LICENSE', 'msys-*'],
dest: 'dist/Kitematic-win32/resources/resources/'
}],
options: {
@ -137,7 +164,7 @@ module.exports = function (grunt) {
files: [{
expand: true,
cwd: 'resources',
src: ['**/*'],
src: ['docker*', 'boot2docker.iso', 'macsudo', 'terminal'],
dest: '<%= OSX_FILENAME %>/Contents/Resources/resources/'
}, {
src: 'util/kitematic.icns',
@ -152,7 +179,7 @@ module.exports = function (grunt) {
rename: {
installer: {
src: 'installer/Setup.exe',
dest: 'installer/KitematicSetup.exe'
dest: 'installer/KitematicSetup-' + packagejson.version + '.exe'
}
},
@ -258,7 +285,7 @@ module.exports = function (grunt) {
},
less: {
files: ['styles/**/*.less'],
tasks: ['newer:less']
tasks: ['less']
},
copy: {
files: ['images/*', 'index.html', 'fonts/*'],
@ -266,10 +293,10 @@ module.exports = function (grunt) {
}
}
});
grunt.registerTask('default', ['download-binary', 'newer:babel', 'newer:less', 'newer:copy:dev', 'shell:electron', 'watchChokidar']);
grunt.registerTask('default', ['download-binary', 'newer:babel', 'less', 'newer:copy:dev', 'shell:electron', 'watchChokidar']);
if (process.platform === 'win32') {
grunt.registerTask('release', ['clean', 'download-binary', 'babel', 'less', 'copy:dev', 'electron:windows', 'copy:windows', 'create-windows-installer', 'rename:installer']);
grunt.registerTask('release', ['clean', 'download-binary', 'babel', 'less', 'copy:dev', 'electron:windows', 'copy:windows', 'rcedit:exes', 'create-windows-installer', 'rename:installer']);
} else {
grunt.registerTask('release', ['clean:dist', 'clean:build', 'download-binary', 'babel', 'less', 'copy:dev', 'electron:osx', 'copy:osx', 'shell:sign', 'shell:zip']);
}

View File

@ -1,6 +1,6 @@
{
"name": "Kitematic",
"version": "0.6.6",
"version": "0.6.7",
"author": "Kitematic",
"description": "Simple Docker Container management for Mac OS X.",
"homepage": "https://kitematic.com/",
@ -80,6 +80,7 @@
"grunt-electron": "^1.0.0",
"grunt-electron-installer": "^0.33.0",
"grunt-newer": "^1.1.1",
"grunt-rcedit": "^0.3.1",
"grunt-rename": "^0.1.4",
"grunt-shell": "^1.1.2",
"grunt-shell-spawn": "^0.3.8",

20
resources/MSYS_LICENSE Normal file
View File

@ -0,0 +1,20 @@
Kitematic includes (but does not link to) various DLLs included with the msysgit Git-1.9.5-preview20150319 distribution. Included is the MSYS runtime license.
Source is available online at https://github.com/msysgit/git/tree/v1.9.5.msysgit.1
File: MSYS_LICENSE
Copyright (C): 2001, Earnie Boyd <earnie@users.sf.net>
File $Revision$
File Revision $Date$
MSYS Release: 1.0.2
MSYS Release Date: November 30th, 2001
The software, both source and binary forms, are covered via differing licenses.
Each license has it's own set of rules so please make sure you read them
carefully to see how it applies to you, particularly if you're going to
distribute the software.
The MSYS runtime software source can found in the winsup/cygwin directory. The
existing code portions of this source is covered by the CYGWIN_LICENSE which can
be found in this directory in a file by the name of CYGWIN_LICENSE. MSYS
specific software code added regardless of existing license is covered by the
ESPL which can be found in a file by the same name.

205
resources/OPENSSH_LICENSE Normal file
View File

@ -0,0 +1,205 @@
Kitematic includes OpenSSH ssh.exe from the msysgit distribution version Git-1.9.5-preview20150319, available online at https://github.com/msysgit/git/tree/v1.9.5.msysgit.1
This file is part of the OpenSSH software.
The licences which components of this software fall under are as
follows. First, we will summarize and say that all components
are under a BSD licence, or a licence more free than that.
OpenSSH contains no GPL code.
1)
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
* All rights reserved
*
* As far as I am concerned, the code I have written for this software
* can be used freely for any purpose. Any derived versions of this
* software must be clearly marked as such, and if the derived work is
* incompatible with the protocol description in the RFC file, it must be
* called by a name other than "ssh" or "Secure Shell".
[Tatu continues]
* However, I am not implying to give any licenses to any patents or
* copyrights held by third parties, and the software includes parts that
* are not under my direct control. As far as I know, all included
* source code is used in accordance with the relevant license agreements
* and can be used freely for any purpose (the GNU license being the most
* restrictive); see below for details.
[However, none of that term is relevant at this point in time. All of
these restrictively licenced software components which he talks about
have been removed from OpenSSH, i.e.,
- RSA is no longer included, found in the OpenSSL library
- IDEA is no longer included, its use is deprecated
- DES is now external, in the OpenSSL library
- GMP is no longer used, and instead we call BN code from OpenSSL
- Zlib is now external, in a library
- The make-ssh-known-hosts script is no longer included
- TSS has been removed
- MD5 is now external, in the OpenSSL library
- RC4 support has been replaced with ARC4 support from OpenSSL
- Blowfish is now external, in the OpenSSL library
[The licence continues]
Note that any information and cryptographic algorithms used in this
software are publicly available on the Internet and at any major
bookstore, scientific library, and patent office worldwide. More
information can be found e.g. at "http://www.cs.hut.fi/crypto".
The legal status of this program is some combination of all these
permissions and restrictions. Use only at your own responsibility.
You will be responsible for any legal consequences yourself; I am not
making any claims whether possessing or using this is legal or not in
your country, and I am not taking any responsibility on your behalf.
NO WARRANTY
BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
2)
The 32-bit CRC compensation attack detector in deattack.c was
contributed by CORE SDI S.A. under a BSD-style license.
* Cryptographic attack detector for ssh - source code
*
* Copyright (c) 1998 CORE SDI S.A., Buenos Aires, Argentina.
*
* All rights reserved. Redistribution and use in source and binary
* forms, with or without modification, are permitted provided that
* this copyright notice is retained.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES ARE DISCLAIMED. IN NO EVENT SHALL CORE SDI S.A. BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY OR
* CONSEQUENTIAL DAMAGES RESULTING FROM THE USE OR MISUSE OF THIS
* SOFTWARE.
*
* Ariel Futoransky <futo@core-sdi.com>
* <http://www.core-sdi.com>
3)
ssh-keyscan was contributed by David Mazieres under a BSD-style
license.
* Copyright 1995, 1996 by David Mazieres <dm@lcs.mit.edu>.
*
* Modification and redistribution in source and binary forms is
* permitted provided that due credit is given to the author and the
* OpenBSD project by leaving this copyright notice intact.
4)
The Rijndael implementation by Vincent Rijmen, Antoon Bosselaers
and Paulo Barreto is in the public domain and distributed
with the following license:
* @version 3.0 (December 2000)
*
* Optimised ANSI C code for the Rijndael cipher (now AES)
*
* @author Vincent Rijmen <vincent.rijmen@esat.kuleuven.ac.be>
* @author Antoon Bosselaers <antoon.bosselaers@esat.kuleuven.ac.be>
* @author Paulo Barreto <paulo.barreto@terra.com.br>
*
* This code is hereby placed in the public domain.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
5)
One component of the ssh source code is under a 3-clause BSD license,
held by the University of California, since we pulled these parts from
original Berkeley code.
* Copyright (c) 1983, 1990, 1992, 1993, 1995
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
6)
Remaining components of the software are provided under a standard
2-term BSD licence with the following names as copyright holders:
Markus Friedl
Theo de Raadt
Niels Provos
Dug Song
Aaron Campbell
Damien Miller
Kevin Steves
Daniel Kouril
Wesley Griffin
Per Allansson
Nils Nordman
Simon Wilkinson
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

BIN
resources/msys-1.0.dll Normal file

Binary file not shown.

Binary file not shown.

BIN
resources/msys-minires.dll Normal file

Binary file not shown.

BIN
resources/msys-z.dll Normal file

Binary file not shown.

BIN
resources/ssh.exe Normal file

Binary file not shown.

View File

@ -28,9 +28,9 @@ class ContainerActions {
dockerUtil.restart(name);
}
update (name, containerOpts) {
this.dispatch({name, containerOpts});
dockerUtil.updateContainer(name, containerOpts);
update (name, container) {
this.dispatch({name, container});
dockerUtil.updateContainer(name, container);
}
clearPending () {

View File

@ -2,8 +2,10 @@ var app = require('app');
var autoUpdater = require('auto-updater');
var BrowserWindow = require('browser-window');
var fs = require('fs');
var os = require('os');
var ipc = require('ipc');
var path = require('path');
var child_process = require('child_process');
process.env.NODE_PATH = path.join(__dirname, 'node_modules');
process.env.RESOURCES_PATH = path.join(__dirname, '/../resources');
@ -17,45 +19,28 @@ try {
settingsjson = JSON.parse(fs.readFileSync(path.join(__dirname, 'settings.json'), 'utf8'));
} catch (err) {}
var handleStartupEvent = function() {
if (process.platform !== 'win32') {
return false;
}
let updateCmd = (args, cb) => {
let updateExe = path.resolve(path.dirname(process.execPath), '..', 'Update.exe');
let child = child_process.spawn(updateExe, args, {detached: true});
child.on('close', cb);
};
if (process.platform === 'win32') {
var squirrelCommand = process.argv[1];
let target = path.basename(process.execPath);
switch (squirrelCommand) {
case '--squirrel-install':
case '--squirrel-updated':
// Optionally do things such as:
//
// - Install desktop and start menu shortcuts
// - Add your .exe to the PATH
// - Write to the registry for things like file associations and
// explorer context menus
// Always quit when done
app.quit();
return true;
updateCmd(['--createShortcut', target], app.quit);
break;
case '--squirrel-uninstall':
// Undo anything you did in the --squirrel-install and
// --squirrel-updated handlers
// Always quit when done
app.quit();
return true;
updateCmd(['--removeShortcut', target], app.quit);
break;
case '--squirrel-obsolete':
// This is called on the outgoing version of your app before
// we update to the new version - it's the opposite of
// --squirrel-updated
app.quit();
return true;
break;
}
};
handleStartupEvent();
}
var openURL = null;
app.on('open-url', function (event, url) {
@ -72,7 +57,7 @@ app.on('ready', function () {
'standard-window': false,
resizable: true,
frame: false,
show: false,
show: false
});
mainWindow.loadUrl(path.normalize('file://' + path.join(__dirname, 'index.html')));
@ -90,12 +75,21 @@ app.on('ready', function () {
autoUpdater.quitAndInstall();
});
app.on('before-quit', function () {
// TODO: make this work for right click + close
if (!updating && mainWindow.webContents) {
if (os.platform() === 'win32') {
mainWindow.on('close', function () {
mainWindow.webContents.send('application:quitting');
}
});
return true;
});
app.on('window-all-closed', function() {
app.quit();
});
} else if (os.platform() === 'darwin') {
app.on('before-quit', function () {
if (!updating) {
mainWindow.webContents.send('application:quitting');
}
});
}
mainWindow.webContents.on('new-window', function (e) {
e.preventDefault();
@ -125,7 +119,7 @@ app.on('ready', function () {
});
if (process.env.NODE_ENV !== 'development') {
autoUpdater.setFeedUrl('https://updates.kitematic.com/releases/latest?version=' + app.getVersion() + '&beta=' + !!settingsjson.beta);
autoUpdater.setFeedUrl('https://updates.kitematic.com/releases/latest?version=' + app.getVersion() + '&beta=' + !!settingsjson.beta + '&platform=' + os.platform());
}
});

View File

@ -18,18 +18,18 @@ var ContainerHomeFolder = React.createClass({
from: 'home'
});
if (hostVolume.indexOf(process.env.HOME) === -1) {
if (hostVolume.indexOf(util.windowsToLinuxPath(util.home())) === -1) {
dialog.showMessageBox({
message: 'Enable all volumes to edit files via Finder? This may not work with all database containers.',
buttons: ['Enable Volumes', 'Cancel']
}, (index) => {
if (index === 0) {
var volumes = _.clone(this.props.container.Volumes);
var newHostVolume = path.join(util.home(), util.documents(), 'Kitematic', this.props.container.Name, containerVolume);
var newHostVolume = util.escapePath(path.join(util.home(), util.documents(), 'Kitematic', this.props.container.Name, containerVolume));
volumes[containerVolume] = newHostVolume;
var binds = _.pairs(volumes).map(function (pair) {
if(util.isWindows()) {
return util.windowsToLinuxPath(pair[1]) + ':' + pair[0];
return util.windowsToLinuxPath(pair[1]) + ':' + pair[0];
}
return pair[1] + ':' + pair[0];
});
@ -44,7 +44,8 @@ var ContainerHomeFolder = React.createClass({
}
});
} else {
shell.showItemInFolder(hostVolume);
let path = util.isWindows() ? util.linuxToWindowsPath(hostVolume) : hostVolume;
shell.showItemInFolder(path);
}
},
handleClickChangeFolders: function () {

View File

@ -92,7 +92,7 @@ var ContainerHomePreview = React.createClass({
<thead>
<tr>
<th>DOCKER PORT</th>
<th>MAC PORT</th>
<th>ACCESS URL</th>
</tr>
</thead>
<tbody>

View File

@ -46,7 +46,7 @@ var ContainerSettingsPorts = React.createClass({
},
render: function () {
if (!this.props.container) {
return (<div></div>);
return false;
}
var ports = _.map(_.pairs(this.state.ports), pair => {
var key = pair[0];
@ -66,7 +66,7 @@ var ContainerSettingsPorts = React.createClass({
<thead>
<tr>
<th>DOCKER PORT</th>
<th>MAC PORT</th>
<th>ACCESS URL</th>
</tr>
</thead>
<tbody>

View File

@ -9,57 +9,71 @@ var containerActions = require('../actions/ContainerActions');
var ContainerSettingsVolumes = React.createClass({
handleChooseVolumeClick: function (dockerVol) {
var self = this;
dialog.showOpenDialog({properties: ['openDirectory', 'createDirectory']}, (filenames) => {
if (!filenames) {
return;
}
var directory = filenames[0];
if (directory) {
metrics.track('Choose Directory for Volume');
if(util.isWindows()) {
directory = util.windowsToLinuxPath(directory);
}
var volumes = _.clone(self.props.container.Volumes);
volumes[dockerVol] = directory;
var binds = _.pairs(volumes).map(function (pair) {
return pair[1] + ':' + pair[0];
});
containerActions.update(this.props.container.Name, {Binds: binds, Volumes: volumes});
var directory = filenames[0];
if (!directory || directory.indexOf(util.home()) === -1) {
dialog.showMessageBox({
type: 'warning',
buttons: ['OK'],
message: 'Invalid directory. Volume directories must be under your Users directory'
});
return;
}
metrics.track('Choose Directory for Volume');
if(util.isWindows()) {
directory = util.escapePath(util.windowsToLinuxPath(directory));
}
var volumes = _.clone(this.props.container.Volumes);
volumes[dockerVol] = directory;
var binds = _.pairs(volumes).map(function (pair) {
return pair[1] + ':' + pair[0];
});
containerActions.update(this.props.container.Name, {Binds: binds, Volumes: volumes});
});
},
handleRemoveVolumeClick: function (dockerVol) {
metrics.track('Removed Volume Directory', {
from: 'settings'
});
var hostConfig = _.clone(this.props.container.HostConfig);
var binds = hostConfig.Binds;
var volumes = _.clone(this.props.container.Volumes);
delete volumes[dockerVol];
var binds = _.pairs(volumes).map(function (pair) {
return pair[1] + ':' + pair[0];
});
containerActions.update(this.props.container.Name, {Binds: binds, Volumes: volumes});
volumes[dockerVol] = null;
var index = _.findIndex(binds, bind => bind.indexOf(`:${dockerVol}`) !== -1);
if (index >= 0) {
binds.splice(index, 1);
}
containerActions.update(this.props.container.Name, {HostConfig: hostConfig, Binds: binds, Volumes: volumes});
},
handleOpenVolumeClick: function (path) {
metrics.track('Opened Volume Directory', {
from: 'settings'
});
shell.showItemInFolder(path);
shell.showItemInFolder(util.linuxToWindowsPath(path));
},
render: function () {
if (!this.props.container) {
return false;
}
var homeDir = util.isWindows() ? util.windowsToLinuxPath(util.home()) : util.home();
var volumes = _.map(this.props.container.Volumes, (val, key) => {
if (!val || val.indexOf(process.env.HOME) === -1) {
if (!val || val.indexOf(homeDir) === -1) {
val = (
<span className="value-right">No Folder</span>
);
} else {
let local = util.isWindows() ? util.linuxToWindowsPath(val) : val;
val = (
<a className="value-right" onClick={this.handleOpenVolumeClick.bind(this, val)}>{val.replace(process.env.HOME, '~')}</a>
<a className="value-right" onClick={this.handleOpenVolumeClick.bind(this, val)}>{local.replace(process.env.HOME, '~')}</a>
);
}
return (
@ -81,7 +95,7 @@ var ContainerSettingsVolumes = React.createClass({
<thead>
<tr>
<th>DOCKER FOLDER</th>
<th>MAC FOLDER</th>
<th>LOCAL FOLDER</th>
<th></th>
</tr>
</thead>

View File

@ -4,6 +4,7 @@ var RetinaImage = require('react-retina-image');
var remote = require('remote');
var ipc = require('ipc');
var autoUpdater = remote.require('auto-updater');
var util = require('../utils/Util');
var metrics = require('../utils/MetricsUtil');
var Menu = remote.require('menu');
var MenuItem = remote.require('menu-item');
@ -52,7 +53,11 @@ var Header = React.createClass({
}
},
handleClose: function () {
remote.getCurrentWindow().hide();
if (util.isWindows()) {
remote.getCurrentWindow().close();
} else {
remote.getCurrentWindow().hide();
}
},
handleMinimize: function () {
remote.getCurrentWindow().minimize();
@ -94,22 +99,29 @@ var Header = React.createClass({
});
accountActions.verify();
},
renderLogo: function () {
return (
<div className="logo">
<RetinaImage src="logo.png"/>
</div>
);
},
renderWindowButtons: function () {
let buttons;
if (this.state.fullscreen) {
if (util.isWindows()) {
buttons = (
<div className="buttons">
<div className="button button-close disabled"></div>
<div className="button button-minimize disabled"></div>
<div className="button button-fullscreenclose enabled" onClick={this.handleFullscreen}></div>
<div className="windows-buttons">
<div className="windows-button button-minimize enabled" onClick={this.handleMinimize}><div className="icon"></div></div>
<div className={`windows-button ${this.state.fullscreen ? 'button-fullscreenclose' : 'button-fullscreen'} enabled`} onClick={this.handleFullscreen}><div className="icon"></div></div>
<div className="windows-button button-close enabled" onClick={this.handleClose}></div>
</div>
);
} else {
buttons = (
<div className="buttons">
<div className="button button-close enabled" onClick={this.handleClose}></div>
<div className="button button-minimize enabled" onClick={this.handleMinimize}></div>
<div className="button button-fullscreen enabled" onClick={this.handleFullscreen}></div>
<div className="button button-close enabled" onClick={this.handleClose}></div>
<div className="button button-minimize enabled" onClick={this.handleMinimize}></div>
<div className="button button-fullscreen enabled" onClick={this.handleFullscreen}></div>
</div>
);
}
@ -150,16 +162,14 @@ var Header = React.createClass({
return (
<div className={headerClasses}>
<div className="left-header">
{this.renderWindowButtons()}
{util.isWindows () ? this.renderLogo() : this.renderWindowButtons()}
{username}
</div>
<div className="right-header">
<div className="updates">
{updateWidget}
</div>
<div className="logo">
<RetinaImage src="logo.png"/>
</div>
{util.isWindows () ? this.renderWindowButtons() : this.renderLogo()}
</div>
</div>
);
@ -173,9 +183,10 @@ var Header = React.createClass({
return (
<div className={headerClasses}>
<div className="left-header">
{this.renderWindowButtons()}
{util.isWindows () ? null : this.renderWindowButtons()}
</div>
<div className="right-header">
{util.isWindows () ? this.renderWindowButtons() : null}
</div>
</div>
);

View File

@ -74,7 +74,7 @@ class ContainerStore {
return;
}
deepExtend(containers[name], container);
_.extend(containers[name], container);
if (containers[name].State) {
containers[name].State.Updating = true;
@ -89,7 +89,8 @@ class ContainerStore {
return;
}
// Trigger log update
LogStore.fetch(container.Name);
// TODO: fix this loading multiple times
// LogStore.fetch(container.Name);
containers[container.Name] = container;

View File

@ -49,7 +49,7 @@ var _steps = [{
} catch (err) {
throw null;
}
} else if (util.isWindows() && !virtualBox.active()) {
} else if (!util.isWindows() && !virtualBox.active()) {
yield util.exec(setupUtil.macSudoCmd(util.escapePath('/Library/Application Support/VirtualBox/LaunchDaemons/VirtualBoxStartup.sh') + ' restart'));
}
})
@ -59,7 +59,7 @@ var _steps = [{
message: 'To run Docker containers on your computer, Kitematic is starting a Linux virtual machine. This may take a minute...',
totalPercent: 60,
percent: 0,
seconds: 80,
seconds: 110,
run: Promise.coroutine(function* (progressCallback) {
setupUtil.simulateProgress(this.seconds, progressCallback);
var exists = yield machine.exists();
@ -149,7 +149,7 @@ var SetupStore = assign(Object.create(EventEmitter.prototype), {
var vboxNeedsInstall = !virtualBox.installed();
required.download = vboxNeedsInstall && (!fs.existsSync(vboxfile) || setupUtil.checksum(vboxfile) !== virtualBox.checksum());
required.install = vboxNeedsInstall || (util.isWindows() && !virtualBox.active());
required.install = vboxNeedsInstall || (!util.isWindows() && !virtualBox.active());
required.init = required.install || !(yield machine.exists()) || (yield machine.state()) !== 'Running' || !isoversion || util.compareVersions(isoversion, packagejson['docker-version']) < 0;
var exists = yield machine.exists();

View File

@ -5,7 +5,7 @@ var fs = require('fs');
var util = require('./Util');
var resources = require('./ResourcesUtil');
var NAME = 'dev';
var NAME = util.isWindows () ? 'kitematic' : 'dev';
var DockerMachine = {
command: function () {
@ -60,7 +60,7 @@ var DockerMachine = {
if (util.isWindows()) {
return util.exec([this.command(), '-D', 'create', '-d', 'virtualbox', '--virtualbox-memory', '2048', NAME]);
} else {
return util.exec([this.command(), '-D', 'create', '-d', 'virtualbox', '--virtualbox-boot2docker-url', path.join(process.cwd(), 'resources', 'boot2docker-' + dockerversion + '.iso'), '--virtualbox-memory', '2048', NAME]);
return util.exec([this.command(), '-D', 'create', '-d', 'virtualbox' ,'--virtualbox-boot2docker-url', path.join(process.cwd(), 'resources', 'boot2docker-' + dockerversion + '.iso'), '--virtualbox-memory', '2048', NAME]);
}
},
start: function () {
@ -161,8 +161,7 @@ var DockerMachine = {
{env: {
'DOCKER_HOST' : machine.url,
'DOCKER_CERT_PATH' : path.join(util.home(), '.docker/machine/machines/' + machine.name),
'DOCKER_TLS_VERIFY': 1,
'PATH': resources.resourceDir()
'DOCKER_TLS_VERIFY': 1
}
});
});
@ -173,7 +172,7 @@ var DockerMachine = {
util.exec(cmd).then(() => {});
});
}
}
},
};
module.exports = DockerMachine;

View File

@ -25,8 +25,6 @@ export default {
throw new Error('Certificate directory does not exist');
}
console.log(ip);
this.host = ip;
this.client = new dockerode({
protocol: 'https',
@ -369,13 +367,14 @@ export default {
let columns = {};
columns.amount = 4; // arbitrary
columns.toFill = 0; // the current column index, waiting for layer IDs to be displayed
let error = null;
// data is associated with one layer only (can be identified with id)
stream.on('data', str => {
var data = JSON.parse(str);
if (data.error) {
callback(data.error);
error = data.error;
return;
}
@ -449,7 +448,7 @@ export default {
}
});
stream.on('end', function () {
callback();
callback(error);
});
});
},

View File

@ -74,11 +74,10 @@ module.exports = {
let data = JSON.parse(body);
if (response.statusCode === 200 && data && data.token) {
localStorage.setItem('auth.jwt', data.token);
this.request(req, callback);
} else {
this.logout();
}
this.request(req, callback);
});
} else {
callback(error, response, body);

View File

@ -10,6 +10,15 @@ var app = remote.require('app');
module.exports = {
exec: function (args, options) {
options = options || {};
// Add resources dir to exec path for Windows
if (this.isWindows()) {
options.env = options.env || {};
if (!options.env.PATH) {
options.env.PATH = process.env.RESOURCES_PATH + ';' + process.env.PATH;
}
}
let fn = Array.isArray(args) ? exec : child_process.exec;
return new Promise((resolve, reject) => {
fn(args, options, (stderr, stdout, code) => {
@ -44,7 +53,8 @@ module.exports = {
return app.getPath('home');
},
documents: function () {
return this.isWindows() ? 'My\ Documents' : 'Documents';
// TODO: fix me for windows 7
return 'Documents';
},
supportDir: function () {
return app.getPath('userData');
@ -141,5 +151,8 @@ module.exports = {
}
return fullPath;
},
linuxToWindowsPath: function (linuxAbsPath) {
return linuxAbsPath.replace('/c', 'C:').split('/').join('\\');
},
webPorts: ['80', '8000', '8080', '3000', '5000', '2368', '9200', '8983']
};

View File

@ -12,12 +12,12 @@
.no-drag {
-webkit-app-region: no-drag;
}
.left-header {
display: flex;
min-width: @sidebar-width + 1px;
}
.right-header {
display: flex;
justify-content: flex-end;
@ -33,9 +33,9 @@
}
.logo {
padding: 0.9rem 1rem 0 0;
padding: 0.9rem 1rem 0 1rem;
}
.login-wrapper {
flex: 1 auto;
display: flex;
@ -65,6 +65,81 @@
}
}
.windows-buttons {
display: flex;
align-items: flex-start;
justify-content: flex-end;
flex: 0 1 auto;
margin-right: 7px;
-webkit-app-region: no-drag;
.windows-button {
height: 25px;
margin-left: 1px;
-webkit-transition: -webkit-filter 0.3s;
&:hover {
-webkit-transition: -webkit-filter 0s;
}
&.button-minimize, &.button-fullscreen, &.button-fullscreenclose {
min-width: 34px;
background-color: white;
.icon {
background-repeat: no-repeat;
-webkit-filter: brightness(0.3);
height: 25px;
}
&:hover {
-webkit-filter: brightness(0.9);
}
&:active {
-webkit-filter: brightness(0.8);
}
}
&.button-minimize {
.icon {
background-position: 50% 18px;
.at2x('windows-minimize.png', 14px, 2px);
}
}
&.button-fullscreen {
.icon {
background-position: center;
.at2x('windows-fullscreen.png', 14px, 2px);
}
}
&.button-fullscreenclose {
.icon {
background-position: center;
.at2x('windows-fullscreenclose.png', 14px, 2px);
}
}
&.button-close {
min-width: 58px;
background: #C75050;
.at2x('windows-close.png', 12px, 9px);
background-repeat: no-repeat;
background-position: center;
box-shadow: inset 0 0 0 -1px rgba(255, 255, 255, 0.4);
&:hover {
-webkit-filter: saturate(120%);
}
&:active {
-webkit-filter: brightness(0.8);
}
}
}
}
.buttons {
display: flex;
margin: 0 1.5rem;

View File

@ -10,9 +10,9 @@
flex-direction: row;
flex: 1 auto;
padding: 2rem;
.image {
display: flex;
flex: 1 auto;
@ -163,7 +163,7 @@
.btn-close {
-webkit-app-region: no-drag;
position: absolute;
top: 16px;
bottom: 16px;
right: 16px;
font-size: 14px;
}