@verdnatura/myt 1.5.6 → 1.5.8

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.
package/myt.js CHANGED
@@ -10,52 +10,46 @@ const path = require('path');
10
10
  const mysql = require('mysql2/promise');
11
11
  const nodegit = require('nodegit');
12
12
  const camelToSnake = require('./lib/util').camelToSnake;
13
- const docker = require('./lib/docker');
14
- const Command = require('./lib/command');
15
13
 
16
14
  class Myt {
17
- get usage() {
18
- return {
19
- description: 'Utility for database versioning',
20
- params: {
21
- remote: 'Name of remote to use',
22
- workspace: 'The base directory of the project',
23
- socket: 'Wether to connect to database via socket',
24
- debug: 'Wether to enable debug mode',
25
- version: 'Display the version number and exit',
26
- help: 'Display this help message'
27
- }
28
- };
29
- }
30
-
31
- get localOpts() {
32
- return {
33
- alias: {
34
- remote: 'r',
35
- workspace: 'w',
36
- socket: 'k',
37
- debug: 'd',
38
- version: 'v',
39
- help: 'h'
40
- },
41
- boolean: [
42
- 'debug',
43
- 'version',
44
- 'help'
45
- ],
46
- default: {
47
- workspace: process.cwd()
48
- }
49
- };
50
- }
51
-
52
- async run(CommandClass) {
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
+ socket: 'Wether to connect to database via socket',
21
+ debug: 'Wether to enable debug mode',
22
+ version: 'Display the version number and exit',
23
+ help: 'Display this help message'
24
+ }
25
+ };
26
+
27
+ static opts = {
28
+ alias: {
29
+ remote: 'r',
30
+ workspace: 'w',
31
+ socket: 'k',
32
+ debug: 'd',
33
+ version: 'v',
34
+ help: 'h'
35
+ },
36
+ boolean: [
37
+ 'debug',
38
+ 'version',
39
+ 'help'
40
+ ]
41
+ };
42
+
43
+ async run(Command) {
53
44
  console.log(
54
45
  'Myt'.green,
55
46
  `v${packageJson.version}`.magenta
56
47
  );
57
48
 
58
- const baseOpts = this.localOpts;
49
+ let baseOpts = this.constructor.opts;
50
+ baseOpts.default = Object.assign(baseOpts.default || {}, {
51
+ workspace: process.cwd()
52
+ });
59
53
  const opts = this.getopts(baseOpts);
60
54
 
61
55
  if (opts.debug) {
@@ -68,7 +62,7 @@ class Myt {
68
62
 
69
63
  try {
70
64
  const commandName = opts._[0];
71
- if (!CommandClass && commandName) {
65
+ if (!Command && commandName) {
72
66
  if (!/^[a-z]+$/.test(commandName))
73
67
  throw new Error (`Invalid command name '${commandName}'`);
74
68
 
@@ -76,20 +70,20 @@ class Myt {
76
70
 
77
71
  if (!await fs.pathExists(commandFile))
78
72
  throw new Error (`Unknown command '${commandName}'`);
79
- CommandClass = require(commandFile);
73
+ Command = require(commandFile);
80
74
  }
81
75
 
82
- if (!CommandClass) {
83
- this.showHelp(baseOpts, this.usage);
76
+ if (!Command) {
77
+ this.showHelp(baseOpts, this.constructor.usage);
84
78
  process.exit(0);
85
79
  }
86
80
 
87
81
  const allOpts = Object.assign({}, baseOpts);
88
82
 
89
- if (CommandClass.localOpts)
90
- for (const key in CommandClass.localOpts) {
83
+ if (Command.opts)
84
+ for (const key in Command.opts) {
91
85
  const baseValue = baseOpts[key];
92
- const cmdValue = CommandClass.localOpts[key];
86
+ const cmdValue = Command.opts[key];
93
87
  if (Array.isArray(baseValue))
94
88
  allOpts[key] = baseValue.concat(cmdValue);
95
89
  else if (typeof baseValue == 'object')
@@ -103,7 +97,7 @@ class Myt {
103
97
  console.log('Command options:'.magenta, commandOpts);
104
98
  Object.assign(opts, commandOpts);
105
99
 
106
- const operandToOpt = CommandClass.usage.operand;
100
+ const operandToOpt = Command.usage.operand;
107
101
  if (opts._.length >= 2 && operandToOpt)
108
102
  opts[operandToOpt] = opts._[1];
109
103
 
@@ -111,7 +105,7 @@ class Myt {
111
105
  console.log('Final options:'.magenta, opts);
112
106
 
113
107
  if (opts.help) {
114
- this.showHelp(CommandClass.localOpts, CommandClass.usage, commandName);
108
+ this.showHelp(Command.opts, Command.usage, commandName);
115
109
  process.exit(0);
116
110
  }
117
111
 
@@ -150,7 +144,7 @@ class Myt {
150
144
  parameter('Remote:', opts.remote || 'local');
151
145
 
152
146
  await this.load(opts);
153
- await this.runCommand(CommandClass, opts);
147
+ await this.runCommand(Command, opts);
154
148
  await this.unload();
155
149
  } catch (err) {
156
150
  if (err.name == 'Error' && !opts.debug) {
@@ -169,9 +163,8 @@ class Myt {
169
163
  process.exit();
170
164
  }
171
165
 
172
- async runCommand(CommandClass, opts) {
173
- const command = new CommandClass();
174
- command.opts = opts;
166
+ async runCommand(Command, opts) {
167
+ const command = new Command(this, opts);
175
168
  return await command.run(this, opts);
176
169
  }
177
170
 
@@ -315,70 +308,6 @@ class Myt {
315
308
  };
316
309
  }
317
310
 
318
- async changedRoutines(commitSha) {
319
- const repo = await this.openRepo();
320
- const changes = [];
321
- const changesMap = new Map();
322
-
323
- async function pushChanges(diff) {
324
- if (!diff) return;
325
- const patches = await diff.patches();
326
-
327
- for (const patch of patches) {
328
- const path = patch.newFile().path();
329
- const match = path.match(/^routines\/(.+)\.sql$/);
330
- if (!match) continue;
331
-
332
- let change = changesMap.get(match[1]);
333
- if (!change) {
334
- change = {path: match[1]};
335
- changes.push(change);
336
- changesMap.set(match[1], change);
337
- }
338
- change.mark = patch.isDeleted() ? '-' : '+';
339
- }
340
- }
341
-
342
- const head = await repo.getHeadCommit();
343
-
344
- if (head && commitSha) {
345
- let commit;
346
- let notFound;
347
-
348
- try {
349
- commit = await repo.getCommit(commitSha);
350
- notFound = false;
351
- } catch (err) {
352
- if (err.errorFunction == 'Commit.lookup')
353
- notFound = true;
354
- else
355
- throw err;
356
- }
357
-
358
- if (notFound) {
359
- console.warn(`Database commit not found, trying git fetch`.yellow);
360
- await repo.fetchAll();
361
- commit = await repo.getCommit(commitSha);
362
- }
363
-
364
- const commitTree = await commit.getTree();
365
-
366
- const headTree = await head.getTree();
367
- const diff = await headTree.diff(commitTree);
368
- await pushChanges(diff);
369
- }
370
-
371
- await pushChanges(await this.getUnstaged(repo));
372
- await pushChanges(await this.getStaged(repo));
373
-
374
- return changes.sort((a, b) => {
375
- if (b.mark != a.mark)
376
- return b.mark == '-' ? 1 : -1;
377
- return a.path.localeCompare(b.path);
378
-
379
- });
380
- }
381
-
382
311
  async openRepo() {
383
312
  const {opts} = this;
384
313
 
@@ -388,94 +317,6 @@ class Myt {
388
317
  return await nodegit.Repository.open(opts.workspace);
389
318
  }
390
319
 
391
- async getStaged(repo) {
392
- const head = await repo.getHeadCommit();
393
-
394
- try {
395
- const emptyTree = '4b825dc642cb6eb9a060e54bf8d69288fbee4904';
396
- const headTree = await (head
397
- ? head.getTree()
398
- : nodegit.Tree.lookup(repo, emptyTree)
399
- );
400
- return await nodegit.Diff.treeToIndex(repo, headTree, null);
401
- } catch (err) {
402
- console.warn('Cannot fetch staged changes:', err.message);
403
- }
404
- }
405
-
406
- async getUnstaged(repo) {
407
- return await nodegit.Diff.indexToWorkdir(repo, null, {
408
- flags: nodegit.Diff.OPTION.SHOW_UNTRACKED_CONTENT
409
- | nodegit.Diff.OPTION.RECURSE_UNTRACKED_DIRS
410
- });
411
- }
412
-
413
- async initDump(dumpFile) {
414
- const dumpDir = this.opts.dumpDir;
415
- if (!await fs.pathExists(dumpDir))
416
- await fs.mkdir(dumpDir);
417
-
418
- const dumpPath = path.join(dumpDir, dumpFile);
419
-
420
- // FIXME: If it's called after docker.build() statement it creates an
421
- // "invalid" WriteStream
422
- const dumpStream = await fs.createWriteStream(dumpPath);
423
-
424
- await docker.build(__dirname, {
425
- tag: 'myt/client',
426
- file: path.join(__dirname, 'server', 'Dockerfile.client')
427
- }, this.opts.debug);
428
-
429
- return dumpStream;
430
- }
431
-
432
- async dumpFixtures(dumpStream, tables, replace) {
433
- const fixturesArgs = [
434
- '--no-create-info',
435
- '--skip-triggers',
436
- '--skip-extended-insert',
437
- '--skip-disable-keys',
438
- '--skip-add-locks',
439
- '--skip-set-charset',
440
- '--skip-comments',
441
- '--skip-tz-utc'
442
- ];
443
-
444
- if (replace)
445
- fixturesArgs.push('--replace');
446
-
447
- for (const schema in tables) {
448
- const escapedSchema = '`'+ schema.replace('`', '``') +'`';
449
- await dumpStream.write(
450
- `USE ${escapedSchema};\n`,
451
- 'utf8'
452
- );
453
-
454
- const args = fixturesArgs.concat([schema], tables[schema]);
455
- await this.runDump('mysqldump', args, dumpStream);
456
- }
457
- }
458
-
459
- async runDump(command, args, dumpStream) {
460
- const iniPath = path.join(this.opts.subdir || '', 'remotes', this.opts.iniFile);
461
- const myArgs = [
462
- `--defaults-file=${iniPath}`
463
- ];
464
- const execOptions = {
465
- stdio: [
466
- process.stdin,
467
- dumpStream,
468
- process.stderr
469
- ]
470
- };
471
- const commandArgs = [command].concat(myArgs, args);
472
- await docker.run('myt/client', commandArgs, {
473
- addHost: 'host.docker.internal:host-gateway',
474
- volume: `${this.opts.mytDir}:/workspace`,
475
- rm: true
476
- }, execOptions);
477
- }
478
-
479
320
  showHelp(opts, usage, command) {
480
321
  const prefix = `${'Usage:'.gray} [npx] myt`;
481
322
 
@@ -505,139 +346,6 @@ class Myt {
505
346
  }
506
347
  }
507
348
  }
508
-
509
- /**
510
- * Executes an SQL script.
511
- *
512
- * @param {Connection} conn MySQL connection object
513
- * @returns {Array<Result>} The resultset
514
- */
515
- async queryFromFile(conn, file) {
516
- const sql = await fs.readFile(file, 'utf8');
517
- return await this.multiQuery(conn, sql);
518
- }
519
-
520
- /**
521
- * Executes a multi-query string.
522
- *
523
- * @param {Connection} conn MySQL connection object
524
- * @param {String} sql SQL multi-query string
525
- * @returns {Array<Result>} The resultset
526
- */
527
- async multiQuery(conn, sql) {
528
- let results = [];
529
- const stmts = this.querySplit(sql);
530
-
531
- for (const stmt of stmts)
532
- results = results.concat(await conn.query(stmt));
533
-
534
- return results;
535
- }
536
-
537
- /**
538
- * Splits an SQL muti-query into a single-query array, it does an small
539
- * parse to correctly handle the DELIMITER statement.
540
- *
541
- * @param {Array<String>} stmts The splitted SQL statements
542
- */
543
- querySplit(sql) {
544
- const stmts = [];
545
- let i,
546
- char,
547
- token,
548
- escaped,
549
- stmtStart;
550
-
551
- let delimiter = ';';
552
- const delimiterRe = /\s*delimiter\s+(\S+)[^\S\r\n]*(?:\r?\n|\r|$)/yi;
553
-
554
- function begins(str) {
555
- let j;
556
- for (j = 0; j < str.length; j++)
557
- if (sql[i + j] != str[j])
558
- return false;
559
- i += j;
560
- return true;
561
- }
562
-
563
- for (i = 0; i < sql.length;) {
564
- stmtStart = i;
565
-
566
- delimiterRe.lastIndex = i;
567
- const match = sql.match(delimiterRe);
568
- if (match) {
569
- delimiter = match[1];
570
- i += match[0].length;
571
- continue;
572
- }
573
-
574
- let delimiterFound = false;
575
- while (i < sql.length) {
576
- char = sql[i];
577
-
578
- if (token) {
579
- if (!escaped && begins(token.end))
580
- token = null;
581
- else {
582
- escaped = !escaped && token.escape(char);
583
- i++;
584
- }
585
- } else {
586
- delimiterFound = begins(delimiter);
587
- if (delimiterFound) break;
588
-
589
- const tok = tokenIndex.get(char);
590
- if (tok && begins(tok.start))
591
- token = tok;
592
- else
593
- i++;
594
- }
595
- }
596
-
597
- let len = i - stmtStart;
598
- if (delimiterFound) len -= delimiter.length;
599
- const stmt = sql.substr(stmtStart, len);
600
-
601
- if (!/^\s*$/.test(stmt))
602
- stmts.push(stmt);
603
- }
604
-
605
- return stmts;
606
- }
607
- }
608
-
609
- const tokens = {
610
- string: {
611
- start: '\'',
612
- end: '\'',
613
- escape: char => char == '\'' || char == '\\'
614
- },
615
- quotedString: {
616
- start: '"',
617
- end: '"',
618
- escape: char => char == '"' || char == '\\'
619
- },
620
- id: {
621
- start: '`',
622
- end: '`',
623
- escape: char => char == '`'
624
- },
625
- multiComment: {
626
- start: '/*',
627
- end: '*/',
628
- escape: () => false
629
- },
630
- singleComment: {
631
- start: '-- ',
632
- end: '\n',
633
- escape: () => false
634
- }
635
- };
636
-
637
- const tokenIndex = new Map();
638
- for (const tokenId in tokens) {
639
- const token = tokens[tokenId];
640
- tokenIndex.set(token.start[0], token);
641
349
  }
642
350
 
643
351
  module.exports = Myt;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@verdnatura/myt",
3
- "version": "1.5.6",
3
+ "version": "1.5.8",
4
4
  "author": "Verdnatura Levante SL",
5
5
  "description": "MySQL version control",
6
6
  "license": "GPL-3.0",
@@ -12,7 +12,7 @@
12
12
  "url": "https://github.com/verdnatura/myt.git"
13
13
  },
14
14
  "dependencies": {
15
- "@sqltools/formatter": "^1.2.3",
15
+ "@sqltools/formatter": "^1.2.5",
16
16
  "colors": "^1.4.0",
17
17
  "ejs": "^3.1.6",
18
18
  "fs-extra": "^8.1.0",
@@ -1,4 +1,4 @@
1
1
  #!/bin/bash
2
2
 
3
3
  # FIXME: It can corrupt data
4
- mysqldump $@ | sed 's/ AUTO_INCREMENT=[0-9]* //g'
4
+ mysqldump $@ | sed 's/ AUTO_INCREMENT=[0-9]*//g'
@@ -8,6 +8,6 @@
8
8
  "type": "git"
9
9
  },
10
10
  "dependencies": {
11
- "@verdnatura/myt": "^1.5.6"
11
+ "@verdnatura/myt": "^1.5.8"
12
12
  }
13
13
  }
@@ -0,0 +1,5 @@
1
+ [client]
2
+ host = host.docker.internal
3
+ port = 3306
4
+ user = root
5
+ password = root