@datatruck/cli 0.27.0 → 0.28.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/Action/BackupAction.d.ts +69 -34
- package/Action/BackupAction.js +284 -244
- package/Action/CleanCacheAction.d.ts +8 -4
- package/Action/CleanCacheAction.js +8 -5
- package/Action/ConfigAction.d.ts +12 -5
- package/Action/ConfigAction.js +14 -18
- package/Action/CopyAction.d.ts +49 -0
- package/Action/CopyAction.js +144 -0
- package/Action/InitAction.d.ts +3 -3
- package/Action/InitAction.js +9 -9
- package/Action/PruneAction.d.ts +9 -9
- package/Action/PruneAction.js +39 -23
- package/Action/RestoreAction.d.ts +48 -23
- package/Action/RestoreAction.js +158 -195
- package/Action/SnapshotsAction.d.ts +8 -8
- package/Action/SnapshotsAction.js +8 -8
- package/CHANGELOG.md +495 -0
- package/Command/BackupCommand.d.ts +6 -4
- package/Command/BackupCommand.js +9 -26
- package/Command/CleanCacheCommand.d.ts +4 -4
- package/Command/CleanCacheCommand.js +26 -5
- package/Command/CommandAbstract.d.ts +10 -7
- package/Command/CommandAbstract.js +4 -1
- package/Command/ConfigCommand.d.ts +6 -9
- package/Command/ConfigCommand.js +13 -8
- package/Command/CopyCommand.d.ts +15 -0
- package/Command/CopyCommand.js +61 -0
- package/Command/InitCommand.d.ts +4 -4
- package/Command/InitCommand.js +11 -15
- package/Command/PruneCommand.d.ts +3 -3
- package/Command/PruneCommand.js +13 -12
- package/Command/RestoreCommand.js +9 -17
- package/Command/SnapshotsCommand.d.ts +4 -4
- package/Command/SnapshotsCommand.js +16 -15
- package/Command/StartServerCommand.d.ts +3 -3
- package/Config/Config.d.ts +9 -0
- package/Config/Config.js +17 -0
- package/Config/PrunePolicyConfig.d.ts +2 -2
- package/Factory/CommandFactory.d.ts +27 -34
- package/Factory/CommandFactory.js +27 -54
- package/Factory/RepositoryFactory.d.ts +1 -1
- package/Factory/RepositoryFactory.js +3 -3
- package/Factory/TaskFactory.d.ts +1 -1
- package/Factory/TaskFactory.js +3 -3
- package/Repository/DatatruckRepository.d.ts +9 -8
- package/Repository/DatatruckRepository.js +42 -25
- package/Repository/GitRepository.d.ts +9 -8
- package/Repository/GitRepository.js +22 -25
- package/Repository/RepositoryAbstract.d.ts +39 -37
- package/Repository/RepositoryAbstract.js +4 -5
- package/Repository/ResticRepository.d.ts +9 -8
- package/Repository/ResticRepository.js +30 -28
- package/Task/GitTask.d.ts +6 -7
- package/Task/GitTask.js +24 -30
- package/Task/MariadbTask.d.ts +4 -5
- package/Task/MariadbTask.js +26 -32
- package/Task/MssqlTask.d.ts +5 -3
- package/Task/MssqlTask.js +11 -12
- package/Task/MysqlDumpTask.d.ts +10 -3
- package/Task/MysqlDumpTask.js +107 -31
- package/Task/ScriptTask.d.ts +23 -18
- package/Task/ScriptTask.js +34 -24
- package/Task/SqlDumpTaskAbstract.d.ts +8 -3
- package/Task/SqlDumpTaskAbstract.js +31 -19
- package/Task/TaskAbstract.d.ts +24 -25
- package/Task/TaskAbstract.js +6 -10
- package/cli.js +13 -5
- package/config.schema.json +86 -1
- package/package.json +4 -5
- package/utils/DataFormat.d.ts +23 -12
- package/utils/DataFormat.js +36 -14
- package/utils/cli.d.ts +2 -9
- package/utils/cli.js +9 -52
- package/utils/datatruck/client.d.ts +2 -0
- package/utils/datatruck/client.js +3 -0
- package/utils/datatruck/config.d.ts +2 -0
- package/utils/datatruck/config.js +18 -3
- package/utils/datatruck/paths.d.ts +5 -9
- package/utils/datatruck/paths.js +2 -2
- package/utils/datatruck/snapshot.d.ts +2 -2
- package/utils/date.d.ts +7 -3
- package/utils/date.js +22 -14
- package/utils/fs.d.ts +16 -11
- package/utils/fs.js +81 -48
- package/utils/list.d.ts +64 -0
- package/utils/list.js +145 -0
- package/utils/mysql.d.ts +2 -0
- package/utils/mysql.js +21 -2
- package/utils/process.d.ts +1 -0
- package/utils/process.js +24 -31
- package/utils/progress.d.ts +33 -0
- package/utils/progress.js +113 -0
- package/utils/steps.d.ts +11 -0
- package/utils/steps.js +22 -10
- package/utils/stream.d.ts +7 -0
- package/utils/stream.js +10 -0
- package/utils/string.d.ts +0 -1
- package/utils/string.js +1 -13
- package/utils/tar.d.ts +10 -3
- package/utils/tar.js +70 -44
- package/utils/temp.d.ts +26 -0
- package/utils/temp.js +133 -0
- package/utils/virtual-fs.d.ts +6 -2
- package/utils/virtual-fs.js +6 -0
- package/Action/BackupSessionsAction.d.ts +0 -13
- package/Action/BackupSessionsAction.js +0 -18
- package/Action/RestoreSessionsAction.d.ts +0 -13
- package/Action/RestoreSessionsAction.js +0 -18
- package/Command/BackupSessionsCommand.d.ts +0 -12
- package/Command/BackupSessionsCommand.js +0 -92
- package/Command/RestoreSessionsCommand.d.ts +0 -12
- package/Command/RestoreSessionsCommand.js +0 -91
- package/Decorator/EntityDecorator.d.ts +0 -11
- package/Decorator/EntityDecorator.js +0 -17
- package/Entity/BackupSessionEntity.d.ts +0 -6
- package/Entity/BackupSessionEntity.js +0 -25
- package/Entity/BackupSessionRepositoryEntity.d.ts +0 -6
- package/Entity/BackupSessionRepositoryEntity.js +0 -25
- package/Entity/BackupSessionTaskEntity.d.ts +0 -5
- package/Entity/BackupSessionTaskEntity.js +0 -24
- package/Entity/CrudEntityAbstract.d.ts +0 -5
- package/Entity/CrudEntityAbstract.js +0 -9
- package/Entity/RestoreSessionEntity.d.ts +0 -5
- package/Entity/RestoreSessionEntity.js +0 -24
- package/Entity/RestoreSessionRepositoryEntity.d.ts +0 -6
- package/Entity/RestoreSessionRepositoryEntity.js +0 -25
- package/Entity/RestoreSessionTaskEntity.d.ts +0 -5
- package/Entity/RestoreSessionTaskEntity.js +0 -24
- package/Entity/StateEntityAbstract.d.ts +0 -9
- package/Entity/StateEntityAbstract.js +0 -12
- package/Factory/EntityFactory.d.ts +0 -6
- package/Factory/EntityFactory.js +0 -40
- package/SessionDriver/ConsoleSessionDriver.d.ts +0 -42
- package/SessionDriver/ConsoleSessionDriver.js +0 -208
- package/SessionDriver/SessionDriverAbstract.d.ts +0 -77
- package/SessionDriver/SessionDriverAbstract.js +0 -28
- package/SessionDriver/SqliteSessionDriver.d.ts +0 -20
- package/SessionDriver/SqliteSessionDriver.js +0 -173
- package/SessionManager/BackupSessionManager.d.ts +0 -45
- package/SessionManager/BackupSessionManager.js +0 -218
- package/SessionManager/RestoreSessionManager.d.ts +0 -47
- package/SessionManager/RestoreSessionManager.js +0 -218
- package/SessionManager/SessionManagerAbstract.d.ts +0 -18
- package/SessionManager/SessionManagerAbstract.js +0 -36
- package/migrations/001-initial.sql +0 -98
- package/utils/entity.d.ts +0 -4
- package/utils/entity.js +0 -10
package/Action/BackupAction.js
CHANGED
|
@@ -1,286 +1,326 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
2
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
6
|
exports.BackupAction = void 0;
|
|
4
|
-
const AppError_1 = require("../Error/AppError");
|
|
5
7
|
const RepositoryFactory_1 = require("../Factory/RepositoryFactory");
|
|
6
8
|
const TaskFactory_1 = require("../Factory/TaskFactory");
|
|
9
|
+
const DataFormat_1 = require("../utils/DataFormat");
|
|
10
|
+
const cli_1 = require("../utils/cli");
|
|
7
11
|
const config_1 = require("../utils/datatruck/config");
|
|
12
|
+
const date_1 = require("../utils/date");
|
|
8
13
|
const fs_1 = require("../utils/fs");
|
|
14
|
+
const list_1 = require("../utils/list");
|
|
15
|
+
const progress_1 = require("../utils/progress");
|
|
16
|
+
const steps_1 = require("../utils/steps");
|
|
17
|
+
const temp_1 = require("../utils/temp");
|
|
18
|
+
const assert_1 = require("assert");
|
|
19
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
9
20
|
const crypto_1 = require("crypto");
|
|
10
21
|
class BackupAction {
|
|
11
22
|
config;
|
|
12
23
|
options;
|
|
13
|
-
|
|
14
|
-
repoErrors = {};
|
|
24
|
+
pm;
|
|
15
25
|
constructor(config, options = {}) {
|
|
16
26
|
this.config = config;
|
|
17
27
|
this.options = options;
|
|
28
|
+
this.pm = new progress_1.ProgressManager({
|
|
29
|
+
verbose: options.verbose,
|
|
30
|
+
tty: options.tty,
|
|
31
|
+
enabled: options.progress,
|
|
32
|
+
interval: options.progressInterval,
|
|
33
|
+
});
|
|
18
34
|
}
|
|
19
|
-
|
|
20
|
-
|
|
35
|
+
prepareSnapshot() {
|
|
36
|
+
return {
|
|
21
37
|
id: (0, crypto_1.randomUUID)().replaceAll("-", ""),
|
|
22
38
|
date: this.options.date ?? new Date().toISOString(),
|
|
23
39
|
};
|
|
24
|
-
|
|
25
|
-
|
|
40
|
+
}
|
|
41
|
+
getPackages(snapshot) {
|
|
42
|
+
const packages = (0, config_1.filterPackages)(this.config, {
|
|
26
43
|
packageNames: this.options.packageNames,
|
|
27
44
|
packageTaskNames: this.options.packageTaskNames,
|
|
28
45
|
repositoryNames: this.options.repositoryNames,
|
|
29
46
|
repositoryTypes: this.options.repositoryTypes,
|
|
30
47
|
sourceAction: "backup",
|
|
31
48
|
});
|
|
32
|
-
|
|
49
|
+
return (0, config_1.resolvePackages)(packages, {
|
|
33
50
|
snapshotId: snapshot.id,
|
|
34
51
|
snapshotDate: snapshot.date,
|
|
35
52
|
action: "backup",
|
|
36
53
|
});
|
|
37
|
-
for (const pkg of packages) {
|
|
38
|
-
const sessionId = await session.init({
|
|
39
|
-
snapshotId: snapshot.id,
|
|
40
|
-
packageName: pkg.name,
|
|
41
|
-
tags: this.options.tags?.join(",") ?? "",
|
|
42
|
-
});
|
|
43
|
-
if (pkg.task)
|
|
44
|
-
await session.initTask({
|
|
45
|
-
sessionId: sessionId,
|
|
46
|
-
taskName: pkg.task.name,
|
|
47
|
-
});
|
|
48
|
-
for (const repositoryName of pkg.repositoryNames ?? []) {
|
|
49
|
-
const repo = (0, config_1.findRepositoryOrFail)(this.config, repositoryName);
|
|
50
|
-
await session.initRepository({
|
|
51
|
-
sessionId: sessionId,
|
|
52
|
-
repositoryName: repositoryName,
|
|
53
|
-
repositoryType: repo.type,
|
|
54
|
-
});
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
return [snapshot, packages];
|
|
58
54
|
}
|
|
59
|
-
|
|
60
|
-
const
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
let error;
|
|
69
|
-
if (this.taskErrors[key]?.length) {
|
|
70
|
-
error = AppError_1.AppError.create("Previous task failed", this.taskErrors[key]);
|
|
71
|
-
}
|
|
72
|
-
else {
|
|
73
|
-
try {
|
|
74
|
-
await task.onBackup({
|
|
75
|
-
package: pkg,
|
|
76
|
-
options: this.options,
|
|
77
|
-
snapshot,
|
|
78
|
-
targetPath,
|
|
79
|
-
onProgress: async (progress) => {
|
|
80
|
-
await session.progressTask({
|
|
81
|
-
id: taskId,
|
|
82
|
-
progress,
|
|
83
|
-
});
|
|
84
|
-
},
|
|
85
|
-
});
|
|
86
|
-
}
|
|
87
|
-
catch (_) {
|
|
88
|
-
if (!this.taskErrors[key])
|
|
89
|
-
this.taskErrors[key] = [];
|
|
90
|
-
this.taskErrors[key].push((error = _));
|
|
91
|
-
}
|
|
55
|
+
getRepositoryNames(repositoryNames) {
|
|
56
|
+
const items = [];
|
|
57
|
+
const exclude = new Set();
|
|
58
|
+
for (const name of repositoryNames) {
|
|
59
|
+
const repo = (0, config_1.findRepositoryOrFail)(this.config, name);
|
|
60
|
+
const mirrors = (repo.mirrorRepoNames || []).filter((mirror) => !exclude.has(mirror));
|
|
61
|
+
for (const mirror of mirrors)
|
|
62
|
+
exclude.add(mirror);
|
|
63
|
+
items.push({ name, mirrors });
|
|
92
64
|
}
|
|
93
|
-
|
|
94
|
-
id: taskId,
|
|
95
|
-
error: error?.stack,
|
|
96
|
-
});
|
|
97
|
-
return {
|
|
98
|
-
error: error ? false : true,
|
|
99
|
-
tmpDirs: task.tmpDirs,
|
|
100
|
-
};
|
|
65
|
+
return items.filter((item) => !exclude.has(item.name));
|
|
101
66
|
}
|
|
102
|
-
async backup(
|
|
103
|
-
const
|
|
104
|
-
|
|
105
|
-
|
|
67
|
+
async backup(data) {
|
|
68
|
+
const repoConfig = (0, config_1.findRepositoryOrFail)(this.config, data.repositoryName);
|
|
69
|
+
const pkg = { ...data.pkg, path: data.snapshotPath ?? data.pkg.path };
|
|
70
|
+
(0, assert_1.ok)(pkg.path);
|
|
71
|
+
await (0, fs_1.ensureExistsDir)(pkg.path);
|
|
72
|
+
const repo = (0, RepositoryFactory_1.createRepo)(repoConfig);
|
|
73
|
+
if (this.config.minFreeDiskSpace)
|
|
74
|
+
await repo.ensureFreeDiskSpace(repoConfig.config, this.config.minFreeDiskSpace);
|
|
75
|
+
const packageConfig = pkg.repositoryConfigs?.find((config) => config.type === repoConfig.type &&
|
|
76
|
+
(!config.names || config.names.includes(repoConfig.name)))?.config;
|
|
77
|
+
await repo.backup({
|
|
78
|
+
options: this.options,
|
|
79
|
+
snapshot: data.snapshot,
|
|
80
|
+
package: pkg,
|
|
81
|
+
packageConfig,
|
|
82
|
+
onProgress: data.onProgress,
|
|
106
83
|
});
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
packageConfig: pkg.repositoryConfigs?.find((config) => config.type === repo.type &&
|
|
122
|
-
(!config.names || config.names.includes(repo.name)))?.config,
|
|
123
|
-
options: this.options,
|
|
124
|
-
snapshot: snapshot,
|
|
125
|
-
onProgress: async (progress) => {
|
|
126
|
-
await session.progressRepository({
|
|
127
|
-
id: repositoryId,
|
|
128
|
-
progress,
|
|
129
|
-
});
|
|
130
|
-
},
|
|
131
|
-
});
|
|
132
|
-
}
|
|
133
|
-
catch (_) {
|
|
134
|
-
if (!this.repoErrors[pkg.name])
|
|
135
|
-
this.repoErrors[pkg.name] = [];
|
|
136
|
-
this.repoErrors[pkg.name].push((error = _));
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
await session.endRepository({
|
|
140
|
-
id: repositoryId,
|
|
141
|
-
error: error?.stack,
|
|
84
|
+
}
|
|
85
|
+
async copy(data) {
|
|
86
|
+
const repoConfig = (0, config_1.findRepositoryOrFail)(this.config, data.repositoryName);
|
|
87
|
+
const mirrorRepoConfig = (0, config_1.findRepositoryOrFail)(this.config, data.mirrorRepositoryName);
|
|
88
|
+
const repo = (0, RepositoryFactory_1.createRepo)(repoConfig);
|
|
89
|
+
const mirrorRepo = (0, RepositoryFactory_1.createRepo)(mirrorRepoConfig);
|
|
90
|
+
if (this.config.minFreeDiskSpace)
|
|
91
|
+
await mirrorRepo.ensureFreeDiskSpace(mirrorRepoConfig.config, this.config.minFreeDiskSpace);
|
|
92
|
+
await repo.copy({
|
|
93
|
+
options: this.options,
|
|
94
|
+
package: data.pkg,
|
|
95
|
+
snapshot: data.snapshot,
|
|
96
|
+
mirrorRepositoryConfig: mirrorRepoConfig.config,
|
|
97
|
+
onProgress: data.onProgress,
|
|
142
98
|
});
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
99
|
+
}
|
|
100
|
+
dataFormat(result, options = {}) {
|
|
101
|
+
const renderTitle = (item, color) => {
|
|
102
|
+
let title = item.key.slice(0, 1).toUpperCase() + item.key.slice(1);
|
|
103
|
+
return item.key === "backup" && color ? chalk_1.default.cyan(title) : title;
|
|
146
104
|
};
|
|
105
|
+
const renderData = (item, color) => {
|
|
106
|
+
const g = (v) => (color ? `${chalk_1.default.gray(`(${v})`)}` : `(${v})`);
|
|
107
|
+
return item.key === "snapshot"
|
|
108
|
+
? item.data.id
|
|
109
|
+
: item.key === "task"
|
|
110
|
+
? `${item.data.packageName} ${g(item.data.taskName)}`
|
|
111
|
+
: item.key === "backup"
|
|
112
|
+
? `${item.data.packageName} ${g(item.data.repositoryName)}`
|
|
113
|
+
: item.key === "copy"
|
|
114
|
+
? `${item.data.packageName} ${g(item.data.mirrorRepositoryName)}`
|
|
115
|
+
: item.key === "summary"
|
|
116
|
+
? `Errors: ${item.data.errors}`
|
|
117
|
+
: item.key === "report"
|
|
118
|
+
? item.data.type
|
|
119
|
+
: "";
|
|
120
|
+
};
|
|
121
|
+
return new DataFormat_1.DataFormat({
|
|
122
|
+
streams: options.streams,
|
|
123
|
+
json: result,
|
|
124
|
+
list: () => result
|
|
125
|
+
.filter((item) => item.key !== "cleanup")
|
|
126
|
+
.map((item) => {
|
|
127
|
+
const icon = (0, cli_1.resultColumn)(item.error, false);
|
|
128
|
+
const title = renderTitle(item);
|
|
129
|
+
const data = renderData(item, false);
|
|
130
|
+
return `${icon} ${title}: ${data}`;
|
|
131
|
+
}),
|
|
132
|
+
table: {
|
|
133
|
+
headers: [
|
|
134
|
+
{ value: "", width: 3 },
|
|
135
|
+
{ value: "Title", width: 15 },
|
|
136
|
+
{ value: "Data", align: "left" },
|
|
137
|
+
{ value: "Duration", width: 10 },
|
|
138
|
+
{ value: "Error", width: 50 },
|
|
139
|
+
],
|
|
140
|
+
rows: () => result
|
|
141
|
+
.filter((item) => item.key !== "cleanup")
|
|
142
|
+
.map((item) => [
|
|
143
|
+
(0, cli_1.resultColumn)(item.error),
|
|
144
|
+
renderTitle(item, true),
|
|
145
|
+
renderData(item, true),
|
|
146
|
+
(0, date_1.duration)(item.elapsed),
|
|
147
|
+
(0, cli_1.errorColumn)(item.error, options.verbose),
|
|
148
|
+
]),
|
|
149
|
+
},
|
|
150
|
+
});
|
|
147
151
|
}
|
|
148
|
-
async
|
|
149
|
-
const
|
|
150
|
-
|
|
151
|
-
|
|
152
|
+
async exec() {
|
|
153
|
+
const { options } = this;
|
|
154
|
+
const pm = new progress_1.ProgressManager({
|
|
155
|
+
verbose: options.verbose,
|
|
156
|
+
tty: options.tty,
|
|
157
|
+
enabled: options.progress,
|
|
158
|
+
interval: options.progressInterval,
|
|
152
159
|
});
|
|
153
|
-
|
|
154
|
-
|
|
160
|
+
const l = new list_1.Listr3({
|
|
161
|
+
streams: this.options.streams,
|
|
162
|
+
progressManager: pm,
|
|
155
163
|
});
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
164
|
+
return l
|
|
165
|
+
.add(l.$tasks({
|
|
166
|
+
key: "snapshot",
|
|
167
|
+
data: { id: "" },
|
|
168
|
+
exitOnError: false,
|
|
169
|
+
title: {
|
|
170
|
+
initial: "Prepare snapshots",
|
|
171
|
+
started: "Preparing snapshots",
|
|
172
|
+
failed: "Snapshots prepare failed",
|
|
173
|
+
},
|
|
174
|
+
run: async (task, data) => {
|
|
175
|
+
const { minFreeDiskSpace } = this.config;
|
|
176
|
+
if (minFreeDiskSpace)
|
|
177
|
+
await (0, temp_1.ensureFreeDiskTempSpace)(minFreeDiskSpace);
|
|
178
|
+
const snapshot = this.prepareSnapshot();
|
|
179
|
+
const packages = this.getPackages(snapshot);
|
|
180
|
+
const snapshotId = (data.id = snapshot.id.slice(0, 8));
|
|
181
|
+
task.title = `Snapshots prepared: ${snapshotId} (${packages.length} packages)`;
|
|
182
|
+
return packages.flatMap((pkg) => {
|
|
183
|
+
let taskResult = {};
|
|
184
|
+
const gc = new temp_1.GargabeCollector();
|
|
185
|
+
const repositories = this.getRepositoryNames(pkg.repositoryNames ?? []);
|
|
186
|
+
const mirrorRepositories = repositories
|
|
187
|
+
.filter((r) => r.mirrors.length)
|
|
188
|
+
.flatMap(({ name, mirrors }) => mirrors.map((mirror) => ({ name, mirror })));
|
|
189
|
+
return l.$tasks(!!pkg.task &&
|
|
190
|
+
l.$task({
|
|
191
|
+
key: "task",
|
|
192
|
+
keyIndex: pkg.name,
|
|
193
|
+
data: { taskName: pkg.task.name, packageName: pkg.name },
|
|
194
|
+
title: {
|
|
195
|
+
initial: `Execute task: ${pkg.task?.name}`,
|
|
196
|
+
started: `Executing task: ${pkg.task?.name}`,
|
|
197
|
+
failed: `Task execute failed: ${pkg.task?.name}`,
|
|
198
|
+
completed: `Task executed: ${pkg.task?.name}`,
|
|
199
|
+
},
|
|
200
|
+
exitOnError: false,
|
|
201
|
+
runWrapper: gc.cleanupIfFail.bind(gc),
|
|
202
|
+
run: async (task) => {
|
|
203
|
+
taskResult = await (0, TaskFactory_1.createTask)(pkg.task).backup({
|
|
204
|
+
options,
|
|
205
|
+
package: pkg,
|
|
206
|
+
snapshot,
|
|
207
|
+
onProgress: (p) => pm.update(p, (t) => (task.output = t)),
|
|
208
|
+
});
|
|
209
|
+
},
|
|
210
|
+
}), ...repositories.map(({ name: repositoryName }) => l.$task({
|
|
211
|
+
key: "backup",
|
|
212
|
+
keyIndex: [pkg.name, repositoryName],
|
|
213
|
+
data: {
|
|
214
|
+
packageName: pkg.name,
|
|
215
|
+
repositoryName: repositoryName,
|
|
216
|
+
},
|
|
217
|
+
title: {
|
|
218
|
+
initial: `Create backup: ${repositoryName}`,
|
|
219
|
+
started: `Creating backup: ${repositoryName}`,
|
|
220
|
+
completed: `Backup created: ${repositoryName}`,
|
|
221
|
+
failed: `Backup create failed: ${repositoryName}`,
|
|
222
|
+
},
|
|
223
|
+
exitOnError: false,
|
|
224
|
+
runWrapper: gc.cleanupOnFinish.bind(gc),
|
|
225
|
+
run: async (task) => {
|
|
226
|
+
const taskSummary = pkg.task
|
|
227
|
+
? l.result("task", pkg.name)
|
|
228
|
+
: undefined;
|
|
229
|
+
if (taskSummary?.error)
|
|
230
|
+
throw new Error(`Task failed`);
|
|
231
|
+
await this.backup({
|
|
232
|
+
pkg,
|
|
233
|
+
repositoryName,
|
|
234
|
+
snapshot,
|
|
235
|
+
snapshotPath: taskResult?.snapshotPath,
|
|
236
|
+
onProgress: (p) => this.pm.update(p, (t) => (task.output = t)),
|
|
237
|
+
});
|
|
238
|
+
},
|
|
239
|
+
})), l.$task({
|
|
240
|
+
key: "cleanup",
|
|
241
|
+
keyIndex: pkg.name,
|
|
242
|
+
data: {},
|
|
243
|
+
title: {
|
|
244
|
+
initial: "Clean task files",
|
|
245
|
+
started: "Cleaning task files",
|
|
246
|
+
completed: "Task files cleaned",
|
|
247
|
+
failed: "Task files clean failed",
|
|
248
|
+
},
|
|
249
|
+
exitOnError: false,
|
|
250
|
+
enabled: gc.pending,
|
|
251
|
+
run: () => gc.cleanup(),
|
|
252
|
+
}), ...mirrorRepositories.map(({ name, mirror }) => l.$task({
|
|
253
|
+
key: "copy",
|
|
254
|
+
keyIndex: [pkg.name, mirror],
|
|
255
|
+
data: {
|
|
256
|
+
packageName: pkg.name,
|
|
257
|
+
repositoryName: name,
|
|
258
|
+
mirrorRepositoryName: mirror,
|
|
259
|
+
},
|
|
260
|
+
title: {
|
|
261
|
+
initial: `Copy snapshot: ${mirror}`,
|
|
262
|
+
started: `Copying snapshot: ${mirror}`,
|
|
263
|
+
completed: `Snapshot copied: ${mirror}`,
|
|
264
|
+
failed: `Snapshot copy failed: ${mirror}`,
|
|
265
|
+
},
|
|
266
|
+
exitOnError: false,
|
|
267
|
+
runWrapper: gc.cleanup.bind(gc),
|
|
268
|
+
run: async (task) => {
|
|
269
|
+
const backupSummary = l.result("backup", [
|
|
270
|
+
pkg.name,
|
|
271
|
+
name,
|
|
272
|
+
]);
|
|
273
|
+
if (backupSummary.error)
|
|
274
|
+
throw new Error(`Backup failed`);
|
|
275
|
+
await this.copy({
|
|
276
|
+
repositoryName: name,
|
|
277
|
+
mirrorRepositoryName: mirror,
|
|
278
|
+
pkg,
|
|
279
|
+
snapshot,
|
|
280
|
+
onProgress: (p) => pm.update(p, (t) => (task.output = t)),
|
|
281
|
+
});
|
|
282
|
+
},
|
|
283
|
+
})), ...(this.config.reports || []).map((report, index) => {
|
|
284
|
+
const reportIndex = index + 1;
|
|
285
|
+
return l.$task({
|
|
286
|
+
title: {
|
|
287
|
+
initial: `Send report ${reportIndex}`,
|
|
288
|
+
started: `Sending report ${reportIndex}`,
|
|
289
|
+
completed: `Report sent: ${reportIndex}`,
|
|
290
|
+
failed: `Report send failed: ${reportIndex}`,
|
|
291
|
+
},
|
|
292
|
+
key: "report",
|
|
293
|
+
keyIndex: index,
|
|
294
|
+
data: { type: report.run.type },
|
|
295
|
+
exitOnError: false,
|
|
296
|
+
run: async (task) => {
|
|
297
|
+
const result = l
|
|
298
|
+
.getResult()
|
|
299
|
+
.filter((r) => r.key !== "report");
|
|
300
|
+
const success = result.every((r) => !r.error);
|
|
301
|
+
const enabled = !report.when ||
|
|
302
|
+
(report.when === "success" && success) ||
|
|
303
|
+
(report.when === "error" && !success);
|
|
304
|
+
if (!enabled)
|
|
305
|
+
return task.skip(`Report send skipped: ${reportIndex}`);
|
|
306
|
+
const text = this.dataFormat(result).format(report.format ?? "list");
|
|
307
|
+
await (0, steps_1.runSteps)(report.run, {
|
|
308
|
+
verbose: this.options.verbose,
|
|
309
|
+
process: { vars: { DTT_REPORT: text } },
|
|
310
|
+
telegram: { vars: { TEXT: text } },
|
|
311
|
+
node: {
|
|
312
|
+
vars: {
|
|
313
|
+
dtt: { report: text, result },
|
|
314
|
+
},
|
|
315
|
+
},
|
|
316
|
+
});
|
|
317
|
+
},
|
|
173
318
|
});
|
|
174
|
-
}
|
|
175
|
-
});
|
|
176
|
-
}
|
|
177
|
-
catch (_) {
|
|
178
|
-
if (!this.repoErrors[pkg.name])
|
|
179
|
-
this.repoErrors[pkg.name] = [];
|
|
180
|
-
this.repoErrors[pkg.name].push((error = _));
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
await session.endRepository({
|
|
184
|
-
id: repositoryId,
|
|
185
|
-
error: error?.stack,
|
|
186
|
-
});
|
|
187
|
-
return {
|
|
188
|
-
error: error ? false : true,
|
|
189
|
-
tmpDirs: repoInstance?.tmpDirs ?? [],
|
|
190
|
-
};
|
|
191
|
-
}
|
|
192
|
-
getError(pkg) {
|
|
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);
|
|
203
|
-
}
|
|
204
|
-
splitRepositories(repositoryNames) {
|
|
205
|
-
const mirrorRepoMap = {};
|
|
206
|
-
const allMirrorRepoNames = [];
|
|
207
|
-
const repoNames = repositoryNames ?? [];
|
|
208
|
-
for (const repoName of repoNames) {
|
|
209
|
-
const repo = (0, config_1.findRepositoryOrFail)(this.config, repoName);
|
|
210
|
-
if (repo.mirrorRepoNames)
|
|
211
|
-
mirrorRepoMap[repoName] = repo.mirrorRepoNames.filter((mirrorRepoName) => {
|
|
212
|
-
allMirrorRepoNames.push(mirrorRepoName);
|
|
213
|
-
return repoNames.includes(mirrorRepoName);
|
|
319
|
+
}));
|
|
214
320
|
});
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
mirrors: repoNames.flatMap((sourceName) => {
|
|
219
|
-
const mirrorNames = mirrorRepoMap[sourceName] || [];
|
|
220
|
-
return mirrorNames.map((name) => ({
|
|
221
|
-
sourceName,
|
|
222
|
-
name,
|
|
223
|
-
}));
|
|
224
|
-
}),
|
|
225
|
-
};
|
|
226
|
-
}
|
|
227
|
-
async exec(session) {
|
|
228
|
-
const [snapshot, packages] = await this.init(session);
|
|
229
|
-
const errors = [];
|
|
230
|
-
for (const pkg of packages) {
|
|
231
|
-
const id = session.findId({
|
|
232
|
-
packageName: pkg.name,
|
|
233
|
-
});
|
|
234
|
-
await session.start({
|
|
235
|
-
id,
|
|
236
|
-
});
|
|
237
|
-
let targetPath;
|
|
238
|
-
let taskTmpDirs = [];
|
|
239
|
-
if (pkg.task) {
|
|
240
|
-
const taskInstance = (0, TaskFactory_1.TaskFactory)(pkg.task);
|
|
241
|
-
const result = await taskInstance.onBeforeBackup({
|
|
242
|
-
options: this.options,
|
|
243
|
-
package: pkg,
|
|
244
|
-
snapshot,
|
|
245
|
-
});
|
|
246
|
-
const taskResult = await this.task(session, pkg, taskInstance, snapshot, (targetPath = result?.targetPath));
|
|
247
|
-
taskTmpDirs.push(...taskResult.tmpDirs);
|
|
248
|
-
}
|
|
249
|
-
const { repoNames, mirrors } = this.splitRepositories(pkg.repositoryNames ?? []);
|
|
250
|
-
for (const repoName of repoNames) {
|
|
251
|
-
const repo = (0, config_1.findRepositoryOrFail)(this.config, repoName);
|
|
252
|
-
const { tmpDirs } = await this.backup(session, pkg, repo, snapshot, targetPath);
|
|
253
|
-
if (!this.options.verbose)
|
|
254
|
-
await (0, fs_1.rmTmpDir)(tmpDirs);
|
|
255
|
-
}
|
|
256
|
-
if (!this.options.verbose) {
|
|
257
|
-
await (0, fs_1.rmTmpDir)(taskTmpDirs);
|
|
258
|
-
if (pkg.path && (0, fs_1.isTmpDir)(pkg.path)) {
|
|
259
|
-
await (0, fs_1.rmTmpDir)(pkg.path);
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
for (const mirror of mirrors) {
|
|
263
|
-
const repo = (0, config_1.findRepositoryOrFail)(this.config, mirror.sourceName);
|
|
264
|
-
const mirrorRepo = (0, config_1.findRepositoryOrFail)(this.config, mirror.name);
|
|
265
|
-
const { tmpDirs } = await this.copyBackup(session, pkg, repo, mirrorRepo, snapshot);
|
|
266
|
-
if (!this.options.verbose)
|
|
267
|
-
await (0, fs_1.rmTmpDir)(tmpDirs);
|
|
268
|
-
}
|
|
269
|
-
const error = this.getError(pkg);
|
|
270
|
-
if (error)
|
|
271
|
-
errors.push(error);
|
|
272
|
-
await session.end({
|
|
273
|
-
id: id,
|
|
274
|
-
error: error?.message,
|
|
275
|
-
});
|
|
276
|
-
}
|
|
277
|
-
await session.endDrivers({
|
|
278
|
-
snapshotId: snapshot.id.slice(0, 8),
|
|
279
|
-
});
|
|
280
|
-
return {
|
|
281
|
-
total: packages.length,
|
|
282
|
-
errors,
|
|
283
|
-
};
|
|
321
|
+
},
|
|
322
|
+
}))
|
|
323
|
+
.exec();
|
|
284
324
|
}
|
|
285
325
|
}
|
|
286
326
|
exports.BackupAction = BackupAction;
|
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
import { IfRequireKeys } from "../utils/ts";
|
|
2
|
-
export type
|
|
2
|
+
export type CleanCacheActionOptions = {
|
|
3
3
|
verbose?: boolean;
|
|
4
4
|
};
|
|
5
5
|
export declare class CleanCacheAction<TRequired extends boolean = true> {
|
|
6
|
-
readonly options: IfRequireKeys<TRequired,
|
|
7
|
-
constructor(options: IfRequireKeys<TRequired,
|
|
8
|
-
exec(): Promise<
|
|
6
|
+
readonly options: IfRequireKeys<TRequired, CleanCacheActionOptions>;
|
|
7
|
+
constructor(options: IfRequireKeys<TRequired, CleanCacheActionOptions>);
|
|
8
|
+
exec(): Promise<{
|
|
9
|
+
errors: never[];
|
|
10
|
+
path: string;
|
|
11
|
+
freedSize: number;
|
|
12
|
+
}>;
|
|
9
13
|
}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.CleanCacheAction = void 0;
|
|
4
4
|
const fs_1 = require("../utils/fs");
|
|
5
|
+
const temp_1 = require("../utils/temp");
|
|
5
6
|
const promises_1 = require("fs/promises");
|
|
6
7
|
class CleanCacheAction {
|
|
7
8
|
options;
|
|
@@ -9,11 +10,13 @@ class CleanCacheAction {
|
|
|
9
10
|
this.options = options;
|
|
10
11
|
}
|
|
11
12
|
async exec() {
|
|
12
|
-
const path = (0,
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
});
|
|
13
|
+
const path = (0, temp_1.parentTmpDir)();
|
|
14
|
+
let freedSize = 0;
|
|
15
|
+
if (await (0, fs_1.existsDir)(path)) {
|
|
16
|
+
freedSize = await (0, fs_1.fastFolderSizeAsync)(path);
|
|
17
|
+
await (0, promises_1.rm)(path, { recursive: true });
|
|
18
|
+
}
|
|
19
|
+
return { errors: [], path, freedSize };
|
|
17
20
|
}
|
|
18
21
|
}
|
|
19
22
|
exports.CleanCacheAction = CleanCacheAction;
|
package/Action/ConfigAction.d.ts
CHANGED
|
@@ -1,17 +1,24 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { GlobalOptions } from "../Command/CommandAbstract";
|
|
2
2
|
import type { ConfigType } from "../Config/Config";
|
|
3
3
|
import { IfRequireKeys } from "../utils/ts";
|
|
4
|
-
export type
|
|
4
|
+
export type ConfigActionOptions = {
|
|
5
5
|
path: string;
|
|
6
6
|
verbose?: boolean;
|
|
7
7
|
};
|
|
8
8
|
export declare class ConfigAction<TRequired extends boolean = true> {
|
|
9
|
-
readonly options: IfRequireKeys<TRequired,
|
|
10
|
-
constructor(options: IfRequireKeys<TRequired,
|
|
9
|
+
readonly options: IfRequireKeys<TRequired, ConfigActionOptions>;
|
|
10
|
+
constructor(options: IfRequireKeys<TRequired, ConfigActionOptions>);
|
|
11
11
|
static validate(config: ConfigType): void;
|
|
12
12
|
static check(config: ConfigType): void;
|
|
13
13
|
static normalize(config: ConfigType): ConfigType;
|
|
14
|
-
static
|
|
14
|
+
static fromGlobalOptionsWithPath(globalOptions: GlobalOptions<true>): Promise<{
|
|
15
|
+
path: string;
|
|
16
|
+
data: ConfigType;
|
|
17
|
+
} | {
|
|
18
|
+
path: null;
|
|
19
|
+
data: ConfigType;
|
|
20
|
+
}>;
|
|
21
|
+
static fromGlobalOptions(globalOptions: GlobalOptions<true>): Promise<ConfigType>;
|
|
15
22
|
exec(): Promise<{
|
|
16
23
|
path: string;
|
|
17
24
|
data: ConfigType;
|