@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.
- package/config.schema.json +3 -0
- package/lib/actions/BackupAction.d.ts +8 -2
- package/lib/actions/BackupAction.js +22 -8
- package/lib/actions/CopyAction.d.ts +4 -1
- package/lib/actions/CopyAction.js +15 -4
- package/lib/commands/BackupCommand.d.ts +2 -0
- package/lib/repositories/DatatruckRepository.d.ts +6 -2
- package/lib/repositories/DatatruckRepository.js +11 -3
- package/lib/repositories/GitRepository.d.ts +6 -2
- package/lib/repositories/GitRepository.js +8 -3
- package/lib/repositories/RepositoryAbstract.d.ts +6 -2
- package/lib/repositories/ResticRepository.d.ts +6 -2
- package/lib/repositories/ResticRepository.js +10 -4
- package/lib/utils/Restic.d.ts +1 -2
- package/lib/utils/Restic.js +0 -8
- package/lib/utils/datatruck/client.d.ts +3 -1
- package/lib/utils/datatruck/client.js +1 -1
- package/lib/utils/datatruck/config-type.d.ts +1 -0
- package/lib/utils/datatruck/report-list.d.ts +2 -0
- package/lib/utils/datatruck/report-list.js +1 -1
- package/lib/utils/http.d.ts +3 -1
- package/lib/utils/http.js +6 -1
- package/lib/utils/virtual-fs.d.ts +6 -2
- package/lib/utils/virtual-fs.js +4 -1
- package/package.json +2 -2
package/config.schema.json
CHANGED
|
@@ -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<
|
|
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<
|
|
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) => [
|
|
109
|
-
|
|
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<
|
|
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([
|
|
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<
|
|
54
|
-
|
|
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
|
|
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<
|
|
26
|
-
|
|
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:
|
|
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<
|
|
87
|
-
|
|
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<
|
|
37
|
-
|
|
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;
|
package/lib/utils/Restic.d.ts
CHANGED
|
@@ -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) =>
|
|
97
|
+
onStream?: (data: ResticBackupStream) => void;
|
|
99
98
|
}): Promise<ExecResult>;
|
|
100
99
|
restore(options: {
|
|
101
100
|
id: string;
|
package/lib/utils/Restic.js
CHANGED
|
@@ -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<
|
|
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:
|
|
44
|
+
title: `[${options.hostname}] DTT ${options.action}`,
|
|
45
45
|
message,
|
|
46
46
|
success,
|
|
47
47
|
},
|
package/lib/utils/http.d.ts
CHANGED
|
@@ -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<
|
|
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
|
-
|
|
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<
|
|
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<
|
|
44
|
+
download(source: string, target: string): Promise<{
|
|
45
|
+
bytes: number;
|
|
46
|
+
}>;
|
|
43
47
|
}
|
package/lib/utils/virtual-fs.js
CHANGED
|
@@ -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
|
-
|
|
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.
|
|
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.
|
|
38
|
+
"listr2": "^8.0.1",
|
|
39
39
|
"micromatch": "^4.0.5",
|
|
40
40
|
"mysql2": "^3.6.5",
|
|
41
41
|
"tty-table": "^4.2.3",
|