@verdnatura/myt 1.8.6 → 2.0.2
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 +80 -52
- 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 +35 -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 +283 -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-apply.js
ADDED
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
const Myt = require('./myt');
|
|
2
|
+
const Command = require('./lib/command');
|
|
3
|
+
const Push = require('./myt-push');
|
|
4
|
+
const fs = require('fs-extra');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
const connExt = require('./lib/conn');
|
|
7
|
+
const SqlString = require('sqlstring');
|
|
8
|
+
const spawn = require('child_process').spawn;
|
|
9
|
+
|
|
10
|
+
class Apply extends Command {
|
|
11
|
+
static usage = {
|
|
12
|
+
description: 'Initialize database',
|
|
13
|
+
params: {
|
|
14
|
+
structure: 'Apply only structure',
|
|
15
|
+
changes: 'Apply only changes',
|
|
16
|
+
realm: 'Name of fixture realm to use',
|
|
17
|
+
docker: 'Whether to load git changes from file',
|
|
18
|
+
load: 'Commit SHA to save'
|
|
19
|
+
},
|
|
20
|
+
operand: 'realm'
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
static args = {
|
|
24
|
+
alias: {
|
|
25
|
+
structure: 's',
|
|
26
|
+
changes: 'c',
|
|
27
|
+
realm: 'm',
|
|
28
|
+
docker: 'k',
|
|
29
|
+
load: 'l'
|
|
30
|
+
},
|
|
31
|
+
boolean: [
|
|
32
|
+
'structure',
|
|
33
|
+
'changes',
|
|
34
|
+
'docker'
|
|
35
|
+
],
|
|
36
|
+
string: [
|
|
37
|
+
'realm',
|
|
38
|
+
'load'
|
|
39
|
+
],
|
|
40
|
+
default: {
|
|
41
|
+
remote: 'socket'
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
static reporter = {
|
|
46
|
+
mockingDate: 'Mocking date functions.',
|
|
47
|
+
applyingFixtures: 'Applying fixtures.',
|
|
48
|
+
applyingRealm: 'Applying realm fixtures.',
|
|
49
|
+
creatingTriggers: 'Creating triggers.',
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
async _run(myt, ctx, cfg, opts) {
|
|
53
|
+
const {
|
|
54
|
+
structureDir,
|
|
55
|
+
dumpDir,
|
|
56
|
+
fixturesDir
|
|
57
|
+
} = ctx;
|
|
58
|
+
|
|
59
|
+
const {triggersImport} = cfg;
|
|
60
|
+
|
|
61
|
+
const conn = await myt.createConnection();
|
|
62
|
+
const applyAll = !(opts.structure || opts.changes);
|
|
63
|
+
|
|
64
|
+
if (ctx.isProtectedRemote)
|
|
65
|
+
throw new Error('Cannot apply to protected remote');
|
|
66
|
+
|
|
67
|
+
if (applyAll || opts.structure) {
|
|
68
|
+
await this.importFile(path.join(structureDir, 'before.sql'));
|
|
69
|
+
|
|
70
|
+
const importFiles = [
|
|
71
|
+
'structure.sql',
|
|
72
|
+
'data.sql',
|
|
73
|
+
];
|
|
74
|
+
|
|
75
|
+
if (triggersImport == 'before')
|
|
76
|
+
importFiles.push('triggers.sql');
|
|
77
|
+
|
|
78
|
+
importFiles.push('privileges.sql');
|
|
79
|
+
|
|
80
|
+
for (const file of importFiles)
|
|
81
|
+
await this.importFile(path.join(dumpDir, file), true);
|
|
82
|
+
|
|
83
|
+
await this.importFile(path.join(structureDir, 'after.sql'));
|
|
84
|
+
|
|
85
|
+
// Mock date functions
|
|
86
|
+
|
|
87
|
+
this.emit('mockingDate');
|
|
88
|
+
const mockDateScript = path.join(structureDir, 'mock-date.sql');
|
|
89
|
+
|
|
90
|
+
if (cfg.mockDate) {
|
|
91
|
+
if (!await fs.pathExists(mockDateScript))
|
|
92
|
+
throw new Error(`Date mock enabled but mock script does not exist: ${mockDateScript}`);
|
|
93
|
+
|
|
94
|
+
let sql = await fs.readFile(mockDateScript, 'utf8');
|
|
95
|
+
sql = sql.replace(/@mockDate/g, SqlString.escape(cfg.mockDate));
|
|
96
|
+
await connExt.multiQuery(conn, sql);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (applyAll || opts.changes) {
|
|
101
|
+
// Apply changes
|
|
102
|
+
|
|
103
|
+
await myt.run(Push, {
|
|
104
|
+
triggers: triggersImport == 'after',
|
|
105
|
+
docker: true,
|
|
106
|
+
load: opts.load,
|
|
107
|
+
commit: true
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
// Apply fixtures
|
|
111
|
+
|
|
112
|
+
this.emit('applyingFixtures');
|
|
113
|
+
const fixturesFiles = [
|
|
114
|
+
['before.sql'],
|
|
115
|
+
['.dump.sql', true],
|
|
116
|
+
['after.sql'],
|
|
117
|
+
['local.sql'],
|
|
118
|
+
];
|
|
119
|
+
for (const [file, force] of fixturesFiles)
|
|
120
|
+
await this.importFile(path.join(fixturesDir, file), force);
|
|
121
|
+
|
|
122
|
+
// Apply realms
|
|
123
|
+
|
|
124
|
+
if (opts.realm) {
|
|
125
|
+
this.emit('applyingRealm');
|
|
126
|
+
const realmDir = `realms/${opts.realm}`;
|
|
127
|
+
let realmFiles = await fs.readdir(realmDir);
|
|
128
|
+
realmFiles = realmFiles.map(file => path.parse(file).name);
|
|
129
|
+
for (const file of realmFiles) {
|
|
130
|
+
await this.importFile(path.join(realmDir, `${file}.sql`));
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Create triggers
|
|
135
|
+
|
|
136
|
+
if (triggersImport == 'after') {
|
|
137
|
+
this.emit('creatingTriggers');
|
|
138
|
+
|
|
139
|
+
for (const schema of cfg.schemas) {
|
|
140
|
+
const triggersPath = `${ctx.routinesDir}/${schema}/triggers`;
|
|
141
|
+
if (!await fs.pathExists(triggersPath))
|
|
142
|
+
continue;
|
|
143
|
+
|
|
144
|
+
const triggersDir = await fs.readdir(triggersPath);
|
|
145
|
+
for (const triggerFile of triggersDir)
|
|
146
|
+
await connExt.queryFromFile(conn, `${triggersPath}/${triggerFile}`);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
await conn.end();
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
async importFile(file, force) {
|
|
155
|
+
const {cfg, ctx} = this;
|
|
156
|
+
|
|
157
|
+
if (!await fs.exists(file)) {
|
|
158
|
+
if (cfg.debug)
|
|
159
|
+
console.debug('Import:', `${file} does not exist, ignoring`);
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const iniPath = path.join(ctx.mytDir, 'remotes', ctx.iniFile);
|
|
164
|
+
const execArgs = [
|
|
165
|
+
`--defaults-file=${iniPath}`,
|
|
166
|
+
'--default-character-set=utf8',
|
|
167
|
+
'--comments',
|
|
168
|
+
];
|
|
169
|
+
|
|
170
|
+
if (force)
|
|
171
|
+
execArgs.push('--force');
|
|
172
|
+
|
|
173
|
+
let stdio;
|
|
174
|
+
const mysqlBin = 'mariadb';
|
|
175
|
+
if (cfg.debug === true) {
|
|
176
|
+
const quotedArgs = execArgs
|
|
177
|
+
.map(x => /\s/g.test(x) ? `"${x}"` : x)
|
|
178
|
+
.join(' ');
|
|
179
|
+
console.debug('Command:', `${mysqlBin} ${quotedArgs} < ${file}`.yellow);
|
|
180
|
+
|
|
181
|
+
stdio = ['pipe', 'inherit', 'inherit'];
|
|
182
|
+
} else {
|
|
183
|
+
stdio = ['pipe', 'ignore', 'ignore'];
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const child = spawn(mysqlBin, execArgs, {stdio});
|
|
187
|
+
fs.createReadStream(file).pipe(child.stdin);
|
|
188
|
+
|
|
189
|
+
return await new Promise((resolve, reject) => {
|
|
190
|
+
child.on('exit', code => {
|
|
191
|
+
if (code !== 0)
|
|
192
|
+
reject(new Error(`${mysqlBin} exit code ${code}`));
|
|
193
|
+
else
|
|
194
|
+
resolve(code);
|
|
195
|
+
});
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
module.exports = Apply;
|
|
201
|
+
|
|
202
|
+
if (require.main === module)
|
|
203
|
+
new Myt().cli(Apply);
|
package/myt-build.js
ADDED
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
const Myt = require('./myt');
|
|
2
|
+
const Command = require('./lib/command');
|
|
3
|
+
const docker = require('./lib/docker');
|
|
4
|
+
const fs = require('fs-extra');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
const {hashElement} = require('folder-hash');
|
|
7
|
+
const {createHash} = require('crypto');
|
|
8
|
+
|
|
9
|
+
class Build extends Command {
|
|
10
|
+
static usage = {
|
|
11
|
+
description: 'Build local database server container',
|
|
12
|
+
params: {
|
|
13
|
+
name: 'Built image name',
|
|
14
|
+
tag: 'Built image tag',
|
|
15
|
+
force: 'Whether to force build',
|
|
16
|
+
realm: 'Name of fixture realm to use'
|
|
17
|
+
},
|
|
18
|
+
operand: 'realm'
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
static args = {
|
|
22
|
+
alias: {
|
|
23
|
+
name: 'n',
|
|
24
|
+
tag: 't',
|
|
25
|
+
force: 'f',
|
|
26
|
+
realm: 'm'
|
|
27
|
+
},
|
|
28
|
+
string: [
|
|
29
|
+
'name',
|
|
30
|
+
'tag',
|
|
31
|
+
'realm'
|
|
32
|
+
],
|
|
33
|
+
boolean: [
|
|
34
|
+
'force'
|
|
35
|
+
]
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
static reporter = {
|
|
39
|
+
buildingBaseImages: 'Building base images.',
|
|
40
|
+
buildingServerImage: 'Building server image.',
|
|
41
|
+
serverImageTag: function(tag) {
|
|
42
|
+
console.log('Image:', tag);
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
async _run(myt, ctx, cfg, opts) {
|
|
47
|
+
const {
|
|
48
|
+
dumpDir,
|
|
49
|
+
dockerDir,
|
|
50
|
+
version,
|
|
51
|
+
subdir
|
|
52
|
+
} = ctx;
|
|
53
|
+
|
|
54
|
+
const {
|
|
55
|
+
realm,
|
|
56
|
+
force
|
|
57
|
+
} = opts;
|
|
58
|
+
|
|
59
|
+
const serverDir = path.join(__dirname, 'docker/server');
|
|
60
|
+
|
|
61
|
+
if (!await fs.pathExists(`${dumpDir}/structure.sql`))
|
|
62
|
+
throw new Error('Dump file not found');
|
|
63
|
+
|
|
64
|
+
// Initialize
|
|
65
|
+
|
|
66
|
+
const imageLabels = [`myt.version=${version}`];
|
|
67
|
+
const tagHash = createHash('sha1');
|
|
68
|
+
const repo = await myt.openRepo();
|
|
69
|
+
|
|
70
|
+
const commit = await repo.getHeadCommit();
|
|
71
|
+
const commitSha = commit?.sha() || '';
|
|
72
|
+
tagHash.update(commitSha);
|
|
73
|
+
imageLabels.push(`myt.commit-sha=${commitSha}`);
|
|
74
|
+
|
|
75
|
+
// Get changed files hash
|
|
76
|
+
|
|
77
|
+
const repoExt = require('./lib/repo');
|
|
78
|
+
const regex = subdir && new RegExp(`^${subdir}\/(.+)`);
|
|
79
|
+
const gitChanges = new Set();
|
|
80
|
+
const dirnames = new Set();
|
|
81
|
+
const {sep} = path;
|
|
82
|
+
|
|
83
|
+
async function pushChanges(diff) {
|
|
84
|
+
if (!diff) return;
|
|
85
|
+
const patches = await diff.patches();
|
|
86
|
+
for (const patch of patches) {
|
|
87
|
+
let filePath = patch.newFile().path();
|
|
88
|
+
if (regex) {
|
|
89
|
+
const match = filePath.match(regex);
|
|
90
|
+
if (!match) continue;
|
|
91
|
+
}
|
|
92
|
+
gitChanges.add(filePath);
|
|
93
|
+
|
|
94
|
+
const parts = path.dirname(filePath).split(sep);
|
|
95
|
+
for(let i = 0; i < parts.length; i++)
|
|
96
|
+
dirnames.add(parts.slice(0, i + 1).join(sep));
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
await pushChanges(await repoExt.getUnstaged(repo));
|
|
101
|
+
await pushChanges(await repoExt.getStaged(repo));
|
|
102
|
+
|
|
103
|
+
if (cfg.debug)
|
|
104
|
+
console.debug('Uncommited:'.magenta, gitChanges);
|
|
105
|
+
|
|
106
|
+
let changesSha;
|
|
107
|
+
if (gitChanges.size) {
|
|
108
|
+
const {hash} = await hashElement(subdir, {
|
|
109
|
+
algo: 'sha1',
|
|
110
|
+
encoding: 'hex',
|
|
111
|
+
folders: {
|
|
112
|
+
include: [...dirnames],
|
|
113
|
+
matchBasename: false,
|
|
114
|
+
matchPath: true
|
|
115
|
+
},
|
|
116
|
+
files: {
|
|
117
|
+
include: [...gitChanges],
|
|
118
|
+
matchBasename: false,
|
|
119
|
+
matchPath: true
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
changesSha = hash;
|
|
124
|
+
tagHash.update(changesSha);
|
|
125
|
+
imageLabels.push(`myt.changes-sha=${changesSha}`);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (realm)
|
|
129
|
+
imageLabels.push(`myt.realm=${realm}`);
|
|
130
|
+
|
|
131
|
+
// Get image tag
|
|
132
|
+
|
|
133
|
+
const tagSha = tagHash.digest('hex');
|
|
134
|
+
const shortSha = tagSha.substring(0, cfg.gitShortLen);
|
|
135
|
+
|
|
136
|
+
let imageTag = shortSha;
|
|
137
|
+
if (realm) imageTag += `-${realm}`;
|
|
138
|
+
|
|
139
|
+
const imageName = opts.name || cfg.code;
|
|
140
|
+
const tag = `${imageName}:${imageTag}`;
|
|
141
|
+
|
|
142
|
+
// Check for existent image
|
|
143
|
+
|
|
144
|
+
let labels;
|
|
145
|
+
try {
|
|
146
|
+
labels = await docker.inspect(tag, {
|
|
147
|
+
format: '{{json .Config.Labels}}'
|
|
148
|
+
});
|
|
149
|
+
} catch (err) {
|
|
150
|
+
if (err.code !== 1) throw err;
|
|
151
|
+
labels = {};
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const useCache = !force
|
|
155
|
+
&& labels['myt.version'] == version
|
|
156
|
+
&& labels['myt.commit-sha'] == commitSha
|
|
157
|
+
&& (
|
|
158
|
+
(!changesSha && !labels['myt.changes-sha'])
|
|
159
|
+
|| (labels['myt.changes-sha'] == changesSha)
|
|
160
|
+
)
|
|
161
|
+
&& (
|
|
162
|
+
(!realm && !labels['myt.realm'])
|
|
163
|
+
|| (labels['myt.realm'] == realm)
|
|
164
|
+
);
|
|
165
|
+
|
|
166
|
+
if (!useCache) {
|
|
167
|
+
// Build base server image
|
|
168
|
+
|
|
169
|
+
const baseTag = `myt/base:${version}`;
|
|
170
|
+
const mytTag = `myt/app:${version}`;
|
|
171
|
+
|
|
172
|
+
let baseLabels;
|
|
173
|
+
try {
|
|
174
|
+
baseLabels = await docker.inspect(baseTag,
|
|
175
|
+
{format: '{{json .Config.Labels}}'}
|
|
176
|
+
);
|
|
177
|
+
} catch (err) {
|
|
178
|
+
if (err.code !== 1) throw err;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
let buildBase = !baseLabels;
|
|
182
|
+
|
|
183
|
+
const buildBaseLabels = [];
|
|
184
|
+
const dbDockerfile = path.join(dockerDir, 'Dockerfile');
|
|
185
|
+
const dbDockerExists = await fs.pathExists(dbDockerfile);
|
|
186
|
+
|
|
187
|
+
if (!buildBase && dbDockerExists) {
|
|
188
|
+
const {hash} = await hashElement(dockerDir, {
|
|
189
|
+
algo: 'sha1',
|
|
190
|
+
encoding: 'hex',
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
buildBase = baseLabels['myt.server-sha'] != hash;
|
|
194
|
+
buildBaseLabels.push(`myt.server-sha=${hash}`);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
if (buildBase || force) {
|
|
198
|
+
this.emit('buildingBaseImages');
|
|
199
|
+
|
|
200
|
+
let dbTag;
|
|
201
|
+
if (dbDockerExists) {
|
|
202
|
+
dbTag = `myt/db:${version}`;
|
|
203
|
+
await docker.build(dockerDir, {
|
|
204
|
+
tag: dbTag,
|
|
205
|
+
file: dbDockerfile,
|
|
206
|
+
label: buildBaseLabels,
|
|
207
|
+
}, cfg.debug);
|
|
208
|
+
} else {
|
|
209
|
+
dbTag = cfg.dbImageTag;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
const buildArgs = [];
|
|
213
|
+
if (dbTag)
|
|
214
|
+
buildArgs.push(`DB_TAG=${dbTag}`);
|
|
215
|
+
|
|
216
|
+
await docker.build(__dirname, {
|
|
217
|
+
tag: baseTag,
|
|
218
|
+
file: path.join(serverDir, 'Dockerfile.base'),
|
|
219
|
+
buildArg: buildArgs,
|
|
220
|
+
label: buildBaseLabels,
|
|
221
|
+
}, cfg.debug);
|
|
222
|
+
|
|
223
|
+
await docker.build(__dirname, {
|
|
224
|
+
tag: mytTag,
|
|
225
|
+
file: path.join(serverDir, 'Dockerfile.myt'),
|
|
226
|
+
buildArg: `BASE_TAG=${baseTag}`,
|
|
227
|
+
label: buildBaseLabels,
|
|
228
|
+
}, cfg.debug);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// Build dump image
|
|
232
|
+
|
|
233
|
+
this.emit('buildingServerImage');
|
|
234
|
+
|
|
235
|
+
let dbCommit;
|
|
236
|
+
const versionFile = path.join(dumpDir, 'version.json');
|
|
237
|
+
if (await fs.exists(versionFile)) {
|
|
238
|
+
const dbVersion = JSON.parse(await fs.readFile(versionFile, 'utf8'));
|
|
239
|
+
dbCommit = dbVersion.gitCommit;
|
|
240
|
+
}
|
|
241
|
+
const changes = await myt.getChanges(dbCommit);
|
|
242
|
+
|
|
243
|
+
const changesFile = path.join(ctx.routinesDir, '.changes.json');
|
|
244
|
+
await fs.writeFile(
|
|
245
|
+
changesFile,
|
|
246
|
+
JSON.stringify(changes, null, 1)
|
|
247
|
+
);
|
|
248
|
+
|
|
249
|
+
try {
|
|
250
|
+
const buildArgs = [
|
|
251
|
+
`ROOT_PASS=${cfg.rootPassword}`,
|
|
252
|
+
`BASE_TAG=${baseTag}`,
|
|
253
|
+
`MYT_TAG=${mytTag}`,
|
|
254
|
+
`MYT_COMMIT=${commitSha}`
|
|
255
|
+
];
|
|
256
|
+
|
|
257
|
+
if (realm)
|
|
258
|
+
buildArgs.push(`MYT_REALM=${realm}`);
|
|
259
|
+
|
|
260
|
+
const tags = [tag, `${imageName}:latest`];
|
|
261
|
+
if (opts.tag)
|
|
262
|
+
tags.push(`${imageName}:${opts.tag}`);
|
|
263
|
+
|
|
264
|
+
await docker.build(ctx.mytDir, {
|
|
265
|
+
tag: tags,
|
|
266
|
+
file: path.join(serverDir, 'Dockerfile'),
|
|
267
|
+
label: imageLabels,
|
|
268
|
+
buildArg: buildArgs
|
|
269
|
+
}, cfg.debug);
|
|
270
|
+
} finally {
|
|
271
|
+
await fs.unlink(changesFile);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
this.emit('serverImageTag', tag);
|
|
276
|
+
return tag;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
module.exports = Build;
|
|
281
|
+
|
|
282
|
+
if (require.main === module)
|
|
283
|
+
new Myt().cli(Build);
|
package/myt-clean.js
CHANGED
|
@@ -14,7 +14,7 @@ class Clean extends Command {
|
|
|
14
14
|
}
|
|
15
15
|
};
|
|
16
16
|
|
|
17
|
-
static
|
|
17
|
+
static args = {
|
|
18
18
|
alias: {
|
|
19
19
|
purge: 'p'
|
|
20
20
|
},
|
|
@@ -41,15 +41,17 @@ class Clean extends Command {
|
|
|
41
41
|
}
|
|
42
42
|
};
|
|
43
43
|
|
|
44
|
-
async
|
|
44
|
+
async _run(myt, ctx, cfg, opts) {
|
|
45
|
+
const {versionsDir} = ctx;
|
|
46
|
+
|
|
45
47
|
const conn = await myt.dbConnect();
|
|
46
|
-
const archiveDir = path.join(
|
|
48
|
+
const archiveDir = path.join(versionsDir, '.archive');
|
|
47
49
|
|
|
48
50
|
const dbVersion = await myt.fetchDbVersion() || {};
|
|
49
51
|
const number = parseInt(dbVersion.number);
|
|
50
52
|
|
|
51
53
|
const oldVersions = [];
|
|
52
|
-
const versionDirs = await fs.readdir(
|
|
54
|
+
const versionDirs = await fs.readdir(versionsDir);
|
|
53
55
|
for (const versionDir of versionDirs) {
|
|
54
56
|
const version = await myt.loadVersion(versionDir);
|
|
55
57
|
const shouldArchive = version
|
|
@@ -60,15 +62,17 @@ class Clean extends Command {
|
|
|
60
62
|
oldVersions.push(versionDir);
|
|
61
63
|
}
|
|
62
64
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
65
|
+
const {maxOldVersions} = cfg;
|
|
66
|
+
|
|
67
|
+
if (maxOldVersions
|
|
68
|
+
&& oldVersions.length > maxOldVersions) {
|
|
69
|
+
oldVersions.splice(-maxOldVersions);
|
|
66
70
|
|
|
67
71
|
if (!await fs.pathExists(archiveDir))
|
|
68
72
|
await fs.mkdir(archiveDir);
|
|
69
73
|
|
|
70
74
|
for (const oldVersion of oldVersions) {
|
|
71
|
-
const srcDir = path.join(
|
|
75
|
+
const srcDir = path.join(versionsDir, oldVersion);
|
|
72
76
|
const dstDir = path.join(archiveDir, oldVersion);
|
|
73
77
|
|
|
74
78
|
if (!await fs.pathExists(dstDir))
|
|
@@ -91,7 +95,7 @@ class Clean extends Command {
|
|
|
91
95
|
this.emit('versionsArchived');
|
|
92
96
|
|
|
93
97
|
if (opts.purge) {
|
|
94
|
-
const versionDb = new VersionDb(myt,
|
|
98
|
+
const versionDb = new VersionDb(myt, versionsDir);
|
|
95
99
|
versionDb.load();
|
|
96
100
|
|
|
97
101
|
const archiveDb = new VersionDb(myt, archiveDir);
|
|
@@ -101,7 +105,7 @@ class Clean extends Command {
|
|
|
101
105
|
`SELECT number, file FROM versionLog
|
|
102
106
|
WHERE code = ?
|
|
103
107
|
ORDER BY number, file`,
|
|
104
|
-
[
|
|
108
|
+
[cfg.code]
|
|
105
109
|
);
|
|
106
110
|
|
|
107
111
|
let nPurged = 0;
|
|
@@ -113,7 +117,7 @@ class Clean extends Command {
|
|
|
113
117
|
await conn.query(
|
|
114
118
|
`DELETE FROM versionLog
|
|
115
119
|
WHERE code = ? AND number = ? AND file = ?`,
|
|
116
|
-
[
|
|
120
|
+
[cfg.code, script.number, script.file]
|
|
117
121
|
);
|
|
118
122
|
nPurged++;
|
|
119
123
|
}
|
package/myt-create.js
CHANGED
|
@@ -13,7 +13,7 @@ class Create extends Command {
|
|
|
13
13
|
operand: 'name'
|
|
14
14
|
};
|
|
15
15
|
|
|
16
|
-
static
|
|
16
|
+
static args = {
|
|
17
17
|
alias: {
|
|
18
18
|
type: 't',
|
|
19
19
|
name: 'n'
|
|
@@ -27,12 +27,14 @@ class Create extends Command {
|
|
|
27
27
|
}
|
|
28
28
|
};
|
|
29
29
|
|
|
30
|
-
async cli(
|
|
31
|
-
await super.cli(
|
|
30
|
+
async cli() {
|
|
31
|
+
await super.cli();
|
|
32
32
|
console.log('Routine created.');
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
async
|
|
35
|
+
async _run(myt, ctx, cfg, opts) {
|
|
36
|
+
const {type} = opts;
|
|
37
|
+
|
|
36
38
|
const match = opts.name.match(/^(\w+)\.(\w+)$/);
|
|
37
39
|
if (!match)
|
|
38
40
|
throw new Error('Invalid object name, should contain schema and routine name');
|
|
@@ -43,30 +45,36 @@ class Create extends Command {
|
|
|
43
45
|
const params = {
|
|
44
46
|
schema,
|
|
45
47
|
name,
|
|
46
|
-
definer:
|
|
48
|
+
definer: cfg.defaultDefiner
|
|
47
49
|
};
|
|
48
50
|
|
|
49
|
-
switch (
|
|
51
|
+
switch (type) {
|
|
50
52
|
case 'event':
|
|
51
|
-
case 'function':
|
|
52
53
|
case 'procedure':
|
|
53
54
|
case 'trigger':
|
|
54
|
-
params.body = "BEGIN\n-- Your code goes here\nEND";
|
|
55
|
+
params.body = "BEGIN\n\t-- Your code goes here\nEND";
|
|
56
|
+
break;
|
|
57
|
+
case 'function':
|
|
58
|
+
params.body = "BEGIN\n\tRETURN 1;\nEND";
|
|
55
59
|
break;
|
|
56
60
|
case 'view':
|
|
57
61
|
params.definition = "SELECT TRUE"
|
|
58
62
|
break;
|
|
59
63
|
}
|
|
60
64
|
|
|
61
|
-
const exporter = new Exporter(
|
|
65
|
+
const exporter = new Exporter(type, cfg.replace);
|
|
62
66
|
await exporter.init();
|
|
63
67
|
const sql = exporter.format(params);
|
|
64
68
|
|
|
65
|
-
const routineDir = `${
|
|
69
|
+
const routineDir = `${ctx.routinesDir}/${schema}/${type}s`;
|
|
66
70
|
if (!await fs.pathExists(routineDir))
|
|
67
|
-
await fs.mkdir(routineDir);
|
|
71
|
+
await fs.mkdir(routineDir, {recursive: true});
|
|
68
72
|
|
|
69
73
|
const routineFile = `${routineDir}/${name}.sql`;
|
|
74
|
+
|
|
75
|
+
if (await fs.exists(routineFile))
|
|
76
|
+
throw new Error('Routine already exists');
|
|
77
|
+
|
|
70
78
|
await fs.writeFile(routineFile, sql);
|
|
71
79
|
}
|
|
72
80
|
}
|