UNPKG

4.98 kBJavaScriptView Raw
1#!/usr/bin/env node
2
3const path = require('path');
4const {spawn} = require('child_process');
5const program = require('commander');
6const chalk = require('chalk');
7const deepAssign = require('deep-assign');
8const updateNotifier = require('update-notifier');
9
10const pkg = require('../package.json');
11const {preprocessArgs, fileExists, parseConfig} = require('./internals');
12
13const defaultConfig = parseConfig(require('./defaultConfig.json'));
14
15// Notify for updates
16if (pkg && pkg.version) {
17 updateNotifier({pkg}).notify();
18}
19
20// Preprocess args
21const {dockerProjectArgs, args, action} = preprocessArgs(process.argv);
22
23// Define commander
24program
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// Set defaults
38const basePath = program.path || process.cwd();
39const env = program.env || process.env.NODE_ENV || 'development';
40
41// Try to find package json
42const packageFile = path.resolve(basePath + '/package.json');
43const doprFile = path.resolve(basePath + '/docker-project.json');
44const packageFileExists = fileExists(packageFile);
45const doprFileExists = fileExists(doprFile);
46if (!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// Ensure local configuration exists
53const packageConfig = packageFileExists
54 ? parseConfig(require(packageFile).dopr)
55 : null;
56const doprConfigRaw = doprFileExists
57 ? require(doprFile)
58 : null;
59const doprConfig = parseConfig(doprConfigRaw && (doprConfigRaw.dopr || doprConfigRaw));
60if (!packageConfig && !doprConfig) {
61 console.log(chalk.red('dopr is not configured.'));
62 process.exit(1);
63}
64
65// Construct configuration
66const mergedConfig = deepAssign({}, defaultConfig, packageConfig, doprConfig);
67const environmentConfig = (mergedConfig.environments && mergedConfig.environments[env]) || {};
68const config = deepAssign({}, mergedConfig, environmentConfig);
69
70// Default action
71const 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// Program action
83const 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// Final action
91const configAction = (config.actions && config.actions[action]) || {};
92const cliAction = Object.assign({}, defaultAction, configAction, programAction);
93const dockerComposeFile = path.resolve(cliAction.file);
94
95// Validation
96if (!dockerComposeFile) {
97 console.log(chalk.red('\'file\' not configured.'));
98 process.exit(1);
99}
100if (!fileExists(dockerComposeFile)) {
101 console.log(chalk.red('docker-compose file not found at: ' + dockerComposeFile));
102 process.exit(1);
103}
104if (cliAction.exec && !cliAction.service) {
105 console.log(chalk.red('\'service\' not configured.'));
106 process.exit(1);
107}
108
109// Parse command
110const cliCommand = cliAction.command
111 .replace('%action%', action || '')
112 .replace('%args%', args.join(' '))
113 .split(' ');
114
115// Args
116const user = cliAction.user ? ['--user', cliAction.user] : [];
117
118const 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
125if (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// Fire!
132const childProcess = spawn('docker-compose', cliArgs, {
133 cwd: basePath,
134 env,
135 stdio: 'inherit',
136 shell: true
137});
138
139childProcess.on('close', code => {
140 if (program.verbose) {
141 console.log((code > 0 ? chalk.red : chalk.gray)(`command exited with code ${code}`));
142 }
143
144 // Pass through exit code
145 process.exit(code);
146});