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