@verdnatura/myt 1.8.6 → 2.0.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.
- package/README.md +79 -51
- package/assets/myt.default.yml +7 -0
- package/assets/structure.sql +1 -1
- package/{server/Dockerfile.client → docker/client/Dockerfile} +3 -3
- package/docker/server/Dockerfile +37 -0
- package/docker/server/Dockerfile.base +6 -0
- package/docker/server/Dockerfile.myt +38 -0
- package/docker/server/docker-init.sh +36 -0
- package/{server → docker/server}/docker-start.sh +1 -1
- package/lib/command.js +14 -6
- package/lib/docker.js +60 -34
- package/lib/dumper.js +11 -9
- package/lib/exporter-engine.js +4 -4
- package/lib/server.js +2 -8
- package/myt-apply.js +203 -0
- package/myt-build.js +284 -0
- package/myt-clean.js +15 -11
- package/myt-create.js +19 -11
- package/myt-dump.js +41 -36
- package/myt-fixtures.js +6 -6
- package/myt-init.js +5 -3
- package/myt-pull.js +12 -11
- package/myt-push.js +99 -96
- package/myt-run.js +28 -144
- package/myt-start.js +4 -4
- package/myt-version.js +13 -13
- package/myt.js +285 -149
- package/package.json +4 -3
- package/prebuilts/README.md +15 -0
- package/prebuilts/nodegit-v0.28.0-alpha.36-node-v127-linux-x64.tar.gz +0 -0
- package/template/.dockerignore +5 -0
- package/template/myt.config.yml +1 -4
- package/template/package.json +1 -1
- package/template/realms/.keep +0 -0
- package/template/remotes/docker.ini +1 -1
- package/template/remotes/local.ini +1 -1
- package/template/remotes/socket.ini +3 -0
- package/template/structure/.dump/structure.sql +32 -0
- package/server/Dockerfile.base +0 -1
- package/server/Dockerfile.dump +0 -16
- package/server/Dockerfile.server +0 -16
- package/server/docker-import.sh +0 -10
- package/server/docker-init.sh +0 -24
- /package/{server → docker/client}/docker-dump.sh +0 -0
- /package/{server → docker/client}/docker-fixtures.sh +0 -0
- /package/{server → docker/server}/docker.cnf +0 -0
- /package/template/{dump/Dockerfile.base → docker/Dockerfile} +0 -0
- /package/template/{dump/fixtures.after.sql → fixtures/after.sql} +0 -0
- /package/template/{dump/fixtures.before.sql → fixtures/before.sql} +0 -0
- /package/template/{dump/dump.after.sql → structure/after.sql} +0 -0
- /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
|
|
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
|
-
|
|
53
|
-
|
|
59
|
+
const args = Object.assign({}, this.constructor.args);
|
|
60
|
+
args.default = Object.assign(args.default || {}, {
|
|
54
61
|
workspace: process.cwd()
|
|
55
62
|
});
|
|
56
|
-
const
|
|
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(
|
|
88
|
+
this.showHelp(args, this.constructor.usage);
|
|
81
89
|
process.exit(0);
|
|
82
90
|
}
|
|
83
91
|
|
|
84
|
-
const
|
|
92
|
+
const allArgs = Object.assign({}, args);
|
|
85
93
|
|
|
86
|
-
if (Command.
|
|
87
|
-
for (const key in Command.
|
|
88
|
-
const baseValue =
|
|
89
|
-
const cmdValue = Command.
|
|
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
|
-
|
|
99
|
+
allArgs[key] = baseValue.concat(cmdValue);
|
|
92
100
|
else if (typeof baseValue == 'object')
|
|
93
|
-
|
|
101
|
+
allArgs[key] = Object.assign({}, baseValue, cmdValue);
|
|
94
102
|
else
|
|
95
|
-
|
|
103
|
+
allArgs[key] = cmdValue;
|
|
96
104
|
}
|
|
97
105
|
|
|
98
|
-
const
|
|
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 (
|
|
105
|
-
|
|
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
|
-
|
|
108
|
-
|
|
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
|
|
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
|
-
|
|
125
|
-
|
|
126
|
-
|
|
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 (
|
|
159
|
+
if (matchVersion) {
|
|
131
160
|
const myVersion = packageJson.version.match(versionRegex);
|
|
132
161
|
|
|
133
162
|
const isSameVersion =
|
|
134
|
-
|
|
135
|
-
|
|
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 =
|
|
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
|
-
|
|
147
|
-
|
|
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.
|
|
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(
|
|
208
|
+
return await command.cli();
|
|
174
209
|
else
|
|
175
|
-
return await command.run(
|
|
210
|
+
return await command.run();
|
|
176
211
|
}
|
|
177
212
|
|
|
178
|
-
|
|
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
|
|
248
|
+
const cfg = Object.assign({}, defaultConfig);
|
|
183
249
|
|
|
184
|
-
const
|
|
185
|
-
|
|
250
|
+
const configFiles = [
|
|
251
|
+
configFile,
|
|
252
|
+
'myt.local.yml'
|
|
253
|
+
];
|
|
254
|
+
const mergeKeys = new Set([
|
|
255
|
+
'privileges'
|
|
256
|
+
]);
|
|
186
257
|
|
|
187
|
-
|
|
188
|
-
const
|
|
189
|
-
|
|
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
|
-
|
|
265
|
+
cfg[key] = wsConfig[key];
|
|
196
266
|
} else {
|
|
197
|
-
|
|
198
|
-
|
|
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(
|
|
206
|
-
|
|
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 (
|
|
229
|
-
iniDir = `${
|
|
230
|
-
iniFile = `${
|
|
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(`${
|
|
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
|
-
|
|
331
|
+
// Context configuration
|
|
332
|
+
|
|
333
|
+
const routinesBaseRegex = subdir
|
|
334
|
+
? `${subdir}\/routines`
|
|
335
|
+
: 'routines';
|
|
336
|
+
|
|
337
|
+
Object.assign(ctx, {
|
|
276
338
|
iniFile,
|
|
277
|
-
|
|
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
|
-
|
|
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
|
-
|
|
288
|
-
const
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
if (
|
|
293
|
-
|
|
294
|
-
|
|
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
|
-
|
|
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
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
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.
|
|
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.
|
|
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 {
|
|
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 =
|
|
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
|
-
[
|
|
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
|
|
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 {
|
|
511
|
+
const {cfg} = this;
|
|
410
512
|
|
|
411
|
-
if (!await fs.pathExists(`${
|
|
513
|
+
if (!await fs.pathExists(`${cfg.workspace}/.git`))
|
|
412
514
|
throw new Error ('Git not initialized');
|
|
413
515
|
|
|
414
|
-
|
|
516
|
+
const nodegit = require('nodegit');
|
|
517
|
+
return await nodegit.Repository.open(cfg.workspace);
|
|
415
518
|
}
|
|
416
519
|
|
|
417
|
-
|
|
418
|
-
const
|
|
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
|
-
|
|
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 (
|
|
429
|
-
|
|
547
|
+
if (head && commitSha) {
|
|
548
|
+
let commit;
|
|
549
|
+
let notFound;
|
|
430
550
|
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
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
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
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": "
|
|
3
|
+
"version": "2.0.1",
|
|
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.
|
|
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.
|
|
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"
|