@verdnatura/myt 1.5.6 → 1.5.7
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/lib/command.js +3 -6
- package/lib/conn.js +141 -0
- package/lib/dumper.js +83 -0
- package/lib/repo.js +29 -0
- package/lib/util.js +3 -2
- package/myt-clean.js +1 -2
- package/myt-dump.js +10 -9
- package/myt-fixtures.js +6 -5
- package/myt-init.js +0 -1
- package/myt-pull.js +4 -4
- package/myt-push.js +68 -5
- package/myt-run.js +3 -3
- package/myt-start.js +0 -1
- package/myt-version.js +1 -2
- package/myt.js +45 -337
- package/package.json +1 -1
- package/server/docker-dump.sh +1 -1
- package/template/package.json +1 -1
- package/template/remotes/docker.ini +5 -0
package/lib/command.js
CHANGED
package/lib/conn.js
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
const fs = require('fs-extra');
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Executes an SQL script.
|
|
5
|
+
*
|
|
6
|
+
* @param {Connection} conn MySQL connection object
|
|
7
|
+
* @returns {Array<Result>} The resultset
|
|
8
|
+
*/
|
|
9
|
+
async function queryFromFile(conn, file) {
|
|
10
|
+
const sql = await fs.readFile(file, 'utf8');
|
|
11
|
+
return await this.multiQuery(conn, sql);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Executes a multi-query string.
|
|
16
|
+
*
|
|
17
|
+
* @param {Connection} conn MySQL connection object
|
|
18
|
+
* @param {String} sql SQL multi-query string
|
|
19
|
+
* @returns {Array<Result>} The resultset
|
|
20
|
+
*/
|
|
21
|
+
async function multiQuery(conn, sql) {
|
|
22
|
+
let results = [];
|
|
23
|
+
const stmts = this.querySplit(sql);
|
|
24
|
+
|
|
25
|
+
for (const stmt of stmts)
|
|
26
|
+
results = results.concat(await conn.query(stmt));
|
|
27
|
+
|
|
28
|
+
return results;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Splits an SQL muti-query into a single-query array, it does an small
|
|
33
|
+
* parse to correctly handle the DELIMITER statement.
|
|
34
|
+
*
|
|
35
|
+
* @param {Array<String>} stmts The splitted SQL statements
|
|
36
|
+
*/
|
|
37
|
+
function querySplit(sql) {
|
|
38
|
+
const stmts = [];
|
|
39
|
+
let i,
|
|
40
|
+
char,
|
|
41
|
+
token,
|
|
42
|
+
escaped,
|
|
43
|
+
stmtStart;
|
|
44
|
+
|
|
45
|
+
let delimiter = ';';
|
|
46
|
+
const delimiterRe = /\s*delimiter\s+(\S+)[^\S\r\n]*(?:\r?\n|\r|$)/yi;
|
|
47
|
+
|
|
48
|
+
function begins(str) {
|
|
49
|
+
let j;
|
|
50
|
+
for (j = 0; j < str.length; j++)
|
|
51
|
+
if (sql[i + j] != str[j])
|
|
52
|
+
return false;
|
|
53
|
+
i += j;
|
|
54
|
+
return true;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
for (i = 0; i < sql.length;) {
|
|
58
|
+
stmtStart = i;
|
|
59
|
+
|
|
60
|
+
delimiterRe.lastIndex = i;
|
|
61
|
+
const match = sql.match(delimiterRe);
|
|
62
|
+
if (match) {
|
|
63
|
+
delimiter = match[1];
|
|
64
|
+
i += match[0].length;
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
let delimiterFound = false;
|
|
69
|
+
while (i < sql.length) {
|
|
70
|
+
char = sql[i];
|
|
71
|
+
|
|
72
|
+
if (token) {
|
|
73
|
+
if (!escaped && begins(token.end))
|
|
74
|
+
token = null;
|
|
75
|
+
else {
|
|
76
|
+
escaped = !escaped && token.escape(char);
|
|
77
|
+
i++;
|
|
78
|
+
}
|
|
79
|
+
} else {
|
|
80
|
+
delimiterFound = begins(delimiter);
|
|
81
|
+
if (delimiterFound) break;
|
|
82
|
+
|
|
83
|
+
const tok = tokenIndex.get(char);
|
|
84
|
+
if (tok && begins(tok.start))
|
|
85
|
+
token = tok;
|
|
86
|
+
else
|
|
87
|
+
i++;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
let len = i - stmtStart;
|
|
92
|
+
if (delimiterFound) len -= delimiter.length;
|
|
93
|
+
const stmt = sql.substr(stmtStart, len);
|
|
94
|
+
|
|
95
|
+
if (!/^\s*$/.test(stmt))
|
|
96
|
+
stmts.push(stmt);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return stmts;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const tokens = {
|
|
103
|
+
string: {
|
|
104
|
+
start: '\'',
|
|
105
|
+
end: '\'',
|
|
106
|
+
escape: char => char == '\'' || char == '\\'
|
|
107
|
+
},
|
|
108
|
+
quotedString: {
|
|
109
|
+
start: '"',
|
|
110
|
+
end: '"',
|
|
111
|
+
escape: char => char == '"' || char == '\\'
|
|
112
|
+
},
|
|
113
|
+
id: {
|
|
114
|
+
start: '`',
|
|
115
|
+
end: '`',
|
|
116
|
+
escape: char => char == '`'
|
|
117
|
+
},
|
|
118
|
+
multiComment: {
|
|
119
|
+
start: '/*',
|
|
120
|
+
end: '*/',
|
|
121
|
+
escape: () => false
|
|
122
|
+
},
|
|
123
|
+
singleComment: {
|
|
124
|
+
start: '-- ',
|
|
125
|
+
end: '\n',
|
|
126
|
+
escape: () => false
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
const tokenIndex = new Map();
|
|
131
|
+
for (const tokenId in tokens) {
|
|
132
|
+
const token = tokens[tokenId];
|
|
133
|
+
tokenIndex.set(token.start[0], token);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
module.exports = {
|
|
137
|
+
queryFromFile,
|
|
138
|
+
multiQuery,
|
|
139
|
+
querySplit,
|
|
140
|
+
tokens
|
|
141
|
+
};
|
package/lib/dumper.js
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
const docker = require('./docker');
|
|
2
|
+
const fs = require('fs-extra');
|
|
3
|
+
const path = require('path');
|
|
4
|
+
|
|
5
|
+
module.exports = class Dumper {
|
|
6
|
+
constructor(opts) {
|
|
7
|
+
this.opts = opts;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
async init(dumpFile) {
|
|
11
|
+
const dumpDir = this.opts.dumpDir;
|
|
12
|
+
if (!await fs.pathExists(dumpDir))
|
|
13
|
+
await fs.mkdir(dumpDir);
|
|
14
|
+
|
|
15
|
+
const dumpPath = path.join(dumpDir, dumpFile);
|
|
16
|
+
|
|
17
|
+
// FIXME: If it's called after docker.build() statement it creates an
|
|
18
|
+
// "invalid" WriteStream
|
|
19
|
+
const dumpStream = await fs.createWriteStream(dumpPath);
|
|
20
|
+
|
|
21
|
+
const buidDir = path.join(__dirname, '..',)
|
|
22
|
+
await docker.build(buidDir, {
|
|
23
|
+
tag: 'myt/client',
|
|
24
|
+
file: path.join(buidDir, 'server', 'Dockerfile.client')
|
|
25
|
+
}, this.opts.debug);
|
|
26
|
+
|
|
27
|
+
this.dumpStream = dumpStream;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
async use(schema) {
|
|
31
|
+
const escapedSchema = '`'+ schema.replace('`', '``') +'`';
|
|
32
|
+
await this.dumpStream.write(
|
|
33
|
+
`USE ${escapedSchema};\n`,
|
|
34
|
+
'utf8'
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async dumpFixtures(tables, replace) {
|
|
39
|
+
const fixturesArgs = [
|
|
40
|
+
'--no-create-info',
|
|
41
|
+
'--skip-triggers',
|
|
42
|
+
'--skip-extended-insert',
|
|
43
|
+
'--skip-disable-keys',
|
|
44
|
+
'--skip-add-locks',
|
|
45
|
+
'--skip-set-charset',
|
|
46
|
+
'--skip-comments',
|
|
47
|
+
'--skip-tz-utc'
|
|
48
|
+
];
|
|
49
|
+
|
|
50
|
+
if (replace)
|
|
51
|
+
fixturesArgs.push('--replace');
|
|
52
|
+
|
|
53
|
+
for (const schema in tables) {
|
|
54
|
+
await this.use(schema);
|
|
55
|
+
const args = fixturesArgs.concat([schema], tables[schema]);
|
|
56
|
+
await this.runDump('mysqldump', args, this.dumpStream);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
async runDump(command, args) {
|
|
61
|
+
const iniPath = path.join(this.opts.subdir || '', 'remotes', this.opts.iniFile);
|
|
62
|
+
const myArgs = [
|
|
63
|
+
`--defaults-file=${iniPath}`
|
|
64
|
+
];
|
|
65
|
+
const execOptions = {
|
|
66
|
+
stdio: [
|
|
67
|
+
process.stdin,
|
|
68
|
+
this.dumpStream,
|
|
69
|
+
process.stderr
|
|
70
|
+
]
|
|
71
|
+
};
|
|
72
|
+
const commandArgs = [command].concat(myArgs, args);
|
|
73
|
+
await docker.run('myt/client', commandArgs, {
|
|
74
|
+
addHost: 'host.docker.internal:host-gateway',
|
|
75
|
+
volume: `${this.opts.mytDir}:/workspace`,
|
|
76
|
+
rm: true
|
|
77
|
+
}, execOptions);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
async end() {
|
|
81
|
+
await this.dumpStream.end();
|
|
82
|
+
}
|
|
83
|
+
}
|
package/lib/repo.js
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
const nodegit = require('nodegit');
|
|
2
|
+
|
|
3
|
+
async function getStaged(repo) {
|
|
4
|
+
const head = await repo.getHeadCommit();
|
|
5
|
+
|
|
6
|
+
try {
|
|
7
|
+
const emptyTree = '4b825dc642cb6eb9a060e54bf8d69288fbee4904';
|
|
8
|
+
const headTree = await (head
|
|
9
|
+
? head.getTree()
|
|
10
|
+
: nodegit.Tree.lookup(repo, emptyTree)
|
|
11
|
+
);
|
|
12
|
+
return await nodegit.Diff.treeToIndex(repo, headTree, null);
|
|
13
|
+
} catch (err) {
|
|
14
|
+
console.warn('Cannot fetch staged changes:', err.message);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
async function getUnstaged(repo) {
|
|
19
|
+
const Diff = nodegit.Diff;
|
|
20
|
+
return await Diff.indexToWorkdir(repo, null, {
|
|
21
|
+
flags: Diff.OPTION.SHOW_UNTRACKED_CONTENT
|
|
22
|
+
| Diff.OPTION.RECURSE_UNTRACKED_DIRS
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
module.exports = {
|
|
27
|
+
getStaged,
|
|
28
|
+
getUnstaged
|
|
29
|
+
};
|
package/lib/util.js
CHANGED
package/myt-clean.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
|
|
2
1
|
const Myt = require('./myt');
|
|
3
2
|
const Command = require('./lib/command');
|
|
4
3
|
const fs = require('fs-extra');
|
|
@@ -11,7 +10,7 @@ class Clean extends Command {
|
|
|
11
10
|
description: 'Cleans old applied versions'
|
|
12
11
|
};
|
|
13
12
|
|
|
14
|
-
static
|
|
13
|
+
static opts = {
|
|
15
14
|
default: {
|
|
16
15
|
remote: 'production'
|
|
17
16
|
}
|
package/myt-dump.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
|
|
2
1
|
const Myt = require('./myt');
|
|
3
2
|
const Command = require('./lib/command');
|
|
4
3
|
const fs = require('fs-extra');
|
|
4
|
+
const Dumper = require('./lib/dumper');
|
|
5
5
|
|
|
6
6
|
class Dump extends Command {
|
|
7
7
|
static usage = {
|
|
@@ -9,14 +9,15 @@ class Dump extends Command {
|
|
|
9
9
|
operand: 'remote'
|
|
10
10
|
};
|
|
11
11
|
|
|
12
|
-
static
|
|
12
|
+
static opts = {
|
|
13
13
|
default: {
|
|
14
14
|
remote: 'production'
|
|
15
15
|
}
|
|
16
16
|
};
|
|
17
17
|
|
|
18
18
|
async run(myt, opts) {
|
|
19
|
-
const
|
|
19
|
+
const dumper = new Dumper(opts);
|
|
20
|
+
await dumper.init('.dump.sql');
|
|
20
21
|
|
|
21
22
|
console.log('Dumping structure.');
|
|
22
23
|
let dumpArgs = [
|
|
@@ -29,10 +30,10 @@ class Dump extends Command {
|
|
|
29
30
|
'--databases'
|
|
30
31
|
];
|
|
31
32
|
dumpArgs = dumpArgs.concat(opts.schemas);
|
|
32
|
-
await
|
|
33
|
+
await dumper.runDump('docker-dump.sh', dumpArgs);
|
|
33
34
|
|
|
34
35
|
console.log('Dumping fixtures.');
|
|
35
|
-
await
|
|
36
|
+
await dumper.dumpFixtures(opts.fixtures);
|
|
36
37
|
|
|
37
38
|
console.log('Dumping privileges.');
|
|
38
39
|
const privs = opts.privileges;
|
|
@@ -45,12 +46,12 @@ class Dump extends Command {
|
|
|
45
46
|
];
|
|
46
47
|
if (privs.where) args.push('--where', privs.where);
|
|
47
48
|
args = args.concat(['mysql'], privs.tables);
|
|
48
|
-
|
|
49
|
-
await
|
|
50
|
-
await
|
|
49
|
+
|
|
50
|
+
await dumper.use('mysql');
|
|
51
|
+
await dumper.runDump('mysqldump', args);
|
|
51
52
|
}
|
|
52
53
|
|
|
53
|
-
await
|
|
54
|
+
await dumper.end();
|
|
54
55
|
|
|
55
56
|
console.log('Saving version.');
|
|
56
57
|
await myt.dbConnect();
|
package/myt-fixtures.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
|
|
2
1
|
const Myt = require('./myt');
|
|
3
2
|
const Command = require('./lib/command');
|
|
3
|
+
const Dumper = require('./lib/dumper');
|
|
4
4
|
|
|
5
5
|
class Fixtures extends Command {
|
|
6
6
|
static usage = {
|
|
@@ -8,16 +8,17 @@ class Fixtures extends Command {
|
|
|
8
8
|
operand: 'remote'
|
|
9
9
|
};
|
|
10
10
|
|
|
11
|
-
static
|
|
11
|
+
static opts = {
|
|
12
12
|
default: {
|
|
13
13
|
remote: 'docker'
|
|
14
14
|
}
|
|
15
15
|
};
|
|
16
16
|
|
|
17
17
|
async run(myt, opts) {
|
|
18
|
-
const
|
|
19
|
-
await
|
|
20
|
-
await
|
|
18
|
+
const dumper = new Dumper(opts);
|
|
19
|
+
await dumper.init('fixtures.sql');
|
|
20
|
+
await dumper.dumpFixtures(opts.localFixtures, true);
|
|
21
|
+
await dumper.end();
|
|
21
22
|
}
|
|
22
23
|
}
|
|
23
24
|
|
package/myt-init.js
CHANGED
package/myt-pull.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
|
|
2
1
|
const Myt = require('./myt');
|
|
3
2
|
const Command = require('./lib/command');
|
|
4
3
|
const fs = require('fs-extra');
|
|
5
4
|
const nodegit = require('nodegit');
|
|
6
5
|
const ExporterEngine = require('./lib/exporter-engine');
|
|
6
|
+
const repoExt = require('./lib/repo');
|
|
7
7
|
|
|
8
8
|
class Pull extends Command {
|
|
9
9
|
static usage = {
|
|
@@ -17,7 +17,7 @@ class Pull extends Command {
|
|
|
17
17
|
operand: 'remote'
|
|
18
18
|
};
|
|
19
19
|
|
|
20
|
-
static
|
|
20
|
+
static opts = {
|
|
21
21
|
alias: {
|
|
22
22
|
force: 'f',
|
|
23
23
|
checkout: 'c',
|
|
@@ -52,14 +52,14 @@ class Pull extends Command {
|
|
|
52
52
|
|
|
53
53
|
// Check for unstaged changes
|
|
54
54
|
|
|
55
|
-
const unstagedDiff = await
|
|
55
|
+
const unstagedDiff = await repoExt.getUnstaged(repo);
|
|
56
56
|
|
|
57
57
|
if (await hasChanges(unstagedDiff))
|
|
58
58
|
throw new Error('You have unstaged changes, save them before pull');
|
|
59
59
|
|
|
60
60
|
// Check for staged changes
|
|
61
61
|
|
|
62
|
-
const stagedDiff = await
|
|
62
|
+
const stagedDiff = await repoExt.getStaged(repo);
|
|
63
63
|
|
|
64
64
|
if (await hasChanges(stagedDiff))
|
|
65
65
|
throw new Error('You have staged changes, save them before pull');
|
package/myt-push.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
|
|
2
1
|
const Myt = require('./myt');
|
|
3
2
|
const Command = require('./lib/command');
|
|
4
3
|
const fs = require('fs-extra');
|
|
5
4
|
const nodegit = require('nodegit');
|
|
6
5
|
const ExporterEngine = require('./lib/exporter-engine');
|
|
6
|
+
const connExt = require('./lib/conn');
|
|
7
|
+
const repoExt = require('./lib/repo');
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
10
|
* Pushes changes to remote.
|
|
@@ -20,7 +21,7 @@ class Push extends Command {
|
|
|
20
21
|
operand: 'remote'
|
|
21
22
|
};
|
|
22
23
|
|
|
23
|
-
static
|
|
24
|
+
static opts = {
|
|
24
25
|
alias: {
|
|
25
26
|
force: 'f',
|
|
26
27
|
commit: 'c',
|
|
@@ -217,7 +218,7 @@ class Push extends Command {
|
|
|
217
218
|
|
|
218
219
|
let err;
|
|
219
220
|
try {
|
|
220
|
-
await
|
|
221
|
+
await connExt.queryFromFile(pushConn,
|
|
221
222
|
`${scriptsDir}/${script}`);
|
|
222
223
|
} catch (e) {
|
|
223
224
|
err = e;
|
|
@@ -261,7 +262,7 @@ class Push extends Command {
|
|
|
261
262
|
const gitExists = await fs.pathExists(`${opts.workspace}/.git`);
|
|
262
263
|
|
|
263
264
|
let nRoutines = 0;
|
|
264
|
-
let changes = await
|
|
265
|
+
let changes = await this.changedRoutines(version.gitCommit);
|
|
265
266
|
changes = this.parseChanges(changes);
|
|
266
267
|
|
|
267
268
|
const routines = [];
|
|
@@ -334,7 +335,7 @@ class Push extends Command {
|
|
|
334
335
|
if (change.type.name === 'VIEW')
|
|
335
336
|
await pushConn.query(`USE ${scapedSchema}`);
|
|
336
337
|
|
|
337
|
-
await
|
|
338
|
+
await connExt.multiQuery(pushConn, newSql);
|
|
338
339
|
|
|
339
340
|
if (change.isRoutine) {
|
|
340
341
|
await conn.query(
|
|
@@ -416,6 +417,68 @@ class Push extends Command {
|
|
|
416
417
|
);
|
|
417
418
|
}
|
|
418
419
|
|
|
420
|
+
async changedRoutines(commitSha) {
|
|
421
|
+
const repo = await this.myt.openRepo();
|
|
422
|
+
const changes = [];
|
|
423
|
+
const changesMap = new Map();
|
|
424
|
+
|
|
425
|
+
async function pushChanges(diff) {
|
|
426
|
+
if (!diff) return;
|
|
427
|
+
const patches = await diff.patches();
|
|
428
|
+
|
|
429
|
+
for (const patch of patches) {
|
|
430
|
+
const path = patch.newFile().path();
|
|
431
|
+
const match = path.match(/^routines\/(.+)\.sql$/);
|
|
432
|
+
if (!match) continue;
|
|
433
|
+
|
|
434
|
+
let change = changesMap.get(match[1]);
|
|
435
|
+
if (!change) {
|
|
436
|
+
change = {path: match[1]};
|
|
437
|
+
changes.push(change);
|
|
438
|
+
changesMap.set(match[1], change);
|
|
439
|
+
}
|
|
440
|
+
change.mark = patch.isDeleted() ? '-' : '+';
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
const head = await repo.getHeadCommit();
|
|
445
|
+
|
|
446
|
+
if (head && commitSha) {
|
|
447
|
+
let commit;
|
|
448
|
+
let notFound;
|
|
449
|
+
|
|
450
|
+
try {
|
|
451
|
+
commit = await repo.getCommit(commitSha);
|
|
452
|
+
notFound = false;
|
|
453
|
+
} catch (err) {
|
|
454
|
+
if (err.errorFunction == 'Commit.lookup')
|
|
455
|
+
notFound = true;
|
|
456
|
+
else
|
|
457
|
+
throw err;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
if (notFound) {
|
|
461
|
+
console.warn(`Database commit not found, trying git fetch`.yellow);
|
|
462
|
+
await repo.fetchAll();
|
|
463
|
+
commit = await repo.getCommit(commitSha);
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
const commitTree = await commit.getTree();
|
|
467
|
+
|
|
468
|
+
const headTree = await head.getTree();
|
|
469
|
+
const diff = await headTree.diff(commitTree);
|
|
470
|
+
await pushChanges(diff);
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
await pushChanges(await repoExt.getUnstaged(repo));
|
|
474
|
+
await pushChanges(await repoExt.getStaged(repo));
|
|
475
|
+
|
|
476
|
+
return changes.sort((a, b) => {
|
|
477
|
+
if (b.mark != a.mark)
|
|
478
|
+
return b.mark == '-' ? 1 : -1;
|
|
479
|
+
return a.path.localeCompare(b.path);
|
|
480
|
+
});
|
|
481
|
+
}
|
|
419
482
|
}
|
|
420
483
|
|
|
421
484
|
const typeMap = {
|
package/myt-run.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
|
|
2
1
|
const Myt = require('./myt');
|
|
3
2
|
const Command = require('./lib/command');
|
|
4
3
|
const Push = require('./myt-push');
|
|
@@ -6,6 +5,7 @@ const docker = require('./lib/docker');
|
|
|
6
5
|
const fs = require('fs-extra');
|
|
7
6
|
const path = require('path');
|
|
8
7
|
const Server = require('./lib/server');
|
|
8
|
+
const connExt = require('./lib/conn');
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* Builds the database image and runs a container. It only rebuilds the
|
|
@@ -22,7 +22,7 @@ class Run extends Command {
|
|
|
22
22
|
}
|
|
23
23
|
};
|
|
24
24
|
|
|
25
|
-
static
|
|
25
|
+
static opts = {
|
|
26
26
|
alias: {
|
|
27
27
|
ci: 'c',
|
|
28
28
|
random: 'r'
|
|
@@ -144,7 +144,7 @@ class Run extends Command {
|
|
|
144
144
|
|
|
145
145
|
const triggersDir = await fs.readdir(triggersPath);
|
|
146
146
|
for (const triggerFile of triggersDir)
|
|
147
|
-
await
|
|
147
|
+
await connExt.queryFromFile(conn, `${triggersPath}/${triggerFile}`);
|
|
148
148
|
}
|
|
149
149
|
|
|
150
150
|
return server;
|
package/myt-start.js
CHANGED
package/myt-version.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
|
|
2
1
|
const Myt = require('./myt');
|
|
3
2
|
const Command = require('./lib/command');
|
|
4
3
|
const fs = require('fs-extra');
|
|
@@ -15,7 +14,7 @@ class Version extends Command {
|
|
|
15
14
|
operand: 'name'
|
|
16
15
|
};
|
|
17
16
|
|
|
18
|
-
static
|
|
17
|
+
static opts = {
|
|
19
18
|
alias: {
|
|
20
19
|
name: 'n'
|
|
21
20
|
},
|
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
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
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
|
-
|
|
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 (!
|
|
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
|
-
|
|
73
|
+
Command = require(commandFile);
|
|
80
74
|
}
|
|
81
75
|
|
|
82
|
-
if (!
|
|
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 (
|
|
90
|
-
for (const key in
|
|
83
|
+
if (Command.opts)
|
|
84
|
+
for (const key in Command.opts) {
|
|
91
85
|
const baseValue = baseOpts[key];
|
|
92
|
-
const cmdValue =
|
|
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 =
|
|
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(
|
|
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(
|
|
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(
|
|
173
|
-
const command = new
|
|
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
package/server/docker-dump.sh
CHANGED
package/template/package.json
CHANGED