@verdnatura/myt 1.8.6 → 2.0.2

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 (51) hide show
  1. package/README.md +80 -52
  2. package/assets/myt.default.yml +7 -0
  3. package/assets/structure.sql +1 -1
  4. package/{server/Dockerfile.client → docker/client/Dockerfile} +3 -3
  5. package/docker/server/Dockerfile +37 -0
  6. package/docker/server/Dockerfile.base +6 -0
  7. package/docker/server/Dockerfile.myt +35 -0
  8. package/docker/server/docker-init.sh +36 -0
  9. package/{server → docker/server}/docker-start.sh +1 -1
  10. package/lib/command.js +14 -6
  11. package/lib/docker.js +60 -34
  12. package/lib/dumper.js +11 -9
  13. package/lib/exporter-engine.js +4 -4
  14. package/lib/server.js +2 -8
  15. package/myt-apply.js +203 -0
  16. package/myt-build.js +283 -0
  17. package/myt-clean.js +15 -11
  18. package/myt-create.js +19 -11
  19. package/myt-dump.js +41 -36
  20. package/myt-fixtures.js +6 -6
  21. package/myt-init.js +5 -3
  22. package/myt-pull.js +12 -11
  23. package/myt-push.js +99 -96
  24. package/myt-run.js +28 -144
  25. package/myt-start.js +4 -4
  26. package/myt-version.js +13 -13
  27. package/myt.js +285 -149
  28. package/package.json +4 -3
  29. package/prebuilts/README.md +15 -0
  30. package/prebuilts/nodegit-v0.28.0-alpha.36-node-v127-linux-x64.tar.gz +0 -0
  31. package/template/.dockerignore +5 -0
  32. package/template/myt.config.yml +1 -4
  33. package/template/package.json +1 -1
  34. package/template/realms/.keep +0 -0
  35. package/template/remotes/docker.ini +1 -1
  36. package/template/remotes/local.ini +1 -1
  37. package/template/remotes/socket.ini +3 -0
  38. package/template/structure/.dump/structure.sql +32 -0
  39. package/server/Dockerfile.base +0 -1
  40. package/server/Dockerfile.dump +0 -16
  41. package/server/Dockerfile.server +0 -16
  42. package/server/docker-import.sh +0 -10
  43. package/server/docker-init.sh +0 -24
  44. /package/{server → docker/client}/docker-dump.sh +0 -0
  45. /package/{server → docker/client}/docker-fixtures.sh +0 -0
  46. /package/{server → docker/server}/docker.cnf +0 -0
  47. /package/template/{dump/Dockerfile.base → docker/Dockerfile} +0 -0
  48. /package/template/{dump/fixtures.after.sql → fixtures/after.sql} +0 -0
  49. /package/template/{dump/fixtures.before.sql → fixtures/before.sql} +0 -0
  50. /package/template/{dump/dump.after.sql → structure/after.sql} +0 -0
  51. /package/template/{dump/dump.before.sql → structure/before.sql} +0 -0
package/myt.js CHANGED
@@ -8,7 +8,6 @@ const fs = require('fs-extra');
8
8
  const ini = require('ini');
9
9
  const path = require('path');
10
10
  const mysql = require('mysql2/promise');
11
- const nodegit = require('nodegit');
12
11
  const camelToSnake = require('./lib/util').camelToSnake;
13
12
 
14
13
  const scriptRegex = /^[0-9]{2}-[a-zA-Z0-9_]+(?:\.(?!undo)([a-zA-Z0-9_]+))?(\.undo)?\.sql$/;
@@ -25,7 +24,7 @@ class Myt {
25
24
  }
26
25
  };
27
26
 
