diff --git a/Gruntfile.js b/Gruntfile.js index 15565958c9..abf1ae62b8 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -1,15 +1,10 @@ var path = require('path'); -var fs = require('fs'); var execFile = require('child_process').execFile; var packagejson = require('./package.json'); var electron = require('electron-prebuilt'); -var WINDOWS_DOCKER_URL = 'https://get.docker.com/builds/Windows/x86_64/docker-' + packagejson['docker-version'] + '.exe'; -var DARWIN_DOCKER_URL = 'https://get.docker.com/builds/Darwin/x86_64/docker-' + packagejson['docker-version']; var WINDOWS_DOCKER_MACHINE_URL = 'https://github.com/docker/machine/releases/download/v' + packagejson['docker-machine-version'] + '/docker-machine_windows-amd64.exe'; var DARWIN_DOCKER_MACHINE_URL = 'https://github.com/docker/machine/releases/download/v' + packagejson['docker-machine-version'] + '/docker-machine_darwin-amd64'; -var DARWIN_COMPOSE_URL = 'https://github.com/docker/compose/releases/download/' + packagejson['docker-compose-version'] + '/docker-compose-Darwin-x86_64'; -var BOOT2DOCKER_ISO_URL = 'https://github.com/boot2docker/boot2docker/releases/download/v' + packagejson['docker-version'] + '/boot2docker.iso'; module.exports = function (grunt) { require('load-grunt-tasks')(grunt); @@ -21,25 +16,12 @@ module.exports = function (grunt) { env.NODE_ENV = target; var certificateFile = grunt.option('certificateFile'); - var certificatePassword = grunt.option('certificatePassword'); var version = function (str) { var match = str.match(/(\d+\.\d+\.\d+)/); return match ? match[1] : null; }; - grunt.registerTask('download-boot2docker-iso', 'Downloads provided boot2docker version', function () { - try { - var data = fs.readFileSync(path.join('resources', 'boot2docker.iso'), {encoding: 'utf-8'}); - var match = data.match(/Boot2Docker-v(\d+\.\d+\.\d+)/); - if (match && match[1] !== packagejson['docker-version']) { - grunt.task.run('curl:boot2docker-iso'); - } - } catch (err) { - grunt.task.run('curl:boot2docker-iso'); - } - }); - grunt.registerMultiTask('download-binary', 'Downloads binary unless version up to date', function () { var target = grunt.task.current.target; var done = this.async(); @@ -64,15 +46,19 @@ module.exports = function (grunt) { APPNAME += ' (Beta)'; } - var OSX_OUT = './dist/osx'; - var OSX_FILENAME = OSX_OUT + '/' + APPNAME + '.app'; + var OSX_OUT = './dist'; + var OSX_OUT_X64 = OSX_OUT + '/' + APPNAME + '-darwin-x64'; + var OSX_FILENAME = OSX_OUT_X64 + '/' + APPNAME + '.app'; grunt.initConfig({ IDENTITY: 'Developer ID Application: Docker Inc', APPNAME: APPNAME, + APPNAME_ESCAPED: APPNAME.replace(/ /g, '\\ ').replace(/\(/g,'\\(').replace(/\)/g,'\\)'), OSX_OUT: OSX_OUT, + OSX_OUT_ESCAPED: OSX_OUT.replace(/ /g, '\\ ').replace(/\(/g,'\\(').replace(/\)/g,'\\)'), + OSX_OUT_X64: OSX_OUT_X64, OSX_FILENAME: OSX_FILENAME, - OSX_FILENAME_ESCAPED: OSX_FILENAME.replace(' ', '\\ ').replace('(','\\(').replace(')','\\)'), + OSX_FILENAME_ESCAPED: OSX_FILENAME.replace(/ /g, '\\ ').replace(/\(/g,'\\(').replace(/\)/g,'\\)'), // electron electron: { @@ -80,7 +66,7 @@ module.exports = function (grunt) { options: { name: BASENAME, dir: 'build/', - out: 'dist/', + out: 'dist', version: packagejson['electron-version'], platform: 'win32', arch: 'x64', @@ -92,7 +78,7 @@ module.exports = function (grunt) { options: { name: APPNAME, dir: 'build/', - out: '<%= OSX_OUT %>', + out: 'dist', version: packagejson['electron-version'], platform: 'darwin', arch: 'x64', @@ -103,11 +89,25 @@ module.exports = function (grunt) { } }, + prompt: { + 'create-windows-installer': { + options: { + questions: [ + { + config: 'certificatePassword', + type: 'password', + message: 'Certificate Password: ' + } + ] + } + } + }, + rcedit: { exes: { files: [{ expand: true, - cwd: 'dist/' + BASENAME + '-win32', + cwd: 'dist/' + BASENAME + '-win32-x64', src: [BASENAME + '.exe'] }], options: { @@ -115,7 +115,7 @@ module.exports = function (grunt) { 'file-version': packagejson.version, 'product-version': packagejson.version, 'version-string': { - 'CompanyName': 'Docker Inc', + 'CompanyName': 'Docker', 'ProductVersion': packagejson.version, 'ProductName': APPNAME, 'FileDescription': APPNAME, @@ -128,34 +128,27 @@ module.exports = function (grunt) { }, 'create-windows-installer': { - appDirectory: 'dist/' + BASENAME + '-win32/', - authors: 'Docker Inc.', - loadingGif: 'util/loading.gif', - setupIcon: 'util/setup.ico', - iconUrl: 'https://raw.githubusercontent.com/kitematic/kitematic/master/util/kitematic.ico', - description: APPNAME, - title: APPNAME, - exe: BASENAME + '.exe', - version: packagejson.version, - signWithParams: '/f ' + certificateFile + ' /p ' + certificatePassword + ' /tr http://timestamp.comodoca.com/rfc3161' + config: { + appDirectory: path.join(__dirname, 'dist/' + BASENAME + '-win32-x64'), + outputDirectory: path.join(__dirname, 'dist'), + authors: 'Docker Inc.', + loadingGif: 'util/loading.gif', + setupIcon: 'util/setup.ico', + iconUrl: 'https://raw.githubusercontent.com/kitematic/kitematic/master/util/kitematic.ico', + description: APPNAME, + title: APPNAME, + exe: BASENAME + '.exe', + version: packagejson.version, + signWithParams: '/f ' + certificateFile + ' /p <%= certificatePassword %> /tr http://timestamp.comodoca.com/rfc3161' + } }, // docker binaries 'download-binary': { - docker: { - version: packagejson['docker-version'], - binary: path.join('resources', 'docker'), - download: 'curl:docker' - }, 'docker-machine': { version: packagejson['docker-machine-version'], binary: path.join('resources', 'docker-machine'), download: 'curl:docker-machine' - }, - 'docker-compose': { - version: packagejson['docker-compose-version'], - binary: path.join('resources', 'docker-compose'), - download: 'curl:docker-compose' } }, @@ -188,8 +181,8 @@ module.exports = function (grunt) { files: [{ expand: true, cwd: 'resources', - src: ['docker*', 'boot2docker.iso', 'ssh.exe', 'OPENSSH_LICENSE', 'msys-*'], - dest: 'dist/' + BASENAME + '-win32/resources/resources/' + src: ['docker*', 'ssh.exe', 'OPENSSH_LICENSE', 'msys-*'], + dest: 'dist/' + BASENAME + '-win32-x64/resources/resources' }], options: { mode: true @@ -199,7 +192,7 @@ module.exports = function (grunt) { files: [{ expand: true, cwd: 'resources', - src: ['docker*', 'boot2docker.iso', 'macsudo', 'terminal'], + src: ['docker*', 'macsudo', 'terminal'], dest: '<%= OSX_FILENAME %>/Contents/Resources/resources/' }, { src: 'util/kitematic.icns', @@ -213,28 +206,16 @@ module.exports = function (grunt) { rename: { installer: { - src: 'installer/Setup.exe', - dest: 'installer/' + BASENAME + 'Setup-' + packagejson.version + '-Windows-Alpha.exe' + src: 'dist/Setup.exe', + dest: 'dist/' + BASENAME + 'Setup-' + packagejson.version + '-Windows-Alpha.exe' } }, // download binaries curl: { - docker: { - src: process.platform === 'win32' ? WINDOWS_DOCKER_URL : DARWIN_DOCKER_URL, - dest: process.platform === 'win32' ? path.join('resources', 'docker.exe') : path.join('resources', 'docker') - }, 'docker-machine': { src: process.platform === 'win32' ? WINDOWS_DOCKER_MACHINE_URL : DARWIN_DOCKER_MACHINE_URL, dest: process.platform === 'win32' ? path.join('resources', 'docker-machine.exe') : path.join('resources', 'docker-machine') - }, - 'docker-compose': { - src: DARWIN_COMPOSE_URL, - dest: 'resources/docker-compose' - }, - 'boot2docker-iso': { - src: BOOT2DOCKER_ISO_URL, - dest: path.join('resources', 'boot2docker.iso') } }, @@ -275,41 +256,6 @@ module.exports = function (grunt) { } }, - plistbuddy: { - addBundleURLTypes: { - method: 'Add', - entry: 'CFBundleURLTypes', - type: 'array', - src: '<%= OSX_FILENAME %>/Contents/Info.plist' - }, - addBundleURLTypesDict: { - method: 'Add', - entry: 'CFBundleURLTypes:0', - type: 'dict', - src: '<%= OSX_FILENAME %>/Contents/Info.plist' - }, - addBundleURLTypesDictName: { - method: 'Add', - entry: 'CFBundleURLTypes:0:CFBundleURLName', - type: 'string', - value: 'Docker App Protocol', - src: '<%= OSX_FILENAME %>/Contents/Info.plist' - }, - addBundleURLTypesDictSchemes: { - method: 'Add', - entry: 'CFBundleURLTypes:0:CFBundleURLSchemes', - type: 'array', - src: '<%= OSX_FILENAME %>/Contents/Info.plist' - }, - addBundleURLTypesDictSchemesDocker: { - method: 'Add', - entry: 'CFBundleURLTypes:0:CFBundleURLSchemes:0', - type: 'string', - value: 'docker', - src: '<%= OSX_FILENAME %>/Contents/Info.plist' - } - }, - shell: { electron: { command: electron + ' ' + 'build', @@ -332,13 +278,27 @@ module.exports = function (grunt) { ].join(' && '), }, zip: { - command: 'ditto -c -k --sequesterRsrc --keepParent <%= OSX_FILENAME_ESCAPED %> <%= OSX_OUT %>/' + BASENAME + '-' + packagejson.version + '-Mac.zip', + command: 'ditto -c -k --sequesterRsrc --keepParent <%= OSX_FILENAME_ESCAPED %> dist/' + BASENAME + '-' + packagejson.version + '-Mac.zip', } }, clean: { - release: ['build/', 'dist/', 'installer/'], - isos: ['resources/boot2docker*'] + release: ['build/', 'dist/'], + }, + + compress: { + windows: { + options: { + archive: './dist/' + BASENAME + '-' + packagejson.version + '-Windows-Alpha.zip', + mode: 'zip' + }, + files: [{ + expand: true, + dot: true, + cwd: './dist/Kitematic-win32-x64', + src: '**/*' + }] + }, }, // livereload @@ -365,16 +325,12 @@ module.exports = function (grunt) { } }); - if (process.platform === 'win32') { - grunt.registerTask('default', ['download-binary:docker', 'download-binary:docker-machine', 'download-boot2docker-iso', 'newer:babel', 'less', 'newer:copy:dev', 'shell:electron', 'watchChokidar']); - } else { - grunt.registerTask('default', ['download-binary', 'download-boot2docker-iso', 'newer:babel', 'less', 'newer:copy:dev', 'shell:electron', 'watchChokidar']); - } + grunt.registerTask('default', ['download-binary', 'newer:babel', 'less', 'newer:copy:dev', 'shell:electron', 'watchChokidar']); if (process.platform === 'win32') { - grunt.registerTask('release', ['clean:release', 'download-binary:docker', 'download-binary:docker-machine', 'download-boot2docker-iso', 'babel', 'less', 'copy:dev', 'electron:windows', 'copy:windows', 'rcedit:exes', 'create-windows-installer', 'rename:installer']); + grunt.registerTask('release', ['clean:release', 'download-binary', 'babel', 'less', 'copy:dev', 'electron:windows', 'copy:windows', 'rcedit:exes', 'compress', 'prompt:create-windows-installer', 'create-windows-installer', 'rename:installer']); } else { - grunt.registerTask('release', ['clean:release', 'download-binary', 'download-boot2docker-iso', 'babel', 'less', 'copy:dev', 'electron:osx', 'copy:osx', 'plistbuddy', 'shell:sign', 'shell:zip']); + grunt.registerTask('release', ['clean:release', 'download-binary', 'babel', 'less', 'copy:dev', 'electron:osx', 'copy:osx', 'shell:sign', 'shell:zip']); } process.on('SIGINT', function () { diff --git a/MAINTAINERS b/MAINTAINERS new file mode 100644 index 0000000000..f863c81756 --- /dev/null +++ b/MAINTAINERS @@ -0,0 +1,4 @@ +Jeff Morgan (@jeffdm) +Sean Li (@elesant) +Michael Chiang (@mchiang0610) +Ben French (@FrenchBen) diff --git a/README.md b/README.md index d8aa568120..4de3a967c7 100644 --- a/README.md +++ b/README.md @@ -11,12 +11,6 @@ Kitematic is a simple application for managing Docker containers on Mac and Wind [Download the latest version](https://kitematic.com/download) of Kitematic. -On the Mac, Kitematic can copy the binaries for Docker, Docker Machine and Docker Compose into `/usr/local/bin` for command line use outside of Kitematic. - -To do so please click on the Kitematic dropdown menu and select `Install Docker Commands` - -![Install Docker Commands](https://cloud.githubusercontent.com/assets/3325447/8246881/fa5f1ee0-15fa-11e5-936b-147bd53ec5a0.png) - ## Documentation Kitematic's documentation and other information can be found at [http://kitematic.com/docs](http://kitematic.com/docs). @@ -38,7 +32,7 @@ Please read through our [Contributing Guidelines](https://github.com/kitematic/k ## Community - [![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). +- Ask questions on our [user forum](https://forums.docker.com/c/open-source-projects/kitematic). - **#kitematic** on IRC. [Join the channel](http://webchat.freenode.net/?channels=%23kitematic&uio=d4). - Follow [@kitematic on Twitter](https://twitter.com/kitematic). diff --git a/ROADMAP.md b/ROADMAP.md index 3417331b8c..8eeb8d6ec7 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -28,4 +28,9 @@ **June 2015** -* Microsoft Windows port +* Microsoft Windows alpha + +**July 2015** + +* Refactor to Flux Architecture +* Stability & code quality improvements diff --git a/__tests__/SetupStore-test.js b/__tests__/SetupStore-test.js index 5c693c7331..184a2f3185 100644 --- a/__tests__/SetupStore-test.js +++ b/__tests__/SetupStore-test.js @@ -20,7 +20,7 @@ describe('SetupStore', function () { pit('downloads virtualbox if it is installed but has an outdated version', function () { virtualBox.installed.mockReturnValue(true); - virtualBox.version.mockReturnValue(Promise.resolve('4.3.16')); + virtualBox.version.mockReturnValue(Promise.resolve('5.0.0')); util.compareVersions.mockReturnValue(-1); setupUtil.download.mockReturnValue(Promise.resolve()); virtualBox.filename.mockReturnValue(''); @@ -55,6 +55,7 @@ describe('SetupStore', function () { machine.stop.mockReturnValue(Promise.resolve()); machine.start.mockReturnValue(Promise.resolve()); machine.upgrade.mockReturnValue(Promise.resolve()); + util.packagejson.mockReturnValue({'docker-version':'1.8.0'}); util.compareVersions.mockReturnValue(-1); machine.create.mockClear(); machine.upgrade.mockClear(); diff --git a/__tests__/Util-test.js b/__tests__/Util-test.js index 932185ca33..6b4367a8ee 100644 --- a/__tests__/Util-test.js +++ b/__tests__/Util-test.js @@ -30,6 +30,12 @@ describe('Util', function () { expect(util.removeSensitiveData(testdata).indexOf('/Users//.docker')).toNotEqual(-1); }); + it('filters Windows username data', function () { + var testdata = String.raw`C:\\Users\\johnappleseed\\.docker\\machine`; + expect(util.removeSensitiveData(testdata).indexOf('johnappleseed')).toEqual(-1); + expect(util.removeSensitiveData(testdata).indexOf('')).toNotEqual(-1); + }); + it ('returns input if empty or not a string', function () { expect(util.removeSensitiveData('')).toBe(''); expect(util.removeSensitiveData(1)).toBe(1); diff --git a/__tests__/VirtualBoxUtil-test.js b/__tests__/VirtualBoxUtil-test.js index 3bf9a4427b..12976e016e 100644 --- a/__tests__/VirtualBoxUtil-test.js +++ b/__tests__/VirtualBoxUtil-test.js @@ -3,17 +3,13 @@ var virtualBox = require('../src/utils/VirtualBoxUtil'); var util = require('../src/utils/Util'); describe('VirtualBox', function () { - it('returns the right command', function () { - expect(virtualBox.command()).toBe('/usr/bin/VBoxManage'); - }); - - describe('version 4.3.20r96996', function () { + describe('version 5.0.0r101573', function () { pit('correctly parses virtualbox version', function () { util.exec.mockImplementation(function () { - return Promise.resolve('4.3.20r96996'); + return Promise.resolve('5.0.0r101573'); }); return virtualBox.version().then(function (version) { - expect(version).toBe('4.3.20'); + expect(version).toBe('5.0.0'); }); }); }); diff --git a/docs/Dockerfile b/docs/Dockerfile index ac3cf79880..041cd99d5f 100644 --- a/docs/Dockerfile +++ b/docs/Dockerfile @@ -1,30 +1,27 @@ -FROM docs/base:hugo +FROM docs/base:latest MAINTAINER Mary Anthony (@moxiegirl) -# to get the git info for this repo +# To get the git info for this repo COPY . /src COPY . /docs/content/kitematic/ +RUN svn checkout https://github.com/docker/compose/trunk/docs /docs/content/compose +RUN svn checkout https://github.com/docker/docker/trunk/docs /docs/content/docker +RUN svn checkout https://github.com/docker/swarm/trunk/docs /docs/content/swarm +RUN svn checkout https://github.com/docker/distribution/trunk/docs /docs/content/registry +RUN svn checkout https://github.com/docker/tutorials/trunk/docs /docs/content +RUN svn checkout https://github.com/docker/opensource/trunk/docs /docs/content/opensource +RUN svn checkout https://github.com/docker/machine/trunk/docs /docs/content/machine + + # Sed to process GitHub Markdown # 1-2 Remove comment code from metadata block # 3 Change ](/word to ](/project/ in links # 4 Change ](word.md) to ](/project/word) # 5 Remove .md extension from link text -# 6 Change ](./ to ](/project/word) -# 7 Change ](../../ to ](/project/ -# 8 Change ](../ to ](/project/ -# 9 Change ](# to ](/project/ +# 6 Change ](../ to ](/project/word) +# 7 Change ](../../ to ](/project/ --> not implemented # -RUN find /docs/content/kitematic -type f -name "*.md" -exec sed -i.old \ - -e '/^/g' \ - -e '/^/g' \ - -e 's/\(\]\)\([(]\)\(\/\)/\1\2\/kitematic\//g' \ - -e 's/\(\][(]\)\([A-z].*\)\(\.md\)/\1\/kitematic\/\2/g' \ - -e 's/\([(]\)\(.*\)\(\.md\)/\1\2/g' \ - -e 's/\(\][(]\)\(\.\/\)/\1\/kitematic\//g' \ - -e 's/\(\][(]\)\(\.\.\/\.\.\/\)/\1\/kitematic\//g' \ - -e 's/\(\][(]\)\(\.\.\/\)/\1\/kitematic\//g' {} \; - - - \ No newline at end of file +# +RUN /src/pre-process.sh /docs diff --git a/docs/faq.md b/docs/faq.md index 69a7290e91..3b9cd4cfc5 100755 --- a/docs/faq.md +++ b/docs/faq.md @@ -21,8 +21,7 @@ software released under the Apache 2.0 license. ### How can I contribute to Kitematic? We always welcome (and deeply appreciate!) new contributions to the project. The -best way to start contributing to Kitematic is to review our doc on -[contributing](https://github.com/kitematic/kitematic/blob/master/CONTRIBUTING.md). +best way to start contributing to Kitematic is to review our doc on contributing. ### How does Kitematic work with Docker? @@ -31,10 +30,9 @@ the Docker Remote API. ### Which platforms does Kitematic support? -Right now Kitematic only works on Mac OS X. That said, Windows is on the -short-term -[roadmap](https://github.com/kitematic/kitematic/blob/master/ROADMAP.md) (coming -soon!) and a Linux version is in high demand. +Right now Kitematic works on Mac OS X and Windows. Linux is planned in the +future. Review our product roadmap. ### Why does Kitematic collect usage analytics and bug reports? diff --git a/docs/index.md b/docs/index.md index ef3fd45ec3..0fbbf1dd1b 100755 --- a/docs/index.md +++ b/docs/index.md @@ -9,45 +9,8 @@ weight=2 +++ -# Kitematic: Install Kitematic +# Kitematic -You install Kitematic much the same way you install any application on a Mac or -Windows PC: download an image and run an installer. +Kitematic, the Docker GUI, runs on Mac OS X and Windows operating systems. Beginning with the 1.8 Docker release, you use the Docker Toolbox to install Kitematic. See the [Mac OS X installation guide](https://docs.docker.com/mac) or the [Windows installation guide](https://docs.docker.com/installation/windows) for details on installing with Docker Toolbox. -## Download Kitematic - -[Download the Kitematic zip file](https://kitematic.com/download/), unzip the -file by double-clicking it, and then double-click the application to run it. -You'll probably also want to put the application in your Applications folder. - -## Initial Setup - -Opening Kitematic for the first time sets up everything you need to run Docker -containers. If you don't already have VirtualBox installed, Kitematic will -download and install the latest version. - -![Installing](../images/installing.png) - -All Done! Within a minute you should be ready to start running your first -container! - -![containers](../images/containers.png) - -## Technical Details - -Kitematic is a self-contained .app, with an exception: - -- It will install VirtualBox if it's not already installed. - -### Why does Kitematic need my root password? - -Kitematic needs your root password for two reasons: - -- Installing VirtualBox requires root access as it includes Mac OS X kernel extensions. -- Copying `docker` and `docker-machine` to `/usr/local/bin` may require root - permission if the default permissions for this directory have been changed - prior to installing Kitematic. - -## Next Steps - -For information about using Kitematic, take a look at the [User Guide](/userguide). +For information about using Kitematic, take a look at the [User Guide](userguide). diff --git a/docs/known-issues.md b/docs/known-issues.md index a6dd7dbf3b..a12c9bbe1b 100755 --- a/docs/known-issues.md +++ b/docs/known-issues.md @@ -29,8 +29,8 @@ commands on the command line: - `docker-machine create -d virtualbox dev` Then re-open Kitematic. This usually fixes the issue, but if it persists, feel -free to view our [existing GitHub -issues](https://github.com/kitematic/kitematic/issues?q=is%3Aopen+is%3Aissue+label%3Abug). +free to view our existing GitHub +issues. ## Contributing Fixes @@ -46,6 +46,5 @@ if you're looking to help fix specific issues around VM provisioning. ## View All Issues -For a full list of Kitematic bugs or issues see our [GitHub -issues](https://github.com/kitematic/kitematic/issues?q=is%3Aopen+is%3Aissue+label%3Abug) -labelled as `bug`. +For a full list of Kitematic bugs or issues see our existing GitHub +issues labelled as `bug`. diff --git a/docs/pre-process.sh b/docs/pre-process.sh new file mode 100755 index 0000000000..75e9611f2f --- /dev/null +++ b/docs/pre-process.sh @@ -0,0 +1,61 @@ +#!/bin/bash -e + +# Populate an array with just docker dirs and one with content dirs +docker_dir=(`ls -d /docs/content/docker/*`) +content_dir=(`ls -d /docs/content/*`) + +# Loop content not of docker/ +# +# Sed to process GitHub Markdown +# 1-2 Remove comment code from metadata block +# 3 Remove .md extension from link text +# 4 Change ](/ to ](/project/ in links +# 5 Change ](word) to ](/project/word) +# 6 Change ](../../ to ](/project/ +# 7 Change ](../ to ](/project/word) +# +for i in "${content_dir[@]}" +do + : + case $i in + "/docs/content/windows") + ;; + "/docs/content/mac") + ;; + "/docs/content/linux") + ;; + "/docs/content/docker") + y=${i##*/} + find $i -type f -name "*.md" -exec sed -i.old \ + -e '/^/g' \ + -e '/^/g' {} \; + ;; + *) + y=${i##*/} + find $i -type f -name "*.md" -exec sed -i.old \ + -e '/^/g' \ + -e '/^/g' \ + -e 's/\(\]\)\([(]\)\(\/\)/\1\2\/'$y'\//g' \ + -e 's/\(\][(]\)\([A-z].*\)\(\.md\)/\1\/'$y'\/\2/g' \ + -e 's/\([(]\)\(.*\)\(\.md\)/\1\2/g' \ + -e 's/\(\][(]\)\(\.\/\)/\1\/'$y'\//g' \ + -e 's/\(\][(]\)\(\.\.\/\.\.\/\)/\1\/'$y'\//g' \ + -e 's/\(\][(]\)\(\.\.\/\)/\1\/'$y'\//g' {} \; + ;; + esac +done + +# +# Move docker directories to content +# +for i in "${docker_dir[@]}" +do + : + if [ -d $i ] + then + mv $i /docs/content/ + fi +done + +rm -rf /docs/content/docker + diff --git a/docs/userguide.md b/docs/userguide.md index a281109b0e..6589d5c1a6 100755 --- a/docs/userguide.md +++ b/docs/userguide.md @@ -2,7 +2,7 @@ +++ title = "Kitematic User Guide: Intro & Overview" description = "Documentation that provides an overview of Kitematic and installation instructions" -keywords = ["docker, documentation, about, technology, kitematic, gui"] +keywords = ["docker, documentation, about, technology, kitematic, gui"] [menu.main] parent="smn_workw_kitematic" +++ @@ -16,12 +16,12 @@ Kitematic is an open source project built to simplify and streamline using Docker on a Mac or Windows (coming soon) PC. Kitematic automates the Docker installation and setup process and provides an intuitive graphical user interface (GUI) for running Docker containers. Kitematic integrates with -[Docker Machine](http://docs.docker.com/machine/) to provision a VirtualBox VM +[Docker Machine](https://docs.docker.com/machine/) to provision a VirtualBox VM and install the Docker Engine locally on your machine. Once installed, the Kitematic GUI launches and from the home screen you will be presented with curated images that you can run instantly. You can search for any -public images on Docker Hub from Kitematic just by typing in the search bar. +public images on Docker Hub from Kitematic just by typing in the search bar. You can use the GUI to create, run and manage your containers just by clicking on buttons. Kitematic allows you to switch back and forth between the Docker CLI and the GUI. Kitematic also automates advanced features such as managing ports @@ -78,17 +78,17 @@ The currently detected "web" ports are, `80`, `8000`, `8080`, `3000`, `5000`, ### Viewing container logs -You can view the entire main container process' log output either by cicking on the "Logs" +You can view the entire main container process' log output either by clicking on the "Logs" preview image, or by clicking on the "Logs" tab. You can then scroll through the logs from the current running container. Note that -if you make changes to the container Settings, then the container will be restarted, -so will reset this log view. +if you make changes to the container settings, then the container will be restarted, +so this will reset this log view. ### Starting a terminal in a container The "Terminal" icon at the top of the container summary will `docker exec sh `. -This will allow you to make quick changes, or to debug a problem. +This will allow you to make quick changes, or to debug a problem. > **Note**: Your exec'ed `sh` process will not have the same environment settings > as the main container process and its children. @@ -100,7 +100,7 @@ on your Mac by clicking on the folders in the "Edit Files" section of the container summary screen. This allows you to manage files in volumes via the Finder. -Kitematic exposes a container's volume data under `~/Kitematic//`. +Kitematic exposes a container's volume data under `~/Documents/Kitematic//`. Quick access to this folder (or directory) is available via the app: ![Accessing the volumes directory](../images/volumes-dir.png) @@ -117,7 +117,7 @@ use the default directory created for the website_files volume. Instead, you already have the HTML, Javascript, and CSS for your website under `~/workspace/website`. -Navigate to the "Settings" tab of the container, and goto the "Volumes". This +Navigate to the "Settings" tab of the container, and go to the "Volumes". This screen allows you to set the mappings individually. ![screen shot 2015-02-28 at 2 48 01 pm](../images/change-folder.png) @@ -142,12 +142,12 @@ Many images use environment variables to let you customise them. The "General" "Settings" tab allows you to add and modify the environment variables used to start a container. -The list of Environment variables will show any that have been set on the image +The list of environment variables will show any that have been set on the image metadata - for example, using the `ENV` instruction in the Dockerfile. -Wen you "Save" the changed environment variables, the container will be stopped +When you "Save" the changed environment variables, the container will be stopped, removed and re-created. ### Delete container @@ -155,14 +155,14 @@ removed and re-created. On the "General" "Settings" tab, you can delete the container. Clicking "Delete Container" will also stop the container if necessary. -You can also delete a container clicking the `X` icon in the container list. +You can also delete a container by clicking the `X` icon in the container list. Kitematic will prompt you to confirm that you want to delete. #### List the exposed Ports and how to access them To see the complete list of exposed ports, go to "Settings" then "Ports". This -page lists all the container ports exposed, and the IP address and host only +page lists all the container ports exposed, and the IP address and host-only network port that you can access use to access that container from your OS X system. @@ -172,7 +172,7 @@ You can interact with existing containers in Kitematic or create new containers via the Docker Command Line Interface (CLI). Any changes you make on the CLI are directly reflected in Kitematic. -To open a terminal via Kitematic, just press whale button at the bottom left, as +To open a terminal via Kitematic, just press the whale button at the bottom left, as shown below: ![CLI access button](../images/cli-access-button.png) @@ -185,9 +185,9 @@ will pull and run a new Redis container via the Docker CLI. ![Docker CLI terminal window](../images/cli-terminal.png) -> **Note**: If you're creating containers from the commandline, use `docker run -d` +> **Note**: If you're creating containers from the command line, use `docker run -d` > so that Kitematic can re-create the container when settings are changed via the -> Kitematic user interface. containers started without `-d` will fail to re-start. +> Kitematic user interface. Containers started without `-d` will fail to re-start. Now, go back to Kitematic. The Redis container should now be visible. @@ -196,4 +196,4 @@ Now, go back to Kitematic. The Redis container should now be visible. ## Next Steps For an example using Kitematic to run a Minecraft server, take a look at -the [Mincraft server](./minecraft-server.md) page. +the [Minecraft server](./minecraft-server.md) page. diff --git a/images/cartoon-docker-compose.png b/images/cartoon-docker-compose.png new file mode 100644 index 0000000000..26594ae27b Binary files /dev/null and b/images/cartoon-docker-compose.png differ diff --git a/images/cartoon-docker-compose@2x.png b/images/cartoon-docker-compose@2x.png new file mode 100644 index 0000000000..70a83edfda Binary files /dev/null and b/images/cartoon-docker-compose@2x.png differ diff --git a/images/cartoon-docker-machine.png b/images/cartoon-docker-machine.png new file mode 100644 index 0000000000..15ca37ad9b Binary files /dev/null and b/images/cartoon-docker-machine.png differ diff --git a/images/cartoon-docker-machine@2x.png b/images/cartoon-docker-machine@2x.png new file mode 100644 index 0000000000..6410040847 Binary files /dev/null and b/images/cartoon-docker-machine@2x.png differ diff --git a/images/cartoon-docker.png b/images/cartoon-docker.png new file mode 100644 index 0000000000..7eb4d4daa6 Binary files /dev/null and b/images/cartoon-docker.png differ diff --git a/images/cartoon-docker@2x.png b/images/cartoon-docker@2x.png new file mode 100644 index 0000000000..3de525f1f1 Binary files /dev/null and b/images/cartoon-docker@2x.png differ diff --git a/images/cartoon-kitematic.png b/images/cartoon-kitematic.png new file mode 100644 index 0000000000..495aabae1d Binary files /dev/null and b/images/cartoon-kitematic.png differ diff --git a/images/cartoon-kitematic@2x.png b/images/cartoon-kitematic@2x.png new file mode 100644 index 0000000000..b0621c99e1 Binary files /dev/null and b/images/cartoon-kitematic@2x.png differ diff --git a/package.json b/package.json index e49b7c56a2..0163b80a3a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "Kitematic", - "version": "0.7.3", + "version": "0.8.3", "author": "Kitematic", "description": "Simple Docker Container management for Mac OS X.", "homepage": "https://kitematic.com/", @@ -22,15 +22,14 @@ "url": "http://www.apache.org/licenses/LICENSE-2.0.html" } ], - "docker-version": "1.7.0", - "docker-machine-version": "0.3.0-rc2", - "docker-compose-version": "1.3.0", + "docker-version": "1.8.1", + "docker-machine-version": "0.4.1", "electron-version": "0.27.2", - "virtualbox-version": "4.3.28", - "virtualbox-filename": "VirtualBox-4.3.28.pkg", - "virtualbox-filename-win": "VirtualBox-4.3.28.exe", - "virtualbox-checksum": "60521caff652fc32ad733eee2eea27f03d0b4f7239af3bf4b21dc6f0251cf47a", - "virtualbox-checksum-win": "82039b615bf18dfff92ac1d9a15b2cbd5c59c608631ccf77e2371d0fd67a6cf7", + "virtualbox-version": "5.0.2", + "virtualbox-filename": "VirtualBox-5.0.2.pkg", + "virtualbox-filename-win": "VirtualBox-5.0.2.exe", + "virtualbox-checksum": "384414edab277c376a2ac3d02e5f316434fa4d54d31db44dc85b6e560eda4143", + "virtualbox-checksum-win": "a8183d21566fc6cb327e4b71303fa0d04fc6cd079ba83d66818d18c5ef427037", "dependencies": { "alt": "^0.16.2", "ansi-to-html": "0.3.0", @@ -71,16 +70,18 @@ "grunt-chmod": "^1.0.3", "grunt-cli": "^0.1.13", "grunt-contrib-clean": "^0.6.0", + "grunt-contrib-compress": "^0.13.0", "grunt-contrib-copy": "^0.8.0", "grunt-contrib-less": "^1.0.1", "grunt-contrib-watch-chokidar": "^1.0.0", "grunt-curl": "^2.2.0", "grunt-download-electron": "^2.1.1", - "grunt-electron": "^1.0.0", - "grunt-electron-installer": "^0.33.0", + "grunt-electron": "^2.0.0", + "grunt-electron-installer": "^0.37.0", "grunt-if-missing": "^1.0.0", "grunt-newer": "^1.1.1", "grunt-plistbuddy": "^0.1.1", + "grunt-prompt": "^1.3.0", "grunt-rcedit": "^0.3.1", "grunt-rename": "^0.1.4", "grunt-shell": "^1.1.2", diff --git a/resources/terminal b/resources/terminal index af5af69872..ea55c18810 100755 --- a/resources/terminal +++ b/resources/terminal @@ -1,8 +1,7 @@ #!/bin/bash DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" -PATH=$DIR:$PATH -CMD="export PATH='$PATH' && clear && $*" +CMD="clear && $*" ITERM_EXISTS=`osascript < +
+ Go Back +
+
+ +

Docker {packages.name}

+

{packages.version}

+
+
+

Kitematic is built with:

+
+
+ +

Docker Engine

+

{packages["docker-version"]}

+
+
+ +

Docker Machine

+

{packages["docker-machine-version"]}

+
+
+

Third-Party Software

+
+
+

VirtualBox

+

{packages["virtualbox-version"]}

+
+
+
+
+

Electron

+

{packages["electron-version"]}

+
+
+
+ + ); + } +}); + +module.exports = Preferences; diff --git a/src/components/Account.react.js b/src/components/Account.react.js index bf5c486d4c..eed1b87aec 100644 --- a/src/components/Account.react.js +++ b/src/components/Account.react.js @@ -1,10 +1,10 @@ -var React = require('react/addons'); -var Router = require('react-router'); -var RetinaImage = require('react-retina-image'); -var Header = require('./Header.react'); -var metrics = require('../utils/MetricsUtil'); -var accountStore = require('../stores/AccountStore'); -var accountActions = require('../actions/AccountActions'); +import React from 'react/addons'; +import Router from 'react-router'; +import RetinaImage from 'react-retina-image'; +import Header from './Header.react'; +import metrics from '../utils/MetricsUtil'; +import accountStore from '../stores/AccountStore'; +import accountActions from '../actions/AccountActions'; module.exports = React.createClass({ mixins: [Router.Navigation], diff --git a/src/components/AccountLogin.react.js b/src/components/AccountLogin.react.js index 427c272511..7aeae351e7 100644 --- a/src/components/AccountLogin.react.js +++ b/src/components/AccountLogin.react.js @@ -1,10 +1,10 @@ -var _ = require('underscore'); -var React = require('react/addons'); -var Router = require('react-router'); -var validator = require('validator'); -var accountActions = require('../actions/AccountActions'); -var metrics = require('../utils/MetricsUtil'); -var shell = require('shell'); +import _ from 'underscore'; +import React from 'react/addons'; +import Router from 'react-router'; +import validator from 'validator'; +import accountActions from '../actions/AccountActions'; +import metrics from '../utils/MetricsUtil'; +import shell from 'shell'; module.exports = React.createClass({ mixins: [Router.Navigation, React.addons.LinkedStateMixin], @@ -60,7 +60,7 @@ module.exports = React.createClass({ }, handleClickForgotPassword: function () { - shell.openExternal('https://hub.docker.com/account/forgot-password/'); + shell.openExternal('https://hub.docker.com/reset-password/'); }, render: function () { diff --git a/src/components/AccountSignup.react.js b/src/components/AccountSignup.react.js index 6c4b85da11..f1a23ff478 100644 --- a/src/components/AccountSignup.react.js +++ b/src/components/AccountSignup.react.js @@ -1,9 +1,9 @@ -var _ = require('underscore'); -var React = require('react/addons'); -var Router = require('react-router'); -var validator = require('validator'); -var accountActions = require('../actions/AccountActions'); -var metrics = require('../utils/MetricsUtil'); +import _ from 'underscore'; +import React from 'react/addons'; +import Router from 'react-router'; +import validator from 'validator'; +import accountActions from '../actions/AccountActions'; +import metrics from '../utils/MetricsUtil'; module.exports = React.createClass({ mixins: [Router.Navigation, React.addons.LinkedStateMixin], diff --git a/src/components/ContainerDetails.react.js b/src/components/ContainerDetails.react.js index 793de1cbd2..e1d5668760 100644 --- a/src/components/ContainerDetails.react.js +++ b/src/components/ContainerDetails.react.js @@ -1,10 +1,10 @@ -var React = require('react/addons'); -var Router = require('react-router'); -var ContainerDetailsHeader = require('./ContainerDetailsHeader.react'); -var ContainerDetailsSubheader = require('./ContainerDetailsSubheader.react'); -var containerUtil = require('../utils/ContainerUtil'); -var util = require('../utils/Util'); -var _ = require('underscore'); +import React from 'react/addons'; +import Router from 'react-router'; +import ContainerDetailsHeader from './ContainerDetailsHeader.react'; +import ContainerDetailsSubheader from './ContainerDetailsSubheader.react'; +import containerUtil from '../utils/ContainerUtil'; +import util from '../utils/Util'; +import _ from 'underscore'; var ContainerDetails = React.createClass({ contextTypes: { diff --git a/src/components/ContainerDetailsHeader.react.js b/src/components/ContainerDetailsHeader.react.js index 363e55a3d7..70e90e3ef8 100644 --- a/src/components/ContainerDetailsHeader.react.js +++ b/src/components/ContainerDetailsHeader.react.js @@ -1,4 +1,4 @@ -var React = require('react/addons'); +import React from 'react/addons'; var ContainerDetailsHeader = React.createClass({ render: function () { diff --git a/src/components/ContainerDetailsSubheader.react.js b/src/components/ContainerDetailsSubheader.react.js index e53204bd90..b793e8332b 100644 --- a/src/components/ContainerDetailsSubheader.react.js +++ b/src/components/ContainerDetailsSubheader.react.js @@ -1,11 +1,11 @@ -var _ = require('underscore'); -var React = require('react'); -var shell = require('shell'); -var metrics = require('../utils/MetricsUtil'); -var ContainerUtil = require('../utils/ContainerUtil'); -var classNames = require('classnames'); -var containerActions = require('../actions/ContainerActions'); -var dockerMachineUtil = require('../utils/DockerMachineUtil'); +import _ from 'underscore'; +import React from 'react'; +import shell from 'shell'; +import metrics from '../utils/MetricsUtil'; +import ContainerUtil from '../utils/ContainerUtil'; +import classNames from 'classnames'; +import containerActions from '../actions/ContainerActions'; +import dockerMachineUtil from '../utils/DockerMachineUtil'; var ContainerDetailsSubheader = React.createClass({ contextTypes: { diff --git a/src/components/ContainerHome.react.js b/src/components/ContainerHome.react.js index 198730eeb1..f47cd9620b 100644 --- a/src/components/ContainerHome.react.js +++ b/src/components/ContainerHome.react.js @@ -1,12 +1,12 @@ -var _ = require('underscore'); -var $ = require('jquery'); -var React = require('react/addons'); -var Radial = require('./Radial.react'); -var ContainerProgress = require('./ContainerProgress.react'); -var ContainerHomePreview = require('./ContainerHomePreview.react'); -var ContainerHomeLogs = require('./ContainerHomeLogs.react'); -var ContainerHomeFolders = require('./ContainerHomeFolders.react'); -var shell = require('shell'); +import _ from 'underscore'; +import $ from 'jquery'; +import React from 'react/addons'; +import Radial from './Radial.react'; +import ContainerProgress from './ContainerProgress.react'; +import ContainerHomePreview from './ContainerHomePreview.react'; +import ContainerHomeLogs from './ContainerHomeLogs.react'; +import ContainerHomeFolders from './ContainerHomeFolders.react'; +import shell from 'shell'; var ContainerHome = React.createClass({ contextTypes: { @@ -39,11 +39,11 @@ var ContainerHome = React.createClass({ showWeb: function () { return _.keys(this.props.ports).length > 0; }, - + showFolders: function () { - return this.props.container.Volumes && _.keys(this.props.container.Volumes).length > 0 && this.props.container.State.Running; + return this.props.container.Mounts && this.props.container.Mounts.length > 0 && this.props.container.State.Running; }, - + render: function () { if (!this.props.container) { return; diff --git a/src/components/ContainerHomeFolders.react.js b/src/components/ContainerHomeFolders.react.js index 53ba985fe5..0aecb10622 100644 --- a/src/components/ContainerHomeFolders.react.js +++ b/src/components/ContainerHomeFolders.react.js @@ -1,42 +1,49 @@ -var _ = require('underscore'); -var React = require('react/addons'); -var RetinaImage = require('react-retina-image'); -var path = require('path'); -var shell = require('shell'); -var util = require('../utils/Util'); -var metrics = require('../utils/MetricsUtil'); -var containerActions = require('../actions/ContainerActions'); -var dialog = require('remote').require('dialog'); -var mkdirp = require('mkdirp'); +import _ from 'underscore'; +import React from 'react/addons'; +import RetinaImage from 'react-retina-image'; +import path from 'path'; +import shell from 'shell'; +import util from '../utils/Util'; +import metrics from '../utils/MetricsUtil'; +import containerActions from '../actions/ContainerActions'; +import remote from 'remote'; +var dialog = remote.require('dialog'); +import mkdirp from 'mkdirp'; var ContainerHomeFolder = React.createClass({ contextTypes: { router: React.PropTypes.func }, - handleClickFolder: function (hostVolume, containerVolume) { + handleClickFolder: function (source, destination) { metrics.track('Opened Volume Directory', { from: 'home' }); - if (hostVolume.indexOf(util.windowsToLinuxPath(util.home())) === -1) { + if (source.indexOf(util.windowsToLinuxPath(util.home())) === -1) { dialog.showMessageBox({ - message: 'Enable all volumes to edit files via Finder? This may not work with all database containers.', + message: `Enable all volumes to edit files? 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 = util.escapePath(path.join(util.home(), util.documents(), 'Kitematic', this.props.container.Name, containerVolume)); - volumes[containerVolume] = newHostVolume; - var binds = _.pairs(volumes).map(function (pair) { - if(util.isWindows()) { - return util.windowsToLinuxPath(pair[1]) + ':' + pair[0]; + var mounts = _.clone(this.props.container.Mounts); + var newSource = util.escapePath(path.join(util.home(), util.documents(), 'Kitematic', this.props.container.Name, destination)); + var binds = mounts.map(function (m) { + let source = m.Source; + if (m.Destination === destination) { + source = newSource; } - return pair[1] + ':' + pair[0]; + + if(util.isWindows()) { + return util.windowsToLinuxPath(source) + ':' + m.Destination; + } + + return source + ':' + m.Destination; }); - mkdirp(newHostVolume, function (err) { + + mkdirp(newSource, function (err) { console.log(err); if (!err) { - shell.showItemInFolder(newHostVolume); + shell.showItemInFolder(newSource); } }); @@ -44,7 +51,7 @@ var ContainerHomeFolder = React.createClass({ } }); } else { - let path = util.isWindows() ? util.linuxToWindowsPath(hostVolume) : hostVolume; + let path = util.isWindows() ? util.linuxToWindowsPath(source) : source; shell.showItemInFolder(path); } }, @@ -59,12 +66,13 @@ var ContainerHomeFolder = React.createClass({ return false; } - var folders = _.map(_.omit(this.props.container.Volumes, (v, k) => k.indexOf('/Users/') !== -1), (val, key) => { - var firstFolder = key; + var folders = _.map(this.props.container.Mounts, (m, i) => { + let destination = m.Destination; + let source = m.Source; return ( -
+
-
{firstFolder}
+
{destination}
); }); diff --git a/src/components/ContainerHomeLogs.react.js b/src/components/ContainerHomeLogs.react.js index d673c82db3..0853f98692 100644 --- a/src/components/ContainerHomeLogs.react.js +++ b/src/components/ContainerHomeLogs.react.js @@ -1,8 +1,8 @@ -var $ = require('jquery'); -var React = require('react/addons'); -var LogStore = require('../stores/LogStore'); -var Router = require('react-router'); -var metrics = require('../utils/MetricsUtil'); +import $ from 'jquery'; +import React from 'react/addons'; +import LogStore from '../stores/LogStore'; +import Router from 'react-router'; +import metrics from '../utils/MetricsUtil'; var _prevBottom = 0; diff --git a/src/components/ContainerHomePreview.react.js b/src/components/ContainerHomePreview.react.js index c4d7818d4f..74efe462df 100644 --- a/src/components/ContainerHomePreview.react.js +++ b/src/components/ContainerHomePreview.react.js @@ -1,8 +1,8 @@ -var _ = require('underscore'); -var React = require('react/addons'); -var request = require('request'); -var shell = require('shell'); -var metrics = require('../utils/MetricsUtil'); +import _ from 'underscore'; +import React from 'react/addons'; +import request from 'request'; +import shell from 'shell'; +import metrics from '../utils/MetricsUtil'; var ContainerHomePreview = React.createClass({ contextTypes: { diff --git a/src/components/ContainerList.react.js b/src/components/ContainerList.react.js index 4a042b4c81..ee557c0110 100644 --- a/src/components/ContainerList.react.js +++ b/src/components/ContainerList.react.js @@ -1,5 +1,5 @@ -var React = require('react/addons'); -var ContainerListItem = require('./ContainerListItem.react'); +import React from 'react/addons'; +import ContainerListItem from './ContainerListItem.react'; var ContainerList = React.createClass({ componentWillMount: function () { diff --git a/src/components/ContainerListItem.react.js b/src/components/ContainerListItem.react.js index 0955ef2f74..589ff6ef3c 100644 --- a/src/components/ContainerListItem.react.js +++ b/src/components/ContainerListItem.react.js @@ -1,12 +1,11 @@ -var $ = require('jquery'); -var React = require('react/addons'); -var Router = require('react-router'); -var remote = require('remote'); +import $ from 'jquery'; +import React from 'react/addons'; +import Router from 'react-router'; +import remote from 'remote'; var dialog = remote.require('dialog'); -var metrics = require('../utils/MetricsUtil'); -var OverlayTrigger = require('react-bootstrap').OverlayTrigger; -var Tooltip = require('react-bootstrap').Tooltip; -var containerActions = require('../actions/ContainerActions'); +import metrics from '../utils/MetricsUtil'; +import {OverlayTrigger, Tooltip} from 'react-bootstrap'; +import containerActions from '../actions/ContainerActions'; var ContainerListItem = React.createClass({ handleItemMouseEnter: function () { diff --git a/src/components/ContainerLogs.react.js b/src/components/ContainerLogs.react.js index 6355154ee4..7da5e84129 100644 --- a/src/components/ContainerLogs.react.js +++ b/src/components/ContainerLogs.react.js @@ -1,6 +1,6 @@ -var $ = require('jquery'); -var React = require('react/addons'); -var LogStore = require('../stores/LogStore'); +import $ from 'jquery'; +import React from 'react/addons'; +import LogStore from '../stores/LogStore'; var _prevBottom = 0; diff --git a/src/components/ContainerProgress.react.js b/src/components/ContainerProgress.react.js index 175fb4d72a..ba51d4564d 100644 --- a/src/components/ContainerProgress.react.js +++ b/src/components/ContainerProgress.react.js @@ -1,4 +1,4 @@ -var React = require('react'); +import React from 'react'; /* diff --git a/src/components/ContainerSettings.react.js b/src/components/ContainerSettings.react.js index def09db363..6a9f191781 100644 --- a/src/components/ContainerSettings.react.js +++ b/src/components/ContainerSettings.react.js @@ -1,7 +1,7 @@ -var $ = require('jquery'); -var _ = require('underscore'); -var React = require('react/addons'); -var Router = require('react-router'); +import $ from 'jquery'; +import _ from 'underscore'; +import React from 'react/addons'; +import Router from 'react-router'; var ContainerSettings = React.createClass({ contextTypes: { diff --git a/src/components/ContainerSettingsAdvanced.react.js b/src/components/ContainerSettingsAdvanced.react.js index c041445472..6120498c3c 100644 --- a/src/components/ContainerSettingsAdvanced.react.js +++ b/src/components/ContainerSettingsAdvanced.react.js @@ -1,7 +1,7 @@ -var React = require('react/addons'); -var metrics = require('../utils/MetricsUtil'); -var ContainerUtil = require('../utils/ContainerUtil'); -var containerActions = require('../actions/ContainerActions'); +import React from 'react/addons'; +import metrics from '../utils/MetricsUtil'; +import ContainerUtil from '../utils/ContainerUtil'; +import containerActions from '../actions/ContainerActions'; var ContainerSettingsAdvanced = React.createClass({ mixins: [React.addons.LinkedStateMixin], diff --git a/src/components/ContainerSettingsGeneral.react.js b/src/components/ContainerSettingsGeneral.react.js index e95bee4b06..a9650d43ce 100644 --- a/src/components/ContainerSettingsGeneral.react.js +++ b/src/components/ContainerSettingsGeneral.react.js @@ -1,11 +1,11 @@ -var _ = require('underscore'); -var React = require('react/addons'); -var remote = require('remote'); -var metrics = require('../utils/MetricsUtil'); +import _ from 'underscore'; +import React from 'react/addons'; +import metrics from '../utils/MetricsUtil'; +import remote from 'remote'; var dialog = remote.require('dialog'); -var ContainerUtil = require('../utils/ContainerUtil'); -var containerActions = require('../actions/ContainerActions'); -var util = require('../utils/Util'); +import ContainerUtil from '../utils/ContainerUtil'; +import containerActions from '../actions/ContainerActions'; +import util from '../utils/Util'; var ContainerSettingsGeneral = React.createClass({ mixins: [React.addons.LinkedStateMixin], diff --git a/src/components/ContainerSettingsPorts.react.js b/src/components/ContainerSettingsPorts.react.js index 1738db5a5b..bfd2537b4b 100644 --- a/src/components/ContainerSettingsPorts.react.js +++ b/src/components/ContainerSettingsPorts.react.js @@ -1,9 +1,9 @@ -var _ = require('underscore'); -var React = require('react/addons'); -var shell = require('shell'); -var ContainerUtil = require('../utils/ContainerUtil'); -var metrics = require('../utils/MetricsUtil'); -var webPorts = require('../utils/Util').webPorts; +import _ from 'underscore'; +import React from 'react/addons'; +import shell from 'shell'; +import ContainerUtil from '../utils/ContainerUtil'; +import metrics from '../utils/MetricsUtil'; +import {webPorts} from '../utils/Util'; var ContainerSettingsPorts = React.createClass({ contextTypes: { diff --git a/src/components/ContainerSettingsVolumes.react.js b/src/components/ContainerSettingsVolumes.react.js index 154bc87666..4a3a920ec3 100644 --- a/src/components/ContainerSettingsVolumes.react.js +++ b/src/components/ContainerSettingsVolumes.react.js @@ -1,11 +1,11 @@ -var _ = require('underscore'); -var React = require('react/addons'); -var remote = require('remote'); +import _ from 'underscore'; +import React from 'react/addons'; +import remote from 'remote'; var dialog = remote.require('dialog'); -var shell = require('shell'); -var util = require('../utils/Util'); -var metrics = require('../utils/MetricsUtil'); -var containerActions = require('../actions/ContainerActions'); +import shell from 'shell'; +import util from '../utils/Util'; +import metrics from '../utils/MetricsUtil'; +import containerActions from '../actions/ContainerActions'; var ContainerSettingsVolumes = React.createClass({ handleChooseVolumeClick: function (dockerVol) { @@ -26,16 +26,23 @@ var ContainerSettingsVolumes = React.createClass({ } metrics.track('Choose Directory for Volume'); + if(util.isWindows()) { directory = util.escapePath(util.windowsToLinuxPath(directory)); } - var volumes = _.clone(this.props.container.Volumes); - volumes[dockerVol] = directory; - var binds = _.pairs(volumes).map(function (pair) { - return pair[1] + ':' + pair[0]; + + var mounts = _.clone(this.props.container.Mounts); + _.each(mounts, m => { + if (m.Destination === dockerVol) { + m.Source = directory; + } }); - containerActions.update(this.props.container.Name, {Binds: binds, Volumes: volumes}); + var binds = mounts.map(m => { + return m.Source + ':' + m.Destination; + }); + + containerActions.update(this.props.container.Name, {Binds: binds, Mounts: mounts}); }); }, handleRemoveVolumeClick: function (dockerVol) { @@ -45,13 +52,17 @@ var ContainerSettingsVolumes = React.createClass({ var hostConfig = _.clone(this.props.container.HostConfig); var binds = hostConfig.Binds; - var volumes = _.clone(this.props.container.Volumes); - volumes[dockerVol] = null; + var mounts = _.clone(this.props.container.Mounts); + _.each(mounts, m => { + if (m.Destination === dockerVol) { + m.Source = null; + } + }); var index = _.findIndex(binds, bind => bind.indexOf(`:${dockerVol}`) !== -1); if (index >= 0) { binds.splice(index, 1); } - containerActions.update(this.props.container.Name, {HostConfig: hostConfig, Binds: binds, Volumes: volumes}); + containerActions.update(this.props.container.Name, {HostConfig: hostConfig, Binds: binds, Mounts: mounts}); }, handleOpenVolumeClick: function (path) { metrics.track('Opened Volume Directory', { @@ -69,24 +80,25 @@ var ContainerSettingsVolumes = React.createClass({ } var homeDir = util.isWindows() ? util.windowsToLinuxPath(util.home()) : util.home(); - var volumes = _.map(this.props.container.Volumes, (val, key) => { - if (!val || val.indexOf(homeDir) === -1) { - val = ( + var mounts= _.map(this.props.container.Mounts, (m, i) => { + let source = m.Source, destination = m.Destination; + if (!m.Source || m.Source.indexOf(homeDir) === -1) { + source = ( No Folder ); } else { - let local = util.isWindows() ? util.linuxToWindowsPath(val) : val; - val = ( - {local.replace(process.env.HOME, '~')} + let local = util.isWindows() ? util.linuxToWindowsPath(source) : source; + source = ( + {local.replace(process.env.HOME, '~')} ); } return ( - {key} - {val} + {destination} + {source} - Change - Remove + Change + Remove ); @@ -104,7 +116,7 @@ var ContainerSettingsVolumes = React.createClass({ - {volumes} + {mounts}
diff --git a/src/components/Containers.react.js b/src/components/Containers.react.js index 449afb4835..b7058b28df 100644 --- a/src/components/Containers.react.js +++ b/src/components/Containers.react.js @@ -1,13 +1,13 @@ -var $ = require('jquery'); -var _ = require('underscore'); -var React = require('react'); -var Router = require('react-router'); -var containerStore = require('../stores/ContainerStore'); -var ContainerList = require('./ContainerList.react'); -var Header = require('./Header.react'); -var metrics = require('../utils/MetricsUtil'); -var shell = require('shell'); -var machine = require('../utils/DockerMachineUtil'); +import $ from 'jquery'; +import _ from 'underscore'; +import React from 'react'; +import Router from 'react-router'; +import containerStore from '../stores/ContainerStore'; +import ContainerList from './ContainerList.react'; +import Header from './Header.react'; +import metrics from '../utils/MetricsUtil'; +import shell from 'shell'; +import machine from '../utils/DockerMachineUtil'; var Containers = React.createClass({ contextTypes: { diff --git a/src/components/Header.react.js b/src/components/Header.react.js index 826b8a4124..e69c14ea80 100644 --- a/src/components/Header.react.js +++ b/src/components/Header.react.js @@ -1,17 +1,16 @@ -var React = require('react/addons'); -var remote = require('remote'); -var RetinaImage = require('react-retina-image'); -var remote = require('remote'); -var ipc = require('ipc'); +import React from 'react/addons'; +import remote from 'remote'; +import RetinaImage from 'react-retina-image'; +import ipc from 'ipc'; var autoUpdater = remote.require('auto-updater'); -var util = require('../utils/Util'); -var metrics = require('../utils/MetricsUtil'); +import util from '../utils/Util'; +import metrics from '../utils/MetricsUtil'; var Menu = remote.require('menu'); var MenuItem = remote.require('menu-item'); -var accountStore = require('../stores/AccountStore'); -var accountActions = require('../actions/AccountActions'); -var Router = require('react-router'); -var classNames = require('classnames'); +import accountStore from '../stores/AccountStore'; +import accountActions from '../actions/AccountActions'; +import Router from 'react-router'; +import classNames from 'classnames'; var Header = React.createClass({ mixins: [Router.Navigation], diff --git a/src/components/ImageCard.react.js b/src/components/ImageCard.react.js index a92d69b19b..6d77201f9f 100644 --- a/src/components/ImageCard.react.js +++ b/src/components/ImageCard.react.js @@ -1,13 +1,13 @@ -var $ = require('jquery'); -var React = require('react/addons'); -var Router = require('react-router'); -var shell = require('shell'); -var RetinaImage = require('react-retina-image'); -var metrics = require('../utils/MetricsUtil'); -var containerActions = require('../actions/ContainerActions'); -var containerStore = require('../stores/ContainerStore'); -var tagStore = require('../stores/TagStore'); -var tagActions = require('../actions/TagActions'); +import $ from 'jquery'; +import React from 'react/addons'; +import Router from 'react-router'; +import shell from 'shell'; +import RetinaImage from 'react-retina-image'; +import metrics from '../utils/MetricsUtil'; +import containerActions from '../actions/ContainerActions'; +import containerStore from '../stores/ContainerStore'; +import tagStore from '../stores/TagStore'; +import tagActions from '../actions/TagActions'; var ImageCard = React.createClass({ mixins: [Router.Navigation], @@ -108,7 +108,8 @@ var ImageCard = React.createClass({ description = "No description."; } var logoStyle = { - backgroundImage: `linear-gradient(-180deg, ${this.props.image.gradient_start} 4%, ${this.props.image.gradient_end} 100%)` + //backgroundImage: `linear-gradient(-180deg, ${this.props.image.gradient_start} 4%, ${this.props.image.gradient_end} 100%)` + backgroundColor: this.props.image.gradient_start }; var imgsrc; if (this.props.image.img) { diff --git a/src/components/NewContainerPull.react.js b/src/components/NewContainerPull.react.js index 24dbc63a13..c8e5bdd74c 100644 --- a/src/components/NewContainerPull.react.js +++ b/src/components/NewContainerPull.react.js @@ -1,9 +1,9 @@ -var React = require('react/addons'); -var Router = require('react-router'); -var shell = require('shell'); -var containerActions = require('../actions/ContainerActions'); -var containerStore = require('../stores/ContainerStore'); -var metrics = require('../utils/MetricsUtil'); +import React from 'react/addons'; +import Router from 'react-router'; +import shell from 'shell'; +import containerActions from '../actions/ContainerActions'; +import containerStore from '../stores/ContainerStore'; +import metrics from '../utils/MetricsUtil'; module.exports = React.createClass({ mixins: [Router.Navigation], diff --git a/src/components/NewContainerSearch.react.js b/src/components/NewContainerSearch.react.js index 9b19739aef..db72833fb4 100644 --- a/src/components/NewContainerSearch.react.js +++ b/src/components/NewContainerSearch.react.js @@ -1,15 +1,15 @@ -var _ = require('underscore'); -var React = require('react/addons'); -var Router = require('react-router'); -var RetinaImage = require('react-retina-image'); -var ImageCard = require('./ImageCard.react'); -var Promise = require('bluebird'); -var metrics = require('../utils/MetricsUtil'); -var classNames = require('classnames'); -var repositoryActions = require('../actions/RepositoryActions'); -var repositoryStore = require('../stores/RepositoryStore'); -var accountStore = require('../stores/AccountStore'); -var accountActions = require('../actions/AccountActions'); +import _ from 'underscore'; +import React from 'react/addons'; +import Router from 'react-router'; +import RetinaImage from 'react-retina-image'; +import ImageCard from './ImageCard.react'; +import Promise from 'bluebird'; +import metrics from '../utils/MetricsUtil'; +import classNames from 'classnames'; +import repositoryActions from '../actions/RepositoryActions'; +import repositoryStore from '../stores/RepositoryStore'; +import accountStore from '../stores/AccountStore'; +import accountActions from '../actions/AccountActions'; var _searchPromise = null; @@ -222,7 +222,7 @@ module.exports = React.createClass({
- +
diff --git a/src/components/Preferences.react.js b/src/components/Preferences.react.js index fc96ce7dff..2326d11d7b 100644 --- a/src/components/Preferences.react.js +++ b/src/components/Preferences.react.js @@ -1,6 +1,6 @@ -var React = require('react/addons'); -var metrics = require('../utils/MetricsUtil'); -var Router = require('react-router'); +import React from 'react/addons'; +import metrics from '../utils/MetricsUtil'; +import Router from 'react-router'; var Preferences = React.createClass({ mixins: [Router.Navigation], @@ -42,7 +42,7 @@ var Preferences = React.createClass({
VM Settings
- Shut Down Linux VM on closing Kitematic + Shutdown Linux VM on closing Kitematic
diff --git a/src/components/Radial.react.js b/src/components/Radial.react.js index bdc9ea1773..0989ea01b6 100644 --- a/src/components/Radial.react.js +++ b/src/components/Radial.react.js @@ -1,5 +1,5 @@ -var React = require('react'); -var classNames = require('classnames'); +import React from 'react'; +import classNames from 'classnames'; var Radial = React.createClass({ render: function () { diff --git a/src/components/Setup.react.js b/src/components/Setup.react.js index fb0cfab3e2..0d42839562 100644 --- a/src/components/Setup.react.js +++ b/src/components/Setup.react.js @@ -1,11 +1,11 @@ -var React = require('react/addons'); -var Router = require('react-router'); -var Radial = require('./Radial.react.js'); -var SetupStore = require('../stores/SetupStore'); -var RetinaImage = require('react-retina-image'); -var Header = require('./Header.react'); -var Util = require('../utils/Util'); -var metrics = require('../utils/MetricsUtil'); +import React from 'react/addons'; +import Router from 'react-router'; +import Radial from './Radial.react.js'; +import SetupStore from '../stores/SetupStore'; +import RetinaImage from 'react-retina-image'; +import Header from './Header.react'; +import Util from '../utils/Util'; +import metrics from '../utils/MetricsUtil'; var Setup = React.createClass({ mixins: [ Router.Navigation ], diff --git a/src/menutemplate.js b/src/menutemplate.js index df41ffa201..0f9eda4092 100644 --- a/src/menutemplate.js +++ b/src/menutemplate.js @@ -17,7 +17,12 @@ var MenuTemplate = function () { submenu: [ { label: 'About Kitematic', - selector: 'orderFrontStandardAboutPanel:' + click: function () { + metrics.track('Opened About', { + from: 'menu' + }); + router.get().transitionTo('about'); + } }, { type: 'separator' @@ -37,35 +42,9 @@ var MenuTemplate = function () { type: 'separator' }, { - label: 'Install Docker Commands', - enabled: true, - click: function () { - metrics.track('Installed Docker Commands'); - if (!setupUtil.shouldUpdateBinaries()) { - dialog.showMessageBox({ - message: 'Docker binaries are already installed in /usr/local/bin', - buttons: ['OK'] - }); - return; - } - - let copy = setupUtil.needsBinaryFix() ? - util.exec(setupUtil.macSudoCmd(setupUtil.copyBinariesCmd() + ' && ' + setupUtil.fixBinariesCmd())) : - util.exec(setupUtil.copyBinariesCmd()); - - copy.then(() => { - dialog.showMessageBox({ - message: 'Docker binaries have been installed under /usr/local/bin', - buttons: ['OK'] - }); - }).catch((err) => { - console.log(err); - }); - }, + type: 'separator' }, { - type: 'separator' - }, { label: 'Hide Kitematic', accelerator: util.CommandOrCtrl() + '+H', selector: 'hide:' diff --git a/src/routes.js b/src/routes.js index ca86453266..bce15a611a 100644 --- a/src/routes.js +++ b/src/routes.js @@ -1,21 +1,22 @@ -var React = require('react/addons'); -var Setup = require('./components/Setup.react'); -var Account = require('./components/Account.react'); -var AccountSignup = require('./components/AccountSignup.react'); -var AccountLogin = require('./components/AccountLogin.react'); -var Containers = require('./components/Containers.react'); -var ContainerDetails = require('./components/ContainerDetails.react'); -var ContainerHome = require('./components/ContainerHome.react'); -var ContainerLogs = require('./components/ContainerLogs.react'); -var ContainerSettings = require('./components/ContainerSettings.react'); -var ContainerSettingsGeneral = require('./components/ContainerSettingsGeneral.react'); -var ContainerSettingsPorts = require('./components/ContainerSettingsPorts.react'); -var ContainerSettingsVolumes = require('./components/ContainerSettingsVolumes.react'); -var ContainerSettingsAdvanced = require('./components/ContainerSettingsAdvanced.react'); -var Preferences = require('./components/Preferences.react'); -var NewContainerSearch = require('./components/NewContainerSearch.react'); -var NewContainerPull = require('./components/NewContainerPull.react'); -var Router = require('react-router'); +import React from 'react/addons'; +import Setup from './components/Setup.react'; +import Account from './components/Account.react'; +import AccountSignup from './components/AccountSignup.react'; +import AccountLogin from './components/AccountLogin.react'; +import Containers from './components/Containers.react'; +import ContainerDetails from './components/ContainerDetails.react'; +import ContainerHome from './components/ContainerHome.react'; +import ContainerLogs from './components/ContainerLogs.react'; +import ContainerSettings from './components/ContainerSettings.react'; +import ContainerSettingsGeneral from './components/ContainerSettingsGeneral.react'; +import ContainerSettingsPorts from './components/ContainerSettingsPorts.react'; +import ContainerSettingsVolumes from './components/ContainerSettingsVolumes.react'; +import ContainerSettingsAdvanced from './components/ContainerSettingsAdvanced.react'; +import Preferences from './components/Preferences.react'; +import About from './components/About.react'; +import NewContainerSearch from './components/NewContainerSearch.react'; +import NewContainerPull from './components/NewContainerPull.react'; +import Router from 'react-router'; var Route = Router.Route; var DefaultRoute = Router.DefaultRoute; @@ -51,6 +52,7 @@ var routes = ( + diff --git a/src/stores/LogStore.js b/src/stores/LogStore.js index 5f81580d78..89ed09cebd 100644 --- a/src/stores/LogStore.js +++ b/src/stores/LogStore.js @@ -1,8 +1,8 @@ -var EventEmitter = require('events').EventEmitter; -var assign = require('object-assign'); -var Convert = require('ansi-to-html'); -var docker = require('../utils/DockerUtil'); -var stream = require('stream'); +import {EventEmitter} from 'events'; +import assign from 'object-assign'; +import Convert from 'ansi-to-html'; +import docker from '../utils/DockerUtil'; +import stream from 'stream'; var _convert = new Convert(); var _logs = {}; diff --git a/src/stores/SetupStore.js b/src/stores/SetupStore.js index 7d8061bb55..9d8418bbe9 100644 --- a/src/stores/SetupStore.js +++ b/src/stores/SetupStore.js @@ -1,16 +1,16 @@ -var EventEmitter = require('events').EventEmitter; -var _ = require('underscore'); -var path = require('path'); -var fs = require('fs'); -var Promise = require('bluebird'); -var machine = require('../utils/DockerMachineUtil'); -var virtualBox = require('../utils/VirtualBoxUtil'); -var setupUtil = require('../utils/SetupUtil'); -var util = require('../utils/Util'); -var assign = require('object-assign'); -var metrics = require('../utils/MetricsUtil'); -var bugsnag = require('bugsnag-js'); -var docker = require('../utils/DockerUtil'); +import {EventEmitter} from 'events'; +import _ from 'underscore'; +import path from 'path'; +import fs from 'fs'; +import Promise from 'bluebird'; +import machine from '../utils/DockerMachineUtil'; +import virtualBox from '../utils/VirtualBoxUtil'; +import setupUtil from '../utils/SetupUtil'; +import util from '../utils/Util'; +import assign from 'object-assign'; +import metrics from '../utils/MetricsUtil'; +import bugsnag from 'bugsnag-js'; +import docker from '../utils/DockerUtil'; var _currentStep = null; var _error = null; @@ -73,7 +73,8 @@ var _steps = [{ var isoversion = machine.isoversion(); var packagejson = util.packagejson(); - if (!isoversion || util.compareVersions(isoversion, packagejson['docker-version']) < 0) { + var packagejsonVersion = packagejson['docker-version'].split('-')[0]; + if (!isoversion || util.compareVersions(isoversion, packagejsonVersion) < 0) { yield machine.start(); yield machine.upgrade(); } diff --git a/src/utils/ContainerUtil.js b/src/utils/ContainerUtil.js index b94c1632c5..129e77e0ad 100644 --- a/src/utils/ContainerUtil.js +++ b/src/utils/ContainerUtil.js @@ -1,5 +1,5 @@ -var _ = require('underscore'); -var docker = require('../utils/DockerUtil'); +import _ from 'underscore'; +import docker from '../utils/DockerUtil'; var ContainerUtil = { env: function (container) { diff --git a/src/utils/DockerMachineUtil.js b/src/utils/DockerMachineUtil.js index 9037fa76ce..f3764e0fca 100644 --- a/src/utils/DockerMachineUtil.js +++ b/src/utils/DockerMachineUtil.js @@ -1,22 +1,20 @@ -var _ = require('underscore'); -var path = require('path'); -var Promise = require('bluebird'); -var fs = require('fs'); -var util = require('./Util'); -var resources = require('./ResourcesUtil'); - -var NAME = util.isWindows () ? 'kitematic' : 'dev'; +import _ from 'underscore'; +import path from 'path'; +import Promise from 'bluebird'; +import fs from 'fs'; +import util from './Util'; +import resources from './ResourcesUtil'; var DockerMachine = { command: function () { return resources.dockerMachine(); }, name: function () { - return NAME; + return 'default'; }, - isoversion: function () { + isoversion: function (machineName = this.name()) { try { - var data = fs.readFileSync(path.join(util.home(), '.docker', 'machine', 'machines', NAME, 'boot2docker.iso'), 'utf8'); + var data = fs.readFileSync(path.join(util.home(), '.docker', 'machine', 'machines', machineName, 'boot2docker.iso'), 'utf8'); var match = data.match(/Boot2Docker-v(\d+\.\d+\.\d+)/); if (match) { return match[1]; @@ -27,7 +25,7 @@ var DockerMachine = { return null; } }, - info: function () { + info: function (machineName = this.name()) { return util.exec([this.command(), 'ls']).then(stdout => { var lines = stdout.trim().split('\n').filter(line => line.indexOf('time=') === -1); var machines = {}; @@ -41,54 +39,50 @@ var DockerMachine = { }; machines[machine.name] = machine; }); - if (machines[NAME]) { - return Promise.resolve(machines[NAME]); + if (machines[machineName]) { + return Promise.resolve(machines[machineName]); } else { return Promise.reject(new Error('Machine does not exist.')); } }); }, - exists: function () { - return this.info().then(() => { + exists: function (machineName = this.name()) { + return this.info(machineName).then(() => { return true; }).catch(() => { return false; }); }, - create: function () { - if (util.isWindows()) { - return util.exec([this.command(), '-D', 'create', '-d', 'virtualbox', '--virtualbox-memory', '2048', NAME]); - } else { - return util.exec([this.command(), '-D', 'create', '-d', 'virtualbox' ,'--virtualbox-boot2docker-url', path.join(process.env.RESOURCES_PATH, 'boot2docker.iso'), '--virtualbox-memory', '2048', NAME]); - } + create: function (machineName = this.name()) { + return util.exec([this.command(), '-D', 'create', '-d', 'virtualbox', '--virtualbox-memory', '2048', machineName]); }, - start: function () { - return util.exec([this.command(), '-D', 'start', NAME]); + start: function (machineName = this.name()) { + return util.exec([this.command(), '-D', 'start', machineName]); }, - stop: function () { - return util.exec([this.command(), 'stop', NAME]); + stop: function (machineName = this.name()) { + return util.exec([this.command(), 'stop', machineName]); }, - upgrade: function () { - return util.exec([this.command(), 'upgrade', NAME]); + upgrade: function (machineName = this.name()) { + return util.exec([this.command(), 'upgrade', machineName]); }, - rm: function () { - return util.exec([this.command(), 'rm', '-f', NAME]); + rm: function (machineName = this.name()) { + return util.exec([this.command(), 'rm', '-f', machineName]); }, - ip: function () { - return util.exec([this.command(), 'ip', NAME]).then(stdout => { + ip: function (machineName = this.name()) { + return util.exec([this.command(), 'ip', machineName]).then(stdout => { return Promise.resolve(stdout.trim().replace('\n', '')); }); }, - regenerateCerts: function () { - return util.exec([this.command(), 'tls-regenerate-certs', '-f', NAME]); + regenerateCerts: function (machineName = this.name()) { + return util.exec([this.command(), 'tls-regenerate-certs', '-f', machineName]); }, - state: function () { - return this.info().then(info => { + state: function (machineName = this.name()) { + return this.info(machineName).then(info => { return info ? info.state : null; }); }, - disk: function () { - return util.exec([this.command(), 'ssh', NAME, 'df']).then(stdout => { + disk: function (machineName = this.name()) { + return util.exec([this.command(), 'ssh', machineName, 'df']).then(stdout => { try { var lines = stdout.split('\n'); var dataline = _.find(lines, function (line) { @@ -111,8 +105,8 @@ var DockerMachine = { } }); }, - memory: function () { - return util.exec([this.command(), 'ssh', NAME, 'free -m']).then(stdout => { + memory: function (machineName = this.name()) { + return util.exec([this.command(), 'ssh', machineName, 'free -m']).then(stdout => { try { var lines = stdout.split('\n'); var dataline = _.find(lines, function (line) { @@ -137,8 +131,8 @@ var DockerMachine = { } }); }, - stats: function () { - this.state().then(state => { + stats: function (machineName = this.name()) { + this.state(machineName).then(state => { if (state === 'Stopped') { return Promise.resolve({state: state}); } @@ -152,10 +146,10 @@ var DockerMachine = { }); }); }, - dockerTerminal: function (cmd) { + dockerTerminal: function (cmd, machineName = this.name()) { if(util.isWindows()) { cmd = cmd || ''; - this.info().then(machine => { + this.info(machineName).then(machine => { util.exec('start powershell.exe ' + cmd, {env: { 'DOCKER_HOST' : machine.url, @@ -166,7 +160,7 @@ var DockerMachine = { }); } else { cmd = cmd || process.env.SHELL; - this.info().then(machine => { + this.info(machineName).then(machine => { util.exec([resources.terminal(), `DOCKER_HOST=${machine.url} DOCKER_CERT_PATH=${path.join(util.home(), '.docker/machine/machines/' + machine.name)} DOCKER_TLS_VERIFY=1 ${cmd}`]).then(() => {}); }); } diff --git a/src/utils/DockerUtil.js b/src/utils/DockerUtil.js index 393786e5d2..70b8f297d3 100644 --- a/src/utils/DockerUtil.js +++ b/src/utils/DockerUtil.js @@ -120,9 +120,9 @@ export default { }, fetchContainer (id) { - this.client.getContainer(id).inspect((error, container) => { + this.client.getContainer(id).inspect((error, container) => { if (error) { - containerServerActions.error({name: id, error}); + containerServerActions.error({name: id, error}); } else { container.Name = container.Name.replace('/', ''); containerServerActions.updated({container}); @@ -135,26 +135,23 @@ export default { if (err) { return; } - - let modifiedContainers = _.map(containers, container => { - container.Name = container.Names[0].replace('/', ''); - delete container.Names; - - // HACK: fill in some data based on simple list data - container.State = {}; - container.Config = { - Image: container.Image - }; - if (container.Status.indexOf('Exited') !== -1) { - container.State.Stopped = true; - } else if (container.Status.indexOf('Paused') !== -1) { - container.State.Stopped = true; - } else if (container.Status.indexOf('Up') !== -1) { - container.State.Running = true; + async.map(containers, (container, callback) => { + this.client.getContainer(container.Id).inspect((error, container) => { + if (error) { + callback(null, null); + return; + } + container.Name = container.Name.replace('/', ''); + callback(null, container); + }); + }, (err, containers) => { + containers = containers.filter(c => c !== null); + if (err) { + // TODO: add a global error handler for this + return; } - return container; + containerServerActions.allUpdated({containers: _.indexBy(containers.concat(_.values(this.placeholders)), 'Name')}); }); - containerServerActions.allUpdated({containers: _.indexBy(modifiedContainers.concat(_.values(this.placeholders)), 'Name')}); }); }, @@ -223,6 +220,11 @@ export default { existingData.Env = existingData.Config.Env; } + if ((!existingData.Tty || !existingData.OpenStdin) && existingData.Config && (existingData.Config.Tty || existingData.Config.OpenStdin)) { + existingData.Tty = existingData.Config.Tty; + existingData.OpenStdin = existingData.Config.OpenStdin; + } + var fullData = _.extend(existingData, data); this.createContainer(name, fullData); }); @@ -305,12 +307,12 @@ export default { let container = this.client.getContainer(name); container.unpause(function () { - container.kill(function (error) { - if (error) { - containerServerActions.error({name, error}); - return; - } - container.remove(function () { + container.kill(function () { + container.remove(function (error) { + if (error) { + containerServerActions.error({name, error}); + return; + } containerServerActions.destroyed({id: name}); var volumePath = path.join(util.home(), 'Kitematic', name); if (fs.existsSync(volumePath)) { diff --git a/src/utils/HubUtil.js b/src/utils/HubUtil.js index 6f02d03975..f88b4d51a6 100644 --- a/src/utils/HubUtil.js +++ b/src/utils/HubUtil.js @@ -1,7 +1,7 @@ -var _ = require('underscore'); -var request = require('request'); -var accountServerActions = require('../actions/AccountServerActions'); -var metrics = require('./MetricsUtil'); +import _ from 'underscore'; +import request from 'request'; +import accountServerActions from '../actions/AccountServerActions'; +import metrics from './MetricsUtil'; let HUB2_ENDPOINT = process.env.HUB2_ENDPOINT || 'https://hub.docker.com/v2'; diff --git a/src/utils/MetricsUtil.js b/src/utils/MetricsUtil.js index a608ce1ea3..be8be4e7f3 100644 --- a/src/utils/MetricsUtil.js +++ b/src/utils/MetricsUtil.js @@ -1,11 +1,11 @@ -var assign = require('object-assign'); -var Mixpanel = require('mixpanel'); -var uuid = require('node-uuid'); -var fs = require('fs'); -var path = require('path'); -var util = require('./Util'); -var os = require('os'); -var osxRelease = require('osx-release'); +import assign from 'object-assign'; +import Mixpanel from 'mixpanel'; +import uuid from 'node-uuid'; +import fs from 'fs'; +import path from 'path'; +import util from './Util'; +import os from 'os'; +import osxRelease from 'osx-release'; var settings; try { diff --git a/src/utils/RegHubUtil.js b/src/utils/RegHubUtil.js index a10ed54a1d..bcf0136560 100644 --- a/src/utils/RegHubUtil.js +++ b/src/utils/RegHubUtil.js @@ -1,10 +1,10 @@ -var _ = require('underscore'); -var request = require('request'); -var async = require('async'); -var util = require('../utils/Util'); -var hubUtil = require('../utils/HubUtil'); -var repositoryServerActions = require('../actions/RepositoryServerActions'); -var tagServerActions = require('../actions/TagServerActions'); +import _ from 'underscore'; +import request from 'request'; +import async from 'async'; +import util from '../utils/Util'; +import hubUtil from '../utils/HubUtil'; +import repositoryServerActions from '../actions/RepositoryServerActions'; +import tagServerActions from '../actions/TagServerActions'; let REGHUB2_ENDPOINT = process.env.REGHUB2_ENDPOINT || 'https://registry.hub.docker.com/v2'; let searchReq = null; @@ -103,11 +103,11 @@ module.exports = { }, (error, response, body) => { if (response.statusCode === 200) { let data = JSON.parse(body); - tagServerActions.tagsUpdated({repo, tags: data}); - if (callback) { callback(null, data); } - } else if (error || response.statusCode === 401) { + tagServerActions.tagsUpdated({repo, tags: data.results || []}); + if (callback) { callback(null, data.results || []); } + } else { repositoryServerActions.error({repo}); - if (callback) { callback(new Error('Failed to fetch repos')); } + if (callback) { callback(new Error('Failed to fetch tags for repo')); } } }); }, diff --git a/src/utils/ResourcesUtil.js b/src/utils/ResourcesUtil.js index 3aaed0e6fe..5fd652ef93 100644 --- a/src/utils/ResourcesUtil.js +++ b/src/utils/ResourcesUtil.js @@ -1,5 +1,5 @@ -var util = require('./Util'); -var path = require('path'); +import util from './Util'; +import path from 'path'; module.exports = { resourceDir: function () { diff --git a/src/utils/SetupUtil.js b/src/utils/SetupUtil.js index f22e7a4c33..80b0edc6f2 100644 --- a/src/utils/SetupUtil.js +++ b/src/utils/SetupUtil.js @@ -1,12 +1,12 @@ -var _ = require('underscore'); -var crypto = require('crypto'); -var fs = require('fs'); -var path = require('path'); -var request = require('request'); -var progress = require('request-progress'); -var Promise = require('bluebird'); -var util = require('./Util'); -var resources = require('./ResourcesUtil'); +import _ from 'underscore'; +import crypto from 'crypto'; +import fs from 'fs'; +import path from 'path'; +import request from 'request'; +import progress from 'request-progress'; +import Promise from 'bluebird'; +import util from './Util'; +import resources from './ResourcesUtil'; var virtualBox = require ('./VirtualBoxUtil'); var SetupUtil = { diff --git a/src/utils/URLUtil.js b/src/utils/URLUtil.js index 2b4e8a8933..68b102fd22 100644 --- a/src/utils/URLUtil.js +++ b/src/utils/URLUtil.js @@ -1,6 +1,6 @@ -var util = require('./Util'); -var parseUri = require('parseUri'); -var containerServerActions = require('../actions/ContainerServerActions'); +import util from './Util'; +import parseUri from 'parseUri'; +import containerServerActions from '../actions/ContainerServerActions'; module.exports = { TYPE_WHITELIST: ['repository'], diff --git a/src/utils/Util.js b/src/utils/Util.js index 1dc83a6178..d7cd809326 100644 --- a/src/utils/Util.js +++ b/src/utils/Util.js @@ -1,10 +1,10 @@ -var exec = require('exec'); -var child_process = require('child_process'); -var Promise = require('bluebird'); -var fs = require('fs'); -var path = require('path'); -var crypto = require('crypto'); -var remote = require('remote'); +import exec from 'exec'; +import child_process from 'child_process'; +import Promise from 'bluebird'; +import fs from 'fs'; +import path from 'path'; +import crypto from 'crypto'; +import remote from 'remote'; var app = remote.require('app'); module.exports = { @@ -71,7 +71,8 @@ module.exports = { } return str.replace(/-----BEGIN CERTIFICATE-----.*-----END CERTIFICATE-----/mg, '') .replace(/-----BEGIN RSA PRIVATE KEY-----.*-----END RSA PRIVATE KEY-----/mg, '') - .replace(/\/Users\/[^\/]*\//mg, '/Users//'); + .replace(/\/Users\/[^\/]*\//mg, '/Users//') + .replace(/\\Users\\[^\/]*\\/mg, '\\Users\\\\'); }, packagejson: function () { return JSON.parse(fs.readFileSync(path.join(__dirname, '..', 'package.json'), 'utf8')); diff --git a/src/utils/VirtualBoxUtil.js b/src/utils/VirtualBoxUtil.js index b6729cef60..338717676a 100644 --- a/src/utils/VirtualBoxUtil.js +++ b/src/utils/VirtualBoxUtil.js @@ -1,13 +1,13 @@ -var fs = require('fs'); -var util = require('./Util'); -var Promise = require('bluebird'); +import fs from 'fs'; +import util from './Util'; +import Promise from 'bluebird'; var VirtualBox = { command: function () { if(util.isWindows()) { return 'C:\\Program Files\\Oracle\\VirtualBox\\VBoxManage.exe'; } else { - return '/usr/bin/VBoxManage'; + return '/Applications/VirtualBox.app/Contents/MacOS/VBoxManage'; } }, filename: function () { @@ -23,7 +23,7 @@ var VirtualBox = { if(util.isWindows()) { return fs.existsSync('C:\\Program Files\\Oracle\\VirtualBox\\VBoxManage.exe') && fs.existsSync('C:\\Program Files\\Oracle\\VirtualBox\\VirtualBox.exe'); } else { - return fs.existsSync('/usr/bin/VBoxManage') && fs.existsSync('/Applications/VirtualBox.app') && fs.existsSync('/Applications/VirtualBox.app/Contents/MacOS/VBoxManage'); + return fs.existsSync('/Applications/VirtualBox.app') && fs.existsSync('/Applications/VirtualBox.app/Contents/MacOS/VBoxManage'); } }, active: function () { diff --git a/src/utils/WebUtil.js b/src/utils/WebUtil.js index 318d529764..c146e0eb9f 100644 --- a/src/utils/WebUtil.js +++ b/src/utils/WebUtil.js @@ -1,9 +1,10 @@ -var app = require('remote').require('app'); -var fs = require('fs'); -var util = require('./Util'); -var path = require('path'); -var bugsnag = require('bugsnag-js'); -var metrics = require('./MetricsUtil'); +import remote from 'remote'; +var app = remote.require('app'); +import fs from 'fs'; +import util from './Util'; +import path from 'path'; +import bugsnag from 'bugsnag-js'; +import metrics from './MetricsUtil'; var WebUtil = { addWindowSizeSaving: function () { diff --git a/styles/new-container.less b/styles/new-container.less index 0202a08910..30fa2ce62d 100644 --- a/styles/new-container.less +++ b/styles/new-container.less @@ -284,7 +284,7 @@ border-bottom-left-radius: @border-radius; justify-content: center; text-align: center; - box-shadow: inset 0px 0px 0px 1px rgba(0,0,0,0.2); + box-shadow: inset 0px 0px 0px 1px rgba(0,0,0,0.1); img { width: 35px; height: auto; @@ -293,7 +293,7 @@ } .card { position: relative; - border: 1px solid darken(@gray-lightest, 5%); + border: 1px solid darken(@gray-lightest, 0%); border-left: 0; border-top-right-radius: @border-radius; border-bottom-right-radius: @border-radius; diff --git a/styles/preferences.less b/styles/preferences.less index 189d46bf55..0294fd2a38 100644 --- a/styles/preferences.less +++ b/styles/preferences.less @@ -6,7 +6,7 @@ align-items: flex-start; justify-content: center; - .preferences-content { + .preferences-content, .about-content { flex: 1 auto; margin-top: 45px; padding: 50px; @@ -37,4 +37,34 @@ } } } + .about-content { + margin-top: 0px; + height: 100%; + overflow: auto; + .items { + display: flex; + .item { + flex: 1 auto; + margin-right: 1rem; + text-align: center; + } + } + h3 { + color: @gray-normal; + font-weight: 400; + font-size: 18px; + text-align: center; + } + img { + height: 100px; + width: auto; + } + h4 { + margin-bottom: 0.5rem; + } + p { + color: @gray-light; + font-size: 1.5rem; + } + } } diff --git a/styles/setup.less b/styles/setup.less index 6d29178240..321df2edab 100644 --- a/styles/setup.less +++ b/styles/setup.less @@ -197,6 +197,7 @@ border-radius: 4px; max-height: 400px; overflow: auto; + -webkit-user-select: initial; } } .setup-actions {