@datatruck/cli 0.5.0 → 0.6.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/CHANGELOG.md +14 -0
- package/Repository/GitRepository.js +2 -3
- package/Repository/LocalRepository.d.ts +4 -0
- package/Repository/LocalRepository.js +38 -37
- package/Repository/RepositoryAbstract.d.ts +4 -4
- package/Repository/ResticRepository.js +16 -9
- package/SessionDriver/ConsoleSessionDriver.js +12 -11
- package/Task/GitTask.d.ts +4 -0
- package/Task/GitTask.js +28 -29
- package/Task/MariadbTask.js +26 -14
- package/Task/MssqlTask.js +1 -2
- package/Task/TaskAbstract.d.ts +4 -4
- package/config.schema.json +8 -0
- package/package.json +2 -2
- package/util/GitUtil.js +2 -2
- package/util/ResticUtil.js +1 -2
- package/util/fs-util.d.ts +33 -0
- package/util/fs-util.js +73 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# @datatruck/cli
|
|
2
2
|
|
|
3
|
+
## 0.6.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [`0c6877d`](https://github.com/swordev/datatruck/commit/0c6877d189761e75dd434b0a8d72b71621d024de) Thanks [@juanrgm](https://github.com/juanrgm)! - Show more progress stats
|
|
8
|
+
|
|
9
|
+
* [`751e1f6`](https://github.com/swordev/datatruck/commit/751e1f6d6b33d3fa96eb40d998fdd140ce0e3875) Thanks [@juanrgm](https://github.com/juanrgm)! - Add `fileCopyConcurrency` option
|
|
10
|
+
|
|
11
|
+
- [`05487e6`](https://github.com/swordev/datatruck/commit/05487e6a33f875a3afb7ff0815b16da6f2a41301) Thanks [@juanrgm](https://github.com/juanrgm)! - Parse InnoDB error in `MariadbTask` to avoid infinite wait
|
|
12
|
+
|
|
13
|
+
### Patch Changes
|
|
14
|
+
|
|
15
|
+
- [`b62a6f8`](https://github.com/swordev/datatruck/commit/b62a6f8a82409339afd65d4f96476eb57bbfb5a2) Thanks [@juanrgm](https://github.com/juanrgm)! - Resolve target/restore path in local repository
|
|
16
|
+
|
|
3
17
|
## 0.5.0
|
|
4
18
|
|
|
5
19
|
### Minor Changes
|
|
@@ -13,7 +13,6 @@ const string_util_1 = require("../util/string-util");
|
|
|
13
13
|
const RepositoryAbstract_1 = require("./RepositoryAbstract");
|
|
14
14
|
const assert_1 = require("assert");
|
|
15
15
|
const fast_glob_1 = __importDefault(require("fast-glob"));
|
|
16
|
-
const fs_extra_1 = require("fs-extra");
|
|
17
16
|
const promises_1 = require("fs/promises");
|
|
18
17
|
const micromatch_1 = require("micromatch");
|
|
19
18
|
const path_1 = require("path");
|
|
@@ -62,7 +61,7 @@ class GitRepository extends RepositoryAbstract_1.RepositoryAbstract {
|
|
|
62
61
|
log: data.options.verbose,
|
|
63
62
|
});
|
|
64
63
|
if (await git.canBeInit(this.config.repo)) {
|
|
65
|
-
await (0,
|
|
64
|
+
await (0, promises_1.mkdir)(git.options.dir);
|
|
66
65
|
await git.exec(["init", "--bare", this.config.repo]);
|
|
67
66
|
}
|
|
68
67
|
const branchName = this.config.branch ?? "master";
|
|
@@ -182,7 +181,7 @@ class GitRepository extends RepositoryAbstract_1.RepositoryAbstract {
|
|
|
182
181
|
const target = (0, path_1.join)(tmpPath, entry);
|
|
183
182
|
const dir = (0, path_1.dirname)(target);
|
|
184
183
|
if (!createdPaths.includes(dir)) {
|
|
185
|
-
await (0,
|
|
184
|
+
await (0, promises_1.mkdir)(dir, {
|
|
186
185
|
recursive: true,
|
|
187
186
|
});
|
|
188
187
|
createdPaths.push(dir);
|
|
@@ -16,11 +16,9 @@ const RepositoryAbstract_1 = require("./RepositoryAbstract");
|
|
|
16
16
|
const assert_1 = require("assert");
|
|
17
17
|
const fast_glob_1 = __importDefault(require("fast-glob"));
|
|
18
18
|
const fs_1 = require("fs");
|
|
19
|
-
const fs_extra_1 = require("fs-extra");
|
|
20
19
|
const promises_1 = require("fs/promises");
|
|
21
20
|
const micromatch_1 = require("micromatch");
|
|
22
21
|
const path_1 = require("path");
|
|
23
|
-
const path_2 = require("path");
|
|
24
22
|
const readline_1 = require("readline");
|
|
25
23
|
exports.localRepositoryName = "local";
|
|
26
24
|
exports.localRepositoryDefinition = {
|
|
@@ -62,6 +60,10 @@ exports.localPackageRepositoryDefinition = {
|
|
|
62
60
|
},
|
|
63
61
|
],
|
|
64
62
|
},
|
|
63
|
+
fileCopyConcurrency: {
|
|
64
|
+
type: "integer",
|
|
65
|
+
minimum: 1,
|
|
66
|
+
},
|
|
65
67
|
},
|
|
66
68
|
};
|
|
67
69
|
class LocalRepository extends RepositoryAbstract_1.RepositoryAbstract {
|
|
@@ -111,11 +113,11 @@ class LocalRepository extends RepositoryAbstract_1.RepositoryAbstract {
|
|
|
111
113
|
if (data.options.verbose)
|
|
112
114
|
(0, cli_util_1.logExec)(`Deleting ${snapshotPath}`);
|
|
113
115
|
if (await (0, fs_util_1.checkDir)(snapshotPath))
|
|
114
|
-
await (0,
|
|
116
|
+
await (0, promises_1.rm)(snapshotPath, {
|
|
115
117
|
recursive: true,
|
|
116
118
|
});
|
|
117
119
|
if (await (0, fs_util_1.checkFile)(metaPath))
|
|
118
|
-
await (0,
|
|
120
|
+
await (0, promises_1.rm)(metaPath);
|
|
119
121
|
}
|
|
120
122
|
async onSnapshots(data) {
|
|
121
123
|
if (!(await (0, fs_util_1.checkDir)(this.config.outPath)))
|
|
@@ -173,8 +175,7 @@ class LocalRepository extends RepositoryAbstract_1.RepositoryAbstract {
|
|
|
173
175
|
snapshotDate: data.snapshot.date,
|
|
174
176
|
packageName: data.package.name,
|
|
175
177
|
});
|
|
176
|
-
const outPath = (0, path_1.join)(this.config.outPath, snapshotName);
|
|
177
|
-
const createdPaths = [];
|
|
178
|
+
const outPath = (0, path_1.resolve)((0, path_1.join)(this.config.outPath, snapshotName));
|
|
178
179
|
const pkg = data.package;
|
|
179
180
|
await (0, promises_1.mkdir)(outPath, {
|
|
180
181
|
recursive: true,
|
|
@@ -260,35 +261,26 @@ class LocalRepository extends RepositoryAbstract_1.RepositoryAbstract {
|
|
|
260
261
|
}
|
|
261
262
|
if (data.options.verbose)
|
|
262
263
|
(0, cli_util_1.logExec)(`Copying files to ${outPath}`);
|
|
263
|
-
|
|
264
|
-
input:
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
else {
|
|
264
|
+
await (0, fs_util_1.cpy)({
|
|
265
|
+
input: {
|
|
266
|
+
type: "pathList",
|
|
267
|
+
path: pathLists.path,
|
|
268
|
+
basePath: sourcePath,
|
|
269
|
+
},
|
|
270
|
+
targetPath: outPath,
|
|
271
|
+
concurrency: this.config.fileCopyConcurrency,
|
|
272
|
+
async onPath({ isDir, entryPath }) {
|
|
273
|
+
if (isDir)
|
|
274
|
+
return;
|
|
275
275
|
currentFiles++;
|
|
276
276
|
await data.onProgress({
|
|
277
277
|
total: pathLists.total.all,
|
|
278
278
|
current: currentFiles,
|
|
279
279
|
percent: (0, math_util_1.progressPercent)(pathLists.total.all, currentFiles),
|
|
280
|
-
step:
|
|
280
|
+
step: entryPath,
|
|
281
281
|
});
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
await (0, promises_1.mkdir)(dir, {
|
|
285
|
-
recursive: true,
|
|
286
|
-
});
|
|
287
|
-
createdPaths.push(dir);
|
|
288
|
-
}
|
|
289
|
-
await (0, promises_1.copyFile)(source, target);
|
|
290
|
-
}
|
|
291
|
-
}
|
|
282
|
+
},
|
|
283
|
+
});
|
|
292
284
|
const metaPath = `${outPath}.json`;
|
|
293
285
|
const nodePkg = (0, fs_util_1.parsePackageFile)();
|
|
294
286
|
const meta = {
|
|
@@ -303,8 +295,9 @@ class LocalRepository extends RepositoryAbstract_1.RepositoryAbstract {
|
|
|
303
295
|
await (0, promises_1.writeFile)(metaPath, LocalRepository.stringifyMetaData(meta));
|
|
304
296
|
}
|
|
305
297
|
async onRestore(data) {
|
|
306
|
-
const
|
|
307
|
-
(0, assert_1.ok)(
|
|
298
|
+
const relRestorePath = data.targetPath ?? data.package.restorePath;
|
|
299
|
+
(0, assert_1.ok)(relRestorePath);
|
|
300
|
+
const restorePath = (0, path_1.resolve)(relRestorePath);
|
|
308
301
|
const [snapshot] = await this.onSnapshots({
|
|
309
302
|
options: {
|
|
310
303
|
ids: [data.options.snapshotId],
|
|
@@ -325,19 +318,27 @@ class LocalRepository extends RepositoryAbstract_1.RepositoryAbstract {
|
|
|
325
318
|
}, true);
|
|
326
319
|
if (data.options.verbose)
|
|
327
320
|
(0, cli_util_1.logExec)(`Copying files to ${restorePath}`);
|
|
328
|
-
await (0,
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
321
|
+
await (0, fs_util_1.cpy)({
|
|
322
|
+
input: {
|
|
323
|
+
type: "glob",
|
|
324
|
+
sourcePath,
|
|
325
|
+
},
|
|
326
|
+
targetPath: restorePath,
|
|
327
|
+
concurrency: this.config.fileCopyConcurrency,
|
|
328
|
+
onPath: async ({ entryPath, entrySourcePath }) => {
|
|
329
|
+
const isRootFile = (0, path_1.basename)(entryPath) === entryPath;
|
|
330
|
+
const isZipFile = isRootFile &&
|
|
331
|
+
entryPath.startsWith(".") &&
|
|
332
|
+
entryPath.endsWith(".dd.zip");
|
|
332
333
|
await data.onProgress({
|
|
333
334
|
total: totalFiles,
|
|
334
335
|
current: Math.max(currentFiles, 0),
|
|
335
336
|
percent: (0, math_util_1.progressPercent)(totalFiles, Math.max(currentFiles, 0)),
|
|
336
|
-
step:
|
|
337
|
+
step: entryPath,
|
|
337
338
|
});
|
|
338
339
|
if (isZipFile) {
|
|
339
340
|
await (0, zip_util_1.unzip)({
|
|
340
|
-
input:
|
|
341
|
+
input: entrySourcePath,
|
|
341
342
|
output: restorePath,
|
|
342
343
|
verbose: data.options.verbose,
|
|
343
344
|
onStream: async (stream) => await data.onProgress({
|
|
@@ -14,10 +14,10 @@ export declare type SnapshotResultType = SnapshotType & {
|
|
|
14
14
|
tags: string[];
|
|
15
15
|
};
|
|
16
16
|
export declare type ProgressDataType = {
|
|
17
|
-
total
|
|
18
|
-
current
|
|
19
|
-
percent
|
|
20
|
-
step
|
|
17
|
+
total?: number;
|
|
18
|
+
current?: number;
|
|
19
|
+
percent?: number;
|
|
20
|
+
step?: string;
|
|
21
21
|
stepPercent?: number | null;
|
|
22
22
|
};
|
|
23
23
|
export declare type InitDataType = {
|
|
@@ -190,6 +190,9 @@ class ResticRepository extends RepositoryAbstract_1.RepositoryAbstract {
|
|
|
190
190
|
});
|
|
191
191
|
if (data.options.verbose)
|
|
192
192
|
(0, cli_util_1.logExec)(`Writing paths lists`);
|
|
193
|
+
await data.onProgress({
|
|
194
|
+
step: "Writing excluded paths list...",
|
|
195
|
+
});
|
|
193
196
|
gitignorePath = await (0, fs_util_1.writeGitIgnoreList)({
|
|
194
197
|
paths: stream,
|
|
195
198
|
});
|
|
@@ -197,12 +200,19 @@ class ResticRepository extends RepositoryAbstract_1.RepositoryAbstract {
|
|
|
197
200
|
if (data.options.tags?.some((tag) => tag.startsWith(ResticRepository.refPrefix)))
|
|
198
201
|
throw new AppError_1.AppError(`Tag prefix is not allowed`);
|
|
199
202
|
const packageTag = ResticRepository.buildSnapshotTag(RepositoryAbstract_1.SnapshotTagEnum.PACKAGE, data.package.name);
|
|
203
|
+
await data.onProgress({
|
|
204
|
+
step: "Fetching last snapshot...",
|
|
205
|
+
});
|
|
200
206
|
const [lastSnapshot] = await restic.snapshots({
|
|
201
207
|
json: true,
|
|
202
208
|
tags: [packageTag],
|
|
203
209
|
latest: 1,
|
|
204
210
|
});
|
|
205
211
|
const nodePkg = (0, fs_util_1.parsePackageFile)();
|
|
212
|
+
let lastProgress;
|
|
213
|
+
await data.onProgress({
|
|
214
|
+
step: "Executing backup action...",
|
|
215
|
+
});
|
|
206
216
|
await restic.backup({
|
|
207
217
|
cwd: sourcePath,
|
|
208
218
|
paths: ["."],
|
|
@@ -223,22 +233,19 @@ class ResticRepository extends RepositoryAbstract_1.RepositoryAbstract {
|
|
|
223
233
|
],
|
|
224
234
|
onStream: async (streamData) => {
|
|
225
235
|
if (streamData.message_type === "status") {
|
|
226
|
-
await data.onProgress({
|
|
227
|
-
total:
|
|
228
|
-
current: streamData.
|
|
229
|
-
? Number((streamData.bytes_done / 1024 / 1024).toFixed(2))
|
|
230
|
-
: 0,
|
|
236
|
+
await data.onProgress((lastProgress = {
|
|
237
|
+
total: streamData.total_files,
|
|
238
|
+
current: streamData.files_done ?? 0,
|
|
231
239
|
percent: Number((streamData.percent_done * 100).toFixed(2)),
|
|
232
240
|
step: streamData.current_files?.join(", ") ?? "-",
|
|
233
|
-
});
|
|
241
|
+
}));
|
|
234
242
|
}
|
|
235
243
|
},
|
|
236
244
|
});
|
|
237
245
|
await data.onProgress({
|
|
238
|
-
|
|
246
|
+
total: lastProgress?.total || 0,
|
|
247
|
+
current: lastProgress?.total || 0,
|
|
239
248
|
percent: 100,
|
|
240
|
-
step: "",
|
|
241
|
-
total: 100,
|
|
242
249
|
});
|
|
243
250
|
}
|
|
244
251
|
async onRestore(data) {
|
|
@@ -82,18 +82,19 @@ class ConsoleSessionDriver extends SessionDriverAbstract_1.SessionDriverAbstract
|
|
|
82
82
|
const parts = [
|
|
83
83
|
`${padding}${message.textPrefix} [${(0, chalk_1.grey)(sessionId)}] ${message.text}`,
|
|
84
84
|
badges,
|
|
85
|
-
...(haveProgressBar
|
|
86
|
-
? [
|
|
87
|
-
(0, chalk_1.cyan)((0, cli_util_1.renderProgressBar)(message.progressPercent ?? 0, 10)),
|
|
88
|
-
`${message.progressPercent?.toFixed(2)}%`,
|
|
89
|
-
`${message.progressCurrent}/${message.progressTotal}`,
|
|
90
|
-
message.progressStep,
|
|
91
|
-
`${message.progressStepPercent
|
|
92
|
-
? (0, chalk_1.cyan)((0, cli_util_1.renderProgressBar)(message.progressStepPercent ?? 0, 10))
|
|
93
|
-
: ""}`,
|
|
94
|
-
].filter((v) => !!v?.length)
|
|
95
|
-
: []),
|
|
96
85
|
];
|
|
86
|
+
if (typeof message.progressPercent === "number") {
|
|
87
|
+
parts.push((0, chalk_1.cyan)((0, cli_util_1.renderProgressBar)(message.progressPercent ?? 0, 10)), `${message.progressPercent?.toFixed(2)}%`);
|
|
88
|
+
}
|
|
89
|
+
if (typeof message.progressCurrent === "number" ||
|
|
90
|
+
typeof message.progressTotal === "number") {
|
|
91
|
+
parts.push(`${message.progressCurrent ?? "?"}/${message.progressTotal ?? "?"}`);
|
|
92
|
+
}
|
|
93
|
+
if (typeof message.progressStep === "string")
|
|
94
|
+
parts.push(message.progressStep);
|
|
95
|
+
if (typeof message.progressStepPercent === "number") {
|
|
96
|
+
parts.push((0, chalk_1.cyan)((0, cli_util_1.renderProgressBar)(message.progressStepPercent ?? 0, 10)));
|
|
97
|
+
}
|
|
97
98
|
return parts.join(` ${sep} `);
|
|
98
99
|
}
|
|
99
100
|
async onWrite(data) {
|
package/Task/GitTask.d.ts
CHANGED
|
@@ -18,6 +18,10 @@ export declare type GitTaskConfigType = {
|
|
|
18
18
|
* @default true
|
|
19
19
|
*/
|
|
20
20
|
includeConfig?: boolean;
|
|
21
|
+
/**
|
|
22
|
+
* @default 1
|
|
23
|
+
*/
|
|
24
|
+
fileCopyConcurrency?: number;
|
|
21
25
|
};
|
|
22
26
|
export declare const gitTaskName = "git";
|
|
23
27
|
export declare const gitTaskDefinition: JSONSchema7;
|
package/Task/GitTask.js
CHANGED
|
@@ -9,7 +9,6 @@ const process_util_1 = require("../util/process-util");
|
|
|
9
9
|
const TaskAbstract_1 = require("./TaskAbstract");
|
|
10
10
|
const assert_1 = require("assert");
|
|
11
11
|
const fs_1 = require("fs");
|
|
12
|
-
const fs_extra_1 = require("fs-extra");
|
|
13
12
|
const promises_1 = require("fs/promises");
|
|
14
13
|
const micromatch_1 = require("micromatch");
|
|
15
14
|
const path_1 = require("path");
|
|
@@ -49,6 +48,10 @@ exports.gitTaskDefinition = {
|
|
|
49
48
|
includeConfig: {
|
|
50
49
|
type: "boolean",
|
|
51
50
|
},
|
|
51
|
+
fileCopyConcurrency: {
|
|
52
|
+
type: "integer",
|
|
53
|
+
minimum: 1,
|
|
54
|
+
},
|
|
52
55
|
},
|
|
53
56
|
};
|
|
54
57
|
class GitTask extends TaskAbstract_1.TaskAbstract {
|
|
@@ -69,6 +72,9 @@ class GitTask extends TaskAbstract_1.TaskAbstract {
|
|
|
69
72
|
(0, assert_1.ok)(typeof targetPath === "string");
|
|
70
73
|
// Bundle
|
|
71
74
|
const bundlePath = (0, path_1.join)(targetPath, "repo.bundle");
|
|
75
|
+
await data.onProgress({
|
|
76
|
+
step: "Creating bundle...",
|
|
77
|
+
});
|
|
72
78
|
await (0, process_util_1.exec)(this.command, ["bundle", "create", bundlePath, "--all"], {
|
|
73
79
|
cwd: path,
|
|
74
80
|
}, {
|
|
@@ -152,40 +158,28 @@ class GitTask extends TaskAbstract_1.TaskAbstract {
|
|
|
152
158
|
for (const option of lsFilesConfig) {
|
|
153
159
|
if (!option.include)
|
|
154
160
|
continue;
|
|
155
|
-
const createdPaths = [];
|
|
156
161
|
const outPath = (0, path_1.join)(targetPath, `repo.${option.name}`);
|
|
157
162
|
await (0, fs_util_1.mkdirIfNotExists)(outPath);
|
|
158
163
|
if (data.options.verbose)
|
|
159
164
|
(0, cli_util_1.logExec)(`Copying ${option.name} files to ${outPath}`);
|
|
160
|
-
|
|
161
|
-
input:
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
});
|
|
170
|
-
}
|
|
171
|
-
else {
|
|
165
|
+
await (0, fs_util_1.cpy)({
|
|
166
|
+
input: {
|
|
167
|
+
type: "pathList",
|
|
168
|
+
path: option.pathsPath,
|
|
169
|
+
basePath: path,
|
|
170
|
+
},
|
|
171
|
+
targetPath: outPath,
|
|
172
|
+
concurrency: this.config.fileCopyConcurrency,
|
|
173
|
+
onPath: async ({ entryPath }) => {
|
|
172
174
|
currentFiles++;
|
|
173
175
|
await data.onProgress({
|
|
174
176
|
total,
|
|
175
177
|
current: currentFiles,
|
|
176
178
|
percent: (0, math_util_1.progressPercent)(total, currentFiles),
|
|
177
|
-
step:
|
|
179
|
+
step: entryPath,
|
|
178
180
|
});
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
await (0, promises_1.mkdir)(dir, {
|
|
182
|
-
recursive: true,
|
|
183
|
-
});
|
|
184
|
-
createdPaths.push(dir);
|
|
185
|
-
}
|
|
186
|
-
await (0, promises_1.copyFile)(source, target);
|
|
187
|
-
}
|
|
188
|
-
}
|
|
181
|
+
},
|
|
182
|
+
});
|
|
189
183
|
await (0, promises_1.rm)(option.pathsPath);
|
|
190
184
|
}
|
|
191
185
|
}
|
|
@@ -235,10 +229,15 @@ class GitTask extends TaskAbstract_1.TaskAbstract {
|
|
|
235
229
|
if (await (0, fs_util_1.checkDir)(sourcePath)) {
|
|
236
230
|
if (data.options.verbose)
|
|
237
231
|
(0, cli_util_1.logExec)(`Copying ${name} files to ${restorePath}`);
|
|
238
|
-
await (0,
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
232
|
+
await (0, fs_util_1.cpy)({
|
|
233
|
+
input: {
|
|
234
|
+
type: "glob",
|
|
235
|
+
sourcePath,
|
|
236
|
+
},
|
|
237
|
+
targetPath: restorePath,
|
|
238
|
+
concurrency: this.config.fileCopyConcurrency,
|
|
239
|
+
onPath: async ({ entryPath }) => {
|
|
240
|
+
await incrementProgress(entryPath);
|
|
242
241
|
},
|
|
243
242
|
});
|
|
244
243
|
}
|
package/Task/MariadbTask.js
CHANGED
|
@@ -83,21 +83,30 @@ class MariadbTask extends TaskAbstract_1.TaskAbstract {
|
|
|
83
83
|
await (0, fs_util_1.forEachFile)(sourcePath, () => {
|
|
84
84
|
total++;
|
|
85
85
|
});
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
86
|
+
let childProcess;
|
|
87
|
+
const onData = async (strLines) => {
|
|
88
|
+
const paths = [];
|
|
89
|
+
const pathRegex = /\[\d{1,}\] \d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2} Copying (.+) to/;
|
|
90
|
+
const lines = strLines.split(/\r?\n/);
|
|
91
|
+
let fatalError = false;
|
|
92
|
+
for (const line of lines) {
|
|
93
|
+
if (line.includes("[ERROR] InnoDB: Unsupported redo log format.") ||
|
|
94
|
+
line.includes("Error: cannot read redo log header")) {
|
|
95
|
+
fatalError = true;
|
|
95
96
|
}
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
97
|
+
else {
|
|
98
|
+
const matches = pathRegex.exec(line);
|
|
99
|
+
if (matches) {
|
|
100
|
+
current++;
|
|
101
|
+
paths.push(matches[1]);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
if (fatalError) {
|
|
106
|
+
childProcess.kill();
|
|
107
|
+
}
|
|
108
|
+
else if (paths.length) {
|
|
109
|
+
const path = (0, posix_1.normalize)(paths[0]);
|
|
101
110
|
await data.onProgress({
|
|
102
111
|
current,
|
|
103
112
|
percent: (0, math_util_1.progressPercent)(total, current),
|
|
@@ -108,6 +117,9 @@ class MariadbTask extends TaskAbstract_1.TaskAbstract {
|
|
|
108
117
|
};
|
|
109
118
|
await (0, process_util_1.exec)(command, args, undefined, {
|
|
110
119
|
log: this.verbose,
|
|
120
|
+
onSpawn: (p) => {
|
|
121
|
+
childProcess = p;
|
|
122
|
+
},
|
|
111
123
|
stdout: {
|
|
112
124
|
onData,
|
|
113
125
|
},
|
package/Task/MssqlTask.js
CHANGED
|
@@ -8,7 +8,6 @@ const fs_util_1 = require("../util/fs-util");
|
|
|
8
8
|
const process_util_1 = require("../util/process-util");
|
|
9
9
|
const TaskAbstract_1 = require("./TaskAbstract");
|
|
10
10
|
const assert_1 = require("assert");
|
|
11
|
-
const fs_extra_1 = require("fs-extra");
|
|
12
11
|
const promises_1 = require("fs/promises");
|
|
13
12
|
const micromatch_1 = require("micromatch");
|
|
14
13
|
const path_1 = require("path");
|
|
@@ -85,7 +84,7 @@ class MssqlTask extends TaskAbstract_1.TaskAbstract {
|
|
|
85
84
|
const restorePath = data.package.restorePath;
|
|
86
85
|
(0, assert_1.ok)(typeof restorePath === "string");
|
|
87
86
|
await (0, fs_util_1.mkdirIfNotExists)(restorePath);
|
|
88
|
-
const files = await (0,
|
|
87
|
+
const files = await (0, promises_1.readdir)(restorePath);
|
|
89
88
|
for (const file of files) {
|
|
90
89
|
if (!file.endsWith(MssqlTask.SUFFIX))
|
|
91
90
|
continue;
|
package/Task/TaskAbstract.d.ts
CHANGED
|
@@ -3,10 +3,10 @@ import { RestoreActionOptionsType } from "../Action/RestoreAction";
|
|
|
3
3
|
import { PackageConfigType } from "../Config/PackageConfig";
|
|
4
4
|
import { SnapshotType } from "../Repository/RepositoryAbstract";
|
|
5
5
|
export declare type ProgressDataType = {
|
|
6
|
-
total
|
|
7
|
-
current
|
|
8
|
-
percent
|
|
9
|
-
step
|
|
6
|
+
total?: number;
|
|
7
|
+
current?: number;
|
|
8
|
+
percent?: number;
|
|
9
|
+
step?: string;
|
|
10
10
|
stepPercent?: number;
|
|
11
11
|
};
|
|
12
12
|
export declare type BackupDataType = {
|
package/config.schema.json
CHANGED
|
@@ -487,6 +487,10 @@
|
|
|
487
487
|
}
|
|
488
488
|
}
|
|
489
489
|
]
|
|
490
|
+
},
|
|
491
|
+
"fileCopyConcurrency": {
|
|
492
|
+
"type": "integer",
|
|
493
|
+
"minimum": 1
|
|
490
494
|
}
|
|
491
495
|
}
|
|
492
496
|
},
|
|
@@ -615,6 +619,10 @@
|
|
|
615
619
|
},
|
|
616
620
|
"includeConfig": {
|
|
617
621
|
"type": "boolean"
|
|
622
|
+
},
|
|
623
|
+
"fileCopyConcurrency": {
|
|
624
|
+
"type": "integer",
|
|
625
|
+
"minimum": 1
|
|
618
626
|
}
|
|
619
627
|
}
|
|
620
628
|
},
|
package/package.json
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@datatruck/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"dependencies": {
|
|
5
5
|
"ajv": "^8.11.0",
|
|
6
|
+
"async": "^3.2.4",
|
|
6
7
|
"chalk": "^4.1.2",
|
|
7
8
|
"cli-table3": "^0.6.2",
|
|
8
9
|
"commander": "^9.2.0",
|
|
9
10
|
"dayjs": "^1.11.2",
|
|
10
11
|
"fast-glob": "^3.2.11",
|
|
11
|
-
"fs-extra": "^10.1.0",
|
|
12
12
|
"micromatch": "^4.0.5",
|
|
13
13
|
"sqlite": "^4.1.1",
|
|
14
14
|
"sqlite3": "^5.0.8"
|
package/util/GitUtil.js
CHANGED
|
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.GitUtil = void 0;
|
|
4
4
|
const fs_util_1 = require("./fs-util");
|
|
5
5
|
const process_util_1 = require("./process-util");
|
|
6
|
-
const
|
|
6
|
+
const promises_1 = require("fs/promises");
|
|
7
7
|
class GitUtil {
|
|
8
8
|
constructor(options) {
|
|
9
9
|
this.options = options;
|
|
@@ -16,7 +16,7 @@ class GitUtil {
|
|
|
16
16
|
}
|
|
17
17
|
async canBeInit(repo) {
|
|
18
18
|
return ((0, fs_util_1.isLocalDir)(repo) &&
|
|
19
|
-
(!(await (0, fs_util_1.checkDir)(repo)) || !(await (0,
|
|
19
|
+
(!(await (0, fs_util_1.checkDir)(repo)) || !(await (0, promises_1.readdir)(repo)).length));
|
|
20
20
|
}
|
|
21
21
|
async clone(options) {
|
|
22
22
|
return await this.exec([
|
package/util/ResticUtil.js
CHANGED
|
@@ -4,7 +4,6 @@ exports.ResticUtil = void 0;
|
|
|
4
4
|
const fs_util_1 = require("./fs-util");
|
|
5
5
|
const process_util_1 = require("./process-util");
|
|
6
6
|
const string_util_1 = require("./string-util");
|
|
7
|
-
const fs_extra_1 = require("fs-extra");
|
|
8
7
|
const promises_1 = require("fs/promises");
|
|
9
8
|
const path_1 = require("path");
|
|
10
9
|
class ResticUtil {
|
|
@@ -20,7 +19,7 @@ class ResticUtil {
|
|
|
20
19
|
if (input.passwordFile)
|
|
21
20
|
input = {
|
|
22
21
|
...input,
|
|
23
|
-
password: (await (0,
|
|
22
|
+
password: (await (0, promises_1.readFile)(input.passwordFile)).toString(),
|
|
24
23
|
};
|
|
25
24
|
return `${input.backend}:${(0, string_util_1.formatUri)(input, hidePassword)}`;
|
|
26
25
|
}
|
package/util/fs-util.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
|
+
import { Interface } from "readline";
|
|
2
3
|
export declare function isLocalDir(path: string): boolean;
|
|
3
4
|
export declare function isDirEmpty(path: string): Promise<boolean>;
|
|
4
5
|
export declare function mkdirIfNotExists(path: string): Promise<string>;
|
|
@@ -44,3 +45,35 @@ export declare function writePathLists(options: {
|
|
|
44
45
|
multipleStats: Record<string, number>;
|
|
45
46
|
};
|
|
46
47
|
}>;
|
|
48
|
+
export declare function cpy(options: {
|
|
49
|
+
input: {
|
|
50
|
+
type: "glob";
|
|
51
|
+
sourcePath: string;
|
|
52
|
+
include?: string[];
|
|
53
|
+
exclude?: string[];
|
|
54
|
+
} | {
|
|
55
|
+
type: "stream";
|
|
56
|
+
basePath: string;
|
|
57
|
+
value: Interface;
|
|
58
|
+
} | {
|
|
59
|
+
type: "pathList";
|
|
60
|
+
path: string;
|
|
61
|
+
basePath: string;
|
|
62
|
+
};
|
|
63
|
+
targetPath: string;
|
|
64
|
+
/**
|
|
65
|
+
* @default 1
|
|
66
|
+
*/
|
|
67
|
+
concurrency?: number;
|
|
68
|
+
onPath?: (data: {
|
|
69
|
+
isDir: boolean;
|
|
70
|
+
entryPath: string;
|
|
71
|
+
entrySourcePath: string;
|
|
72
|
+
entryTargetPath: string;
|
|
73
|
+
stats: {
|
|
74
|
+
paths: number;
|
|
75
|
+
files: number;
|
|
76
|
+
dirs: number;
|
|
77
|
+
};
|
|
78
|
+
}) => Promise<boolean | void>;
|
|
79
|
+
}): Promise<void>;
|
package/util/fs-util.js
CHANGED
|
@@ -3,17 +3,19 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.writePathLists = exports.writeGitIgnoreList = exports.forEachFile = exports.checkDir = exports.checkFile = exports.readPartialFile = exports.mkTmpDir = exports.tmpDir = exports.sessionTmpDir = exports.parentTmpDir = exports.existsFile = exports.findFile = exports.parsePackageFile = exports.parseFile = exports.parseFileExtensions = exports.readdirIfExists = exports.writeJSONFile = exports.existsDir = exports.ensureEmptyDir = exports.mkdirIfNotExists = exports.isDirEmpty = exports.isLocalDir = void 0;
|
|
6
|
+
exports.cpy = exports.writePathLists = exports.writeGitIgnoreList = exports.forEachFile = exports.checkDir = exports.checkFile = exports.readPartialFile = exports.mkTmpDir = exports.tmpDir = exports.sessionTmpDir = exports.parentTmpDir = exports.existsFile = exports.findFile = exports.parsePackageFile = exports.parseFile = exports.parseFileExtensions = exports.readdirIfExists = exports.writeJSONFile = exports.existsDir = exports.ensureEmptyDir = exports.mkdirIfNotExists = exports.isDirEmpty = exports.isLocalDir = void 0;
|
|
7
7
|
const globalData_1 = __importDefault(require("../globalData"));
|
|
8
8
|
const path_util_1 = require("./path-util");
|
|
9
|
+
const async_1 = require("async");
|
|
9
10
|
const crypto_1 = require("crypto");
|
|
11
|
+
const fast_glob_1 = __importDefault(require("fast-glob"));
|
|
10
12
|
const fs_1 = require("fs");
|
|
11
13
|
const fs_2 = require("fs");
|
|
12
|
-
const fs_extra_1 = require("fs-extra");
|
|
13
14
|
const promises_1 = require("fs/promises");
|
|
14
15
|
const micromatch_1 = require("micromatch");
|
|
15
16
|
const path_1 = require("path");
|
|
16
17
|
const path_2 = require("path");
|
|
18
|
+
const readline_1 = require("readline");
|
|
17
19
|
function isLocalDir(path) {
|
|
18
20
|
return /^[\/\.]|([A-Z]:)/i.test(path);
|
|
19
21
|
}
|
|
@@ -25,7 +27,7 @@ async function isDirEmpty(path) {
|
|
|
25
27
|
exports.isDirEmpty = isDirEmpty;
|
|
26
28
|
async function mkdirIfNotExists(path) {
|
|
27
29
|
try {
|
|
28
|
-
await (0,
|
|
30
|
+
await (0, promises_1.mkdir)(path, {
|
|
29
31
|
recursive: true,
|
|
30
32
|
});
|
|
31
33
|
}
|
|
@@ -128,7 +130,7 @@ function tmpDir(prefix, id) {
|
|
|
128
130
|
exports.tmpDir = tmpDir;
|
|
129
131
|
async function mkTmpDir(prefix, id) {
|
|
130
132
|
const path = tmpDir(prefix, id);
|
|
131
|
-
await (0,
|
|
133
|
+
await (0, promises_1.mkdir)(path, { recursive: true });
|
|
132
134
|
return path;
|
|
133
135
|
}
|
|
134
136
|
exports.mkTmpDir = mkTmpDir;
|
|
@@ -313,3 +315,70 @@ async function writePathLists(options) {
|
|
|
313
315
|
};
|
|
314
316
|
}
|
|
315
317
|
exports.writePathLists = writePathLists;
|
|
318
|
+
async function cpy(options) {
|
|
319
|
+
const stats = { paths: 0, files: 0, dirs: 0 };
|
|
320
|
+
const dirs = new Set();
|
|
321
|
+
const makeRecursiveDir = async (path) => {
|
|
322
|
+
if (!dirs.has(path)) {
|
|
323
|
+
stats.paths++;
|
|
324
|
+
stats.dirs++;
|
|
325
|
+
await (0, promises_1.mkdir)(path, {
|
|
326
|
+
recursive: true,
|
|
327
|
+
});
|
|
328
|
+
dirs.add(path);
|
|
329
|
+
}
|
|
330
|
+
};
|
|
331
|
+
const task = async (rawEntryPath, basePath) => {
|
|
332
|
+
const isDir = rawEntryPath.endsWith("/");
|
|
333
|
+
const entryPath = (0, path_1.normalize)(rawEntryPath);
|
|
334
|
+
const entrySourcePath = (0, path_1.resolve)((0, path_1.join)(basePath, rawEntryPath));
|
|
335
|
+
const entryTargetPath = (0, path_1.resolve)((0, path_1.join)(options.targetPath, rawEntryPath));
|
|
336
|
+
const onPathResult = await options?.onPath?.({
|
|
337
|
+
isDir,
|
|
338
|
+
entryPath,
|
|
339
|
+
entrySourcePath,
|
|
340
|
+
entryTargetPath,
|
|
341
|
+
stats,
|
|
342
|
+
});
|
|
343
|
+
if (onPathResult === false) {
|
|
344
|
+
return;
|
|
345
|
+
}
|
|
346
|
+
else if (isDir) {
|
|
347
|
+
await makeRecursiveDir(entryTargetPath);
|
|
348
|
+
}
|
|
349
|
+
else {
|
|
350
|
+
const dir = (0, path_1.dirname)(entryTargetPath);
|
|
351
|
+
await makeRecursiveDir(dir);
|
|
352
|
+
stats.files++;
|
|
353
|
+
await (0, promises_1.cp)(entrySourcePath, entryTargetPath);
|
|
354
|
+
}
|
|
355
|
+
};
|
|
356
|
+
const { input } = options;
|
|
357
|
+
if (input.type === "glob") {
|
|
358
|
+
const stream = await (0, fast_glob_1.default)(input.include || ["**"], {
|
|
359
|
+
cwd: input.sourcePath,
|
|
360
|
+
ignore: input.exclude,
|
|
361
|
+
dot: true,
|
|
362
|
+
onlyFiles: false,
|
|
363
|
+
markDirectories: true,
|
|
364
|
+
});
|
|
365
|
+
await (0, async_1.eachLimit)(stream, options.concurrency ?? 1, async (entryPath) => await task(entryPath, input.sourcePath));
|
|
366
|
+
}
|
|
367
|
+
else if (input.type === "stream") {
|
|
368
|
+
await (0, async_1.eachLimit)(input.value, options.concurrency ?? 1, async (entryPath) => await task(entryPath, input.basePath));
|
|
369
|
+
}
|
|
370
|
+
else if (input.type === "pathList") {
|
|
371
|
+
const stream = (0, readline_1.createInterface)({
|
|
372
|
+
input: (0, fs_1.createReadStream)(input.path),
|
|
373
|
+
});
|
|
374
|
+
await cpy({
|
|
375
|
+
...options,
|
|
376
|
+
input: {
|
|
377
|
+
type: "stream",
|
|
378
|
+
value: stream,
|
|
379
|
+
basePath: input.basePath,
|
|
380
|
+
},
|
|
381
|
+
});
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
exports.cpy = cpy;
|