From e6badec15549bd91da70783e8f8c07d5f06ea9c5 Mon Sep 17 00:00:00 2001 From: Alexandre Vazquez Date: Fri, 5 Feb 2016 22:40:35 +0100 Subject: [PATCH 01/10] Fixes #1206 . In this PR we allow to add new ports using the Ports tab inside the Settings section. It also allows to remove them using the same form. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We include a button with an icon to remove and add ports as needed and the GUI follows the same approach that the one to add environment variables. Signed-off-by: Alexandre Vázquez alexandre.vazquez@gmail.com --- .../ContainerSettingsPorts.react.js | 175 ++++++++++++++---- src/utils/DockerUtil.js | 18 +- 2 files changed, 158 insertions(+), 35 deletions(-) diff --git a/src/components/ContainerSettingsPorts.react.js b/src/components/ContainerSettingsPorts.react.js index 83fbd198e8..b3c75e1d92 100644 --- a/src/components/ContainerSettingsPorts.react.js +++ b/src/components/ContainerSettingsPorts.react.js @@ -5,6 +5,7 @@ import ContainerUtil from '../utils/ContainerUtil'; import containerActions from '../actions/ContainerActions'; import containerStore from '../stores/ContainerStore'; import metrics from '../utils/MetricsUtil'; +import docker from '../utils/DockerUtil'; import {webPorts} from '../utils/Util'; import {DropdownButton, MenuItem} from 'react-bootstrap'; @@ -13,8 +14,18 @@ var ContainerSettingsPorts = React.createClass({ router: React.PropTypes.func }, getInitialState: function () { + var ports = ContainerUtil.ports(this.props.container); + var initialPorts = this.props.container.InitialPorts; + ports[''] = { + ip: docker.host, + url: '', + port: '', + portType: 'tcp', + error: null + }; return { - ports: ContainerUtil.ports(this.props.container) + ports: ports, + initialPorts: initialPorts }; }, handleViewLink: function (url) { @@ -23,32 +34,67 @@ var ContainerSettingsPorts = React.createClass({ }); shell.openExternal(url); }, - handleChangePort: function(key, e) { - let ports = this.state.ports; - let port = e.target.value; - - // save updated port - ports[key] = _.extend(ports[key], { - url: 'http://' + ports[key]['ip'] + ':' + port, - port: port, - error: null - }); + createEmptyPort: function (ports) { + ports[''] = { + ip: docker.host, + url: '', + port: '', + portType: 'tcp' + }; + document.getElementById('portKey').value = ''; + document.getElementById('portValue').value = ''; + }, + addPort: function () { + var portKey = document.getElementById('portKey').value; + var portValue = document.getElementById('portValue').value; + var portTypeValue = document.getElementById('portType').textContent; + var ports = this.state.ports; + if (portKey !== '') { + ports[portKey] = { + ip: docker.host, + url: docker.host + ':' + portValue, + port: portValue, + portType: portTypeValue.trim(), + error: null + }; + this.checkPort(ports, portKey, portKey); + if (ports[portKey].error === null) { + this.createEmptyPort(ports); + } + } + return ports; + }, + handleAddPort: function (e) { + var ports = this.addPort(); + this.setState({ports: ports}); + metrics.track('Added Pending Port'); + }, + checkPort: function (ports, port, key) { // basic validation, if number is integer, if its in range, if there // is no collision with ports of other containers and also if there is no // collision with ports for current container const otherContainers = _.filter(_.values(containerStore.getState().containers), c => c.Name !== this.props.container.Name); const otherPorts = _.flatten(otherContainers.map(container => { - return _.values(container.NetworkSettings.Ports).map(hosts => hosts.map(host => { - return {port: host.HostPort, name: container.Name} - })); + try { + return _.values(container.NetworkSettings.Ports).map(hosts => hosts.map(host => { + return {port: host.HostPort, name: container.Name}; + }) + ); + }catch (err) { + + } })).reduce((prev, pair) => { - prev[pair.port] = pair.name; + try { + prev[pair.port] = pair.name; + }catch (err) { + + } return prev; }, {}); const duplicates = _.filter(ports, (v, i) => { - return (i != key && _.isEqual(v.port, port)); + return (i !== key && _.isEqual(v.port, port)); }); if (!port.match(/^[0-9]+$/g)) { @@ -56,12 +102,41 @@ var ContainerSettingsPorts = React.createClass({ } else if (port <= 0 || port > 65535) { ports[key].error = 'Needs to be in range <1,65535>.'; } else if (otherPorts[port]) { - ports[key].error = 'Collision with container "'+ otherPorts[port] +'"'; + ports[key].error = 'Collision with container "' + otherPorts[port] + '"'; } else if (duplicates.length > 0) { ports[key].error = 'Collision with another port in this container.'; - } else if (port == 22 || port == 2376) { + } else if (port === 22 || port === 2376) { ports[key].error = 'Ports 22 and 2376 are reserved ports for Kitematic/Docker.'; } + }, + handleChangePort: function (key, e) { + let ports = this.state.ports; + let port = e.target.value; + // save updated port + ports[key] = _.extend(ports[key], { + url: 'http://' + ports[key].ip + ':' + port, + port: port, + error: null + }); + this.checkPort(ports, port, key); + + this.setState({ports: ports}); + }, + handleChangePortKey: function (key, e) { + let ports = this.state.ports; + let portKey = e.target.value; + + // save updated port + var currentPort = ports[key]; + + delete ports[key]; + ports[portKey] = currentPort; + + this.setState({ports: ports}); + }, + handleRemovePort: function (key, e) { + let ports = this.state.ports; + delete ports[key]; this.setState({ports: ports}); }, handleChangePortType: function (key, portType) { @@ -77,19 +152,38 @@ var ContainerSettingsPorts = React.createClass({ }); this.setState({ports: ports}); }, + isInitialPort: function (key, ports) { + for (var idx in ports) { + if (ports.hasOwnProperty(idx)) { + var p = idx.split('/'); + if (p.length > 0) { + if (p[0] === key) { + return true; + } + } + } + } + return false; + }, handleSave: function () { - let exposedPorts = {}; - let portBindings = _.reduce(this.state.ports, (res, value, key) => { - res[key + '/' + value.portType] = [{ - HostPort: value.port - }]; - exposedPorts[key] = {}; + let ports = this.state.ports; + ports = this.addPort(); + this.setState({ports: ports}); + let bindings = _.reduce(ports, (res, value, key) => { + if (key !== '') { + res[key + '/' + value.portType] = [{ + HostPort: value.port + }]; + } return res; }, {}); - let hostConfig = _.extend(this.props.container.HostConfig, {PortBindings: portBindings}); + containerActions.update(this.props.container.Name, { + NetworkSettings: { + Ports: bindings + } + }); - containerActions.update(this.props.container.Name, {ExposedPorts: exposedPorts, HostConfig: hostConfig}); }, render: function () { if (!this.props.container) { @@ -102,24 +196,37 @@ var ContainerSettingsPorts = React.createClass({ var key = pair[0]; var {ip, port, url, portType, error} = pair[1]; isValid = (error) ? false : isValid; - let ipLink = (this.props.container.State.Running && !this.props.container.State.Paused && !this.props.container.State.ExitCode && !this.props.container.State.Restarting) ? ({ip}):({ip}); + let ipLink = (this.props.container.State.Running && !this.props.container.State.Paused && !this.props.container.State.ExitCode && !this.props.container.State.Restarting) ? ({ip}) : ({ip}); + var icon = ''; + var portKey = ''; + var portValue = ''; + if (key === '') { + icon = ; + portKey = ; + portValue = ; + }else { + if (this.isInitialPort(key, this.state.initialPorts)) { + icon = ; + }else { + icon = ; + } + portKey = ; + portValue = ; + } return ( - {key} + {portKey} {ipLink}: - + {portValue} - + TCP UDP + {icon} {error} ); diff --git a/src/utils/DockerUtil.js b/src/utils/DockerUtil.js index a06765d330..ea6f799cfb 100644 --- a/src/utils/DockerUtil.js +++ b/src/utils/DockerUtil.js @@ -85,6 +85,7 @@ export default { startContainer (name) { let container = this.client.getContainer(name); + container.start((error) => { if (error) { containerServerActions.error({name, error}); @@ -117,7 +118,7 @@ export default { if (!containerData.HostConfig || (containerData.HostConfig && !containerData.HostConfig.PortBindings)) { containerData.PublishAllPorts = true; } - + if (image.Config.Cmd) { containerData.Cmd = image.Config.Cmd; } else if (!image.Config.Entrypoint) { @@ -148,6 +149,14 @@ export default { containerServerActions.error({name: id, error}); } else { container.Name = container.Name.replace('/', ''); + this.client.getImage(container.Image).inspect((error, image) => { + if (error) { + containerServerActions.error({name, error}); + return; + } + container.InitialPorts = image.Config.ExposedPorts; + }); + containerServerActions.updated({container}); } }); @@ -165,6 +174,13 @@ export default { return; } container.Name = container.Name.replace('/', ''); + this.client.getImage(container.Image).inspect((error, image) => { + if (error) { + containerServerActions.error({name, error}); + return; + } + container.InitialPorts = image.Config.ExposedPorts; + }); callback(null, container); }); }, (err, containers) => { From a0332d279bb90e9849d52315f1c57562bfce5058 Mon Sep 17 00:00:00 2001 From: Alexandre Vazquez Date: Sun, 27 Mar 2016 16:03:11 +0200 Subject: [PATCH 02/10] Updating the branch to the latest master and making it work with the new docker daemon evolutions --- .../ContainerSettingsPorts.react.js | 45 ++++++++++--------- src/utils/DockerUtil.js | 1 + 2 files changed, 24 insertions(+), 22 deletions(-) diff --git a/src/components/ContainerSettingsPorts.react.js b/src/components/ContainerSettingsPorts.react.js index b3c75e1d92..2e5a086433 100644 --- a/src/components/ContainerSettingsPorts.react.js +++ b/src/components/ContainerSettingsPorts.react.js @@ -45,22 +45,24 @@ var ContainerSettingsPorts = React.createClass({ document.getElementById('portValue').value = ''; }, addPort: function () { - var portKey = document.getElementById('portKey').value; - var portValue = document.getElementById('portValue').value; - var portTypeValue = document.getElementById('portType').textContent; - var ports = this.state.ports; - if (portKey !== '') { - ports[portKey] = { - ip: docker.host, - url: docker.host + ':' + portValue, - port: portValue, - portType: portTypeValue.trim(), - error: null - }; + if (document.getElementById('portKey') != null){ + var portKey = document.getElementById('portKey').value; + var portValue = document.getElementById('portValue').value; + var portTypeValue = document.getElementById('portType').textContent; + var ports = this.state.ports; + if (portKey !== '') { + ports[portKey] = { + ip: docker.host, + url: docker.host + ':' + portValue, + port: portValue, + portType: portTypeValue.trim(), + error: null + }; - this.checkPort(ports, portKey, portKey); - if (ports[portKey].error === null) { - this.createEmptyPort(ports); + this.checkPort(ports, portKey, portKey); + if (ports[portKey].error === null) { + this.createEmptyPort(ports); + } } } return ports; @@ -169,20 +171,19 @@ var ContainerSettingsPorts = React.createClass({ let ports = this.state.ports; ports = this.addPort(); this.setState({ports: ports}); - let bindings = _.reduce(ports, (res, value, key) => { + let exposedPorts = {}; + let portBindings = _.reduce(ports, (res, value, key) => { if (key !== '') { res[key + '/' + value.portType] = [{ HostPort: value.port }]; + exposedPorts[key + '/' + value.portType] = {}; } return res; }, {}); - containerActions.update(this.props.container.Name, { - NetworkSettings: { - Ports: bindings - } - }); + let hostConfig = _.extend(this.props.container.HostConfig, {PortBindings: portBindings}); + containerActions.update(this.props.container.Name, {ExposedPorts: exposedPorts, HostConfig: hostConfig}); }, render: function () { @@ -201,7 +202,7 @@ var ContainerSettingsPorts = React.createClass({ var portKey = ''; var portValue = ''; if (key === '') { - icon = ; + icon = ; portKey = ; portValue = ; }else { diff --git a/src/utils/DockerUtil.js b/src/utils/DockerUtil.js index 1a469facfc..1d8e8bcb50 100644 --- a/src/utils/DockerUtil.js +++ b/src/utils/DockerUtil.js @@ -296,6 +296,7 @@ export default { data.Mounts = data.Mounts || existingData.Mounts; var fullData = _.extend(existingData, data); + console.log(fullData); this.createContainer(name, fullData); }); }, From c377ca9f90c96aeca853ebde077a2bb14068e8a9 Mon Sep 17 00:00:00 2001 From: French Ben Date: Thu, 19 May 2016 18:40:36 -0700 Subject: [PATCH 03/10] Removed debug from util Signed-off-by: French Ben --- src/utils/DockerUtil.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/utils/DockerUtil.js b/src/utils/DockerUtil.js index f425d74ad1..40befff578 100644 --- a/src/utils/DockerUtil.js +++ b/src/utils/DockerUtil.js @@ -359,7 +359,6 @@ var DockerUtil = { data.Mounts = data.Mounts || existingData.Mounts; var fullData = _.extend(existingData, data); - console.log(fullData); this.createContainer(name, fullData); }); }, From bca6cf391b6f2f228832ba040ce845ce4e8916a1 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Sat, 21 May 2016 14:05:07 +0200 Subject: [PATCH 04/10] Update ben's e-mail address Signed-off-by: Sebastiaan van Stijn --- MAINTAINERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index 25cdb0fcc0..fdb4bd5463 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -32,7 +32,7 @@ [people.FrenchBen] Name = "Ben French" - Email = "me@frenchben.com" + Email = "frenchben@docker.com" GitHub = "FrenchBen" [people.jeffdm] From ab1c7fa66ff2e79d6246154bb3667234296f9d30 Mon Sep 17 00:00:00 2001 From: Alexei Yuzhakov Date: Sun, 22 May 2016 09:29:11 +0600 Subject: [PATCH 05/10] Add ability to run privileged containers (fix #269). Signed-off-by: Alexei Yuzhakov --- .../ContainerSettingsAdvanced.react.js | 17 ++++++++++++++--- src/utils/ContainerUtil.js | 9 +++++---- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/src/components/ContainerSettingsAdvanced.react.js b/src/components/ContainerSettingsAdvanced.react.js index 6120498c3c..08b8e351b1 100644 --- a/src/components/ContainerSettingsAdvanced.react.js +++ b/src/components/ContainerSettingsAdvanced.react.js @@ -1,3 +1,4 @@ +import _ from 'underscore'; import React from 'react/addons'; import metrics from '../utils/MetricsUtil'; import ContainerUtil from '../utils/ContainerUtil'; @@ -11,10 +12,11 @@ var ContainerSettingsAdvanced = React.createClass({ }, getInitialState: function () { - let [tty, openStdin] = ContainerUtil.mode(this.props.container) || [true, true]; + let [tty, openStdin, privileged] = ContainerUtil.mode(this.props.container) || [true, true, false]; return { tty: tty, - openStdin: openStdin + openStdin: openStdin, + privileged: privileged }; }, @@ -22,7 +24,9 @@ var ContainerSettingsAdvanced = React.createClass({ metrics.track('Saved Advanced Options'); let tty = this.state.tty; let openStdin = this.state.openStdin; - containerActions.update(this.props.container.Name, {Tty: tty, OpenStdin: openStdin}); + let privileged = this.state.privileged; + let hostConfig = _.extend(this.props.container.HostConfig, {Privileged: privileged}); + containerActions.update(this.props.container.Name, {Tty: tty, OpenStdin: openStdin, HostConfig: hostConfig}); }, handleChangeTty: function () { @@ -37,6 +41,12 @@ var ContainerSettingsAdvanced = React.createClass({ }); }, + handleChangePrivileged: function () { + this.setState({ + privileged: !this.state.privileged + }); + }, + render: function () { if (!this.props.container) { return false; @@ -49,6 +59,7 @@ var ContainerSettingsAdvanced = React.createClass({

