Merge branch 'master' into jeffdm-windows
Conflicts: package.json src/ContainerHomeFolders.react.js src/ContainerStore.js
|
@ -53,6 +53,10 @@ We're thrilled to receive pull requests of any kind. Anything from bug fix, test
|
|||
|
||||
That said, please let us know what you're planning to do! For large changes always create a proposal. Maintainers will love to give you advice on building it and it keeps the app's design coherent.
|
||||
|
||||
### Pull Request Requirements:
|
||||
- Tests
|
||||
- [Signed Off](https://github.com/docker/docker/blob/master/CONTRIBUTING.md#sign-your-work)
|
||||
|
||||
## Code Guidelines
|
||||
|
||||
### Javascript
|
||||
|
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 3.6 KiB After Width: | Height: | Size: 3.3 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 2.8 KiB |
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 3.9 KiB After Width: | Height: | Size: 3.5 KiB |
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "Kitematic",
|
||||
"version": "0.5.11",
|
||||
"version": "0.5.13",
|
||||
"author": "Kitematic",
|
||||
"description": "Simple Docker Container management for Mac OS X.",
|
||||
"homepage": "https://kitematic.com/",
|
||||
|
@ -42,10 +42,10 @@
|
|||
"docker-version": "1.5.0",
|
||||
"docker-machine-version": "0.1.0-kitematic-0.5.10",
|
||||
"atom-shell-version": "0.21.3",
|
||||
"virtualbox-version": "4.3.24",
|
||||
"virtualbox-filename": "VirtualBox-4.3.24.pkg",
|
||||
"virtualbox-version": "4.3.26",
|
||||
"virtualbox-filename": "VirtualBox-4.3.26.pkg",
|
||||
"virtualbox-filename-win": "VirtualBox-4.3.26.exe",
|
||||
"virtualbox-checksum": "100eee21df3808fc1b1e461e86f10b6d2a748935c5f31bc665f4ff0777e44a0b",
|
||||
"virtualbox-checksum": "668f61c95efe37f8fc65cafe95b866fba64e37f2492dfc1e2b44a7ac3dcafa3b",
|
||||
"virtualbox-checksum-win": "9cb265babf307d825f5178693af95ffca077f80ae22cf43868c3538c159123ff",
|
||||
"dependencies": {
|
||||
"ansi-to-html": "0.3.0",
|
||||
|
|
|
@ -2,20 +2,46 @@ var _ = require('underscore');
|
|||
var React = require('react/addons');
|
||||
var RetinaImage = require('react-retina-image');
|
||||
var path = require('path');
|
||||
var exec = require('exec');
|
||||
var shell = require('shell');
|
||||
var util = require('./Util');
|
||||
var metrics = require('./Metrics');
|
||||
var Router = require('react-router');
|
||||
var util = require('./Util');
|
||||
var ContainerStore = require('./ContainerStore');
|
||||
|
||||
var ContainerHomeFolder = React.createClass({
|
||||
mixins: [Router.State, Router.Navigation],
|
||||
handleClickFolder: function (path) {
|
||||
handleClickFolder: function (hostVolume, containerVolume) {
|
||||
metrics.track('Opened Volume Directory', {
|
||||
from: 'home'
|
||||
});
|
||||
util.openPathOrUrl(path, function (err) {
|
||||
if (err) { throw err; }
|
||||
|
||||
if (hostVolume.indexOf(process.env.HOME) === -1) {
|
||||
var volumes = _.clone(this.props.container.Volumes);
|
||||
var newHostVolume = path.join(util.home(), 'Kitematic', this.props.container.Name, containerVolume);
|
||||
volumes[containerVolume] = newHostVolume;
|
||||
var binds = _.pairs(volumes).map(function (pair) {
|
||||
if(util.isWindows()) {
|
||||
var home = util.home();
|
||||
home = home.charAt(0).toLowerCase() + home.slice(1);
|
||||
home = '/' + home.replace(':', '').replace(/\\/g, '/');
|
||||
var fullPath = path.join(home, 'Kitematic', pair[1], pair[0]);
|
||||
fullPath = fullPath.replace(/\\/g, '/');
|
||||
return fullPath + ':' + pair[0];
|
||||
}
|
||||
return pair[1] + ':' + pair[0];
|
||||
});
|
||||
ContainerStore.updateContainer(this.props.container.Name, {
|
||||
Binds: binds
|
||||
}, function (err) {
|
||||
if (err) {
|
||||
console.log(err);
|
||||
return;
|
||||
}
|
||||
shell.showItemInFolder(newHostVolume);
|
||||
});
|
||||
} else {
|
||||
shell.showItemInFolder(hostVolume);
|
||||
}
|
||||
},
|
||||
handleClickChangeFolders: function () {
|
||||
metrics.track('Viewed Volume Settings', {
|
||||
|
@ -24,24 +50,21 @@ var ContainerHomeFolder = React.createClass({
|
|||
this.transitionTo('containerSettingsVolumes', {name: this.getParams().name});
|
||||
},
|
||||
render: function () {
|
||||
var folders;
|
||||
if (this.props.container) {
|
||||
var self = this;
|
||||
folders = _.map(self.props.container.Volumes, function (val, key) {
|
||||
if (!this.props.container) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var folders = _.map(this.props.container.Volumes, (val, key) => {
|
||||
var firstFolder = key.split(path.sep)[1];
|
||||
if (!val || val.indexOf(process.env.HOME) === -1) {
|
||||
return;
|
||||
} else {
|
||||
return (
|
||||
<div key={key} className="folder" onClick={self.handleClickFolder.bind(self, val)}>
|
||||
<div key={key} className="folder" onClick={this.handleClickFolder.bind(this, val, key)}>
|
||||
<RetinaImage src="folder.png" />
|
||||
<div className="text">{firstFolder}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
if (this.props.container && this.props.container.Volumes && _.keys(this.props.container.Volumes).length > 0 && this.props.container.State.Running) {
|
||||
|
||||
if (this.props.container.Volumes && _.keys(this.props.container.Volumes).length > 0 && this.props.container.State.Running) {
|
||||
return (
|
||||
<div className="folders wrapper">
|
||||
<h4>Edit Files</h4>
|
||||
|
@ -52,9 +75,7 @@ var ContainerHomeFolder = React.createClass({
|
|||
</div>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<div></div>
|
||||
);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -52,6 +52,9 @@ var ContainerHomeLogs = React.createClass({
|
|||
var logs = this.state.logs.map(function (l, i) {
|
||||
return <p key={i} dangerouslySetInnerHTML={{__html: l}}></p>;
|
||||
});
|
||||
if (logs.length === 0) {
|
||||
logs = "No logs for this container.";
|
||||
}
|
||||
return (
|
||||
<div className="mini-logs wrapper">
|
||||
<h4>Logs</h4>
|
||||
|
|
|
@ -45,6 +45,9 @@ var ContainerLogs = React.createClass({
|
|||
var logs = this.state.logs.map(function (l, i) {
|
||||
return <p key={i} dangerouslySetInnerHTML={{__html: l}}></p>;
|
||||
});
|
||||
if (logs.length === 0) {
|
||||
logs = "No logs for this container.";
|
||||
}
|
||||
return (
|
||||
<div className="details-panel details-logs logs">
|
||||
{logs}
|
||||
|
|
|
@ -2,7 +2,6 @@ var _ = require('underscore');
|
|||
var React = require('react/addons');
|
||||
var Router = require('react-router');
|
||||
var remote = require('remote');
|
||||
var exec = require('exec');
|
||||
var dialog = remote.require('dialog');
|
||||
var metrics = require('./Metrics');
|
||||
var ContainerStore = require('./ContainerStore');
|
||||
|
@ -32,6 +31,21 @@ var ContainerSettingsVolumes = React.createClass({
|
|||
}
|
||||
});
|
||||
},
|
||||
handleRemoveVolumeClick: function (dockerVol) {
|
||||
metrics.track('Removed Volume Directory', {
|
||||
from: 'settings'
|
||||
});
|
||||
var volumes = _.clone(this.props.container.Volumes);
|
||||
delete volumes[dockerVol];
|
||||
var binds = _.pairs(volumes).map(function (pair) {
|
||||
return pair[1] + ':' + pair[0];
|
||||
});
|
||||
ContainerStore.updateContainer(this.props.container.Name, {
|
||||
Binds: binds
|
||||
}, function (err) {
|
||||
if (err) { console.log(err); }
|
||||
});
|
||||
},
|
||||
handleOpenVolumeClick: function (path) {
|
||||
metrics.track('Opened Volume Directory', {
|
||||
from: 'settings'
|
||||
|
@ -51,6 +65,7 @@ var ContainerSettingsVolumes = React.createClass({
|
|||
<span>
|
||||
<a className="value-right">No Folder</a>
|
||||
<a className="btn btn-action small" onClick={self.handleChooseVolumeClick.bind(self, key)}>Change</a>
|
||||
<a className="btn btn-action small" onClick={self.handleRemoveVolumeClick.bind(self, key)}>Remove</a>
|
||||
</span>
|
||||
);
|
||||
} else {
|
||||
|
@ -58,6 +73,7 @@ var ContainerSettingsVolumes = React.createClass({
|
|||
<span>
|
||||
<a className="value-right" onClick={self.handleOpenVolumeClick.bind(self, val)}>{val.replace(process.env.HOME, '~')}</a>
|
||||
<a className="btn btn-action small" onClick={self.handleChooseVolumeClick.bind(self, key)}>Change</a>
|
||||
<a className="btn btn-action small" onClick={self.handleRemoveVolumeClick.bind(self, key)}>Remove</a>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,14 +1,12 @@
|
|||
var _ = require('underscore');
|
||||
var EventEmitter = require('events').EventEmitter;
|
||||
var async = require('async');
|
||||
var path = require('path');
|
||||
var assign = require('object-assign');
|
||||
var docker = require('./Docker');
|
||||
var metrics = require('./Metrics');
|
||||
var registry = require('./Registry');
|
||||
var logstore = require('./LogStore');
|
||||
var bugsnag = require('bugsnag-js');
|
||||
var util = require('./Util');
|
||||
|
||||
var _placeholders = {};
|
||||
var _containers = {};
|
||||
|
@ -92,32 +90,7 @@ var ContainerStore = assign(Object.create(EventEmitter.prototype), {
|
|||
},
|
||||
_startContainer: function (name, containerData, callback) {
|
||||
var self = this;
|
||||
docker.client().getImage(containerData.Image).inspect(function (err, data) {
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
var binds = containerData.Binds || [];
|
||||
if (data.Config.Volumes) {
|
||||
_.each(data.Config.Volumes, function (value, key) {
|
||||
var existingBind = _.find(binds, b => {
|
||||
return b.indexOf(':' + key) !== -1;
|
||||
});
|
||||
if (!existingBind) {
|
||||
var home = util.home();
|
||||
|
||||
if(util.isWindows()) {
|
||||
home = home.charAt(0).toLowerCase() + home.slice(1);
|
||||
home = "/" + home.replace(':', '').replace(/\\/g, '/');
|
||||
var fullPath = path.join(home, 'Kitematic', name, key);
|
||||
fullPath = fullPath.replace(/\\/g, '/');
|
||||
binds.push(fullPath + ':' + key);
|
||||
} else {
|
||||
binds.push(path.join(home, 'Kitematic', name, key) + ':' + key);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
var startopts = {
|
||||
Binds: binds
|
||||
};
|
||||
|
@ -134,7 +107,6 @@ var ContainerStore = assign(Object.create(EventEmitter.prototype), {
|
|||
}
|
||||
self.fetchContainer(name, callback);
|
||||
});
|
||||
});
|
||||
},
|
||||
_createContainer: function (name, containerData, callback) {
|
||||
var existing = docker.client().getContainer(name);
|
||||
|
@ -147,6 +119,9 @@ var ContainerStore = assign(Object.create(EventEmitter.prototype), {
|
|||
if (containerData.Config && containerData.Config.Image) {
|
||||
containerData.Image = containerData.Config.Image;
|
||||
}
|
||||
if (!containerData.Env && containerData.Config && containerData.Config.Env) {
|
||||
containerData.Env = containerData.Config.Env;
|
||||
}
|
||||
existing.kill(function () {
|
||||
existing.remove(function () {
|
||||
docker.client().createContainer(containerData, function (err) {
|
||||
|
@ -154,11 +129,7 @@ var ContainerStore = assign(Object.create(EventEmitter.prototype), {
|
|||
callback(err, null);
|
||||
return;
|
||||
}
|
||||
if (containerData.State && !containerData.State.Running) {
|
||||
self.fetchContainer(containerData.name, callback);
|
||||
} else {
|
||||
self._startContainer(name, containerData, callback);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -5,6 +5,7 @@ var ContainerStore = require('./ContainerStore');
|
|||
var metrics = require('./Metrics');
|
||||
var OverlayTrigger = require('react-bootstrap').OverlayTrigger;
|
||||
var Tooltip = require('react-bootstrap').Tooltip;
|
||||
var util = require('./Util');
|
||||
|
||||
var ImageCard = React.createClass({
|
||||
getInitialState: function () {
|
||||
|
@ -40,6 +41,15 @@ var ImageCard = React.createClass({
|
|||
var $tagOverlay = $(this.getDOMNode()).find('.tag-overlay');
|
||||
$tagOverlay.fadeOut(300);
|
||||
},
|
||||
handleRepoClick: function () {
|
||||
var $repoUri = 'https://registry.hub.docker.com/';
|
||||
if (this.props.image.is_official) {
|
||||
$repoUri = $repoUri + "_/";
|
||||
} else {
|
||||
$repoUri = $repoUri + "u/";
|
||||
}
|
||||
util.exec(['open', $repoUri + this.props.image.name]);
|
||||
},
|
||||
render: function () {
|
||||
var self = this;
|
||||
var name;
|
||||
|
@ -57,8 +67,8 @@ var ImageCard = React.createClass({
|
|||
name = (
|
||||
<div>
|
||||
<div className="namespace official">{namespace}</div>
|
||||
<OverlayTrigger placement="bottom" overlay={<Tooltip>{this.props.image.name}</Tooltip>}>
|
||||
<span className="repo">{repo}</span>
|
||||
<OverlayTrigger placement="bottom" overlay={<Tooltip>View on DockerHub</Tooltip>}>
|
||||
<span className="repo" onClick={this.handleRepoClick}>{repo}</span>
|
||||
</OverlayTrigger>
|
||||
</div>
|
||||
);
|
||||
|
@ -66,8 +76,8 @@ var ImageCard = React.createClass({
|
|||
name = (
|
||||
<div>
|
||||
<div className="namespace">{namespace}</div>
|
||||
<OverlayTrigger placement="bottom" overlay={<Tooltip>{this.props.image.name}</Tooltip>}>
|
||||
<span className="repo">{repo}</span>
|
||||
<OverlayTrigger placement="bottom" overlay={<Tooltip>View on DockerHub</Tooltip>}>
|
||||
<span className="repo" onClick={this.handleRepoClick}>{repo}</span>
|
||||
</OverlayTrigger>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -201,7 +201,7 @@
|
|||
left: -20px;
|
||||
.at2x('runningwave.png', 20px, 20px);
|
||||
-webkit-animation-name: translatewave;
|
||||
-webkit-animation-duration: 8.0s;
|
||||
-webkit-animation-duration: 7.0s;
|
||||
-webkit-animation-iteration-count: infinite;
|
||||
-webkit-animation-timing-function: linear;
|
||||
}
|
||||
|
|
|
@ -186,6 +186,7 @@
|
|||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
.description {
|
||||
|
|
|
@ -25,7 +25,6 @@
|
|||
.action {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
top: 10px;
|
||||
&.disabled {
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
@ -36,11 +35,11 @@
|
|||
.btn-label {
|
||||
position: absolute;
|
||||
color: @brand-action;
|
||||
font-size: 10px;
|
||||
font-size: 9px;
|
||||
width: 200px;
|
||||
top: 30px;
|
||||
top: 45px;
|
||||
&.view {
|
||||
left: 6px;
|
||||
left: 7px;
|
||||
//left: 0px;
|
||||
}
|
||||
&.restart {
|
||||
|
|