@datatruck/cli 0.36.2 → 0.36.4

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.
@@ -149,7 +149,8 @@ class BackupAction {
149
149
  let title = item.key.slice(0, 1).toUpperCase() + item.key.slice(1);
150
150
  return item.key === "backup" && color ? chalk_1.default.cyan(title) : title;
151
151
  };
152
- const renderData = (item, color, result = []) => {
152
+ const renderData = (item, result, format) => {
153
+ const color = format !== "list";
153
154
  const g = (v) => (color ? `${chalk_1.default.gray(`(${v})`)}` : `(${v})`);
154
155
  return (0, cli_1.renderListTaskItem)(item, color, {
155
156
  snapshot: (data) => data.id,
@@ -176,6 +177,9 @@ class BackupAction {
176
177
  result += item.data.pruned;
177
178
  return result;
178
179
  }, 0),
180
+ ...(format === "list" && {
181
+ duration: (0, date_1.duration)(item.elapsed),
182
+ }),
179
183
  }),
180
184
  });
181
185
  };
@@ -187,7 +191,7 @@ class BackupAction {
187
191
  .map((item) => {
188
192
  const icon = (0, cli_1.renderResult)(item.error, false);
189
193
  const title = renderTitle(item);
190
- const data = renderData(item, false, result);
194
+ const data = renderData(item, result, "list");
191
195
  return `${icon} ${title}: ${data}`;
192
196
  }),
193
197
  table: {
@@ -203,7 +207,7 @@ class BackupAction {
203
207
  .map((item) => [
204
208
  (0, cli_1.renderResult)(item.error),
205
209
  renderTitle(item, true),
206
- renderData(item, true, result),
210
+ renderData(item, result, "table"),
207
211
  (0, date_1.duration)(item.elapsed),
208
212
  (0, cli_1.renderError)(item.error, options.verbose),
209
213
  ]),
@@ -76,7 +76,8 @@ class CopyAction {
76
76
  let title = item.key.slice(0, 1).toUpperCase() + item.key.slice(1);
77
77
  return item.key === "copy" && color ? chalk_1.default.cyan(title) : title;
78
78
  };
79
- const renderData = (item, color, items = []) => {
79
+ const renderData = (item, items = [], format) => {
80
+ const color = format !== "list";
80
81
  const g = (v) => (color ? `${chalk_1.default.gray(`(${v})`)}` : `(${v})`);
81
82
  return (0, cli_1.renderListTaskItem)(item, color, {
82
83
  snapshots: (data) => data.snapshots.length,
@@ -93,6 +94,9 @@ class CopyAction {
93
94
  errors: data.errors,
94
95
  copied: items.filter((i) => i.key === "copy" && !i.error && !i.data.skipped).length,
95
96
  skipped: items.filter((i) => i.key === "copy" && !i.error && i.data.skipped).length,
97
+ ...(format === "list" && {
98
+ duration: (0, date_1.duration)(item.elapsed),
99
+ }),
96
100
  }),
97
101
  });
98
102
  };
@@ -102,7 +106,7 @@ class CopyAction {
102
106
  list: () => result.map((item) => {
103
107
  const icon = (0, cli_1.renderResult)(item.error, false);
104
108
  const title = renderTitle(item);
105
- const data = renderData(item, false, result);
109
+ const data = renderData(item, result, "list");
106
110
  return `${icon} ${title}: ${data}`;
107
111
  }),
108
112
  table: {
@@ -116,7 +120,7 @@ class CopyAction {
116
120
  rows: () => result.map((item) => [
117
121
  (0, cli_1.renderResult)(item.error),
118
122
  renderTitle(item, true),
119
- renderData(item, true, result),
123
+ renderData(item, result, "table"),
120
124
  (0, date_1.duration)(item.elapsed),
121
125
  (0, cli_1.renderError)(item.error, options.verbose),
122
126
  ]),
@@ -147,9 +151,8 @@ class CopyAction {
147
151
  async copyCrossRepository(options) {
148
152
  const env_1 = { stack: [], error: void 0, hasError: false };
149
153
  try {
150
- const { repo, repoConfig, mirrorRepo, mirrorConfig, snapshot } = options;
154
+ const { repo, mirrorRepo, mirrorConfig, snapshot } = options;
151
155
  const tmp = __addDisposableResource(env_1, await (0, temp_1.useTempDir)("copy", "restore"), true);
152
- const pkg = (0, config_1.findPackageOrFail)(this.config, snapshot.packageName);
153
156
  await repo.restore({
154
157
  options: {
155
158
  verbose: this.options.verbose,
@@ -157,7 +160,7 @@ class CopyAction {
157
160
  },
158
161
  snapshot: { id: snapshot.id, date: snapshot.date },
159
162
  package: { name: snapshot.packageName },
160
- packageConfig: (0, config_1.findPackageRepositoryConfig)(pkg, repoConfig),
163
+ packageConfig: undefined,
161
164
  snapshotPath: tmp.path,
162
165
  onProgress: options.onProgress,
163
166
  });
@@ -174,7 +177,7 @@ class CopyAction {
174
177
  name: snapshot.packageName,
175
178
  path: tmp.path,
176
179
  },
177
- packageConfig: (0, config_1.findPackageRepositoryConfig)(pkg, mirrorConfig),
180
+ packageConfig: undefined,
178
181
  onProgress: options.onProgress,
179
182
  });
180
183
  }
package/lib/cli.js CHANGED
@@ -76,7 +76,8 @@ function makeCommandAction(command) {
76
76
  }
77
77
  }
78
78
  }
79
- process.stdout.write("", () => process.exit(exitCode));
79
+ await (0, cli_1.waitForStdDrain)(5000);
80
+ process.exit(exitCode);
80
81
  };
81
82
  }
82
83
  const program = new commander_1.Command();
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.StartServerCommand = void 0;
4
4
  const ConfigAction_1 = require("../actions/ConfigAction");
5
+ const cli_1 = require("../utils/cli");
5
6
  const cron_server_1 = require("../utils/datatruck/cron-server");
6
7
  const repository_server_1 = require("../utils/datatruck/repository-server");
7
8
  const error_1 = require("../utils/error");
@@ -22,7 +23,7 @@ class StartServerCommand extends CommandAbstract_1.CommandAbstract {
22
23
  });
23
24
  const port = repositoryOptions.listen?.port ?? 8888;
24
25
  const address = repositoryOptions.listen?.address ?? "127.0.0.1";
25
- console.info(`Listening datatruck repository on http://${address}:${port}`);
26
+ (0, cli_1.logJson)("datatruck-server", `listening server on http://${address}:${port}`);
26
27
  server.on("error", (error) => {
27
28
  console.error(`SERVER ERROR`, error);
28
29
  process.exit(1);
@@ -30,6 +31,7 @@ class StartServerCommand extends CommandAbstract_1.CommandAbstract {
30
31
  server.listen(port, address);
31
32
  }
32
33
  const cronOptions = config.server?.cron || {};
34
+ const cronJobs = cronOptions.actions || [];
33
35
  if (cronOptions.enabled ?? true) {
34
36
  if (typeof this.configPath !== "string")
35
37
  throw new error_1.AppError(`Config path is required by cron server`);
@@ -39,7 +41,9 @@ class StartServerCommand extends CommandAbstract_1.CommandAbstract {
39
41
  configPath: this.configPath,
40
42
  });
41
43
  server.start();
42
- console.info(`Cron server started`);
44
+ (0, cli_1.logJson)("cron-server", `server started`, {
45
+ jobs: cronJobs.length,
46
+ });
43
47
  }
44
48
  process.on("SIGINT", () => process.exit(1));
45
49
  process.on("SIGTERM", () => process.exit(1));
@@ -27,7 +27,7 @@ export type RepoFetchSnapshotsData = {
27
27
  export type RepoCopyData<TRepositoryConfig> = {
28
28
  options: BackupActionOptions;
29
29
  snapshot: PreSnapshot;
30
- package: PackageConfig;
30
+ package: Pick<PackageConfig, "name">;
31
31
  mirrorRepositoryConfig: TRepositoryConfig;
32
32
  onProgress: (data: Progress) => void;
33
33
  };
@@ -35,8 +35,9 @@ export type RepoBackupData<TPackageConfig> = {
35
35
  options: BackupActionOptions;
36
36
  snapshot: PreSnapshot;
37
37
  hostname: string;
38
- package: Omit<PackageConfig, "path"> & {
39
- path: string;
38
+ package: Pick<PackageConfig, "name" | "include" | "exclude"> & {
39
+ path: NonNullable<PackageConfig["path"]>;
40
+ task?: Pick<NonNullable<PackageConfig["task"]>, "name">;
40
41
  };
41
42
  packageConfig: TPackageConfig | undefined;
42
43
  onProgress: (data: Progress) => void;
@@ -44,9 +45,9 @@ export type RepoBackupData<TPackageConfig> = {
44
45
  export type RepoRestoreData<TPackageConfig> = {
45
46
  options: RestoreActionOptions;
46
47
  snapshot: PreSnapshot;
47
- package: PackageConfig;
48
+ package: Pick<PackageConfig, "name">;
48
49
  snapshotPath: string;
49
- packageConfig: TPackageConfig;
50
+ packageConfig: TPackageConfig | undefined;
50
51
  onProgress: (data: Progress) => void;
51
52
  };
52
53
  export type RepoPruneData = {
@@ -1,5 +1,6 @@
1
1
  /// <reference types="node" />
2
2
  import { Listr3TaskResultEnd } from "./list";
3
+ import chalk from "chalk";
3
4
  export declare const showCursorCommand = "\u001B[?25h";
4
5
  export declare function renderProgressBar(progress: number, size?: number, subprogress?: number): string;
5
6
  export declare function logExec(command: string, argv?: string[], env?: NodeJS.ProcessEnv, logToStderr?: boolean): void;
@@ -29,3 +30,7 @@ export declare function stringifyOptions<T1, T2 extends {
29
30
  [K in keyof T1]: unknown;
30
31
  }>(options: OptionsConfig<T1, T2>, object: any): string[];
31
32
  export declare function confirm(message: string): Promise<unknown>;
33
+ export declare function waitForStdDrain(ms?: number): Promise<void>;
34
+ export declare function colorizeValue(value: unknown, color?: typeof chalk.ForegroundColor): string;
35
+ export declare function colorizeObject(input: Record<string, any>): string;
36
+ export declare function logJson(ctx: string, msg: string, data?: any): void;
package/lib/utils/cli.js CHANGED
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.confirm = exports.stringifyOptions = exports.parseOptions = exports.renderObject = exports.renderListTaskItem = exports.renderError = exports.renderResult = exports.logExec = exports.renderProgressBar = exports.showCursorCommand = void 0;
6
+ exports.logJson = exports.colorizeObject = exports.colorizeValue = exports.waitForStdDrain = exports.confirm = exports.stringifyOptions = exports.parseOptions = exports.renderObject = exports.renderListTaskItem = exports.renderError = exports.renderResult = exports.logExec = exports.renderProgressBar = exports.showCursorCommand = void 0;
7
7
  const error_1 = require("./error");
8
8
  const chalk_1 = __importDefault(require("chalk"));
9
9
  const chalk_2 = require("chalk");
@@ -149,3 +149,48 @@ function confirm(message) {
149
149
  });
150
150
  }
151
151
  exports.confirm = confirm;
152
+ async function waitForStdDrain(ms) {
153
+ await Promise.all([process.stdout, process.stderr].map((stream) => new Promise((resolve) => {
154
+ {
155
+ const finish = () => {
156
+ clearTimeout(timeout);
157
+ resolve();
158
+ };
159
+ const timeout = ms ? setTimeout(finish, ms) : undefined;
160
+ const drained = stream.write("", finish);
161
+ if (drained)
162
+ finish();
163
+ }
164
+ })));
165
+ }
166
+ exports.waitForStdDrain = waitForStdDrain;
167
+ function colorizeValue(value, color) {
168
+ const json = JSON.stringify(value);
169
+ return color ? chalk_1.default[color](json) : json;
170
+ }
171
+ exports.colorizeValue = colorizeValue;
172
+ function colorizeObject(input) {
173
+ const object = {};
174
+ for (const key in input) {
175
+ const value = input[key];
176
+ if (value !== undefined)
177
+ object[colorizeValue(key)] =
178
+ typeof value === "object" && !!value && !Array.isArray(value)
179
+ ? colorizeObject(value)
180
+ : colorizeValue(value, "green");
181
+ }
182
+ const values = Object.entries(object)
183
+ .map(([key, value]) => `${key}: ${value}`)
184
+ .join(", ");
185
+ return `{ ${values} }`;
186
+ }
187
+ exports.colorizeObject = colorizeObject;
188
+ function logJson(ctx, msg, data) {
189
+ const json = colorizeObject({
190
+ ctx,
191
+ msg,
192
+ data,
193
+ });
194
+ console.log(json);
195
+ }
196
+ exports.logJson = logJson;
@@ -26,8 +26,7 @@ function createJobs(actions, worker) {
26
26
  }
27
27
  function createCronServer(options, config) {
28
28
  const worker = async (action, index) => {
29
- if (config.log)
30
- console.info(`> [job] ${index} - ${action.name}`);
29
+ let pid = 0;
31
30
  try {
32
31
  const Command = command_1.datatruckCommandMap[action.name];
33
32
  const command = new Command({ config: { packages: [], repositories: [] } }, {});
@@ -35,19 +34,24 @@ function createCronServer(options, config) {
35
34
  ? ({ ...action.options, confirm: true })
36
35
  : action.options);
37
36
  const [node, bin] = process.argv;
38
- await async_process_1.AsyncProcess.exec(node, [
37
+ const p = new async_process_1.AsyncProcess(node, [
39
38
  process.env.pm_exec_path ?? bin,
40
39
  "-c",
41
40
  config.configPath,
42
41
  action.name,
43
42
  ...cliOptions,
44
- ], { $log: config.verbose });
43
+ ], { $log: config.verbose, $exitCode: false });
44
+ pid = p.child.pid || 0;
45
45
  if (config.log)
46
- console.info(`< [job] ${index} - ${action.name}`);
46
+ (0, cli_1.logJson)("cron-server", `${action.name} started`, { pid });
47
+ const exitCode = await p.waitForClose();
48
+ if (config.log)
49
+ (0, cli_1.logJson)("cron-server", `${action.name} finished`, { pid, exitCode });
47
50
  }
48
51
  catch (error) {
49
52
  if (config.log)
50
- console.error(`< [job] ${index} - ${action.name}`, error);
53
+ (0, cli_1.logJson)("cron-server", `${action.name} failed`, { pid });
54
+ console.error(error);
51
55
  }
52
56
  };
53
57
  let jobs = createJobs(options.actions || [], worker);
@@ -55,12 +59,14 @@ function createCronServer(options, config) {
55
59
  onRead: () => ConfigAction_1.ConfigAction.findAndParseFile(config.configPath),
56
60
  onCheck: (prev, current) => (0, string_1.compareJsons)(prev, current),
57
61
  onError: (error) => {
58
- if (config.log)
59
- console.error(`< [jobs] update error`, error);
62
+ if (config.log) {
63
+ (0, cli_1.logJson)("cron-server", "job update error");
64
+ console.error(error);
65
+ }
60
66
  },
61
67
  onChange: (data) => {
62
68
  if (config.log)
63
- console.info(`[jobs] updated`);
69
+ (0, cli_1.logJson)("cron-server", "jobs updated");
64
70
  handler.stop();
65
71
  const cron = data?.server?.cron;
66
72
  const enabled = cron?.enabled ?? true;
@@ -9,7 +9,7 @@ export type ParsePathsOptions = {
9
9
  };
10
10
  export declare function parsePaths(values: (string | SpawnStep)[], options: ParsePathsOptions): Promise<string[]>;
11
11
  export type BackupPathsOptions = {
12
- package: PackageConfig;
12
+ package: Pick<PackageConfig, "name" | "path" | "include" | "exclude">;
13
13
  snapshot: PreSnapshot;
14
14
  path: string;
15
15
  verbose?: boolean;
@@ -12,6 +12,7 @@ async function parsePaths(values, options) {
12
12
  await (0, spawnSteps_1.runSpawnSteps)(value, {
13
13
  tempDir: options.tempDir,
14
14
  verbose: options.verbose,
15
+ data: options.data,
15
16
  onLine: (path) => paths.push(path),
16
17
  });
17
18
  }
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.createDatatruckRepositoryServer = exports.headerKey = void 0;
4
4
  const ConfigAction_1 = require("../../actions/ConfigAction");
5
+ const cli_1 = require("../cli");
5
6
  const http_1 = require("../http");
6
7
  const virtual_fs_1 = require("../virtual-fs");
7
8
  const fs_1 = require("fs");
@@ -77,7 +78,10 @@ function createDatatruckRepositoryServer(inOptions, config = {}) {
77
78
  return res.end();
78
79
  }
79
80
  if (config.log)
80
- console.info(`> [repository] ${repository} - ${req.url}`);
81
+ (0, cli_1.logJson)("repository-server", "request", {
82
+ repository,
83
+ url: req.url,
84
+ });
81
85
  const fs = new virtual_fs_1.LocalFs({
82
86
  backend: backend.path,
83
87
  });
@@ -123,12 +127,18 @@ function createDatatruckRepositoryServer(inOptions, config = {}) {
123
127
  res.write(JSON.stringify(json));
124
128
  }
125
129
  if (config.log)
126
- console.info(`< [repository] ${repository} - ${action}`);
130
+ (0, cli_1.logJson)("repository-server", "request finished", {
131
+ url: req.url,
132
+ });
127
133
  res.end();
128
134
  }
129
135
  catch (error) {
130
- if (config.log)
131
- console.error(`< [repository] ${req.url}`, error);
136
+ if (config.log) {
137
+ (0, cli_1.logJson)("repository-server", "request failed", {
138
+ url: req.url,
139
+ });
140
+ console.error(error);
141
+ }
132
142
  res.statusCode = 500;
133
143
  res.statusMessage = error.message;
134
144
  res.end();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@datatruck/cli",
3
- "version": "0.36.2",
3
+ "version": "0.36.4",
4
4
  "description": "Tool for creating and managing backups",
5
5
  "homepage": "https://github.com/swordev/datatruck#readme",
6
6
  "bugs": {
@@ -26,18 +26,18 @@
26
26
  "config.schema.json"
27
27
  ],
28
28
  "dependencies": {
29
- "@supercharge/promise-pool": "^3.1.0",
29
+ "@supercharge/promise-pool": "^3.1.1",
30
30
  "ajv": "^8.12.0",
31
31
  "async": "^3.2.5",
32
32
  "chalk": "^4.1.2",
33
- "commander": "^11.1.0",
34
- "croner": "^8.0.0",
33
+ "commander": "^12.0.0",
34
+ "croner": "^8.0.1",
35
35
  "dayjs": "^1.11.10",
36
36
  "fast-folder-size": "^2.2.0",
37
37
  "fast-glob": "^3.3.2",
38
- "listr2": "^8.0.1",
38
+ "listr2": "^8.0.2",
39
39
  "micromatch": "^4.0.5",
40
- "mysql2": "^3.7.0",
40
+ "mysql2": "^3.9.1",
41
41
  "tty-table": "^4.2.3",
42
42
  "yaml": "^2.3.4"
43
43
  },