diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 5c885f559f..3fe22bdc17 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -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 diff --git a/images/button-restart.png b/images/button-restart.png index 69f030d3ec..0cf9ae43ce 100644 Binary files a/images/button-restart.png and b/images/button-restart.png differ diff --git a/images/button-restart@2x.png b/images/button-restart@2x.png index c710010d35..bb62729cb3 100644 Binary files a/images/button-restart@2x.png and b/images/button-restart@2x.png differ diff --git a/images/button-terminal.png b/images/button-terminal.png index e64a22aa5e..cfcedd974f 100644 Binary files a/images/button-terminal.png and b/images/button-terminal.png differ diff --git a/images/button-terminal@2x.png b/images/button-terminal@2x.png index be2c92fa24..658786d3f6 100644 Binary files a/images/button-terminal@2x.png and b/images/button-terminal@2x.png differ diff --git a/images/button-view.png b/images/button-view.png index 4d67f928f4..0eb0c2e5bd 100644 Binary files a/images/button-view.png and b/images/button-view.png differ diff --git a/images/button-view@2x.png b/images/button-view@2x.png index a445545558..9a9e206961 100644 Binary files a/images/button-view@2x.png and b/images/button-view@2x.png differ diff --git a/package.json b/package.json index e657c0d092..060c0a72e6 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/src/ContainerHomeFolders.react.js b/src/ContainerHomeFolders.react.js index e20b95e1fd..aa1218f4a6 100644 --- a/src/ContainerHomeFolders.react.js +++ b/src/ContainerHomeFolders.react.js @@ -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) { - var firstFolder = key.split(path.sep)[1]; - if (!val || val.indexOf(process.env.HOME) === -1) { - return; - } else { - return ( -
- -
{firstFolder}
-
- ); - } - }); + if (!this.props.container) { + return false; } - if (this.props.container && this.props.container.Volumes && _.keys(this.props.container.Volumes).length > 0 && this.props.container.State.Running) { + + var folders = _.map(this.props.container.Volumes, (val, key) => { + var firstFolder = key.split(path.sep)[1]; + return ( +
+ +
{firstFolder}
+
+ ); + }); + + if (this.props.container.Volumes && _.keys(this.props.container.Volumes).length > 0 && this.props.container.State.Running) { return (

Edit Files

@@ -52,9 +75,7 @@ var ContainerHomeFolder = React.createClass({
); } else { - return ( -
- ); + return false; } } }); diff --git a/src/ContainerHomeLogs.react.js b/src/ContainerHomeLogs.react.js index a13f310c3c..d400a48d72 100644 --- a/src/ContainerHomeLogs.react.js +++ b/src/ContainerHomeLogs.react.js @@ -52,6 +52,9 @@ var ContainerHomeLogs = React.createClass({ var logs = this.state.logs.map(function (l, i) { return

; }); + if (logs.length === 0) { + logs = "No logs for this container."; + } return (

Logs

diff --git a/src/ContainerLogs.react.js b/src/ContainerLogs.react.js index cdde86f4d2..e20910cc49 100644 --- a/src/ContainerLogs.react.js +++ b/src/ContainerLogs.react.js @@ -45,6 +45,9 @@ var ContainerLogs = React.createClass({ var logs = this.state.logs.map(function (l, i) { return

; }); + if (logs.length === 0) { + logs = "No logs for this container."; + } return (
{logs} diff --git a/src/ContainerSettingsVolumes.react.js b/src/ContainerSettingsVolumes.react.js index 86d2aec584..0b6d2ad614 100644 --- a/src/ContainerSettingsVolumes.react.js +++ b/src/ContainerSettingsVolumes.react.js @@ -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({ No Folder Change + Remove ); } else { @@ -58,6 +73,7 @@ var ContainerSettingsVolumes = React.createClass({ {val.replace(process.env.HOME, '~')} Change + Remove ); } diff --git a/src/ContainerStore.js b/src/ContainerStore.js index 3ced6dbafb..b0cc3940df 100644 --- a/src/ContainerStore.js +++ b/src/ContainerStore.js @@ -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,48 +90,22 @@ var ContainerStore = assign(Object.create(EventEmitter.prototype), { }, _startContainer: function (name, containerData, callback) { var self = this; - docker.client().getImage(containerData.Image).inspect(function (err, data) { + var binds = containerData.Binds || []; + var startopts = { + Binds: binds + }; + if (containerData.NetworkSettings && containerData.NetworkSettings.Ports) { + startopts.PortBindings = containerData.NetworkSettings.Ports; + } else{ + startopts.PublishAllPorts = true; + } + var container = docker.client().getContainer(name); + container.start(startopts, function (err) { 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 - }; - if (containerData.NetworkSettings && containerData.NetworkSettings.Ports) { - startopts.PortBindings = containerData.NetworkSettings.Ports; - } else{ - startopts.PublishAllPorts = true; - } - var container = docker.client().getContainer(name); - container.start(startopts, function (err) { - if (err) { - callback(err); - return; - } - self.fetchContainer(name, callback); - }); + self.fetchContainer(name, callback); }); }, _createContainer: function (name, containerData, callback) { @@ -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); - } + self._startContainer(name, containerData, callback); }); }); }); diff --git a/src/ImageCard.react.js b/src/ImageCard.react.js index b132aec25f..db895f6e32 100644 --- a/src/ImageCard.react.js +++ b/src/ImageCard.react.js @@ -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 = (
{namespace}
- {this.props.image.name}}> - {repo} + View on DockerHub}> + {repo}
); @@ -66,8 +76,8 @@ var ImageCard = React.createClass({ name = (
{namespace}
- {this.props.image.name}}> - {repo} + View on DockerHub}> + {repo}
); diff --git a/src/SetupStore.js b/src/SetupStore.js index 87acb0189a..3615fff789 100644 --- a/src/SetupStore.js +++ b/src/SetupStore.js @@ -180,7 +180,7 @@ var SetupStore = assign(Object.create(EventEmitter.prototype), { var required = {}; var vboxfile = path.join(util.supportDir(), setupUtil.virtualBoxFileName()); var vboxNeedsInstall = !virtualBox.installed(); - + required.download = vboxNeedsInstall && (!fs.existsSync(vboxfile) || setupUtil.checksum(vboxfile) !== setupUtil.virtualBoxChecksum()); required.install = vboxNeedsInstall || setupUtil.needsBinaryFix(); required.init = required.install || !(yield machine.exists()) || (yield machine.state()) !== 'Running' || !isoversion || setupUtil.compareVersions(isoversion, packagejson['docker-version']) < 0; diff --git a/styles/left-panel.less b/styles/left-panel.less index ecba94e97c..2aa98270fc 100644 --- a/styles/left-panel.less +++ b/styles/left-panel.less @@ -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; } diff --git a/styles/new-container.less b/styles/new-container.less index 0a201ae8f3..a00f9c3eb3 100644 --- a/styles/new-container.less +++ b/styles/new-container.less @@ -186,6 +186,7 @@ overflow: hidden; text-overflow: ellipsis; white-space: nowrap; + text-decoration: underline; } } .description { diff --git a/styles/right-panel.less b/styles/right-panel.less index 2d5af45524..384b3e8934 100644 --- a/styles/right-panel.less +++ b/styles/right-panel.less @@ -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 {