@verdnatura/myt 1.5.20 → 1.5.21
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 +11 -5
- package/assets/myt.default.yml +17 -0
- package/helpers/local-fixtures.js +79 -0
- package/lib/command.js +6 -0
- package/lib/dumper.js +34 -13
- package/myt-dump.js +81 -22
- package/myt-fixtures.js +2 -3
- package/myt-pull.js +1 -1
- package/myt-push.js +11 -4
- package/myt-run.js +44 -31
- package/myt-version.js +1 -1
- package/myt.js +20 -3
- package/package.json +4 -4
- package/server/Dockerfile.dump +2 -5
- package/server/docker-fixtures.sh +1 -0
- package/server/docker-import.sh +4 -0
- package/server/docker-init.sh +6 -3
- package/template/dump/dump.after.sql +1 -0
- package/template/dump/dump.before.sql +1 -0
- package/template/dump/fixtures.after.sql +1 -0
- package/template/dump/fixtures.before.sql +1 -0
- package/template/dump/afterDump.sql +0 -1
- package/template/dump/beforeDump.sql +0 -1
- package/template/dump/fixtures.sql +0 -1
package/README.md
CHANGED
|
@@ -13,6 +13,11 @@ development, so any help is welcomed! Feel free to contribute.
|
|
|
13
13
|
|
|
14
14
|
## Installation
|
|
15
15
|
|
|
16
|
+
Required libraries to build with *node-gyp*.
|
|
17
|
+
```text
|
|
18
|
+
# apt install libkrb5-dev libssl-dev
|
|
19
|
+
```
|
|
20
|
+
|
|
16
21
|
It's recommended to install the package globally.
|
|
17
22
|
|
|
18
23
|
```text
|
|
@@ -148,9 +153,10 @@ routines.
|
|
|
148
153
|
|
|
149
154
|
You can create your local fixture and structure files.
|
|
150
155
|
|
|
151
|
-
* *dump/
|
|
152
|
-
* *dump/
|
|
153
|
-
* *dump/fixtures.sql*
|
|
156
|
+
* *dump/dump.before.sql*
|
|
157
|
+
* *dump/dump.after.sql*
|
|
158
|
+
* *dump/fixtures.before.sql*
|
|
159
|
+
* *dump/fixtures.after.sql*
|
|
154
160
|
|
|
155
161
|
## Versioning commands
|
|
156
162
|
|
|
@@ -180,7 +186,7 @@ When *--checkout* option is provided, it does the following before export:
|
|
|
180
186
|
Applies versions and routine changes into database.
|
|
181
187
|
|
|
182
188
|
```text
|
|
183
|
-
$ myt push [<remote>] [-f|--force] [-c|--commit] [-s|--sums]
|
|
189
|
+
$ myt push [<remote>] [-f|--force] [-c|--commit] [-s|--sums] [-t|--triggers]
|
|
184
190
|
```
|
|
185
191
|
|
|
186
192
|
Commit is saved into database only if *--commit* option is provided, it
|
|
@@ -221,7 +227,7 @@ Exports database structure and fixtures from remote into hidden files located
|
|
|
221
227
|
in *dump* folder. If no remote is specified *production* is used.
|
|
222
228
|
|
|
223
229
|
```text
|
|
224
|
-
$ myt dump [<remote>] [-l|--lock]
|
|
230
|
+
$ myt dump [<remote>] [-l|--lock] [-t|--triggers]
|
|
225
231
|
```
|
|
226
232
|
|
|
227
233
|
### fixtures
|
package/assets/myt.default.yml
CHANGED
|
@@ -2,7 +2,24 @@ versionSchema: myt
|
|
|
2
2
|
versionDigits: 5
|
|
3
3
|
maxOldVersions: 20
|
|
4
4
|
mockDate: false
|
|
5
|
+
mockFunctions:
|
|
6
|
+
- mockTime
|
|
7
|
+
- mockUtcTime
|
|
5
8
|
sumViews: true
|
|
9
|
+
privileges:
|
|
10
|
+
userTable: global_priv
|
|
11
|
+
userWhere: >-
|
|
12
|
+
JSON_VALUE(`Priv`, '$.is_role')
|
|
13
|
+
AND JSON_VALUE(`Priv`, '$.authentication_string') IS NULL
|
|
14
|
+
AND JSON_VALUE(`Priv`, '$.mysql_old_password') IS NULL
|
|
15
|
+
AND JSON_VALUE(`Priv`, '$.mysql_native_password') IS NULL
|
|
16
|
+
tables:
|
|
17
|
+
- db
|
|
18
|
+
- tables_priv
|
|
19
|
+
- columns_priv
|
|
20
|
+
- procs_priv
|
|
21
|
+
where: >-
|
|
22
|
+
`Host` = ''
|
|
6
23
|
schemas:
|
|
7
24
|
- myt
|
|
8
25
|
fixtures:
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
#!/bin/node
|
|
2
|
+
|
|
3
|
+
require('require-yaml');
|
|
4
|
+
const mysql = require('mysql2/promise');
|
|
5
|
+
const {fixtures} = require(`${process.cwd()}/myt.config.yml`);
|
|
6
|
+
|
|
7
|
+
const config = {
|
|
8
|
+
host: 'localhost',
|
|
9
|
+
user: 'root',
|
|
10
|
+
password: 'root'
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
async function connectAndQuery() {
|
|
14
|
+
try {
|
|
15
|
+
const connection = await mysql.createConnection(config);
|
|
16
|
+
|
|
17
|
+
// Get schemas
|
|
18
|
+
|
|
19
|
+
const [schemaResults] = await connection.query('SHOW DATABASES');
|
|
20
|
+
const schemaNames = schemaResults.map(r => r.Database);
|
|
21
|
+
|
|
22
|
+
// Filter system schemas
|
|
23
|
+
|
|
24
|
+
const systemSchemas = ['mysql', 'sys', 'information_schema', 'performance_schema'];
|
|
25
|
+
const userSchemas = schemaNames.filter(s => !systemSchemas.includes(s));
|
|
26
|
+
|
|
27
|
+
if (userSchemas.length === 0) {
|
|
28
|
+
console.log('There are no user schemas with tables that have records.');
|
|
29
|
+
connection.end();
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Get tables with records
|
|
34
|
+
|
|
35
|
+
userSchemas.sort((a, b) => a.localeCompare(b));
|
|
36
|
+
for (const schemaName of userSchemas) {
|
|
37
|
+
const [tableResults] = await connection.query(
|
|
38
|
+
`SELECT TABLE_NAME tableName
|
|
39
|
+
FROM information_schema.TABLES
|
|
40
|
+
WHERE TABLE_SCHEMA = ?
|
|
41
|
+
AND TABLE_TYPE <> 'VIEW'`,
|
|
42
|
+
[schemaName]
|
|
43
|
+
);
|
|
44
|
+
const tableNames = tableResults.map(r => r.tableName);
|
|
45
|
+
if (tableNames.length === 0) continue;
|
|
46
|
+
|
|
47
|
+
const schemaFixtures = new Set(fixtures[schemaName]);
|
|
48
|
+
const nonEmptyTables = [];
|
|
49
|
+
|
|
50
|
+
for (const tableName of tableNames) {
|
|
51
|
+
if (schemaFixtures.has(tableName)) continue;
|
|
52
|
+
try {
|
|
53
|
+
const [[row]] = await connection.query(
|
|
54
|
+
`SELECT COUNT(*) \`count\` FROM ??.??`,
|
|
55
|
+
[schemaName, tableName]
|
|
56
|
+
);
|
|
57
|
+
if (row.count == 0) continue;
|
|
58
|
+
if (!fixtures[schemaName]?.[tableName])
|
|
59
|
+
nonEmptyTables.push(tableName);
|
|
60
|
+
} catch (err) {
|
|
61
|
+
console.error('Error:', err.message);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
nonEmptyTables.sort((a, b) => a.localeCompare(b));
|
|
66
|
+
if (nonEmptyTables.length) {
|
|
67
|
+
console.log(`${schemaName}:`);
|
|
68
|
+
for (const tableName of nonEmptyTables)
|
|
69
|
+
console.log(` - ${tableName}`);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
connection.end();
|
|
74
|
+
} catch (err) {
|
|
75
|
+
console.error('Error:', err.message);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
connectAndQuery();
|
package/lib/command.js
CHANGED
|
@@ -10,4 +10,10 @@ module.exports = class MytCommand {
|
|
|
10
10
|
async run(myt, opts) {
|
|
11
11
|
throw new Error('run command not defined');
|
|
12
12
|
}
|
|
13
|
+
|
|
14
|
+
emit(event) {
|
|
15
|
+
const messages = this.constructor.messages;
|
|
16
|
+
if (messages && messages[event])
|
|
17
|
+
console.log(messages[event]);
|
|
18
|
+
}
|
|
13
19
|
}
|
package/lib/dumper.js
CHANGED
|
@@ -8,12 +8,11 @@ module.exports = class Dumper {
|
|
|
8
8
|
this.opts = opts;
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
async init(dumpFile) {
|
|
12
|
-
const dumpDir = this.opts.dumpDir;
|
|
11
|
+
async init(dumpDir, dumpFile) {
|
|
13
12
|
if (!await fs.pathExists(dumpDir))
|
|
14
|
-
await fs.mkdir(dumpDir);
|
|
13
|
+
await fs.mkdir(dumpDir, {recursive: true});
|
|
15
14
|
|
|
16
|
-
const dumpPath = path.join(dumpDir, dumpFile);
|
|
15
|
+
const dumpPath = path.join(dumpDir, `${dumpFile}.sql`);
|
|
17
16
|
|
|
18
17
|
// FIXME: If it's called after docker.build() statement it creates an
|
|
19
18
|
// "invalid" WriteStream
|
|
@@ -28,6 +27,10 @@ module.exports = class Dumper {
|
|
|
28
27
|
this.dumpStream = dumpStream;
|
|
29
28
|
}
|
|
30
29
|
|
|
30
|
+
async end() {
|
|
31
|
+
await this.dumpStream.end();
|
|
32
|
+
}
|
|
33
|
+
|
|
31
34
|
async use(schema) {
|
|
32
35
|
await this.dumpStream.write(
|
|
33
36
|
`USE ${SqlString.escapeId(schema, true)};\n`,
|
|
@@ -36,7 +39,7 @@ module.exports = class Dumper {
|
|
|
36
39
|
}
|
|
37
40
|
|
|
38
41
|
async dumpFixtures(tables, replace, args) {
|
|
39
|
-
|
|
42
|
+
let fixturesArgs = [
|
|
40
43
|
'--no-create-info',
|
|
41
44
|
'--skip-triggers',
|
|
42
45
|
'--skip-extended-insert',
|
|
@@ -45,18 +48,40 @@ module.exports = class Dumper {
|
|
|
45
48
|
'--skip-set-charset',
|
|
46
49
|
'--skip-comments',
|
|
47
50
|
'--skip-tz-utc'
|
|
48
|
-
]
|
|
49
|
-
|
|
51
|
+
]
|
|
52
|
+
if (args)
|
|
53
|
+
fixturesArgs = fixturesArgs.concat(args);
|
|
50
54
|
if (replace)
|
|
51
55
|
fixturesArgs.push('--replace');
|
|
52
56
|
|
|
53
57
|
for (const schema in tables) {
|
|
54
|
-
await this.use(schema);
|
|
55
58
|
const args = fixturesArgs.concat([schema], tables[schema]);
|
|
56
|
-
await this.
|
|
59
|
+
await this.use(schema);
|
|
60
|
+
await this.runDump('mysqldump', args);
|
|
57
61
|
}
|
|
58
62
|
}
|
|
59
63
|
|
|
64
|
+
async dumpPrivileges(tables, args, where) {
|
|
65
|
+
let privArgs = [
|
|
66
|
+
'--no-create-info',
|
|
67
|
+
'--skip-triggers',
|
|
68
|
+
'--insert-ignore',
|
|
69
|
+
'--skip-extended-insert',
|
|
70
|
+
'--skip-add-locks',
|
|
71
|
+
'--skip-set-charset',
|
|
72
|
+
'--skip-comments',
|
|
73
|
+
'--skip-tz-utc'
|
|
74
|
+
];
|
|
75
|
+
if (args)
|
|
76
|
+
privArgs = privArgs.concat(args);
|
|
77
|
+
if (where)
|
|
78
|
+
privArgs.push('--where', where);
|
|
79
|
+
args = privArgs.concat(['mysql'], tables);
|
|
80
|
+
|
|
81
|
+
await this.use('mysql');
|
|
82
|
+
await this.runDump('mysqldump', args);
|
|
83
|
+
}
|
|
84
|
+
|
|
60
85
|
async runDump(command, args) {
|
|
61
86
|
const iniPath = path.join(this.opts.subdir || '', 'remotes', this.opts.iniFile);
|
|
62
87
|
const myArgs = [
|
|
@@ -76,8 +101,4 @@ module.exports = class Dumper {
|
|
|
76
101
|
rm: true
|
|
77
102
|
}, execOptions);
|
|
78
103
|
}
|
|
79
|
-
|
|
80
|
-
async end() {
|
|
81
|
-
await this.dumpStream.end();
|
|
82
|
-
}
|
|
83
104
|
}
|
package/myt-dump.js
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
const Myt = require('./myt');
|
|
2
2
|
const Command = require('./lib/command');
|
|
3
|
-
const fs = require('fs-extra');
|
|
4
3
|
const Dumper = require('./lib/dumper');
|
|
4
|
+
const fs = require('fs-extra');
|
|
5
|
+
const path = require('path');
|
|
5
6
|
|
|
6
7
|
class Dump extends Command {
|
|
7
8
|
static usage = {
|
|
8
9
|
description: 'Dumps structure and fixtures from remote',
|
|
9
10
|
params: {
|
|
10
|
-
lock: 'Whether to lock tables on dump'
|
|
11
|
+
lock: 'Whether to lock tables on dump',
|
|
12
|
+
triggers: 'Wether to include triggers into dump'
|
|
11
13
|
},
|
|
12
14
|
operand: 'remote'
|
|
13
15
|
};
|
|
@@ -17,54 +19,111 @@ class Dump extends Command {
|
|
|
17
19
|
remote: 'production'
|
|
18
20
|
},
|
|
19
21
|
alias: {
|
|
20
|
-
lock: 'l'
|
|
22
|
+
lock: 'l',
|
|
23
|
+
triggers: 't'
|
|
21
24
|
},
|
|
22
25
|
boolean: [
|
|
23
|
-
'lock'
|
|
26
|
+
'lock',
|
|
27
|
+
'triggers'
|
|
24
28
|
]
|
|
25
29
|
};
|
|
26
30
|
|
|
31
|
+
static messages = {
|
|
32
|
+
dumpStructure: 'Dumping structure.',
|
|
33
|
+
dumpData: 'Dumping data.',
|
|
34
|
+
dumpPrivileges: 'Dumping privileges.',
|
|
35
|
+
dumpTriggers: 'Dumping triggers.'
|
|
36
|
+
};
|
|
37
|
+
|
|
27
38
|
async run(myt, opts) {
|
|
28
|
-
|
|
29
|
-
|
|
39
|
+
let dumper;
|
|
40
|
+
const dumpDataDir = path.join(opts.dumpDir, '.dump');
|
|
30
41
|
const baseArgs = [
|
|
31
42
|
`--lock-tables=${opts.lock ? 'true' : 'false'}`
|
|
32
43
|
];
|
|
33
44
|
|
|
34
|
-
|
|
45
|
+
await fs.remove(dumpDataDir);
|
|
46
|
+
|
|
47
|
+
// Structure
|
|
48
|
+
|
|
49
|
+
this.emit('dumpStructure');
|
|
50
|
+
|
|
51
|
+
dumper = new Dumper(opts);
|
|
52
|
+
await dumper.init(dumpDataDir, 'structure');
|
|
35
53
|
let dumpArgs = [
|
|
36
54
|
'--default-character-set=utf8',
|
|
37
55
|
'--no-data',
|
|
38
56
|
'--comments',
|
|
39
57
|
'--routines',
|
|
40
58
|
'--events',
|
|
41
|
-
'--skip-triggers'
|
|
42
|
-
'--databases'
|
|
59
|
+
'--skip-triggers'
|
|
43
60
|
].concat(baseArgs);
|
|
44
61
|
|
|
62
|
+
dumpArgs.push('--databases');
|
|
45
63
|
dumpArgs = dumpArgs.concat(opts.schemas);
|
|
46
64
|
await dumper.runDump('docker-dump.sh', dumpArgs);
|
|
65
|
+
await dumper.end();
|
|
66
|
+
|
|
67
|
+
// Data
|
|
68
|
+
|
|
69
|
+
this.emit('dumpData');
|
|
47
70
|
|
|
48
|
-
|
|
71
|
+
dumper = new Dumper(opts);
|
|
72
|
+
await dumper.init(dumpDataDir, 'data');
|
|
49
73
|
await dumper.dumpFixtures(opts.fixtures, false, baseArgs);
|
|
74
|
+
await dumper.end();
|
|
75
|
+
|
|
76
|
+
// Privileges
|
|
50
77
|
|
|
51
|
-
console.log('Dumping privileges.');
|
|
52
78
|
const privs = opts.privileges;
|
|
53
|
-
if (privs
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
79
|
+
if (privs) {
|
|
80
|
+
this.emit('dumpPrivileges');
|
|
81
|
+
|
|
82
|
+
dumper = new Dumper(opts);
|
|
83
|
+
await dumper.init(dumpDataDir, 'privileges');
|
|
84
|
+
|
|
85
|
+
const {tables, userTable, where} = privs;
|
|
86
|
+
|
|
87
|
+
if (tables)
|
|
88
|
+
await dumper.dumpPrivileges(tables, baseArgs, where);
|
|
62
89
|
|
|
63
|
-
|
|
64
|
-
|
|
90
|
+
if (userTable) {
|
|
91
|
+
let userWhere = '';
|
|
92
|
+
for (const cond of [where, privs.userWhere]) {
|
|
93
|
+
if (!cond) continue;
|
|
94
|
+
if (userWhere) userWhere += ' AND ';
|
|
95
|
+
userWhere += cond;
|
|
96
|
+
}
|
|
97
|
+
await dumper.dumpPrivileges([userTable], baseArgs, userWhere);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
await dumper.end();
|
|
65
101
|
}
|
|
66
102
|
|
|
67
103
|
await dumper.end();
|
|
104
|
+
|
|
105
|
+
// Triggers
|
|
106
|
+
|
|
107
|
+
if (opts.triggers) {
|
|
108
|
+
this.emit('dumpTriggers');
|
|
109
|
+
|
|
110
|
+
const dumper = new Dumper(opts);
|
|
111
|
+
await dumper.init(dumpDataDir, 'triggers');
|
|
112
|
+
|
|
113
|
+
let dumpArgs = [
|
|
114
|
+
'--default-character-set=utf8',
|
|
115
|
+
'--no-create-info',
|
|
116
|
+
'--no-data',
|
|
117
|
+
'--no-create-db',
|
|
118
|
+
'--skip-opt',
|
|
119
|
+
'--comments'
|
|
120
|
+
].concat(baseArgs);
|
|
121
|
+
|
|
122
|
+
dumpArgs.push('--databases');
|
|
123
|
+
dumpArgs = dumpArgs.concat(opts.schemas);
|
|
124
|
+
await dumper.runDump('mysqldump', dumpArgs);
|
|
125
|
+
await dumper.end();
|
|
126
|
+
}
|
|
68
127
|
}
|
|
69
128
|
}
|
|
70
129
|
|
package/myt-fixtures.js
CHANGED
|
@@ -16,8 +16,8 @@ class Fixtures extends Command {
|
|
|
16
16
|
|
|
17
17
|
async run(myt, opts) {
|
|
18
18
|
const dumper = new Dumper(opts);
|
|
19
|
-
await dumper.init('fixtures
|
|
20
|
-
await dumper.dumpFixtures(opts.localFixtures,
|
|
19
|
+
await dumper.init(opts.dumpDir, '.fixtures');
|
|
20
|
+
await dumper.dumpFixtures(opts.localFixtures, false);
|
|
21
21
|
await dumper.end();
|
|
22
22
|
}
|
|
23
23
|
}
|
|
@@ -26,4 +26,3 @@ module.exports = Fixtures;
|
|
|
26
26
|
|
|
27
27
|
if (require.main === module)
|
|
28
28
|
new Myt().run(Fixtures);
|
|
29
|
-
|
package/myt-pull.js
CHANGED
|
@@ -98,7 +98,7 @@ class Pull extends Command {
|
|
|
98
98
|
const schemas = await fs.readdir(routinesDir);
|
|
99
99
|
for (const schema of schemas) {
|
|
100
100
|
if (opts.schemas.indexOf(schema) == -1)
|
|
101
|
-
await fs.remove(`${routinesDir}/${schema}
|
|
101
|
+
await fs.remove(`${routinesDir}/${schema}`);
|
|
102
102
|
}
|
|
103
103
|
|
|
104
104
|
for (const schema in shaSums) {
|
package/myt-push.js
CHANGED
|
@@ -311,7 +311,7 @@ class Push extends Command {
|
|
|
311
311
|
|
|
312
312
|
for (const change of changes)
|
|
313
313
|
try {
|
|
314
|
-
if (opts.
|
|
314
|
+
if (opts.triggers && change.type.name === 'TRIGGER')
|
|
315
315
|
continue;
|
|
316
316
|
|
|
317
317
|
const schema = change.schema;
|
|
@@ -325,7 +325,14 @@ class Push extends Command {
|
|
|
325
325
|
newSql = await fs.readFile(fullPath, 'utf8');
|
|
326
326
|
const oldSql = await engine.fetchRoutine(type, schema, name);
|
|
327
327
|
const oldSum = engine.getShaSum(type, schema, name);
|
|
328
|
-
|
|
328
|
+
|
|
329
|
+
const isMockFn = type == 'function'
|
|
330
|
+
&& schema == opts.versionSchema
|
|
331
|
+
&& opts.remote == 'local'
|
|
332
|
+
&& opts.mockDate
|
|
333
|
+
&& opts.mockFunctions
|
|
334
|
+
&& opts.mockFunctions.indexOf(name) !== -1;
|
|
335
|
+
const ignore = newSql == oldSql || isMockFn;
|
|
329
336
|
|
|
330
337
|
let statusMsg;
|
|
331
338
|
if (exists && !oldSql)
|
|
@@ -336,7 +343,7 @@ class Push extends Command {
|
|
|
336
343
|
statusMsg = '[·]'.yellow;
|
|
337
344
|
|
|
338
345
|
let actionMsg;
|
|
339
|
-
if (
|
|
346
|
+
if (ignore)
|
|
340
347
|
actionMsg = '[I]'.blue;
|
|
341
348
|
else
|
|
342
349
|
actionMsg = '[A]'.green;
|
|
@@ -348,7 +355,7 @@ class Push extends Command {
|
|
|
348
355
|
change.fullName
|
|
349
356
|
);
|
|
350
357
|
|
|
351
|
-
if (!
|
|
358
|
+
if (!ignore) {
|
|
352
359
|
const scapedSchema = SqlString.escapeId(schema, true);
|
|
353
360
|
|
|
354
361
|
if (exists) {
|
package/myt-run.js
CHANGED
|
@@ -36,9 +36,10 @@ class Run extends Command {
|
|
|
36
36
|
|
|
37
37
|
async run(myt, opts) {
|
|
38
38
|
const dumpDir = opts.dumpDir;
|
|
39
|
+
const dumpDataDir = path.join(dumpDir, '.dump');
|
|
39
40
|
const serverDir = path.join(__dirname, 'server');
|
|
40
41
|
|
|
41
|
-
if (!await fs.pathExists(`${
|
|
42
|
+
if (!await fs.pathExists(`${dumpDataDir}/structure.sql`))
|
|
42
43
|
throw new Error('To run local database you have to create a dump first');
|
|
43
44
|
|
|
44
45
|
// Build base image
|
|
@@ -118,12 +119,29 @@ class Run extends Command {
|
|
|
118
119
|
}
|
|
119
120
|
|
|
120
121
|
await server.wait();
|
|
122
|
+
const conn = await myt.createConnection();
|
|
123
|
+
|
|
124
|
+
// Mock date functions
|
|
125
|
+
|
|
126
|
+
console.log('Mocking date functions.');
|
|
127
|
+
const mockDateScript = path.join(dumpDir, 'mockDate.sql');
|
|
128
|
+
|
|
129
|
+
if (opts.mockDate) {
|
|
130
|
+
if (!await fs.pathExists(mockDateScript))
|
|
131
|
+
throw new Error(`Date mock enabled but mock script does not exist: ${mockDateScript}`);
|
|
132
|
+
|
|
133
|
+
let sql = await fs.readFile(mockDateScript, 'utf8');
|
|
134
|
+
sql = sql.replace(/@mockDate/g, SqlString.escape(opts.mockDate));
|
|
135
|
+
await connExt.multiQuery(conn, sql);
|
|
136
|
+
}
|
|
121
137
|
|
|
122
138
|
// Apply changes
|
|
123
139
|
|
|
140
|
+
const hasTriggers = await fs.exists(`${dumpDataDir}/triggers.sql`);
|
|
141
|
+
|
|
124
142
|
Object.assign(opts, {
|
|
143
|
+
triggers: hasTriggers,
|
|
125
144
|
commit: true,
|
|
126
|
-
trigger: true,
|
|
127
145
|
dbConfig
|
|
128
146
|
});
|
|
129
147
|
await myt.runCommand(Push, opts);
|
|
@@ -131,40 +149,35 @@ class Run extends Command {
|
|
|
131
149
|
// Apply fixtures
|
|
132
150
|
|
|
133
151
|
console.log('Applying fixtures.');
|
|
134
|
-
|
|
135
|
-
'
|
|
136
|
-
|
|
137
|
-
'
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
const triggersPath = `${opts.routinesDir}/${schema}/triggers`;
|
|
148
|
-
if (!await fs.pathExists(triggersPath))
|
|
149
|
-
continue;
|
|
150
|
-
|
|
151
|
-
const triggersDir = await fs.readdir(triggersPath);
|
|
152
|
-
for (const triggerFile of triggersDir)
|
|
153
|
-
await connExt.queryFromFile(conn, `${triggersPath}/${triggerFile}`);
|
|
152
|
+
const fixturesFiles = [
|
|
153
|
+
'fixtures.before',
|
|
154
|
+
'.fixtures',
|
|
155
|
+
'fixtures.after',
|
|
156
|
+
'fixtures.local'
|
|
157
|
+
]
|
|
158
|
+
for (const file of fixturesFiles) {
|
|
159
|
+
if (!await fs.exists(`${dumpDir}/${file}.sql`)) continue;
|
|
160
|
+
await ct.exec(null, 'docker-import.sh',
|
|
161
|
+
[`/workspace/dump/${file}`],
|
|
162
|
+
'spawn',
|
|
163
|
+
true
|
|
164
|
+
);
|
|
154
165
|
}
|
|
155
166
|
|
|
156
|
-
//
|
|
167
|
+
// Create triggers
|
|
157
168
|
|
|
158
|
-
|
|
159
|
-
|
|
169
|
+
if (!hasTriggers) {
|
|
170
|
+
console.log('Creating triggers.');
|
|
160
171
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
172
|
+
for (const schema of opts.schemas) {
|
|
173
|
+
const triggersPath = `${opts.routinesDir}/${schema}/triggers`;
|
|
174
|
+
if (!await fs.pathExists(triggersPath))
|
|
175
|
+
continue;
|
|
164
176
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
177
|
+
const triggersDir = await fs.readdir(triggersPath);
|
|
178
|
+
for (const triggerFile of triggersDir)
|
|
179
|
+
await connExt.queryFromFile(conn, `${triggersPath}/${triggerFile}`);
|
|
180
|
+
}
|
|
168
181
|
}
|
|
169
182
|
|
|
170
183
|
return server;
|
package/myt-version.js
CHANGED
|
@@ -123,7 +123,7 @@ class Version extends Command {
|
|
|
123
123
|
} catch (err) {
|
|
124
124
|
await conn.query('ROLLBACK');
|
|
125
125
|
if (newVersionDir && await fs.pathExists(newVersionDir))
|
|
126
|
-
await fs.remove(newVersionDir
|
|
126
|
+
await fs.remove(newVersionDir);
|
|
127
127
|
throw err;
|
|
128
128
|
}
|
|
129
129
|
}
|
package/myt.js
CHANGED
|
@@ -170,12 +170,29 @@ class Myt {
|
|
|
170
170
|
async load(opts) {
|
|
171
171
|
// Configuration file
|
|
172
172
|
|
|
173
|
-
const
|
|
173
|
+
const defaultConfig = require(`${__dirname}/assets/myt.default.yml`);
|
|
174
|
+
const config = Object.assign({}, defaultConfig);
|
|
174
175
|
|
|
175
176
|
const configFile = 'myt.config.yml';
|
|
176
177
|
const configPath = path.join(opts.workspace, configFile);
|
|
177
|
-
|
|
178
|
-
|
|
178
|
+
|
|
179
|
+
if (await fs.pathExists(configPath)) {
|
|
180
|
+
const mergeKeys = new Set([
|
|
181
|
+
'privileges'
|
|
182
|
+
]);
|
|
183
|
+
|
|
184
|
+
const wsConfig = require(configPath);
|
|
185
|
+
for (const key in wsConfig) {
|
|
186
|
+
if (!mergeKeys.has(key)) {
|
|
187
|
+
config[key] = wsConfig[key];
|
|
188
|
+
} else {
|
|
189
|
+
config[key] = Object.assign({},
|
|
190
|
+
config[key],
|
|
191
|
+
wsConfig[key]
|
|
192
|
+
);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
179
196
|
|
|
180
197
|
Object.assign(opts, config);
|
|
181
198
|
opts.configFile = configFile;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@verdnatura/myt",
|
|
3
|
-
"version": "1.5.
|
|
3
|
+
"version": "1.5.21",
|
|
4
4
|
"author": "Verdnatura Levante SL",
|
|
5
5
|
"description": "MySQL version control",
|
|
6
6
|
"license": "GPL-3.0",
|
|
@@ -15,10 +15,10 @@
|
|
|
15
15
|
"@sqltools/formatter": "^1.2.5",
|
|
16
16
|
"colors": "^1.4.0",
|
|
17
17
|
"ejs": "^3.1.9",
|
|
18
|
-
"fs-extra": "^
|
|
18
|
+
"fs-extra": "^11.1.1",
|
|
19
19
|
"getopts": "^2.3.0",
|
|
20
|
-
"ini": "^1.
|
|
21
|
-
"mysql2": "^
|
|
20
|
+
"ini": "^4.1.1",
|
|
21
|
+
"mysql2": "^3.3.4",
|
|
22
22
|
"nodegit": "^0.27.0",
|
|
23
23
|
"require-yaml": "^0.0.1",
|
|
24
24
|
"sha.js": "^2.4.11"
|
package/server/Dockerfile.dump
CHANGED
package/server/docker-import.sh
CHANGED
package/server/docker-init.sh
CHANGED
|
@@ -13,8 +13,11 @@ docker_temp_server_start "$CMD"
|
|
|
13
13
|
docker_setup_db
|
|
14
14
|
docker_process_init_files /docker-entrypoint-initdb.d/*
|
|
15
15
|
|
|
16
|
-
docker-import.sh dump/
|
|
17
|
-
docker-import.sh dump/.dump
|
|
18
|
-
docker-import.sh dump/
|
|
16
|
+
docker-import.sh dump/dump.before
|
|
17
|
+
docker-import.sh dump/.dump/structure
|
|
18
|
+
docker-import.sh dump/.dump/data
|
|
19
|
+
docker-import.sh dump/.dump/triggers
|
|
20
|
+
docker-import.sh dump/.dump/privileges
|
|
21
|
+
docker-import.sh dump/dump.after
|
|
19
22
|
|
|
20
23
|
docker_temp_server_stop
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
-- Executed after dump
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
-- Executed before dump
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
-- Executed after fixtures
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
-- Executed before fixtures
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
-- Executed after data dump
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
-- Executed before data dump
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
-- Place your local fixtures here
|