@verdnatura/myt 1.5.19 → 1.5.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/.dockerignore +2 -2
  2. package/LICENSE +17 -17
  3. package/README.md +278 -278
  4. package/assets/db.ini +5 -5
  5. package/assets/myt.default.yml +11 -11
  6. package/assets/structure.sql +24 -24
  7. package/cli.js +4 -4
  8. package/exporters/event.ejs +18 -18
  9. package/exporters/event.js +31 -31
  10. package/exporters/event.sql +19 -19
  11. package/exporters/function.ejs +17 -17
  12. package/exporters/function.js +27 -27
  13. package/exporters/function.sql +16 -16
  14. package/exporters/procedure.ejs +11 -11
  15. package/exporters/procedure.js +23 -23
  16. package/exporters/procedure.sql +14 -14
  17. package/exporters/trigger.ejs +4 -4
  18. package/exporters/trigger.js +10 -10
  19. package/exporters/trigger.sql +13 -13
  20. package/exporters/view.ejs +7 -7
  21. package/exporters/view.js +29 -29
  22. package/exporters/view.sql +12 -12
  23. package/lib/command.js +13 -13
  24. package/lib/conn.js +141 -141
  25. package/lib/docker.js +133 -133
  26. package/lib/dumper.js +83 -83
  27. package/lib/exporter-engine.js +112 -112
  28. package/lib/exporter.js +86 -86
  29. package/lib/repo.js +29 -29
  30. package/lib/server.js +71 -71
  31. package/lib/util.js +7 -7
  32. package/myt-clean.js +58 -58
  33. package/myt-create.js +74 -74
  34. package/myt-dump.js +75 -75
  35. package/myt-fixtures.js +29 -29
  36. package/myt-init.js +36 -36
  37. package/myt-pull.js +183 -183
  38. package/myt-push.js +568 -568
  39. package/myt-run.js +177 -177
  40. package/myt-start.js +54 -54
  41. package/myt-version.js +218 -218
  42. package/myt.js +361 -361
  43. package/package.json +41 -41
  44. package/server/Dockerfile.base +1 -1
  45. package/server/Dockerfile.client +14 -14
  46. package/server/Dockerfile.dump +16 -16
  47. package/server/Dockerfile.server +25 -25
  48. package/server/docker-dump.sh +4 -4
  49. package/server/docker-fixtures.sh +4 -4
  50. package/server/docker-import.sh +6 -6
  51. package/server/docker-init.sh +20 -20
  52. package/server/docker-start.sh +10 -10
  53. package/server/docker.cnf +9 -9
  54. package/template/dump/Dockerfile.base +1 -1
  55. package/template/dump/afterDump.sql +1 -1
  56. package/template/dump/beforeDump.sql +1 -1
  57. package/template/dump/fixtures.sql +1 -1
  58. package/template/myt.config.yml +11 -11
  59. package/template/package.json +13 -13
  60. package/template/remotes/docker.ini +4 -4
  61. package/template/remotes/local.ini +5 -5
  62. package/template/routines/README.md +1 -1
  63. package/template/versions/00001-firstVersion/00-firstScript.sql +1 -1
  64. package/template/versions/README.md +1 -1
