@verdnatura/myt 1.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.dockerignore +2 -0
- package/LICENSE +17 -0
- package/README.md +268 -0
- package/assets/db.ini +5 -0
- package/assets/myvc.default.yml +9 -0
- package/assets/structure.sql +24 -0
- package/cli.js +4 -0
- package/exporters/event.ejs +21 -0
- package/exporters/event.js +25 -0
- package/exporters/event.sql +19 -0
- package/exporters/function.ejs +20 -0
- package/exporters/function.js +23 -0
- package/exporters/function.sql +16 -0
- package/exporters/procedure.ejs +14 -0
- package/exporters/procedure.js +23 -0
- package/exporters/procedure.sql +14 -0
- package/exporters/trigger.ejs +7 -0
- package/exporters/trigger.js +5 -0
- package/exporters/trigger.sql +13 -0
- package/exporters/view.ejs +7 -0
- package/exporters/view.js +25 -0
- package/exporters/view.sql +12 -0
- package/lib/command.js +16 -0
- package/lib/docker.js +133 -0
- package/lib/exporter-engine.js +112 -0
- package/lib/exporter.js +115 -0
- package/lib/server.js +71 -0
- package/lib/util.js +6 -0
- package/myt-clean.js +52 -0
- package/myt-dump.js +71 -0
- package/myt-fixtures.js +28 -0
- package/myt-init.js +25 -0
- package/myt-pull.js +129 -0
- package/myt-push.js +475 -0
- package/myt-run.js +160 -0
- package/myt-start.js +51 -0
- package/myt-version.js +219 -0
- package/myt.js +646 -0
- package/package.json +43 -0
- package/server/Dockerfile.base +1 -0
- package/server/Dockerfile.client +14 -0
- package/server/Dockerfile.dump +16 -0
- package/server/Dockerfile.server +25 -0
- package/server/docker-dump.sh +4 -0
- package/server/docker-fixtures.sh +4 -0
- package/server/docker-import.sh +6 -0
- package/server/docker-init.sh +20 -0
- package/server/docker-start.sh +10 -0
- package/server/docker.cnf +9 -0
- package/template/dump/fixtures.sql +1 -0
- package/template/dump/structure.sql +1 -0
- package/template/myvc.config.yml +11 -0
- package/template/package.json +13 -0
- package/template/remotes/local.ini +5 -0
- package/template/routines/README.md +1 -0
- package/template/versions/00001-firstVersion/00-firstScript.sql +1 -0
- package/template/versions/README.md +1 -0
package/lib/docker.js
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
const spawn = require('child_process').spawn;
|
|
2
|
+
const execFile = require('child_process').execFile;
|
|
3
|
+
const camelToSnake = require('./util').camelToSnake;
|
|
4
|
+
|
|
5
|
+
const docker = {
|
|
6
|
+
async run(image, commandArgs, options, execOptions) {
|
|
7
|
+
const args = commandArgs
|
|
8
|
+
? [image].concat(commandArgs)
|
|
9
|
+
: image;
|
|
10
|
+
const execMode = options.detach ? 'exec' : 'spawn';
|
|
11
|
+
|
|
12
|
+
const child = await this.command('run',
|
|
13
|
+
args,
|
|
14
|
+
options,
|
|
15
|
+
execMode,
|
|
16
|
+
execOptions
|
|
17
|
+
);
|
|
18
|
+
return options.detach
|
|
19
|
+
? new Container(child.stdout.trim())
|
|
20
|
+
: child;
|
|
21
|
+
},
|
|
22
|
+
|
|
23
|
+
async build(url, options, execOptions) {
|
|
24
|
+
return await this.command('build',
|
|
25
|
+
url,
|
|
26
|
+
options,
|
|
27
|
+
'spawn',
|
|
28
|
+
execOptions
|
|
29
|
+
);
|
|
30
|
+
},
|
|
31
|
+
|
|
32
|
+
async start(id, options) {
|
|
33
|
+
const ct = new Container(id);
|
|
34
|
+
await ct.start(options);
|
|
35
|
+
return ct;
|
|
36
|
+
},
|
|
37
|
+
|
|
38
|
+
async stop(id, options) {
|
|
39
|
+
const ct = new Container(id);
|
|
40
|
+
return await ct.stop(options);
|
|
41
|
+
},
|
|
42
|
+
|
|
43
|
+
async rm(id, options) {
|
|
44
|
+
const ct = new Container(id);
|
|
45
|
+
return await ct.rm(options);
|
|
46
|
+
},
|
|
47
|
+
|
|
48
|
+
async inspect(id, options) {
|
|
49
|
+
const ct = new Container(id);
|
|
50
|
+
return await ct.inspect(options);
|
|
51
|
+
},
|
|
52
|
+
|
|
53
|
+
async command(command, args, options, execMode, execOptions) {
|
|
54
|
+
const execArgs = [command];
|
|
55
|
+
|
|
56
|
+
if (options)
|
|
57
|
+
for (const option in options) {
|
|
58
|
+
execArgs.push(`--${camelToSnake(option)}`);
|
|
59
|
+
if (typeof options[option] !== 'boolean')
|
|
60
|
+
execArgs.push(options[option]);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (Array.isArray(args))
|
|
64
|
+
Array.prototype.push.apply(execArgs, args);
|
|
65
|
+
else if (args)
|
|
66
|
+
execArgs.push(args);
|
|
67
|
+
|
|
68
|
+
return await new Promise((resolve, reject) => {
|
|
69
|
+
if (execMode == 'spawn') {
|
|
70
|
+
if (execOptions === true)
|
|
71
|
+
execOptions = {
|
|
72
|
+
stdio: [
|
|
73
|
+
process.stdin,
|
|
74
|
+
process.stdout,
|
|
75
|
+
process.stderr
|
|
76
|
+
]
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
const child = spawn('docker', execArgs, execOptions || undefined);
|
|
80
|
+
child.on('exit', code => {
|
|
81
|
+
if (code !== 0) {
|
|
82
|
+
const quotedArgs = execArgs
|
|
83
|
+
.map(x => /\s/g.test(x) ? `"${x}"` : x)
|
|
84
|
+
.join(' ');
|
|
85
|
+
reject(new Error(`Docker exit code ${code}: 'docker ${quotedArgs}'`));
|
|
86
|
+
} else
|
|
87
|
+
resolve(code);
|
|
88
|
+
});
|
|
89
|
+
} else {
|
|
90
|
+
execFile('docker', execArgs, (err, stdout, stderr) => {
|
|
91
|
+
if (err)
|
|
92
|
+
reject(err);
|
|
93
|
+
else
|
|
94
|
+
resolve({stdout, stderr});
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
class Container {
|
|
102
|
+
constructor(id) {
|
|
103
|
+
if (!id)
|
|
104
|
+
throw new Error('Container id argument is required');
|
|
105
|
+
this.id = id;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
async start(options) {
|
|
109
|
+
await docker.command('start', this.id, options);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
async stop(options) {
|
|
113
|
+
await docker.command('stop', this.id, options);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
async rm(options) {
|
|
117
|
+
await docker.command('rm', this.id, options);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
async inspect(options) {
|
|
121
|
+
const child = await docker.command('inspect', this.id, options);
|
|
122
|
+
return JSON.parse(child.stdout);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
async exec(options, command, commandArgs, execMode, execOptions) {
|
|
126
|
+
let args = [this.id, command];
|
|
127
|
+
if (commandArgs) args = args.concat(commandArgs);
|
|
128
|
+
await docker.command('exec', args, options, execMode, execOptions);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
module.exports = docker;
|
|
133
|
+
module.exports.Container = Container;
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
|
|
2
|
+
const shajs = require('sha.js');
|
|
3
|
+
const fs = require('fs-extra');
|
|
4
|
+
const Exporter = require('./exporter');
|
|
5
|
+
|
|
6
|
+
module.exports = class ExporterEngine {
|
|
7
|
+
constructor(conn, mytDir) {
|
|
8
|
+
this.conn = conn;
|
|
9
|
+
this.pullFile = `${mytDir}/.pullinfo.json`;
|
|
10
|
+
this.exporters = [];
|
|
11
|
+
this.exporterMap = {};
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
async init () {
|
|
15
|
+
if (await fs.pathExists(this.pullFile)) {
|
|
16
|
+
this.pullInfo = JSON.parse(await fs.readFile(this.pullFile, 'utf8'));
|
|
17
|
+
const lastPull = this.pullInfo.lastPull;
|
|
18
|
+
if (lastPull)
|
|
19
|
+
this.pullInfo.lastPull = new Date(lastPull);
|
|
20
|
+
} else
|
|
21
|
+
this.pullInfo = {
|
|
22
|
+
lastPull: null,
|
|
23
|
+
shaSums: {}
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
this.shaSums = this.pullInfo.shaSums;
|
|
27
|
+
this.lastPull = this.pullInfo.lastPull;
|
|
28
|
+
this.infoChanged = false;
|
|
29
|
+
|
|
30
|
+
const types = [
|
|
31
|
+
'function',
|
|
32
|
+
'procedure',
|
|
33
|
+
'view',
|
|
34
|
+
'trigger',
|
|
35
|
+
'event'
|
|
36
|
+
];
|
|
37
|
+
|
|
38
|
+
for (const type of types) {
|
|
39
|
+
const exporter = new Exporter(this, type, this.conn);
|
|
40
|
+
await exporter.init();
|
|
41
|
+
|
|
42
|
+
this.exporters.push(exporter);
|
|
43
|
+
this.exporterMap[type] = exporter;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
async fetchRoutine(type, schema, name) {
|
|
48
|
+
const exporter = this.exporterMap[type];
|
|
49
|
+
const [row] = await exporter.query(schema, name);
|
|
50
|
+
return row && exporter.format(row);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
async fetchShaSum(type, schema, name) {
|
|
54
|
+
const sql = await this.fetchRoutine(type, schema, name);
|
|
55
|
+
this.setShaSum(type, schema, name, this.shaSum(sql));
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
shaSum(sql) {
|
|
59
|
+
if (!sql) return null;
|
|
60
|
+
return shajs('sha256')
|
|
61
|
+
.update(JSON.stringify(sql))
|
|
62
|
+
.digest('hex');
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
getShaSum(type, schema, name) {
|
|
66
|
+
try {
|
|
67
|
+
return this.shaSums[schema][type][name];
|
|
68
|
+
} catch (e) {};
|
|
69
|
+
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
setShaSum(type, schema, name, shaSum) {
|
|
74
|
+
if (!shaSum) {
|
|
75
|
+
this.deleteShaSum(type, schema, name);
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const shaSums = this.shaSums;
|
|
80
|
+
if (!shaSums[schema])
|
|
81
|
+
shaSums[schema] = {};
|
|
82
|
+
if (!shaSums[schema][type])
|
|
83
|
+
shaSums[schema][type] = {};
|
|
84
|
+
shaSums[schema][type][name] = shaSum;
|
|
85
|
+
this.infoChanged = true;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
deleteShaSum(type, schema, name) {
|
|
89
|
+
try {
|
|
90
|
+
delete this.shaSums[schema][type][name];
|
|
91
|
+
this.infoChanged = true;
|
|
92
|
+
} catch (e) {};
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
deleteSchemaSums(schema) {
|
|
96
|
+
delete this.shaSums[schema];
|
|
97
|
+
this.infoChanged = true;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
async refreshPullDate() {
|
|
101
|
+
const [[row]] = await this.conn.query(`SELECT NOW() now`);
|
|
102
|
+
this.pullInfo.lastPull = row.now;
|
|
103
|
+
this.infoChanged = true;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
async saveInfo() {
|
|
107
|
+
if (!this.infoChanged) return;
|
|
108
|
+
await fs.writeFile(this.pullFile,
|
|
109
|
+
JSON.stringify(this.pullInfo, null, ' '));
|
|
110
|
+
this.infoChanged = false;
|
|
111
|
+
}
|
|
112
|
+
}
|
package/lib/exporter.js
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
|
|
2
|
+
const ejs = require('ejs');
|
|
3
|
+
const fs = require('fs-extra');
|
|
4
|
+
|
|
5
|
+
module.exports = class Exporter {
|
|
6
|
+
constructor(engine, objectType, conn) {
|
|
7
|
+
this.engine = engine;
|
|
8
|
+
this.objectType = objectType;
|
|
9
|
+
this.dstDir = `${objectType}s`;
|
|
10
|
+
this.conn = conn;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
async init() {
|
|
14
|
+
const templateDir = `${__dirname}/../exporters/${this.objectType}`;
|
|
15
|
+
this.sql = await fs.readFile(`${templateDir}.sql`, 'utf8');
|
|
16
|
+
|
|
17
|
+
const templateFile = await fs.readFile(`${templateDir}.ejs`, 'utf8');
|
|
18
|
+
this.template = ejs.compile(templateFile);
|
|
19
|
+
this.attrs = require(`${templateDir}.js`);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
async export(exportDir, schema, update, saveSum) {
|
|
23
|
+
const res = await this.query(schema);
|
|
24
|
+
if (!res.length) return;
|
|
25
|
+
|
|
26
|
+
const routineDir = `${exportDir}/${schema}/${this.dstDir}`;
|
|
27
|
+
if (!await fs.pathExists(routineDir))
|
|
28
|
+
await fs.mkdir(routineDir);
|
|
29
|
+
|
|
30
|
+
const routineSet = new Set();
|
|
31
|
+
for (const params of res)
|
|
32
|
+
routineSet.add(params.name);
|
|
33
|
+
|
|
34
|
+
const routines = await fs.readdir(routineDir);
|
|
35
|
+
for (const routineFile of routines) {
|
|
36
|
+
const match = routineFile.match(/^(.*)\.sql$/);
|
|
37
|
+
if (!match) continue;
|
|
38
|
+
const routine = match[1];
|
|
39
|
+
if (!routineSet.has(routine))
|
|
40
|
+
await fs.remove(`${routineDir}/${routine}.sql`);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const engine = this.engine;
|
|
44
|
+
|
|
45
|
+
for (const params of res) {
|
|
46
|
+
const routineName = params.name;
|
|
47
|
+
const sql = this.format(params);
|
|
48
|
+
const routineFile = `${routineDir}/${routineName}.sql`;
|
|
49
|
+
|
|
50
|
+
const oldSum = engine.getShaSum(routineName);
|
|
51
|
+
if (oldSum || saveSum) {
|
|
52
|
+
const shaSum = engine.shaSum(sql);
|
|
53
|
+
if (oldSum !== shaSum) {
|
|
54
|
+
engine.setShaSum(
|
|
55
|
+
this.objectType, schema, routineName, shaSum);
|
|
56
|
+
update = true;
|
|
57
|
+
}
|
|
58
|
+
} else if (params.modified && engine.lastPull) {
|
|
59
|
+
if (params.modified > engine.lastPull)
|
|
60
|
+
update = true;
|
|
61
|
+
} else if (await fs.pathExists(routineFile)) {
|
|
62
|
+
const currentSql = await fs.readFile(routineFile, 'utf8');
|
|
63
|
+
if (sql != currentSql)
|
|
64
|
+
update = true;
|
|
65
|
+
} else
|
|
66
|
+
update = true;
|
|
67
|
+
|
|
68
|
+
if (update)
|
|
69
|
+
await fs.writeFile(routineFile, sql);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
async query(schema, name) {
|
|
74
|
+
const {conn} = this;
|
|
75
|
+
|
|
76
|
+
const ops = [];
|
|
77
|
+
function addOp(col, value) {
|
|
78
|
+
ops.push(conn.format('?? = ?', [col, value]));
|
|
79
|
+
}
|
|
80
|
+
if (schema)
|
|
81
|
+
addOp(this.attrs.schemaCol, schema);
|
|
82
|
+
if (name)
|
|
83
|
+
addOp(this.attrs.nameCol, name);
|
|
84
|
+
|
|
85
|
+
const filter = {
|
|
86
|
+
toSqlString() {
|
|
87
|
+
return ops.join(' AND ');
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const [res] = await conn.query(this.sql, [filter]);
|
|
92
|
+
return res;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
format(params) {
|
|
96
|
+
const {conn, attrs} = this;
|
|
97
|
+
|
|
98
|
+
if (attrs.formatter)
|
|
99
|
+
attrs.formatter(params, conn);
|
|
100
|
+
|
|
101
|
+
if (attrs.escapeCols)
|
|
102
|
+
for (const escapeCol of attrs.escapeCols) {
|
|
103
|
+
if (params[escapeCol])
|
|
104
|
+
params[escapeCol] = conn.escape(params[escapeCol])
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const split = params.definer.split('@');
|
|
108
|
+
params.schema = conn.escapeId(params.schema, true);
|
|
109
|
+
params.name = conn.escapeId(params.name, true);
|
|
110
|
+
params.definer =
|
|
111
|
+
`${conn.escapeId(split[0], true)}@${conn.escapeId(split[1], true)}`;
|
|
112
|
+
|
|
113
|
+
return this.template(params);
|
|
114
|
+
}
|
|
115
|
+
}
|
package/lib/server.js
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
|
|
2
|
+
const mysql = require('mysql2/promise');
|
|
3
|
+
|
|
4
|
+
module.exports = class Server {
|
|
5
|
+
constructor(ct, dbConfig) {
|
|
6
|
+
Object.assign(this, {
|
|
7
|
+
ct,
|
|
8
|
+
dbConfig
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
wait() {
|
|
13
|
+
return new Promise((resolve, reject) => {
|
|
14
|
+
const mysql = require('mysql2');
|
|
15
|
+
|
|
16
|
+
let interval = 100;
|
|
17
|
+
let elapsedTime = 0;
|
|
18
|
+
let maxInterval = 4 * 60 * 1000;
|
|
19
|
+
|
|
20
|
+
const dbConfig = this.dbConfig;
|
|
21
|
+
let myConf = {
|
|
22
|
+
user: dbConfig.user,
|
|
23
|
+
password: dbConfig.password,
|
|
24
|
+
host: dbConfig.host,
|
|
25
|
+
port: dbConfig.port
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
console.log('Waiting for MySQL init process...');
|
|
29
|
+
|
|
30
|
+
async function checker() {
|
|
31
|
+
elapsedTime += interval;
|
|
32
|
+
let status;
|
|
33
|
+
|
|
34
|
+
try {
|
|
35
|
+
status = await this.ct.inspect({
|
|
36
|
+
format: '{{json .State.Status}}'
|
|
37
|
+
});
|
|
38
|
+
} catch (err) {
|
|
39
|
+
return reject(new Error(err.message));
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (status === 'exited')
|
|
43
|
+
return reject(new Error('Docker exited, please see the docker logs for more info'));
|
|
44
|
+
|
|
45
|
+
const conn = mysql.createConnection(myConf);
|
|
46
|
+
conn.on('error', () => {});
|
|
47
|
+
conn.connect(err => {
|
|
48
|
+
conn.destroy();
|
|
49
|
+
if (!err) {
|
|
50
|
+
console.log('MySQL process ready.');
|
|
51
|
+
return resolve();
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (elapsedTime >= maxInterval)
|
|
55
|
+
reject(new Error(`MySQL not initialized whithin ${elapsedTime / 1000} secs`));
|
|
56
|
+
else
|
|
57
|
+
setTimeout(bindedChecker, interval);
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
const bindedChecker = checker.bind(this);
|
|
61
|
+
bindedChecker();
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
async rm() {
|
|
66
|
+
try {
|
|
67
|
+
await this.ct.stop();
|
|
68
|
+
await this.ct.rm({volumes: true});
|
|
69
|
+
} catch (e) {}
|
|
70
|
+
}
|
|
71
|
+
};
|
package/lib/util.js
ADDED
package/myt-clean.js
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
|
|
2
|
+
const Myt = require('./myt');
|
|
3
|
+
const Command = require('./lib/command');
|
|
4
|
+
const fs = require('fs-extra');
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Cleans old applied versions.
|
|
8
|
+
*/
|
|
9
|
+
class Clean extends Command {
|
|
10
|
+
static usage = {
|
|
11
|
+
description: 'Cleans old applied versions'
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
static localOpts = {
|
|
15
|
+
default: {
|
|
16
|
+
remote: 'production'
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
async run(myt, opts) {
|
|
21
|
+
await myt.dbConnect();
|
|
22
|
+
const version = await myt.fetchDbVersion() || {};
|
|
23
|
+
const number = version.number;
|
|
24
|
+
|
|
25
|
+
const oldVersions = [];
|
|
26
|
+
const versionDirs = await fs.readdir(opts.versionsDir);
|
|
27
|
+
for (const versionDir of versionDirs) {
|
|
28
|
+
const dirVersion = myt.parseVersionDir(versionDir);
|
|
29
|
+
if (!dirVersion) continue;
|
|
30
|
+
|
|
31
|
+
if (parseInt(dirVersion.number) < parseInt(number))
|
|
32
|
+
oldVersions.push(versionDir);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (opts.maxOldVersions
|
|
36
|
+
&& oldVersions.length > opts.maxOldVersions) {
|
|
37
|
+
oldVersions.splice(-opts.maxOldVersions);
|
|
38
|
+
|
|
39
|
+
for (const oldVersion of oldVersions)
|
|
40
|
+
await fs.remove(`${opts.versionsDir}/${oldVersion}`,
|
|
41
|
+
{recursive: true});
|
|
42
|
+
|
|
43
|
+
console.log(`Old versions deleted: ${oldVersions.length}`);
|
|
44
|
+
} else
|
|
45
|
+
console.log(`No versions to delete.`);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
module.exports = Clean;
|
|
50
|
+
|
|
51
|
+
if (require.main === module)
|
|
52
|
+
new Myt().run(Clean);
|
package/myt-dump.js
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
|
|
2
|
+
const Myt = require('./myt');
|
|
3
|
+
const Command = require('./lib/command');
|
|
4
|
+
const fs = require('fs-extra');
|
|
5
|
+
|
|
6
|
+
class Dump extends Command {
|
|
7
|
+
static usage = {
|
|
8
|
+
description: 'Dumps structure and fixtures from remote',
|
|
9
|
+
operand: 'remote'
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
static localOpts = {
|
|
13
|
+
default: {
|
|
14
|
+
remote: 'production'
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
async run(myt, opts) {
|
|
19
|
+
const dumpStream = await myt.initDump('.dump.sql');
|
|
20
|
+
|
|
21
|
+
console.log('Dumping structure.');
|
|
22
|
+
let dumpArgs = [
|
|
23
|
+
'--default-character-set=utf8',
|
|
24
|
+
'--no-data',
|
|
25
|
+
'--comments',
|
|
26
|
+
'--triggers',
|
|
27
|
+
'--routines',
|
|
28
|
+
'--events',
|
|
29
|
+
'--databases'
|
|
30
|
+
];
|
|
31
|
+
dumpArgs = dumpArgs.concat(opts.schemas);
|
|
32
|
+
await myt.runDump('docker-dump.sh', dumpArgs, dumpStream);
|
|
33
|
+
|
|
34
|
+
console.log('Dumping fixtures.');
|
|
35
|
+
await myt.dumpFixtures(dumpStream, opts.fixtures);
|
|
36
|
+
|
|
37
|
+
console.log('Dumping privileges.');
|
|
38
|
+
const privs = opts.privileges;
|
|
39
|
+
if (privs && Array.isArray(privs.tables)) {
|
|
40
|
+
let args = [
|
|
41
|
+
'--no-create-info',
|
|
42
|
+
'--skip-triggers',
|
|
43
|
+
'--insert-ignore',
|
|
44
|
+
'--complete-insert'
|
|
45
|
+
];
|
|
46
|
+
if (privs.where) args.push('--where', privs.where);
|
|
47
|
+
args = args.concat(['mysql'], privs.tables);
|
|
48
|
+
|
|
49
|
+
await dumpStream.write('USE `mysql`;\n', 'utf8');
|
|
50
|
+
await myt.runDump('mysqldump', args, dumpStream);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
await dumpStream.end();
|
|
54
|
+
|
|
55
|
+
console.log('Saving version.');
|
|
56
|
+
await myt.dbConnect();
|
|
57
|
+
const version = await myt.fetchDbVersion();
|
|
58
|
+
if (version) {
|
|
59
|
+
await fs.writeFile(
|
|
60
|
+
`${opts.dumpDir}/.dump.json`,
|
|
61
|
+
JSON.stringify(version)
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
module.exports = Dump;
|
|
68
|
+
|
|
69
|
+
if (require.main === module)
|
|
70
|
+
new Myt().run(Dump);
|
|
71
|
+
|
package/myt-fixtures.js
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
|
|
2
|
+
const Myt = require('./myt');
|
|
3
|
+
const Command = require('./lib/command');
|
|
4
|
+
|
|
5
|
+
class Fixtures extends Command {
|
|
6
|
+
static usage = {
|
|
7
|
+
description: 'Dumps local fixtures from database',
|
|
8
|
+
operand: 'remote'
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
static localOpts = {
|
|
12
|
+
default: {
|
|
13
|
+
remote: 'docker'
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
async run(myt, opts) {
|
|
18
|
+
const dumpStream = await myt.initDump('fixtures.sql');
|
|
19
|
+
await myt.dumpFixtures(dumpStream, opts.localFixtures, true);
|
|
20
|
+
await dumpStream.end();
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
module.exports = Fixtures;
|
|
25
|
+
|
|
26
|
+
if (require.main === module)
|
|
27
|
+
new Myt().run(Fixtures);
|
|
28
|
+
|
package/myt-init.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
|
|
2
|
+
const Myt = require('./myt');
|
|
3
|
+
const Command = require('./lib/command');
|
|
4
|
+
const fs = require('fs-extra');
|
|
5
|
+
|
|
6
|
+
class Init extends Command {
|
|
7
|
+
static usage = {
|
|
8
|
+
description: 'Initialize an empty workspace'
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
async run(myt, opts) {
|
|
12
|
+
const templateDir = `${__dirname}/template`;
|
|
13
|
+
const templates = await fs.readdir(templateDir);
|
|
14
|
+
for (let template of templates) {
|
|
15
|
+
const dst = `${opts.mytDir}/${template}`;
|
|
16
|
+
if (!await fs.pathExists(dst))
|
|
17
|
+
await fs.copy(`${templateDir}/${template}`, dst);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
module.exports = Init;
|
|
23
|
+
|
|
24
|
+
if (require.main === module)
|
|
25
|
+
new Myt().run(Init);
|