@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.
Files changed (57) hide show
  1. package/.dockerignore +2 -0
  2. package/LICENSE +17 -0
  3. package/README.md +268 -0
  4. package/assets/db.ini +5 -0
  5. package/assets/myvc.default.yml +9 -0
  6. package/assets/structure.sql +24 -0
  7. package/cli.js +4 -0
  8. package/exporters/event.ejs +21 -0
  9. package/exporters/event.js +25 -0
  10. package/exporters/event.sql +19 -0
  11. package/exporters/function.ejs +20 -0
  12. package/exporters/function.js +23 -0
  13. package/exporters/function.sql +16 -0
  14. package/exporters/procedure.ejs +14 -0
  15. package/exporters/procedure.js +23 -0
  16. package/exporters/procedure.sql +14 -0
  17. package/exporters/trigger.ejs +7 -0
  18. package/exporters/trigger.js +5 -0
  19. package/exporters/trigger.sql +13 -0
  20. package/exporters/view.ejs +7 -0
  21. package/exporters/view.js +25 -0
  22. package/exporters/view.sql +12 -0
  23. package/lib/command.js +16 -0
  24. package/lib/docker.js +133 -0
  25. package/lib/exporter-engine.js +112 -0
  26. package/lib/exporter.js +115 -0
  27. package/lib/server.js +71 -0
  28. package/lib/util.js +6 -0
  29. package/myt-clean.js +52 -0
  30. package/myt-dump.js +71 -0
  31. package/myt-fixtures.js +28 -0
  32. package/myt-init.js +25 -0
  33. package/myt-pull.js +129 -0
  34. package/myt-push.js +475 -0
  35. package/myt-run.js +160 -0
  36. package/myt-start.js +51 -0
  37. package/myt-version.js +219 -0
  38. package/myt.js +646 -0
  39. package/package.json +43 -0
  40. package/server/Dockerfile.base +1 -0
  41. package/server/Dockerfile.client +14 -0
  42. package/server/Dockerfile.dump +16 -0
  43. package/server/Dockerfile.server +25 -0
  44. package/server/docker-dump.sh +4 -0
  45. package/server/docker-fixtures.sh +4 -0
  46. package/server/docker-import.sh +6 -0
  47. package/server/docker-init.sh +20 -0
  48. package/server/docker-start.sh +10 -0
  49. package/server/docker.cnf +9 -0
  50. package/template/dump/fixtures.sql +1 -0
  51. package/template/dump/structure.sql +1 -0
  52. package/template/myvc.config.yml +11 -0
  53. package/template/package.json +13 -0
  54. package/template/remotes/local.ini +5 -0
  55. package/template/routines/README.md +1 -0
  56. package/template/versions/00001-firstVersion/00-firstScript.sql +1 -0
  57. package/template/versions/README.md +1 -0
