From e92678508b86251ece3f794c5ff91239476517e2 Mon Sep 17 00:00:00 2001 From: Jeffrey Morgan Date: Wed, 11 Mar 2015 18:43:19 -0400 Subject: [PATCH 01/32] WIP button --- src/ContainerStore.js | 14 +++++++++++++- src/Main.js | 19 +++++++++++++------ src/browser.js | 21 +++++++++++++++++++++ util/Info.plist | 11 +++++++++++ 4 files changed, 58 insertions(+), 7 deletions(-) diff --git a/src/ContainerStore.js b/src/ContainerStore.js index 121033316f..64db81119b 100644 --- a/src/ContainerStore.js +++ b/src/ContainerStore.js @@ -16,6 +16,7 @@ var _progress = {}; var _muted = {}; var _blocked = {}; var _error = null; +var _pendingCreates = []; var ContainerStore = assign(Object.create(EventEmitter.prototype), { CLIENT_CONTAINER_EVENT: 'client_container_event', @@ -238,7 +239,6 @@ var ContainerStore = assign(Object.create(EventEmitter.prototype), { } }, init: function (callback) { - // TODO: Load cached data from db on loading this.fetchAllContainers(err => { if (err) { _error = err; @@ -249,6 +249,9 @@ var ContainerStore = assign(Object.create(EventEmitter.prototype), { } else { callback(); } + _pendingCreates.forEach(e => { + this.create(e.repository, e.tag, () => {}); + }); var placeholderData = JSON.parse(localStorage.getItem('store.placeholders')); if (placeholderData) { _placeholders = _.omit(placeholderData, _.keys(_containers)); @@ -305,6 +308,15 @@ var ContainerStore = assign(Object.create(EventEmitter.prototype), { }); }, create: function (repository, tag, callback) { + if (!docker.host()) { + console.log('buffering ' + repository); + _pendingCreates.push({ + repository: repository, + tag: tag + }); + return; + } + tag = tag || 'latest'; var imageName = repository + ':' + tag; var containerName = this._generateName(repository); diff --git a/src/Main.js b/src/Main.js index 9f582f3588..ffc14d3f71 100644 --- a/src/Main.js +++ b/src/Main.js @@ -27,6 +27,19 @@ setInterval(function () { router.run(Handler => React.render(, document.body)); +ipc.on('application:quitting', opts => { + if (!opts.updating && localStorage.getItem('settings.closeVMOnQuit') === 'true') { + machine.stop(); + } +}); + +ipc.on('application:open-url', opts => { + console.log('Creating container'); + var url = opts.url.substr(19); + console.log(url); + ContainerStore.create(url, 'latest', () => {}); +}); + SetupStore.setup().then(() => { Menu.setApplicationMenu(Menu.buildFromTemplate(template())); ContainerStore.on(ContainerStore.SERVER_ERROR_EVENT, (err) => { @@ -43,9 +56,3 @@ SetupStore.setup().then(() => { console.log(err); bugsnag.notify(err); }); - -ipc.on('application:quitting', opts => { - if (!opts.updating && localStorage.getItem('settings.closeVMOnQuit') === 'true') { - machine.stop(); - } -}); diff --git a/src/browser.js b/src/browser.js index 8722673dcb..9ba05e9576 100644 --- a/src/browser.js +++ b/src/browser.js @@ -10,6 +10,8 @@ process.env.RESOURCES_PATH = path.join(__dirname, '/../resources'); process.chdir(path.join(__dirname, '..')); process.env.PATH = '/usr/local/bin:' + process.env.PATH; +console.log(process.argv); + var size = {}, settingsjson = {}; try { size = JSON.parse(fs.readFileSync(path.join(process.env[(process.platform === 'win32') ? 'USERPROFILE' : 'HOME'], 'Library', 'Application\ Support', 'Kitematic', 'size'))); @@ -18,6 +20,13 @@ try { settingsjson = JSON.parse(fs.readFileSync(path.join(__dirname, '..', 'settings.json'), 'utf8')); } catch (err) {} + +var openURL = null; +app.on('open-url', function (event, url) { + event.preventDefault(); + openURL = url; +}); + app.on('ready', function () { var mainWindow = new BrowserWindow({ width: size.width || 1000, @@ -65,6 +74,18 @@ app.on('ready', function () { mainWindow.show(); mainWindow.focus(); + if (openURL) { + mainWindow.webContents.send('application:open-url', { + url: openURL + }); + } + app.on('open-url', function (event, url) { + event.preventDefault(); + mainWindow.webContents.send('application:open-url', { + url: url + }); + }); + if (process.env.NODE_ENV !== 'development') { autoUpdater.setFeedUrl('https://updates.kitematic.com/releases/latest?version=' + app.getVersion() + '&beta=' + !!settingsjson.beta); } diff --git a/util/Info.plist b/util/Info.plist index 396be69183..6a030f001b 100644 --- a/util/Info.plist +++ b/util/Info.plist @@ -26,5 +26,16 @@ AtomApplication NSSupportsAutomaticGraphicsSwitching + CFBundleURLTypes + + + CFBundleURLSchemes + + kitematic + + CFBundleURLName + Kitematic Session Protocol + + From 9e2c11f6d0d6322ad652627d704419c3d42b70b7 Mon Sep 17 00:00:00 2001 From: Jeffrey Morgan Date: Wed, 18 Mar 2015 11:51:59 -0700 Subject: [PATCH 02/32] Changing protocol to Docker --- src/Main.js | 20 ++++++++++++++++---- util/Info.plist | 4 ++-- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/src/Main.js b/src/Main.js index ffc14d3f71..79f96e9ed1 100644 --- a/src/Main.js +++ b/src/Main.js @@ -34,10 +34,22 @@ ipc.on('application:quitting', opts => { }); ipc.on('application:open-url', opts => { - console.log('Creating container'); - var url = opts.url.substr(19); - console.log(url); - ContainerStore.create(url, 'latest', () => {}); + console.log('Creating container from protocol'); + var parser = document.createElement('a'); + parser.href = opts.url; + + if (parser.protocol !== 'docker:') { + return; + } + + var pathname = parser.pathname.replace('//', ''); + var slash = pathname.indexOf('/'); + var base = pathname.slice(0, slash); + + if (base === 'runRepo') { + var repo = pathname.substring(slash + 1); + ContainerStore.create(repo, 'latest', () => {}); + } }); SetupStore.setup().then(() => { diff --git a/util/Info.plist b/util/Info.plist index 6a030f001b..5efd29e633 100644 --- a/util/Info.plist +++ b/util/Info.plist @@ -31,10 +31,10 @@ CFBundleURLSchemes - kitematic + docker CFBundleURLName - Kitematic Session Protocol + Docker App Protocol From 89d3728bda1b63c951101b6c4c7e82429816f45c Mon Sep 17 00:00:00 2001 From: Jeffrey Morgan Date: Mon, 6 Apr 2015 14:19:47 -0400 Subject: [PATCH 03/32] Removing console side --- src/browser.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/browser.js b/src/browser.js index 9ba05e9576..ee7810450a 100644 --- a/src/browser.js +++ b/src/browser.js @@ -10,8 +10,6 @@ process.env.RESOURCES_PATH = path.join(__dirname, '/../resources'); process.chdir(path.join(__dirname, '..')); process.env.PATH = '/usr/local/bin:' + process.env.PATH; -console.log(process.argv); - var size = {}, settingsjson = {}; try { size = JSON.parse(fs.readFileSync(path.join(process.env[(process.platform === 'win32') ? 'USERPROFILE' : 'HOME'], 'Library', 'Application\ Support', 'Kitematic', 'size'))); From d7dea0bcfadfcef6c58be71e6fc68731b76020a1 Mon Sep 17 00:00:00 2001 From: Jeffrey Morgan Date: Mon, 6 Apr 2015 21:45:45 -0400 Subject: [PATCH 04/32] wip button Signed-off-by: Jeffrey Morgan --- src/ContainerStore.js | 32 +++++++++++++++++++------------- src/Containers.react.js | 20 +++++++++++++++----- src/Main.js | 3 ++- src/NewContainer.react.js | 3 ++- src/PullContainer.react.js | 16 ++++++++++++++++ src/Routes.js | 2 ++ 6 files changed, 56 insertions(+), 20 deletions(-) create mode 100644 src/PullContainer.react.js diff --git a/src/ContainerStore.js b/src/ContainerStore.js index f3c0f5613c..d4a7615f31 100644 --- a/src/ContainerStore.js +++ b/src/ContainerStore.js @@ -14,7 +14,7 @@ var _progress = {}; var _muted = {}; var _blocked = {}; var _error = null; -var _pendingCreates = []; +var _pending = null; var ContainerStore = assign(Object.create(EventEmitter.prototype), { CLIENT_CONTAINER_EVENT: 'client_container_event', @@ -237,9 +237,6 @@ var ContainerStore = assign(Object.create(EventEmitter.prototype), { } else { callback(); } - _pendingCreates.forEach(e => { - this.create(e.repository, e.tag, () => {}); - }); var placeholderData = JSON.parse(localStorage.getItem('store.placeholders')); if (placeholderData) { _placeholders = _.omit(placeholderData, _.keys(_containers)); @@ -296,19 +293,14 @@ var ContainerStore = assign(Object.create(EventEmitter.prototype), { }); }, create: function (repository, tag, callback) { - if (!docker.host()) { - console.log('buffering ' + repository); - _pendingCreates.push({ - repository: repository, - tag: tag - }); - return; - } - tag = tag || 'latest'; var imageName = repository + ':' + tag; var containerName = this._generateName(repository); + if (_pending && _pending.repository === repository && _pending.tag === tag) { + _pending = null; + } + _placeholders[containerName] = { Name: containerName, Image: imageName, @@ -483,6 +475,20 @@ var ContainerStore = assign(Object.create(EventEmitter.prototype), { }, downloading: function () { return !!_.keys(_placeholders).length; + }, + pending: function () { + return _pending; + }, + setPending: function (repository, tag) { + _pending = { + repository: repository, + tag: tag + }; + this.emit(this.CLIENT_CONTAINER_EVENT, null, 'pending'); + }, + clearPending: function () { + _pending = null; + this.emit(this.CLIENT_CONTAINER_EVENT, null, 'pending'); } }); diff --git a/src/Containers.react.js b/src/Containers.react.js index af151dabca..62f316c1b2 100644 --- a/src/Containers.react.js +++ b/src/Containers.react.js @@ -33,7 +33,11 @@ var Containers = React.createClass({ ContainerStore.on(ContainerStore.SERVER_CONTAINER_EVENT, this.update); ContainerStore.on(ContainerStore.CLIENT_CONTAINER_EVENT, this.updateFromClient); - if (this.state.sorted.length) { + console.log(this.state); + if (this.state.pending) { + console.log('pending'); + this.transitionTo('pull'); + } else if (this.state.sorted.length) { this.transitionTo('containerHome', {name: this.state.sorted[0].Name}); } @@ -64,7 +68,8 @@ var Containers = React.createClass({ this.setState({ containers: ContainerStore.containers(), sorted: ContainerStore.sorted(), - downloading: ContainerStore.downloading() + downloading: ContainerStore.downloading(), + pending: ContainerStore.pending() }); if (status === 'destroy') { this.onDestroy(); @@ -74,9 +79,14 @@ var Containers = React.createClass({ this.setState({ containers: ContainerStore.containers(), sorted: ContainerStore.sorted(), - downloading: ContainerStore.downloading() + downloading: ContainerStore.downloading(), + pending: ContainerStore.pending() }); - if (status === 'create') { + console.log('update from client'); + + if (this.state.pending) { + this.transitionTo('pull'); + } else if (status === 'create') { this.transitionTo('containerHome', {name: name}); } else if (status === 'destroy') { this.onDestroy(); @@ -197,7 +207,7 @@ var Containers = React.createClass({
- + ); diff --git a/src/Main.js b/src/Main.js index 79f96e9ed1..72588cbac0 100644 --- a/src/Main.js +++ b/src/Main.js @@ -48,9 +48,10 @@ ipc.on('application:open-url', opts => { if (base === 'runRepo') { var repo = pathname.substring(slash + 1); - ContainerStore.create(repo, 'latest', () => {}); + ContainerStore.setPending(repo, 'latest'); } }); +ContainerStore.setPending('kitematic/ghost', 'latest'); SetupStore.setup().then(() => { Menu.setApplicationMenu(Menu.buildFromTemplate(template())); diff --git a/src/NewContainer.react.js b/src/NewContainer.react.js index 05e772cedd..eb8059a31a 100644 --- a/src/NewContainer.react.js +++ b/src/NewContainer.react.js @@ -4,8 +4,8 @@ var React = require('react/addons'); var RetinaImage = require('react-retina-image'); var Radial = require('./Radial.react'); var ImageCard = require('./ImageCard.react'); -var Promise = require('bluebird'); var metrics = require('./Metrics'); +var Promise = require('bluebird'); var _recommended = []; var _searchPromise = null; @@ -142,6 +142,7 @@ var NewContainer = React.createClass({ 'icon-magnifier': true, 'search-icon': true }); + return (
diff --git a/src/PullContainer.react.js b/src/PullContainer.react.js new file mode 100644 index 0000000000..0fa81925ee --- /dev/null +++ b/src/PullContainer.react.js @@ -0,0 +1,16 @@ +var React = require('react/addons'); + +var PullContainer = React.createClass({ + componentDidMount: function () { + }, + render: function () { + console.log(this.props.pending); + return ( +
+ test +
+ ); + } +}); + +module.exports = PullContainer; diff --git a/src/Routes.js b/src/Routes.js index eab468fa1c..05dd638a0e 100644 --- a/src/Routes.js +++ b/src/Routes.js @@ -10,6 +10,7 @@ var ContainerSettingsPorts = require('./ContainerSettingsPorts.react'); var ContainerSettingsVolumes = require('./ContainerSettingsVolumes.react'); var Preferences = require('./Preferences.react'); var NewContainer = require('./NewContainer.react'); +var PullContainer = require('./PullContainer.react'); var Router = require('react-router'); var Route = Router.Route; @@ -38,6 +39,7 @@ var routes = ( + From a5b003b381550cf05759ac587ba4fc6b9b68c054 Mon Sep 17 00:00:00 2001 From: Jeffrey Morgan Date: Tue, 7 Apr 2015 16:06:08 -0400 Subject: [PATCH 05/32] Adding pull container feature --- .jshintrc | 2 +- src/ContainerList.react.js | 8 +-- src/ContainerStore.js | 4 -- src/Containers.react.js | 60 +++++-------------- src/Main.js | 10 ++-- src/NewContainerPull.react.js | 42 +++++++++++++ ...r.react.js => NewContainerSearch.react.js} | 9 +-- src/Routes.js | 26 ++++---- styles/new-container.less | 29 +++++++++ styles/theme.less | 1 - 10 files changed, 111 insertions(+), 80 deletions(-) create mode 100644 src/NewContainerPull.react.js rename src/{NewContainer.react.js => NewContainerSearch.react.js} (95%) diff --git a/.jshintrc b/.jshintrc index daeaf45072..9240a3ae9f 100644 --- a/.jshintrc +++ b/.jshintrc @@ -27,5 +27,5 @@ "jest": true, "pit": true }, - "predef": [ "Promise" ] + "predef": [ "-Promise" ] } diff --git a/src/ContainerList.react.js b/src/ContainerList.react.js index 1d6d25a034..dd2ae9b139 100644 --- a/src/ContainerList.react.js +++ b/src/ContainerList.react.js @@ -13,15 +13,9 @@ var ContainerList = React.createClass({ ); }); - var newItem; - if (!this.props.downloading) { - newItem = ; - } else { - newItem = ''; - } return (
    - {newItem} + {containers}
); diff --git a/src/ContainerStore.js b/src/ContainerStore.js index d4a7615f31..70de5b65a5 100644 --- a/src/ContainerStore.js +++ b/src/ContainerStore.js @@ -297,10 +297,6 @@ var ContainerStore = assign(Object.create(EventEmitter.prototype), { var imageName = repository + ':' + tag; var containerName = this._generateName(repository); - if (_pending && _pending.repository === repository && _pending.tag === tag) { - _pending = null; - } - _placeholders[containerName] = { Name: containerName, Image: imageName, diff --git a/src/Containers.react.js b/src/Containers.react.js index 62f316c1b2..ab613bb61a 100644 --- a/src/Containers.react.js +++ b/src/Containers.react.js @@ -10,8 +10,6 @@ var metrics = require('./Metrics'); var autoUpdater = remote.require('auto-updater'); var RetinaImage = require('react-retina-image'); var machine = require('./DockerMachine'); -var OverlayTrigger = require('react-bootstrap').OverlayTrigger; -var Tooltip = require('react-bootstrap').Tooltip; var util = require('./Util'); var Containers = React.createClass({ @@ -33,14 +31,6 @@ var Containers = React.createClass({ ContainerStore.on(ContainerStore.SERVER_CONTAINER_EVENT, this.update); ContainerStore.on(ContainerStore.CLIENT_CONTAINER_EVENT, this.updateFromClient); - console.log(this.state); - if (this.state.pending) { - console.log('pending'); - this.transitionTo('pull'); - } else if (this.state.sorted.length) { - this.transitionTo('containerHome', {name: this.state.sorted[0].Name}); - } - ipc.on('application:update-available', () => { this.setState({ updateAvailable: true @@ -52,44 +42,33 @@ var Containers = React.createClass({ ContainerStore.removeListener(ContainerStore.SERVER_CONTAINER_EVENT, this.update); ContainerStore.removeListener(ContainerStore.CLIENT_CONTAINER_EVENT, this.updateFromClient); }, - onDestroy: function () { - if (this.state.sorted.length) { - this.transitionTo('containerHome', {name: this.state.sorted[0].Name}); - } else { - this.transitionTo('containers'); - } - }, updateError: function (err) { this.setState({ error: err }); }, update: function (name, status) { + var sorted = ContainerStore.sorted(); this.setState({ containers: ContainerStore.containers(), - sorted: ContainerStore.sorted(), - downloading: ContainerStore.downloading(), - pending: ContainerStore.pending() + sorted: sorted, + pending: ContainerStore.pending(), + downloading: ContainerStore.downloading() }); if (status === 'destroy') { - this.onDestroy(); + if (sorted.length) { + this.transitionTo('containerHome', {name: sorted[0].Name}); + } else { + this.transitionTo('containers'); + } } }, updateFromClient: function (name, status) { - this.setState({ - containers: ContainerStore.containers(), - sorted: ContainerStore.sorted(), - downloading: ContainerStore.downloading(), - pending: ContainerStore.pending() - }); - console.log('update from client'); - - if (this.state.pending) { - this.transitionTo('pull'); - } else if (status === 'create') { + this.update(name, status); + if (status === 'create') { this.transitionTo('containerHome', {name: name}); - } else if (status === 'destroy') { - this.onDestroy(); + } else if (status === 'pending' && ContainerStore.pending()) { + this.transitionTo('pull'); } }, handleScroll: function (e) { @@ -172,17 +151,6 @@ var Containers = React.createClass({ ); } - var button; - if (this.state.downloading) { - button = ( - Only one Docker image can be downloaded at a time.}> - - - ); - } else { - button = ; - } - var container = this.getParams().name ? this.state.containers[this.getParams().name] : {}; return (
@@ -192,7 +160,7 @@ var Containers = React.createClass({

Containers

- {button} +
diff --git a/src/Main.js b/src/Main.js index 72588cbac0..729025ce88 100644 --- a/src/Main.js +++ b/src/Main.js @@ -51,16 +51,18 @@ ipc.on('application:open-url', opts => { ContainerStore.setPending(repo, 'latest'); } }); -ContainerStore.setPending('kitematic/ghost', 'latest'); SetupStore.setup().then(() => { + if (ContainerStore.pending()) { + router.transitionTo('pull'); + } else { + router.transitionTo('new'); + } Menu.setApplicationMenu(Menu.buildFromTemplate(template())); ContainerStore.on(ContainerStore.SERVER_ERROR_EVENT, (err) => { bugsnag.notify(err); }); - ContainerStore.init(function () { - router.transitionTo('containers'); - }); + ContainerStore.init(function () {}); }).catch(err => { metrics.track('Setup Failed', { step: 'catch', diff --git a/src/NewContainerPull.react.js b/src/NewContainerPull.react.js new file mode 100644 index 0000000000..3efd71aab5 --- /dev/null +++ b/src/NewContainerPull.react.js @@ -0,0 +1,42 @@ +var React = require('react/addons'); +var Router = require('react-router'); +var shell = require('shell'); +var ContainerStore = require('./ContainerStore'); + +module.exports = React.createClass({ + mixins: [Router.Navigation], + handleOpenClick: function () { + var repo = this.props.pending.repository; + if (repo.indexOf('/') === -1) { + shell.openExternal(`https://registry.hub.docker.com/_/${this.props.pending.repository}`); + } else { + shell.openExternal(`https://registry.hub.docker.com/u/${this.props.pending.repository}`); + } + }, + handleCancelClick: function () { + ContainerStore.clearPending(); + this.transitionTo('new'); + }, + handleConfirmClick: function () { + ContainerStore.clearPending(); + ContainerStore.create(this.props.pending.repository, this.props.pending.tag, function () {}); + }, + render: function () { + if (!this.props.pending) { + return false; + } + return ( +
+
+
+

You're about to download {this.props.pending.repository}.

+

Please confirm to create the container.

+ +
+
+
+ ); + } +}); diff --git a/src/NewContainer.react.js b/src/NewContainerSearch.react.js similarity index 95% rename from src/NewContainer.react.js rename to src/NewContainerSearch.react.js index eb8059a31a..3ac67f2882 100644 --- a/src/NewContainer.react.js +++ b/src/NewContainerSearch.react.js @@ -4,13 +4,13 @@ var React = require('react/addons'); var RetinaImage = require('react-retina-image'); var Radial = require('./Radial.react'); var ImageCard = require('./ImageCard.react'); -var metrics = require('./Metrics'); var Promise = require('bluebird'); +var metrics = require('./Metrics'); var _recommended = []; var _searchPromise = null; -var NewContainer = React.createClass({ +module.exports = React.createClass({ getInitialState: function () { return { query: '', @@ -47,7 +47,7 @@ var NewContainer = React.createClass({ loading: true }); - _searchPromise = Promise.delay(200).then(() => Promise.resolve($.get('https://registry.hub.docker.com/v1/search?q=' + query))).cancellable().then(data => { + _searchPromise = Promise.delay(200).cancellable().then(() => Promise.resolve($.get('https://registry.hub.docker.com/v1/search?q=' + query))).then(data => { metrics.track('Searched for Images'); this.setState({ results: data.results, @@ -142,7 +142,6 @@ var NewContainer = React.createClass({ 'icon-magnifier': true, 'search-icon': true }); - return (
@@ -167,5 +166,3 @@ var NewContainer = React.createClass({ ); } }); - -module.exports = NewContainer; diff --git a/src/Routes.js b/src/Routes.js index 05dd638a0e..cfdade8b51 100644 --- a/src/Routes.js +++ b/src/Routes.js @@ -9,13 +9,14 @@ var ContainerSettingsGeneral = require('./ContainerSettingsGeneral.react'); var ContainerSettingsPorts = require('./ContainerSettingsPorts.react'); var ContainerSettingsVolumes = require('./ContainerSettingsVolumes.react'); var Preferences = require('./Preferences.react'); -var NewContainer = require('./NewContainer.react'); -var PullContainer = require('./PullContainer.react'); +var NewContainerSearch = require('./NewContainerSearch.react'); +var NewContainerPull = require('./NewContainerPull.react'); var Router = require('react-router'); var Route = Router.Route; var DefaultRoute = Router.DefaultRoute; var RouteHandler = Router.RouteHandler; +var Redirect = Router.Redirect; var App = React.createClass({ render: function () { @@ -28,18 +29,21 @@ var App = React.createClass({ var routes = ( - - - - - - - + + + + + + + + + + + - - + diff --git a/styles/new-container.less b/styles/new-container.less index a00f9c3eb3..86b60d8903 100644 --- a/styles/new-container.less +++ b/styles/new-container.less @@ -1,3 +1,32 @@ +.new-container-pull { + display: flex; + flex: 1 auto; + align-items: center; + justify-content: center; + .content { + text-align: center; + + .buttons { + margin-top: 30px; + .btn { + margin-left: 10px; + margin-right: 10px; + padding: 8px 18px; + font-size: 14px; + background: white; + font-weight: 300; + } + } + } + h1 { + font-size: 20px; + color: @gray-normal; + font-weight: 400; + text-align: center; + margin-top: 10px; + } +} + .new-container { display: flex; flex: 1 auto; diff --git a/styles/theme.less b/styles/theme.less index 07e8cedd22..d20daccc76 100644 --- a/styles/theme.less +++ b/styles/theme.less @@ -134,7 +134,6 @@ input[type="text"] { font-weight: 400; text-shadow: none; padding: 5px 14px 5px 14px; - height: 30px; cursor: default; &.small { From 5a0fd8db6191b14792d7de073a72fc829cb442b0 Mon Sep 17 00:00:00 2001 From: Jeffrey Morgan Date: Tue, 7 Apr 2015 21:13:27 -0400 Subject: [PATCH 06/32] removing circle.yml --- circle.yml | 10 ---------- 1 file changed, 10 deletions(-) delete mode 100644 circle.yml diff --git a/circle.yml b/circle.yml deleted file mode 100644 index 9933147169..0000000000 --- a/circle.yml +++ /dev/null @@ -1,10 +0,0 @@ -machine: - node: - version: 0.10.36 -dependencies: - cache_directories: - - "resources" - - "node_modules" -notify: - webhooks: - - url: https://coveralls.io/webhook From 1b8bff01ccf5551a383d7994a66dc8bd24587f75 Mon Sep 17 00:00:00 2001 From: Jeffrey Morgan Date: Wed, 8 Apr 2015 16:26:20 -0400 Subject: [PATCH 07/32] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index bb6f6b105e..cd23272083 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ [![Build Status](https://travis-ci.org/kitematic/kitematic.svg?branch=master)](https://travis-ci.org/kitematic/kitematic) [![Coverage Status](https://coveralls.io/repos/kitematic/kitematic/badge.svg?branch=master)](https://coveralls.io/r/kitematic/kitematic?branch=master) -[![bitHound Score](https://app.bithound.io/kitematic/kitematic/badges/score.svg)](http://app.bithound.io/kitematic/kitematic) +[![bitHound Score](https://www.bithound.io/github/kitematic/kitematic/badges/score.svg)](https://www.bithound.io/github/kitematic/kitematic) [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/kitematic/kitematic?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) ![Kitematic Logo](https://cloud.githubusercontent.com/assets/251292/5269258/1b229c3c-7a2f-11e4-96f1-e7baf3c86d73.png) From bc4b37cffd6103e28e9f34f4c166127f2f34d1bc Mon Sep 17 00:00:00 2001 From: Jeffrey Morgan Date: Thu, 9 Apr 2015 19:57:17 -0400 Subject: [PATCH 08/32] Better log performance by capping at 3000 lines. Detach stream when log view is unmounted. Signed-off-by: Jeffrey Morgan --- src/ContainerHome.react.js | 4 +- src/ContainerHomeLogs.react.js | 48 ++++++++++----------- src/ContainerLogs.react.js | 37 +++++++--------- src/LogStore.js | 78 ++++++++++++++++++++-------------- 4 files changed, 87 insertions(+), 80 deletions(-) diff --git a/src/ContainerHome.react.js b/src/ContainerHome.react.js index 530e1b2668..a25fa678b5 100644 --- a/src/ContainerHome.react.js +++ b/src/ContainerHome.react.js @@ -112,7 +112,7 @@ var ContainerHome = React.createClass({
- +
@@ -138,7 +138,7 @@ var ContainerHome = React.createClass({
- +
{right}
diff --git a/src/ContainerHomeLogs.react.js b/src/ContainerHomeLogs.react.js index d400a48d72..4a471202ff 100644 --- a/src/ContainerHomeLogs.react.js +++ b/src/ContainerHomeLogs.react.js @@ -6,51 +6,50 @@ var metrics = require('./Metrics'); var _prevBottom = 0; -var ContainerHomeLogs = React.createClass({ - mixins: [Router.State, Router.Navigation], +module.exports = React.createClass({ + mixins: [Router.Navigation], getInitialState: function () { return { logs: [] }; }, - componentWillReceiveProps: function () { - this.init(); - }, componentDidMount: function() { - this.init(); - LogStore.on(LogStore.SERVER_LOGS_EVENT, this.updateLogs); + if (!this.props.container) { + return; + } + this.update(); + this.scrollToBottom(); + LogStore.on(LogStore.SERVER_LOGS_EVENT, this.update); + LogStore.fetch(this.props.container.Name); }, componentWillUnmount: function() { - LogStore.removeListener(LogStore.SERVER_LOGS_EVENT, this.updateLogs); + LogStore.detach(this.props.container.Name); + LogStore.removeListener(LogStore.SERVER_LOGS_EVENT, this.update); }, componentDidUpdate: function () { - // Scroll logs to bottom + this.scrollToBottom(); + }, + scrollToBottom: function () { var parent = $('.logs'); if (parent.scrollTop() >= _prevBottom - 50) { parent.scrollTop(parent[0].scrollHeight - parent.height()); } _prevBottom = parent[0].scrollHeight - parent.height(); }, - init: function () { - this.updateLogs(); - }, - updateLogs: function (name) { - if (name && name !== this.getParams().name) { - return; - } - this.setState({ - logs: LogStore.logs(this.getParams().name) - }); - }, handleClickLogs: function () { metrics.track('Viewed Logs', { from: 'preview' }); - this.transitionTo('containerLogs', {name: this.getParams().name}); + this.transitionTo('containerLogs', {name: this.props.container.Name}); + }, + update: function () { + this.setState({ + logs: LogStore.logs(this.props.container.Name) + }); }, render: function () { var logs = this.state.logs.map(function (l, i) { - return

; + return ; }); if (logs.length === 0) { logs = "No logs for this container."; @@ -62,11 +61,8 @@ var ContainerHomeLogs = React.createClass({
{logs}
-
View Logs
-
+
View Logs
); } }); - -module.exports = ContainerHomeLogs; diff --git a/src/ContainerLogs.react.js b/src/ContainerLogs.react.js index e20910cc49..f7b0b24064 100644 --- a/src/ContainerLogs.react.js +++ b/src/ContainerLogs.react.js @@ -1,49 +1,46 @@ var $ = require('jquery'); var React = require('react/addons'); var LogStore = require('./LogStore'); -var Router = require('react-router'); var _prevBottom = 0; -var ContainerLogs = React.createClass({ - mixins: [Router.State], +module.exports = React.createClass({ getInitialState: function () { return { logs: [] }; }, - componentWillReceiveProps: function () { - this.init(); - }, componentDidMount: function() { - this.init(); - LogStore.on(LogStore.SERVER_LOGS_EVENT, this.updateLogs); + if (!this.props.container) { + return; + } + this.update(); + this.scrollToBottom(); + LogStore.on(LogStore.SERVER_LOGS_EVENT, this.update); + LogStore.fetch(this.props.container.Name); }, componentWillUnmount: function() { - LogStore.removeListener(LogStore.SERVER_LOGS_EVENT, this.updateLogs); + LogStore.detach(this.props.container.Name); + LogStore.removeListener(LogStore.SERVER_LOGS_EVENT, this.update); }, componentDidUpdate: function () { - // Scroll logs to bottom + this.scrollToBottom(); + }, + scrollToBottom: function () { var parent = $('.details-logs'); if (parent.scrollTop() >= _prevBottom - 50) { parent.scrollTop(parent[0].scrollHeight - parent.height()); } _prevBottom = parent[0].scrollHeight - parent.height(); }, - init: function () { - this.updateLogs(); - }, - updateLogs: function (name) { - if (name && name !== this.getParams().name) { - return; - } + update: function () { this.setState({ - logs: LogStore.logs(this.getParams().name) + logs: LogStore.logs(this.props.container.Name) }); }, render: function () { var logs = this.state.logs.map(function (l, i) { - return

; + return ; }); if (logs.length === 0) { logs = "No logs for this container."; @@ -55,5 +52,3 @@ var ContainerLogs = React.createClass({ ); } }); - -module.exports = ContainerLogs; diff --git a/src/LogStore.js b/src/LogStore.js index 9735b5b98c..a42010131b 100644 --- a/src/LogStore.js +++ b/src/LogStore.js @@ -2,59 +2,77 @@ var EventEmitter = require('events').EventEmitter; var assign = require('object-assign'); var Convert = require('ansi-to-html'); var docker = require('./Docker'); +var stream = require('stream'); var _convert = new Convert(); var _logs = {}; var _streams = {}; -var LogStore = assign(Object.create(EventEmitter.prototype), { +var MAX_LOG_SIZE = 3000; + +module.exports = assign(Object.create(EventEmitter.prototype), { SERVER_LOGS_EVENT: 'server_logs_event', - _escapeHTML: function (html) { + _escape: function (html) { var text = document.createTextNode(html); var div = document.createElement('div'); div.appendChild(text); return div.innerHTML; }, - fetchLogs: function (name) { + fetch: function (name) { if (!name || !docker.client()) { return; } - var index = 0; - var self = this; docker.client().getContainer(name).logs({ - follow: true, stdout: true, stderr: true, - timestamps: true - }, function (err, stream) { - if (_streams[name]) { - return; - } - _streams[name] = stream; + timestamps: false, + tail: MAX_LOG_SIZE, + follow: false + }, (err, logStream) => { if (err) { - return; + throw err; } - _logs[name] = []; - stream.setEncoding('utf8'); - stream.on('data', function (buf) { - // Every other message is a header - if (index % 2 === 1) { - //var time = buf.substr(0,buf.indexOf(' ')); - var msg = buf.substr(buf.indexOf(' ')+1); - _logs[name].push(_convert.toHtml(self._escapeHTML(msg))); - self.emit(self.SERVER_LOGS_EVENT); - } - index += 1; + var logs = []; + logStream.setEncoding('utf-8'); + logStream.on('data', (chunk) => { + logs.push(_convert.toHtml(this._escape(chunk))); }); - stream.on('end', function () { - delete _streams[name]; + logStream.on('end', () => { + _logs[name] = logs; + this.emit(this.SERVER_LOGS_EVENT); + this.attach(name); }); }); }, - logs: function (name) { - if (!_streams[name]) { - this.fetchLogs(name); + attach: function (name) { + if (!name || !docker.client() || _streams[name]) { + return; } + docker.client().getContainer(name).attach({ + stdout: true, + stderr: true, + logs: false, + stream: true + }, (err, logStream) => { + if (err) { + throw err; + } + logStream.setEncoding('utf-8'); + logStream.on('data', (chunk) => { + _logs[name].push(_convert.toHtml(this._escape(chunk))); + if (_logs[name].length > MAX_LOG_SIZE) { + _logs[name] = _logs[name].slice(_logs[name].length - MAX_LOG_SIZE, MAX_LOG_SIZE); + } + this.emit(this.SERVER_LOGS_EVENT); + }); + }); + }, + detach: function (name) { + if (_streams[name]) { + _streams[name].destroy(); + } + }, + logs: function (name) { return _logs[name] || []; }, rename: function (name, newName) { @@ -63,5 +81,3 @@ var LogStore = assign(Object.create(EventEmitter.prototype), { } } }); - -module.exports = LogStore; From d746a5a24fdc47f8034306a65b2922de80a0838a Mon Sep 17 00:00:00 2001 From: Jeffrey Morgan Date: Thu, 9 Apr 2015 20:37:02 -0400 Subject: [PATCH 09/32] Update README.md --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index cd23272083..009717c23a 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ [![Build Status](https://travis-ci.org/kitematic/kitematic.svg?branch=master)](https://travis-ci.org/kitematic/kitematic) [![Coverage Status](https://coveralls.io/repos/kitematic/kitematic/badge.svg?branch=master)](https://coveralls.io/r/kitematic/kitematic?branch=master) [![bitHound Score](https://www.bithound.io/github/kitematic/kitematic/badges/score.svg)](https://www.bithound.io/github/kitematic/kitematic) -[![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/kitematic/kitematic?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) + ![Kitematic Logo](https://cloud.githubusercontent.com/assets/251292/5269258/1b229c3c-7a2f-11e4-96f1-e7baf3c86d73.png) @@ -27,9 +27,9 @@ Please read through our [Contributing Guidelines](https://github.com/kitematic/k ## Community -- For questions on how to use Kitematic, see our [user forum](https://forums.docker.com/c/kitematic). +- Join the Kitematic Gitter channel [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/kitematic/kitematic?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) +- Ask questions on our [user forum](https://forums.docker.com/c/kitematic). - **#kitematic** on IRC. [Join the channel](http://webchat.freenode.net/?channels=%23kitematic&uio=d4). -- Join the Kitematic [Gitter Channel](https://gitter.im/kitematic/kitematic) - Follow [@kitematic on Twitter](https://twitter.com/kitematic). - Read and subscribe to [the Kitematic Blog](http://blog.kitematic.com). From 33c8ca3ad0ab3a1b76184cf8258f71a071991a9e Mon Sep 17 00:00:00 2001 From: Jeffrey Morgan Date: Thu, 9 Apr 2015 20:37:18 -0400 Subject: [PATCH 10/32] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 009717c23a..72996ff18c 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ Please read through our [Contributing Guidelines](https://github.com/kitematic/k ## Community -- Join the Kitematic Gitter channel [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/kitematic/kitematic?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) +- [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/kitematic/kitematic?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) - Ask questions on our [user forum](https://forums.docker.com/c/kitematic). - **#kitematic** on IRC. [Join the channel](http://webchat.freenode.net/?channels=%23kitematic&uio=d4). - Follow [@kitematic on Twitter](https://twitter.com/kitematic). From b06999e36aa72fcf925b004d74ca085963b55dd1 Mon Sep 17 00:00:00 2001 From: Jeffrey Morgan Date: Sat, 11 Apr 2015 17:59:03 -0400 Subject: [PATCH 11/32] New click-to-pull protocol Signed-off-by: Jeffrey Morgan --- src/ContainerStore.js | 4 ++++ src/Main.js | 13 ++++++------- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/ContainerStore.js b/src/ContainerStore.js index 70de5b65a5..262ddc6a57 100644 --- a/src/ContainerStore.js +++ b/src/ContainerStore.js @@ -202,6 +202,10 @@ var ContainerStore = assign(Object.create(EventEmitter.prototype), { var data = JSON.parse(json); console.log(data); + if (data.status === 'pull' || data.status === 'untag' || data.status === 'delete') { + return; + } + // If the event is delete, remove the container if (data.status === 'destroy') { var container = _.findWhere(_.values(_containers), {Id: data.id}); diff --git a/src/Main.js b/src/Main.js index 729025ce88..9726bc67d0 100644 --- a/src/Main.js +++ b/src/Main.js @@ -34,7 +34,6 @@ ipc.on('application:quitting', opts => { }); ipc.on('application:open-url', opts => { - console.log('Creating container from protocol'); var parser = document.createElement('a'); parser.href = opts.url; @@ -43,12 +42,12 @@ ipc.on('application:open-url', opts => { } var pathname = parser.pathname.replace('//', ''); - var slash = pathname.indexOf('/'); - var base = pathname.slice(0, slash); - - if (base === 'runRepo') { - var repo = pathname.substring(slash + 1); - ContainerStore.setPending(repo, 'latest'); + var tokens = pathname.split('/'); + var type = tokens[0]; + var method = tokens[1]; + var repo = tokens.slice(2).join('/'); + if (type === 'repository' && method === 'run') { + ContainerStore.setPending(repo, parser.tag || 'latest'); } }); From 9772dbd22700dedf93c8d31f2b57c31c293d64de Mon Sep 17 00:00:00 2001 From: Jeffrey Morgan Date: Sun, 12 Apr 2015 16:51:37 -0400 Subject: [PATCH 12/32] Adding architecture guide to CONTRIBUTING.md --- CONTRIBUTING.md | 79 +++++++++++++++++++++++++++++++------------------ 1 file changed, 50 insertions(+), 29 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3fe22bdc17..d2c5ab75ab 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -6,29 +6,54 @@ Before you fil an issue or a pull request, quickly read of the following tips on ## Table of Contents - - [Development](#development) + - [Getting Started](#getting-started) + - [Architecture](#architecture) - [GitHub Issues](#github-issues) - [Pull Requests](#pull-requests) - [Code Guidelines](#code-guidelines) - [Testing](#testing) - [License](#license) -### Development - -- `npm install` - -To run the app in development: - -- `npm start` - -### Building & Release - -- `npm run release` - -### Unit Tests - -- `npm test` - +### Getting Started + +- `npm install` + +To run the app in development: + +- `npm start` + +### Building & Release + +- `npm run release` + +### Unit Tests + +- `npm test` + +## Architecture + +### Overview + +**Note: This architecture is work in progress and doesn't reflect the current state of the app, yet!** + +Kitematic is an application built using [atom-shell](https://github.com/atom/atom-shell) and is powered by the [Docker Engine](https://github.com/docker/docker). While it's work in progress, the goal is to make Kitematic a high-performance, portable Javascript ES6 application built with React and Reflux. It adopts a single data flow pattern: + +``` +╔═════════╗ ╔════════╗ ╔═════════════════╗ +║ Actions ║──────>║ Stores ║──────>║ View Components ║ +╚═════════╝ ╚════════╝ ╚═════════════════╝ + ^ │ + └──────────────────────────────────────┘ +``` + +As explained in the [Reflux](https://github.com/spoike/refluxjs), there are three primary types of objects: +- **Actions**: The main logic workhorses of the application. These objects interact with the Docker API and other endpoints to fetch new data and flowing it into the stores, which in turn create events that cause views to update. +- **Views**: Views make up the UI, and trigger available actions. +- **Stores**: Stores store the state of the application. + +### Guidelines + +- Avoid asynchronous code in Stores or Views. Instead, put code involving callbacks, promises or generators in actions. ## GitHub Issues @@ -54,9 +79,15 @@ 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 +- Includes tests - [Signed Off](https://github.com/docker/docker/blob/master/CONTRIBUTING.md#sign-your-work) +## Testing + +Please try to test any new code. +- Tests can be run using `npm test` +- Kitematic uses the [Jest framework](https://facebook.github.io/jest/) by Facebook. To keep tests fast, please mock as much as possible. + ## Code Guidelines ### Javascript @@ -70,17 +101,7 @@ Kitematic is es6 ready. Please use es6 constructs where possible, they are power Run `npm run lint` before committing to ensure your javascript is up to standard. Feel free to suggest changes to the lint spec in `.jshint`. -### React - -- Use tags and elements appropriate for React / an HTML5 doctype (e.g., self-closing tags) -- Try to avoid using JQuery or manually changing the DOM. Use React instead. -- Try to build self-contained components that listen and emit events. This is definitely nowhere near perfect yet for the existing codebase. - -## Testing - -While the project is early, please try to test any new code. -- Tests can be run using `npm test` -- Kitematic uses the [Jest framework](https://facebook.github.io/jest/) by Facebook. To keep tests fast, please mock as much as possible. +We designed Kitematic to be easy to build, extend and distribute for developers. ## License From 2e330fd97bf3b0d81171774f59b59bd518823e08 Mon Sep 17 00:00:00 2001 From: Jeffrey Morgan Date: Sun, 12 Apr 2015 16:52:08 -0400 Subject: [PATCH 13/32] Update CONTRIBUTING.md --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d2c5ab75ab..f0a4c54814 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -46,7 +46,7 @@ Kitematic is an application built using [atom-shell](https://github.com/atom/ato └──────────────────────────────────────┘ ``` -As explained in the [Reflux](https://github.com/spoike/refluxjs), there are three primary types of objects: +As explained in the [Reflux docs](https://github.com/spoike/refluxjs), there are three primary types of objects: - **Actions**: The main logic workhorses of the application. These objects interact with the Docker API and other endpoints to fetch new data and flowing it into the stores, which in turn create events that cause views to update. - **Views**: Views make up the UI, and trigger available actions. - **Stores**: Stores store the state of the application. From d4f18763a1a69aa12f5c03ccc9e083337df0782b Mon Sep 17 00:00:00 2001 From: Jeffrey Morgan Date: Mon, 13 Apr 2015 12:47:23 -0400 Subject: [PATCH 14/32] Fixing bug where logs would include headers Signed-off-by: Jeffrey Morgan --- src/LogStore.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/LogStore.js b/src/LogStore.js index a42010131b..fa14b34e11 100644 --- a/src/LogStore.js +++ b/src/LogStore.js @@ -33,8 +33,9 @@ module.exports = assign(Object.create(EventEmitter.prototype), { throw err; } var logs = []; - logStream.setEncoding('utf-8'); - logStream.on('data', (chunk) => { + var outstream = new stream.PassThrough(); + docker.client().modem.demuxStream(logStream, outstream, outstream); + outstream.on('data', (chunk) => { logs.push(_convert.toHtml(this._escape(chunk))); }); logStream.on('end', () => { @@ -57,8 +58,9 @@ module.exports = assign(Object.create(EventEmitter.prototype), { if (err) { throw err; } - logStream.setEncoding('utf-8'); - logStream.on('data', (chunk) => { + var outstream = new stream.PassThrough(); + docker.client().modem.demuxStream(logStream, outstream, outstream); + outstream.on('data', (chunk) => { _logs[name].push(_convert.toHtml(this._escape(chunk))); if (_logs[name].length > MAX_LOG_SIZE) { _logs[name] = _logs[name].slice(_logs[name].length - MAX_LOG_SIZE, MAX_LOG_SIZE); From 6679f9a85113c4e1246d5fe590572f0ae67c76d1 Mon Sep 17 00:00:00 2001 From: Jeffrey Morgan Date: Tue, 14 Apr 2015 13:26:27 -0400 Subject: [PATCH 15/32] Check URL parameters via regexp Signed-off-by: Jeffrey Morgan --- src/ContainerHomeFolders.react.js | 34 +++++++++++++++++++------------ src/Main.js | 15 ++++++++++---- src/NewContainerPull.react.js | 2 +- 3 files changed, 33 insertions(+), 18 deletions(-) diff --git a/src/ContainerHomeFolders.react.js b/src/ContainerHomeFolders.react.js index 1ace6d4e90..2e3e796cc3 100644 --- a/src/ContainerHomeFolders.react.js +++ b/src/ContainerHomeFolders.react.js @@ -7,6 +7,7 @@ var util = require('./Util'); var metrics = require('./Metrics'); var Router = require('react-router'); var ContainerStore = require('./ContainerStore'); +var dialog = require('remote').require('dialog'); var ContainerHomeFolder = React.createClass({ mixins: [Router.State, Router.Navigation], @@ -16,20 +17,27 @@ var ContainerHomeFolder = React.createClass({ }); 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) { - return pair[1] + ':' + pair[0]; - }); - ContainerStore.updateContainer(this.props.container.Name, { - Binds: binds - }, function (err) { - if (err) { - console.log(err); - return; + 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(), 'Kitematic', this.props.container.Name, containerVolume); + volumes[containerVolume] = newHostVolume; + var binds = _.pairs(volumes).map(function (pair) { + return pair[1] + ':' + pair[0]; + }); + ContainerStore.updateContainer(this.props.container.Name, { + Binds: binds + }, (err) => { + if (err) { + console.log(err); + return; + } + shell.showItemInFolder(newHostVolume); + }); } - shell.showItemInFolder(newHostVolume); }); } else { shell.showItemInFolder(hostVolume); diff --git a/src/Main.js b/src/Main.js index 9726bc67d0..4400f3ee29 100644 --- a/src/Main.js +++ b/src/Main.js @@ -34,6 +34,8 @@ ipc.on('application:quitting', opts => { }); ipc.on('application:open-url', opts => { + var repoRegexp = /[a-z0-9]+(?:[._-][a-z0-9]+)*/; + var tagRegexp = /[\w][\w.-]{0,127}/; var parser = document.createElement('a'); parser.href = opts.url; @@ -44,10 +46,15 @@ ipc.on('application:open-url', opts => { var pathname = parser.pathname.replace('//', ''); var tokens = pathname.split('/'); var type = tokens[0]; - var method = tokens[1]; - var repo = tokens.slice(2).join('/'); - if (type === 'repository' && method === 'run') { - ContainerStore.setPending(repo, parser.tag || 'latest'); + var repo = tokens.slice(1).join('/'); + var tag = parser.tag || 'latest'; + + if (repo.indexOf('/') !== -1 || !repoRegexp.test(repo) || !tagRegexp.test(tag)) { + return; + } + + if (type === 'repository') { + ContainerStore.setPending(repo, tag); } }); diff --git a/src/NewContainerPull.react.js b/src/NewContainerPull.react.js index 3efd71aab5..bc915b4dda 100644 --- a/src/NewContainerPull.react.js +++ b/src/NewContainerPull.react.js @@ -29,7 +29,7 @@ module.exports = React.createClass({
-

You're about to download {this.props.pending.repository}.

+

You're about to download and run {this.props.pending.repository}:{this.props.pending.tag}.

Please confirm to create the container.

Cancel Confirm From e4491bd8999796305a4b0b31a2e8e72393d89041 Mon Sep 17 00:00:00 2001 From: Jeffrey Morgan Date: Wed, 15 Apr 2015 13:15:44 -0400 Subject: [PATCH 16/32] Adding 'run' method back Signed-off-by: Jeffrey Morgan --- src/Main.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Main.js b/src/Main.js index 4400f3ee29..aa6ab5abdb 100644 --- a/src/Main.js +++ b/src/Main.js @@ -46,14 +46,15 @@ ipc.on('application:open-url', opts => { var pathname = parser.pathname.replace('//', ''); var tokens = pathname.split('/'); var type = tokens[0]; - var repo = tokens.slice(1).join('/'); + var method = tokens[1]; + var repo = tokens.slice(2).join('/'); var tag = parser.tag || 'latest'; if (repo.indexOf('/') !== -1 || !repoRegexp.test(repo) || !tagRegexp.test(tag)) { return; } - if (type === 'repository') { + if (type === 'repository' && method === 'run') { ContainerStore.setPending(repo, tag); } }); From ebc1143723dc26dc9dddbac3b6ef4429a584d610 Mon Sep 17 00:00:00 2001 From: Jeffrey Morgan Date: Wed, 15 Apr 2015 15:28:50 -0400 Subject: [PATCH 17/32] removing tag support for custom url --- src/Main.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/Main.js b/src/Main.js index aa6ab5abdb..18d2819264 100644 --- a/src/Main.js +++ b/src/Main.js @@ -35,7 +35,6 @@ ipc.on('application:quitting', opts => { ipc.on('application:open-url', opts => { var repoRegexp = /[a-z0-9]+(?:[._-][a-z0-9]+)*/; - var tagRegexp = /[\w][\w.-]{0,127}/; var parser = document.createElement('a'); parser.href = opts.url; @@ -48,14 +47,13 @@ ipc.on('application:open-url', opts => { var type = tokens[0]; var method = tokens[1]; var repo = tokens.slice(2).join('/'); - var tag = parser.tag || 'latest'; - if (repo.indexOf('/') !== -1 || !repoRegexp.test(repo) || !tagRegexp.test(tag)) { + if (repo.indexOf('/') !== -1 || !repoRegexp.test(repo)) { return; } if (type === 'repository' && method === 'run') { - ContainerStore.setPending(repo, tag); + ContainerStore.setPending(repo, 'latest'); } }); From 4b1291c952f740c0c94c54cb325a486db70e9e1d Mon Sep 17 00:00:00 2001 From: Jeffrey Morgan Date: Wed, 15 Apr 2015 15:46:45 -0400 Subject: [PATCH 18/32] Add comment for supported repos --- src/Main.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Main.js b/src/Main.js index 18d2819264..44235be0bc 100644 --- a/src/Main.js +++ b/src/Main.js @@ -48,6 +48,7 @@ ipc.on('application:open-url', opts => { var method = tokens[1]; var repo = tokens.slice(2).join('/'); + // Only accept official repos for now if (repo.indexOf('/') !== -1 || !repoRegexp.test(repo)) { return; } From ce314020cece500d9ff460f916364f8a9d8a5db9 Mon Sep 17 00:00:00 2001 From: Jeffrey Morgan Date: Wed, 15 Apr 2015 17:17:59 -0400 Subject: [PATCH 19/32] Bump atom shell version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 057aac3a84..43c981279e 100644 --- a/package.json +++ b/package.json @@ -47,7 +47,7 @@ }, "docker-version": "1.5.0", "docker-machine-version": "0.1.0-kitematic-0.5.10", - "atom-shell-version": "0.21.3", + "atom-shell-version": "0.23.0", "virtualbox-version": "4.3.26", "virtualbox-filename": "VirtualBox-4.3.26.pkg", "virtualbox-checksum": "668f61c95efe37f8fc65cafe95b866fba64e37f2492dfc1e2b44a7ac3dcafa3b", From 0875ecd4b30c8e2cbee0abcf4beef1a3e7838dfc Mon Sep 17 00:00:00 2001 From: Jeffrey Morgan Date: Wed, 15 Apr 2015 17:44:36 -0400 Subject: [PATCH 20/32] Removing unused file Signed-off-by: Jeffrey Morgan --- src/PullContainer.react.js | 16 ---------------- 1 file changed, 16 deletions(-) delete mode 100644 src/PullContainer.react.js diff --git a/src/PullContainer.react.js b/src/PullContainer.react.js deleted file mode 100644 index 0fa81925ee..0000000000 --- a/src/PullContainer.react.js +++ /dev/null @@ -1,16 +0,0 @@ -var React = require('react/addons'); - -var PullContainer = React.createClass({ - componentDidMount: function () { - }, - render: function () { - console.log(this.props.pending); - return ( -
- test -
- ); - } -}); - -module.exports = PullContainer; From 61a03c3a7d4b47c335d036a43999b408d3645a7b Mon Sep 17 00:00:00 2001 From: Jeffrey Morgan Date: Thu, 16 Apr 2015 13:58:07 -0400 Subject: [PATCH 21/32] Revert "Adding Custom URL Handler for running containers" --- .jshintrc | 2 +- src/ContainerHomeFolders.react.js | 34 +++++-------- src/ContainerList.react.js | 8 +++- src/ContainerStore.js | 20 +------- src/Containers.react.js | 48 ++++++++++++++----- src/Main.js | 46 ++++-------------- ...rSearch.react.js => NewContainer.react.js} | 6 ++- src/NewContainerPull.react.js | 42 ---------------- src/Routes.js | 24 ++++------ src/browser.js | 19 -------- styles/new-container.less | 29 ----------- styles/theme.less | 1 + util/Info.plist | 11 ----- 13 files changed, 80 insertions(+), 210 deletions(-) rename src/{NewContainerSearch.react.js => NewContainer.react.js} (95%) delete mode 100644 src/NewContainerPull.react.js diff --git a/.jshintrc b/.jshintrc index 9240a3ae9f..daeaf45072 100644 --- a/.jshintrc +++ b/.jshintrc @@ -27,5 +27,5 @@ "jest": true, "pit": true }, - "predef": [ "-Promise" ] + "predef": [ "Promise" ] } diff --git a/src/ContainerHomeFolders.react.js b/src/ContainerHomeFolders.react.js index 2e3e796cc3..1ace6d4e90 100644 --- a/src/ContainerHomeFolders.react.js +++ b/src/ContainerHomeFolders.react.js @@ -7,7 +7,6 @@ var util = require('./Util'); var metrics = require('./Metrics'); var Router = require('react-router'); var ContainerStore = require('./ContainerStore'); -var dialog = require('remote').require('dialog'); var ContainerHomeFolder = React.createClass({ mixins: [Router.State, Router.Navigation], @@ -17,27 +16,20 @@ var ContainerHomeFolder = React.createClass({ }); if (hostVolume.indexOf(process.env.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(), 'Kitematic', this.props.container.Name, containerVolume); - volumes[containerVolume] = newHostVolume; - var binds = _.pairs(volumes).map(function (pair) { - return pair[1] + ':' + pair[0]; - }); - ContainerStore.updateContainer(this.props.container.Name, { - Binds: binds - }, (err) => { - if (err) { - console.log(err); - return; - } - shell.showItemInFolder(newHostVolume); - }); + 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) { + 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); diff --git a/src/ContainerList.react.js b/src/ContainerList.react.js index 1e6f358d6d..d2954dc603 100644 --- a/src/ContainerList.react.js +++ b/src/ContainerList.react.js @@ -19,9 +19,15 @@ var ContainerList = React.createClass({ ); }); + var newItem; + if (!this.props.downloading) { + newItem = ; + } else { + newItem = ''; + } return (
    - + {newItem} {containers}
); diff --git a/src/ContainerStore.js b/src/ContainerStore.js index 262ddc6a57..109a022d19 100644 --- a/src/ContainerStore.js +++ b/src/ContainerStore.js @@ -14,7 +14,6 @@ var _progress = {}; var _muted = {}; var _blocked = {}; var _error = null; -var _pending = null; var ContainerStore = assign(Object.create(EventEmitter.prototype), { CLIENT_CONTAINER_EVENT: 'client_container_event', @@ -202,10 +201,6 @@ var ContainerStore = assign(Object.create(EventEmitter.prototype), { var data = JSON.parse(json); console.log(data); - if (data.status === 'pull' || data.status === 'untag' || data.status === 'delete') { - return; - } - // If the event is delete, remove the container if (data.status === 'destroy') { var container = _.findWhere(_.values(_containers), {Id: data.id}); @@ -231,6 +226,7 @@ var ContainerStore = assign(Object.create(EventEmitter.prototype), { } }, init: function (callback) { + // TODO: Load cached data from db on loading this.fetchAllContainers(err => { if (err) { _error = err; @@ -475,20 +471,6 @@ var ContainerStore = assign(Object.create(EventEmitter.prototype), { }, downloading: function () { return !!_.keys(_placeholders).length; - }, - pending: function () { - return _pending; - }, - setPending: function (repository, tag) { - _pending = { - repository: repository, - tag: tag - }; - this.emit(this.CLIENT_CONTAINER_EVENT, null, 'pending'); - }, - clearPending: function () { - _pending = null; - this.emit(this.CLIENT_CONTAINER_EVENT, null, 'pending'); } }); diff --git a/src/Containers.react.js b/src/Containers.react.js index ab613bb61a..af151dabca 100644 --- a/src/Containers.react.js +++ b/src/Containers.react.js @@ -10,6 +10,8 @@ var metrics = require('./Metrics'); var autoUpdater = remote.require('auto-updater'); var RetinaImage = require('react-retina-image'); var machine = require('./DockerMachine'); +var OverlayTrigger = require('react-bootstrap').OverlayTrigger; +var Tooltip = require('react-bootstrap').Tooltip; var util = require('./Util'); var Containers = React.createClass({ @@ -31,6 +33,10 @@ var Containers = React.createClass({ ContainerStore.on(ContainerStore.SERVER_CONTAINER_EVENT, this.update); ContainerStore.on(ContainerStore.CLIENT_CONTAINER_EVENT, this.updateFromClient); + if (this.state.sorted.length) { + this.transitionTo('containerHome', {name: this.state.sorted[0].Name}); + } + ipc.on('application:update-available', () => { this.setState({ updateAvailable: true @@ -42,33 +48,38 @@ var Containers = React.createClass({ ContainerStore.removeListener(ContainerStore.SERVER_CONTAINER_EVENT, this.update); ContainerStore.removeListener(ContainerStore.CLIENT_CONTAINER_EVENT, this.updateFromClient); }, + onDestroy: function () { + if (this.state.sorted.length) { + this.transitionTo('containerHome', {name: this.state.sorted[0].Name}); + } else { + this.transitionTo('containers'); + } + }, updateError: function (err) { this.setState({ error: err }); }, update: function (name, status) { - var sorted = ContainerStore.sorted(); this.setState({ containers: ContainerStore.containers(), - sorted: sorted, - pending: ContainerStore.pending(), + sorted: ContainerStore.sorted(), downloading: ContainerStore.downloading() }); if (status === 'destroy') { - if (sorted.length) { - this.transitionTo('containerHome', {name: sorted[0].Name}); - } else { - this.transitionTo('containers'); - } + this.onDestroy(); } }, updateFromClient: function (name, status) { - this.update(name, status); + this.setState({ + containers: ContainerStore.containers(), + sorted: ContainerStore.sorted(), + downloading: ContainerStore.downloading() + }); if (status === 'create') { this.transitionTo('containerHome', {name: name}); - } else if (status === 'pending' && ContainerStore.pending()) { - this.transitionTo('pull'); + } else if (status === 'destroy') { + this.onDestroy(); } }, handleScroll: function (e) { @@ -151,6 +162,17 @@ var Containers = React.createClass({ ); } + var button; + if (this.state.downloading) { + button = ( + Only one Docker image can be downloaded at a time.}> + + + ); + } else { + button = ; + } + var container = this.getParams().name ? this.state.containers[this.getParams().name] : {}; return (
@@ -160,7 +182,7 @@ var Containers = React.createClass({

Containers

- + {button}
@@ -175,7 +197,7 @@ var Containers = React.createClass({
- +
); diff --git a/src/Main.js b/src/Main.js index 44235be0bc..9f582f3588 100644 --- a/src/Main.js +++ b/src/Main.js @@ -27,48 +27,14 @@ setInterval(function () { router.run(Handler => React.render(, document.body)); -ipc.on('application:quitting', opts => { - if (!opts.updating && localStorage.getItem('settings.closeVMOnQuit') === 'true') { - machine.stop(); - } -}); - -ipc.on('application:open-url', opts => { - var repoRegexp = /[a-z0-9]+(?:[._-][a-z0-9]+)*/; - var parser = document.createElement('a'); - parser.href = opts.url; - - if (parser.protocol !== 'docker:') { - return; - } - - var pathname = parser.pathname.replace('//', ''); - var tokens = pathname.split('/'); - var type = tokens[0]; - var method = tokens[1]; - var repo = tokens.slice(2).join('/'); - - // Only accept official repos for now - if (repo.indexOf('/') !== -1 || !repoRegexp.test(repo)) { - return; - } - - if (type === 'repository' && method === 'run') { - ContainerStore.setPending(repo, 'latest'); - } -}); - SetupStore.setup().then(() => { - if (ContainerStore.pending()) { - router.transitionTo('pull'); - } else { - router.transitionTo('new'); - } Menu.setApplicationMenu(Menu.buildFromTemplate(template())); ContainerStore.on(ContainerStore.SERVER_ERROR_EVENT, (err) => { bugsnag.notify(err); }); - ContainerStore.init(function () {}); + ContainerStore.init(function () { + router.transitionTo('containers'); + }); }).catch(err => { metrics.track('Setup Failed', { step: 'catch', @@ -77,3 +43,9 @@ SetupStore.setup().then(() => { console.log(err); bugsnag.notify(err); }); + +ipc.on('application:quitting', opts => { + if (!opts.updating && localStorage.getItem('settings.closeVMOnQuit') === 'true') { + machine.stop(); + } +}); diff --git a/src/NewContainerSearch.react.js b/src/NewContainer.react.js similarity index 95% rename from src/NewContainerSearch.react.js rename to src/NewContainer.react.js index 3ac67f2882..05e772cedd 100644 --- a/src/NewContainerSearch.react.js +++ b/src/NewContainer.react.js @@ -10,7 +10,7 @@ var metrics = require('./Metrics'); var _recommended = []; var _searchPromise = null; -module.exports = React.createClass({ +var NewContainer = React.createClass({ getInitialState: function () { return { query: '', @@ -47,7 +47,7 @@ module.exports = React.createClass({ loading: true }); - _searchPromise = Promise.delay(200).cancellable().then(() => Promise.resolve($.get('https://registry.hub.docker.com/v1/search?q=' + query))).then(data => { + _searchPromise = Promise.delay(200).then(() => Promise.resolve($.get('https://registry.hub.docker.com/v1/search?q=' + query))).cancellable().then(data => { metrics.track('Searched for Images'); this.setState({ results: data.results, @@ -166,3 +166,5 @@ module.exports = React.createClass({ ); } }); + +module.exports = NewContainer; diff --git a/src/NewContainerPull.react.js b/src/NewContainerPull.react.js deleted file mode 100644 index bc915b4dda..0000000000 --- a/src/NewContainerPull.react.js +++ /dev/null @@ -1,42 +0,0 @@ -var React = require('react/addons'); -var Router = require('react-router'); -var shell = require('shell'); -var ContainerStore = require('./ContainerStore'); - -module.exports = React.createClass({ - mixins: [Router.Navigation], - handleOpenClick: function () { - var repo = this.props.pending.repository; - if (repo.indexOf('/') === -1) { - shell.openExternal(`https://registry.hub.docker.com/_/${this.props.pending.repository}`); - } else { - shell.openExternal(`https://registry.hub.docker.com/u/${this.props.pending.repository}`); - } - }, - handleCancelClick: function () { - ContainerStore.clearPending(); - this.transitionTo('new'); - }, - handleConfirmClick: function () { - ContainerStore.clearPending(); - ContainerStore.create(this.props.pending.repository, this.props.pending.tag, function () {}); - }, - render: function () { - if (!this.props.pending) { - return false; - } - return ( -
-
-
-

You're about to download and run {this.props.pending.repository}:{this.props.pending.tag}.

-

Please confirm to create the container.

- -
-
-
- ); - } -}); diff --git a/src/Routes.js b/src/Routes.js index cfdade8b51..eab468fa1c 100644 --- a/src/Routes.js +++ b/src/Routes.js @@ -9,14 +9,12 @@ var ContainerSettingsGeneral = require('./ContainerSettingsGeneral.react'); var ContainerSettingsPorts = require('./ContainerSettingsPorts.react'); var ContainerSettingsVolumes = require('./ContainerSettingsVolumes.react'); var Preferences = require('./Preferences.react'); -var NewContainerSearch = require('./NewContainerSearch.react'); -var NewContainerPull = require('./NewContainerPull.react'); +var NewContainer = require('./NewContainer.react'); var Router = require('react-router'); var Route = Router.Route; var DefaultRoute = Router.DefaultRoute; var RouteHandler = Router.RouteHandler; -var Redirect = Router.Redirect; var App = React.createClass({ render: function () { @@ -29,21 +27,17 @@ var App = React.createClass({ var routes = ( - - - - - - - + + + + + + + - - - - - + diff --git a/src/browser.js b/src/browser.js index ee7810450a..8722673dcb 100644 --- a/src/browser.js +++ b/src/browser.js @@ -18,13 +18,6 @@ try { settingsjson = JSON.parse(fs.readFileSync(path.join(__dirname, '..', 'settings.json'), 'utf8')); } catch (err) {} - -var openURL = null; -app.on('open-url', function (event, url) { - event.preventDefault(); - openURL = url; -}); - app.on('ready', function () { var mainWindow = new BrowserWindow({ width: size.width || 1000, @@ -72,18 +65,6 @@ app.on('ready', function () { mainWindow.show(); mainWindow.focus(); - if (openURL) { - mainWindow.webContents.send('application:open-url', { - url: openURL - }); - } - app.on('open-url', function (event, url) { - event.preventDefault(); - mainWindow.webContents.send('application:open-url', { - url: url - }); - }); - if (process.env.NODE_ENV !== 'development') { autoUpdater.setFeedUrl('https://updates.kitematic.com/releases/latest?version=' + app.getVersion() + '&beta=' + !!settingsjson.beta); } diff --git a/styles/new-container.less b/styles/new-container.less index 86b60d8903..a00f9c3eb3 100644 --- a/styles/new-container.less +++ b/styles/new-container.less @@ -1,32 +1,3 @@ -.new-container-pull { - display: flex; - flex: 1 auto; - align-items: center; - justify-content: center; - .content { - text-align: center; - - .buttons { - margin-top: 30px; - .btn { - margin-left: 10px; - margin-right: 10px; - padding: 8px 18px; - font-size: 14px; - background: white; - font-weight: 300; - } - } - } - h1 { - font-size: 20px; - color: @gray-normal; - font-weight: 400; - text-align: center; - margin-top: 10px; - } -} - .new-container { display: flex; flex: 1 auto; diff --git a/styles/theme.less b/styles/theme.less index d20daccc76..07e8cedd22 100644 --- a/styles/theme.less +++ b/styles/theme.less @@ -134,6 +134,7 @@ input[type="text"] { font-weight: 400; text-shadow: none; padding: 5px 14px 5px 14px; + height: 30px; cursor: default; &.small { diff --git a/util/Info.plist b/util/Info.plist index 5efd29e633..396be69183 100644 --- a/util/Info.plist +++ b/util/Info.plist @@ -26,16 +26,5 @@ AtomApplication NSSupportsAutomaticGraphicsSwitching - CFBundleURLTypes - - - CFBundleURLSchemes - - docker - - CFBundleURLName - Docker App Protocol - - From 98000359baee0cde8098b851e86f58bbe670671a Mon Sep 17 00:00:00 2001 From: Jeffrey Morgan Date: Thu, 16 Apr 2015 16:29:18 -0400 Subject: [PATCH 22/32] bumping to 0.5.14 with docker 1.6 --- gulpfile.js | 1 + package.json | 8 ++++---- src/SetupStore.js | 5 ----- util/deps | 4 +--- 4 files changed, 6 insertions(+), 12 deletions(-) diff --git a/gulpfile.js b/gulpfile.js index 2836301e15..1146a693e2 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -106,6 +106,7 @@ gulp.task('dist', function () { 'mkdir -p dist/osx/<%= filename %>/Contents/Resources/app/resources', 'cp -v resources/* dist/osx/<%= filename %>/Contents/Resources/app/resources/ || :', 'cp <%= icon %> dist/osx/<%= filename %>/Contents/Resources/atom.icns', + 'cp ./util/Info.plist dist/osx/<%= filename %>/Contents/Info.plist', '/usr/libexec/PlistBuddy -c "Set :CFBundleVersion <%= version %>" dist/osx/<%= filename %>/Contents/Info.plist', '/usr/libexec/PlistBuddy -c "Set :CFBundleDisplayName <%= name %>" dist/osx/<%= filename %>/Contents/Info.plist', '/usr/libexec/PlistBuddy -c "Set :CFBundleName <%= name %>" dist/osx/<%= filename %>/Contents/Info.plist', diff --git a/package.json b/package.json index 43c981279e..ec6a219f2d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "Kitematic", - "version": "0.5.13", + "version": "0.5.14", "author": "Kitematic", "description": "Simple Docker Container management for Mac OS X.", "homepage": "https://kitematic.com/", @@ -16,7 +16,7 @@ "release": "gulp release", "release:beta": "gulp release --beta", "preinstall": "./util/deps", - "postinstall": "if [ `uname` == 'Darwin' ]; then gulp download && cp util/Info.plist cache/Atom.app/Contents/Info.plist && cp util/kitematic.icns cache/Atom.app/Contents/Resources/atom.icns; fi", + "postinstall": "gulp download", "lint": "jsxhint src && jsxhint browser" }, "licenses": [ @@ -45,8 +45,8 @@ "/node_modules/bluebird" ] }, - "docker-version": "1.5.0", - "docker-machine-version": "0.1.0-kitematic-0.5.10", + "docker-version": "1.6.0", + "docker-machine-version": "0.2.0", "atom-shell-version": "0.23.0", "virtualbox-version": "4.3.26", "virtualbox-filename": "VirtualBox-4.3.26.pkg", diff --git a/src/SetupStore.js b/src/SetupStore.js index 981b007ae1..c4bab1ff0c 100644 --- a/src/SetupStore.js +++ b/src/SetupStore.js @@ -77,10 +77,6 @@ var _steps = [{ return; } - if ((yield machine.state()) === 'Saved') { - yield virtualBox.wake(machine.name()); - } - var isoversion = machine.isoversion(); var packagejson = util.packagejson(); if (!isoversion || setupUtil.compareVersions(isoversion, packagejson['docker-version']) < 0) { @@ -89,7 +85,6 @@ var _steps = [{ } if ((yield machine.state()) !== 'Running') { yield machine.start(); - yield machine.regenerateCerts(); } }) }]; diff --git a/util/deps b/util/deps index 4bd6a249bc..ecd9322500 100755 --- a/util/deps +++ b/util/deps @@ -21,9 +21,7 @@ fi if [ ! -f $DOCKER_MACHINE_CLI_FILE ]; then echo "-----> Downloading Docker Machine CLI..." rm -rf docker-machine* - # Use temporary, new version of docker-machine that has some important fixes - # curl -L -o $DOCKER_MACHINE_CLI_FILE https://github.com/docker/machine/releases/download/v$DOCKER_MACHINE_CLI_VERSION/docker-machine_darwin-amd64 - curl -L -o $DOCKER_MACHINE_CLI_FILE https://github.com/kitematic/kitematic/releases/download/v0.5.10/docker-machine_darwin-amd64 + curl -L -o $DOCKER_MACHINE_CLI_FILE https://github.com/docker/machine/releases/download/v$DOCKER_MACHINE_CLI_VERSION/docker-machine_darwin-amd64 chmod +x $DOCKER_MACHINE_CLI_FILE fi From 3cf1a378359c5ce107a5cf42c7d4a5d7c214dd87 Mon Sep 17 00:00:00 2001 From: Jeffrey Morgan Date: Thu, 16 Apr 2015 16:47:51 -0400 Subject: [PATCH 23/32] fixing test --- src/SetupStore-test.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/SetupStore-test.js b/src/SetupStore-test.js index 42add6fd37..47a07968a6 100644 --- a/src/SetupStore-test.js +++ b/src/SetupStore-test.js @@ -76,19 +76,16 @@ describe('SetupStore', function () { machine.isoversion.mockReturnValue('1.0'); machine.stop.mockReturnValue(Promise.resolve()); machine.start.mockReturnValue(Promise.resolve()); - machine.regenerateCerts.mockReturnValue(Promise.resolve()); machine.upgrade.mockReturnValue(Promise.resolve()); setupUtil.compareVersions.mockReturnValue(-1); machine.create.mockClear(); machine.upgrade.mockClear(); machine.stop.mockClear(); machine.start.mockClear(); - machine.regenerateCerts.mockClear(); return setupStore.steps().init.run(() => {}).then(() => { expect(machine.create).not.toBeCalled(); expect(machine.stop).toBeCalled(); expect(machine.upgrade).toBeCalled(); - expect(machine.regenerateCerts).toBeCalled(); expect(machine.start).toBeCalled(); }); }); From f4c87f63c921a635f73072ce9823cbbb6709c118 Mon Sep 17 00:00:00 2001 From: Jeffrey Morgan Date: Thu, 16 Apr 2015 16:56:36 -0400 Subject: [PATCH 24/32] Update .travis.yml --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index dabf931807..43bdc57979 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,6 +8,7 @@ cache: directories: - resources - node_modules + - cache after_success: - which ./node_modules/coveralls/bin/coveralls.js && cat coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js From 77ee7b3485cd3727ac3a540ba00438a91b567a2c Mon Sep 17 00:00:00 2001 From: Jeffrey Morgan Date: Thu, 16 Apr 2015 17:22:43 -0400 Subject: [PATCH 25/32] removing postinstall --- package.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/package.json b/package.json index ec6a219f2d..b410c402fa 100644 --- a/package.json +++ b/package.json @@ -12,11 +12,10 @@ "bugs": "https://github.com/kitematic/kitematic/issues", "scripts": { "start": "gulp", - "test": "jest --coverage", + "test": "jest", "release": "gulp release", "release:beta": "gulp release --beta", "preinstall": "./util/deps", - "postinstall": "gulp download", "lint": "jsxhint src && jsxhint browser" }, "licenses": [ From 3a634740460ec14af07656fcfef51c04dbdd4e27 Mon Sep 17 00:00:00 2001 From: Jeffrey Morgan Date: Thu, 16 Apr 2015 17:25:29 -0400 Subject: [PATCH 26/32] Update .travis.yml --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 43bdc57979..dabf931807 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,7 +8,6 @@ cache: directories: - resources - node_modules - - cache after_success: - which ./node_modules/coveralls/bin/coveralls.js && cat coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js From 07a1468f7b0e3de9630c0b41f85e973f21f7820a Mon Sep 17 00:00:00 2001 From: Jeffrey Morgan Date: Thu, 16 Apr 2015 17:26:49 -0400 Subject: [PATCH 27/32] Update .travis.yml --- .travis.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index dabf931807..a9888c7891 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,4 @@ language: node_js -node_js: - - "0.10" sudo: false From c92b74bfa07a1fb8c661c8c774e4654a8c253b74 Mon Sep 17 00:00:00 2001 From: Jeffrey Morgan Date: Thu, 16 Apr 2015 20:52:48 -0400 Subject: [PATCH 28/32] Fix upgrade and delete bugs --- src/ContainerHomeLogs.react.js | 4 ++++ src/SetupStore.js | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/ContainerHomeLogs.react.js b/src/ContainerHomeLogs.react.js index 4a471202ff..fba6a1b4f4 100644 --- a/src/ContainerHomeLogs.react.js +++ b/src/ContainerHomeLogs.react.js @@ -23,6 +23,10 @@ module.exports = React.createClass({ LogStore.fetch(this.props.container.Name); }, componentWillUnmount: function() { + if (!this.props.container) { + return; + } + LogStore.detach(this.props.container.Name); LogStore.removeListener(LogStore.SERVER_LOGS_EVENT, this.update); }, diff --git a/src/SetupStore.js b/src/SetupStore.js index c4bab1ff0c..53972c90d7 100644 --- a/src/SetupStore.js +++ b/src/SetupStore.js @@ -80,7 +80,7 @@ var _steps = [{ var isoversion = machine.isoversion(); var packagejson = util.packagejson(); if (!isoversion || setupUtil.compareVersions(isoversion, packagejson['docker-version']) < 0) { - yield machine.stop(); + yield machine.start(); yield machine.upgrade(); } if ((yield machine.state()) !== 'Running') { From 5ca0cef1f67258eb3e6fcd3af11687b7771f77c8 Mon Sep 17 00:00:00 2001 From: Jeffrey Morgan Date: Thu, 16 Apr 2015 20:53:14 -0400 Subject: [PATCH 29/32] bump to 0.5.15 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index b410c402fa..3059ca6d93 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "Kitematic", - "version": "0.5.14", + "version": "0.5.15", "author": "Kitematic", "description": "Simple Docker Container management for Mac OS X.", "homepage": "https://kitematic.com/", From 851b01ef0409cf04c11926f28bda8a8e541d0a2d Mon Sep 17 00:00:00 2001 From: Jeffrey Morgan Date: Thu, 16 Apr 2015 21:01:21 -0400 Subject: [PATCH 30/32] Fixing test --- src/SetupStore-test.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/SetupStore-test.js b/src/SetupStore-test.js index 47a07968a6..c956440170 100644 --- a/src/SetupStore-test.js +++ b/src/SetupStore-test.js @@ -80,11 +80,9 @@ describe('SetupStore', function () { setupUtil.compareVersions.mockReturnValue(-1); machine.create.mockClear(); machine.upgrade.mockClear(); - machine.stop.mockClear(); machine.start.mockClear(); return setupStore.steps().init.run(() => {}).then(() => { expect(machine.create).not.toBeCalled(); - expect(machine.stop).toBeCalled(); expect(machine.upgrade).toBeCalled(); expect(machine.start).toBeCalled(); }); From 87f5572bfb61230224e7b037ad144817dd74791d Mon Sep 17 00:00:00 2001 From: Jeffrey Morgan Date: Fri, 17 Apr 2015 09:54:29 -0400 Subject: [PATCH 31/32] Use minimist for parsing args --- gulpfile.js | 13 ++++++------- package.json | 4 ++-- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/gulpfile.js b/gulpfile.js index 1146a693e2..4c1b05b3e2 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -17,7 +17,7 @@ var shell = require('gulp-shell'); var sourcemaps = require('gulp-sourcemaps'); var dependencies = Object.keys(packagejson.dependencies); -var isBeta = process.argv.indexOf('--beta') !== -1; +var argv = require('minimist')(process.argv.slice(2)); var settings; try { @@ -25,15 +25,15 @@ try { } catch (err) { settings = {}; } -settings.beta = isBeta; +settings.beta = argv.beta; var options = { dev: process.argv.indexOf('release') === -1, - beta: isBeta, - appFilename: isBeta ? 'Kitematic (Beta).app' : 'Kitematic.app', - appName: isBeta ? 'Kitematic (Beta)' : 'Kitematic', + beta: argv.beta, + appFilename: argv.beta ? 'Kitematic (Beta).app' : 'Kitematic.app', + appName: argv.beta ? 'Kitematic (Beta)' : 'Kitematic', name: 'Kitematic', - icon: isBeta ? './util/kitematic-beta.icns' : './util/kitematic.icns', + icon: argv.beta ? './util/kitematic-beta.icns' : './util/kitematic.icns', bundle: 'com.kitematic.kitematic' }; @@ -63,7 +63,6 @@ gulp.task('styles', function () { return gulp.src('styles/main.less') .pipe(plumber(function(error) { gutil.log(gutil.colors.red('Error (' + error.plugin + '): ' + error.message)); - // emit the end event, to properly end the task this.emit('end'); })) .pipe(gulpif(options.dev, changed('./build'))) diff --git a/package.json b/package.json index 3059ca6d93..87509623c8 100644 --- a/package.json +++ b/package.json @@ -59,7 +59,6 @@ "dockerode": "^2.1.1", "exec": "0.2.0", "jquery": "^2.1.3", - "minimist": "^1.1.0", "mixpanel": "0.0.20", "node-uuid": "^1.4.2", "object-assign": "^2.0.0", @@ -74,7 +73,6 @@ }, "devDependencies": { "babel": "^4.5.5", - "gulp": "^3.8.11", "gulp-babel": "^4.0.0", "gulp-changed": "^1.1.1", "gulp-concat": "^2.5.2", @@ -89,8 +87,10 @@ "gulp-shell": "^0.3.0", "gulp-sourcemaps": "^1.5.0", "gulp-util": "^3.0.4", + "gulp": "^3.8.11", "jest-cli": "kitematic/jest", "jsxhint": "^0.12.1", + "minimist": "^1.1.0", "react-tools": "^0.12.2", "run-sequence": "^1.0.2" } From 531a10a66b06fdef2d52e609c2e9cfb034906c4c Mon Sep 17 00:00:00 2001 From: Jeffrey Morgan Date: Fri, 17 Apr 2015 09:59:10 -0400 Subject: [PATCH 32/32] Upgrading outdated dependencies --- package.json | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/package.json b/package.json index 3059ca6d93..a97043887f 100644 --- a/package.json +++ b/package.json @@ -53,45 +53,45 @@ "dependencies": { "ansi-to-html": "0.3.0", "async": "^0.9.0", - "bluebird": "^2.9.12", + "bluebird": "^2.9.24", "bugsnag-js": "^2.4.7", "coveralls": "^2.11.2", "dockerode": "^2.1.1", "exec": "0.2.0", "jquery": "^2.1.3", - "minimist": "^1.1.0", - "mixpanel": "0.0.20", - "node-uuid": "^1.4.2", + "minimist": "^1.1.1", + "mixpanel": "0.2.0", + "node-uuid": "^1.4.3", "object-assign": "^2.0.0", - "react": "^0.12.2", - "react-bootstrap": "^0.15.1", + "react": "^0.13.1", + "react-bootstrap": "^0.20.3", "react-retina-image": "^1.1.2", - "react-router": "^0.12.4", - "request": "^2.53.0", + "react-router": "^0.13.2", + "request": "^2.55.0", "request-progress": "^0.3.1", - "rimraf": "^2.2.8", - "underscore": "^1.8.2" + "rimraf": "^2.3.2", + "underscore": "^1.8.3" }, "devDependencies": { - "babel": "^4.5.5", + "babel": "^5.1.10", "gulp": "^3.8.11", - "gulp-babel": "^4.0.0", - "gulp-changed": "^1.1.1", + "gulp-babel": "^5.1.0", + "gulp-changed": "^1.2.1", "gulp-concat": "^2.5.2", "gulp-cssmin": "^0.1.6", "gulp-download-atom-shell": "0.0.4", "gulp-if": "^1.2.5", "gulp-insert": "^0.4.0", - "gulp-less": "^3.0.1", + "gulp-less": "^3.0.2", "gulp-livereload": "^3.8.0", - "gulp-plumber": "^0.6.6", - "gulp-react": "^2.0.0", - "gulp-shell": "^0.3.0", - "gulp-sourcemaps": "^1.5.0", + "gulp-plumber": "^1.0.0", + "gulp-react": "^3.0.1", + "gulp-shell": "^0.4.1", + "gulp-sourcemaps": "^1.5.2", "gulp-util": "^3.0.4", "jest-cli": "kitematic/jest", - "jsxhint": "^0.12.1", - "react-tools": "^0.12.2", + "jsxhint": "^0.14.0", + "react-tools": "^0.13.1", "run-sequence": "^1.0.2" } }