1 | var fs = require('fs');
|
2 | var path = require('path');
|
3 | var util = require('util');
|
4 |
|
5 | var xtend = require('xtend');
|
6 | var camelize = require('camelize');
|
7 | var optimist = require('optimist');
|
8 |
|
9 | var OPTIONS = {
|
10 | config: {
|
11 | describe: 'Specify a JSON config file to use as defaults',
|
12 | type: 'string'
|
13 | },
|
14 | update: {
|
15 | describe: 'The update mode: dummy, query or document (see documentation for more details)',
|
16 | type: 'string',
|
17 | default: 'document'
|
18 | },
|
19 | 'dry-run': {
|
20 | describe: 'Run patch without modifying data (same as --update dummy, overwrites --update option)',
|
21 | type: 'string'
|
22 | },
|
23 | db: {
|
24 | describe: 'Connection string for application database',
|
25 | type: 'string'
|
26 | },
|
27 | 'log-db': {
|
28 | describe: 'Connection string for log database',
|
29 | type: 'string'
|
30 | },
|
31 | parallel: {
|
32 | describe: 'Specify a parallelism level for the patch. Defaults to 1',
|
33 | type: 'string'
|
34 | },
|
35 | output: {
|
36 | describe: 'Output progress while runing the patch',
|
37 | type: 'boolean',
|
38 | default: true
|
39 | },
|
40 | force: {
|
41 | describe: 'Force a run without providing a log db',
|
42 | type: 'boolean'
|
43 | },
|
44 | version: {
|
45 | describe: 'Prints version',
|
46 | type: 'boolean'
|
47 | }
|
48 | };
|
49 |
|
50 | var OPTION_KEYS = Object.keys(OPTIONS).reduce(function(acc, key) {
|
51 | acc[camelize(key)] = true;
|
52 | return acc;
|
53 | }, {});
|
54 |
|
55 | var cmd = optimist()
|
56 | .usage('Usage: $0 [patch] [options]')
|
57 | .options(OPTIONS);
|
58 |
|
59 | var error = function(name) {
|
60 | var args = Array.prototype.slice.call(arguments, 1);
|
61 | return { option: name, message: util.format.apply(null, args) };
|
62 | };
|
63 |
|
64 | var version = function(argv) {
|
65 | var v = require('../../package').version;
|
66 | return util.format('mongopatch v%s', v);
|
67 | };
|
68 |
|
69 | var validate = function(patch, options) {
|
70 | if(!patch) {
|
71 | return error('patch', 'Patch path required');
|
72 | }
|
73 | if(!fs.existsSync(patch)) {
|
74 | return error('patch', 'Cannot find patch with path "%s"', patch);
|
75 | }
|
76 |
|
77 | var invalidOptions = Object.keys(options).filter(function(arg) {
|
78 | return !OPTION_KEYS.hasOwnProperty(arg);
|
79 | });
|
80 |
|
81 | if(invalidOptions.length) {
|
82 | var option = invalidOptions.shift();
|
83 | return error(option, 'Uknown option: %s', option);
|
84 | }
|
85 |
|
86 | if(!options.db) {
|
87 | return error('db', '--db option required');
|
88 | }
|
89 | if(!options.update) {
|
90 | return error('update', '--update option required');
|
91 | }
|
92 |
|
93 | if(['dummy', 'query', 'document'].indexOf(options.update) < 0) {
|
94 | return error('update', '--update option invalid');
|
95 | }
|
96 |
|
97 | if(options.update !== 'dummy' && !options.logDb && !options.force) {
|
98 | return error('logDb', '--log-db option required');
|
99 | }
|
100 | };
|
101 |
|
102 | var parse = function(argv) {
|
103 | var options = cmd.parse(argv);
|
104 | var patch = options._[0];
|
105 |
|
106 | patch = patch && path.resolve(process.cwd(), patch);
|
107 |
|
108 | delete options._;
|
109 | delete options.$0;
|
110 |
|
111 | options = camelize(options);
|
112 |
|
113 | options.dryRun = ('dryRun' in options) || options.update === 'dummy';
|
114 | if('parallel' in options) {
|
115 | options.parallel = parseInt(options.parallel, 10) || 10;
|
116 | }
|
117 | if(options.dryRun) {
|
118 | options.update = 'dummy';
|
119 | }
|
120 |
|
121 | var conf = options.config ? JSON.parse(fs.readFileSync(options.config, 'utf-8')) : {};
|
122 | options = xtend(camelize(conf), options);
|
123 |
|
124 | return {
|
125 | patch: patch,
|
126 | options: options,
|
127 | error: validate(patch, options),
|
128 | help: cmd.help,
|
129 | version: version
|
130 | };
|
131 | };
|
132 |
|
133 | module.exports = function(argv) {
|
134 | argv = argv || process.argv.slice(2);
|
135 | return parse(argv);
|
136 | };
|