@datatruck/cli 0.21.0 → 0.22.0

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.
@@ -1,7 +1,6 @@
1
1
  import type { ConfigType } from "../Config/Config";
2
2
  import { PackageConfigType } from "../Config/PackageConfig";
3
3
  import { RepositoryConfigType } from "../Config/RepositoryConfig";
4
- import { AppError } from "../Error/AppError";
5
4
  import { SnapshotType } from "../Repository/RepositoryAbstract";
6
5
  import { BackupSessionManager } from "../SessionManager/BackupSessionManager";
7
6
  import { TaskAbstract } from "../Task/TaskAbstract";
@@ -35,7 +34,7 @@ export declare class BackupAction<TRequired extends boolean = true> {
35
34
  error: boolean;
36
35
  tmpDirs: string[];
37
36
  }>;
38
- protected getError(pkg: PackageConfigType): AppError | null;
37
+ protected getError(pkg: PackageConfigType): Error | undefined;
39
38
  protected splitRepositories(repositoryNames: string[]): {
40
39
  repoNames: string[];
41
40
  mirrors: {
@@ -45,6 +44,6 @@ export declare class BackupAction<TRequired extends boolean = true> {
45
44
  };
46
45
  exec(session: BackupSessionManager): Promise<{
47
46
  total: number;
48
- errors: number;
47
+ errors: Error[];
49
48
  }>;
50
49
  }
@@ -67,7 +67,7 @@ class BackupAction {
67
67
  const key = `${pkg.name}`;
68
68
  let error;
69
69
  if (this.taskErrors[key]?.length) {
70
- error = new AppError_1.AppError("Previous task failed");
70
+ error = AppError_1.AppError.create("Previous task failed", this.taskErrors[key]);
71
71
  }
72
72
  else {
73
73
  try {
@@ -110,7 +110,7 @@ class BackupAction {
110
110
  let error;
111
111
  let repoInstance;
112
112
  if (this.taskErrors[pkg.name]?.length) {
113
- error = new AppError_1.AppError("Task failed");
113
+ error = AppError_1.AppError.create("Task failed", this.taskErrors[pkg.name]);
114
114
  }
115
115
  else {
116
116
  try {
@@ -156,7 +156,7 @@ class BackupAction {
156
156
  let error;
157
157
  let repoInstance;
158
158
  if (this.taskErrors[pkg.name]?.length) {
159
- error = new AppError_1.AppError("Task failed");
159
+ error = AppError_1.AppError.create("Task failed", this.taskErrors[pkg.name]);
160
160
  }
161
161
  else {
162
162
  try {
@@ -190,20 +190,16 @@ class BackupAction {
190
190
  };
191
191
  }
192
192
  getError(pkg) {
193
- const taskErrors = this.taskErrors[pkg.name]?.length;
194
- const repoErrors = this.repoErrors[pkg.name]?.length;
195
- if (taskErrors && repoErrors) {
196
- return new AppError_1.AppError("Task and repository failed");
197
- }
198
- else if (taskErrors && !repoErrors) {
199
- return new AppError_1.AppError("Task failed");
200
- }
201
- else if (!taskErrors && repoErrors) {
202
- return new AppError_1.AppError("Repository failed");
203
- }
204
- else {
205
- return null;
206
- }
193
+ const taskErrors = this.taskErrors[pkg.name] || [];
194
+ const repoErrors = this.repoErrors[pkg.name] || [];
195
+ const errors = [...taskErrors, ...repoErrors];
196
+ if (!errors.length)
197
+ return;
198
+ return AppError_1.AppError.create(taskErrors.length && repoErrors.length
199
+ ? "Task and repository failed"
200
+ : taskErrors.length && !repoErrors.length
201
+ ? "Task failed"
202
+ : "Repository failed", errors);
207
203
  }
208
204
  splitRepositories(repositoryNames) {
209
205
  const mirrorRepoMap = {};
@@ -230,7 +226,7 @@ class BackupAction {
230
226
  }
231
227
  async exec(session) {
232
228
  const [snapshot, packages] = await this.init(session);
233
- let errors = 0;
229
+ const errors = [];
234
230
  for (const pkg of packages) {
235
231
  const id = session.findId({
236
232
  packageName: pkg.name,
@@ -272,7 +268,7 @@ class BackupAction {
272
268
  }
273
269
  const error = this.getError(pkg);
274
270
  if (error)
275
- errors++;
271
+ errors.push(error);
276
272
  await session.end({
277
273
  id: id,
278
274
  error: error?.message,
@@ -283,7 +279,7 @@ class BackupAction {
283
279
  });
284
280
  return {
285
281
  total: packages.length,
286
- errors: errors,
282
+ errors,
287
283
  };
288
284
  }
289
285
  }
@@ -39,6 +39,8 @@ export declare class RestoreAction<TRequired extends boolean = true> {
39
39
  tmpDirs: string[];
40
40
  }>;
41
41
  protected getError(pkg: PackageConfigType): AppError | null;
42
- exec(session: RestoreSessionManager): Promise<boolean>;
42
+ exec(session: RestoreSessionManager): Promise<{
43
+ errors: Error[];
44
+ }>;
43
45
  }
44
46
  export {};
@@ -100,10 +100,10 @@ class RestoreAction {
100
100
  });
101
101
  let error;
102
102
  if (this.repoErrors[pkg.name]?.length) {
103
- error = new AppError_1.AppError("Repository failed");
103
+ error = AppError_1.AppError.create("Repository failed", this.repoErrors[pkg.name]);
104
104
  }
105
105
  else if (this.taskErrors[pkg.name]?.length) {
106
- error = new AppError_1.AppError("Previous task failed");
106
+ error = AppError_1.AppError.create("Previous task failed", this.taskErrors[pkg.name]);
107
107
  }
108
108
  else {
109
109
  try {
@@ -222,7 +222,7 @@ class RestoreAction {
222
222
  });
223
223
  const snapshotAndConfigs = this.assocConfigs(packages, snapshots);
224
224
  await this.init(session, this.options.snapshotId, snapshotAndConfigs);
225
- let sessionErrors = 0;
225
+ const errors = [];
226
226
  for (const [snapshot, pkg] of snapshotAndConfigs) {
227
227
  (0, assert_1.ok)(pkg);
228
228
  const repo = (0, config_1.findRepositoryOrFail)(this.config, snapshot.repositoryName);
@@ -257,10 +257,10 @@ class RestoreAction {
257
257
  error: error?.message,
258
258
  });
259
259
  if (error)
260
- sessionErrors++;
260
+ errors.push(error);
261
261
  }
262
262
  await session.endDrivers();
263
- return !sessionErrors;
263
+ return { errors };
264
264
  }
265
265
  }
266
266
  exports.RestoreAction = RestoreAction;
@@ -12,5 +12,5 @@ export type BackupCommandOptionsType<TResolved = false> = {
12
12
  };
13
13
  export declare class BackupCommand extends CommandAbstract<BackupCommandOptionsType<false>, BackupCommandOptionsType<true>> {
14
14
  onOptions(): import("../utils/cli").OptionsType<BackupCommandOptionsType<false>, BackupCommandOptionsType<true>>;
15
- onExec(): Promise<0 | 1>;
15
+ onExec(): Promise<1 | 0>;
16
16
  }
@@ -74,7 +74,7 @@ class BackupCommand extends CommandAbstract_1.CommandAbstract {
74
74
  progressInterval: this.globalOptions.progressInterval,
75
75
  });
76
76
  const result = await backup.exec(sessionManager);
77
- if (result.errors) {
77
+ if (result.errors.length) {
78
78
  return 1;
79
79
  }
80
80
  else if (!result.total) {
@@ -12,5 +12,5 @@ export type RestoreCommandOptionsType<TResolved = false> = {
12
12
  };
13
13
  export declare class RestoreCommand extends CommandAbstract<RestoreCommandOptionsType<false>, RestoreCommandOptionsType<true>> {
14
14
  onOptions(): import("../utils/cli").OptionsType<RestoreCommandOptionsType<false>, RestoreCommandOptionsType<true>>;
15
- onExec(): Promise<0 | 1>;
15
+ onExec(): Promise<1 | 0>;
16
16
  }
@@ -74,7 +74,7 @@ class RestoreCommand extends CommandAbstract_1.CommandAbstract {
74
74
  progressInterval: this.globalOptions.progressInterval,
75
75
  });
76
76
  const result = await restore.exec(sessionManager);
77
- return result ? 0 : 1;
77
+ return result.errors.length ? 1 : 0;
78
78
  }
79
79
  }
80
80
  exports.RestoreCommand = RestoreCommand;
@@ -1,3 +1,4 @@
1
1
  export declare class AppError extends Error {
2
2
  constructor(message: string);
3
+ static create(message: string, errors: Error[]): Error;
3
4
  }
package/Error/AppError.js CHANGED
@@ -6,5 +6,13 @@ class AppError extends Error {
6
6
  super(message);
7
7
  this.name = AppError.name;
8
8
  }
9
+ static create(message, errors) {
10
+ if (errors.length === 1) {
11
+ return errors[0];
12
+ }
13
+ else {
14
+ return new AggregateError(errors, message);
15
+ }
16
+ }
9
17
  }
10
18
  exports.AppError = AppError;
@@ -38,7 +38,7 @@ exports.definitions = {
38
38
  [DefinitionEnum_1.DefinitionEnum.resticPackageRepository]: ResticRepository_1.resticPackageRepositoryDefinition,
39
39
  [DefinitionEnum_1.DefinitionEnum.gitTask]: GitTask_1.gitTaskDefinition,
40
40
  [DefinitionEnum_1.DefinitionEnum.scriptTask]: ScriptTask_1.scriptTaskDefinition,
41
- [DefinitionEnum_1.DefinitionEnum.sqlDumpTask]: SqlDumpTaskAbstract_1.sqlDumpTaskDefinition,
41
+ [DefinitionEnum_1.DefinitionEnum.sqlDumpTask]: (0, SqlDumpTaskAbstract_1.sqlDumpTaskDefinition)(),
42
42
  [DefinitionEnum_1.DefinitionEnum.mariadbTask]: MariadbTask_1.mariadbTaskDefinition,
43
43
  [DefinitionEnum_1.DefinitionEnum.mssqlTask]: MssqlTask_1.mssqlTaskDefinition,
44
44
  [DefinitionEnum_1.DefinitionEnum.mysqlDumpTask]: MysqlDumpTask_1.mysqlDumpTaskDefinition,
@@ -9,6 +9,9 @@ export type MetaDataType = {
9
9
  tags: string[];
10
10
  version: string;
11
11
  size: number;
12
+ tarStats?: Record<string, {
13
+ files: number;
14
+ }>;
12
15
  };
13
16
  export type DatatruckRepositoryConfigType = {
14
17
  outPath: string;
@@ -196,7 +196,7 @@ class DatatruckRepository extends RepositoryAbstract_1.RepositoryAbstract {
196
196
  if (entry.dirent.isDirectory() &&
197
197
  !(await (0, fs_1.isEmptyDir)((0, path_1.join)(sourcePath, entry.path))))
198
198
  return false;
199
- let packIndex = configPacks.findIndex((pack) => (0, string_1.checkPath)(entry.path, pack.include, pack.exclude));
199
+ let packIndex = configPacks.findIndex((pack) => (0, string_1.match)(entry.path, pack.include, pack.exclude));
200
200
  if (packIndex === -1)
201
201
  packIndex = defaultsPackIndex;
202
202
  const pack = packs[packIndex];
@@ -213,22 +213,26 @@ class DatatruckRepository extends RepositoryAbstract_1.RepositoryAbstract {
213
213
  });
214
214
  await stream.end();
215
215
  let packIndex = 0;
216
+ const tarStats = {};
216
217
  for (const pack of packs) {
217
218
  const packBasename = [`pack`, packIndex.toString(), pack.name]
218
219
  .filter((v) => typeof v === "string")
219
220
  .map((v) => encodeURIComponent(v.toString().replace(/[\\/]/g, "-")))
220
- .join("-");
221
- const ext = pack.compress ? `.tar.gz` : `.tar`;
221
+ .join("-") + (pack.compress ? `.tar.gz` : `.tar`);
222
222
  const includeList = stream.path(packIndex);
223
- if (includeList)
223
+ if (includeList) {
224
+ tarStats[packBasename] = {
225
+ files: stream.lines(packIndex),
226
+ };
224
227
  await (0, tar_1.createTar)({
225
228
  compress: pack.compress,
226
229
  verbose: data.options.verbose,
227
230
  includeList,
228
231
  path: sourcePath,
229
- output: (0, path_1.join)(outPath, packBasename) + ext,
232
+ output: (0, path_1.join)(outPath, packBasename),
230
233
  onEntry: async (data) => await scanner.progress(pack.compress ? "Compressing" : "Packing", data.path),
231
234
  });
235
+ }
232
236
  packIndex++;
233
237
  }
234
238
  await scanner.end();
@@ -243,6 +247,7 @@ class DatatruckRepository extends RepositoryAbstract_1.RepositoryAbstract {
243
247
  task: data.package.task?.name,
244
248
  version: nodePkg.version,
245
249
  size: await (0, fs_1.fastFolderSizeAsync)(outPath),
250
+ tarStats,
246
251
  };
247
252
  if (data.options.verbose)
248
253
  (0, cli_1.logExec)(`Writing metadata into ${metaPath}`);
@@ -298,6 +303,8 @@ class DatatruckRepository extends RepositoryAbstract_1.RepositoryAbstract {
298
303
  packageName: data.package.name,
299
304
  });
300
305
  const sourcePath = (0, path_1.join)(this.config.outPath, snapshotName);
306
+ const metaPath = (0, path_1.join)(sourcePath, "meta.json");
307
+ const meta = await DatatruckRepository.parseMetaData(metaPath);
301
308
  const scanner = await (0, fs_1.createFileScanner)({
302
309
  onProgress: data.onProgress,
303
310
  glob: {
@@ -306,26 +313,37 @@ class DatatruckRepository extends RepositoryAbstract_1.RepositoryAbstract {
306
313
  },
307
314
  });
308
315
  const tarFiles = [];
316
+ const tarStats = meta?.tarStats || {};
309
317
  await scanner.start(async (entry) => {
310
318
  const path = (0, path_1.join)(sourcePath, entry.name);
311
319
  const isTar = entry.name.endsWith(".tar");
312
320
  const isTarGz = entry.name.endsWith(".tar.gz");
313
321
  if (isTar || isTarGz) {
314
322
  tarFiles.push(path);
315
- await (0, tar_1.listTar)({
316
- input: path,
317
- verbose: data.options.verbose,
318
- onEntry: () => {
319
- scanner.total++;
320
- },
321
- });
323
+ if (typeof tarStats[entry.name]?.files === "number") {
324
+ scanner.total += tarStats[entry.name].files;
325
+ }
326
+ else {
327
+ scanner.progress("Scanning", entry.name, false);
328
+ const selfTarStats = (tarStats[entry.name] = { files: 0 });
329
+ await (0, tar_1.listTar)({
330
+ input: path,
331
+ verbose: data.options.verbose,
332
+ onEntry: () => {
333
+ scanner.total++;
334
+ selfTarStats.files++;
335
+ },
336
+ });
337
+ }
322
338
  }
323
339
  return false;
324
340
  });
325
341
  if (data.options.verbose)
326
342
  (0, cli_1.logExec)(`Unpacking files to ${restorePath}`);
327
343
  for (const tarFile of tarFiles) {
344
+ const entryName = (0, path_1.basename)(tarFile);
328
345
  await (0, tar_1.extractTar)({
346
+ total: tarStats[entryName].files,
329
347
  input: tarFile,
330
348
  output: restorePath,
331
349
  decompress: tarFile.endsWith(".tar.gz"),
@@ -1,17 +1,15 @@
1
- import { SqlDumpTaskAbstract, SqlDumpTaskConfigType, TargetDatabaseType } from "./SqlDumpTaskAbstract";
2
- import { JSONSchema7 } from "json-schema";
1
+ import { SqlDumpTaskConfigType } from "./SqlDumpTaskAbstract";
2
+ import { BackupDataType, RestoreDataType, TaskAbstract } from "./TaskAbstract";
3
3
  export declare const mysqlDumpTaskName = "mysql-dump";
4
- export type MysqlDumpTaskConfigType = {} & SqlDumpTaskConfigType;
5
- export declare const mysqlDumpTaskDefinition: JSONSchema7;
6
- export declare class MysqlDumpTask extends SqlDumpTaskAbstract<MysqlDumpTaskConfigType> {
7
- buildConnectionArgs(database?: boolean): Promise<string[]>;
8
- onDatabaseIsEmpty(name: string): Promise<boolean>;
9
- onCreateDatabase(database: TargetDatabaseType): Promise<void>;
10
- onExecQuery(query: string): Promise<import("../utils/process").ExecResultType>;
11
- onFetchTableNames(database: string): Promise<string[]>;
12
- onExportTables(tableNames: string[], output: string, onProgress: (progress: {
13
- totalBytes: number;
14
- }) => void): Promise<void>;
15
- onExportStoredPrograms(output: string): Promise<void>;
16
- onImport(path: string, database: string): Promise<void>;
4
+ export type MysqlDumpTaskConfigType = {
5
+ /**
6
+ * @default "sql"
7
+ */
8
+ dataFormat?: "csv" | "sql";
9
+ csvSharedPath?: string;
10
+ } & SqlDumpTaskConfigType;
11
+ export declare const mysqlDumpTaskDefinition: import("json-schema").JSONSchema7;
12
+ export declare class MysqlDumpTask extends TaskAbstract<MysqlDumpTaskConfigType> {
13
+ onBackup(data: BackupDataType): Promise<void>;
14
+ onRestore(data: RestoreDataType): Promise<void>;
17
15
  }