Merge pull request #985 from moxiegirl/rebase-docs

Rebase docs
This commit is contained in:
Jeffrey Morgan 2015-08-27 14:28:32 -07:00
commit 54d4ff1400
72 changed files with 734 additions and 656 deletions

View File

@ -1,15 +1,10 @@
var path = require('path'); var path = require('path');
var fs = require('fs');
var execFile = require('child_process').execFile; var execFile = require('child_process').execFile;
var packagejson = require('./package.json'); var packagejson = require('./package.json');
var electron = require('electron-prebuilt'); 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 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_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) { module.exports = function (grunt) {
require('load-grunt-tasks')(grunt); require('load-grunt-tasks')(grunt);
@ -21,25 +16,12 @@ module.exports = function (grunt) {
env.NODE_ENV = target; env.NODE_ENV = target;
var certificateFile = grunt.option('certificateFile'); var certificateFile = grunt.option('certificateFile');
var certificatePassword = grunt.option('certificatePassword');
var version = function (str) { var version = function (str) {
var match = str.match(/(\d+\.\d+\.\d+)/); var match = str.match(/(\d+\.\d+\.\d+)/);
return match ? match[1] : null; 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 () { grunt.registerMultiTask('download-binary', 'Downloads binary unless version up to date', function () {
var target = grunt.task.current.target; var target = grunt.task.current.target;
var done = this.async(); var done = this.async();
@ -64,15 +46,19 @@ module.exports = function (grunt) {
APPNAME += ' (Beta)'; APPNAME += ' (Beta)';
} }
var OSX_OUT = './dist/osx'; var OSX_OUT = './dist';
var OSX_FILENAME = OSX_OUT + '/' + APPNAME + '.app'; var OSX_OUT_X64 = OSX_OUT + '/' + APPNAME + '-darwin-x64';
var OSX_FILENAME = OSX_OUT_X64 + '/' + APPNAME + '.app';
grunt.initConfig({ grunt.initConfig({
IDENTITY: 'Developer ID Application: Docker Inc', IDENTITY: 'Developer ID Application: Docker Inc',
APPNAME: APPNAME, APPNAME: APPNAME,
APPNAME_ESCAPED: APPNAME.replace(/ /g, '\\ ').replace(/\(/g,'\\(').replace(/\)/g,'\\)'),
OSX_OUT: OSX_OUT, 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: OSX_FILENAME,
OSX_FILENAME_ESCAPED: OSX_FILENAME.replace(' ', '\\ ').replace('(','\\(').replace(')','\\)'), OSX_FILENAME_ESCAPED: OSX_FILENAME.replace(/ /g, '\\ ').replace(/\(/g,'\\(').replace(/\)/g,'\\)'),
// electron // electron
electron: { electron: {
@ -80,7 +66,7 @@ module.exports = function (grunt) {
options: { options: {
name: BASENAME, name: BASENAME,
dir: 'build/', dir: 'build/',
out: 'dist/', out: 'dist',
version: packagejson['electron-version'], version: packagejson['electron-version'],
platform: 'win32', platform: 'win32',
arch: 'x64', arch: 'x64',
@ -92,7 +78,7 @@ module.exports = function (grunt) {
options: { options: {
name: APPNAME, name: APPNAME,
dir: 'build/', dir: 'build/',
out: '<%= OSX_OUT %>', out: 'dist',
version: packagejson['electron-version'], version: packagejson['electron-version'],
platform: 'darwin', platform: 'darwin',
arch: 'x64', arch: 'x64',
@ -103,11 +89,25 @@ module.exports = function (grunt) {
} }
}, },
prompt: {
'create-windows-installer': {
options: {
questions: [
{
config: 'certificatePassword',
type: 'password',
message: 'Certificate Password: '
}
]
}
}
},
rcedit: { rcedit: {
exes: { exes: {
files: [{ files: [{
expand: true, expand: true,
cwd: 'dist/' + BASENAME + '-win32', cwd: 'dist/' + BASENAME + '-win32-x64',
src: [BASENAME + '.exe'] src: [BASENAME + '.exe']
}], }],
options: { options: {
@ -115,7 +115,7 @@ module.exports = function (grunt) {
'file-version': packagejson.version, 'file-version': packagejson.version,
'product-version': packagejson.version, 'product-version': packagejson.version,
'version-string': { 'version-string': {
'CompanyName': 'Docker Inc', 'CompanyName': 'Docker',
'ProductVersion': packagejson.version, 'ProductVersion': packagejson.version,
'ProductName': APPNAME, 'ProductName': APPNAME,
'FileDescription': APPNAME, 'FileDescription': APPNAME,
@ -128,7 +128,9 @@ module.exports = function (grunt) {
}, },
'create-windows-installer': { 'create-windows-installer': {
appDirectory: 'dist/' + BASENAME + '-win32/', config: {
appDirectory: path.join(__dirname, 'dist/' + BASENAME + '-win32-x64'),
outputDirectory: path.join(__dirname, 'dist'),
authors: 'Docker Inc.', authors: 'Docker Inc.',
loadingGif: 'util/loading.gif', loadingGif: 'util/loading.gif',
setupIcon: 'util/setup.ico', setupIcon: 'util/setup.ico',
@ -137,25 +139,16 @@ module.exports = function (grunt) {
title: APPNAME, title: APPNAME,
exe: BASENAME + '.exe', exe: BASENAME + '.exe',
version: packagejson.version, version: packagejson.version,
signWithParams: '/f ' + certificateFile + ' /p ' + certificatePassword + ' /tr http://timestamp.comodoca.com/rfc3161' signWithParams: '/f ' + certificateFile + ' /p <%= certificatePassword %> /tr http://timestamp.comodoca.com/rfc3161'
}
}, },
// docker binaries // docker binaries
'download-binary': { 'download-binary': {
docker: {
version: packagejson['docker-version'],
binary: path.join('resources', 'docker'),
download: 'curl:docker'
},
'docker-machine': { 'docker-machine': {
version: packagejson['docker-machine-version'], version: packagejson['docker-machine-version'],
binary: path.join('resources', 'docker-machine'), binary: path.join('resources', 'docker-machine'),
download: 'curl: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: [{ files: [{
expand: true, expand: true,
cwd: 'resources', cwd: 'resources',
src: ['docker*', 'boot2docker.iso', 'ssh.exe', 'OPENSSH_LICENSE', 'msys-*'], src: ['docker*', 'ssh.exe', 'OPENSSH_LICENSE', 'msys-*'],
dest: 'dist/' + BASENAME + '-win32/resources/resources/' dest: 'dist/' + BASENAME + '-win32-x64/resources/resources'
}], }],
options: { options: {
mode: true mode: true
@ -199,7 +192,7 @@ module.exports = function (grunt) {
files: [{ files: [{
expand: true, expand: true,
cwd: 'resources', cwd: 'resources',
src: ['docker*', 'boot2docker.iso', 'macsudo', 'terminal'], src: ['docker*', 'macsudo', 'terminal'],
dest: '<%= OSX_FILENAME %>/Contents/Resources/resources/' dest: '<%= OSX_FILENAME %>/Contents/Resources/resources/'
}, { }, {
src: 'util/kitematic.icns', src: 'util/kitematic.icns',
@ -213,28 +206,16 @@ module.exports = function (grunt) {
rename: { rename: {
installer: { installer: {
src: 'installer/Setup.exe', src: 'dist/Setup.exe',
dest: 'installer/' + BASENAME + 'Setup-' + packagejson.version + '-Windows-Alpha.exe' dest: 'dist/' + BASENAME + 'Setup-' + packagejson.version + '-Windows-Alpha.exe'
} }
}, },
// download binaries // download binaries
curl: { 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': { 'docker-machine': {
src: process.platform === 'win32' ? WINDOWS_DOCKER_MACHINE_URL : DARWIN_DOCKER_MACHINE_URL, 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') 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: { shell: {
electron: { electron: {
command: electron + ' ' + 'build', command: electron + ' ' + 'build',
@ -332,13 +278,27 @@ module.exports = function (grunt) {
].join(' && '), ].join(' && '),
}, },
zip: { 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: { clean: {
release: ['build/', 'dist/', 'installer/'], release: ['build/', 'dist/'],
isos: ['resources/boot2docker*'] },
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 // livereload
@ -365,16 +325,12 @@ module.exports = function (grunt) {
} }
}); });
if (process.platform === 'win32') { grunt.registerTask('default', ['download-binary', 'newer:babel', 'less', 'newer:copy:dev', 'shell:electron', 'watchChokidar']);
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']);
}
if (process.platform === 'win32') { 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 { } 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 () { process.on('SIGINT', function () {

4
MAINTAINERS Normal file
View File

@ -0,0 +1,4 @@
Jeff Morgan <jmorgan@docker.com> (@jeffdm)
Sean Li <mail@shang.li> (@elesant)
Michael Chiang <mchiang@docker.com> (@mchiang0610)
Ben French <me@frenchben.com> (@FrenchBen)

View File

@ -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. [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 ## Documentation
Kitematic's documentation and other information can be found at [http://kitematic.com/docs](http://kitematic.com/docs). 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 ## Community
- [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/kitematic/kitematic?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) - [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/kitematic/kitematic?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
- Ask questions on our [user forum](https://forums.docker.com/c/kitematic). - 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). - **#kitematic** on IRC. [Join the channel](http://webchat.freenode.net/?channels=%23kitematic&uio=d4).
- Follow [@kitematic on Twitter](https://twitter.com/kitematic). - Follow [@kitematic on Twitter](https://twitter.com/kitematic).

View File

@ -28,4 +28,9 @@
**June 2015** **June 2015**
* Microsoft Windows port * Microsoft Windows alpha
**July 2015**
* Refactor to Flux Architecture
* Stability & code quality improvements

View File

@ -20,7 +20,7 @@ describe('SetupStore', function () {
pit('downloads virtualbox if it is installed but has an outdated version', function () { pit('downloads virtualbox if it is installed but has an outdated version', function () {
virtualBox.installed.mockReturnValue(true); virtualBox.installed.mockReturnValue(true);
virtualBox.version.mockReturnValue(Promise.resolve('4.3.16')); virtualBox.version.mockReturnValue(Promise.resolve('5.0.0'));
util.compareVersions.mockReturnValue(-1); util.compareVersions.mockReturnValue(-1);
setupUtil.download.mockReturnValue(Promise.resolve()); setupUtil.download.mockReturnValue(Promise.resolve());
virtualBox.filename.mockReturnValue(''); virtualBox.filename.mockReturnValue('');
@ -55,6 +55,7 @@ describe('SetupStore', function () {
machine.stop.mockReturnValue(Promise.resolve()); machine.stop.mockReturnValue(Promise.resolve());
machine.start.mockReturnValue(Promise.resolve()); machine.start.mockReturnValue(Promise.resolve());
machine.upgrade.mockReturnValue(Promise.resolve()); machine.upgrade.mockReturnValue(Promise.resolve());
util.packagejson.mockReturnValue({'docker-version':'1.8.0'});
util.compareVersions.mockReturnValue(-1); util.compareVersions.mockReturnValue(-1);
machine.create.mockClear(); machine.create.mockClear();
machine.upgrade.mockClear(); machine.upgrade.mockClear();

View File

@ -30,6 +30,12 @@ describe('Util', function () {
expect(util.removeSensitiveData(testdata).indexOf('/Users/<redacted>/.docker')).toNotEqual(-1); expect(util.removeSensitiveData(testdata).indexOf('/Users/<redacted>/.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('<redacted>')).toNotEqual(-1);
});
it ('returns input if empty or not a string', function () { it ('returns input if empty or not a string', function () {
expect(util.removeSensitiveData('')).toBe(''); expect(util.removeSensitiveData('')).toBe('');
expect(util.removeSensitiveData(1)).toBe(1); expect(util.removeSensitiveData(1)).toBe(1);

View File

@ -3,17 +3,13 @@ var virtualBox = require('../src/utils/VirtualBoxUtil');
var util = require('../src/utils/Util'); var util = require('../src/utils/Util');
describe('VirtualBox', function () { describe('VirtualBox', function () {
it('returns the right command', function () { describe('version 5.0.0r101573', function () {
expect(virtualBox.command()).toBe('/usr/bin/VBoxManage');
});
describe('version 4.3.20r96996', function () {
pit('correctly parses virtualbox version', function () { pit('correctly parses virtualbox version', function () {
util.exec.mockImplementation(function () { util.exec.mockImplementation(function () {
return Promise.resolve('4.3.20r96996'); return Promise.resolve('5.0.0r101573');
}); });
return virtualBox.version().then(function (version) { return virtualBox.version().then(function (version) {
expect(version).toBe('4.3.20'); expect(version).toBe('5.0.0');
}); });
}); });
}); });

View File

@ -1,30 +1,27 @@
FROM docs/base:hugo FROM docs/base:latest
MAINTAINER Mary Anthony <mary@docker.com> (@moxiegirl) MAINTAINER Mary Anthony <mary@docker.com> (@moxiegirl)
# to get the git info for this repo # To get the git info for this repo
COPY . /src COPY . /src
COPY . /docs/content/kitematic/ 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 # Sed to process GitHub Markdown
# 1-2 Remove comment code from metadata block # 1-2 Remove comment code from metadata block
# 3 Change ](/word to ](/project/ in links # 3 Change ](/word to ](/project/ in links
# 4 Change ](word.md) to ](/project/word) # 4 Change ](word.md) to ](/project/word)
# 5 Remove .md extension from link text # 5 Remove .md extension from link text
# 6 Change ](./ to ](/project/word) # 6 Change ](../ to ](/project/word)
# 7 Change ](../../ to ](/project/ # 7 Change ](../../ to ](/project/ --> not implemented
# 8 Change ](../ to ](/project/
# 9 Change ](# to ](/project/
# #
RUN find /docs/content/kitematic -type f -name "*.md" -exec sed -i.old \ #
-e '/^<!.*metadata]>/g' \ RUN /src/pre-process.sh /docs
-e '/^<!.*end-metadata.*>/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' {} \;

View File

@ -21,8 +21,7 @@ software released under the Apache 2.0 license.
### How can I contribute to Kitematic? ### How can I contribute to Kitematic?
We always welcome (and deeply appreciate!) new contributions to the project. The 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 best way to start contributing to Kitematic is to review our doc on <a href="https://github.com/kitematic/kitematic/blob/master/CONTRIBUTING.md">contributing</a>.
[contributing](https://github.com/kitematic/kitematic/blob/master/CONTRIBUTING.md).
### How does Kitematic work with Docker? ### How does Kitematic work with Docker?
@ -31,10 +30,9 @@ the Docker Remote API.
### Which platforms does Kitematic support? ### Which platforms does Kitematic support?
Right now Kitematic only works on Mac OS X. That said, Windows is on the Right now Kitematic works on Mac OS X and Windows. Linux is planned in the
short-term future. Review our product <a
[roadmap](https://github.com/kitematic/kitematic/blob/master/ROADMAP.md) (coming href="https://github.com/kitematic/kitematic/blob/master/ROADMAP.md">roadmap</a>.
soon!) and a Linux version is in high demand.
### Why does Kitematic collect usage analytics and bug reports? ### Why does Kitematic collect usage analytics and bug reports?

View File

@ -9,45 +9,8 @@ weight=2
+++ +++
<![end-metadata]--> <![end-metadata]-->
# Kitematic: Install Kitematic # Kitematic
You install Kitematic much the same way you install any application on a Mac or 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.
Windows PC: download an image and run an installer.
## Download Kitematic For information about using Kitematic, take a look at the [User Guide](userguide).
[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).

View File

@ -29,8 +29,8 @@ commands on the command line:
- `docker-machine create -d virtualbox dev` - `docker-machine create -d virtualbox dev`
Then re-open Kitematic. This usually fixes the issue, but if it persists, feel Then re-open Kitematic. This usually fixes the issue, but if it persists, feel
free to view our [existing GitHub free to view our <a href="https://github.com/kitematic/kitematic/issues?q=is%3Aopen+is%3Aissue+label%3Abug">existing GitHub
issues](https://github.com/kitematic/kitematic/issues?q=is%3Aopen+is%3Aissue+label%3Abug). issues</a>.
## Contributing Fixes ## Contributing Fixes
@ -46,6 +46,5 @@ if you're looking to help fix specific issues around VM provisioning.
## View All Issues ## View All Issues
For a full list of Kitematic bugs or issues see our [GitHub For a full list of Kitematic bugs or issues see our <a href="https://github.com/kitematic/kitematic/issues?q=is%3Aopen+is%3Aissue+label%3Abug">existing GitHub
issues](https://github.com/kitematic/kitematic/issues?q=is%3Aopen+is%3Aissue+label%3Abug) issues</a> labelled as `bug`.
labelled as `bug`.

61
docs/pre-process.sh Executable file
View File

@ -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 '/^<!.*metadata]>/g' \
-e '/^<!.*end-metadata.*>/g' {} \;
;;
*)
y=${i##*/}
find $i -type f -name "*.md" -exec sed -i.old \
-e '/^<!.*metadata]>/g' \
-e '/^<!.*end-metadata.*>/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

View File

@ -16,7 +16,7 @@ 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 Docker on a Mac or Windows (coming soon) PC. Kitematic automates the Docker
installation and setup process and provides an intuitive graphical user installation and setup process and provides an intuitive graphical user
interface (GUI) for running Docker containers. Kitematic integrates with 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. and install the Docker Engine locally on your machine.
Once installed, the Kitematic GUI launches and from the home screen you will be Once installed, the Kitematic GUI launches and from the home screen you will be
@ -78,12 +78,12 @@ The currently detected "web" ports are, `80`, `8000`, `8080`, `3000`, `5000`,
### Viewing container logs ### 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. preview image, or by clicking on the "Logs" tab.
You can then scroll through the logs from the current running container. Note that 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, if you make changes to the container settings, then the container will be restarted,
so will reset this log view. so this will reset this log view.
### Starting a terminal in a container ### Starting a terminal in a container
@ -100,7 +100,7 @@ on your Mac by clicking on the folders in the "Edit Files" section of the
container summary screen. container summary screen.
This allows you to manage files in volumes via the Finder. This allows you to manage files in volumes via the Finder.
Kitematic exposes a container's volume data under `~/Kitematic/<container's name>/`. Kitematic exposes a container's volume data under `~/Documents/Kitematic/<container's name>/`.
Quick access to this folder (or directory) is available via the app: Quick access to this folder (or directory) is available via the app:
![Accessing the volumes directory](../images/volumes-dir.png) ![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 already have the HTML, Javascript, and CSS for your website under
`~/workspace/website`. `~/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 allows you to set the mappings individually.
![screen shot 2015-02-28 at 2 48 01 pm](../images/change-folder.png) ![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 "Settings" tab allows you to add and modify the environment variables used to
start a container. 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. metadata - for example, using the `ENV` instruction in the Dockerfile.
<TODO: image of the jenkins container> <TODO: image of the jenkins container>
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. removed and re-created.
### Delete container ### Delete container
@ -155,14 +155,14 @@ removed and re-created.
On the "General" "Settings" tab, you can delete the container. Clicking "Delete On the "General" "Settings" tab, you can delete the container. Clicking "Delete
Container" will also stop the container if necessary. 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. Kitematic will prompt you to confirm that you want to delete.
#### List the exposed Ports and how to access them #### List the exposed Ports and how to access them
To see the complete list of exposed ports, go to "Settings" then "Ports". This 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 network port that you can access use to access that container from your OS X
system. 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 via the Docker Command Line Interface (CLI). Any changes you make on the CLI are
directly reflected in Kitematic. 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: shown below:
![CLI access button](../images/cli-access-button.png) ![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) ![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 > 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. 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 ## Next Steps
For an example using Kitematic to run a Minecraft server, take a look at 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.

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 93 KiB

BIN
images/cartoon-docker.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

View File

@ -1,6 +1,6 @@
{ {
"name": "Kitematic", "name": "Kitematic",
"version": "0.7.3", "version": "0.8.3",
"author": "Kitematic", "author": "Kitematic",
"description": "Simple Docker Container management for Mac OS X.", "description": "Simple Docker Container management for Mac OS X.",
"homepage": "https://kitematic.com/", "homepage": "https://kitematic.com/",
@ -22,15 +22,14 @@
"url": "http://www.apache.org/licenses/LICENSE-2.0.html" "url": "http://www.apache.org/licenses/LICENSE-2.0.html"
} }
], ],
"docker-version": "1.7.0", "docker-version": "1.8.1",
"docker-machine-version": "0.3.0-rc2", "docker-machine-version": "0.4.1",
"docker-compose-version": "1.3.0",
"electron-version": "0.27.2", "electron-version": "0.27.2",
"virtualbox-version": "4.3.28", "virtualbox-version": "5.0.2",
"virtualbox-filename": "VirtualBox-4.3.28.pkg", "virtualbox-filename": "VirtualBox-5.0.2.pkg",
"virtualbox-filename-win": "VirtualBox-4.3.28.exe", "virtualbox-filename-win": "VirtualBox-5.0.2.exe",
"virtualbox-checksum": "60521caff652fc32ad733eee2eea27f03d0b4f7239af3bf4b21dc6f0251cf47a", "virtualbox-checksum": "384414edab277c376a2ac3d02e5f316434fa4d54d31db44dc85b6e560eda4143",
"virtualbox-checksum-win": "82039b615bf18dfff92ac1d9a15b2cbd5c59c608631ccf77e2371d0fd67a6cf7", "virtualbox-checksum-win": "a8183d21566fc6cb327e4b71303fa0d04fc6cd079ba83d66818d18c5ef427037",
"dependencies": { "dependencies": {
"alt": "^0.16.2", "alt": "^0.16.2",
"ansi-to-html": "0.3.0", "ansi-to-html": "0.3.0",
@ -71,16 +70,18 @@
"grunt-chmod": "^1.0.3", "grunt-chmod": "^1.0.3",
"grunt-cli": "^0.1.13", "grunt-cli": "^0.1.13",
"grunt-contrib-clean": "^0.6.0", "grunt-contrib-clean": "^0.6.0",
"grunt-contrib-compress": "^0.13.0",
"grunt-contrib-copy": "^0.8.0", "grunt-contrib-copy": "^0.8.0",
"grunt-contrib-less": "^1.0.1", "grunt-contrib-less": "^1.0.1",
"grunt-contrib-watch-chokidar": "^1.0.0", "grunt-contrib-watch-chokidar": "^1.0.0",
"grunt-curl": "^2.2.0", "grunt-curl": "^2.2.0",
"grunt-download-electron": "^2.1.1", "grunt-download-electron": "^2.1.1",
"grunt-electron": "^1.0.0", "grunt-electron": "^2.0.0",
"grunt-electron-installer": "^0.33.0", "grunt-electron-installer": "^0.37.0",
"grunt-if-missing": "^1.0.0", "grunt-if-missing": "^1.0.0",
"grunt-newer": "^1.1.1", "grunt-newer": "^1.1.1",
"grunt-plistbuddy": "^0.1.1", "grunt-plistbuddy": "^0.1.1",
"grunt-prompt": "^1.3.0",
"grunt-rcedit": "^0.3.1", "grunt-rcedit": "^0.3.1",
"grunt-rename": "^0.1.4", "grunt-rename": "^0.1.4",
"grunt-shell": "^1.1.2", "grunt-shell": "^1.1.2",

View File

@ -1,8 +1,7 @@
#!/bin/bash #!/bin/bash
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
PATH=$DIR:$PATH CMD="clear && $*"
CMD="export PATH='$PATH' && clear && $*"
ITERM_EXISTS=`osascript <<EOF ITERM_EXISTS=`osascript <<EOF
set doesExist to false set doesExist to false

View File

@ -1,24 +1,23 @@
require.main.paths.splice(0, 0, process.env.NODE_PATH); require.main.paths.splice(0, 0, process.env.NODE_PATH);
var remote = require('remote'); import remote from 'remote';
var Menu = remote.require('menu'); var Menu = remote.require('menu');
var React = require('react'); import React from 'react';
var SetupStore = require('./stores/SetupStore'); import SetupStore from './stores/SetupStore';
var ipc = require('ipc'); import ipc from 'ipc';
var machine = require('./utils/DockerMachineUtil'); import machine from './utils/DockerMachineUtil';
var metrics = require('./utils/MetricsUtil'); import metrics from './utils/MetricsUtil';
var router = require('./router'); import template from './menutemplate';
var template = require('./menutemplate'); import webUtil from './utils/WebUtil';
var webUtil = require('./utils/WebUtil'); import hubUtil from './utils/HubUtil';
var hubUtil = require('./utils/HubUtil');
var urlUtil = require ('./utils/URLUtil'); var urlUtil = require ('./utils/URLUtil');
var app = remote.require('app'); var app = remote.require('app');
var request = require('request'); import request from 'request';
var docker = require('./utils/DockerUtil'); import docker from './utils/DockerUtil';
var hub = require('./utils/HubUtil'); import hub from './utils/HubUtil';
var Router = require('react-router'); import Router from 'react-router';
var routes = require('./routes'); import routes from './routes';
var routerContainer = require('./router'); import routerContainer from './router';
var repositoryActions = require('./actions/RepositoryActions'); import repositoryActions from './actions/RepositoryActions';
hubUtil.init(); hubUtil.init();

View File

@ -1,11 +1,11 @@
var app = require('app'); import app from 'app';
var autoUpdater = require('auto-updater'); import autoUpdater from 'auto-updater';
var BrowserWindow = require('browser-window'); import BrowserWindow from 'browser-window';
var fs = require('fs'); import fs from 'fs';
var os = require('os'); import os from 'os';
var ipc = require('ipc'); import ipc from 'ipc';
var path = require('path'); import path from 'path';
var child_process = require('child_process'); import child_process from 'child_process';
process.env.NODE_PATH = path.join(__dirname, 'node_modules'); process.env.NODE_PATH = path.join(__dirname, 'node_modules');
process.env.RESOURCES_PATH = path.join(__dirname, '/../resources'); process.env.RESOURCES_PATH = path.join(__dirname, '/../resources');

View File

@ -0,0 +1,69 @@
import React from 'react/addons';
import metrics from '../utils/MetricsUtil';
import utils from '../utils/Util';
import Router from 'react-router';
import RetinaImage from 'react-retina-image';
var packages;
try {
packages = utils.packagejson();
} catch (err) {
packages = {};
}
var Preferences = React.createClass({
mixins: [Router.Navigation],
getInitialState: function () {
return {
metricsEnabled: metrics.enabled()
};
},
handleGoBackClick: function () {
this.goBack();
metrics.track('Went Back From About');
},
render: function () {
return (
<div className="preferences">
<div className="about-content">
<a onClick={this.handleGoBackClick}>Go Back</a>
<div className="items">
<div className="item">
<RetinaImage src="cartoon-kitematic.png"/>
<h4>Docker {packages.name}</h4>
<p>{packages.version}</p>
</div>
</div>
<h3>Kitematic is built with:</h3>
<div className="items">
<div className="item">
<RetinaImage src="cartoon-docker.png"/>
<h4>Docker Engine</h4>
<p>{packages["docker-version"]}</p>
</div>
<div className="item">
<RetinaImage src="cartoon-docker-machine.png"/>
<h4>Docker Machine</h4>
<p>{packages["docker-machine-version"]}</p>
</div>
</div>
<h3>Third-Party Software</h3>
<div className="items">
<div className="item">
<h4>VirtualBox</h4>
<p>{packages["virtualbox-version"]}</p>
</div>
</div>
<div className="items">
<div className="item">
<h4>Electron</h4>
<p>{packages["electron-version"]}</p>
</div>
</div>
</div>
</div>
);
}
});
module.exports = Preferences;

View File

@ -1,10 +1,10 @@
var React = require('react/addons'); import React from 'react/addons';
var Router = require('react-router'); import Router from 'react-router';
var RetinaImage = require('react-retina-image'); import RetinaImage from 'react-retina-image';
var Header = require('./Header.react'); import Header from './Header.react';
var metrics = require('../utils/MetricsUtil'); import metrics from '../utils/MetricsUtil';
var accountStore = require('../stores/AccountStore'); import accountStore from '../stores/AccountStore';
var accountActions = require('../actions/AccountActions'); import accountActions from '../actions/AccountActions';
module.exports = React.createClass({ module.exports = React.createClass({
mixins: [Router.Navigation], mixins: [Router.Navigation],

View File

@ -1,10 +1,10 @@
var _ = require('underscore'); import _ from 'underscore';
var React = require('react/addons'); import React from 'react/addons';
var Router = require('react-router'); import Router from 'react-router';
var validator = require('validator'); import validator from 'validator';
var accountActions = require('../actions/AccountActions'); import accountActions from '../actions/AccountActions';
var metrics = require('../utils/MetricsUtil'); import metrics from '../utils/MetricsUtil';
var shell = require('shell'); import shell from 'shell';
module.exports = React.createClass({ module.exports = React.createClass({
mixins: [Router.Navigation, React.addons.LinkedStateMixin], mixins: [Router.Navigation, React.addons.LinkedStateMixin],
@ -60,7 +60,7 @@ module.exports = React.createClass({
}, },
handleClickForgotPassword: function () { handleClickForgotPassword: function () {
shell.openExternal('https://hub.docker.com/account/forgot-password/'); shell.openExternal('https://hub.docker.com/reset-password/');
}, },
render: function () { render: function () {

View File

@ -1,9 +1,9 @@
var _ = require('underscore'); import _ from 'underscore';
var React = require('react/addons'); import React from 'react/addons';
var Router = require('react-router'); import Router from 'react-router';
var validator = require('validator'); import validator from 'validator';
var accountActions = require('../actions/AccountActions'); import accountActions from '../actions/AccountActions';
var metrics = require('../utils/MetricsUtil'); import metrics from '../utils/MetricsUtil';
module.exports = React.createClass({ module.exports = React.createClass({
mixins: [Router.Navigation, React.addons.LinkedStateMixin], mixins: [Router.Navigation, React.addons.LinkedStateMixin],

View File

@ -1,10 +1,10 @@
var React = require('react/addons'); import React from 'react/addons';
var Router = require('react-router'); import Router from 'react-router';
var ContainerDetailsHeader = require('./ContainerDetailsHeader.react'); import ContainerDetailsHeader from './ContainerDetailsHeader.react';
var ContainerDetailsSubheader = require('./ContainerDetailsSubheader.react'); import ContainerDetailsSubheader from './ContainerDetailsSubheader.react';
var containerUtil = require('../utils/ContainerUtil'); import containerUtil from '../utils/ContainerUtil';
var util = require('../utils/Util'); import util from '../utils/Util';
var _ = require('underscore'); import _ from 'underscore';
var ContainerDetails = React.createClass({ var ContainerDetails = React.createClass({
contextTypes: { contextTypes: {

View File

@ -1,4 +1,4 @@
var React = require('react/addons'); import React from 'react/addons';
var ContainerDetailsHeader = React.createClass({ var ContainerDetailsHeader = React.createClass({
render: function () { render: function () {

View File

@ -1,11 +1,11 @@
var _ = require('underscore'); import _ from 'underscore';
var React = require('react'); import React from 'react';
var shell = require('shell'); import shell from 'shell';
var metrics = require('../utils/MetricsUtil'); import metrics from '../utils/MetricsUtil';
var ContainerUtil = require('../utils/ContainerUtil'); import ContainerUtil from '../utils/ContainerUtil';
var classNames = require('classnames'); import classNames from 'classnames';
var containerActions = require('../actions/ContainerActions'); import containerActions from '../actions/ContainerActions';
var dockerMachineUtil = require('../utils/DockerMachineUtil'); import dockerMachineUtil from '../utils/DockerMachineUtil';
var ContainerDetailsSubheader = React.createClass({ var ContainerDetailsSubheader = React.createClass({
contextTypes: { contextTypes: {

View File

@ -1,12 +1,12 @@
var _ = require('underscore'); import _ from 'underscore';
var $ = require('jquery'); import $ from 'jquery';
var React = require('react/addons'); import React from 'react/addons';
var Radial = require('./Radial.react'); import Radial from './Radial.react';
var ContainerProgress = require('./ContainerProgress.react'); import ContainerProgress from './ContainerProgress.react';
var ContainerHomePreview = require('./ContainerHomePreview.react'); import ContainerHomePreview from './ContainerHomePreview.react';
var ContainerHomeLogs = require('./ContainerHomeLogs.react'); import ContainerHomeLogs from './ContainerHomeLogs.react';
var ContainerHomeFolders = require('./ContainerHomeFolders.react'); import ContainerHomeFolders from './ContainerHomeFolders.react';
var shell = require('shell'); import shell from 'shell';
var ContainerHome = React.createClass({ var ContainerHome = React.createClass({
contextTypes: { contextTypes: {
@ -41,7 +41,7 @@ var ContainerHome = React.createClass({
}, },
showFolders: function () { 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 () { render: function () {

View File

@ -1,42 +1,49 @@
var _ = require('underscore'); import _ from 'underscore';
var React = require('react/addons'); import React from 'react/addons';
var RetinaImage = require('react-retina-image'); import RetinaImage from 'react-retina-image';
var path = require('path'); import path from 'path';
var shell = require('shell'); import shell from 'shell';
var util = require('../utils/Util'); import util from '../utils/Util';
var metrics = require('../utils/MetricsUtil'); import metrics from '../utils/MetricsUtil';
var containerActions = require('../actions/ContainerActions'); import containerActions from '../actions/ContainerActions';
var dialog = require('remote').require('dialog'); import remote from 'remote';
var mkdirp = require('mkdirp'); var dialog = remote.require('dialog');
import mkdirp from 'mkdirp';
var ContainerHomeFolder = React.createClass({ var ContainerHomeFolder = React.createClass({
contextTypes: { contextTypes: {
router: React.PropTypes.func router: React.PropTypes.func
}, },
handleClickFolder: function (hostVolume, containerVolume) { handleClickFolder: function (source, destination) {
metrics.track('Opened Volume Directory', { metrics.track('Opened Volume Directory', {
from: 'home' from: 'home'
}); });
if (hostVolume.indexOf(util.windowsToLinuxPath(util.home())) === -1) { if (source.indexOf(util.windowsToLinuxPath(util.home())) === -1) {
dialog.showMessageBox({ 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'] buttons: ['Enable Volumes', 'Cancel']
}, (index) => { }, (index) => {
if (index === 0) { if (index === 0) {
var volumes = _.clone(this.props.container.Volumes); var mounts = _.clone(this.props.container.Mounts);
var newHostVolume = util.escapePath(path.join(util.home(), util.documents(), 'Kitematic', this.props.container.Name, containerVolume)); var newSource = util.escapePath(path.join(util.home(), util.documents(), 'Kitematic', this.props.container.Name, destination));
volumes[containerVolume] = newHostVolume; var binds = mounts.map(function (m) {
var binds = _.pairs(volumes).map(function (pair) { let source = m.Source;
if(util.isWindows()) { if (m.Destination === destination) {
return util.windowsToLinuxPath(pair[1]) + ':' + pair[0]; 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); console.log(err);
if (!err) { if (!err) {
shell.showItemInFolder(newHostVolume); shell.showItemInFolder(newSource);
} }
}); });
@ -44,7 +51,7 @@ var ContainerHomeFolder = React.createClass({
} }
}); });
} else { } else {
let path = util.isWindows() ? util.linuxToWindowsPath(hostVolume) : hostVolume; let path = util.isWindows() ? util.linuxToWindowsPath(source) : source;
shell.showItemInFolder(path); shell.showItemInFolder(path);
} }
}, },
@ -59,12 +66,13 @@ var ContainerHomeFolder = React.createClass({
return false; return false;
} }
var folders = _.map(_.omit(this.props.container.Volumes, (v, k) => k.indexOf('/Users/') !== -1), (val, key) => { var folders = _.map(this.props.container.Mounts, (m, i) => {
var firstFolder = key; let destination = m.Destination;
let source = m.Source;
return ( return (
<div key={key} className="folder" onClick={this.handleClickFolder.bind(this, val, key)}> <div key={i} className="folder" onClick={this.handleClickFolder.bind(this, source, destination)}>
<RetinaImage src="folder.png" /> <RetinaImage src="folder.png" />
<div className="text">{firstFolder}</div> <div className="text">{destination}</div>
</div> </div>
); );
}); });

View File

@ -1,8 +1,8 @@
var $ = require('jquery'); import $ from 'jquery';
var React = require('react/addons'); import React from 'react/addons';
var LogStore = require('../stores/LogStore'); import LogStore from '../stores/LogStore';
var Router = require('react-router'); import Router from 'react-router';
var metrics = require('../utils/MetricsUtil'); import metrics from '../utils/MetricsUtil';
var _prevBottom = 0; var _prevBottom = 0;

View File

@ -1,8 +1,8 @@
var _ = require('underscore'); import _ from 'underscore';
var React = require('react/addons'); import React from 'react/addons';
var request = require('request'); import request from 'request';
var shell = require('shell'); import shell from 'shell';
var metrics = require('../utils/MetricsUtil'); import metrics from '../utils/MetricsUtil';
var ContainerHomePreview = React.createClass({ var ContainerHomePreview = React.createClass({
contextTypes: { contextTypes: {

View File

@ -1,5 +1,5 @@
var React = require('react/addons'); import React from 'react/addons';
var ContainerListItem = require('./ContainerListItem.react'); import ContainerListItem from './ContainerListItem.react';
var ContainerList = React.createClass({ var ContainerList = React.createClass({
componentWillMount: function () { componentWillMount: function () {

View File

@ -1,12 +1,11 @@
var $ = require('jquery'); import $ from 'jquery';
var React = require('react/addons'); import React from 'react/addons';
var Router = require('react-router'); import Router from 'react-router';
var remote = require('remote'); import remote from 'remote';
var dialog = remote.require('dialog'); var dialog = remote.require('dialog');
var metrics = require('../utils/MetricsUtil'); import metrics from '../utils/MetricsUtil';
var OverlayTrigger = require('react-bootstrap').OverlayTrigger; import {OverlayTrigger, Tooltip} from 'react-bootstrap';
var Tooltip = require('react-bootstrap').Tooltip; import containerActions from '../actions/ContainerActions';
var containerActions = require('../actions/ContainerActions');
var ContainerListItem = React.createClass({ var ContainerListItem = React.createClass({
handleItemMouseEnter: function () { handleItemMouseEnter: function () {

View File

@ -1,6 +1,6 @@
var $ = require('jquery'); import $ from 'jquery';
var React = require('react/addons'); import React from 'react/addons';
var LogStore = require('../stores/LogStore'); import LogStore from '../stores/LogStore';
var _prevBottom = 0; var _prevBottom = 0;

View File

@ -1,4 +1,4 @@
var React = require('react'); import React from 'react';
/* /*

View File

@ -1,7 +1,7 @@
var $ = require('jquery'); import $ from 'jquery';
var _ = require('underscore'); import _ from 'underscore';
var React = require('react/addons'); import React from 'react/addons';
var Router = require('react-router'); import Router from 'react-router';
var ContainerSettings = React.createClass({ var ContainerSettings = React.createClass({
contextTypes: { contextTypes: {

View File

@ -1,7 +1,7 @@
var React = require('react/addons'); import React from 'react/addons';
var metrics = require('../utils/MetricsUtil'); import metrics from '../utils/MetricsUtil';
var ContainerUtil = require('../utils/ContainerUtil'); import ContainerUtil from '../utils/ContainerUtil';
var containerActions = require('../actions/ContainerActions'); import containerActions from '../actions/ContainerActions';
var ContainerSettingsAdvanced = React.createClass({ var ContainerSettingsAdvanced = React.createClass({
mixins: [React.addons.LinkedStateMixin], mixins: [React.addons.LinkedStateMixin],

View File

@ -1,11 +1,11 @@
var _ = require('underscore'); import _ from 'underscore';
var React = require('react/addons'); import React from 'react/addons';
var remote = require('remote'); import metrics from '../utils/MetricsUtil';
var metrics = require('../utils/MetricsUtil'); import remote from 'remote';
var dialog = remote.require('dialog'); var dialog = remote.require('dialog');
var ContainerUtil = require('../utils/ContainerUtil'); import ContainerUtil from '../utils/ContainerUtil';
var containerActions = require('../actions/ContainerActions'); import containerActions from '../actions/ContainerActions';
var util = require('../utils/Util'); import util from '../utils/Util';
var ContainerSettingsGeneral = React.createClass({ var ContainerSettingsGeneral = React.createClass({
mixins: [React.addons.LinkedStateMixin], mixins: [React.addons.LinkedStateMixin],

View File

@ -1,9 +1,9 @@
var _ = require('underscore'); import _ from 'underscore';
var React = require('react/addons'); import React from 'react/addons';
var shell = require('shell'); import shell from 'shell';
var ContainerUtil = require('../utils/ContainerUtil'); import ContainerUtil from '../utils/ContainerUtil';
var metrics = require('../utils/MetricsUtil'); import metrics from '../utils/MetricsUtil';
var webPorts = require('../utils/Util').webPorts; import {webPorts} from '../utils/Util';
var ContainerSettingsPorts = React.createClass({ var ContainerSettingsPorts = React.createClass({
contextTypes: { contextTypes: {

View File

@ -1,11 +1,11 @@
var _ = require('underscore'); import _ from 'underscore';
var React = require('react/addons'); import React from 'react/addons';
var remote = require('remote'); import remote from 'remote';
var dialog = remote.require('dialog'); var dialog = remote.require('dialog');
var shell = require('shell'); import shell from 'shell';
var util = require('../utils/Util'); import util from '../utils/Util';
var metrics = require('../utils/MetricsUtil'); import metrics from '../utils/MetricsUtil';
var containerActions = require('../actions/ContainerActions'); import containerActions from '../actions/ContainerActions';
var ContainerSettingsVolumes = React.createClass({ var ContainerSettingsVolumes = React.createClass({
handleChooseVolumeClick: function (dockerVol) { handleChooseVolumeClick: function (dockerVol) {
@ -26,16 +26,23 @@ var ContainerSettingsVolumes = React.createClass({
} }
metrics.track('Choose Directory for Volume'); metrics.track('Choose Directory for Volume');
if(util.isWindows()) { if(util.isWindows()) {
directory = util.escapePath(util.windowsToLinuxPath(directory)); directory = util.escapePath(util.windowsToLinuxPath(directory));
} }
var volumes = _.clone(this.props.container.Volumes);
volumes[dockerVol] = directory; var mounts = _.clone(this.props.container.Mounts);
var binds = _.pairs(volumes).map(function (pair) { _.each(mounts, m => {
return pair[1] + ':' + pair[0]; 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) { handleRemoveVolumeClick: function (dockerVol) {
@ -45,13 +52,17 @@ var ContainerSettingsVolumes = React.createClass({
var hostConfig = _.clone(this.props.container.HostConfig); var hostConfig = _.clone(this.props.container.HostConfig);
var binds = hostConfig.Binds; var binds = hostConfig.Binds;
var volumes = _.clone(this.props.container.Volumes); var mounts = _.clone(this.props.container.Mounts);
volumes[dockerVol] = null; _.each(mounts, m => {
if (m.Destination === dockerVol) {
m.Source = null;
}
});
var index = _.findIndex(binds, bind => bind.indexOf(`:${dockerVol}`) !== -1); var index = _.findIndex(binds, bind => bind.indexOf(`:${dockerVol}`) !== -1);
if (index >= 0) { if (index >= 0) {
binds.splice(index, 1); 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) { handleOpenVolumeClick: function (path) {
metrics.track('Opened Volume Directory', { metrics.track('Opened Volume Directory', {
@ -69,24 +80,25 @@ var ContainerSettingsVolumes = React.createClass({
} }
var homeDir = util.isWindows() ? util.windowsToLinuxPath(util.home()) : util.home(); var homeDir = util.isWindows() ? util.windowsToLinuxPath(util.home()) : util.home();
var volumes = _.map(this.props.container.Volumes, (val, key) => { var mounts= _.map(this.props.container.Mounts, (m, i) => {
if (!val || val.indexOf(homeDir) === -1) { let source = m.Source, destination = m.Destination;
val = ( if (!m.Source || m.Source.indexOf(homeDir) === -1) {
source = (
<span className="value-right">No Folder</span> <span className="value-right">No Folder</span>
); );
} else { } else {
let local = util.isWindows() ? util.linuxToWindowsPath(val) : val; let local = util.isWindows() ? util.linuxToWindowsPath(source) : source;
val = ( source = (
<a className="value-right" onClick={this.handleOpenVolumeClick.bind(this, val)}>{local.replace(process.env.HOME, '~')}</a> <a className="value-right" onClick={this.handleOpenVolumeClick.bind(this, source)}>{local.replace(process.env.HOME, '~')}</a>
); );
} }
return ( return (
<tr> <tr>
<td>{key}</td> <td>{destination}</td>
<td>{val}</td> <td>{source}</td>
<td> <td>
<a className="btn btn-action small" disabled={this.props.container.State.Updating} onClick={this.handleChooseVolumeClick.bind(this, key)}>Change</a> <a className="btn btn-action small" disabled={this.props.container.State.Updating} onClick={this.handleChooseVolumeClick.bind(this, destination)}>Change</a>
<a className="btn btn-action small" disabled={this.props.container.State.Updating} onClick={this.handleRemoveVolumeClick.bind(this, key)}>Remove</a> <a className="btn btn-action small" disabled={this.props.container.State.Updating} onClick={this.handleRemoveVolumeClick.bind(this, destination)}>Remove</a>
</td> </td>
</tr> </tr>
); );
@ -104,7 +116,7 @@ var ContainerSettingsVolumes = React.createClass({
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{volumes} {mounts}
</tbody> </tbody>
</table> </table>
</div> </div>

View File

@ -1,13 +1,13 @@
var $ = require('jquery'); import $ from 'jquery';
var _ = require('underscore'); import _ from 'underscore';
var React = require('react'); import React from 'react';
var Router = require('react-router'); import Router from 'react-router';
var containerStore = require('../stores/ContainerStore'); import containerStore from '../stores/ContainerStore';
var ContainerList = require('./ContainerList.react'); import ContainerList from './ContainerList.react';
var Header = require('./Header.react'); import Header from './Header.react';
var metrics = require('../utils/MetricsUtil'); import metrics from '../utils/MetricsUtil';
var shell = require('shell'); import shell from 'shell';
var machine = require('../utils/DockerMachineUtil'); import machine from '../utils/DockerMachineUtil';
var Containers = React.createClass({ var Containers = React.createClass({
contextTypes: { contextTypes: {

View File

@ -1,17 +1,16 @@
var React = require('react/addons'); import React from 'react/addons';
var remote = require('remote'); import remote from 'remote';
var RetinaImage = require('react-retina-image'); import RetinaImage from 'react-retina-image';
var remote = require('remote'); import ipc from 'ipc';
var ipc = require('ipc');
var autoUpdater = remote.require('auto-updater'); var autoUpdater = remote.require('auto-updater');
var util = require('../utils/Util'); import util from '../utils/Util';
var metrics = require('../utils/MetricsUtil'); import metrics from '../utils/MetricsUtil';
var Menu = remote.require('menu'); var Menu = remote.require('menu');
var MenuItem = remote.require('menu-item'); var MenuItem = remote.require('menu-item');
var accountStore = require('../stores/AccountStore'); import accountStore from '../stores/AccountStore';
var accountActions = require('../actions/AccountActions'); import accountActions from '../actions/AccountActions';
var Router = require('react-router'); import Router from 'react-router';
var classNames = require('classnames'); import classNames from 'classnames';
var Header = React.createClass({ var Header = React.createClass({
mixins: [Router.Navigation], mixins: [Router.Navigation],

View File

@ -1,13 +1,13 @@
var $ = require('jquery'); import $ from 'jquery';
var React = require('react/addons'); import React from 'react/addons';
var Router = require('react-router'); import Router from 'react-router';
var shell = require('shell'); import shell from 'shell';
var RetinaImage = require('react-retina-image'); import RetinaImage from 'react-retina-image';
var metrics = require('../utils/MetricsUtil'); import metrics from '../utils/MetricsUtil';
var containerActions = require('../actions/ContainerActions'); import containerActions from '../actions/ContainerActions';
var containerStore = require('../stores/ContainerStore'); import containerStore from '../stores/ContainerStore';
var tagStore = require('../stores/TagStore'); import tagStore from '../stores/TagStore';
var tagActions = require('../actions/TagActions'); import tagActions from '../actions/TagActions';
var ImageCard = React.createClass({ var ImageCard = React.createClass({
mixins: [Router.Navigation], mixins: [Router.Navigation],
@ -108,7 +108,8 @@ var ImageCard = React.createClass({
description = "No description."; description = "No description.";
} }
var logoStyle = { 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; var imgsrc;
if (this.props.image.img) { if (this.props.image.img) {

View File

@ -1,9 +1,9 @@
var React = require('react/addons'); import React from 'react/addons';
var Router = require('react-router'); import Router from 'react-router';
var shell = require('shell'); import shell from 'shell';
var containerActions = require('../actions/ContainerActions'); import containerActions from '../actions/ContainerActions';
var containerStore = require('../stores/ContainerStore'); import containerStore from '../stores/ContainerStore';
var metrics = require('../utils/MetricsUtil'); import metrics from '../utils/MetricsUtil';
module.exports = React.createClass({ module.exports = React.createClass({
mixins: [Router.Navigation], mixins: [Router.Navigation],

View File

@ -1,15 +1,15 @@
var _ = require('underscore'); import _ from 'underscore';
var React = require('react/addons'); import React from 'react/addons';
var Router = require('react-router'); import Router from 'react-router';
var RetinaImage = require('react-retina-image'); import RetinaImage from 'react-retina-image';
var ImageCard = require('./ImageCard.react'); import ImageCard from './ImageCard.react';
var Promise = require('bluebird'); import Promise from 'bluebird';
var metrics = require('../utils/MetricsUtil'); import metrics from '../utils/MetricsUtil';
var classNames = require('classnames'); import classNames from 'classnames';
var repositoryActions = require('../actions/RepositoryActions'); import repositoryActions from '../actions/RepositoryActions';
var repositoryStore = require('../stores/RepositoryStore'); import repositoryStore from '../stores/RepositoryStore';
var accountStore = require('../stores/AccountStore'); import accountStore from '../stores/AccountStore';
var accountActions = require('../actions/AccountActions'); import accountActions from '../actions/AccountActions';
var _searchPromise = null; var _searchPromise = null;
@ -222,7 +222,7 @@ module.exports = React.createClass({
<div className="new-container-header"> <div className="new-container-header">
<div className="search"> <div className="search">
<div className="search-bar"> <div className="search-bar">
<input type="search" ref="searchInput" className="form-control" placeholder="Search image on Docker Hub" onChange={this.handleChange}/> <input type="search" ref="searchInput" className="form-control" placeholder="Search for Docker images from Docker Hub" onChange={this.handleChange}/>
<div className={magnifierClasses}></div> <div className={magnifierClasses}></div>
<div className={loadingClasses}><div></div></div> <div className={loadingClasses}><div></div></div>
</div> </div>

View File

@ -1,6 +1,6 @@
var React = require('react/addons'); import React from 'react/addons';
var metrics = require('../utils/MetricsUtil'); import metrics from '../utils/MetricsUtil';
var Router = require('react-router'); import Router from 'react-router';
var Preferences = React.createClass({ var Preferences = React.createClass({
mixins: [Router.Navigation], mixins: [Router.Navigation],
@ -42,7 +42,7 @@ var Preferences = React.createClass({
<div className="title">VM Settings</div> <div className="title">VM Settings</div>
<div className="option"> <div className="option">
<div className="option-name"> <div className="option-name">
Shut Down Linux VM on closing Kitematic Shutdown Linux VM on closing Kitematic
</div> </div>
<div className="option-value"> <div className="option-value">
<input type="checkbox" checked={this.state.closeVMOnQuit} onChange={this.handleChangeCloseVMOnQuit}/> <input type="checkbox" checked={this.state.closeVMOnQuit} onChange={this.handleChangeCloseVMOnQuit}/>

View File

@ -1,5 +1,5 @@
var React = require('react'); import React from 'react';
var classNames = require('classnames'); import classNames from 'classnames';
var Radial = React.createClass({ var Radial = React.createClass({
render: function () { render: function () {

View File

@ -1,11 +1,11 @@
var React = require('react/addons'); import React from 'react/addons';
var Router = require('react-router'); import Router from 'react-router';
var Radial = require('./Radial.react.js'); import Radial from './Radial.react.js';
var SetupStore = require('../stores/SetupStore'); import SetupStore from '../stores/SetupStore';
var RetinaImage = require('react-retina-image'); import RetinaImage from 'react-retina-image';
var Header = require('./Header.react'); import Header from './Header.react';
var Util = require('../utils/Util'); import Util from '../utils/Util';
var metrics = require('../utils/MetricsUtil'); import metrics from '../utils/MetricsUtil';
var Setup = React.createClass({ var Setup = React.createClass({
mixins: [ Router.Navigation ], mixins: [ Router.Navigation ],

View File

@ -17,7 +17,12 @@ var MenuTemplate = function () {
submenu: [ submenu: [
{ {
label: 'About Kitematic', label: 'About Kitematic',
selector: 'orderFrontStandardAboutPanel:' click: function () {
metrics.track('Opened About', {
from: 'menu'
});
router.get().transitionTo('about');
}
}, },
{ {
type: 'separator' type: 'separator'
@ -37,35 +42,9 @@ var MenuTemplate = function () {
type: 'separator' type: 'separator'
}, },
{ {
label: 'Install Docker Commands', type: 'separator'
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'
}, {
label: 'Hide Kitematic', label: 'Hide Kitematic',
accelerator: util.CommandOrCtrl() + '+H', accelerator: util.CommandOrCtrl() + '+H',
selector: 'hide:' selector: 'hide:'

View File

@ -1,21 +1,22 @@
var React = require('react/addons'); import React from 'react/addons';
var Setup = require('./components/Setup.react'); import Setup from './components/Setup.react';
var Account = require('./components/Account.react'); import Account from './components/Account.react';
var AccountSignup = require('./components/AccountSignup.react'); import AccountSignup from './components/AccountSignup.react';
var AccountLogin = require('./components/AccountLogin.react'); import AccountLogin from './components/AccountLogin.react';
var Containers = require('./components/Containers.react'); import Containers from './components/Containers.react';
var ContainerDetails = require('./components/ContainerDetails.react'); import ContainerDetails from './components/ContainerDetails.react';
var ContainerHome = require('./components/ContainerHome.react'); import ContainerHome from './components/ContainerHome.react';
var ContainerLogs = require('./components/ContainerLogs.react'); import ContainerLogs from './components/ContainerLogs.react';
var ContainerSettings = require('./components/ContainerSettings.react'); import ContainerSettings from './components/ContainerSettings.react';
var ContainerSettingsGeneral = require('./components/ContainerSettingsGeneral.react'); import ContainerSettingsGeneral from './components/ContainerSettingsGeneral.react';
var ContainerSettingsPorts = require('./components/ContainerSettingsPorts.react'); import ContainerSettingsPorts from './components/ContainerSettingsPorts.react';
var ContainerSettingsVolumes = require('./components/ContainerSettingsVolumes.react'); import ContainerSettingsVolumes from './components/ContainerSettingsVolumes.react';
var ContainerSettingsAdvanced = require('./components/ContainerSettingsAdvanced.react'); import ContainerSettingsAdvanced from './components/ContainerSettingsAdvanced.react';
var Preferences = require('./components/Preferences.react'); import Preferences from './components/Preferences.react';
var NewContainerSearch = require('./components/NewContainerSearch.react'); import About from './components/About.react';
var NewContainerPull = require('./components/NewContainerPull.react'); import NewContainerSearch from './components/NewContainerSearch.react';
var Router = require('react-router'); import NewContainerPull from './components/NewContainerPull.react';
import Router from 'react-router';
var Route = Router.Route; var Route = Router.Route;
var DefaultRoute = Router.DefaultRoute; var DefaultRoute = Router.DefaultRoute;
@ -51,6 +52,7 @@ var routes = (
<Route name="pull" path="containers/new/pull" handler={NewContainerPull}></Route> <Route name="pull" path="containers/new/pull" handler={NewContainerPull}></Route>
</Route> </Route>
<Route name="preferences" path="/preferences" handler={Preferences}/> <Route name="preferences" path="/preferences" handler={Preferences}/>
<Route name="about" path="/about" handler={About}/>
</Route> </Route>
<DefaultRoute name="setup" handler={Setup}/> <DefaultRoute name="setup" handler={Setup}/>
</Route> </Route>

View File

@ -1,8 +1,8 @@
var EventEmitter = require('events').EventEmitter; import {EventEmitter} from 'events';
var assign = require('object-assign'); import assign from 'object-assign';
var Convert = require('ansi-to-html'); import Convert from 'ansi-to-html';
var docker = require('../utils/DockerUtil'); import docker from '../utils/DockerUtil';
var stream = require('stream'); import stream from 'stream';
var _convert = new Convert(); var _convert = new Convert();
var _logs = {}; var _logs = {};

View File

@ -1,16 +1,16 @@
var EventEmitter = require('events').EventEmitter; import {EventEmitter} from 'events';
var _ = require('underscore'); import _ from 'underscore';
var path = require('path'); import path from 'path';
var fs = require('fs'); import fs from 'fs';
var Promise = require('bluebird'); import Promise from 'bluebird';
var machine = require('../utils/DockerMachineUtil'); import machine from '../utils/DockerMachineUtil';
var virtualBox = require('../utils/VirtualBoxUtil'); import virtualBox from '../utils/VirtualBoxUtil';
var setupUtil = require('../utils/SetupUtil'); import setupUtil from '../utils/SetupUtil';
var util = require('../utils/Util'); import util from '../utils/Util';
var assign = require('object-assign'); import assign from 'object-assign';
var metrics = require('../utils/MetricsUtil'); import metrics from '../utils/MetricsUtil';
var bugsnag = require('bugsnag-js'); import bugsnag from 'bugsnag-js';
var docker = require('../utils/DockerUtil'); import docker from '../utils/DockerUtil';
var _currentStep = null; var _currentStep = null;
var _error = null; var _error = null;
@ -73,7 +73,8 @@ var _steps = [{
var isoversion = machine.isoversion(); var isoversion = machine.isoversion();
var packagejson = util.packagejson(); 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.start();
yield machine.upgrade(); yield machine.upgrade();
} }

View File

@ -1,5 +1,5 @@
var _ = require('underscore'); import _ from 'underscore';
var docker = require('../utils/DockerUtil'); import docker from '../utils/DockerUtil';
var ContainerUtil = { var ContainerUtil = {
env: function (container) { env: function (container) {

View File

@ -1,22 +1,20 @@
var _ = require('underscore'); import _ from 'underscore';
var path = require('path'); import path from 'path';
var Promise = require('bluebird'); import Promise from 'bluebird';
var fs = require('fs'); import fs from 'fs';
var util = require('./Util'); import util from './Util';
var resources = require('./ResourcesUtil'); import resources from './ResourcesUtil';
var NAME = util.isWindows () ? 'kitematic' : 'dev';
var DockerMachine = { var DockerMachine = {
command: function () { command: function () {
return resources.dockerMachine(); return resources.dockerMachine();
}, },
name: function () { name: function () {
return NAME; return 'default';
}, },
isoversion: function () { isoversion: function (machineName = this.name()) {
try { 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+)/); var match = data.match(/Boot2Docker-v(\d+\.\d+\.\d+)/);
if (match) { if (match) {
return match[1]; return match[1];
@ -27,7 +25,7 @@ var DockerMachine = {
return null; return null;
} }
}, },
info: function () { info: function (machineName = this.name()) {
return util.exec([this.command(), 'ls']).then(stdout => { return util.exec([this.command(), 'ls']).then(stdout => {
var lines = stdout.trim().split('\n').filter(line => line.indexOf('time=') === -1); var lines = stdout.trim().split('\n').filter(line => line.indexOf('time=') === -1);
var machines = {}; var machines = {};
@ -41,54 +39,50 @@ var DockerMachine = {
}; };
machines[machine.name] = machine; machines[machine.name] = machine;
}); });
if (machines[NAME]) { if (machines[machineName]) {
return Promise.resolve(machines[NAME]); return Promise.resolve(machines[machineName]);
} else { } else {
return Promise.reject(new Error('Machine does not exist.')); return Promise.reject(new Error('Machine does not exist.'));
} }
}); });
}, },
exists: function () { exists: function (machineName = this.name()) {
return this.info().then(() => { return this.info(machineName).then(() => {
return true; return true;
}).catch(() => { }).catch(() => {
return false; return false;
}); });
}, },
create: function () { create: function (machineName = this.name()) {
if (util.isWindows()) { return util.exec([this.command(), '-D', 'create', '-d', 'virtualbox', '--virtualbox-memory', '2048', machineName]);
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]);
}
}, },
start: function () { start: function (machineName = this.name()) {
return util.exec([this.command(), '-D', 'start', NAME]); return util.exec([this.command(), '-D', 'start', machineName]);
}, },
stop: function () { stop: function (machineName = this.name()) {
return util.exec([this.command(), 'stop', NAME]); return util.exec([this.command(), 'stop', machineName]);
}, },
upgrade: function () { upgrade: function (machineName = this.name()) {
return util.exec([this.command(), 'upgrade', NAME]); return util.exec([this.command(), 'upgrade', machineName]);
}, },
rm: function () { rm: function (machineName = this.name()) {
return util.exec([this.command(), 'rm', '-f', NAME]); return util.exec([this.command(), 'rm', '-f', machineName]);
}, },
ip: function () { ip: function (machineName = this.name()) {
return util.exec([this.command(), 'ip', NAME]).then(stdout => { return util.exec([this.command(), 'ip', machineName]).then(stdout => {
return Promise.resolve(stdout.trim().replace('\n', '')); return Promise.resolve(stdout.trim().replace('\n', ''));
}); });
}, },
regenerateCerts: function () { regenerateCerts: function (machineName = this.name()) {
return util.exec([this.command(), 'tls-regenerate-certs', '-f', NAME]); return util.exec([this.command(), 'tls-regenerate-certs', '-f', machineName]);
}, },
state: function () { state: function (machineName = this.name()) {
return this.info().then(info => { return this.info(machineName).then(info => {
return info ? info.state : null; return info ? info.state : null;
}); });
}, },
disk: function () { disk: function (machineName = this.name()) {
return util.exec([this.command(), 'ssh', NAME, 'df']).then(stdout => { return util.exec([this.command(), 'ssh', machineName, 'df']).then(stdout => {
try { try {
var lines = stdout.split('\n'); var lines = stdout.split('\n');
var dataline = _.find(lines, function (line) { var dataline = _.find(lines, function (line) {
@ -111,8 +105,8 @@ var DockerMachine = {
} }
}); });
}, },
memory: function () { memory: function (machineName = this.name()) {
return util.exec([this.command(), 'ssh', NAME, 'free -m']).then(stdout => { return util.exec([this.command(), 'ssh', machineName, 'free -m']).then(stdout => {
try { try {
var lines = stdout.split('\n'); var lines = stdout.split('\n');
var dataline = _.find(lines, function (line) { var dataline = _.find(lines, function (line) {
@ -137,8 +131,8 @@ var DockerMachine = {
} }
}); });
}, },
stats: function () { stats: function (machineName = this.name()) {
this.state().then(state => { this.state(machineName).then(state => {
if (state === 'Stopped') { if (state === 'Stopped') {
return Promise.resolve({state: state}); return Promise.resolve({state: state});
} }
@ -152,10 +146,10 @@ var DockerMachine = {
}); });
}); });
}, },
dockerTerminal: function (cmd) { dockerTerminal: function (cmd, machineName = this.name()) {
if(util.isWindows()) { if(util.isWindows()) {
cmd = cmd || ''; cmd = cmd || '';
this.info().then(machine => { this.info(machineName).then(machine => {
util.exec('start powershell.exe ' + cmd, util.exec('start powershell.exe ' + cmd,
{env: { {env: {
'DOCKER_HOST' : machine.url, 'DOCKER_HOST' : machine.url,
@ -166,7 +160,7 @@ var DockerMachine = {
}); });
} else { } else {
cmd = cmd || process.env.SHELL; 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(() => {}); 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(() => {});
}); });
} }

View File

@ -135,26 +135,23 @@ export default {
if (err) { if (err) {
return; return;
} }
async.map(containers, (container, callback) => {
let modifiedContainers = _.map(containers, container => { this.client.getContainer(container.Id).inspect((error, container) => {
container.Name = container.Names[0].replace('/', ''); if (error) {
delete container.Names; callback(null, null);
return;
// 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;
} }
return container; 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;
}
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; 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); var fullData = _.extend(existingData, data);
this.createContainer(name, fullData); this.createContainer(name, fullData);
}); });
@ -305,12 +307,12 @@ export default {
let container = this.client.getContainer(name); let container = this.client.getContainer(name);
container.unpause(function () { container.unpause(function () {
container.kill(function (error) { container.kill(function () {
container.remove(function (error) {
if (error) { if (error) {
containerServerActions.error({name, error}); containerServerActions.error({name, error});
return; return;
} }
container.remove(function () {
containerServerActions.destroyed({id: name}); containerServerActions.destroyed({id: name});
var volumePath = path.join(util.home(), 'Kitematic', name); var volumePath = path.join(util.home(), 'Kitematic', name);
if (fs.existsSync(volumePath)) { if (fs.existsSync(volumePath)) {

View File

@ -1,7 +1,7 @@
var _ = require('underscore'); import _ from 'underscore';
var request = require('request'); import request from 'request';
var accountServerActions = require('../actions/AccountServerActions'); import accountServerActions from '../actions/AccountServerActions';
var metrics = require('./MetricsUtil'); import metrics from './MetricsUtil';
let HUB2_ENDPOINT = process.env.HUB2_ENDPOINT || 'https://hub.docker.com/v2'; let HUB2_ENDPOINT = process.env.HUB2_ENDPOINT || 'https://hub.docker.com/v2';

View File

@ -1,11 +1,11 @@
var assign = require('object-assign'); import assign from 'object-assign';
var Mixpanel = require('mixpanel'); import Mixpanel from 'mixpanel';
var uuid = require('node-uuid'); import uuid from 'node-uuid';
var fs = require('fs'); import fs from 'fs';
var path = require('path'); import path from 'path';
var util = require('./Util'); import util from './Util';
var os = require('os'); import os from 'os';
var osxRelease = require('osx-release'); import osxRelease from 'osx-release';
var settings; var settings;
try { try {

View File

@ -1,10 +1,10 @@
var _ = require('underscore'); import _ from 'underscore';
var request = require('request'); import request from 'request';
var async = require('async'); import async from 'async';
var util = require('../utils/Util'); import util from '../utils/Util';
var hubUtil = require('../utils/HubUtil'); import hubUtil from '../utils/HubUtil';
var repositoryServerActions = require('../actions/RepositoryServerActions'); import repositoryServerActions from '../actions/RepositoryServerActions';
var tagServerActions = require('../actions/TagServerActions'); import tagServerActions from '../actions/TagServerActions';
let REGHUB2_ENDPOINT = process.env.REGHUB2_ENDPOINT || 'https://registry.hub.docker.com/v2'; let REGHUB2_ENDPOINT = process.env.REGHUB2_ENDPOINT || 'https://registry.hub.docker.com/v2';
let searchReq = null; let searchReq = null;
@ -103,11 +103,11 @@ module.exports = {
}, (error, response, body) => { }, (error, response, body) => {
if (response.statusCode === 200) { if (response.statusCode === 200) {
let data = JSON.parse(body); let data = JSON.parse(body);
tagServerActions.tagsUpdated({repo, tags: data}); tagServerActions.tagsUpdated({repo, tags: data.results || []});
if (callback) { callback(null, data); } if (callback) { callback(null, data.results || []); }
} else if (error || response.statusCode === 401) { } else {
repositoryServerActions.error({repo}); repositoryServerActions.error({repo});
if (callback) { callback(new Error('Failed to fetch repos')); } if (callback) { callback(new Error('Failed to fetch tags for repo')); }
} }
}); });
}, },

View File

@ -1,5 +1,5 @@
var util = require('./Util'); import util from './Util';
var path = require('path'); import path from 'path';
module.exports = { module.exports = {
resourceDir: function () { resourceDir: function () {

View File

@ -1,12 +1,12 @@
var _ = require('underscore'); import _ from 'underscore';
var crypto = require('crypto'); import crypto from 'crypto';
var fs = require('fs'); import fs from 'fs';
var path = require('path'); import path from 'path';
var request = require('request'); import request from 'request';
var progress = require('request-progress'); import progress from 'request-progress';
var Promise = require('bluebird'); import Promise from 'bluebird';
var util = require('./Util'); import util from './Util';
var resources = require('./ResourcesUtil'); import resources from './ResourcesUtil';
var virtualBox = require ('./VirtualBoxUtil'); var virtualBox = require ('./VirtualBoxUtil');
var SetupUtil = { var SetupUtil = {

View File

@ -1,6 +1,6 @@
var util = require('./Util'); import util from './Util';
var parseUri = require('parseUri'); import parseUri from 'parseUri';
var containerServerActions = require('../actions/ContainerServerActions'); import containerServerActions from '../actions/ContainerServerActions';
module.exports = { module.exports = {
TYPE_WHITELIST: ['repository'], TYPE_WHITELIST: ['repository'],

View File

@ -1,10 +1,10 @@
var exec = require('exec'); import exec from 'exec';
var child_process = require('child_process'); import child_process from 'child_process';
var Promise = require('bluebird'); import Promise from 'bluebird';
var fs = require('fs'); import fs from 'fs';
var path = require('path'); import path from 'path';
var crypto = require('crypto'); import crypto from 'crypto';
var remote = require('remote'); import remote from 'remote';
var app = remote.require('app'); var app = remote.require('app');
module.exports = { module.exports = {
@ -71,7 +71,8 @@ module.exports = {
} }
return str.replace(/-----BEGIN CERTIFICATE-----.*-----END CERTIFICATE-----/mg, '<redacted>') return str.replace(/-----BEGIN CERTIFICATE-----.*-----END CERTIFICATE-----/mg, '<redacted>')
.replace(/-----BEGIN RSA PRIVATE KEY-----.*-----END RSA PRIVATE KEY-----/mg, '<redacted>') .replace(/-----BEGIN RSA PRIVATE KEY-----.*-----END RSA PRIVATE KEY-----/mg, '<redacted>')
.replace(/\/Users\/[^\/]*\//mg, '/Users/<redacted>/'); .replace(/\/Users\/[^\/]*\//mg, '/Users/<redacted>/')
.replace(/\\Users\\[^\/]*\\/mg, '\\Users\\<redacted>\\');
}, },
packagejson: function () { packagejson: function () {
return JSON.parse(fs.readFileSync(path.join(__dirname, '..', 'package.json'), 'utf8')); return JSON.parse(fs.readFileSync(path.join(__dirname, '..', 'package.json'), 'utf8'));

View File

@ -1,13 +1,13 @@
var fs = require('fs'); import fs from 'fs';
var util = require('./Util'); import util from './Util';
var Promise = require('bluebird'); import Promise from 'bluebird';
var VirtualBox = { var VirtualBox = {
command: function () { command: function () {
if(util.isWindows()) { if(util.isWindows()) {
return 'C:\\Program Files\\Oracle\\VirtualBox\\VBoxManage.exe'; return 'C:\\Program Files\\Oracle\\VirtualBox\\VBoxManage.exe';
} else { } else {
return '/usr/bin/VBoxManage'; return '/Applications/VirtualBox.app/Contents/MacOS/VBoxManage';
} }
}, },
filename: function () { filename: function () {
@ -23,7 +23,7 @@ var VirtualBox = {
if(util.isWindows()) { if(util.isWindows()) {
return fs.existsSync('C:\\Program Files\\Oracle\\VirtualBox\\VBoxManage.exe') && fs.existsSync('C:\\Program Files\\Oracle\\VirtualBox\\VirtualBox.exe'); return fs.existsSync('C:\\Program Files\\Oracle\\VirtualBox\\VBoxManage.exe') && fs.existsSync('C:\\Program Files\\Oracle\\VirtualBox\\VirtualBox.exe');
} else { } 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 () { active: function () {

View File

@ -1,9 +1,10 @@
var app = require('remote').require('app'); import remote from 'remote';
var fs = require('fs'); var app = remote.require('app');
var util = require('./Util'); import fs from 'fs';
var path = require('path'); import util from './Util';
var bugsnag = require('bugsnag-js'); import path from 'path';
var metrics = require('./MetricsUtil'); import bugsnag from 'bugsnag-js';
import metrics from './MetricsUtil';
var WebUtil = { var WebUtil = {
addWindowSizeSaving: function () { addWindowSizeSaving: function () {

View File

@ -284,7 +284,7 @@
border-bottom-left-radius: @border-radius; border-bottom-left-radius: @border-radius;
justify-content: center; justify-content: center;
text-align: 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 { img {
width: 35px; width: 35px;
height: auto; height: auto;
@ -293,7 +293,7 @@
} }
.card { .card {
position: relative; position: relative;
border: 1px solid darken(@gray-lightest, 5%); border: 1px solid darken(@gray-lightest, 0%);
border-left: 0; border-left: 0;
border-top-right-radius: @border-radius; border-top-right-radius: @border-radius;
border-bottom-right-radius: @border-radius; border-bottom-right-radius: @border-radius;

View File

@ -6,7 +6,7 @@
align-items: flex-start; align-items: flex-start;
justify-content: center; justify-content: center;
.preferences-content { .preferences-content, .about-content {
flex: 1 auto; flex: 1 auto;
margin-top: 45px; margin-top: 45px;
padding: 50px; 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;
}
}
} }

View File

@ -197,6 +197,7 @@
border-radius: 4px; border-radius: 4px;
max-height: 400px; max-height: 400px;
overflow: auto; overflow: auto;
-webkit-user-select: initial;
} }
} }
.setup-actions { .setup-actions {