Skip to content

Commit

Permalink
Merge pull request #28 from crazyfactory/25-improve-cmds
Browse files Browse the repository at this point in the history
Improve commands: alias, chain, reuse/recall
  • Loading branch information
wmathes authored Dec 2, 2017
2 parents 5a5617b + df2f8e7 commit e1c88e5
Show file tree
Hide file tree
Showing 3 changed files with 110 additions and 37 deletions.
50 changes: 42 additions & 8 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,14 @@ Install it globally for quick access

Optionally install it locally, to pin down versions if required.

$ npm i --save @crazyfactory/docker-project-cli
$ npm i --save @crazyfactory/docker-project-cli


## Configuration

Configuration of DOPR can be done either via `package.json` under the `dopr` key or with a provided file defaulting to `docker-project.json`.

Default configuration
**Default configuration:**
```json
{
"file": ["./docker/docker-compose.yml"],
Expand All @@ -39,6 +39,10 @@ Default configuration
"command": "%action% %args%",
"exec": false
},
"pull": {
"command": "%action% %args%",
"exec": false
},
"start": {
"command": "%action% %args%",
"exec": false
Expand All @@ -53,20 +57,50 @@ Default configuration
"command": "%action% %args%",
"user": "node"
},
"npm": "%action% %args%",
"npm": {
"command": "%action% %args%",
"user": "node"
},
"git": "%action% %args%",
"yarn": "%action% %args%"
}
}
```

Notes:
*Notes:*
- This will relay `up`, `down`, `start` and `stop` to `docker-compose -f <file> $params$`
- This will add custom commands like `dopr bash ...`, `dopr composer ...` and `dopr optimize`
- The `node` will be launched with the user `node` by default.
- This will use a different config if NODE_ENV is set to *production* or if dopr is with `--env production`.
- The `"file"` value can be array or string.

**Sample configuration with all usecases:**

```json
{
"actions": {
"multiple-cmd": {
"command": ["echo multiple command as array", "@nested-cmd arg1 arg2"]
},
"nested-cmd": {
"command": ["echo nested command %args%", "@deepnested-cmd --opt1 val1 --opt2 val2"]
},
"deepnested-cmd": {
"command": ["echo deep nested command %args%"]
},
"host-cmd": {
"service": "@host",
"command": "docker-compose version"
}
}
}
```

*Notes:*
- The `"actions".[$key]."command"` can be either array or string.
- The command can be reused or recalled by prefixing it with `@` (see sample above).
- The command that should run in host context will need `"service"` value of `"@host"` (see sample above).

## Usage

### docker-compose shortcuts
Expand All @@ -76,8 +110,8 @@ To start you project in deamon mode run

$ dopr up -d

You can similarly use `down` and `stop`, just like you would with `docker-compose` directly.
You can similarly use `down` and `stop`, just like you would with `docker-compose` directly.

### custom commands

You can add simple custom commands, but we add some by default. They are passed through to the service specified in your dopr configuration.
Expand All @@ -86,7 +120,7 @@ For instance to open a bash session just run

$ dopr bash

You can similarly access `node`, `npm`, `git` and `composer` like so
You can similarly access `node`, `npm`, `git` and `composer` like so

$ dopr npm run my-script

Expand Down
88 changes: 60 additions & 28 deletions src/cli.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#!/usr/bin/env node

