@13w/miri 1.1.9 → 1.1.11
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 -16
- package/dist/mongodb.js +3 -1
- package/package.json +8 -6
package/dist/cli.js
CHANGED
|
@@ -1,19 +1,79 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises';
|
|
1
2
|
Error.stackTraceLimit = Infinity;
|
|
2
3
|
import { join } from 'node:path';
|
|
3
4
|
/* eslint-disable no-console */
|
|
4
5
|
import { Command } from 'commander';
|
|
5
6
|
import { Table } from 'console-table-printer';
|
|
6
7
|
import colors from 'colors';
|
|
8
|
+
import { createTunnel } from 'tunnel-ssh';
|
|
9
|
+
import SSHConfig from 'ssh-config';
|
|
7
10
|
import connection from './mongodb.js';
|
|
8
11
|
import Miri, { IndexStatus, PatchStatus } from './miri.js';
|
|
12
|
+
import { readFileSync } from 'fs';
|
|
13
|
+
import { realpathSync } from 'node:fs';
|
|
14
|
+
const pkg = await readFile(join(import.meta.dirname, '../package.json'), 'utf-8').then((content) => JSON.parse(content));
|
|
15
|
+
const mirirc = await readFile(join(process.cwd(), '.mirirc'), 'utf-8').then((content) => JSON.parse(content), () => ({}));
|
|
16
|
+
const { SSH_AUTH_SOCK, HOME } = process.env;
|
|
9
17
|
const program = new Command();
|
|
10
|
-
program.
|
|
11
|
-
program.option('-
|
|
18
|
+
program.version(pkg.version);
|
|
19
|
+
program.option('-e --env <environment>', 'Environment name from .mirirc', 'default');
|
|
20
|
+
program.option('-m --migrations <folder>', `Folder with migrations (default: "${process.cwd(), join(process.cwd(), 'migrations')}")`);
|
|
21
|
+
program.option('-d --db <mongo-uri>', 'MongoDB Connection URI (default: "mongodb://localhost:27017/test")');
|
|
22
|
+
program.option('--ssh-profile <profile>', 'Connect via SSH using profile');
|
|
23
|
+
program.option('--ssh-host <host>', 'Connect via SSH proxy Host');
|
|
24
|
+
program.option('--ssh-port <port>', 'Connect via SSH proxy Port');
|
|
25
|
+
program.option('--ssh-user <user>', 'Connect via SSH proxy User');
|
|
26
|
+
program.option('--ssh-key <path/to/key>', 'Connect via SSH proxy IdentityKey');
|
|
27
|
+
let configCache;
|
|
28
|
+
const getConfig = (programOpts) => {
|
|
29
|
+
if (configCache) {
|
|
30
|
+
return configCache;
|
|
31
|
+
}
|
|
32
|
+
const envs = mirirc.environments ?? { default: mirirc };
|
|
33
|
+
const env = envs[programOpts.env ?? 'default'] ?? {};
|
|
34
|
+
const config = Object.assign({}, mirirc, env, programOpts);
|
|
35
|
+
if (config.sshProfile) {
|
|
36
|
+
const sshConfigPath = join(HOME ?? '', '.ssh/config');
|
|
37
|
+
const sshConfigContent = readFileSync(sshConfigPath, 'utf-8');
|
|
38
|
+
console.log(`Reading profile ${config.sshProfile} from SSH config ${sshConfigPath}`);
|
|
39
|
+
const parsed = SSHConfig.parse(sshConfigContent).compute(config.sshProfile);
|
|
40
|
+
config.sshHost = programOpts.sshHost ?? parsed.Hostname;
|
|
41
|
+
config.sshPort = programOpts.sshPort ?? parsed.Port;
|
|
42
|
+
config.sshKey = programOpts.sshKey ?? parsed.IdentityFile?.[0];
|
|
43
|
+
config.sshUser = programOpts.sshUser ?? parsed.User;
|
|
44
|
+
}
|
|
45
|
+
// console.dir(config)
|
|
46
|
+
return configCache = config;
|
|
47
|
+
};
|
|
48
|
+
const createSSHTunnel = async (opts) => {
|
|
49
|
+
const config = getConfig(opts);
|
|
50
|
+
const sshOptions = {
|
|
51
|
+
host: config.sshHost,
|
|
52
|
+
port: Number(config.sshPort ?? 22),
|
|
53
|
+
username: config.sshUser,
|
|
54
|
+
agent: SSH_AUTH_SOCK,
|
|
55
|
+
privateKey: config.sshKey ? readFileSync(realpathSync(config.sshKey)) : void 0,
|
|
56
|
+
};
|
|
57
|
+
const dst = new URL(config.db);
|
|
58
|
+
const forwardOptions = {
|
|
59
|
+
dstPort: Number(dst.port ?? 27017),
|
|
60
|
+
dstAddr: dst.hostname,
|
|
61
|
+
};
|
|
62
|
+
const [server] = await createTunnel({ autoClose: true }, {}, sshOptions, forwardOptions);
|
|
63
|
+
const addressInfo = server.address();
|
|
64
|
+
dst.host = addressInfo.family === 'IPv6' ? `[${String(addressInfo.address)}]` : String(addressInfo.address);
|
|
65
|
+
dst.port = String(addressInfo.port);
|
|
66
|
+
return dst.toString();
|
|
67
|
+
};
|
|
12
68
|
const getMiri = async () => {
|
|
13
|
-
const
|
|
14
|
-
const
|
|
69
|
+
const config = getConfig(program.opts());
|
|
70
|
+
const db = config.sshHost ? await createSSHTunnel(config) : config.db;
|
|
71
|
+
const dbUri = new URL(db);
|
|
72
|
+
dbUri.searchParams.append('directConnection', 'true');
|
|
73
|
+
dbUri.searchParams.append('appName', `miri+v${pkg.version}`);
|
|
74
|
+
const client = await connection(dbUri.toString());
|
|
15
75
|
return new Miri(client, {
|
|
16
|
-
localMigrations:
|
|
76
|
+
localMigrations: config.migrations,
|
|
17
77
|
});
|
|
18
78
|
};
|
|
19
79
|
const status = async (remote = false, group) => {
|
|
@@ -59,12 +119,10 @@ initProgram.command('status')
|
|
|
59
119
|
.action(() => status(false, 'init'));
|
|
60
120
|
const indexesProgram = program.command('indexes')
|
|
61
121
|
.description('Manage indexes');
|
|
62
|
-
|
|
122
|
+
indexesProgram.command('status')
|
|
63
123
|
.argument('[collection]', 'MongoDB Collection name')
|
|
64
124
|
.option('-q --quiet', 'Show only changes', false)
|
|
65
|
-
.action(async () => {
|
|
66
|
-
const collection = indexStatusProgram.args[0];
|
|
67
|
-
const { quiet } = indexStatusProgram.opts();
|
|
125
|
+
.action(async (collection, { quiet }) => {
|
|
68
126
|
const miri = await getMiri();
|
|
69
127
|
const structure = await miri.indexesDiff(collection);
|
|
70
128
|
await miri[Symbol.asyncDispose]();
|
|
@@ -94,12 +152,12 @@ const indexStatusProgram = indexesProgram.command('status')
|
|
|
94
152
|
}
|
|
95
153
|
});
|
|
96
154
|
const indexSyncProgram = indexesProgram.command('sync');
|
|
97
|
-
indexSyncProgram.
|
|
98
|
-
.action(async () => {
|
|
155
|
+
indexSyncProgram.argument('[collection]', 'MongoDB Collection name', '')
|
|
156
|
+
.action(async (coll) => {
|
|
99
157
|
const miri = await getMiri();
|
|
100
158
|
let group = '';
|
|
101
159
|
console.group('Starting synchronisation...');
|
|
102
|
-
for await (const { collection, status, name, error } of miri.indexesSync(
|
|
160
|
+
for await (const { collection, status, name, error } of miri.indexesSync(coll)) {
|
|
103
161
|
if (group !== collection) {
|
|
104
162
|
if (group) {
|
|
105
163
|
console.log('Done');
|
|
@@ -122,17 +180,17 @@ indexSyncProgram.option('<collection>', 'MongoDB Collection name', '')
|
|
|
122
180
|
});
|
|
123
181
|
const patchProgram = program.command('patch')
|
|
124
182
|
.description('Applies patch to database');
|
|
125
|
-
|
|
183
|
+
patchProgram.command('diff')
|
|
126
184
|
.description('Displays difference between local and applied migrations')
|
|
127
185
|
.action(() => status());
|
|
128
|
-
|
|
186
|
+
patchProgram.command('sync')
|
|
129
187
|
.description('Applies migrations')
|
|
130
188
|
.option('--remote', 'Remote only')
|
|
131
189
|
.option('--degraded', 'Re-apply patches on degraded migrations')
|
|
132
190
|
.option('--all', 'Re-apply all patches')
|
|
133
|
-
.action(async () => {
|
|
191
|
+
.action(async (opts) => {
|
|
134
192
|
const miri = await getMiri();
|
|
135
|
-
await miri.sync(
|
|
193
|
+
await miri.sync(opts);
|
|
136
194
|
await miri[Symbol.asyncDispose]();
|
|
137
195
|
});
|
|
138
196
|
program.parse();
|
package/dist/mongodb.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
/* eslint-disable no-console */
|
|
1
2
|
import { MongoClient } from 'mongodb';
|
|
2
3
|
let client;
|
|
3
4
|
export default async function connect(uri = 'mongodb://localhost:27017/test') {
|
|
4
5
|
if (!client) {
|
|
5
|
-
|
|
6
|
+
console.log(`Connecting MongoDB to ${uri}`);
|
|
7
|
+
client = await MongoClient.connect(uri);
|
|
6
8
|
client?.topology?.socket?.unref();
|
|
7
9
|
}
|
|
8
10
|
return client;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@13w/miri",
|
|
3
3
|
"description": "MongoDB patch manager",
|
|
4
|
-
"version": "1.1.
|
|
4
|
+
"version": "1.1.11",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
7
7
|
"node": "v20"
|
|
@@ -26,16 +26,18 @@
|
|
|
26
26
|
"colors": "^1.4.0",
|
|
27
27
|
"commander": "^12.0.0",
|
|
28
28
|
"console-table-printer": "^2.12.0",
|
|
29
|
-
"mongodb": "^6.
|
|
29
|
+
"mongodb": "^6.5.0",
|
|
30
|
+
"ssh-config": "^4.4.2",
|
|
31
|
+
"tunnel-ssh": "^5.1.2"
|
|
30
32
|
},
|
|
31
33
|
"devDependencies": {
|
|
32
|
-
"@types/node": "^20.11.
|
|
33
|
-
"@typescript-eslint/eslint-plugin": "^7.
|
|
34
|
-
"@typescript-eslint/parser": "^7.
|
|
34
|
+
"@types/node": "^20.11.27",
|
|
35
|
+
"@typescript-eslint/eslint-plugin": "^7.2.0",
|
|
36
|
+
"@typescript-eslint/parser": "^7.2.0",
|
|
35
37
|
"eslint": "^8.57.0",
|
|
36
38
|
"eslint-plugin-deprecation": "^2.0.0",
|
|
37
39
|
"ts-node": "^10.9.2",
|
|
38
|
-
"typescript": "^5.
|
|
40
|
+
"typescript": "^5.4.2"
|
|
39
41
|
},
|
|
40
42
|
"license": "MIT",
|
|
41
43
|
"scripts": {
|