28
- static opts = {
27
+ static args = {
29
28
  alias: {
30
29
  remote: 'r',
31
30
  workspace: 'w',
@@ -33,6 +32,10 @@ class Myt {
33
32
  version: 'v',
34
33
  help: 'h'
35
34
  },
35
+ string: [
36
+ 'remote',
37
+ 'workspace'
38
+ ],
36
39
  boolean: [
37
40
  'debug',
38
41
  'version',
@@ -40,6 +43,10 @@ class Myt {
40
43
  ]
41
44
  };
42
45
 
46
+ /**
47
+ * Run myt command from CLI.
48
+ * @param {Command} Command class reference
49
+ */
43
50
  async cli(Command) {
44
51
  this.cliMode = true;
45
52
 
@@ -49,16 +56,17 @@ class Myt {
49
56
  );
50
57
  this.packageJson = packageJson;
51
58
 
52
- let baseOpts = this.constructor.opts;
53
- baseOpts.default = Object.assign(baseOpts.default || {}, {
59
+ const args = Object.assign({}, this.constructor.args);
60
+ args.default = Object.assign(args.default || {}, {
54
61
  workspace: process.cwd()
55
62
  });
56
- const opts = this.getopts(baseOpts);
63
+ const argv = process.argv.slice(2);
64
+
65
+ // Temporal until args is merged with command args
66
+ let opts = getopts(argv, args);
57
67
 
58
- if (opts.debug) {
68
+ if (opts.debug)
59
69
  console.warn('Debug mode enabled.'.yellow);
60
- console.log('Global options:'.magenta, opts);
61
- }
62
70
 
63
71
  if (opts.version)
64
72
  process.exit(0);
@@ -77,35 +85,44 @@ class Myt {
77
85
  }
78
86
 
79
87
  if (!Command) {
80
- this.showHelp(baseOpts, this.constructor.usage);
88
+ this.showHelp(args, this.constructor.usage);
81
89
  process.exit(0);
82
90
  }
83
91
 
84
- const allOpts = Object.assign({}, baseOpts);
92
+ const allArgs = Object.assign({}, args);
85
93
 
86
- if (Command.opts)
87
- for (const key in Command.opts) {
88
- const baseValue = baseOpts[key];
89
- const cmdValue = Command.opts[key];
94
+ if (Command.args)
95
+ for (const key in Command.args) {
96
+ const baseValue = args[key];
97
+ const cmdValue = Command.args[key];
90
98
  if (Array.isArray(baseValue))
91
- allOpts[key] = baseValue.concat(cmdValue);
99
+ allArgs[key] = baseValue.concat(cmdValue);
92
100
  else if (typeof baseValue == 'object')
93
- allOpts[key] = Object.assign({}, baseValue, cmdValue);
101
+ allArgs[key] = Object.assign({}, baseValue, cmdValue);
94
102
  else
95
- allOpts[key] = cmdValue;
103
+ allArgs[key] = cmdValue;
96
104
  }
97
105
 
98
- const commandOpts = this.getopts(allOpts);
99
- if (opts.debug)
100
- console.log('Command options:'.magenta, commandOpts);
101
- Object.assign(opts, commandOpts);
106
+ const allOpts = getopts(argv, allArgs);
102
107
 
103
108
  const operandToOpt = Command.usage.operand;
104
- if (opts._.length >= 2 && operandToOpt)
105
- opts[operandToOpt] = opts._[1];
109
+ if (allOpts._.length >= 2 && operandToOpt)
110
+ allOpts[operandToOpt] = allOpts._[1];
111
+
112
+ function fetchOpts(Class) {
113
+ const opts = {};
114
+ for (const param in Class.usage.params)
115
+ opts[param] = allOpts[param];
116
+ return opts;
117
+ }
106
118
 
107
- if (opts.debug)
108
- console.log('Final options:'.magenta, opts);
119
+ opts = fetchOpts(this.constructor);
120
+ const copts = fetchOpts(Command);
121
+
122
+ if (opts.debug) {
123
+ console.debug('Global options:'.magenta, opts);
124
+ console.debug('Command options:'.magenta, copts);
125
+ }
109
126
 
110
127
  if (opts.help) {
111
128
  this.showHelp(Command.opts, Command.usage, commandName);
@@ -114,40 +131,53 @@ class Myt {
114
131
 
115
132
  // Check version
116
133
 
117
- let depVersion;
134
+ let matchVersion;
118
135
  const versionRegex = /^[^~]?([0-9]+)\.([0-9]+).([0-9]+)$/;
119
136
  const wsPackageFile = path.join(opts.workspace, 'package.json');
120
137
 
121
138
  if (await fs.pathExists(wsPackageFile)) {
139
+ let depVersion;
140
+ const depName = packageJson.name;
122
141
  const wsPackageJson = require(wsPackageFile);
142
+
123
143
  try {
124
- depVersion = wsPackageJson
125
- .dependencies[packageJson.name]
126
- .match(versionRegex);
144
+ const {
145
+ dependencies,
146
+ devDependencies
147
+ } = wsPackageJson;
148
+
149
+ if (dependencies)
150
+ depVersion = dependencies[depName];
151
+ if (!depVersion && devDependencies)
152
+ depVersion = devDependencies[depName];
127
153
  } catch (e) {}
154
+
155
+ if (typeof depVersion == 'string')
156
+ matchVersion = depVersion.match(versionRegex);
128
157
  }
129
158
 
130
- if (depVersion) {
159
+ if (matchVersion) {
131
160
  const myVersion = packageJson.version.match(versionRegex);
132
161
 
133
162
  const isSameVersion =
134
- depVersion[1] === myVersion[1] &&
135
- depVersion[2] === myVersion[2];
163
+ matchVersion[1] === myVersion[1] &&
164
+ matchVersion[2] === myVersion[2];
136
165
  if (!isSameVersion)
137
166
  throw new Error(`Myt version differs a lot from package.json, please run 'npm i' first to install the proper version.`);
138
167
 
139
- const isSameMinor = depVersion[3] === myVersion[3];
168
+ const isSameMinor = matchVersion[3] === myVersion[3];
140
169
  if (!isSameMinor)
141
170
  console.warn(`Warning! Myt minor version differs from package.json, maybe you shoud run 'npm i' to install the proper version.`.yellow);
142
171
  }
143
172
 
144
173
  // Load method
145
174
 
146
- parameter('Workspace:', opts.workspace);
147
- parameter('Remote:', opts.remote || 'local');
175
+ await this.init(opts, Command.skipConf);
176
+
177
+ parameter('Workspace:', this.cfg.workspace);
178
+ parameter('Remote:', this.cfg.remote || 'local');
148
179
 
149
- await this.init(opts);
150
- await this.run(Command, opts);
180
+ await this.run(Command, copts);
151
181
  await this.deinit();
152
182
  } catch (err) {
153
183
  if (err.name == 'Error' && !opts.debug) {
@@ -166,68 +196,93 @@ class Myt {
166
196
  process.exit();
167
197
  }
168
198
 
199
+ /**
200
+ * Run myt command.
201
+ * @param {Command} Command class reference
202
+ * @param {Object} opts Command options
203
+ * @returns Command execution result
204
+ */
169
205
  async run(Command, opts) {
170
- if (!opts) opts = this.opts;
171
206
  const command = new Command(this, opts);
172
207
  if (this.cliMode)
173
- return await command.cli(this, opts);
208
+ return await command.cli();
174
209
  else
175
- return await command.run(this, opts);
210
+ return await command.run();
176
211
  }
177
212
 
178
- async init(opts) {
213
+ /**
214
+ * Initialize myt, should be called before running any command.
215
+ * @param {Object} opts Myt options
216
+ * @param {boolean} skipConf Whether to skip configuration file loading
217
+ */
218
+ async init(opts, skipConf) {
219
+ const ctx = {version: packageJson.version};
220
+
221
+ // Myt directory
222
+
223
+ let subdir;
224
+ const configFile = 'myt.config.yml';
225
+
226
+ if (!skipConf) {
227
+ const checkDirs = ['', 'myt', 'db'];
228
+ for (const dir of checkDirs) {
229
+ const cfgPath = path.join(opts.workspace, dir, configFile);
230
+ if (await fs.pathExists(cfgPath)) {
231
+ subdir = dir;
232
+ break;
233
+ }
234
+ }
235
+
236
+ if (subdir == null)
237
+ throw new Error (`Cannot find Myt config file '${configFile}': ${JSON.stringify(checkDirs)}`);
238
+ } else {
239
+ subdir = '';
240
+ }
241
+
242
+ ctx.subdir = subdir;
243
+ const mytDir = ctx.mytDir = path.join(opts.workspace, subdir);
244
+
179
245
  // Configuration file
180
246
 
181
247
  const defaultConfig = require(`${__dirname}/assets/myt.default.yml`);
182
- const config = Object.assign({}, defaultConfig);
248
+ const cfg = Object.assign({}, defaultConfig);
183
249
 
184
- const configFile = 'myt.config.yml';
185
- const configPath = path.join(opts.workspace, configFile);
250
+ const configFiles = [
251
+ configFile,
252
+ 'myt.local.yml'
253
+ ];
254
+ const mergeKeys = new Set([
255
+ 'privileges'
256
+ ]);
186
257
 
187
- if (await fs.pathExists(configPath)) {
188
- const mergeKeys = new Set([
189
- 'privileges'
190
- ]);
258
+ for (const file of configFiles) {
259
+ const configPath = path.join(mytDir, file);
260
+ if (!await fs.pathExists(configPath)) continue;
191
261
 
192
262
  const wsConfig = require(configPath);
193
263
  for (const key in wsConfig) {
194
264
  if (!mergeKeys.has(key)) {
195
- config[key] = wsConfig[key];
265
+ cfg[key] = wsConfig[key];
196
266
  } else {
197
- config[key] = Object.assign({},
198
- config[key],
267
+ cfg[key] = Object.assign({},
268
+ cfg[key],
199
269
  wsConfig[key]
200
270
  );
201
271
  }
202
272
  }
203
273
  }
204
274
 
205
- Object.assign(opts, config);
206
- opts.configFile = configFile;
207
-
208
- const subdir = opts.subdir || '';
209
- if (!opts.mytDir)
210
- opts.mytDir = path.join(opts.workspace, subdir);
211
-
212
- const routinesBaseRegex = subdir
213
- ? `${subdir}\/routines`
214
- : 'routines';
215
-
216
- Object.assign(opts, {
217
- routinesRegex: new RegExp(`^${routinesBaseRegex}\/(.+)\.sql$`),
218
- routinesDir: path.join(opts.mytDir, 'routines'),
219
- versionsDir: path.join(opts.mytDir, 'versions'),
220
- dumpDir: path.join(opts.mytDir, 'dump')
221
- });
275
+ Object.assign(cfg, opts);
276
+ ctx.configFile = configFile;
222
277
 
223
278
  // Database configuration
224
279
 
225
280
  let iniDir = path.join(__dirname, 'assets');
226
281
  let iniFile = 'db.ini';
227
282
 
228
- if (opts.remote) {
229
- iniDir = `${opts.mytDir}/remotes`;
230
- iniFile = `${opts.remote}.ini`;
283
+ if (cfg.remote) {
284
+ iniDir = `${mytDir}/remotes`;
285
+ iniFile = `${cfg.remote}.ini`;
231
286
  }
232
287
  const iniPath = path.join(iniDir, iniFile);
233
288
 
@@ -244,12 +299,13 @@ class Myt {
244
299
  iniConfig[newKey] = value !== undefined ? value : true;
245
300
  }
246
301
  dbConfig = {
247
- host: iniConfig.host,
248
- port: iniConfig.port,
249
- user: iniConfig.user,
250
- password: iniConfig.password,
251
302
  multipleStatements: true
252
303
  };
304
+ const params = ['host', 'port', 'user', 'password'];
305
+ for (const param of params) {
306
+ if (iniConfig[param])
307
+ dbConfig[param] = iniConfig[param];
308
+ }
253
309
  if (iniConfig.enable_cleartext_plugin) {
254
310
  dbConfig.authPlugins = {
255
311
  mysql_clear_password() {
@@ -259,7 +315,7 @@ class Myt {
259
315
  }
260
316
  if (iniConfig.ssl_ca) {
261
317
  dbConfig.ssl = {
262
- ca: await fs.readFile(`${opts.mytDir}/${iniConfig.ssl_ca}`),
318
+ ca: await fs.readFile(`${mytDir}/${iniConfig.ssl_ca}`),
263
319
  rejectUnauthorized: iniConfig.ssl_verify_server_cert != undefined
264
320
  }
265
321
  }
@@ -272,11 +328,34 @@ class Myt {
272
328
  throw newErr;
273
329
  }
274
330
 
275
- Object.assign(opts, {
331
+ // Context configuration
332
+
333
+ const routinesBaseRegex = subdir
334
+ ? `${subdir}\/routines`
335
+ : 'routines';
336
+
337
+ Object.assign(ctx, {
276
338
  iniFile,
277
- dbConfig
339
+ routinesRegex: new RegExp(`^${routinesBaseRegex}\/(.+)\.sql$`),
340
+ routinesDir: path.join(mytDir, 'routines'),
341
+ versionsDir: path.join(mytDir, 'versions'),
342
+ structureDir: path.join(mytDir, 'structure'),
343
+ dumpDir: path.join(mytDir, 'structure', '.dump'),
344
+ fixturesDir: path.join(mytDir, 'fixtures'),
345
+ realmsDir: path.join(mytDir, 'realms'),
346
+ dockerDir: path.join(mytDir, 'docker'),
347
+ isProtectedRemote: cfg.protectedRemotes?.includes(cfg.remote),
348
+ isLocalRemote: cfg.localRemotes?.includes(cfg.remote)
278
349
  });
279
- this.opts = opts;
350
+
351
+ if (cfg.debug)
352
+ console.debug('Context:'.magenta, ctx);
353
+
354
+ // Don't print sensitive data when debugging
355
+ Object.assign(ctx, {dbConfig});
356
+
357
+ this.ctx = ctx;
358
+ this.cfg = cfg;
280
359
  }
281
360
 
282
361
  async deinit() {
@@ -284,62 +363,82 @@ class Myt {
284
363
  await this.conn.end();
285
364
  }
286
365
 
287
- getopts(opts) {
288
- const argv = process.argv.slice(2);
289
- const values = getopts(argv, opts);
290
- const cleanValues = {};
291
- for (const opt in values)
292
- if (opt.length > 1 || opt == '_')
293
- cleanValues[opt] = values[opt];
294
- return cleanValues;
366
+ showHelp(opts, usage, command) {
367
+ const prefix = `${'Usage:'.gray} [npx] myt`;
368
+
369
+ if (command) {
370
+ let log = [prefix, command.blue];
371
+ if (usage.operand) log.push(`[<${usage.operand}>]`);
372
+ if (opts) log.push('[<options>]');
373
+ console.log(log.join(' '))
374
+ } else
375
+ console.log(`${prefix} [<options>] ${'<command>'.blue} [<args>]`);
376
+
377
+ if (usage.description)
378
+ console.log(`${'Description:'.gray} ${usage.description}`);
379
+
380
+ if (opts && opts.alias) {
381
+ const alias = opts.alias;
382
+ const boolean = opts.boolean || [];
383
+
384
+ console.log('Options:'.gray);
385
+ for (const opt in alias) {
386
+ const paramDescription = usage.params[opt] || '';
387
+ let longOpt = opt;
388
+ if (boolean.indexOf(longOpt) === -1)
389
+ longOpt += ` <string>`;
390
+ longOpt = camelToSnake(longOpt).padEnd(22, ' ')
391
+ console.log(` -${alias[opt]}, --${longOpt} ${paramDescription}`);
392
+ }
393
+ }
295
394
  }
296
395
 
297
396
  async dbConnect() {
298
- const {opts} = this;
299
-
300
- if (!this.conn) {
301
- const conn = this.conn = await this.createConnection();
302
-
303
- const [[schema]] = await conn.query(
304
- `SHOW DATABASES LIKE ?`, [opts.versionSchema]
305
- );
306
-
307
- if (!schema)
308
- await conn.query(`CREATE DATABASE ??`, [opts.versionSchema]);
309
- await conn.query(`USE ??`, [opts.versionSchema]);
310
-
311
- const [[res]] = await conn.query(
312
- `SELECT COUNT(*) > 0 tableExists
313
- FROM information_schema.tables
314
- WHERE TABLE_SCHEMA = ?
315
- AND TABLE_NAME = 'version'`,
316
- [opts.versionSchema]
317
- );
318
-
319
- if (!res.tableExists) {
320
- const structure = await fs.readFile(
321
- `${__dirname}/assets/structure.sql`, 'utf8');
322
- await conn.query(structure);
323
- }
397
+ if (this.conn)
398
+ return this.conn;
324
399
 
325
- const [[versionConfig]] = await conn.query(
326
- `SELECT realm FROM versionConfig`
327
- );
328
- this.realm = versionConfig?.realm;
400
+ const {versionSchema} = this.cfg;
401
+ const conn = this.conn = await this.createConnection();
402
+
403
+ const [[schema]] = await conn.query(
404
+ `SHOW DATABASES LIKE ?`, [versionSchema]
405
+ );
406
+
407
+ if (!schema)
408
+ await conn.query(`CREATE DATABASE ??`, [versionSchema]);
409
+ await conn.query(`USE ??`, [versionSchema]);
410
+
411
+ const [[res]] = await conn.query(
412
+ `SELECT COUNT(*) > 0 tableExists
413
+ FROM information_schema.tables
414
+ WHERE TABLE_SCHEMA = ?
415
+ AND TABLE_NAME = 'version'`,
416
+ [versionSchema]
417
+ );
418
+
419
+ if (!res.tableExists) {
420
+ const structure = await fs.readFile(
421
+ `${__dirname}/assets/structure.sql`, 'utf8');
422
+ await conn.query(structure);
329
423
  }
330
424
 
425
+ const [[versionConfig]] = await conn.query(
426
+ `SELECT realm FROM versionConfig`
427
+ );
428
+ this.realm = versionConfig?.realm;
429
+
331
430
  return this.conn;
332
431
  }
333
432
 
334
433
  async createConnection() {
335
- return await mysql.createConnection(this.opts.dbConfig);
434
+ return await mysql.createConnection(this.ctx.dbConfig);
336
435
  }
337
436
 
338
437
  async fetchDbVersion() {
339
438
  const [[version]] = await this.conn.query(
340
439
  `SELECT number, gitCommit
341
440
  FROM version WHERE code = ?`,
342
- [this.opts.code]
441
+ [this.cfg.code]
343
442
  );
344
443
  return version;
345
444
  }
@@ -354,12 +453,12 @@ class Myt {
354
453
  }
355
454
 
356
455
  async loadVersion(versionDir) {
357
- const {opts} = this;
456
+ const {cfg, ctx} = this;
358
457
 
359
458
  const info = this.parseVersionDir(versionDir);
360
459
  if (!info) return null;
361
460
 
362
- const versionsDir = opts.versionsDir;
461
+ const versionsDir = ctx.versionsDir;
363
462
  const scriptsDir = `${versionsDir}/${versionDir}`;
364
463
  const scriptList = await fs.readdir(scriptsDir);
365
464
 
@@ -368,7 +467,7 @@ class Myt {
368
467
  FROM versionLog
369
468
  WHERE code = ?
370
469
  AND number = ?`,
371
- [opts.code, info.number]
470
+ [cfg.code, info.number]
372
471
  );
373
472
  const versionLog = new Map();
374
473
  res.map(x => versionLog.set(x.file, x));
@@ -386,14 +485,17 @@ class Myt {
386
485
  continue;
387
486
  }
388
487
 
488
+ const matchRegex = !!match;
389
489
  const logInfo = versionLog.get(file);
390
490
  const apply = !logInfo || logInfo.hasError;
491
+ const push = apply && matchRegex;
391
492
  if (apply) applyVersion = true;
392
493
 
393
494
  scripts.push({
394
495
  file,
395
- matchRegex: !!match,
396
- apply
496
+ matchRegex,
497
+ apply,
498
+ push
397
499
  });
398
500
  }
399
501
 
@@ -406,42 +508,76 @@ class Myt {
406
508
  }
407
509
 
408
510
  async openRepo() {
409
- const {opts} = this;
511
+ const {cfg} = this;
410
512
 
411
- if (!await fs.pathExists(`${opts.workspace}/.git`))
513
+ if (!await fs.pathExists(`${cfg.workspace}/.git`))
412
514
  throw new Error ('Git not initialized');
413
515
 
414
- return await nodegit.Repository.open(opts.workspace);
516
+ const nodegit = require('nodegit');
517
+ return await nodegit.Repository.open(cfg.workspace);
415
518
  }
416
519
 
417
- showHelp(opts, usage, command) {
418
- const prefix = `${'Usage:'.gray} [npx] myt`;
520
+ async getChanges(commitSha, committed) {
521
+ const repo = await this.openRepo();
522
+ const changes = [];
523
+ const changesMap = new Map();
524
+
525
+ const {ctx} = this;
526
+ async function pushChanges(diff) {
527
+ if (!diff) return;
528
+ const patches = await diff.patches();
529
+
530
+ for (const patch of patches) {
531
+ const path = patch.newFile().path();
532
+ const match = path.match(ctx.routinesRegex);
533
+ if (!match) continue;
534
+
535
+ let change = changesMap.get(match[1]);
536
+ if (!change) {
537
+ change = {path: match[1]};
538
+ changes.push(change);
539
+ changesMap.set(match[1], change);
540
+ }
541
+ change.mark = patch.isDeleted() ? '-' : '+';
542
+ }
543
+ }
419
544
 
420
- if (command) {
421
- let log = [prefix, command.blue];
422
- if (usage.operand) log.push(`[<${usage.operand}>]`);
423
- if (opts) log.push('[<options>]');
424
- console.log(log.join(' '))
425
- } else
426
- console.log(`${prefix} [<options>] ${'<command>'.blue} [<args>]`);
545
+ const head = await repo.getHeadCommit();
427
546
 
428
- if (usage.description)
429
- console.log(`${'Description:'.gray} ${usage.description}`);
547
+ if (head && commitSha) {
548
+ let commit;
549
+ let notFound;
430
550
 
431
- if (opts && opts.alias) {
432
- const alias = opts.alias;
433
- const boolean = opts.boolean || [];
551
+ try {
552
+ commit = await repo.getCommit(commitSha);
553
+ notFound = false;
554
+ } catch (err) {
555
+ if (err.errorFunction == 'Commit.lookup')
556
+ notFound = true;
557
+ else
558
+ throw err;
559
+ }
434
560
 
435
- console.log('Options:'.gray);
436
- for (const opt in alias) {
437
- const paramDescription = usage.params[opt] || '';
438
- let longOpt = opt;
439
- if (boolean.indexOf(longOpt) === -1)
440
- longOpt += ` <string>`;
441
- longOpt = camelToSnake(longOpt).padEnd(22, ' ')
442
- console.log(` -${alias[opt]}, --${longOpt} ${paramDescription}`);
561
+ if (notFound) {
562
+ console.warn(`Database commit not found, trying git fetch`.yellow);
563
+ await repo.fetchAll();
564
+ commit = await repo.getCommit(commitSha);
443
565
  }
566
+
567
+ const commitTree = await commit.getTree();
568
+
569
+ const headTree = await head.getTree();
570
+ const diff = await headTree.diff(commitTree);
571
+ await pushChanges(diff);
444
572
  }
573
+
574
+ if (!committed) {
575
+ const repoExt = require('./lib/repo');
576
+ await pushChanges(await repoExt.getUnstaged(repo));
577
+ await pushChanges(await repoExt.getStaged(repo));
578
+ }
579
+
580
+ return changes;
445
581
  }
446
582
  }
447
583
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@verdnatura/myt",
3
- "version": "1.8.6",
3
+ "version": "2.0.2",
4
4
  "author": "Verdnatura Levante SL",
5
5
  "description": "MySQL version control",
6
6
  "license": "GPL-3.0",
@@ -14,16 +14,17 @@
14
14
  "engines": {
15
15
  "node": ">=20"
16
16
  },
17
- "packageManager": "pnpm@10.28.2",
17
+ "packageManager": "pnpm@10.33.0",
18
18
  "dependencies": {
19
19
  "@sqltools/formatter": "^1.2.5",
20
20
  "colors": "^1.4.0",
21
21
  "ejs": "^3.1.9",
22
+ "folder-hash": "^4.1.2",
22
23
  "fs-extra": "^11.1.1",
23
24
  "getopts": "^2.3.0",
24
25
  "ini": "^4.1.1",
25
26
  "mysql2": "^3.3.4",
26
- "nodegit": "0.28.0-alpha.28",
27
+ "nodegit": "0.28.0-alpha.36",
27
28
  "require-yaml": "^0.0.1",
28
29
  "sha.js": "^2.4.11",
29
30
  "sqlstring": "^2.3.3"