const path = require('path');
const {spawn} = require('child_process');
const {execSync, spawnSync} = require('child_process');
const program = require('commander');
const chalk = require('chalk');
const deepAssign = require('deep-assign');
Expand Down Expand Up @@ -68,7 +68,7 @@ if (!packageConfig && !doprConfig) {
if (typeof packageConfig.file === 'string') {
packageConfig.file = [packageConfig.file];
}
if (typeof doprConfig.file === 'string') {
if (doprConfig && typeof doprConfig.file === 'string') {
doprConfig.file = [doprConfig.file];
}

Expand Down Expand Up @@ -114,6 +114,7 @@ if (cliAction.exec && !cliAction.service) {

const dockerComposeFiles = [];

// Validate/sanitize all docker-compose files!
cliAction.file.forEach((file, pos) => {
file = path.resolve(file);

Expand All @@ -130,41 +131,72 @@ cliAction.file.forEach((file, pos) => {
dockerComposeFiles.push('--file', file);
});

// Parse command
const cliCommand = cliAction.command
.replace('%action%', action || '')
.replace('%args%', args.join(' '))
.split(' ');

// Args
const user = cliAction.user ? ['--user', cliAction.user] : [];

const cliArgs = dockerComposeFiles
.concat(cliAction.exec ? ['exec', ...user, cliAction.service] : [])
.concat(cliAction.detached ? ['-d'] : [])
.concat(cliAction.privileged ? ['--privileged'] : [])
.concat(cliAction.index ? ['--index', cliAction.index] : [])
.concat(cliCommand);

if (program.verbose) {
console.log(chalk.gray('ENV: ' + env));
console.log(chalk.gray('CWD: ' + basePath));
console.log(chalk.gray('CMD: docker-compose ' + cliArgs.join(' ')));
// Supporting array command so treat anything else as array as well.
if (typeof cliAction.command === 'string') {
cliAction.command = [cliAction.command];
}

// Fire!
const childProcess = spawn('docker-compose', cliArgs, {
const cliOptions = {
cwd: basePath,
env,
stdio: 'inherit',
shell: true
});
};

childProcess.on('close', code => {
const exitHandler = code => {
if (program.verbose) {
console.log((code > 0 ? chalk.red : chalk.gray)(`command exited with code ${code}`));
}

// Pass through exit code
process.exit(code);
if (code !== 0) {
process.exit(code);
}
};

// Run commands synchronously one after another!
cliAction.command.forEach(command => {
if (program.verbose) {
console.log(chalk.gray('ENV: ' + env));
console.log(chalk.gray('CWD: ' + basePath));
}

// Is it a reference to another action?
if (command[0] === '@') {
const refArgs = dockerProjectArgs.slice(2).concat(command.substr(1));

if (program.verbose) {
console.log(chalk.gray('CMD: dopr ' + refArgs.join(' ')));
}

// Fire!
return exitHandler(spawnSync('dopr ', refArgs, cliOptions).status);
}

// Command is expected to run in host context!
if (cliAction.service === '@host') {
return execSync(command, cliOptions);
}

// Parse command
const cliCommand = command
.replace('%action%', action || '')
.replace('%args%', args.join(' '))
.split(' ');

// Args
const user = cliAction.user ? ['--user', cliAction.user] : [];

const cliArgs = dockerComposeFiles
.concat(cliAction.exec ? ['exec', ...user, cliAction.service] : [])
.concat(cliAction.detached ? ['-d'] : [])
.concat(cliAction.privileged ? ['--privileged'] : [])
.concat(cliAction.index ? ['--index', cliAction.index] : [])
.concat(cliCommand);

if (program.verbose) {
console.log(chalk.gray('CMD: docker-compose ' + cliArgs.join(' ')));
}

// Fire!
exitHandler(spawnSync('docker-compose', cliArgs, cliOptions).status);
});
9 changes: 8 additions & 1 deletion src/internals.spec.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import test from 'ava';

import {preprocessArgs, parseConfigActions, parseConfig} from './internals';
import {preprocessArgs, parseConfigActions, parseConfig, collect} from './internals';

test('preprocessArgs() with empty array', t => {
t.deepEqual(preprocessArgs(['node.exe', 'cli.js']), {
Expand Down Expand Up @@ -145,3 +145,10 @@ test('parseConfig()', t => {

t.deepEqual(parseConfig(config), expected);
});

test('collect()', t => {
const memo = [];

t.deepEqual(collect(1, memo), [1]);
t.deepEqual(collect('a', memo), [1, 'a']);
});

0 comments on commit e1c88e5

Please sign in to comment.