@verdnatura/myt 1.5.1

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 (57) hide show
  1. package/.dockerignore +2 -0
  2. package/LICENSE +17 -0
  3. package/README.md +268 -0
  4. package/assets/db.ini +5 -0
  5. package/assets/myvc.default.yml +9 -0
  6. package/assets/structure.sql +24 -0
  7. package/cli.js +4 -0
  8. package/exporters/event.ejs +21 -0
  9. package/exporters/event.js +25 -0
  10. package/exporters/event.sql +19 -0
  11. package/exporters/function.ejs +20 -0
  12. package/exporters/function.js +23 -0
  13. package/exporters/function.sql +16 -0
  14. package/exporters/procedure.ejs +14 -0
  15. package/exporters/procedure.js +23 -0
  16. package/exporters/procedure.sql +14 -0
  17. package/exporters/trigger.ejs +7 -0
  18. package/exporters/trigger.js +5 -0
  19. package/exporters/trigger.sql +13 -0
  20. package/exporters/view.ejs +7 -0
  21. package/exporters/view.js +25 -0
  22. package/exporters/view.sql +12 -0
  23. package/lib/command.js +16 -0
  24. package/lib/docker.js +133 -0
  25. package/lib/exporter-engine.js +112 -0
  26. package/lib/exporter.js +115 -0
  27. package/lib/server.js +71 -0
  28. package/lib/util.js +6 -0
  29. package/myt-clean.js +52 -0
  30. package/myt-dump.js +71 -0
  31. package/myt-fixtures.js +28 -0
  32. package/myt-init.js +25 -0
  33. package/myt-pull.js +129 -0
  34. package/myt-push.js +475 -0
  35. package/myt-run.js +160 -0
  36. package/myt-start.js +51 -0
  37. package/myt-version.js +219 -0
  38. package/myt.js +646 -0
  39. package/package.json +43 -0
  40. package/server/Dockerfile.base +1 -0
  41. package/server/Dockerfile.client +14 -0
  42. package/server/Dockerfile.dump +16 -0
  43. package/server/Dockerfile.server +25 -0
  44. package/server/docker-dump.sh +4 -0
  45. package/server/docker-fixtures.sh +4 -0
  46. package/server/docker-import.sh +6 -0
  47. package/server/docker-init.sh +20 -0
  48. package/server/docker-start.sh +10 -0
  49. package/server/docker.cnf +9 -0
  50. package/template/dump/fixtures.sql +1 -0
  51. package/template/dump/structure.sql +1 -0
  52. package/template/myvc.config.yml +11 -0
  53. package/template/package.json +13 -0
  54. package/template/remotes/local.ini +5 -0
  55. package/template/routines/README.md +1 -0
  56. package/template/versions/00001-firstVersion/00-firstScript.sql +1 -0
  57. package/template/versions/README.md +1 -0