package/myt.js CHANGED
@@ -1,361 +1,361 @@
1
- #!/usr/bin/env node
2
-
3
- require('require-yaml');
4
- require('colors');
5
- const getopts = require('getopts');
6
- const packageJson = require('./package.json');
7
- const fs = require('fs-extra');
8
- const ini = require('ini');
9
- const path = require('path');
10
- const mysql = require('mysql2/promise');
11
- const nodegit = require('nodegit');
12
- const camelToSnake = require('./lib/util').camelToSnake;
13
-
14
- class Myt {
15
- static usage = {
16
- description: 'Utility for database versioning',
17
- params: {
18
- remote: 'Name of remote to use',
19
- workspace: 'The base directory of the project',
20
- debug: 'Wether to enable debug mode',
21
- version: 'Display the version number and exit',
22
- help: 'Display this help message'
23
- }
24
- };
25
-
26
- static opts = {
27
- alias: {
28
- remote: 'r',
29
- workspace: 'w',
30
- debug: 'd',
31
- version: 'v',
32
- help: 'h'
33
- },
34
- boolean: [
35
- 'debug',
36
- 'version',
37
- 'help'
38
- ]
39
- };
40
-
41
- async run(Command) {
42
- console.log(
43
- 'Myt'.green,
44
- `v${packageJson.version}`.magenta
45
- );
46
- this.packageJson = packageJson;
47
-
48
- let baseOpts = this.constructor.opts;
49
- baseOpts.default = Object.assign(baseOpts.default || {}, {
50
- workspace: process.cwd()
51
- });
52
- const opts = this.getopts(baseOpts);
53
-
54
- if (opts.debug) {
55
- console.warn('Debug mode enabled.'.yellow);
56
- console.log('Global options:'.magenta, opts);
57
- }
58
-
59
- if (opts.version)
60
- process.exit(0);
61
-
62
- try {
63
- const commandName = opts._[0];
64
- if (!Command && commandName) {
65
- if (!/^[a-z]+$/.test(commandName))
66
- throw new Error (`Invalid command name '${commandName}'`);
67
-
68
- const commandFile = path.join(__dirname, `myt-${commandName}.js`);
69
-
70
- if (!await fs.pathExists(commandFile))
71
- throw new Error (`Unknown command '${commandName}'`);
72
- Command = require(commandFile);
73
- }
74
-
75
- if (!Command) {
76
- this.showHelp(baseOpts, this.constructor.usage);
77
- process.exit(0);
78
- }
79
-
80
- const allOpts = Object.assign({}, baseOpts);
81
-
82
- if (Command.opts)
83
- for (const key in Command.opts) {
84
- const baseValue = baseOpts[key];
85
- const cmdValue = Command.opts[key];
86
- if (Array.isArray(baseValue))
87
- allOpts[key] = baseValue.concat(cmdValue);
88
- else if (typeof baseValue == 'object')
89
- allOpts[key] = Object.assign({}, baseValue, cmdValue);
90
- else
91
- allOpts[key] = cmdValue;
92
- }
93
-
94
- const commandOpts = this.getopts(allOpts);
95
- if (opts.debug)
96
- console.log('Command options:'.magenta, commandOpts);
97
- Object.assign(opts, commandOpts);
98
-
99
- const operandToOpt = Command.usage.operand;
100
- if (opts._.length >= 2 && operandToOpt)
101
- opts[operandToOpt] = opts._[1];
102
-
103
- if (opts.debug)
104
- console.log('Final options:'.magenta, opts);
105
-
106
- if (opts.help) {
107
- this.showHelp(Command.opts, Command.usage, commandName);
108
- process.exit(0);
109
- }
110
-
111
- // Check version
112
-
113
- let depVersion;
114
- const versionRegex = /^[^~]?([0-9]+)\.([0-9]+).([0-9]+)$/;
115
- const wsPackageFile = path.join(opts.workspace, 'package.json');
116
-
117
- if (await fs.pathExists(wsPackageFile)) {
118
- const wsPackageJson = require(wsPackageFile);
119
- try {
120
- depVersion = wsPackageJson
121
- .dependencies[packageJson.name]
122
- .match(versionRegex);
123
- } catch (e) {}
124
- }
125
-
126
- if (depVersion) {
127
- const myVersion = packageJson.version.match(versionRegex);
128
-
129
- const isSameVersion =
130
- depVersion[1] === myVersion[1] &&
131
- depVersion[2] === myVersion[2];
132
- if (!isSameVersion)
133
- throw new Error(`Myt version differs a lot from package.json, please run 'npm i' first to install the proper version.`);
134
-
135
- const isSameMinor = depVersion[3] === myVersion[3];
136
- if (!isSameMinor)
137
- console.warn(`Warning! Myt minor version differs from package.json, maybe you shoud run 'npm i' to install the proper version.`.yellow);
138
- }
139
-
140
- // Load method
141
-
142
- parameter('Workspace:', opts.workspace);
143
- parameter('Remote:', opts.remote || 'local');
144
-
145
- await this.load(opts);
146
- await this.runCommand(Command, opts);
147
- await this.unload();
148
- } catch (err) {
149
- if (err.name == 'Error' && !opts.debug) {
150
- console.error('Error:'.gray, err.message.red);
151
- console.log(`You can get more details about the error by passing the 'debug' option.`.yellow);
152
- } else
153
- console.log(err.stack.magenta);
154
-
155
- process.exit(1);
156
- }
157
-
158
- function parameter(parameter, value) {
159
- console.log(parameter.gray, (value || 'null').blue);
160
- }
161
-
162
- process.exit();
163
- }
164
-
165
- async runCommand(Command, opts) {
166
- const command = new Command(this, opts);
167
- return await command.run(this, opts);
168
- }
169
-
170
- async load(opts) {
171
- // Configuration file
172
-
173
- const config = require(`${__dirname}/assets/myt.default.yml`);
174
-
175
- const configFile = 'myt.config.yml';
176
- const configPath = path.join(opts.workspace, configFile);
177
- if (await fs.pathExists(configPath))
178
- Object.assign(config, require(configPath));
179
-
180
- Object.assign(opts, config);
181
- opts.configFile = configFile;
182
-
183
- if (!opts.mytDir)
184
- opts.mytDir = path.join(opts.workspace, opts.subdir || '');
185
-
186
- opts.routinesDir = path.join(opts.mytDir, 'routines');
187
- opts.versionsDir = path.join(opts.mytDir, 'versions');
188
- opts.dumpDir = path.join(opts.mytDir, 'dump');
189
-
190
- // Database configuration
191
-
192
- let iniDir = path.join(__dirname, 'assets');
193
- let iniFile = 'db.ini';
194
-
195
- if (opts.remote) {
196
- iniDir = `${opts.mytDir}/remotes`;
197
- iniFile = `${opts.remote}.ini`;
198
- }
199
- const iniPath = path.join(iniDir, iniFile);
200
-
201
- if (!await fs.pathExists(iniPath))
202
- throw new Error(`Database config file not found: ${iniPath}`);
203
-
204
- let dbConfig;
205
- try {
206
- const iniData = ini.parse(await fs.readFile(iniPath, 'utf8')).client;
207
- const iniConfig = {};
208
- for (const key in iniData) {
209
- const value = iniData[key];
210
- const newKey = key.replace(/-/g, '_');
211
- iniConfig[newKey] = value !== undefined ? value : true;
212
- }
213
- dbConfig = {
214
- host: iniConfig.host,
215
- port: iniConfig.port,
216
- user: iniConfig.user,
217
- password: iniConfig.password,
218
- multipleStatements: true
219
- };
220
- if (iniConfig.enable_cleartext_plugin) {
221
- dbConfig.authPlugins = {
222
- mysql_clear_password() {
223
- return () => iniConfig.password + '\0';
224
- }
225
- };
226
- }
227
- if (iniConfig.ssl_ca) {
228
- dbConfig.ssl = {
229
- ca: await fs.readFile(`${opts.mytDir}/${iniConfig.ssl_ca}`),
230
- rejectUnauthorized: iniConfig.ssl_verify_server_cert != undefined
231
- }
232
- }
233
- if (iniConfig.socket) {
234
- dbConfig.socketPath = iniConfig.socket;
235
- }
236
- } catch(err) {
237
- const newErr = Error(`Cannot process the ini file, check that the syntax is correct: ${iniPath}`);
238
- newErr.stack += `\nCaused by: ${err.stack}`;
239
- throw newErr;
240
- }
241
-
242
- Object.assign(opts, {
243
- iniFile,
244
- dbConfig
245
- });
246
- this.opts = opts;
247
- }
248
-
249
- async unload() {
250
- if (this.conn)
251
- await this.conn.end();
252
- }
253
-
254
- getopts(opts) {
255
- const argv = process.argv.slice(2);
256
- const values = getopts(argv, opts);
257
- const cleanValues = {};
258
- for (const opt in values)
259
- if (opt.length > 1 || opt == '_')
260
- cleanValues[opt] = values[opt];
261
- return cleanValues;
262
- }
263
-
264
- async dbConnect() {
265
- const {opts} = this;
266
-
267
- if (!this.conn) {
268
- const conn = this.conn = await this.createConnection();
269
-
270
- const [[schema]] = await conn.query(
271
- `SHOW DATABASES LIKE ?`, [opts.versionSchema]
272
- );
273
-
274
- if (!schema)
275
- await conn.query(`CREATE DATABASE ??`, [opts.versionSchema]);
276
- await conn.query(`USE ??`, [opts.versionSchema]);
277
-
278
- const [[res]] = await conn.query(
279
- `SELECT COUNT(*) > 0 tableExists
280
- FROM information_schema.tables
281
- WHERE TABLE_SCHEMA = ?
282
- AND TABLE_NAME = 'version'`,
283
- [opts.versionSchema]
284
- );
285
-
286
- if (!res.tableExists) {
287
- const structure = await fs.readFile(
288
- `${__dirname}/assets/structure.sql`, 'utf8');
289
- await conn.query(structure);
290
- }
291
- }
292
-
293
- return this.conn;
294
- }
295
-
296
- async createConnection() {
297
- return await mysql.createConnection(this.opts.dbConfig);
298
- }
299
-
300
- async fetchDbVersion() {
301
- const [[version]] = await this.conn.query(
302
- `SELECT number, gitCommit
303
- FROM version WHERE code = ?`,
304
- [this.opts.code]
305
- );
306
- return version;
307
- }
308
-
309
- parseVersionDir(versionDir) {
310
- const match = versionDir.match(/^([0-9]+)-([a-zA-Z0-9]+)?$/);
311
- if (!match) return null;
312
- return {
313
- number: match[1],
314
- name: match[2]
315
- };
316
- }
317
-
318
- async openRepo() {
319
- const {opts} = this;
320
-
321
- if (!await fs.pathExists(`${opts.workspace}/.git`))
322
- throw new Error ('Git not initialized');
323
-
324
- return await nodegit.Repository.open(opts.workspace);
325
- }
326
-
327
- showHelp(opts, usage, command) {
328
- const prefix = `${'Usage:'.gray} [npx] myt`;
329
-
330
- if (command) {
331
- let log = [prefix, command.blue];
332
- if (usage.operand) log.push(`[<${usage.operand}>]`);
333
- if (opts) log.push('[<options>]');
334
- console.log(log.join(' '))
335
- } else
336
- console.log(`${prefix} [<options>] ${'<command>'.blue} [<args>]`);
337
-
338
- if (usage.description)
339
- console.log(`${'Description:'.gray} ${usage.description}`);
340
-
341
- if (opts && opts.alias) {
342
- const alias = opts.alias;
343
- const boolean = opts.boolean || [];
344
-
345
- console.log('Options:'.gray);
346
- for (const opt in alias) {
347
- const paramDescription = usage.params[opt] || '';
348
- let longOpt = opt;
349
- if (boolean.indexOf(longOpt) === -1)
350
- longOpt += ` <string>`;
351
- longOpt = camelToSnake(longOpt).padEnd(22, ' ')
352
- console.log(` -${alias[opt]}, --${longOpt} ${paramDescription}`);
353
- }
354
- }
355
- }
356
- }
357
-
358
- module.exports = Myt;
359
-
360
- if (require.main === module)
361
- new Myt().run();
1
+ #!/usr/bin/env node
2
+
3
+ require('require-yaml');
4
+ require('colors');
5
+ const getopts = require('getopts');
6
+ const packageJson = require('./package.json');
7
+ const fs = require('fs-extra');
8
+ const ini = require('ini');
9
+ const path = require('path');
10
+ const mysql = require('mysql2/promise');
11
+ const nodegit = require('nodegit');
12
+ const camelToSnake = require('./lib/util').camelToSnake;
13
+
14
+ class Myt {
15
+ static usage = {
16
+ description: 'Utility for database versioning',
17
+ params: {
18
+ remote: 'Name of remote to use',
19
+ workspace: 'The base directory of the project',
20
+ debug: 'Wether to enable debug mode',
21
+ version: 'Display the version number and exit',
22
+ help: 'Display this help message'
23
+ }
24
+ };
25
+
26
+ static opts = {
27
+ alias: {
28
+ remote: 'r',
29
+ workspace: 'w',
30
+ debug: 'd',
31
+ version: 'v',
32
+ help: 'h'
33
+ },
34
+ boolean: [
35
+ 'debug',
36
+ 'version',
37
+ 'help'
38
+ ]
39
+ };
40
+
41
+ async run(Command) {
42
+ console.log(
43
+ 'Myt'.green,
44
+ `v${packageJson.version}`.magenta
45
+ );
46
+ this.packageJson = packageJson;
47
+
48
+ let baseOpts = this.constructor.opts;
49
+ baseOpts.default = Object.assign(baseOpts.default || {}, {
50
+ workspace: process.cwd()
51
+ });
52
+ const opts = this.getopts(baseOpts);
53
+
54
+ if (opts.debug) {
55
+ console.warn('Debug mode enabled.'.yellow);
56
+ console.log('Global options:'.magenta, opts);
57
+ }
58
+
59
+ if (opts.version)
60
+ process.exit(0);
61
+
62
+ try {
63
+ const commandName = opts._[0];
64
+ if (!Command && commandName) {
65
+ if (!/^[a-z]+$/.test(commandName))
66
+ throw new Error (`Invalid command name '${commandName}'`);
67
+
68
+ const commandFile = path.join(__dirname, `myt-${commandName}.js`);
69
+
70
+ if (!await fs.pathExists(commandFile))
71
+ throw new Error (`Unknown command '${commandName}'`);
72
+ Command = require(commandFile);
73
+ }
74
+
75
+ if (!Command) {
76
+ this.showHelp(baseOpts, this.constructor.usage);
77
+ process.exit(0);
78
+ }
79
+
80
+ const allOpts = Object.assign({}, baseOpts);
81
+
82
+ if (Command.opts)
83
+ for (const key in Command.opts) {
84
+ const baseValue = baseOpts[key];
85
+ const cmdValue = Command.opts[key];
86
+ if (Array.isArray(baseValue))
87
+ allOpts[key] = baseValue.concat(cmdValue);
88
+ else if (typeof baseValue == 'object')
89
+ allOpts[key] = Object.assign({}, baseValue, cmdValue);
90
+ else
91
+ allOpts[key] = cmdValue;
92
+ }
93
+
94
+ const commandOpts = this.getopts(allOpts);
95
+ if (opts.debug)
96
+ console.log('Command options:'.magenta, commandOpts);
97
+ Object.assign(opts, commandOpts);
98
+
99
+ const operandToOpt = Command.usage.operand;
100
+ if (opts._.length >= 2 && operandToOpt)
101
+ opts[operandToOpt] = opts._[1];
102
+
103
+ if (opts.debug)
104
+ console.log('Final options:'.magenta, opts);
105
+
106
+ if (opts.help) {
107
+ this.showHelp(Command.opts, Command.usage, commandName);
108
+ process.exit(0);
109
+ }
110
+
111
+ // Check version
112
+
113
+ let depVersion;
114
+ const versionRegex = /^[^~]?([0-9]+)\.([0-9]+).([0-9]+)$/;
115
+ const wsPackageFile = path.join(opts.workspace, 'package.json');
116
+
117
+ if (await fs.pathExists(wsPackageFile)) {
118
+ const wsPackageJson = require(wsPackageFile);
119
+ try {
120
+ depVersion = wsPackageJson
121
+ .dependencies[packageJson.name]
122
+ .match(versionRegex);
123
+ } catch (e) {}
124
+ }
125
+
126
+ if (depVersion) {
127
+ const myVersion = packageJson.version.match(versionRegex);
128
+
129
+ const isSameVersion =
130
+ depVersion[1] === myVersion[1] &&
131
+ depVersion[2] === myVersion[2];
132
+ if (!isSameVersion)
133
+ throw new Error(`Myt version differs a lot from package.json, please run 'npm i' first to install the proper version.`);
134
+
135
+ const isSameMinor = depVersion[3] === myVersion[3];
136
+ if (!isSameMinor)
137
+ console.warn(`Warning! Myt minor version differs from package.json, maybe you shoud run 'npm i' to install the proper version.`.yellow);
138
+ }
139
+
140
+ // Load method
141
+
142
+ parameter('Workspace:', opts.workspace);
143
+ parameter('Remote:', opts.remote || 'local');
144
+
145
+ await this.load(opts);
146
+ await this.runCommand(Command, opts);
147
+ await this.unload();
148
+ } catch (err) {
149
+ if (err.name == 'Error' && !opts.debug) {
150
+ console.error('Error:'.gray, err.message.red);
151
+ console.log(`You can get more details about the error by passing the 'debug' option.`.yellow);
152
+ } else
153
+ console.log(err.stack.magenta);
154
+
155
+ process.exit(1);
156
+ }
157
+
158
+ function parameter(parameter, value) {
159
+ console.log(parameter.gray, (value || 'null').blue);
160
+ }
161
+
162
+ process.exit();
163
+ }
164
+
165
+ async runCommand(Command, opts) {
166
+ const command = new Command(this, opts);
167
+ return await command.run(this, opts);
168
+ }
169
+
170
+ async load(opts) {
171
+ // Configuration file
172
+
173
+ const config = require(`${__dirname}/assets/myt.default.yml`);
174
+
175
+ const configFile = 'myt.config.yml';
176
+ const configPath = path.join(opts.workspace, configFile);
177
+ if (await fs.pathExists(configPath))
178
+ Object.assign(config, require(configPath));
179
+
180
+ Object.assign(opts, config);
181
+ opts.configFile = configFile;
182
+
183
+ if (!opts.mytDir)
184
+ opts.mytDir = path.join(opts.workspace, opts.subdir || '');
185
+
186
+ opts.routinesDir = path.join(opts.mytDir, 'routines');
187
+ opts.versionsDir = path.join(opts.mytDir, 'versions');
188
+ opts.dumpDir = path.join(opts.mytDir, 'dump');
189
+
190
+ // Database configuration
191
+
192
+ let iniDir = path.join(__dirname, 'assets');
193
+ let iniFile = 'db.ini';
194
+
195
+ if (opts.remote) {
196
+ iniDir = `${opts.mytDir}/remotes`;
197
+ iniFile = `${opts.remote}.ini`;
198
+ }
199
+ const iniPath = path.join(iniDir, iniFile);
200
+
201
+ if (!await fs.pathExists(iniPath))
202
+ throw new Error(`Database config file not found: ${iniPath}`);
203
+
204
+ let dbConfig;
205
+ try {
206
+ const iniData = ini.parse(await fs.readFile(iniPath, 'utf8')).client;
207
+ const iniConfig = {};
208
+ for (const key in iniData) {
209
+ const value = iniData[key];
210
+ const newKey = key.replace(/-/g, '_');
211
+ iniConfig[newKey] = value !== undefined ? value : true;
212
+ }
213
+ dbConfig = {
214
+ host: iniConfig.host,
215
+ port: iniConfig.port,
216
+ user: iniConfig.user,
217
+ password: iniConfig.password,
218
+ multipleStatements: true
219
+ };
220
+ if (iniConfig.enable_cleartext_plugin) {
221
+ dbConfig.authPlugins = {
222
+ mysql_clear_password() {
223
+ return () => iniConfig.password + '\0';
224
+ }
225
+ };
226
+ }
227
+ if (iniConfig.ssl_ca) {
228
+ dbConfig.ssl = {
229
+ ca: await fs.readFile(`${opts.mytDir}/${iniConfig.ssl_ca}`),
230
+ rejectUnauthorized: iniConfig.ssl_verify_server_cert != undefined
231
+ }
232
+ }
233
+ if (iniConfig.socket) {
234
+ dbConfig.socketPath = iniConfig.socket;
235
+ }
236
+ } catch(err) {
237
+ const newErr = Error(`Cannot process the ini file, check that the syntax is correct: ${iniPath}`);
238
+ newErr.stack += `\nCaused by: ${err.stack}`;
239
+ throw newErr;
240
+ }
241
+
242
+ Object.assign(opts, {
243
+ iniFile,
244
+ dbConfig
245
+ });
246
+ this.opts = opts;
247
+ }
248
+
249
+ async unload() {
250
+ if (this.conn)
251
+ await this.conn.end();
252
+ }
253
+
254
+ getopts(opts) {
255
+ const argv = process.argv.slice(2);
256
+ const values = getopts(argv, opts);
257
+ const cleanValues = {};
258
+ for (const opt in values)
259
+ if (opt.length > 1 || opt == '_')
260
+ cleanValues[opt] = values[opt];
261
+ return cleanValues;
262
+ }
263
+
264
+ async dbConnect() {
265
+ const {opts} = this;
266
+
267
+ if (!this.conn) {
268
+ const conn = this.conn = await this.createConnection();
269
+
270
+ const [[schema]] = await conn.query(
271
+ `SHOW DATABASES LIKE ?`, [opts.versionSchema]
272
+ );
273
+
274
+ if (!schema)
275
+ await conn.query(`CREATE DATABASE ??`, [opts.versionSchema]);
276
+ await conn.query(`USE ??`, [opts.versionSchema]);
277
+
278
+ const [[res]] = await conn.query(
279
+ `SELECT COUNT(*) > 0 tableExists
280
+ FROM information_schema.tables
281
+ WHERE TABLE_SCHEMA = ?
282
+ AND TABLE_NAME = 'version'`,
283
+ [opts.versionSchema]
284
+ );
285
+
286
+ if (!res.tableExists) {
287
+ const structure = await fs.readFile(
288
+ `${__dirname}/assets/structure.sql`, 'utf8');
289
+ await conn.query(structure);
290
+ }
291
+ }
292
+
293
+ return this.conn;
294
+ }
295
+
296
+ async createConnection() {
297
+ return await mysql.createConnection(this.opts.dbConfig);
298
+ }
299
+
300
+ async fetchDbVersion() {
301
+ const [[version]] = await this.conn.query(
302
+ `SELECT number, gitCommit
303
+ FROM version WHERE code = ?`,
304
+ [this.opts.code]
305
+ );
306
+ return version;
307
+ }
308
+
309
+ parseVersionDir(versionDir) {
310
+ const match = versionDir.match(/^([0-9]+)-([a-zA-Z0-9]+)?$/);
311
+ if (!match) return null;
312
+ return {
313
+ number: match[1],
314
+ name: match[2]
315
+ };
316
+ }
317
+
318
+ async openRepo() {
319
+ const {opts} = this;
320
+
321
+ if (!await fs.pathExists(`${opts.workspace}/.git`))
322
+ throw new Error ('Git not initialized');
323
+
324
+ return await nodegit.Repository.open(opts.workspace);
325
+ }
326
+
327
+ showHelp(opts, usage, command) {
328
+ const prefix = `${'Usage:'.gray} [npx] myt`;
329
+
330
+ if (command) {
331
+ let log = [prefix, command.blue];
332
+ if (usage.operand) log.push(`[<${usage.operand}>]`);
333
+ if (opts) log.push('[<options>]');
334
+ console.log(log.join(' '))
335
+ } else
336
+ console.log(`${prefix} [<options>] ${'<command>'.blue} [<args>]`);
337
+
338
+ if (usage.description)
339
+ console.log(`${'Description:'.gray} ${usage.description}`);
340
+
341
+ if (opts && opts.alias) {
342
+ const alias = opts.alias;
343
+ const boolean = opts.boolean || [];
344
+
345
+ console.log('Options:'.gray);
346
+ for (const opt in alias) {
347
+ const paramDescription = usage.params[opt] || '';
348
+ let longOpt = opt;
349
+ if (boolean.indexOf(longOpt) === -1)
350
+ longOpt += ` <string>`;
351
+ longOpt = camelToSnake(longOpt).padEnd(22, ' ')
352
+ console.log(` -${alias[opt]}, --${longOpt} ${paramDescription}`);
353
+ }
354
+ }
355
+ }
356
+ }
357
+
358
+ module.exports = Myt;
359
+
360
+ if (require.main === module)
361
+ new Myt().run();