Skip to content

Commit e1c88e5

Browse files
authored
Merge pull request #28 from crazyfactory/25-improve-cmds
Improve commands: alias, chain, reuse/recall
2 parents 5a5617b + df2f8e7 commit e1c88e5

3 files changed

Lines changed: 110 additions & 37 deletions

File tree

readme.md

Lines changed: 42 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,14 @@ Install it globally for quick access
1717

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

20-
$ npm i --save @crazyfactory/docker-project-cli
20+
$ npm i --save @crazyfactory/docker-project-cli
2121

2222

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

27-
Default configuration
27+
**Default configuration:**
2828
```json
2929
{
3030
"file": ["./docker/docker-compose.yml"],
@@ -39,6 +39,10 @@ Default configuration
3939
"command": "%action% %args%",
4040
"exec": false
4141
},
42+
"pull": {
43+
"command": "%action% %args%",
44+
"exec": false
45+
},
4246
"start": {
4347
"command": "%action% %args%",
4448
"exec": false
@@ -53,20 +57,50 @@ Default configuration
5357
"command": "%action% %args%",
5458
"user": "node"
5559
},
56-
"npm": "%action% %args%",
60+
"npm": {
61+
"command": "%action% %args%",
62+
"user": "node"
63+
},
5764
"git": "%action% %args%",
5865
"yarn": "%action% %args%"
5966
}
6067
}
6168
```
6269

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

77+
**Sample configuration with all usecases:**
78+
79+
```json
80+
{
81+
"actions": {
82+
"multiple-cmd": {
83+
"command": ["echo multiple command as array", "@nested-cmd arg1 arg2"]
84+
},
85+
"nested-cmd": {
86+
"command": ["echo nested command %args%", "@deepnested-cmd --opt1 val1 --opt2 val2"]
87+
},
88+
"deepnested-cmd": {
89+
"command": ["echo deep nested command %args%"]
90+
},
91+
"host-cmd": {
92+
"service": "@host",
93+
"command": "docker-compose version"
94+
}
95+
}
96+
}
97+
```
98+
99+
*Notes:*
100+
- The `"actions".[$key]."command"` can be either array or string.
101+
- The command can be reused or recalled by prefixing it with `@` (see sample above).
102+
- The command that should run in host context will need `"service"` value of `"@host"` (see sample above).
103+
70104
## Usage
71105

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

77111
$ dopr up -d
78112

79-
You can similarly use `down` and `stop`, just like you would with `docker-compose` directly.
80-
113+
You can similarly use `down` and `stop`, just like you would with `docker-compose` directly.
114+
81115
### custom commands
82116

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

87121
$ dopr bash
88122

89-
You can similarly access `node`, `npm`, `git` and `composer` like so
123+
You can similarly access `node`, `npm`, `git` and `composer` like so
90124

91125
$ dopr npm run my-script
92126

src/cli.js

Lines changed: 60 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#!/usr/bin/env node
22

33
const path = require('path');
4-
const {spawn} = require('child_process');
4+
const {execSync, spawnSync} = require('child_process');
55
const program = require('commander');
66
const chalk = require('chalk');
77
const deepAssign = require('deep-assign');
@@ -68,7 +68,7 @@ if (!packageConfig && !doprConfig) {
6868
if (typeof packageConfig.file === 'string') {
6969
packageConfig.file = [packageConfig.file];
7070
}
71-
if (typeof doprConfig.file === 'string') {
71+
if (doprConfig && typeof doprConfig.file === 'string') {
7272
doprConfig.file = [doprConfig.file];
7373
}
7474

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

115115
const dockerComposeFiles = [];
116116

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

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

133-
// Parse command
134-
const cliCommand = cliAction.command
135-
.replace('%action%', action || '')
136-
.replace('%args%', args.join(' '))
137-
.split(' ');
138-
139-
// Args
140-
const user = cliAction.user ? ['--user', cliAction.user] : [];
141-
142-
const cliArgs = dockerComposeFiles
143-
.concat(cliAction.exec ? ['exec', ...user, cliAction.service] : [])
144-
.concat(cliAction.detached ? ['-d'] : [])
145-
.concat(cliAction.privileged ? ['--privileged'] : [])
146-
.concat(cliAction.index ? ['--index', cliAction.index] : [])
147-
.concat(cliCommand);
148-
149-
if (program.verbose) {
150-
console.log(chalk.gray('ENV: ' + env));
151-
console.log(chalk.gray('CWD: ' + basePath));
152-
console.log(chalk.gray('CMD: docker-compose ' + cliArgs.join(' ')));
134+
// Supporting array command so treat anything else as array as well.
135+
if (typeof cliAction.command === 'string') {
136+
cliAction.command = [cliAction.command];
153137
}
154138

155-
// Fire!
156-
const childProcess = spawn('docker-compose', cliArgs, {
139+
const cliOptions = {
157140
cwd: basePath,
158-
env,
159141
stdio: 'inherit',
160142
shell: true
161-
});
143+
};
162144

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

168150
// Pass through exit code
169-
process.exit(code);
151+
if (code !== 0) {
152+
process.exit(code);
153+
}
154+
};
155+
156+
// Run commands synchronously one after another!
157+
cliAction.command.forEach(command => {
158+
if (program.verbose) {
159+
console.log(chalk.gray('ENV: ' + env));
160+
console.log(chalk.gray('CWD: ' + basePath));
161+
}
162+
163+
// Is it a reference to another action?
164+
if (command[0] === '@') {
165+
const refArgs = dockerProjectArgs.slice(2).concat(command.substr(1));
166+
167+
if (program.verbose) {
168+
console.log(chalk.gray('CMD: dopr ' + refArgs.join(' ')));
169+
}
170+
171+
// Fire!
172+
return exitHandler(spawnSync('dopr ', refArgs, cliOptions).status);
173+
}
174+
175+
// Command is expected to run in host context!
176+
if (cliAction.service === '@host') {
177+
return execSync(command, cliOptions);
178+
}
179+
180+
// Parse command
181+
const cliCommand = command
182+
.replace('%action%', action || '')
183+
.replace('%args%', args.join(' '))
184+
.split(' ');
185+
186+
// Args
187+
const user = cliAction.user ? ['--user', cliAction.user] : [];
188+
189+
const cliArgs = dockerComposeFiles
190+
.concat(cliAction.exec ? ['exec', ...user, cliAction.service] : [])
191+
.concat(cliAction.detached ? ['-d'] : [])
192+
.concat(cliAction.privileged ? ['--privileged'] : [])
193+
.concat(cliAction.index ? ['--index', cliAction.index] : [])
194+
.concat(cliCommand);
195+
196+
if (program.verbose) {
197+
console.log(chalk.gray('CMD: docker-compose ' + cliArgs.join(' ')));
198+
}
199+
200+
// Fire!
201+
exitHandler(spawnSync('docker-compose', cliArgs, cliOptions).status);
170202
});

src/internals.spec.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import test from 'ava';
22

3-
import {preprocessArgs, parseConfigActions, parseConfig} from './internals';
3+
import {preprocessArgs, parseConfigActions, parseConfig, collect} from './internals';
44

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

146146
t.deepEqual(parseConfig(config), expected);
147147
});
148+
149+
test('collect()', t => {
150+
const memo = [];
151+
152+
t.deepEqual(collect(1, memo), [1]);
153+
t.deepEqual(collect('a', memo), [1, 'a']);
154+
});

0 commit comments

Comments
 (0)