ramm 0.0.28 → 0.0.30

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/ramm.js CHANGED
@@ -2,18 +2,18 @@
2
2
  // src/base/base.ts
3
3
  var {spawn } = globalThis.Bun;
4
4
 
5
- // src/debug.ts
6
- var debugInternal = (func, command) => {
7
- console.info(`>>>${func}<<< ${command}`);
5
+ // src/print.ts
6
+ var printInternal = (left, right) => {
7
+ console.info(`\x1B[32m${left}:\x1B[0m \x1B[38;5;85m${right}\x1B[0m`);
8
8
  };
9
- var debugCommand = (command) => {
10
- debugInternal(">>>\x1B[32mCommand\x1B[0m<<<", `\x1B[38;5;85m${command}\x1B[0m`);
9
+ var printCommand = (command) => {
10
+ printInternal("Command", command);
11
11
  };
12
- var debugApiCall = (command) => {
13
- debugInternal(">>>\x1B[32mapi command\x1B[0m<<<", `\x1B[38;5;85m${command}\x1B[0m`);
12
+ var printFunction = (func) => {
13
+ printInternal("Function", func);
14
14
  };
15
- var debugBlock = (text) => {
16
- debugInternal("\x1B[34mBlock\x1B[0m", `\x1B[38;5;81m${text}\x1B[0m`);
15
+ var printBlock = (name) => {
16
+ console.info(`\x1B[34mBlock:\x1B[0m \x1B[38;5;81m${name}\x1B[0m`);
17
17
  };
18
18
 
19
19
  // src/context.ts
@@ -43,32 +43,36 @@ class Context {
43
43
  }
44
44
 
45
45
  // src/base/tee.ts
46
- var tee = async (read) => {
46
+ var tee = async (read, write, prefix) => {
47
47
  const reader = read.getReader();
48
+ let leftover = "";
48
49
  let output = "";
50
+ const decoder = new TextDecoder("utf-8");
49
51
  while (true) {
50
- const { value, done: doneReading } = await reader.read();
51
- if (doneReading) {
52
+ const { value, done } = await reader.read();
53
+ if (done) {
54
+ if (leftover)
55
+ write(prefix + leftover + `
56
+ `);
52
57
  return output;
53
58
  }
54
- const decoder = new TextDecoder("utf-8");
55
- output += decoder.decode(value);
56
- process.stdout.write(value);
57
- }
58
- };
59
- var teeErr = async (read) => {
60
- const reader = read.getReader();
61
- let output = "";
62
- while (true) {
63
- const { value, done: doneReading } = await reader.read();
64
- if (doneReading) {
65
- return output;
59
+ const decodedOutput = decoder.decode(value, { stream: true });
60
+ output += decodedOutput;
61
+ const lines = (leftover + decodedOutput).split(`
62
+ `);
63
+ leftover = lines.pop() ?? "";
64
+ for (const line of lines) {
65
+ write(prefix + line + `
66
+ `);
66
67
  }
67
- const decoder = new TextDecoder("utf-8");
68
- output += decoder.decode(value);
69
- process.stderr.write(value);
70
68
  }
71
69
  };
70
+ var teeStdout = (read, prefix) => {
71
+ return tee(read, (text) => process.stdout.write(text), prefix);
72
+ };
73
+ var teeStderr = (read, prefix) => {
74
+ return tee(read, (text) => process.stderr.write(text), prefix);
75
+ };
72
76
 
73
77
  // src/base/base.ts
74
78
  var defaultContext = new Context({
@@ -77,29 +81,36 @@ var defaultContext = new Context({
77
81
  userspace: false,
78
82
  sudo: false
79
83
  });
80
- var execCommandMayError = async (command) => {
81
- debugCommand(command);
82
- const result = spawn(["bash", "-c", command], {
84
+ var execCommandRaw = async (command, { store = {}, signal, env, cwd, prefix = "" } = {}) => {
85
+ const spawnResult = spawn(["bash", "-c", command], {
83
86
  stdin: "inherit",
84
87
  stdout: "pipe",
85
- stderr: "pipe"
88
+ stderr: "pipe",
89
+ signal,
90
+ cwd,
91
+ env
86
92
  });
87
- const [output, outputErr] = await Promise.all([
88
- tee(result.stdout),
89
- teeErr(result.stderr)
93
+ store.spawnResult = spawnResult;
94
+ const [stdout, stderr] = await Promise.all([
95
+ teeStdout(spawnResult.stdout, prefix),
96
+ teeStderr(spawnResult.stderr, prefix)
90
97
  ]);
91
- await result.exited;
98
+ await spawnResult.exited;
92
99
  return {
93
- outputErr,
94
- output,
95
- spawnResult: result
100
+ stderr,
101
+ stdout,
102
+ spawnResult
96
103
  };
97
104
  };
98
- var execCommand = async (command) => {
99
- const result = await execCommandMayError(command);
105
+ var execCommandMayError = async (command, props) => {
106
+ printCommand(command);
107
+ return execCommandRaw(command, props);
108
+ };
109
+ var execCommand = async (command, props) => {
110
+ const result = await execCommandMayError(command, props);
100
111
  if (result.spawnResult.exitCode !== 0) {
101
- console.error(">>>Error<<<");
102
- console.error("command:", command);
112
+ console.error(`Error exit code: ${result.spawnResult.exitCode}`);
113
+ console.error(`Command: ${command}`);
103
114
  throw new Error(command);
104
115
  }
105
116
  return result;
@@ -111,6 +122,10 @@ var execBySsh = async (command, context) => {
111
122
  const sshKeyPart = context.sshKey ? ` -i ${context.sshKey}` : "";
112
123
  return await execCommand(`ssh${sshKeyPart} ${context.getAddress()} '${command}'`);
113
124
  };
125
+ var execCommandOverSsh = async (command, context) => {
126
+ const sshKeyPart = context.sshKey ? ` -i ${context.sshKey}` : "";
127
+ return await execCommand(`ssh${sshKeyPart} ${context.getAddress()} '${command}'`);
128
+ };
114
129
  // src/init.ts
115
130
  var installBun = async (context) => {
116
131
  const bunPath = new URL(import.meta.resolve("./bun.sh")).pathname;
@@ -148,6 +163,7 @@ var stopSystemdService = async (constext, name) => {
148
163
  await execCommand(formatUserspace(constext, `systemctl stop ${name}`));
149
164
  };
150
165
  var restartSystemdService = async (name, constext = defaultContext) => {
166
+ printFunction(`restartSystemdService ${name}`);
151
167
  await execCommand(formatUserspace(constext, `systemctl restart ${name}`));
152
168
  };
153
169
  var checkSystemdService = async (context, serviceName) => {
@@ -155,6 +171,7 @@ var checkSystemdService = async (context, serviceName) => {
155
171
  return spawnResult.exitCode === 0;
156
172
  };
157
173
  var createSystemdService = async (context, serviceName, pathToServiceFile) => {
174
+ printFunction(`createSystemdService ${serviceName}`);
158
175
  const pathToSeviceTarget = getSystemdPathToService(context, serviceName);
159
176
  await execCommand(`cp -v ${pathToServiceFile} ${pathToSeviceTarget}`);
160
177
  await execCommand(formatUserspace(context, "systemctl daemon-reload"));
@@ -172,10 +189,10 @@ var installPodman = async () => {
172
189
  var createNetwork = async () => {
173
190
  const netwroks = await execCommand("podman network inspect $(podman network ls -q) -f '{{.NetworkInterface}}'");
174
191
  const podmanNetworks = await execCommand("podman network ls");
175
- if (podmanNetworks.output.includes("ramm")) {
192
+ if (podmanNetworks.stdout.includes("ramm")) {
176
193
  return;
177
194
  }
178
- if (netwroks.output.includes("podman_ramm")) {
195
+ if (netwroks.stdout.includes("podman_ramm")) {
179
196
  return;
180
197
  }
181
198
  await execCommand("podman network create --interface-name=podman_ramm ramm");
@@ -206,7 +223,7 @@ var runPodmanContainerService = async (name, command, context = defaultContext)
206
223
  };
207
224
  var addNftPodmanRule = async () => {
208
225
  const podmanNetworksResult = await execCommand("podman network inspect $(podman network ls -q) -f '{{.NetworkInterface}}'");
209
- const podmanNetworks = podmanNetworksResult.output.trim().split(`
226
+ const podmanNetworks = podmanNetworksResult.stdout.trim().split(`
210
227
  `);
211
228
  await execCommand(`nft add set inet ramm podman_interfaces '{ type ifname; flags dynamic; elements = { ${podmanNetworks.join(", ")} }; }'`);
212
229
  await execCommand("nft insert rule inet ramm prerouting iifname @podman_interfaces accept");
@@ -284,7 +301,6 @@ var normalizeFileContent = (str) => {
284
301
  };
285
302
  var createDir = async (str) => {
286
303
  const dirname = str.split("/").slice(0, -1).join("/");
287
- debugApiCall(`exists(${dirname})`);
288
304
  const exist = await exists(dirname);
289
305
  if (exist) {
290
306
  return;
@@ -293,11 +309,9 @@ var createDir = async (str) => {
293
309
  };
294
310
  var checkStrInFile = async (filePath, str) => {
295
311
  const file2 = Bun.file(filePath);
296
- debugApiCall(`file(${filePath}).exists())`);
297
312
  if (!await file2.exists()) {
298
313
  return false;
299
314
  }
300
- debugApiCall(`file(${filePath}).text()`);
301
315
  const fileText = await file2.text();
302
316
  if (fileText.includes(str)) {
303
317
  return true;
@@ -307,7 +321,6 @@ var checkStrInFile = async (filePath, str) => {
307
321
  var createFileIfNeed = async (rawFilePath) => {
308
322
  const filePath = normalizePath(rawFilePath);
309
323
  await createDir(filePath);
310
- debugApiCall(`file(${filePath}).exists())`);
311
324
  if (!await file(filePath).exists()) {
312
325
  await execCommand(`touch ${filePath}`);
313
326
  }
@@ -318,24 +331,20 @@ var writeIfNew = async (rawFilePath, str) => {
318
331
  if (await checkStrInFile(filePath, str)) {
319
332
  return;
320
333
  }
321
- debugApiCall(`appendFile(${filePath})`);
322
334
  await appendFile(filePath, normalizeFileContent(str));
323
335
  };
324
336
  var writeFile = async (pathToFile, str) => {
325
337
  const normalizedPath = normalizePath(pathToFile);
326
338
  await createFileIfNeed(normalizedPath);
327
- debugApiCall(`write(${normalizedPath})`);
328
339
  await write(normalizedPath, str);
329
340
  };
330
341
  var writeIfNewCompletely = async (pathToFile, str) => {
331
342
  const normalizedPath = normalizePath(pathToFile);
332
343
  await createFileIfNeed(normalizedPath);
333
- debugApiCall(`file(${normalizedPath}).text()`);
334
344
  const fileText = await file(normalizedPath).text();
335
345
  if (fileText === str) {
336
346
  return;
337
347
  }
338
- debugApiCall(`write(${normalizedPath})`);
339
348
  await write(normalizedPath, str);
340
349
  };
341
350
  // src/ssh.ts
@@ -348,29 +357,27 @@ var addKeyToHostConfig = async (pathToHost, address, pathToKey) => {
348
357
  await writeIfNew(pathToHost, text);
349
358
  };
350
359
  var getServerFingerprintBySsh = async (context) => {
351
- const { output } = await execBySsh('ssh-keyscan -t ed25519 localhost | grep -v "^#"', context);
352
- return output.replace("localhost", context.domain);
360
+ const { stdout } = await execBySsh('ssh-keyscan -t ed25519 localhost | grep -v "^#"', context);
361
+ return stdout.replace("localhost", context.domain);
353
362
  };
354
363
  async function createSshKey(pathToKey, comment) {
364
+ printFunction(createSshKey.name);
355
365
  const name = pathToKey.split("/").at(-1);
356
366
  const pathToKeyPub = `${pathToKey}.pub`;
357
367
  const pathToDir = pathToKey.split("/").slice(0, -1).join("/");
358
368
  const pathToKeyFile = file2(pathToKey);
359
369
  const pathToKeyPubFile = file2(pathToKeyPub);
360
- debugApiCall(`file(${pathToKey}).exist()`);
361
- debugApiCall(`file(${pathToKeyPub}).exist()`);
362
370
  if (await pathToKeyFile.exists() && await pathToKeyPubFile.exists()) {
363
371
  return await pathToKeyPubFile.text();
364
372
  }
365
373
  await execCommand(`mkdir -p ${pathToDir}`);
366
374
  await execCommand(`ssh-keygen -t ed25519 -f ${pathToKey} -N "" -C "${comment || name}"`);
367
375
  await execCommand(`chmod 600 ${pathToKey}`);
368
- debugApiCall(`file(${pathToKeyPub}).text()`);
369
376
  const pubKey = await Bun.file(pathToKeyPub).text();
370
377
  return pubKey;
371
378
  }
372
379
  var addSshKeyToAuthorized = async (pubKey, context) => {
373
- const { output: keys } = await execBySsh("cat .ssh/authorized_keys", context);
380
+ const { stdout: keys } = await execBySsh("cat .ssh/authorized_keys", context);
374
381
  if (keys.includes(pubKey)) {
375
382
  return;
376
383
  }
@@ -400,7 +407,7 @@ var createCron = async ({
400
407
  const pathToFileNorm = normalizePath(pathToFile);
401
408
  const constructedLine = `${time} ${pathToFileNorm}`;
402
409
  const tempFile = `/tmp/ramm_cron}`;
403
- const { output: cronConfig } = await execCommandMayError("crontab -l");
410
+ const { stdout: cronConfig } = await execCommandMayError("crontab -l");
404
411
  let newCronConfig = cronConfig;
405
412
  if (cronConfig.includes(constructedLine)) {
406
413
  return;
@@ -438,14 +445,14 @@ var buildAndRunOverSsh = async ({
438
445
  };
439
446
  var pathToJson = "/tmp/ramm_json";
440
447
  var passVarsClient = async (data, context) => {
448
+ printFunction("passVarsClient");
441
449
  const json = JSON.stringify(data);
442
- debugApiCall(`writeFile(${pathToJson})`);
443
450
  await writeFile(pathToJson, json);
444
451
  await copyFilesBySsh(pathToJson, pathToJson, context);
445
452
  await execCommand(`rm -rf ${pathToJson}`);
446
453
  };
447
454
  var passVarsServer = async () => {
448
- debugApiCall(`file(${pathToJson}).json`);
455
+ printFunction("passVarsServer");
449
456
  const jsonData = await file3(pathToJson).json();
450
457
  await execCommand(`rm -rf ${pathToJson}`);
451
458
  return jsonData;
@@ -459,6 +466,7 @@ export {
459
466
  runPodmanContainerService,
460
467
  runPodmanContainer,
461
468
  restartSystemdService,
469
+ printBlock,
462
470
  passVarsServer,
463
471
  passVarsClient,
464
472
  normalizePath,
@@ -468,9 +476,9 @@ export {
468
476
  installPodman,
469
477
  installBun,
470
478
  getServerFingerprintBySsh,
479
+ execCommandOverSsh,
471
480
  execCommandMayError as execCommand,
472
481
  execBySsh,
473
- debugBlock,
474
482
  createCron,
475
483
  createAndAddSshKey,
476
484
  copyFilesBySsh,
@@ -1,18 +1,40 @@
1
+ import { type Subprocess } from "bun";
1
2
  import { Context } from "../context.ts";
2
3
  export declare const defaultContext: Context;
3
- export declare const execCommandMayError: (command: string) => Promise<{
4
- outputErr: string;
5
- output: string;
6
- spawnResult: import("bun").Subprocess<"inherit", "pipe", "pipe">;
4
+ export type ExecCommandStore = {
5
+ spawnResult?: Subprocess<"inherit", "pipe", "pipe">;
6
+ };
7
+ type ExecProps = {
8
+ store?: ExecCommandStore;
9
+ env?: Record<string, string>;
10
+ cwd?: string;
11
+ prefix?: string;
12
+ signal?: AbortSignal;
13
+ } | void;
14
+ export declare const execCommandRaw: (command: string, { store, signal, env, cwd, prefix }?: ExecProps) => Promise<{
15
+ stderr: string;
16
+ stdout: string;
17
+ spawnResult: Subprocess<"inherit", "pipe", "pipe">;
7
18
  }>;
8
- export declare const execCommand: (command: string) => Promise<{
9
- outputErr: string;
10
- output: string;
11
- spawnResult: import("bun").Subprocess<"inherit", "pipe", "pipe">;
19
+ export declare const execCommandMayError: (command: string, props: ExecProps) => Promise<{
20
+ stderr: string;
21
+ stdout: string;
22
+ spawnResult: Subprocess<"inherit", "pipe", "pipe">;
23
+ }>;
24
+ export declare const execCommand: (command: string, props: ExecProps) => Promise<{
25
+ stderr: string;
26
+ stdout: string;
27
+ spawnResult: Subprocess<"inherit", "pipe", "pipe">;
12
28
  }>;
13
29
  export declare const copyFilesBySsh: (from: string, to: string, context: Context) => Promise<void>;
14
30
  export declare const execBySsh: (command: string, context: Context) => Promise<{
15
- outputErr: string;
16
- output: string;
17
- spawnResult: import("bun").Subprocess<"inherit", "pipe", "pipe">;
31
+ stderr: string;
32
+ stdout: string;
33
+ spawnResult: Subprocess<"inherit", "pipe", "pipe">;
34
+ }>;
35
+ export declare const execCommandOverSsh: (command: string, context: Context) => Promise<{
36
+ stderr: string;
37
+ stdout: string;
38
+ spawnResult: Subprocess<"inherit", "pipe", "pipe">;
18
39
  }>;
40
+ export {};
@@ -1,3 +1,2 @@
1
- export declare const tee: (read: ReadableStream) => Promise<string>;
2
- export declare const teeErr: (read: ReadableStream) => Promise<string>;
3
- export declare const readStreamToStr: (read: ReadableStream) => Promise<string>;
1
+ export declare const teeStdout: (read: ReadableStream, prefix: string) => Promise<string>;
2
+ export declare const teeStderr: (read: ReadableStream, prefix: string) => Promise<string>;
@@ -2,8 +2,8 @@ import type { Context } from "./context.ts";
2
2
  export declare const installPodman: () => Promise<void>;
3
3
  export declare const getCreateCommand: (name: string) => Promise<string>;
4
4
  export declare const loginPodman: (address: string, login: string, password: string) => Promise<{
5
- outputErr: string;
6
- output: string;
5
+ stderr: string;
6
+ stdout: string;
7
7
  spawnResult: import("bun").Subprocess<"inherit", "pipe", "pipe">;
8
8
  }>;
9
9
  export declare const runPodmanContainer: (name: string, command: string) => Promise<void>;
@@ -0,0 +1,3 @@
1
+ export declare const printCommand: (command: string) => void;
2
+ export declare const printFunction: (func: string) => void;
3
+ export declare const printBlock: (name: string) => void;
@@ -1,11 +1,11 @@
1
- export { execBySsh, execCommandMayError as execCommand, copyFilesBySsh, } from "./base/base.ts";
1
+ export { execBySsh, execCommandOverSsh, execCommandMayError as execCommand, copyFilesBySsh, } from "./base/base.ts";
2
2
  export { Context } from "./context.ts";
3
3
  export { installBun } from "./init.ts";
4
4
  export { installPodman, runPodmanContainer, loginPodman } from "./podman.ts";
5
5
  export { runPodmanContainerService, addNftPodmanRule } from "./podman.ts";
6
6
  export { restartSystemdService } from "./systemd.ts";
7
7
  export { installSystemPackage } from "./packages.ts";
8
- export { debugBlock } from "./debug.ts";
8
+ export { printBlock } from "./print.ts";
9
9
  export { setupNftable } from "./nft.ts";
10
10
  export { writeIfNew, writeFile, writeIfNewCompletely as writeFileIfNotMatch, normalizeFileContent, } from "./files.ts";
11
11
  export { createAndAddSshKey, getServerFingerprintBySsh, addKeyToHostConfig, setupSshKey, } from "./ssh.ts";
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "ramm",
3
3
  "type": "module",
4
- "version": "0.0.28",
4
+ "version": "0.0.30",
5
5
  "scripts": {
6
6
  "build": "bun build ./src/ramm.ts --target bun --outdir ./dist && cp ./src/bun.sh ./dist/bun.sh",
7
7
  "types": "tsc --project tsconfig.types.json"