Allocate a TTY for this container

Keep STDIN open even if not attached

+

Privileged mode

Save diff --git a/src/utils/ContainerUtil.js b/src/utils/ContainerUtil.js index a19576970f..5d1d2014ca 100644 --- a/src/utils/ContainerUtil.js +++ b/src/utils/ContainerUtil.js @@ -15,10 +15,11 @@ var ContainerUtil = { // Provide Foreground options mode: function (container) { - if (!container || !container.Config) { - return [true, true]; - } - return [container.Config.Tty, container.Config.OpenStdin]; + return [ + (container && container.Config) ? container.Config.Tty : true, + (container && container.Config) ? container.Config.OpenStdin : true, + (container && container.HostConfig) ? container.HostConfig.Privileged : false + ]; }, // TODO: inject host here instead of requiring Docker From 8bd9ee63f444721a9aea01eaf93f117a4ae1ef73 Mon Sep 17 00:00:00 2001 From: Alexei Yuzhakov Date: Tue, 24 May 2016 22:13:31 +0600 Subject: [PATCH 06/10] Fix ability to click on link on port mapping page (fix #1700). Signed-off-by: Alexei Yuzhakov --- src/components/ContainerSettingsPorts.react.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/ContainerSettingsPorts.react.js b/src/components/ContainerSettingsPorts.react.js index 2e5a086433..4a9359b45e 100644 --- a/src/components/ContainerSettingsPorts.react.js +++ b/src/components/ContainerSettingsPorts.react.js @@ -32,7 +32,7 @@ var ContainerSettingsPorts = React.createClass({ metrics.track('Opened In Browser', { from: 'settings' }); - shell.openExternal(url); + shell.openExternal('http://' + url); }, createEmptyPort: function (ports) { ports[''] = { From a6ecf84fe48a1e7333794bff297632fe4d119a3d Mon Sep 17 00:00:00 2001 From: French Ben Date: Thu, 26 May 2016 14:08:51 -0700 Subject: [PATCH 07/10] Added error output when error is unknown Signed-off-by: French Ben --- src/components/ContainerHome.react.js | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/components/ContainerHome.react.js b/src/components/ContainerHome.react.js index 4da81d2d96..a92048137d 100644 --- a/src/components/ContainerHome.react.js +++ b/src/components/ContainerHome.react.js @@ -52,12 +52,15 @@ var ContainerHome = React.createClass({ let body; if (this.props.container.Error) { let error = this.props.container.Error.message; - console.log('Err: %o - %o', typeof error, error); - if (error.indexOf('ETIMEDOUT') !== -1) { - error = 'Timeout error - Try and restart your VM by running: \n"docker-machine restart default" in a terminal'; - } - if (error.indexOf('ECONNREFUSED') !== -1) { - error = 'Is your VM up and running? Check that "docker ps" works in a terminal.'; + if (!error) { + error = this.props.container.Error; + } else { + if (error.indexOf('ETIMEDOUT') !== -1) { + error = 'Timeout error - Try and restart your VM by running: \n"docker-machine restart default" in a terminal'; + } + if (error.indexOf('ECONNREFUSED') !== -1) { + error = 'Is your VM up and running? Check that "docker ps" works in a terminal.'; + } } body = (
From 7bca08d2bbd036991d118ef62f802bc93ac0517a Mon Sep 17 00:00:00 2001 From: French Ben Date: Thu, 26 May 2016 15:24:16 -0700 Subject: [PATCH 08/10] Kitematic 0.10.4 Signed-off-by: French Ben --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f5420459cd..a51da6c6b7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "Kitematic", - "version": "0.10.2", + "version": "0.10.4", "author": "Kitematic", "description": "Simple Docker Container management for Mac OS X.", "homepage": "https://kitematic.com/", From f9c6e1d42b21340528fee03f17cf1f843bba3742 Mon Sep 17 00:00:00 2001 From: Albert Domenech Date: Sun, 15 May 2016 13:21:29 +0200 Subject: [PATCH 09/10] Initial debian/ubuntu installer: - Used grunt-packager and grunt-electron-installer-debian - Added debian/ubuntu ai32 arch support - Added Redhat/Centos/Fedora rpm support - Added electron-packager to MacOSX custom task - Fixed OSX Linux build and better conditional legibility - Fixed ESLint indications Signed-off-by: Albert Domenech --- Gruntfile.js | 151 +++++++++++++++++++++++++++++++++++++++++---- package.json | 3 + util/kitematic.png | Bin 0 -> 7987 bytes 3 files changed, 143 insertions(+), 11 deletions(-) create mode 100644 util/kitematic.png diff --git a/Gruntfile.js b/Gruntfile.js index 7cd1864b9a..505fd4a117 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -1,3 +1,4 @@ +var fs = require('fs'); var path = require('path'); var execFile = require('child_process').execFile; var packagejson = require('./package.json'); @@ -26,11 +27,20 @@ module.exports = function (grunt) { var OSX_OUT = './dist'; var OSX_OUT_X64 = OSX_OUT + '/' + OSX_APPNAME + '-darwin-x64'; var OSX_FILENAME = OSX_OUT_X64 + '/' + OSX_APPNAME + '.app'; + var IS_WINDOWS = process.platform === 'win32'; + var IS_LINUX = process.platform === 'linux'; + + var IS_I386 = process.arch === 'ia32'; + var IS_X64 = process.arch === 'x64'; + + var IS_DEB = fs.existsSync('/etc/lsb-release') || fs.existsSync('/etc/debian_version'); + var IS_RPM = fs.existsSync('/etc/redhat-release'); + grunt.initConfig({ IDENTITY: 'Developer ID Application: Docker Inc', OSX_FILENAME: OSX_FILENAME, - OSX_FILENAME_ESCAPED: OSX_FILENAME.replace(/ /g, '\\ ').replace(/\(/g,'\\(').replace(/\)/g,'\\)'), + OSX_FILENAME_ESCAPED: OSX_FILENAME.replace(/ /g, '\\ ').replace(/\(/g, '\\(').replace(/\)/g, '\\)'), // electron electron: { @@ -117,7 +127,9 @@ module.exports = function (grunt) { dest: 'build/' }, { cwd: 'node_modules/', - src: Object.keys(packagejson.dependencies).map(function (dep) { return dep + '/**/*';}), + src: Object.keys(packagejson.dependencies).map(function (dep) { + return dep + '/**/*'; + }), dest: 'build/node_modules/', expand: true }] @@ -181,7 +193,7 @@ module.exports = function (grunt) { expand: true, cwd: 'src/', src: ['**/*.js'], - dest: 'build/', + dest: 'build/' }] } }, @@ -198,28 +210,31 @@ module.exports = function (grunt) { }, sign: { options: { - failOnError: false, + failOnError: false }, command: [ 'codesign --deep -v -f -s "<%= IDENTITY %>" <%= OSX_FILENAME_ESCAPED %>/Contents/Frameworks/*', 'codesign -v -f -s "<%= IDENTITY %>" <%= OSX_FILENAME_ESCAPED %>', 'codesign -vvv --display <%= OSX_FILENAME_ESCAPED %>', 'codesign -v --verify <%= OSX_FILENAME_ESCAPED %>' - ].join(' && '), + ].join(' && ') }, zip: { - command: 'ditto -c -k --sequesterRsrc --keepParent <%= OSX_FILENAME_ESCAPED %> release/' + BASENAME + '-Mac.zip', + command: 'ditto -c -k --sequesterRsrc --keepParent <%= OSX_FILENAME_ESCAPED %> release/' + BASENAME + '-Mac.zip' + }, + linux_npm: { + command: 'cd build && npm install --production' } }, clean: { - release: ['build/', 'dist/'], + release: ['build/', 'dist/'] }, compress: { windows: { options: { - archive: './release/' + BASENAME + '-Windows.zip', + archive: './release/' + BASENAME + '-Windows.zip', mode: 'zip' }, files: [{ @@ -228,7 +243,7 @@ module.exports = function (grunt) { cwd: './dist/Kitematic-win32-x64', src: '**/*' }] - }, + } }, // livereload @@ -252,12 +267,126 @@ module.exports = function (grunt) { files: ['images/*', 'index.html', 'fonts/*'], tasks: ['newer:copy:dev'] } + }, + 'electron-packager': { + build: { + options: { + platform: process.platform, + arch: process.arch, + dir: './build', + out: './dist/', + name: 'Kitematic', + ignore: 'bower.json', + version: packagejson['electron-version'], // set version of electron + overwrite: true + } + }, + osxlnx: { + options: { + platform: 'linux', + arch: 'x64', + dir: './build', + out: './dist/', + name: 'Kitematic', + ignore: 'bower.json', + version: packagejson['electron-version'], // set version of electron + overwrite: true + } + } + }, + 'electron-installer-debian': { + options: { + productName: LINUX_APPNAME, + productDescription: 'Run containers through a simple, yet powerful graphical user interface.', + section: 'devel', + priority: 'optional', + icon: './util/kitematic.png', + lintianOverrides: [ + 'changelog-file-missing-in-native-package', + 'executable-not-elf-or-script', + 'extra-license-file' + ], + categories: [ + 'Utility' + ], + rename: function (dest, src) { + return dest + '<%= name %>_' + packagejson.version + '-<%= revision %>_<%= arch %>.deb'; + } + }, + linux64: { + options: { + arch: 'amd64' + }, + src: './dist/Kitematic-linux-x64/', + dest: './dist/' + }, + linux32: { + options: { + arch: 'i386' + }, + src: './dist/Kitematic-linux-ia32/', + dest: './dist/' + } + }, + 'electron-installer-redhat': { + options: { + productName: LINUX_APPNAME, + productDescription: 'Run containers through a simple, yet powerful graphical user interface.', + priority: 'optional', + icon: './util/kitematic.png', + categories: [ + 'Utilities' + ], + rename: function (dest, src) { + return dest + '<%= name %>_' + packagejson.version + '-<%= revision %>_<%= arch %>.rpm'; + } + }, + linux64: { + options: { + arch: 'x86_64' + }, + src: './dist/Kitematic-linux-x64/', + dest: './dist/' + }, + linux32: { + options: { + arch: 'x86' + }, + src: './dist/Kitematic-linux-ia32/', + dest: './dist/' + } } }); + // Load the plugins for linux packaging + grunt.loadNpmTasks('grunt-electron-packager'); + grunt.loadNpmTasks('grunt-electron-installer-debian'); + grunt.loadNpmTasks('grunt-electron-installer-redhat'); + grunt.registerTask('default', ['newer:babel', 'less', 'newer:copy:dev', 'shell:electron', 'watchChokidar']); - if (!IS_WINDOWS) { - grunt.registerTask('release', ['clean:release', 'babel', 'less', 'copy:dev', 'electron', 'copy:osx', 'shell:sign', 'shell:zip', 'copy:windows', 'rcedit:exes', 'compress']); + + if (!IS_WINDOWS && !IS_LINUX) { + grunt.registerTask('release', ['clean:release', 'babel', 'less', 'copy:dev', 'electron', 'copy:osx', 'shell:sign', 'shell:zip', 'copy:windows', 'rcedit:exes', 'compress', 'shell:linux_npm', 'electron-packager:osxlnx', 'electron-installer-debian:linux64']); + }else if (IS_LINUX) { + + var linuxpackage = null; + // linux package detection + if (IS_DEB && IS_X64) { + linuxpackage = 'electron-installer-debian:linux64'; + } else if (IS_DEB && IS_I386) { + linuxpackage = 'electron-installer-debian:linux32'; + } else if (IS_RPM && IS_X64) { + linuxpackage = 'electron-installer-redhat:linux64'; + }else if (IS_RPM && IS_I386) { + linuxpackage = 'electron-installer-redhat:linux32'; + } + + if (linuxpackage) { + grunt.registerTask('release', ['clean:release', 'babel', 'less', 'copy:dev', 'shell:linux_npm', 'electron-packager:build', linuxpackage]); + }else { + grunt.log.errorlns('Your Linux distribution is not yet supported - arch:' + process.arch + ' platform:' + process.platform); + } + }else { grunt.registerTask('release', ['clean:release', 'babel', 'less', 'copy:dev', 'electron:windows', 'copy:windows', 'rcedit:exes', 'compress']); } diff --git a/package.json b/package.json index f5420459cd..bb98122aed 100644 --- a/package.json +++ b/package.json @@ -70,6 +70,9 @@ "grunt-download-electron": "^2.1.1", "grunt-electron": "^2.0.0", "grunt-electron-installer": "^1.0.4", + "grunt-electron-installer-debian": "^0.3.0", + "grunt-electron-installer-redhat": "^0.3.0", + "grunt-electron-packager": "0.0.7", "grunt-if-missing": "^1.0.0", "grunt-newer": "^1.1.1", "grunt-plistbuddy": "^0.1.1", diff --git a/util/kitematic.png b/util/kitematic.png new file mode 100644 index 0000000000000000000000000000000000000000..75ae25e8802b479c485d6e36e675dcab0aa6ab5c GIT binary patch literal 7987 zcmY*;bzIZm_y23efJsZ|C_y9zCLqW_QW2041%%NmNGgmTCEW^03y8FIsK^jTmnb0} z0@4kmw%_*t{PX=i_Soa~y61K8x#vDlKJJM!(9@!Ua6kY6pwZD*Hv|9>;S~f>f(Z`` zk75VHgUnI&fhqu0CtSI(CMS%+HutsE0X*@S*YYWaK%sKee&PuLSFRHOARr@)jXIx$vv6!%MGA4F@8v{VFG%im#V3* zVG!ANrCO#r60YvIs5|Ci;gnoIQoGs1F~0uZZseDa?30&_U$-W*pM8@7<=b6X z9GTuBcT*M4U;K7Dil09%KRQJ5wSr@A%gM8O35zGZ!k(PDhxd)Gxnmg zyTY|r1$*KB(!W1?%isNU)2PJRl7q)%&ohEksNZ5+zQ(|M^@>}#S^t|n)!3e>4Ui4M zdG{stG%bP)E-nlCFvgN6F9eQYnh=osjHz}p`dsJQSCRJWA)pyj zI=x$FWE!p6$o5XZWL>Q|`}u{%>D&|>N7piRl{H7#?G4lhI*5`2s#HPrp6coiPkyeN zOw(kl{HnUbe-9*2cdJsxqgi#u6Vme3nDySZjl1>bb<|$Tu!n9fHQ^%6q|AS=C4LgS zr|)W}=*dftKz$tI+4XzaXpCvrLa4mF`Nu3yU_!(Wof6GJw&Y9S6)w)~$GkG0zc=(E z$*L9C&=*q5gxbheezJP+keu|up3Sp@jRGbHI@~VI$Dq;3_rYfG0x&;tf`%un=B}9; zpCO?1lUi=MluWa2mh|)3wD$$>)N%K?*b2|;&;xKT!rCp92`yj6_|GrCu4a6XW=3V^ zzOedG^Me#-As}X)Cwr-BJCuKgR=Ox_{i@om z)*p_?#glUGOqYLBnPrZP%z!(szV)8v4n3``U6*f~W{o+SKvh**-KT0J*eB1Ja(+() zeyVqEPY84$f9k$YhWIHHacA*%3XgP7qgs2F#UV6|T3jlQbWdqQ%7U}w@}%XP|8Zss z11fWj=Lug9KD}vHjFSlqqhc=+-}M|8@z-jfpJg@ex46CDsxlekvGirhiggI@7%7FN zYS!*8gV?7caVm37+t_if^8)1;DhCg(BlgW?*iMHve;bn7crOq=3%k#gu3-YXdoN5DJ_0|6-TTsfBxl*qw_~8 z%(=wxtP%EO_q%Vf8L_mQs%Yl#@pC;?{Avcs0&nbBySi{arN>ggRli%_fh3T&!FZbE zO;f^u-U0JT!(0mHG zJobLkAidxqJxJ}cBqSKjKksY6$y-16)%u|3&D{bAkc*G%(}K2w&I9&~FhQm`HMXH# zYgy!BD(i16?!(sB~a35wx9WUq0^GPYXjbeb{lES7UIFA1=^%$e8@b z7ez|`#ekBf6}eh|DKG>WYsjqdE!neOgcV{S6KtJ*vM)N7$V}MX!EQ@3T`G>>`cpW< zf28X`5V?#lJn&zFmq#r5fKlQy|*{nX*d##0j4O45nU>GNX;Uppn}aWnvbJDR7k z%|1%#l*Csy?ToRxnQ2#7x2>O`ICIz2#VTj6Rh~c8J7-9?5t#QK7Mc8<^rUI+xG0Cu z9$1`^WK-!Qptq$7g;-x~YGJCa*pkT~pOv4XX8TH2_?Y=gVBoxyRNHeBlElWXTsg}X zj*ln8g*VBdVr|IxPWug)wX6+5h~y9HO*u7Ds;;-T=cEfbp44(>|M!aTO_>eb&3&m# zpdIkM0LC?FR{3^nxGax1d?UYvjz3*Wn9 zkUJT}lhf1DvTQm^^v}%!&yMc;RHL9UV?h{%PqR`vqs3>CLL3#NBKtmzZn?%vO;m>5 zAYFGbB1Yu`1w{5b{=1|qxiCW&`>zC$xb`uM&!=01?6J64Rz_a;ZP^_vpx)Px@dv6X5S z14{W>Oi`IZlqIyz)YVNSrTmTM3UWgGAx{?-Wn_ye=?&K+m*551V$L(ujF71*vY(nP z096U&6@)l+^4ZMnwQ*^^GB)A+AZVc(6P&NJM|1d*?eCW?K0HO6i09_6hP+lcVS%0ACmhobm%euT~mrJP5okzqYu$ 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 literal 0 HcmV?d00001 From c712f27171630397e67393dafb50fef3c42a4256 Mon Sep 17 00:00:00 2001 From: "ClemMakesApps@gmail.com" Date: Fri, 27 May 2016 14:03:43 -0500 Subject: [PATCH 10/10] Fix electron deprecated API Signed-off-by: Clement Ho --- src/browser.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/browser.js b/src/browser.js index 2069c8022f..7b81f3d541 100644 --- a/src/browser.js +++ b/src/browser.js @@ -41,7 +41,7 @@ app.on('ready', function () { mainWindow.loadURL(path.normalize('file://' + path.join(__dirname, 'index.html'))); - app.on('activate-with-no-open-windows', function () { + app.on('activate', function () { if (mainWindow) { mainWindow.show(); }