1 | #!/usr/bin/env node
|
2 |
|
3 | const path = require('path');
|
4 | const {spawn} = require('child_process');
|
5 | const program = require('commander');
|
6 | const chalk = require('chalk');
|
7 | const deepAssign = require('deep-assign');
|
8 | const updateNotifier = require('update-notifier');
|
9 |
|
10 | const pkg = require('../package.json');
|
11 | const {preprocessArgs, fileExists, parseConfig} = require('./internals');
|
12 |
|
13 | const defaultConfig = parseConfig(require('./defaultConfig.json'));
|
14 |
|
15 |
|
16 | if (pkg && pkg.version) {
|
17 | updateNotifier({pkg}).notify();
|
18 | }
|
19 |
|
20 |
|
21 | const {dockerProjectArgs, args, action} = preprocessArgs(process.argv);
|
22 |
|
23 |
|
24 | program
|
25 | .version(pkg.version || 'dev')
|
26 | .option('-e, --env <name>', 'Overrides the selected environment [default: development]')
|
27 | .option('-p, --path <path>', 'Path to you projects root folder, [default: CWD]')
|
28 | .option('-s, --service <name>', 'Overrides the targeted docker service')
|
29 | .option('-f, --file <filepath>', 'Overrides the targeted docker-compose file')
|
30 | .option('-u, --user <user>', 'Run the command as this user')
|
31 | .option('-i, --index <index>', 'Index of the service is there are multiple [default: 1]')
|
32 | .option('-p, --privileged', 'Give extended privileges to the executed command process')
|
33 | .option('-v, --verbose', 'Adds additional logging')
|
34 | .option('-d, --detached', 'Detached mode: Run command in the background.')
|
35 | .parse(dockerProjectArgs);
|
36 |
|
37 |
|
38 | const basePath = program.path || process.cwd();
|
39 | const env = program.env || process.env.NODE_ENV || 'development';
|
40 |
|
41 |
|
42 | const packageFile = path.resolve(basePath + '/package.json');
|
43 | const doprFile = path.resolve(basePath + '/docker-project.json');
|
44 | const packageFileExists = fileExists(packageFile);
|
45 | const doprFileExists = fileExists(doprFile);
|
46 | if (!packageFileExists && !doprFileExists) {
|
47 | console.error(chalk.red('neither package.json nor docker-project.json found in: ' + basePath));
|
48 | console.log(chalk.gray('Run this tool from your projects root directory or supply a --path.'));
|
49 | process.exit(1);
|
50 | }
|
51 |
|
52 |
|
53 | const packageConfig = packageFileExists
|
54 | ? parseConfig(require(packageFile).dopr)
|
55 | : null;
|
56 | const doprConfigRaw = doprFileExists
|
57 | ? require(doprFile)
|
58 | : null;
|
59 | const doprConfig = parseConfig(doprConfigRaw && (doprConfigRaw.dopr || doprConfigRaw));
|
60 | if (!packageConfig && !doprConfig) {
|
61 | console.log(chalk.red('dopr is not configured.'));
|
62 | process.exit(1);
|
63 | }
|
64 |
|
65 |
|
66 | const mergedConfig = deepAssign({}, defaultConfig, packageConfig, doprConfig);
|
67 | const environmentConfig = (mergedConfig.environments && mergedConfig.environments[env]) || {};
|
68 | const config = deepAssign({}, mergedConfig, environmentConfig);
|
69 |
|
70 |
|
71 | const defaultAction = {
|
72 | file: config.file || null,
|
73 | service: config.service || null,
|
74 | command: config.command || '%action% %args%',
|
75 | user: config.user || null,
|
76 | privileged: config.privileged || false,
|
77 | exec: true,
|
78 | detached: false,
|
79 | index: null
|
80 | };
|
81 |
|
82 |
|
83 | const programAction = {};
|
84 | ['detached', 'exec', 'file', 'index', 'privileged', 'service', 'user'].forEach(key => {
|
85 | if (program[key] !== undefined) {
|
86 | programAction[key] = program[key];
|
87 | }
|
88 | });
|
89 |
|
90 |
|
91 | const configAction = (config.actions && config.actions[action]) || {};
|
92 | const cliAction = Object.assign({}, defaultAction, configAction, programAction);
|
93 | const dockerComposeFile = path.resolve(cliAction.file);
|
94 |
|
95 |
|
96 | if (!dockerComposeFile) {
|
97 | console.log(chalk.red('\'file\' not configured.'));
|
98 | process.exit(1);
|
99 | }
|
100 | if (!fileExists(dockerComposeFile)) {
|
101 | console.log(chalk.red('docker-compose file not found at: ' + dockerComposeFile));
|
102 | process.exit(1);
|
103 | }
|
104 | if (cliAction.exec && !cliAction.service) {
|
105 | console.log(chalk.red('\'service\' not configured.'));
|
106 | process.exit(1);
|
107 | }
|
108 |
|
109 |
|
110 | const cliCommand = cliAction.command
|
111 | .replace('%action%', action || '')
|
112 | .replace('%args%', args.join(' '))
|
113 | .split(' ');
|
114 |
|
115 |
|
116 | const user = cliAction.user ? ['--user', cliAction.user] : [];
|
117 |
|
118 | const cliArgs = ['-f', dockerComposeFile]
|
119 | .concat(cliAction.exec ? ['exec', ...user, cliAction.service] : [])
|
120 | .concat(cliAction.detached ? ['-d'] : [])
|
121 | .concat(cliAction.privileged ? ['--privileged'] : [])
|
122 | .concat(cliAction.index ? ['--index', cliAction.index] : [])
|
123 | .concat(cliCommand);
|
124 |
|
125 | if (program.verbose) {
|
126 | console.log(chalk.gray('ENV: ' + env));
|
127 | console.log(chalk.gray('CWD: ' + basePath));
|
128 | console.log(chalk.gray('CMD: docker-compose ' + cliArgs.join(' ')));
|
129 | }
|
130 |
|
131 |
|
132 | const childProcess = spawn('docker-compose', cliArgs, {
|
133 | cwd: basePath,
|
134 | env,
|
135 | stdio: 'inherit',
|
136 | shell: true
|
137 | });
|
138 |
|
139 | childProcess.on('close', code => {
|
140 | if (program.verbose) {
|
141 | console.log((code > 0 ? chalk.red : chalk.gray)(`command exited with code ${code}`));
|
142 | }
|
143 |
|
144 |
|
145 | process.exit(code);
|
146 | });
|