package/myt-run.js ADDED
@@ -0,0 +1,160 @@
1
+
2
+ const Myt = require('./myt');
3
+ const Command = require('./lib/command');
4
+ const Push = require('./myt-push');
5
+ const docker = require('./lib/docker');
6
+ const fs = require('fs-extra');
7
+ const path = require('path');
8
+ const Server = require('./lib/server');
9
+
10
+ /**
11
+ * Builds the database image and runs a container. It only rebuilds the
12
+ * image when fixtures have been modified or when the day on which the
13
+ * image was built is different to today. Some workarounds have been used
14
+ * to avoid a bug with OverlayFS driver on MacOS.
15
+ */
16
+ class Run extends Command {
17
+ static usage = {
18
+ description: 'Build and start local database server container',
19
+ params: {
20
+ ci: 'Workaround for continuous integration system',
21
+ random: 'Whether to use a random container name or port'
22
+ }
23
+ };
24
+
25
+ static localOpts = {
26
+ alias: {
27
+ ci: 'c',
28
+ random: 'r'
29
+ },
30
+ boolean: [
31
+ 'ci',
32
+ 'random'
33
+ ]
34
+ };
35
+
36
+ async run(myt, opts) {
37
+ const dumpDir = opts.dumpDir;
38
+ const serverDir = path.join(__dirname, 'server');
39
+
40
+ if (!await fs.pathExists(`${dumpDir}/.dump.sql`))
41
+ throw new Error('To run local database you have to create a dump first');
42
+
43
+ // Build base server image
44
+
45
+ let serverDockerfile = path.join(dumpDir, 'Dockerfile');
46
+ if (!await fs.pathExists(serverDockerfile))
47
+ serverDockerfile = path.join(serverDir, 'Dockerfile.base');
48
+
49
+ await docker.build(__dirname, {
50
+ tag: 'myt/base',
51
+ file: serverDockerfile
52
+ }, opts.debug);
53
+
54
+ // Build myt server image
55
+
56
+ await docker.build(__dirname, {
57
+ tag: 'myt/server',
58
+ file: path.join(serverDir, 'Dockerfile.server')
59
+ }, opts.debug);
60
+
61
+ // Build dump image
62
+
63
+ const today = new Date();
64
+ const pad = v => v < 10 ? '0' + v : v;
65
+ const year = today.getFullYear();
66
+ const month = pad(today.getMonth() + 1);
67
+ const day = pad(today.getDate());
68
+ const stamp = `${year}-${month}-${day}`;
69
+
70
+ await docker.build(opts.mytDir, {
71
+ tag: opts.code,
72
+ file: path.join(serverDir, 'Dockerfile.dump'),
73
+ buildArg: `STAMP=${stamp}`
74
+ }, opts.debug);
75
+
76
+ // Run container
77
+
78
+ const isRandom = opts.random;
79
+ const dbConfig = Object.assign({}, opts.dbConfig);
80
+
81
+ let runOptions;
82
+
83
+ if (isRandom)
84
+ runOptions = {publish: '3306'};
85
+ else {
86
+ runOptions = {
87
+ name: opts.code,
88
+ publish: `3306:${dbConfig.port}`
89
+ };
90
+ try {
91
+ const server = new Server(new docker.Container(opts.code));
92
+ await server.rm();
93
+ } catch (e) {}
94
+ }
95
+
96
+ const runChown = process.platform != 'linux';
97
+
98
+ Object.assign(runOptions, null, {
99
+ env: `RUN_CHOWN=${runChown}`,
100
+ detach: true,
101
+ volume: `${path.join(dumpDir, 'fixtures.sql')}:/fixtures.sql:ro`
102
+ });
103
+ const ct = await docker.run(opts.code, null, runOptions);
104
+ const server = new Server(ct, dbConfig);
105
+
106
+ if (isRandom) {
107
+ try {
108
+ const netSettings = await ct.inspect({
109
+ format: '{{json .NetworkSettings}}'
110
+ });
111
+
112
+ if (opts.ci)
113
+ dbConfig.host = netSettings.Gateway;
114
+
115
+ dbConfig.port = netSettings.Ports['3306/tcp'][0].HostPort;
116
+ } catch (err) {
117
+ await server.rm();
118
+ throw err;
119
+ }
120
+ }
121
+
122
+ await server.wait();
123
+
124
+ // Apply changes
125
+
126
+ Object.assign(opts, {
127
+ commit: true,
128
+ dbConfig
129
+ });
130
+ await myt.runCommand(Push, opts);
131
+
132
+ // Apply fixtures
133
+
134
+ console.log('Applying fixtures.');
135
+ await ct.exec(null,
136
+ 'docker-import.sh', ['/fixtures'], 'spawn', opts.debug);
137
+
138
+ // Create triggers
139
+
140
+ console.log('Creating triggers.');
141
+ const conn = await myt.createConnection();
142
+
143
+ for (const schema of opts.schemas) {
144
+ const triggersPath = `${opts.routinesDir}/${schema}/triggers`;
145
+ if (!await fs.pathExists(triggersPath))
146
+ continue;
147
+
148
+ const triggersDir = await fs.readdir(triggersPath);
149
+ for (const triggerFile of triggersDir)
150
+ await myt.queryFromFile(conn, `${triggersPath}/${triggerFile}`);
151
+ }
152
+
153
+ return server;
154
+ }
155
+ }
156
+
157
+ module.exports = Run;
158
+
159
+ if (require.main === module)
160
+ new Myt().run(Run);
package/myt-start.js ADDED
@@ -0,0 +1,51 @@
1
+
2
+ const Myt = require('./myt');
3
+ const Command = require('./lib/command');
4
+ const Container = require('./lib/docker').Container;
5
+ const Server = require('./lib/server');
6
+ const Run = require('./myt-run');
7
+
8
+ /**
9
+ * Does the minium effort to start the database container, if it doesn't
10
+ * exists calls the run command, if it is started does nothing. Keep in
11
+ * mind that when you do not rebuild the docker you may be using an outdated
12
+ * version of it.
13
+ */
14
+ class Start extends Command {
15
+ static usage = {
16
+ description: 'Start local database server container'
17
+ };
18
+
19
+ async run(myt, opts) {
20
+ const ct = new Container(opts.code);
21
+ let status;
22
+
23
+ try {
24
+ status = await ct.inspect({
25
+ format: '{{json .State.Status}}'
26
+ });
27
+ } catch (err) {
28
+ const run = new Run()
29
+ return await run.run(myt, opts);
30
+ }
31
+
32
+ switch (status) {
33
+ case 'running':
34
+ break;
35
+ case 'exited':
36
+ await ct.start();
37
+ break;
38
+ default:
39
+ throw new Error(`Unknown docker status: ${status}`);
40
+ }
41
+
42
+ const server = new Server(ct, opts.dbConfig);
43
+ await server.wait();
44
+ return server;
45
+ }
46
+ }
47
+
48
+ module.exports = Start;
49
+
50
+ if (require.main === module)
51
+ new Myt().run(Start);
package/myt-version.js ADDED
@@ -0,0 +1,219 @@
1
+
2
+ const Myt = require('./myt');
3
+ const Command = require('./lib/command');
4
+ const fs = require('fs-extra');
5
+
6
+ /**
7
+ * Creates a new version.
8
+ */
9
+ class Version extends Command {
10
+ static usage = {
11
+ description: 'Creates a new version',
12
+ params: {
13
+ name: 'Name for the new version'
14
+ },
15
+ operand: 'name'
16
+ };
17
+
18
+ static localOpts = {
19
+ alias: {
20
+ name: 'n'
21
+ },
22
+ string: [
23
+ 'name'
24
+ ],
25
+ default: {
26
+ remote: 'production'
27
+ }
28
+ };
29
+
30
+ async run(myt, opts) {
31
+ let newVersionDir;
32
+
33
+ // Fetch last version number
34
+
35
+ const conn = await myt.dbConnect();
36
+
37
+ try {
38
+ await conn.query('START TRANSACTION');
39
+
40
+ const [[row]] = await conn.query(
41
+ `SELECT number, lastNumber
42
+ FROM version
43
+ WHERE code = ?
44
+ FOR UPDATE`,
45
+ [opts.code]
46
+ );
47
+ const number = row && row.number;
48
+ const lastNumber = row && row.lastNumber;
49
+
50
+ console.log(
51
+ `Database information:`
52
+ + `\n -> Version: ${number}`
53
+ + `\n -> Last version: ${lastNumber}`
54
+ );
55
+
56
+ let newVersion;
57
+ if (lastNumber)
58
+ newVersion = Math.max(
59
+ parseInt(number) || 0,
60
+ parseInt(lastNumber) || 0
61
+ ) + 1;
62
+ else
63
+ newVersion = 1;
64
+
65
+ const versionDigits = number
66
+ ? number.length
67
+ : opts.versionDigits;
68
+
69
+ newVersion = String(newVersion).padStart(versionDigits, '0');
70
+
71
+ // Get version name
72
+
73
+ let versionName = opts.name;
74
+
75
+ const versionNames = new Set();
76
+ const versionDirs = await fs.readdir(opts.versionsDir);
77
+ for (const versionDir of versionDirs) {
78
+ const dirVersion = myt.parseVersionDir(versionDir);
79
+ if (!dirVersion) continue;
80
+ versionNames.add(dirVersion.name);
81
+ }
82
+
83
+ if (!versionName) {
84
+ let attempts;
85
+ const maxAttempts = 1000;
86
+
87
+ for (attempts = 0; attempts < maxAttempts; attempts++) {
88
+ versionName = randomName();
89
+ if (!versionNames.has(versionName)) break;
90
+ }
91
+
92
+ if (attempts === maxAttempts)
93
+ throw new Error(`Cannot create a unique version name after ${attempts} attempts`);
94
+ } else {
95
+ const isNameValid = typeof versionName === 'string'
96
+ && /^[a-zA-Z0-9]+$/.test(versionName);
97
+ if (!isNameValid)
98
+ throw new Error('Version name can only contain letters or numbers');
99
+ if (versionNames.has(versionName))
100
+ throw new Error('Version with same name already exists');
101
+ }
102
+
103
+ // Create version
104
+
105
+ const versionFolder = `${newVersion}-${versionName}`;
106
+ newVersionDir = `${opts.versionsDir}/${versionFolder}`;
107
+
108
+ await conn.query(
109
+ `INSERT INTO version
110
+ SET code = ?,
111
+ lastNumber = ?
112
+ ON DUPLICATE KEY UPDATE
113
+ lastNumber = VALUES(lastNumber)`,
114
+ [opts.code, newVersion]
115
+ );
116
+ await fs.mkdir(newVersionDir);
117
+ await fs.writeFile(
118
+ `${newVersionDir}/00-firstScript.sql`,
119
+ '-- Place your SQL code here\n'
120
+ );
121
+ console.log(`New version created: ${versionFolder}`);
122
+
123
+ await conn.query('COMMIT');
124
+ } catch (err) {
125
+ await conn.query('ROLLBACK');
126
+ if (newVersionDir && await fs.pathExists(newVersionDir))
127
+ await fs.remove(newVersionDir, {recursive: true});
128
+ throw err;
129
+ }
130
+ }
131
+ }
132
+
133
+ function randomName() {
134
+ const color = random(colors);
135
+ let plant = random(plants);
136
+ plant = plant.charAt(0).toUpperCase() + plant.slice(1);
137
+ return color + plant;
138
+ }
139
+
140
+ function random(array) {
141
+ return array[Math.floor(Math.random() * array.length)];
142
+ }
143
+
144
+ const colors = [
145
+ 'aqua',
146
+ 'azure',
147
+ 'black',
148
+ 'blue',
149
+ 'bronze',
150
+ 'brown',
151
+ 'chocolate',
152
+ 'crimson',
153
+ 'golden',
154
+ 'gray',
155
+ 'green',
156
+ 'lime',
157
+ 'maroon',
158
+ 'navy',
159
+ 'orange',
160
+ 'pink',
161
+ 'purple',
162
+ 'red',
163
+ 'salmon',
164
+ 'silver',
165
+ 'teal',
166
+ 'turquoise',
167
+ 'yellow',
168
+ 'wheat',
169
+ 'white'
170
+ ];
171
+
172
+ const plants = [
173
+ 'anthurium',
174
+ 'aralia',
175
+ 'arborvitae',
176
+ 'asparagus',
177
+ 'aspidistra',
178
+ 'bamboo',
179
+ 'birch',
180
+ 'carnation',
181
+ 'camellia',
182
+ 'cataractarum',
183
+ 'chico',
184
+ 'chrysanthemum',
185
+ 'cordyline',
186
+ 'cyca',
187
+ 'cymbidium',
188
+ 'dendro',
189
+ 'dracena',
190
+ 'erica',
191
+ 'eucalyptus',
192
+ 'fern',
193
+ 'galax',
194
+ 'gerbera',
195
+ 'hydrangea',
196
+ 'ivy',
197
+ 'laurel',
198
+ 'lilium',
199
+ 'mastic',
200
+ 'medeola',
201
+ 'monstera',
202
+ 'moss',
203
+ 'oak',
204
+ 'orchid',
205
+ 'palmetto',
206
+ 'paniculata',
207
+ 'phormium',
208
+ 'raphis',
209
+ 'roebelini',
210
+ 'rose',
211
+ 'ruscus',
212
+ 'salal',
213
+ 'tulip'
214
+ ];
215
+
216
+ module.exports = Version;
217
+
218
+ if (require.main === module)
219
+ new Myt().run(Version);