package/myt.js ADDED
@@ -0,0 +1,646 @@
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
+ const docker = require('./lib/docker');
14
+ const Command = require('./lib/command');
15
+
16
+ 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) {
53
+ console.log(
54
+ 'Myt (MySQL Version Control)'.green,
55
+ `v${packageJson.version}`.magenta
56
+ );
57
+
58
+ const baseOpts = this.localOpts;
59
+ const opts = this.getopts(baseOpts);
60
+
61
+ if (opts.debug) {
62
+ console.warn('Debug mode enabled.'.yellow);
63
+ console.log('Global options:'.magenta, opts);
64
+ }
65
+
66
+ if (opts.version)
67
+ process.exit(0);
68
+
69
+ try {
70
+ const commandName = opts._[0];
71
+ if (!CommandClass && commandName) {
72
+ if (!/^[a-z]+$/.test(commandName))
73
+ throw new Error (`Invalid command name '${commandName}'`);
74
+
75
+ const commandFile = path.join(__dirname, `myt-${commandName}.js`);
76
+
77
+ if (!await fs.pathExists(commandFile))
78
+ throw new Error (`Unknown command '${commandName}'`);
79
+ CommandClass = require(commandFile);
80
+ }
81
+
82
+ if (!CommandClass) {
83
+ this.showHelp(baseOpts, this.usage);
84
+ process.exit(0);
85
+ }
86
+
87
+ const allOpts = Object.assign({}, baseOpts);
88
+
89
+ if (CommandClass.localOpts)
90
+ for (const key in CommandClass.localOpts) {
91
+ const baseValue = baseOpts[key];
92
+ const cmdValue = CommandClass.localOpts[key];
93
+ if (Array.isArray(baseValue))
94
+ allOpts[key] = baseValue.concat(cmdValue);
95
+ else if (typeof baseValue == 'object')
96
+ allOpts[key] = Object.assign({}, baseValue, cmdValue);
97
+ else
98
+ allOpts[key] = cmdValue;
99
+ }
100
+
101
+ const commandOpts = this.getopts(allOpts);
102
+ if (opts.debug)
103
+ console.log('Command options:'.magenta, commandOpts);
104
+ Object.assign(opts, commandOpts);
105
+
106
+ const operandToOpt = CommandClass.usage.operand;
107
+ if (opts._.length >= 2 && operandToOpt)
108
+ opts[operandToOpt] = opts._[1];
109
+
110
+ if (opts.debug)
111
+ console.log('Final options:'.magenta, opts);
112
+
113
+ if (opts.help) {
114
+ this.showHelp(CommandClass.localOpts, CommandClass.usage, commandName);
115
+ process.exit(0);
116
+ }
117
+
118
+ // Check version
119
+
120
+ let depVersion;
121
+ const versionRegex = /^[^~]?([0-9]+)\.([0-9]+).([0-9]+)$/;
122
+ const wsPackageFile = path.join(opts.workspace, 'package.json');
123
+
124
+ if (await fs.pathExists(wsPackageFile)) {
125
+ const wsPackageJson = require(wsPackageFile);
126
+ try {
127
+ depVersion = wsPackageJson
128
+ .dependencies
129
+ .myt.match(versionRegex);
130
+ } catch (e) {}
131
+ }
132
+
133
+ if (depVersion) {
134
+ const myVersion = packageJson.version.match(versionRegex);
135
+
136
+ const isSameVersion =
137
+ depVersion[1] === myVersion[1] &&
138
+ depVersion[2] === myVersion[2];
139
+ if (!isSameVersion)
140
+ throw new Error(`Myt version differs a lot from package.json, please run 'npm i' first to install the proper version.`);
141
+
142
+ const isSameMinor = depVersion[3] === myVersion[3];
143
+ if (!isSameMinor)
144
+ console.warn(`Warning! Myt minor version differs from package.json, maybe you shoud run 'npm i' to install the proper version.`.yellow);
145
+ }
146
+
147
+ // Load method
148
+
149
+ parameter('Workspace:', opts.workspace);
150
+ parameter('Remote:', opts.remote || 'local');
151
+
152
+ await this.load(opts);
153
+ await this.runCommand(CommandClass, opts);
154
+ await this.unload();
155
+ } catch (err) {
156
+ if (err.name == 'Error' && !opts.debug) {
157
+ console.error('Error:'.gray, err.message.red);
158
+ console.log(`You can get more details about the error by passing the 'debug' option.`.yellow);
159
+ } else
160
+ console.log(err.stack.magenta);
161
+
162
+ process.exit(1);
163
+ }
164
+
165
+ function parameter(parameter, value) {
166
+ console.log(parameter.gray, (value || 'null').blue);
167
+ }
168
+
169
+ process.exit();
170
+ }
171
+
172
+ async runCommand(CommandClass, opts) {
173
+ const command = new CommandClass();
174
+ command.opts = opts;
175
+ await command.run(this, opts);
176
+ }
177
+
178
+ async load(opts) {
179
+ // Configuration file
180
+
181
+ const config = require(`${__dirname}/assets/myt.default.yml`);
182
+
183
+ const configFile = 'myt.config.yml';
184
+ const configPath = path.join(opts.workspace, configFile);
185
+ if (await fs.pathExists(configPath))
186
+ Object.assign(config, require(configPath));
187
+
188
+ Object.assign(opts, config);
189
+ opts.configFile = configFile;
190
+
191
+ if (!opts.mytDir)
192
+ opts.mytDir = path.join(opts.workspace, opts.subdir || '');
193
+
194
+ opts.routinesDir = path.join(opts.mytDir, 'routines');
195
+ opts.versionsDir = path.join(opts.mytDir, 'versions');
196
+ opts.dumpDir = path.join(opts.mytDir, 'dump');
197
+
198
+ // Database configuration
199
+
200
+ let iniDir = path.join(__dirname, 'assets');
201
+ let iniFile = 'db.ini';
202
+
203
+ if (opts.remote) {
204
+ iniDir = `${opts.mytDir}/remotes`;
205
+ iniFile = `${opts.remote}.ini`;
206
+ }
207
+ const iniPath = path.join(iniDir, iniFile);
208
+
209
+ if (!await fs.pathExists(iniPath))
210
+ throw new Error(`Database config file not found: ${iniPath}`);
211
+
212
+ let dbConfig;
213
+ try {
214
+ const iniConfig = ini.parse(await fs.readFile(iniPath, 'utf8')).client;
215
+ dbConfig = {
216
+ host: iniConfig.host,
217
+ port: iniConfig.port,
218
+ user: iniConfig.user,
219
+ password: iniConfig.password,
220
+ multipleStatements: true,
221
+ 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
+ } catch(err) {
234
+ const newErr = Error(`Cannot process the ini file, check that the syntax is correct: ${iniPath}`);
235
+ newErr.stack += `\nCaused by: ${err.stack}`;
236
+ throw newErr;
237
+ }
238
+
239
+ if (opts.socket)
240
+ dbConfig.socketPath = '/var/run/mysqld/mysqld.sock';
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 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
+ async openRepo() {
383
+ const {opts} = this;
384
+
385
+ if (!await fs.pathExists(`${opts.workspace}/.git`))
386
+ throw new Error ('Git not initialized');
387
+
388
+ return await nodegit.Repository.open(opts.workspace);
389
+ }
390
+
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
+ showHelp(opts, usage, command) {
480
+ const prefix = `${'Usage:'.gray} [npx] myt`;
481
+
482
+ if (command) {
483
+ let log = [prefix, command.blue];
484
+ if (usage.operand) log.push(`[<${usage.operand}>]`);
485
+ if (opts) log.push('[<options>]');
486
+ console.log(log.join(' '))
487
+ } else
488
+ console.log(`${prefix} [<options>] ${'<command>'.blue} [<args>]`);
489
+
490
+ if (usage.description)
491
+ console.log(`${'Description:'.gray} ${usage.description}`);
492
+
493
+ if (opts && opts.alias) {
494
+ const alias = opts.alias;
495
+ const boolean = opts.boolean || [];
496
+
497
+ console.log('Options:'.gray);
498
+ for (const opt in alias) {
499
+ const paramDescription = usage.params[opt] || '';
500
+ let longOpt = opt;
501
+ if (boolean.indexOf(longOpt) === -1)
502
+ longOpt += ` <string>`;
503
+ longOpt = camelToSnake(longOpt).padEnd(22, ' ')
504
+ console.log(` -${alias[opt]}, --${longOpt} ${paramDescription}`);
505
+ }
506
+ }
507
+ }
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
+ }
642
+
643
+ module.exports = Myt;
644
+
645
+ if (require.main === module)
646
+ new Myt().run();
package/package.json ADDED
@@ -0,0 +1,43 @@
1
+ {
2
+ "name": "@verdnatura/myt",
3
+ "version": "1.5.1",
4
+ "author": "Verdnatura Levante SL",
5
+ "description": "MySQL Version Control",
6
+ "license": "GPL-3.0",
7
+ "bin": {
8
+ "myt": "cli.js",
9
+ "myvc": "cli.js",
10
+ "myv": "cli.js"
11
+ },
12
+ "repository": {
13
+ "type": "git",
14
+ "url": "https://github.com/verdnatura/myt.git"
15
+ },
16
+ "dependencies": {
17
+ "@sqltools/formatter": "^1.2.3",
18
+ "colors": "^1.4.0",
19
+ "ejs": "^3.1.6",
20
+ "fs-extra": "^8.1.0",
21
+ "getopts": "^2.3.0",
22
+ "ini": "^1.3.8",
23
+ "mysql2": "^2.3.3",
24
+ "nodegit": "^0.27.0",
25
+ "require-yaml": "0.0.1",
26
+ "sha.js": "^2.4.11"
27
+ },
28
+ "main": "index.js",
29
+ "scripts": {
30
+ "test": "echo \"Error: no test specified\" && exit 1"
31
+ },
32
+ "keywords": [
33
+ "mysql",
34
+ "mariadb",
35
+ "migrations",
36
+ "git",
37
+ "vcs",
38
+ "database",
39
+ "version",
40
+ "control",
41
+ "sql"
42
+ ]
43
+ }
@@ -0,0 +1 @@
1
+ FROM mysql:5.6
@@ -0,0 +1,14 @@
1
+ FROM debian:bullseye-slim
2
+
3
+ ARG DEBIAN_FRONTEND=noninteractive
4
+ RUN apt-get update \
5
+ && apt-get install -y --no-install-recommends \
6
+ mariadb-client \
7
+ libmariadb3 \
8
+ && rm -rf /var/lib/apt/lists/*
9
+
10
+ COPY \
11
+ server/docker-dump.sh \
12
+ server/docker-fixtures.sh \
13
+ /usr/local/bin/
14
+ WORKDIR /workspace