@13w/miri 1.1.12 → 1.1.15
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/dist/cli.js +74 -5
- package/dist/evaluator.js +1 -1
- package/dist/miri.js +48 -2
- package/package.json +25 -20
package/dist/cli.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { readFileSync, realpathSync } from 'node:fs';
|
|
1
2
|
import { readFile } from 'node:fs/promises';
|
|
2
3
|
Error.stackTraceLimit = Infinity;
|
|
3
4
|
import { join } from 'node:path';
|
|
@@ -9,11 +10,47 @@ import { createTunnel } from 'tunnel-ssh';
|
|
|
9
10
|
import SSHConfig from 'ssh-config';
|
|
10
11
|
import connection from './mongodb.js';
|
|
11
12
|
import Miri, { IndexStatus, PatchStatus } from './miri.js';
|
|
12
|
-
import { readFileSync } from 'fs';
|
|
13
|
-
import { realpathSync } from 'node:fs';
|
|
14
13
|
const pkg = await readFile(join(import.meta.dirname, '../package.json'), 'utf-8').then((content) => JSON.parse(content));
|
|
15
14
|
const mirirc = await readFile(join(process.cwd(), '.mirirc'), 'utf-8').then((content) => JSON.parse(content), () => ({}));
|
|
16
15
|
const { SSH_AUTH_SOCK, HOME } = process.env;
|
|
16
|
+
const askPassword = async (message = 'Password: ') => {
|
|
17
|
+
process.stdin.resume();
|
|
18
|
+
process.stdin.setEncoding('utf8');
|
|
19
|
+
process.stdin.setRawMode(true);
|
|
20
|
+
return new Promise((resolve, reject) => {
|
|
21
|
+
let password = '';
|
|
22
|
+
const readPass = function (chunk) {
|
|
23
|
+
const str = chunk.toString();
|
|
24
|
+
// backspace
|
|
25
|
+
if (str.charCodeAt(0) === 127) {
|
|
26
|
+
password = password.slice(0, -1);
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
switch (str) {
|
|
30
|
+
case '\n':
|
|
31
|
+
case '\r':
|
|
32
|
+
case '\u0004':
|
|
33
|
+
// They've finished typing their password
|
|
34
|
+
process.stdin.setRawMode(false);
|
|
35
|
+
process.stdin.pause();
|
|
36
|
+
process.stdout.write('\n');
|
|
37
|
+
process.stdin.off('data', readPass);
|
|
38
|
+
resolve(password);
|
|
39
|
+
return;
|
|
40
|
+
case '\u0003':
|
|
41
|
+
// Ctrl C
|
|
42
|
+
process.stdin.off('data', readPass);
|
|
43
|
+
reject(new Error('Cancelled'));
|
|
44
|
+
default:
|
|
45
|
+
// More passsword characters
|
|
46
|
+
password += str;
|
|
47
|
+
break;
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
process.stdout.write(message);
|
|
51
|
+
process.stdin.on('data', readPass);
|
|
52
|
+
});
|
|
53
|
+
};
|
|
17
54
|
const program = new Command();
|
|
18
55
|
program.version(pkg.version);
|
|
19
56
|
program.option('-e --env <environment>', 'Environment name from .mirirc', 'default');
|
|
@@ -24,6 +61,7 @@ program.option('--ssh-host <host>', 'Connect via SSH proxy Host');
|
|
|
24
61
|
program.option('--ssh-port <port>', 'Connect via SSH proxy Port');
|
|
25
62
|
program.option('--ssh-user <user>', 'Connect via SSH proxy User');
|
|
26
63
|
program.option('--ssh-key <path/to/key>', 'Connect via SSH proxy IdentityKey');
|
|
64
|
+
program.option('--ssh-ask-pass', 'Ask for the private key password', false);
|
|
27
65
|
let configCache;
|
|
28
66
|
const getConfig = (programOpts) => {
|
|
29
67
|
if (configCache) {
|
|
@@ -42,18 +80,23 @@ const getConfig = (programOpts) => {
|
|
|
42
80
|
config.sshKey = programOpts.sshKey ?? parsed.IdentityFile?.[0];
|
|
43
81
|
config.sshUser = programOpts.sshUser ?? parsed.User;
|
|
44
82
|
}
|
|
45
|
-
// console.dir(config)
|
|
46
83
|
return configCache = config;
|
|
47
84
|
};
|
|
48
85
|
const createSSHTunnel = async (opts) => {
|
|
49
86
|
const config = getConfig(opts);
|
|
87
|
+
let passphrase = '';
|
|
88
|
+
if (config.sshKey && config.sshAskPass) {
|
|
89
|
+
passphrase = await askPassword();
|
|
90
|
+
}
|
|
50
91
|
const sshOptions = {
|
|
51
92
|
host: config.sshHost,
|
|
52
93
|
port: Number(config.sshPort ?? 22),
|
|
53
94
|
username: config.sshUser,
|
|
54
|
-
agent: SSH_AUTH_SOCK,
|
|
95
|
+
// agent: SSH_AUTH_SOCK,
|
|
55
96
|
privateKey: config.sshKey ? readFileSync(realpathSync(config.sshKey)) : void 0,
|
|
97
|
+
passphrase,
|
|
56
98
|
};
|
|
99
|
+
console.dir([config, sshOptions]);
|
|
57
100
|
const dst = new URL(config.db);
|
|
58
101
|
const forwardOptions = {
|
|
59
102
|
dstPort: Number(dst.port ?? 27017),
|
|
@@ -152,11 +195,19 @@ initProgram.command('apply')
|
|
|
152
195
|
.argument('[patch]', 'patch name')
|
|
153
196
|
.option('--no-exec', 'Don\' execute patch, just set as done')
|
|
154
197
|
.option('--force', 'Force apply patch')
|
|
155
|
-
.action(async (
|
|
198
|
+
.action(async (patch, opts) => {
|
|
156
199
|
const miri = await getMiri();
|
|
157
200
|
await miri.init(opts, patch);
|
|
158
201
|
await miri[Symbol.asyncDispose]();
|
|
159
202
|
});
|
|
203
|
+
initProgram.command('remove')
|
|
204
|
+
.argument('<patch>', 'patch name')
|
|
205
|
+
.option('--no-exec', 'Don\' execute patch, just set as done')
|
|
206
|
+
.action(async (patch, opts) => {
|
|
207
|
+
const miri = await getMiri();
|
|
208
|
+
await miri.remove('init', patch, Boolean(opts?.exec));
|
|
209
|
+
await miri[Symbol.asyncDispose]();
|
|
210
|
+
});
|
|
160
211
|
initProgram.command('status')
|
|
161
212
|
.action(() => status(false, 'init'));
|
|
162
213
|
const indexesProgram = program.command('indexes')
|
|
@@ -215,5 +266,23 @@ patchProgram.command('sync')
|
|
|
215
266
|
await miri.sync(opts);
|
|
216
267
|
await miri[Symbol.asyncDispose]();
|
|
217
268
|
});
|
|
269
|
+
patchProgram.command('apply')
|
|
270
|
+
.argument('<group>', 'group name')
|
|
271
|
+
.argument('<patch>', 'patch name')
|
|
272
|
+
.option('--no-exec', 'Don\' execute patch, just set as done')
|
|
273
|
+
.action(async (group, patch, opts) => {
|
|
274
|
+
const miri = await getMiri();
|
|
275
|
+
await miri.applySingle(group, patch, Boolean(opts.exec));
|
|
276
|
+
await miri[Symbol.asyncDispose]();
|
|
277
|
+
});
|
|
278
|
+
patchProgram.command('remove')
|
|
279
|
+
.argument('<group>', 'group name')
|
|
280
|
+
.argument('<patch>', 'patch name')
|
|
281
|
+
.option('--no-exec', 'Don\' execute patch, just set as done')
|
|
282
|
+
.action(async (group, patch, opts) => {
|
|
283
|
+
const miri = await getMiri();
|
|
284
|
+
await miri.remove(group, patch, Boolean(opts.exec));
|
|
285
|
+
await miri[Symbol.asyncDispose]();
|
|
286
|
+
});
|
|
218
287
|
program.parse();
|
|
219
288
|
//# sourceMappingURL=cli.js.map
|
package/dist/evaluator.js
CHANGED
|
@@ -12,7 +12,7 @@ const print = (values, type) => {
|
|
|
12
12
|
return String(object);
|
|
13
13
|
}
|
|
14
14
|
else {
|
|
15
|
-
return inspect(object, { colors:
|
|
15
|
+
return inspect(object, { colors: false, customInspect: true, depth: 22 });
|
|
16
16
|
}
|
|
17
17
|
};
|
|
18
18
|
for (const value of values) {
|
package/dist/miri.js
CHANGED
|
@@ -276,7 +276,7 @@ export default class Migrator {
|
|
|
276
276
|
}
|
|
277
277
|
}
|
|
278
278
|
}
|
|
279
|
-
async init({
|
|
279
|
+
async init({ exec = false, force = false } = {}, patch) {
|
|
280
280
|
const localInits = await this.getLocalPatches(true, 'init', patch);
|
|
281
281
|
const remoteInits = await this.getRemotePatches('init', patch);
|
|
282
282
|
if (!localInits.length) {
|
|
@@ -292,7 +292,7 @@ export default class Migrator {
|
|
|
292
292
|
console.groupEnd();
|
|
293
293
|
continue;
|
|
294
294
|
}
|
|
295
|
-
if (force ||
|
|
295
|
+
if (force || exec) {
|
|
296
296
|
console.log('applying');
|
|
297
297
|
await this.applyPatchContent({ body: patch.raw, hash: '' }, `${patch.group}/${patch.name}`, false);
|
|
298
298
|
}
|
|
@@ -322,5 +322,51 @@ export default class Migrator {
|
|
|
322
322
|
}
|
|
323
323
|
return patches;
|
|
324
324
|
}
|
|
325
|
+
async applySingle(group, name, exec = false) {
|
|
326
|
+
const [local] = await this.getLocalPatches(false, group, name);
|
|
327
|
+
const [remote] = await this.getRemotePatches(group, name);
|
|
328
|
+
if (!local) {
|
|
329
|
+
console.group(`Patch ${group}/${name} not found.`);
|
|
330
|
+
return;
|
|
331
|
+
}
|
|
332
|
+
console.group('Patch found');
|
|
333
|
+
if (exec && local.content.up) {
|
|
334
|
+
console.group(colors.cyan('applying migration...'));
|
|
335
|
+
await this.applyPatchContent(local.content.up, `${local.group}/${local.name}`);
|
|
336
|
+
console.log(colors.green('done'));
|
|
337
|
+
console.groupEnd();
|
|
338
|
+
}
|
|
339
|
+
else {
|
|
340
|
+
console.log('set as done');
|
|
341
|
+
}
|
|
342
|
+
const _id = remote?._id ?? local._id;
|
|
343
|
+
await this.#collection.updateOne({ _id }, {
|
|
344
|
+
$set: {
|
|
345
|
+
_id,
|
|
346
|
+
group: local.group,
|
|
347
|
+
name: local.name,
|
|
348
|
+
content: local.content,
|
|
349
|
+
},
|
|
350
|
+
}, { upsert: true });
|
|
351
|
+
console.groupEnd();
|
|
352
|
+
}
|
|
353
|
+
async remove(group, name, exec = false) {
|
|
354
|
+
const [patch] = await this.getRemotePatches(group, name);
|
|
355
|
+
if (!patch) {
|
|
356
|
+
console.group(`Patch ${group}/${name} not found.`);
|
|
357
|
+
return;
|
|
358
|
+
}
|
|
359
|
+
console.group('Patch found, start removing');
|
|
360
|
+
if (exec && patch.content.down) {
|
|
361
|
+
console.group('De-applying patch...');
|
|
362
|
+
await this.applyPatchContent(patch.content.down, `${patch.group}/${patch.name}`);
|
|
363
|
+
console.log('done');
|
|
364
|
+
console.groupEnd();
|
|
365
|
+
}
|
|
366
|
+
console.group('Removing patch from database');
|
|
367
|
+
await this.#collection.deleteOne({ group, name });
|
|
368
|
+
console.log('done');
|
|
369
|
+
console.groupEnd();
|
|
370
|
+
}
|
|
325
371
|
}
|
|
326
372
|
//# sourceMappingURL=miri.js.map
|
package/package.json
CHANGED
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@13w/miri",
|
|
3
3
|
"description": "MongoDB patch manager",
|
|
4
|
-
"version": "1.1.
|
|
4
|
+
"version": "1.1.15",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
7
7
|
"node": "v20"
|
|
8
8
|
},
|
|
9
|
-
"
|
|
9
|
+
"keywords": [
|
|
10
|
+
"MongoDB",
|
|
11
|
+
"patch",
|
|
12
|
+
"migration",
|
|
13
|
+
"ssh"
|
|
14
|
+
],
|
|
10
15
|
"bin": {
|
|
11
16
|
"miri": "bin/miri"
|
|
12
17
|
},
|
|
@@ -19,30 +24,30 @@
|
|
|
19
24
|
],
|
|
20
25
|
"main": "bin/miri",
|
|
21
26
|
"types": "types/miri.d.ts",
|
|
27
|
+
"scripts": {
|
|
28
|
+
"build": "tsc -p tsconfig.json",
|
|
29
|
+
"lint": "eslint src/ --ext .ts --quiet",
|
|
30
|
+
"prepublish": "pnpm run build"
|
|
31
|
+
},
|
|
22
32
|
"dependencies": {
|
|
23
|
-
"@mongosh/service-provider-server": "^2.
|
|
24
|
-
"@mongosh/shell-api": "^2.
|
|
25
|
-
"@mongosh/shell-evaluator": "^2.
|
|
33
|
+
"@mongosh/service-provider-server": "^2.2.12",
|
|
34
|
+
"@mongosh/shell-api": "^2.2.12",
|
|
35
|
+
"@mongosh/shell-evaluator": "^2.2.12",
|
|
26
36
|
"colors": "^1.4.0",
|
|
27
|
-
"commander": "^12.
|
|
28
|
-
"console-table-printer": "^2.12.
|
|
29
|
-
"mongodb": "^6.
|
|
30
|
-
"ssh-config": "^4.4.
|
|
37
|
+
"commander": "^12.1.0",
|
|
38
|
+
"console-table-printer": "^2.12.1",
|
|
39
|
+
"mongodb": "^6.8.0",
|
|
40
|
+
"ssh-config": "^4.4.4",
|
|
31
41
|
"tunnel-ssh": "^5.1.2"
|
|
32
42
|
},
|
|
33
43
|
"devDependencies": {
|
|
34
|
-
"@types/node": "^20.
|
|
35
|
-
"@typescript-eslint/eslint-plugin": "^7.
|
|
36
|
-
"@typescript-eslint/parser": "^7.
|
|
44
|
+
"@types/node": "^20.14.12",
|
|
45
|
+
"@typescript-eslint/eslint-plugin": "^7.17.0",
|
|
46
|
+
"@typescript-eslint/parser": "^7.17.0",
|
|
37
47
|
"eslint": "^8.57.0",
|
|
38
48
|
"eslint-plugin-deprecation": "^2.0.0",
|
|
39
49
|
"ts-node": "^10.9.2",
|
|
40
|
-
"typescript": "^5.4
|
|
50
|
+
"typescript": "^5.5.4"
|
|
41
51
|
},
|
|
42
|
-
"license": "MIT"
|
|
43
|
-
|
|
44
|
-
"build": "tsc -p tsconfig.json",
|
|
45
|
-
"lint": "eslint src/ --ext .ts --quiet",
|
|
46
|
-
"prepublish": "pnpm run build"
|
|
47
|
-
}
|
|
48
|
-
}
|
|
52
|
+
"license": "MIT"
|
|
53
|
+
}
|