diff --git a/Gruntfile.js b/Gruntfile.js index 7cd1864b9a..f0a2cdaeb4 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,38 @@ 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 LINUX_FILENAME = OSX_OUT + '/' + BASENAME + '_' + packagejson.version + '_amd64.deb'; + + 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'); + + 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'; + LINUX_FILENAME = OSX_OUT + '/' + BASENAME + '_' + packagejson.version + '_i386.deb'; + } else if (IS_RPM && IS_X64) { + linuxpackage = 'electron-installer-redhat:linux64'; + LINUX_FILENAME = OSX_OUT + '/' + BASENAME + '_' + packagejson.version + '_x86_64.rpm'; + } else if (IS_RPM && IS_I386) { + linuxpackage = 'electron-installer-redhat:linux32'; + LINUX_FILENAME = OSX_OUT + '/' + BASENAME + '_' + packagejson.version + '_x86.rpm'; + } + 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, '\\)'), + LINUX_FILENAME: LINUX_FILENAME, // electron electron: { @@ -117,7 +145,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 +211,7 @@ module.exports = function (grunt) { expand: true, cwd: 'src/', src: ['**/*.js'], - dest: 'build/', + dest: 'build/' }] } }, @@ -198,28 +228,34 @@ 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' + }, + linux_zip: { + command: 'ditto -c -k --sequesterRsrc --keepParent <%= LINUX_FILENAME %> release/' + BASENAME + '-Ubuntu.zip' } }, 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 +264,7 @@ module.exports = function (grunt) { cwd: './dist/Kitematic-win32-x64', src: '**/*' }] - }, + } }, // livereload @@ -252,12 +288,112 @@ 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 LINUX_FILENAME; + } + }, + 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 LINUX_FILENAME; + } + }, + 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', 'shell:linux_zip']); + }else if (IS_LINUX) { + 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/circle.yml b/circle.yml index 59f1a78674..145ce01957 100644 --- a/circle.yml +++ b/circle.yml @@ -13,3 +13,4 @@ deployment: commands: - github-release upload --user docker --repo kitematic --tag $CIRCLE_TAG --file release/Kitematic-Mac.zip --name Kitematic-$(echo $CIRCLE_TAG | cut -c2-)-Mac.zip - github-release upload --user docker --repo kitematic --tag $CIRCLE_TAG --file release/Kitematic-Windows.zip --name Kitematic-$(echo $CIRCLE_TAG | cut -c2-)-Windows.zip + - github-release upload --user docker --repo kitematic --tag $CIRCLE_TAG --file release/Kitematic-Ubuntu.deb --name Kitematic-$(echo $CIRCLE_TAG | cut -c2-)-Ubuntu.deb diff --git a/package.json b/package.json index f5420459cd..56e7ac43ec 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/", @@ -16,6 +16,7 @@ "test": "jest -c jest-unit.json", "integration": "jest -c jest-integration.json", "release": "grunt release", + "release-verbose": "grunt --verbose release", "lint": "jsxhint src" }, "license": "Apache-2.0", @@ -70,6 +71,7 @@ "grunt-download-electron": "^2.1.1", "grunt-electron": "^2.0.0", "grunt-electron-installer": "^1.0.4", + "grunt-electron-packager": "0.0.7", "grunt-if-missing": "^1.0.0", "grunt-newer": "^1.1.1", "grunt-plistbuddy": "^0.1.1", @@ -85,5 +87,9 @@ "run-sequence": "^1.0.2", "shell-escape": "^0.2.0", "source-map-support": "^0.3.2" + }, + "optionalDependencies": { + "grunt-electron-installer-debian": "^0.3.0", + "grunt-electron-installer-redhat": "^0.3.0" } } 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(); } 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 = (
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[''] = { diff --git a/src/utils/DockerMachineUtil.js b/src/utils/DockerMachineUtil.js index 634c8059ac..049f8b9bd8 100644 --- a/src/utils/DockerMachineUtil.js +++ b/src/utils/DockerMachineUtil.js @@ -147,29 +147,33 @@ var DockerMachine = { }); }, dockerTerminal: function (cmd, machineName = this.name()) { + cmd = cmd || process.env.SHELL || ''; if (util.isWindows()) { - cmd = cmd || ''; - this.url(machineName).then(machineUrl => { - util.exec('start powershell.exe ' + cmd, - {env: { - 'DOCKER_HOST': machineUrl, - 'DOCKER_CERT_PATH': path.join(util.home(), '.docker', 'machine', 'machines', machineName), - 'DOCKER_TLS_VERIFY': 1 - } + if (util.isNative()) { + util.exec('start powershell.exe ' + cmd); + } else { + this.url(machineName).then(machineUrl => { + util.exec('start powershell.exe ' + cmd, + {env: { + 'DOCKER_HOST': machineUrl, + 'DOCKER_CERT_PATH': path.join(util.home(), '.docker', 'machine', 'machines', machineName), + 'DOCKER_TLS_VERIFY': 1 + } + }); }); - }); - } else if (util.isNative()) { - cmd = cmd || process.env.SHELL; - var terminal = util.isLinux() ? util.linuxTerminal() : [path.join(process.env.RESOURCES_PATH, 'terminal')]; - terminal.push(cmd); - if (terminal) { - util.execFile(terminal).then(() => {}); } } else { - cmd = cmd || process.env.SHELL; - this.url(machineName).then(machineUrl => { - util.execFile([path.join(process.env.RESOURCES_PATH, 'terminal'), `DOCKER_HOST=${machineUrl} DOCKER_CERT_PATH=${path.join(util.home(), '.docker/machine/machines/' + machineName)} DOCKER_TLS_VERIFY=1 ${cmd}`]).then(() => {}); - }); + var terminal = util.isLinux() ? util.linuxTerminal() : [path.join(process.env.RESOURCES_PATH, 'terminal')]; + if (util.isNative()) { + terminal.push(cmd); + util.execFile(terminal).then(() => {}); + } else { + this.url(machineName).then(machineUrl => { + terminal.push(`DOCKER_HOST=${machineUrl} DOCKER_CERT_PATH=${path.join(util.home(), '.docker/machine/machines/' + machineName)} DOCKER_TLS_VERIFY=1`); + terminal.push(cmd); + util.execFile(terminal).then(() => {}); + }); + } } }, virtualBoxLogs: function (machineName = this.name()) { diff --git a/util/kitematic.png b/util/kitematic.png new file mode 100644 index 0000000000..75ae25e880 Binary files /dev/null and b/util/kitematic.png differ