@datatruck/cli 0.33.0 → 0.34.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.
@@ -4,6 +4,9 @@
4
4
  "$schema": {
5
5
  "type": "string"
6
6
  },
7
+ "hostname": {
8
+ "type": "string"
9
+ },
7
10
  "tempDir": {
8
11
  "type": "string"
9
12
  },
@@ -31,12 +31,14 @@ type Context = {
31
31
  backup: {
32
32
  packageName: string;
33
33
  repositoryName: string;
34
+ bytes: number;
34
35
  };
35
36
  cleanup: {};
36
37
  copy: {
37
38
  packageName: string;
38
39
  repositoryName: string;
39
40
  mirrorRepositoryName: string;
41
+ bytes: number;
40
42
  };
41
43
  prune: {
42
44
  packageName: string;
@@ -60,14 +62,18 @@ export declare class BackupAction<TRequired extends boolean = true> {
60
62
  snapshotPath: string | undefined;
61
63
  pkg: PackageConfig;
62
64
  onProgress: (data: Progress) => void;
63
- }): Promise<void>;
65
+ }): Promise<{
66
+ bytes: number;
67
+ }>;
64
68
  protected copy(data: {
65
69
  repositoryName: string;
66
70
  mirrorRepositoryName: string;
67
71
  snapshot: PreSnapshot;
68
72
  pkg: PackageConfig;
69
73
  onProgress: (data: Progress) => void;
70
- }): Promise<void>;
74
+ }): Promise<{
75
+ bytes: number;
76
+ }>;
71
77
  dataFormat(result: Listr3TaskResultEnd<Context>[], options?: {
72
78
  streams?: Streams;
73
79
  verbose?: number;
@@ -5,6 +5,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.BackupAction = void 0;
7
7
  const DataFormat_1 = require("../utils/DataFormat");
8
+ const bytes_1 = require("../utils/bytes");
8
9
  const cli_1 = require("../utils/cli");
9
10
  const config_1 = require("../utils/datatruck/config");
10
11
  const report_list_1 = require("../utils/datatruck/report-list");
@@ -20,6 +21,7 @@ const assert_1 = require("assert");
20
21
  const chalk_1 = __importDefault(require("chalk"));
21
22
  const crypto_1 = require("crypto");
22
23
  const dayjs_1 = __importDefault(require("dayjs"));
24
+ const os_1 = require("os");
23
25
  class BackupAction {
24
26
  config;
25
27
  options;
@@ -72,7 +74,7 @@ class BackupAction {
72
74
  await repo.ensureFreeDiskSpace(repoConfig.config, this.config.minFreeDiskSpace);
73
75
  const packageConfig = pkg.repositoryConfigs?.find((config) => config.type === repoConfig.type &&
74
76
  (!config.names || config.names.includes(repoConfig.name)))?.config;
75
- await repo.backup({
77
+ return await repo.backup({
76
78
  options: this.options,
77
79
  snapshot: data.snapshot,
78
80
  package: pkg,
@@ -87,7 +89,7 @@ class BackupAction {
87
89
  const mirrorRepo = await (0, repository_1.createAndInitRepo)(mirrorRepoConfig, this.options.verbose);
88
90
  if (this.config.minFreeDiskSpace)
89
91
  await mirrorRepo.ensureFreeDiskSpace(mirrorRepoConfig.config, this.config.minFreeDiskSpace);
90
- await repo.copy({
92
+ return await repo.copy({
91
93
  options: this.options,
92
94
  package: data.pkg,
93
95
  snapshot: data.snapshot,
@@ -105,8 +107,14 @@ class BackupAction {
105
107
  return (0, cli_1.renderListTaskItem)(item, color, {
106
108
  snapshot: (data) => data.id,
107
109
  task: (data) => [data.packageName, data.taskName],
108
- backup: (data) => [data.packageName, g(data.repositoryName)],
109
- copy: (data) => [data.packageName, g(data.mirrorRepositoryName)],
110
+ backup: (data) => [
111
+ data.packageName,
112
+ g([data.repositoryName, (0, bytes_1.formatBytes)(data.bytes)].join(" ")),
113
+ ],
114
+ copy: (data) => [
115
+ data.packageName,
116
+ g([data.mirrorRepositoryName, (0, bytes_1.formatBytes)(data.bytes)].join(" ")),
117
+ ],
110
118
  prune: (data) => [data.packageName, g(`${data.pruned}/${data.total}`)],
111
119
  cleanup: () => "",
112
120
  report: (data) => data.type,
@@ -220,6 +228,7 @@ class BackupAction {
220
228
  data: {
221
229
  packageName: pkg.name,
222
230
  repositoryName: repositoryName,
231
+ bytes: 0,
223
232
  },
224
233
  title: {
225
234
  initial: `Create backup: ${pkg.name} (${repositoryName})`,
@@ -229,19 +238,20 @@ class BackupAction {
229
238
  },
230
239
  exitOnError: false,
231
240
  runWrapper: gc.cleanupOnFinish.bind(gc),
232
- run: async (task) => {
241
+ run: async (task, data) => {
233
242
  const taskSummary = pkg.task
234
243
  ? l.result("task", pkg.name)
235
244
  : undefined;
236
245
  if (taskSummary?.error)
237
246
  throw new Error(`Task failed`);
238
- await this.backup({
247
+ const backup = await this.backup({
239
248
  pkg,
240
249
  repositoryName,
241
250
  snapshot,
242
251
  snapshotPath: taskResult?.snapshotPath,
243
252
  onProgress: (p) => pm.update(p, (t) => (task.output = t)),
244
253
  });
254
+ data.bytes = backup.bytes;
245
255
  },
246
256
  })), l.$task({
247
257
  key: "cleanup",
@@ -263,6 +273,7 @@ class BackupAction {
263
273
  packageName: pkg.name,
264
274
  repositoryName: name,
265
275
  mirrorRepositoryName: mirror,
276
+ bytes: 0,
266
277
  },
267
278
  title: {
268
279
  initial: `Copy snapshot: ${pkg.name} (${mirror})`,
@@ -272,20 +283,21 @@ class BackupAction {
272
283
  },
273
284
  exitOnError: false,
274
285
  runWrapper: gc.cleanup.bind(gc),
275
- run: async (task) => {
286
+ run: async (task, data) => {
276
287
  const backupSummary = l.result("backup", [
277
288
  pkg.name,
278
289
  name,
279
290
  ]);
280
291
  if (backupSummary.error)
281
292
  throw new Error(`Backup failed`);
282
- await this.copy({
293
+ const copy = await this.copy({
283
294
  repositoryName: name,
284
295
  mirrorRepositoryName: mirror,
285
296
  pkg,
286
297
  snapshot,
287
298
  onProgress: (p) => pm.update(p, (t) => (task.output = t)),
288
299
  });
300
+ data.bytes = copy.bytes;
289
301
  },
290
302
  })), !!this.options.prune &&
291
303
  l.$task({
@@ -317,6 +329,8 @@ class BackupAction {
317
329
  }));
318
330
  }),
319
331
  ...(0, report_list_1.createReportListTasks)(l, {
332
+ hostname: this.config.hostname ?? (0, os_1.hostname)(),
333
+ action: "backup",
320
334
  reports: this.config.reports || [],
321
335
  verbose: this.options.verbose,
322
336
  onMessage: (result, report) => this.dataFormat(result).format(report.format ?? "list"),
@@ -31,6 +31,7 @@ export type Context = {
31
31
  repositoryName: string;
32
32
  mirrorRepositoryName: string;
33
33
  skipped: boolean;
34
+ bytes: number;
34
35
  };
35
36
  } & ReportListTaskContext;
36
37
  export declare class CopyAction<TRequired extends boolean = true> {
@@ -50,6 +51,8 @@ export declare class CopyAction<TRequired extends boolean = true> {
50
51
  mirrorConfig: RepositoryConfig;
51
52
  snapshot: Snapshot;
52
53
  onProgress: (p: Progress) => void;
53
- }): Promise<void>;
54
+ }): Promise<{
55
+ bytes: number;
56
+ }>;
54
57
  exec(): Promise<(import("../utils/list").List3SummaryResult | import("../utils/list").Listr3TaskResult<Context>)[]>;
55
58
  }
@@ -50,6 +50,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
50
50
  Object.defineProperty(exports, "__esModule", { value: true });
51
51
  exports.CopyAction = void 0;
52
52
  const DataFormat_1 = require("../utils/DataFormat");
53
+ const bytes_1 = require("../utils/bytes");
53
54
  const cli_1 = require("../utils/cli");
54
55
  const config_1 = require("../utils/datatruck/config");
55
56
  const report_list_1 = require("../utils/datatruck/report-list");
@@ -61,6 +62,7 @@ const object_1 = require("../utils/object");
61
62
  const progress_1 = require("../utils/progress");
62
63
  const temp_1 = require("../utils/temp");
63
64
  const chalk_1 = __importDefault(require("chalk"));
65
+ const os_1 = require("os");
64
66
  class CopyAction {
65
67
  config;
66
68
  options;
@@ -79,7 +81,11 @@ class CopyAction {
79
81
  snapshots: (data) => data.snapshots.length,
80
82
  copy: (data) => [
81
83
  data.packageName,
82
- g([data.snapshotId.slice(0, 8), data.mirrorRepositoryName].join(" ")),
84
+ g([
85
+ data.snapshotId.slice(0, 8),
86
+ data.mirrorRepositoryName,
87
+ (0, bytes_1.formatBytes)(data.bytes),
88
+ ].join(" ")),
83
89
  ],
84
90
  report: (data) => data.type,
85
91
  summary: (data) => ({
@@ -150,7 +156,7 @@ class CopyAction {
150
156
  });
151
157
  if (this.config.minFreeDiskSpace)
152
158
  await mirrorRepo.ensureFreeDiskSpace(mirrorConfig.config, this.config.minFreeDiskSpace);
153
- await mirrorRepo.backup({
159
+ return await mirrorRepo.backup({
154
160
  options: {
155
161
  verbose: this.options.verbose,
156
162
  tags: snapshot.tags,
@@ -219,6 +225,7 @@ class CopyAction {
219
225
  packageName: snapshot.packageName,
220
226
  repositoryName: repoConfig.name,
221
227
  mirrorRepositoryName: repo2.name,
228
+ bytes: 0,
222
229
  skipped: false,
223
230
  },
224
231
  title: {
@@ -249,16 +256,17 @@ class CopyAction {
249
256
  mirrorConfig,
250
257
  ]);
251
258
  if (sourceRepo.has()) {
252
- await sourceRepo.get().copy({
259
+ const copy = await sourceRepo.get().copy({
253
260
  mirrorRepositoryConfig: mirrorConfig.config,
254
261
  options: { verbose: this.options.verbose },
255
262
  package: { name: snapshot.packageName },
256
263
  snapshot,
257
264
  onProgress: (p) => pm.update(p, (d) => (task.output = d)),
258
265
  });
266
+ data.bytes = copy.bytes;
259
267
  }
260
268
  else {
261
- await this.copyCrossRepository({
269
+ const copy = await this.copyCrossRepository({
262
270
  mirrorConfig,
263
271
  mirrorRepo,
264
272
  repo,
@@ -266,6 +274,7 @@ class CopyAction {
266
274
  snapshot,
267
275
  onProgress: (p) => pm.update(p, (d) => (task.output = d)),
268
276
  });
277
+ data.bytes = copy.bytes;
269
278
  sourceRepo.set(mirrorRepo);
270
279
  }
271
280
  },
@@ -274,6 +283,8 @@ class CopyAction {
274
283
  },
275
284
  }),
276
285
  ...(0, report_list_1.createReportListTasks)(l, {
286
+ hostname: this.config.hostname ?? (0, os_1.hostname)(),
287
+ action: "copy",
277
288
  reports: this.config.reports || [],
278
289
  verbose: this.options.verbose,
279
290
  onMessage: (result, report) => this.dataFormat(result).format(report.format ?? "list"),
@@ -25,12 +25,14 @@ export declare class BackupCommand extends CommandAbstract<BackupCommandOptions<
25
25
  backup: {
26
26
  packageName: string;
27
27
  repositoryName: string;
28
+ bytes: number;
28
29
  };
29
30
  cleanup: {};
30
31
  copy: {
31
32
  packageName: string;
32
33
  repositoryName: string;
33
34
  mirrorRepositoryName: string;
35
+ bytes: number;
34
36
  };
35
37
  prune: {
36
38
  packageName: string;
@@ -50,8 +50,12 @@ export declare class DatatruckRepository extends RepositoryAbstract<DatatruckRep
50
50
  init(data: RepoInitData): Promise<void>;
51
51
  prune(data: RepoPruneData): Promise<void>;
52
52
  fetchSnapshots(data: RepoFetchSnapshotsData): Promise<Snapshot[]>;
53
- backup(data: RepoBackupData<DatatruckPackageRepositoryConfig>): Promise<void>;
54
- copy(data: RepoCopyData<DatatruckRepositoryConfig>): Promise<void>;
53
+ backup(data: RepoBackupData<DatatruckPackageRepositoryConfig>): Promise<{
54
+ bytes: number;
55
+ }>;
56
+ copy(data: RepoCopyData<DatatruckRepositoryConfig>): Promise<{
57
+ bytes: number;
58
+ }>;
55
59
  restore(data: RepoRestoreData<DatatruckPackageRepositoryConfig>): Promise<void>;
56
60
  }
57
61
  export {};
@@ -203,6 +203,7 @@ class DatatruckRepository extends RepositoryAbstract_1.RepositoryAbstract {
203
203
  }
204
204
  scanner.end();
205
205
  // Meta
206
+ const size = Object.values(tarStats).reduce((total, { size }) => total + size, 0);
206
207
  const metaPath = `${snapshotName}/meta.json`;
207
208
  const nodePkg = (0, fs_1.parsePackageFile)();
208
209
  const meta = {
@@ -212,12 +213,15 @@ class DatatruckRepository extends RepositoryAbstract_1.RepositoryAbstract {
212
213
  package: data.package.name,
213
214
  task: data.package.task?.name,
214
215
  version: nodePkg.version,
215
- size: Object.values(tarStats).reduce((total, { size }) => total + size, 0),
216
+ size,
216
217
  tarStats,
217
218
  };
218
219
  if (data.options.verbose)
219
220
  (0, cli_1.logExec)(`Writing metadata into ${fs.resolvePath(metaPath)}`);
220
221
  await fs.writeFile(`${snapshotName}/meta.json`, JSON.stringify(meta));
222
+ return {
223
+ bytes: size,
224
+ };
221
225
  }
222
226
  async copy(data) {
223
227
  const sourceFs = (0, client_1.createFs)(this.config.backend);
@@ -236,6 +240,7 @@ class DatatruckRepository extends RepositoryAbstract_1.RepositoryAbstract {
236
240
  await targetFs.ensureEmptyDir(tmpSnapshotName);
237
241
  const entries = await sourceFs.readdir(snapshotName);
238
242
  const total = entries.length;
243
+ let bytes = 0;
239
244
  let current = 0;
240
245
  for (const entry of entries) {
241
246
  const absolute = {
@@ -250,7 +255,7 @@ class DatatruckRepository extends RepositoryAbstract_1.RepositoryAbstract {
250
255
  const sourceEntry = `${snapshotName}/${entry}`;
251
256
  const targetEntry = `${tmpSnapshotName}/${entry}`;
252
257
  if (targetFs.isLocal()) {
253
- await sourceFs.download(sourceEntry, targetFs.resolvePath(targetEntry), {
258
+ const downloaded = await sourceFs.download(sourceEntry, targetFs.resolvePath(targetEntry), {
254
259
  onProgress: (progress) => data.onProgress({
255
260
  absolute,
256
261
  relative: {
@@ -260,12 +265,13 @@ class DatatruckRepository extends RepositoryAbstract_1.RepositoryAbstract {
260
265
  },
261
266
  }),
262
267
  });
268
+ bytes += downloaded.bytes;
263
269
  }
264
270
  else {
265
271
  const tempDir = await (0, temp_1.mkTmpDir)(exports.datatruckRepositoryName, "repo", "remote-copy", entry);
266
272
  const tempFile = (0, path_1.join)(tempDir, entry);
267
273
  try {
268
- await sourceFs.download(sourceEntry, tempFile, {
274
+ const downloaded = await sourceFs.download(sourceEntry, tempFile, {
269
275
  onProgress: (progress) => data.onProgress({
270
276
  absolute,
271
277
  relative: {
@@ -275,6 +281,7 @@ class DatatruckRepository extends RepositoryAbstract_1.RepositoryAbstract {
275
281
  },
276
282
  }),
277
283
  });
284
+ bytes += downloaded.bytes;
278
285
  await targetFs.upload(tempFile, targetEntry);
279
286
  }
280
287
  finally {
@@ -283,6 +290,7 @@ class DatatruckRepository extends RepositoryAbstract_1.RepositoryAbstract {
283
290
  }
284
291
  }
285
292
  await targetFs.rename(tmpSnapshotName, snapshotName);
293
+ return { bytes };
286
294
  }
287
295
  async restore(data) {
288
296
  const fs = (0, client_1.createFs)(this.config.backend);
@@ -22,7 +22,11 @@ export declare class GitRepository extends RepositoryAbstract<GitRepositoryConfi
22
22
  init(data: RepoInitData): Promise<void>;
23
23
  prune(data: RepoPruneData): Promise<void>;
24
24
  fetchSnapshots(data: RepoFetchSnapshotsData): Promise<Snapshot[]>;
25
- backup(data: RepoBackupData<GitPackageRepositoryConfig>): Promise<void>;
26
- copy(data: RepoCopyData<GitRepositoryConfig>): Promise<void>;
25
+ backup(data: RepoBackupData<GitPackageRepositoryConfig>): Promise<{
26
+ bytes: number;
27
+ }>;
28
+ copy(data: RepoCopyData<GitRepositoryConfig>): Promise<{
29
+ bytes: number;
30
+ }>;
27
31
  restore(data: RepoRestoreData<GitPackageRepositoryConfig>): Promise<void>;
28
32
  }
@@ -187,6 +187,8 @@ class GitRepository extends RepositoryAbstract_1.RepositoryAbstract {
187
187
  if (await git.haveChanges())
188
188
  await git.exec(["commit", "-m", data.snapshot.id]);
189
189
  const nodePkg = (0, fs_1.parsePackageFile)();
190
+ const size = (await (0, fs_1.fastFolderSizeAsync)(tmpPath)) -
191
+ (await (0, fs_1.fastFolderSizeAsync)((0, path_1.join)(tmpPath, ".git")));
190
192
  const meta = GitRepository.buildSnapshotTag({
191
193
  id: data.snapshot.id,
192
194
  shortId: data.snapshot.id.slice(0, 8),
@@ -195,16 +197,19 @@ class GitRepository extends RepositoryAbstract_1.RepositoryAbstract {
195
197
  package: data.package.name,
196
198
  task: data.package.task?.name,
197
199
  version: nodePkg.version,
198
- size: ((await (0, fs_1.fastFolderSizeAsync)(tmpPath)) -
199
- (await (0, fs_1.fastFolderSizeAsync)((0, path_1.join)(tmpPath, ".git")))).toString(),
200
+ size: size.toString(),
200
201
  });
201
202
  await git.addTag(meta.name, meta.message);
202
203
  await git.push({ branchName });
203
204
  await git.pushTags();
204
205
  await (0, promises_1.rm)(tmpPath, { recursive: true });
206
+ return {
207
+ bytes: size,
208
+ };
205
209
  }
206
- copy(data) {
210
+ async copy(data) {
207
211
  throw new Error("Method not implemented.");
212
+ return { bytes: 0 };
208
213
  }
209
214
  async restore(data) {
210
215
  const restorePath = data.snapshotPath;
@@ -83,7 +83,11 @@ export declare abstract class RepositoryAbstract<TConfig> {
83
83
  abstract init(data: RepoInitData): Promise<void>;
84
84
  abstract prune(data: RepoPruneData): Promise<void>;
85
85
  abstract fetchSnapshots(data: RepoFetchSnapshotsData): Promise<Snapshot[]>;
86
- abstract copy(data: RepoCopyData<TConfig>): Promise<void>;
87
- abstract backup(data: RepoBackupData<unknown>): Promise<void>;
86
+ abstract copy(data: RepoCopyData<TConfig>): Promise<{
87
+ bytes: number;
88
+ }>;
89
+ abstract backup(data: RepoBackupData<unknown>): Promise<{
90
+ bytes: number;
91
+ }>;
88
92
  abstract restore(data: RepoRestoreData<unknown>): Promise<void>;
89
93
  }
@@ -33,7 +33,11 @@ export declare class ResticRepository extends RepositoryAbstract<ResticRepositor
33
33
  init(data: RepoInitData): Promise<void>;
34
34
  fetchSnapshots(data: RepoFetchSnapshotsData): Promise<Snapshot[]>;
35
35
  prune(data: RepoPruneData): Promise<void>;
36
- backup(data: RepoBackupData<ResticPackageRepositoryConfig>): Promise<void>;
37
- copy(data: RepoCopyData<ResticRepositoryConfig>): Promise<void>;
36
+ backup(data: RepoBackupData<ResticPackageRepositoryConfig>): Promise<{
37
+ bytes: number;
38
+ }>;
39
+ copy(data: RepoCopyData<ResticRepositoryConfig>): Promise<{
40
+ bytes: number;
41
+ }>;
38
42
  restore(data: RepoRestoreData<ResticPackageRepositoryConfig>): Promise<void>;
39
43
  }
@@ -204,10 +204,6 @@ class ResticRepository extends RepositoryAbstract_1.RepositoryAbstract {
204
204
  allowEmptySnapshot: true,
205
205
  excludeFile: gitignorePath ? [gitignorePath] : undefined,
206
206
  parent: lastSnapshot?.id,
207
- // https://github.com/restic/restic/pull/3200
208
- ...((await restic.checkBackupSetPathSupport()) && {
209
- setPaths: [`/datatruck/${data.package.name}`],
210
- }),
211
207
  tags: [
212
208
  ResticRepository.buildSnapshotTag(RepositoryAbstract_1.SnapshotTagEnum.ID, data.snapshot.id),
213
209
  ResticRepository.buildSnapshotTag(RepositoryAbstract_1.SnapshotTagEnum.SHORT_ID, data.snapshot.id.slice(0, 8)),
@@ -267,6 +263,9 @@ class ResticRepository extends RepositoryAbstract_1.RepositoryAbstract {
267
263
  percent: 100,
268
264
  },
269
265
  });
266
+ return {
267
+ bytes: resticTotalBytes,
268
+ };
270
269
  }
271
270
  async copy(data) {
272
271
  const config = data.mirrorRepositoryConfig;
@@ -289,9 +288,16 @@ class ResticRepository extends RepositoryAbstract_1.RepositoryAbstract {
289
288
  },
290
289
  log: data.options.verbose,
291
290
  });
291
+ let bytes = 0;
292
292
  await restic.copy({
293
293
  id: snapshot.originalId,
294
+ onStream(data) {
295
+ if (data.message_type === "status") {
296
+ bytes = data.total_bytes;
297
+ }
298
+ },
294
299
  });
300
+ return { bytes };
295
301
  }
296
302
  async restore(data) {
297
303
  const restorePath = data.snapshotPath;
@@ -80,7 +80,6 @@ export declare class Restic {
80
80
  id: string;
81
81
  short_id: string;
82
82
  }[]>;
83
- checkBackupSetPathSupport(): Promise<boolean>;
84
83
  backup(options: {
85
84
  cwd?: string;
86
85
  tags?: string[];
@@ -95,7 +94,7 @@ export declare class Restic {
95
94
  }): Promise<ExecResult>;
96
95
  copy(options: {
97
96
  id: string;
98
- onStream?: (data: ResticBackupStream) => Promise<void>;
97
+ onStream?: (data: ResticBackupStream) => void;
99
98
  }): Promise<ExecResult>;
100
99
  restore(options: {
101
100
  id: string;
@@ -102,14 +102,6 @@ class Restic {
102
102
  });
103
103
  return JSON.parse(result.stdout);
104
104
  }
105
- async checkBackupSetPathSupport() {
106
- /*const result = await this.exec(["backup", "--set-path"], {
107
- onExitCodeError: () => false,
108
- stderr: { save: true },
109
- });
110
- return result.stderr.includes("flag needs an argument");*/
111
- return false;
112
- }
113
105
  async backup(options) {
114
106
  const exec = async () => await this.exec([
115
107
  "backup",
@@ -23,7 +23,9 @@ export declare class RemoteFs extends AbstractFs {
23
23
  download(source: string, target: string, options?: {
24
24
  timeout?: number;
25
25
  onProgress?: (progress: BasicProgress) => void;
26
- }): Promise<void>;
26
+ }): Promise<{
27
+ bytes: number;
28
+ }>;
27
29
  }
28
30
  export declare function isRemoteBackend(backend: string): boolean;
29
31
  export declare function createFs(backend: string): AbstractFs;
@@ -80,7 +80,7 @@ class RemoteFs extends virtual_fs_1.AbstractFs {
80
80
  });
81
81
  }
82
82
  async download(source, target, options = {}) {
83
- await (0, http_1.downloadFile)(`${this.url}/download`, target, {
83
+ return await (0, http_1.downloadFile)(`${this.url}/download`, target, {
84
84
  ...options,
85
85
  headers: this.headers,
86
86
  query: { params: JSON.stringify([source]) },
@@ -9,6 +9,7 @@ import type { DatatruckRepositoryServerOptions } from "./repository-server";
9
9
  export { RepositoryConfig, RepositoryConfigEnabledAction, TaskConfig };
10
10
  export type Config = {
11
11
  $schema?: string;
12
+ hostname?: string;
12
13
  tempDir?: string;
13
14
  minFreeDiskSpace?: string | number;
14
15
  repositories: RepositoryConfig[];
@@ -6,6 +6,8 @@ export type ReportListTaskContext = {
6
6
  };
7
7
  };
8
8
  export declare function createReportListTasks<T extends ReportListTaskContext>(list: Listr3<T>, options: {
9
+ action: string;
10
+ hostname: string;
9
11
  reports: DatatruckReportConfig[];
10
12
  onMessage: (result: (List3SummaryResult | Listr3TaskResult<T>)[], report: DatatruckReportConfig) => string;
11
13
  verbose?: boolean;
@@ -41,7 +41,7 @@ function createReportListTasks(list, options) {
41
41
  else if ((0, reportSteps_1.isReportStep)(report.run)) {
42
42
  await (0, reportSteps_1.runReportSteps)(report.run, {
43
43
  data: {
44
- title: "DTT Backup",
44
+ title: `[${options.hostname}] DTT ${options.action}`,
45
45
  message,
46
46
  success,
47
47
  },
@@ -22,7 +22,9 @@ export declare function downloadFile(url: string, output: string, options?: {
22
22
  query?: Record<string, string>;
23
23
  timeout?: number;
24
24
  onProgress?: (progress: BasicProgress) => void;
25
- }): Promise<void>;
25
+ }): Promise<{
26
+ bytes: number;
27
+ }>;
26
28
  export declare function uploadFile(url: string, path: string, options?: {
27
29
  headers?: Record<string, string>;
28
30
  query?: Record<string, string>;
package/lib/utils/http.js CHANGED
@@ -90,6 +90,7 @@ exports.post = post;
90
90
  async function downloadFile(url, output, options = {}) {
91
91
  const timeout = options.timeout ?? 3600 * 1000; // 60m
92
92
  const file = (0, fs_1.createWriteStream)(output);
93
+ let total = 0;
93
94
  await new Promise((resolve, reject) => {
94
95
  const req = request(href(url, options.query), {
95
96
  headers: options.headers,
@@ -97,7 +98,7 @@ async function downloadFile(url, output, options = {}) {
97
98
  const contentLength = res.headers["content-length"] ?? "";
98
99
  if (!/^\d+$/.test(contentLength))
99
100
  return reject(new Error(`Invalid 'content-length': ${contentLength}`));
100
- const total = Number(contentLength);
101
+ total = Number(contentLength);
101
102
  let current = 0;
102
103
  if (res.statusCode === 200) {
103
104
  if (options.onProgress) {
@@ -145,6 +146,10 @@ async function downloadFile(url, output, options = {}) {
145
146
  });
146
147
  req.end();
147
148
  });
149
+ const { size: bytes } = await (0, promises_1.stat)(output);
150
+ if (total !== bytes)
151
+ throw new Error(`Invalid download size: ${total} != ${bytes}`);
152
+ return { bytes };
148
153
  }
149
154
  exports.downloadFile = downloadFile;
150
155
  async function uploadFile(url, path, options = {}) {
@@ -23,7 +23,9 @@ export declare abstract class AbstractFs {
23
23
  abstract download(source: string, target: string, options?: {
24
24
  timeout?: number;
25
25
  onProgress?: (progress: BasicProgress) => void;
26
- }): Promise<void>;
26
+ }): Promise<{
27
+ bytes: number;
28
+ }>;
27
29
  abstract fetchDiskStats(source: string): Promise<DiskStats>;
28
30
  }
29
31
  export declare class LocalFs extends AbstractFs {
@@ -39,5 +41,7 @@ export declare class LocalFs extends AbstractFs {
39
41
  rmAll(path: string): Promise<void>;
40
42
  fetchDiskStats(source: string): Promise<DiskStats>;
41
43
  upload(source: string, target: string): Promise<void>;
42
- download(source: string, target: string): Promise<void>;
44
+ download(source: string, target: string): Promise<{
45
+ bytes: number;
46
+ }>;
43
47
  }
@@ -62,7 +62,10 @@ class LocalFs extends AbstractFs {
62
62
  await (0, promises_1.cp)(source, this.resolvePath(target));
63
63
  }
64
64
  async download(source, target) {
65
- await (0, promises_1.cp)(this.resolvePath(source), target);
65
+ const path = this.resolvePath(source);
66
+ const { size: bytes } = await (0, promises_1.stat)(path);
67
+ await (0, promises_1.cp)(path, target);
68
+ return { bytes };
66
69
  }
67
70
  }
68
71
  exports.LocalFs = LocalFs;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@datatruck/cli",
3
- "version": "0.33.0",
3
+ "version": "0.34.0",
4
4
  "description": "Tool for creating and managing backups",
5
5
  "homepage": "https://github.com/swordev/datatruck#readme",
6
6
  "bugs": {
@@ -35,7 +35,7 @@
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.0",
38
+ "listr2": "^8.0.1",
39
39
  "micromatch": "^4.0.5",
40
40
  "mysql2": "^3.6.5",
41
41
  "tty-table": "^4.2.3",