@datatruck/cli 0.4.0 → 0.6.1
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.js +1 -0
- package/Action/InitAction.js +3 -0
- package/Action/PruneAction.js +1 -1
- package/Action/RestoreAction.js +4 -1
- package/Action/SnapshotsAction.d.ts +2 -1
- package/Action/SnapshotsAction.js +6 -1
- package/CHANGELOG.md +28 -0
- package/Config/RepositoryConfig.d.ts +4 -0
- package/Config/RepositoryConfig.js +19 -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.d.ts +7 -3
- package/Repository/ResticRepository.js +47 -12
- package/SessionDriver/ConsoleSessionDriver.js +12 -11
- package/Task/GitTask.d.ts +4 -0
- package/Task/GitTask.js +28 -29
- package/Task/MariadbTask.js +27 -15
- package/Task/MssqlTask.js +1 -2
- package/Task/TaskAbstract.d.ts +4 -4
- package/config.schema.json +58 -3
- package/package.json +2 -2
- package/util/GitUtil.js +2 -2
- package/util/ResticUtil.d.ts +1 -1
- package/util/ResticUtil.js +3 -4
- package/util/datatruck/config-util.d.ts +4 -1
- package/util/datatruck/config-util.js +11 -1
- package/util/fs-util.d.ts +33 -0
- package/util/fs-util.js +73 -4
- package/util/process-util.d.ts +2 -2
- package/util/process-util.js +4 -4
- package/util/zip-util.d.ts +2 -2
- package/util/zip-util.js +9 -9
package/Action/BackupAction.js
CHANGED
|
@@ -23,6 +23,7 @@ class BackupAction {
|
|
|
23
23
|
packageNames: this.options.packageNames,
|
|
24
24
|
repositoryNames: this.options.repositoryNames,
|
|
25
25
|
repositoryTypes: this.options.repositoryTypes,
|
|
26
|
+
sourceAction: "backup",
|
|
26
27
|
});
|
|
27
28
|
packages = (0, config_util_1.resolvePackages)(packages, {
|
|
28
29
|
snapshotId: snapshot.id,
|
package/Action/InitAction.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.InitAction = void 0;
|
|
4
4
|
const RepositoryFactory_1 = require("../Factory/RepositoryFactory");
|
|
5
|
+
const config_util_1 = require("../util/datatruck/config-util");
|
|
5
6
|
class InitAction {
|
|
6
7
|
constructor(config, options) {
|
|
7
8
|
this.config = config;
|
|
@@ -10,6 +11,8 @@ class InitAction {
|
|
|
10
11
|
async exec() {
|
|
11
12
|
const result = [];
|
|
12
13
|
for (const repo of this.config.repositories) {
|
|
14
|
+
if (!(0, config_util_1.filterRepository)(repo, "init"))
|
|
15
|
+
continue;
|
|
13
16
|
if (this.options.repositoryNames &&
|
|
14
17
|
!this.options.repositoryNames.includes(repo.name))
|
|
15
18
|
continue;
|
package/Action/PruneAction.js
CHANGED
|
@@ -24,7 +24,7 @@ class PruneAction {
|
|
|
24
24
|
}
|
|
25
25
|
async exec() {
|
|
26
26
|
const snapshotsAction = new SnapshotsAction_1.SnapshotsAction(this.config, this.options);
|
|
27
|
-
const snapshots = await snapshotsAction.exec();
|
|
27
|
+
const snapshots = await snapshotsAction.exec("prune");
|
|
28
28
|
const snapshotsDeleted = [];
|
|
29
29
|
const reasons = {};
|
|
30
30
|
const inputFilter = {
|
package/Action/RestoreAction.js
CHANGED
|
@@ -203,7 +203,10 @@ class RestoreAction {
|
|
|
203
203
|
const snapshots = this.groupSnapshots(await this.findSnapshots());
|
|
204
204
|
if (!snapshots.length)
|
|
205
205
|
throw new AppError_1.AppError("None snapshot found");
|
|
206
|
-
let packages = (0, config_util_1.filterPackages)(this.config,
|
|
206
|
+
let packages = (0, config_util_1.filterPackages)(this.config, {
|
|
207
|
+
...this.options,
|
|
208
|
+
sourceAction: "restore",
|
|
209
|
+
});
|
|
207
210
|
packages = (0, config_util_1.resolvePackages)(packages, {
|
|
208
211
|
snapshotId: this.options.snapshotId,
|
|
209
212
|
snapshotDate: snapshots[0].date,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ConfigType } from "../Config/Config";
|
|
2
|
+
import { RepositoryConfigEnabledActionType } from "../Config/RepositoryConfig";
|
|
2
3
|
import { SnapshotResultType } from "../Repository/RepositoryAbstract";
|
|
3
4
|
import { IfRequireKeys } from "../util/ts-util";
|
|
4
5
|
export declare type SnapshotGroupByType = keyof Pick<SnapshotExtendedType, "packageName" | "repositoryName" | "repositoryType">;
|
|
@@ -27,5 +28,5 @@ export declare class SnapshotsAction<TRequired extends boolean = true> {
|
|
|
27
28
|
readonly config: ConfigType;
|
|
28
29
|
readonly options: IfRequireKeys<TRequired, SnapshotsActionOptionsType>;
|
|
29
30
|
constructor(config: ConfigType, options: IfRequireKeys<TRequired, SnapshotsActionOptionsType>);
|
|
30
|
-
exec(): Promise<SnapshotExtendedType[]>;
|
|
31
|
+
exec(sourceAction?: RepositoryConfigEnabledActionType): Promise<SnapshotExtendedType[]>;
|
|
31
32
|
}
|
|
@@ -2,15 +2,20 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.SnapshotsAction = void 0;
|
|
4
4
|
const RepositoryFactory_1 = require("../Factory/RepositoryFactory");
|
|
5
|
+
const config_util_1 = require("../util/datatruck/config-util");
|
|
5
6
|
const snapshot_util_1 = require("../util/datatruck/snapshot-util");
|
|
6
7
|
class SnapshotsAction {
|
|
7
8
|
constructor(config, options) {
|
|
8
9
|
this.config = config;
|
|
9
10
|
this.options = options;
|
|
10
11
|
}
|
|
11
|
-
async exec() {
|
|
12
|
+
async exec(sourceAction) {
|
|
13
|
+
if (!sourceAction)
|
|
14
|
+
sourceAction = "snapshots";
|
|
12
15
|
let result = [];
|
|
13
16
|
for (const repo of this.config.repositories) {
|
|
17
|
+
if (!(0, config_util_1.filterRepository)(repo, sourceAction))
|
|
18
|
+
continue;
|
|
14
19
|
if (this.options.repositoryNames &&
|
|
15
20
|
!this.options.repositoryNames.includes(repo.name))
|
|
16
21
|
continue;
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,33 @@
|
|
|
1
1
|
# @datatruck/cli
|
|
2
2
|
|
|
3
|
+
## 0.6.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [`0ba6229`](https://github.com/swordev/datatruck/commit/0ba6229348c109a59783e72242ab7c0e61f25e36) Thanks [@juanrgm](https://github.com/juanrgm)! - Fix progress bar in restic repository
|
|
8
|
+
|
|
9
|
+
## 0.6.0
|
|
10
|
+
|
|
11
|
+
### Minor Changes
|
|
12
|
+
|
|
13
|
+
- [`0c6877d`](https://github.com/swordev/datatruck/commit/0c6877d189761e75dd434b0a8d72b71621d024de) Thanks [@juanrgm](https://github.com/juanrgm)! - Show more progress stats
|
|
14
|
+
|
|
15
|
+
* [`751e1f6`](https://github.com/swordev/datatruck/commit/751e1f6d6b33d3fa96eb40d998fdd140ce0e3875) Thanks [@juanrgm](https://github.com/juanrgm)! - Add `fileCopyConcurrency` option
|
|
16
|
+
|
|
17
|
+
- [`05487e6`](https://github.com/swordev/datatruck/commit/05487e6a33f875a3afb7ff0815b16da6f2a41301) Thanks [@juanrgm](https://github.com/juanrgm)! - Parse InnoDB error in `MariadbTask` to avoid infinite wait
|
|
18
|
+
|
|
19
|
+
### Patch Changes
|
|
20
|
+
|
|
21
|
+
- [`b62a6f8`](https://github.com/swordev/datatruck/commit/b62a6f8a82409339afd65d4f96476eb57bbfb5a2) Thanks [@juanrgm](https://github.com/juanrgm)! - Resolve target/restore path in local repository
|
|
22
|
+
|
|
23
|
+
## 0.5.0
|
|
24
|
+
|
|
25
|
+
### Minor Changes
|
|
26
|
+
|
|
27
|
+
- [`5aeb2af`](https://github.com/swordev/datatruck/commit/5aeb2afb96692e00bdba501b58df9cc0e02dceaa) Thanks [@juanrgm](https://github.com/juanrgm)! - Add `enabled` option to repository config
|
|
28
|
+
|
|
29
|
+
* [`75de836`](https://github.com/swordev/datatruck/commit/75de8369356cf02ed3fd5c58b1f9bea66432cda8) Thanks [@juanrgm](https://github.com/juanrgm)! - Allow restic password without file
|
|
30
|
+
|
|
3
31
|
## 0.4.0
|
|
4
32
|
|
|
5
33
|
### Minor Changes
|
|
@@ -4,8 +4,12 @@ import { ResticRepositoryConfigType, resticRepositoryName } from "../Repository/
|
|
|
4
4
|
import type { JSONSchema7 } from "json-schema";
|
|
5
5
|
export declare const repositoryConfigDefinition: JSONSchema7;
|
|
6
6
|
export declare type RepositoryConfigTypeType = RepositoryConfigType["type"];
|
|
7
|
+
export declare type RepositoryConfigEnabledActionType = "backup" | "init" | "prune" | "restore" | "snapshots";
|
|
7
8
|
export declare type RepositoryConfigType = {
|
|
8
9
|
name: string;
|
|
10
|
+
enabled?: boolean | {
|
|
11
|
+
[K in "defaults" | RepositoryConfigEnabledActionType]?: boolean;
|
|
12
|
+
};
|
|
9
13
|
} & ({
|
|
10
14
|
type: typeof resticRepositoryName;
|
|
11
15
|
config: ResticRepositoryConfigType;
|
|
@@ -17,6 +17,25 @@ exports.repositoryConfigDefinition = {
|
|
|
17
17
|
properties: {
|
|
18
18
|
type: { type: "string" },
|
|
19
19
|
name: { type: "string" },
|
|
20
|
+
enabled: {
|
|
21
|
+
anyOf: [
|
|
22
|
+
{
|
|
23
|
+
type: "boolean",
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
type: "object",
|
|
27
|
+
additionalProperties: false,
|
|
28
|
+
properties: {
|
|
29
|
+
defaults: { type: "boolean" },
|
|
30
|
+
backup: { type: "boolean" },
|
|
31
|
+
init: { type: "boolean" },
|
|
32
|
+
prune: { type: "boolean" },
|
|
33
|
+
restore: { type: "boolean" },
|
|
34
|
+
snapshots: { type: "boolean" },
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
],
|
|
38
|
+
},
|
|
20
39
|
config: {},
|
|
21
40
|
},
|
|
22
41
|
anyOf: Object.keys(types).map((type) => ({
|
|
@@ -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 = {
|
|
@@ -2,7 +2,9 @@ import { RepositoryType } from "../util/ResticUtil";
|
|
|
2
2
|
import { RepositoryAbstract, BackupDataType, InitDataType, RestoreDataType, SnapshotsDataType, SnapshotResultType, SnapshotTagObjectType, SnapshotTagEnum, PruneDataType } from "./RepositoryAbstract";
|
|
3
3
|
import { JSONSchema7 } from "json-schema";
|
|
4
4
|
export declare type ResticRepositoryConfigType = {
|
|
5
|
-
|
|
5
|
+
password: string | {
|
|
6
|
+
path: string;
|
|
7
|
+
};
|
|
6
8
|
repository: RepositoryType;
|
|
7
9
|
};
|
|
8
10
|
export declare type ResticPackageRepositoryConfigType = {};
|
|
@@ -12,11 +14,13 @@ export declare const resticPackageRepositoryDefinition: JSONSchema7;
|
|
|
12
14
|
export declare class ResticRepository extends RepositoryAbstract<ResticRepositoryConfigType> {
|
|
13
15
|
static refPrefix: string;
|
|
14
16
|
protected env: {
|
|
15
|
-
|
|
17
|
+
RESTIC_PASSWORD?: string;
|
|
18
|
+
RESTIC_PASSWORD_FILE?: string;
|
|
16
19
|
RESTIC_REPOSITORY: string;
|
|
17
20
|
};
|
|
18
21
|
buildEnv(): Promise<{
|
|
19
|
-
|
|
22
|
+
RESTIC_PASSWORD?: string | undefined;
|
|
23
|
+
RESTIC_PASSWORD_FILE?: string | undefined;
|
|
20
24
|
RESTIC_REPOSITORY: string;
|
|
21
25
|
}>;
|
|
22
26
|
static buildSnapshotTag(name: SnapshotTagEnum, value: string): string;
|
|
@@ -18,10 +18,22 @@ const path_1 = require("path");
|
|
|
18
18
|
exports.resticRepositoryName = "restic";
|
|
19
19
|
exports.resticRepositoryDefinition = {
|
|
20
20
|
type: "object",
|
|
21
|
-
required: ["
|
|
21
|
+
required: ["password", "repository"],
|
|
22
22
|
additionalProperties: false,
|
|
23
23
|
properties: {
|
|
24
|
-
|
|
24
|
+
password: {
|
|
25
|
+
anyOf: [
|
|
26
|
+
{ type: "string" },
|
|
27
|
+
{
|
|
28
|
+
type: "object",
|
|
29
|
+
additionalProperties: false,
|
|
30
|
+
required: ["path"],
|
|
31
|
+
properties: {
|
|
32
|
+
path: { type: "string" },
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
],
|
|
36
|
+
},
|
|
25
37
|
repository: {
|
|
26
38
|
type: "object",
|
|
27
39
|
additionalProperties: false,
|
|
@@ -59,7 +71,9 @@ class ResticRepository extends RepositoryAbstract_1.RepositoryAbstract {
|
|
|
59
71
|
if (this.env)
|
|
60
72
|
return this.env;
|
|
61
73
|
return (this.env = {
|
|
62
|
-
|
|
74
|
+
...(typeof this.config.password === "string"
|
|
75
|
+
? { RESTIC_PASSWORD: this.config.password }
|
|
76
|
+
: { RESTIC_PASSWORD_FILE: (0, path_1.resolve)(this.config.password.path) }),
|
|
63
77
|
RESTIC_REPOSITORY: await ResticUtil_1.ResticUtil.formatRepository(this.config.repository),
|
|
64
78
|
});
|
|
65
79
|
}
|
|
@@ -176,6 +190,9 @@ class ResticRepository extends RepositoryAbstract_1.RepositoryAbstract {
|
|
|
176
190
|
});
|
|
177
191
|
if (data.options.verbose)
|
|
178
192
|
(0, cli_util_1.logExec)(`Writing paths lists`);
|
|
193
|
+
await data.onProgress({
|
|
194
|
+
step: "Writing excluded paths list...",
|
|
195
|
+
});
|
|
179
196
|
gitignorePath = await (0, fs_util_1.writeGitIgnoreList)({
|
|
180
197
|
paths: stream,
|
|
181
198
|
});
|
|
@@ -183,12 +200,21 @@ class ResticRepository extends RepositoryAbstract_1.RepositoryAbstract {
|
|
|
183
200
|
if (data.options.tags?.some((tag) => tag.startsWith(ResticRepository.refPrefix)))
|
|
184
201
|
throw new AppError_1.AppError(`Tag prefix is not allowed`);
|
|
185
202
|
const packageTag = ResticRepository.buildSnapshotTag(RepositoryAbstract_1.SnapshotTagEnum.PACKAGE, data.package.name);
|
|
203
|
+
await data.onProgress({
|
|
204
|
+
step: "Fetching last snapshot...",
|
|
205
|
+
});
|
|
186
206
|
const [lastSnapshot] = await restic.snapshots({
|
|
187
207
|
json: true,
|
|
188
208
|
tags: [packageTag],
|
|
189
209
|
latest: 1,
|
|
190
210
|
});
|
|
191
211
|
const nodePkg = (0, fs_util_1.parsePackageFile)();
|
|
212
|
+
let lastProgress;
|
|
213
|
+
let totalFilesChanges = 0;
|
|
214
|
+
const totalFilesChangesLimit = 10;
|
|
215
|
+
await data.onProgress({
|
|
216
|
+
step: "Executing backup action...",
|
|
217
|
+
});
|
|
192
218
|
await restic.backup({
|
|
193
219
|
cwd: sourcePath,
|
|
194
220
|
paths: ["."],
|
|
@@ -209,22 +235,31 @@ class ResticRepository extends RepositoryAbstract_1.RepositoryAbstract {
|
|
|
209
235
|
],
|
|
210
236
|
onStream: async (streamData) => {
|
|
211
237
|
if (streamData.message_type === "status") {
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
238
|
+
let showProgressBar = false;
|
|
239
|
+
if (totalFilesChanges > totalFilesChangesLimit) {
|
|
240
|
+
showProgressBar = true;
|
|
241
|
+
}
|
|
242
|
+
else if (lastProgress?.total !== streamData.total_files) {
|
|
243
|
+
totalFilesChanges = 0;
|
|
244
|
+
}
|
|
245
|
+
else {
|
|
246
|
+
totalFilesChanges++;
|
|
247
|
+
}
|
|
248
|
+
await data.onProgress((lastProgress = {
|
|
249
|
+
total: Math.max(lastProgress?.total || 0, streamData.total_files),
|
|
250
|
+
current: Math.max(lastProgress?.current || 0, streamData.files_done ?? 0),
|
|
251
|
+
percent: showProgressBar
|
|
252
|
+
? Number((streamData.percent_done * 100).toFixed(2))
|
|
216
253
|
: 0,
|
|
217
|
-
percent: Number((streamData.percent_done * 100).toFixed(2)),
|
|
218
254
|
step: streamData.current_files?.join(", ") ?? "-",
|
|
219
|
-
});
|
|
255
|
+
}));
|
|
220
256
|
}
|
|
221
257
|
},
|
|
222
258
|
});
|
|
223
259
|
await data.onProgress({
|
|
224
|
-
|
|
260
|
+
total: lastProgress?.total || 0,
|
|
261
|
+
current: lastProgress?.total || 0,
|
|
225
262
|
percent: 100,
|
|
226
|
-
step: "",
|
|
227
|
-
total: 100,
|
|
228
263
|
});
|
|
229
264
|
}
|
|
230
265
|
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
|
},
|
|
@@ -117,7 +129,7 @@ class MariadbTask extends TaskAbstract_1.TaskAbstract {
|
|
|
117
129
|
});
|
|
118
130
|
await (0, process_util_1.exec)(command, [`--prepare`, `--target-dir=${targetPath}`], undefined, {
|
|
119
131
|
log: this.verbose,
|
|
120
|
-
stderr: { onData:
|
|
132
|
+
stderr: { onData: () => { } },
|
|
121
133
|
});
|
|
122
134
|
}
|
|
123
135
|
async onRestore(data) {
|
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
|
@@ -20,6 +20,37 @@
|
|
|
20
20
|
"name": {
|
|
21
21
|
"type": "string"
|
|
22
22
|
},
|
|
23
|
+
"enabled": {
|
|
24
|
+
"anyOf": [
|
|
25
|
+
{
|
|
26
|
+
"type": "boolean"
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
"type": "object",
|
|
30
|
+
"additionalProperties": false,
|
|
31
|
+
"properties": {
|
|
32
|
+
"defaults": {
|
|
33
|
+
"type": "boolean"
|
|
34
|
+
},
|
|
35
|
+
"backup": {
|
|
36
|
+
"type": "boolean"
|
|
37
|
+
},
|
|
38
|
+
"init": {
|
|
39
|
+
"type": "boolean"
|
|
40
|
+
},
|
|
41
|
+
"prune": {
|
|
42
|
+
"type": "boolean"
|
|
43
|
+
},
|
|
44
|
+
"restore": {
|
|
45
|
+
"type": "boolean"
|
|
46
|
+
},
|
|
47
|
+
"snapshots": {
|
|
48
|
+
"type": "boolean"
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
]
|
|
53
|
+
},
|
|
23
54
|
"config": {}
|
|
24
55
|
},
|
|
25
56
|
"anyOf": [
|
|
@@ -456,19 +487,39 @@
|
|
|
456
487
|
}
|
|
457
488
|
}
|
|
458
489
|
]
|
|
490
|
+
},
|
|
491
|
+
"fileCopyConcurrency": {
|
|
492
|
+
"type": "integer",
|
|
493
|
+
"minimum": 1
|
|
459
494
|
}
|
|
460
495
|
}
|
|
461
496
|
},
|
|
462
497
|
"restic-repository": {
|
|
463
498
|
"type": "object",
|
|
464
499
|
"required": [
|
|
465
|
-
"
|
|
500
|
+
"password",
|
|
466
501
|
"repository"
|
|
467
502
|
],
|
|
468
503
|
"additionalProperties": false,
|
|
469
504
|
"properties": {
|
|
470
|
-
"
|
|
471
|
-
"
|
|
505
|
+
"password": {
|
|
506
|
+
"anyOf": [
|
|
507
|
+
{
|
|
508
|
+
"type": "string"
|
|
509
|
+
},
|
|
510
|
+
{
|
|
511
|
+
"type": "object",
|
|
512
|
+
"additionalProperties": false,
|
|
513
|
+
"required": [
|
|
514
|
+
"path"
|
|
515
|
+
],
|
|
516
|
+
"properties": {
|
|
517
|
+
"path": {
|
|
518
|
+
"type": "string"
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
]
|
|
472
523
|
},
|
|
473
524
|
"repository": {
|
|
474
525
|
"type": "object",
|
|
@@ -568,6 +619,10 @@
|
|
|
568
619
|
},
|
|
569
620
|
"includeConfig": {
|
|
570
621
|
"type": "boolean"
|
|
622
|
+
},
|
|
623
|
+
"fileCopyConcurrency": {
|
|
624
|
+
"type": "integer",
|
|
625
|
+
"minimum": 1
|
|
571
626
|
}
|
|
572
627
|
}
|
|
573
628
|
},
|
package/package.json
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@datatruck/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.1",
|
|
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.d.ts
CHANGED
|
@@ -84,7 +84,7 @@ export declare class ResticUtil {
|
|
|
84
84
|
excludeFile?: string[];
|
|
85
85
|
parent?: string;
|
|
86
86
|
allowEmptySnapshot?: boolean;
|
|
87
|
-
onStream?: (data: BackupStreamType) =>
|
|
87
|
+
onStream?: (data: BackupStreamType) => void;
|
|
88
88
|
}): Promise<ExecResultType>;
|
|
89
89
|
restore(options: {
|
|
90
90
|
id: string;
|
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
|
}
|
|
@@ -116,7 +115,7 @@ class ResticUtil {
|
|
|
116
115
|
},
|
|
117
116
|
stdout: {
|
|
118
117
|
...(options.onStream && {
|
|
119
|
-
onData:
|
|
118
|
+
onData: (data) => {
|
|
120
119
|
for (const rawLine of data.split("\n")) {
|
|
121
120
|
const line = rawLine.trim();
|
|
122
121
|
if (line.startsWith("{") && line.endsWith("}")) {
|
|
@@ -126,7 +125,7 @@ class ResticUtil {
|
|
|
126
125
|
}
|
|
127
126
|
catch (error) { }
|
|
128
127
|
if (parsedLine)
|
|
129
|
-
|
|
128
|
+
options.onStream?.(parsedLine);
|
|
130
129
|
}
|
|
131
130
|
}
|
|
132
131
|
},
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import { ConfigType } from "../../Config/Config";
|
|
2
2
|
import type { PackageConfigType } from "../../Config/PackageConfig";
|
|
3
|
-
|
|
3
|
+
import { RepositoryConfigEnabledActionType, RepositoryConfigType } from "../../Config/RepositoryConfig";
|
|
4
|
+
export declare function findRepositoryOrFail(config: ConfigType, repositoryName: string): RepositoryConfigType;
|
|
5
|
+
export declare function filterRepository(repository: RepositoryConfigType, action?: RepositoryConfigEnabledActionType): boolean;
|
|
4
6
|
export declare function filterPackages(config: ConfigType, options: {
|
|
5
7
|
packageNames?: string[];
|
|
6
8
|
repositoryNames?: string[];
|
|
7
9
|
repositoryTypes?: string[];
|
|
10
|
+
sourceAction?: RepositoryConfigEnabledActionType;
|
|
8
11
|
}): PackageConfigType[];
|
|
9
12
|
declare type ResolvePackagePathParamsType = ResolvePackageParamsType & {
|
|
10
13
|
packageName: string;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.params = exports.dbNameParams = exports.pkgRestorePathParams = exports.pkgExcludeParams = exports.pkgIncludeParams = exports.pkgPathParams = exports.resolvePackages = exports.resolvePackage = exports.resolveDatabaseName = exports.resolvePackagePath = exports.filterPackages = exports.findRepositoryOrFail = void 0;
|
|
3
|
+
exports.params = exports.dbNameParams = exports.pkgRestorePathParams = exports.pkgExcludeParams = exports.pkgIncludeParams = exports.pkgPathParams = exports.resolvePackages = exports.resolvePackage = exports.resolveDatabaseName = exports.resolvePackagePath = exports.filterPackages = exports.filterRepository = exports.findRepositoryOrFail = void 0;
|
|
4
4
|
const AppError_1 = require("../../Error/AppError");
|
|
5
5
|
const fs_util_1 = require("../fs-util");
|
|
6
6
|
const string_util_1 = require("../string-util");
|
|
@@ -12,6 +12,14 @@ function findRepositoryOrFail(config, repositoryName) {
|
|
|
12
12
|
return repo;
|
|
13
13
|
}
|
|
14
14
|
exports.findRepositoryOrFail = findRepositoryOrFail;
|
|
15
|
+
function filterRepository(repository, action) {
|
|
16
|
+
const enabled = repository.enabled ?? true;
|
|
17
|
+
if (typeof enabled === "boolean")
|
|
18
|
+
return enabled;
|
|
19
|
+
const defaults = enabled["defaults"] ?? true;
|
|
20
|
+
return action ? enabled[action] ?? defaults : defaults;
|
|
21
|
+
}
|
|
22
|
+
exports.filterRepository = filterRepository;
|
|
15
23
|
function filterPackages(config, options) {
|
|
16
24
|
const packagePatterns = (0, string_util_1.makePathPatterns)(options.packageNames);
|
|
17
25
|
return config.packages
|
|
@@ -19,6 +27,8 @@ function filterPackages(config, options) {
|
|
|
19
27
|
pkg = Object.assign({}, pkg);
|
|
20
28
|
pkg.repositoryNames = (pkg.repositoryNames ?? []).filter((name) => {
|
|
21
29
|
const repo = findRepositoryOrFail(config, name);
|
|
30
|
+
if (!filterRepository(repo, options?.sourceAction))
|
|
31
|
+
return false;
|
|
22
32
|
return ((!options.repositoryNames ||
|
|
23
33
|
options.repositoryNames.includes(name)) &&
|
|
24
34
|
(!options.repositoryTypes ||
|
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;
|
package/util/process-util.d.ts
CHANGED
|
@@ -23,11 +23,11 @@ export interface ExecSettingsInterface {
|
|
|
23
23
|
onSpawn?: (p: ChildProcess) => void;
|
|
24
24
|
stdout?: {
|
|
25
25
|
save?: boolean;
|
|
26
|
-
onData?: (data: string) =>
|
|
26
|
+
onData?: (data: string) => void;
|
|
27
27
|
};
|
|
28
28
|
stderr?: {
|
|
29
29
|
save?: boolean;
|
|
30
|
-
onData?: (data: string) =>
|
|
30
|
+
onData?: (data: string) => void;
|
|
31
31
|
toExitCode?: boolean;
|
|
32
32
|
};
|
|
33
33
|
onExitCodeError?: (data: ExecResultType, error: Error) => Error | false;
|
package/util/process-util.js
CHANGED
|
@@ -127,7 +127,7 @@ async function exec(command, argv = [], options = null, settings = {}) {
|
|
|
127
127
|
if (log.stdout || settings.stdout) {
|
|
128
128
|
if (!p.stdout)
|
|
129
129
|
throw new Error(`stdout is not defined`);
|
|
130
|
-
p.stdout.on("data",
|
|
130
|
+
p.stdout.on("data", (data) => {
|
|
131
131
|
if (log.stdout)
|
|
132
132
|
logExecStdout({
|
|
133
133
|
data: data.toString(),
|
|
@@ -137,13 +137,13 @@ async function exec(command, argv = [], options = null, settings = {}) {
|
|
|
137
137
|
if (settings.stdout?.save)
|
|
138
138
|
spawnData.stdout += data.toString();
|
|
139
139
|
if (settings.stdout?.onData)
|
|
140
|
-
|
|
140
|
+
settings.stdout.onData(data.toString());
|
|
141
141
|
});
|
|
142
142
|
}
|
|
143
143
|
if (log.stderr || settings.stderr) {
|
|
144
144
|
if (!p.stderr)
|
|
145
145
|
throw new Error(`stderr is not defined`);
|
|
146
|
-
p.stderr.on("data",
|
|
146
|
+
p.stderr.on("data", (data) => {
|
|
147
147
|
if (log.stderr)
|
|
148
148
|
logExecStdout({
|
|
149
149
|
data: data.toString(),
|
|
@@ -153,7 +153,7 @@ async function exec(command, argv = [], options = null, settings = {}) {
|
|
|
153
153
|
if (settings.stderr?.save || settings.stderr?.toExitCode)
|
|
154
154
|
spawnData.stderr += data.toString();
|
|
155
155
|
if (settings.stderr?.onData)
|
|
156
|
-
|
|
156
|
+
settings.stderr.onData(data.toString());
|
|
157
157
|
});
|
|
158
158
|
}
|
|
159
159
|
p.on("error", (error) => (spawnError = error)).on("close", (exitCode) => {
|
package/util/zip-util.d.ts
CHANGED
|
@@ -26,7 +26,7 @@ export interface ZipDataType {
|
|
|
26
26
|
includeList?: string;
|
|
27
27
|
excludeList?: string;
|
|
28
28
|
verbose?: boolean;
|
|
29
|
-
onStream?: (data: ZipStreamDataType) =>
|
|
29
|
+
onStream?: (data: ZipStreamDataType) => void;
|
|
30
30
|
}
|
|
31
31
|
export interface UnzipDataType {
|
|
32
32
|
command?: string;
|
|
@@ -34,7 +34,7 @@ export interface UnzipDataType {
|
|
|
34
34
|
files?: (ZipDataFilterType | string)[];
|
|
35
35
|
output: string;
|
|
36
36
|
verbose?: boolean;
|
|
37
|
-
onStream?: (data: UnzipStreamDataType) =>
|
|
37
|
+
onStream?: (data: UnzipStreamDataType) => void;
|
|
38
38
|
}
|
|
39
39
|
export declare type UnzipStreamDataType = {
|
|
40
40
|
type: "progress";
|
package/util/zip-util.js
CHANGED
|
@@ -30,7 +30,7 @@ function buildArguments(filters) {
|
|
|
30
30
|
return args;
|
|
31
31
|
}
|
|
32
32
|
exports.buildArguments = buildArguments;
|
|
33
|
-
|
|
33
|
+
function parseZipStream(chunk, buffer, cb) {
|
|
34
34
|
const lines = chunk.trim().split(/\r?\n/g);
|
|
35
35
|
for (const line of lines) {
|
|
36
36
|
let matches = null;
|
|
@@ -42,7 +42,7 @@ async function parseZipStream(chunk, buffer, cb) {
|
|
|
42
42
|
if (path !== buffer.lastPath)
|
|
43
43
|
buffer.currentPaths++;
|
|
44
44
|
buffer.lastPath = path;
|
|
45
|
-
|
|
45
|
+
cb({
|
|
46
46
|
type: "progress",
|
|
47
47
|
data: { progress, path, files: buffer.currentPaths },
|
|
48
48
|
});
|
|
@@ -50,7 +50,7 @@ async function parseZipStream(chunk, buffer, cb) {
|
|
|
50
50
|
else if (line.startsWith("Add new data to archive:")) {
|
|
51
51
|
const [, folders] = /(\d+) folders?/i.exec(line) || [, 0];
|
|
52
52
|
const [, files] = /(\d+) files?/i.exec(line) || [, 0];
|
|
53
|
-
|
|
53
|
+
cb({
|
|
54
54
|
type: "summary",
|
|
55
55
|
data: {
|
|
56
56
|
folders: Number(folders),
|
|
@@ -83,9 +83,9 @@ async function zip(data) {
|
|
|
83
83
|
toExitCode: true,
|
|
84
84
|
},
|
|
85
85
|
stdout: {
|
|
86
|
-
onData:
|
|
87
|
-
parseZipStream(chunk, buffer,
|
|
88
|
-
|
|
86
|
+
onData: (chunk) => {
|
|
87
|
+
parseZipStream(chunk, buffer, (stream) => {
|
|
88
|
+
data.onStream?.(stream);
|
|
89
89
|
if (stream.type === "summary")
|
|
90
90
|
result = stream.data;
|
|
91
91
|
});
|
|
@@ -95,7 +95,7 @@ async function zip(data) {
|
|
|
95
95
|
return result;
|
|
96
96
|
}
|
|
97
97
|
exports.zip = zip;
|
|
98
|
-
|
|
98
|
+
function parseUnzipStream(chunk, cb) {
|
|
99
99
|
const lines = chunk.trim().split(/\r?\n/g);
|
|
100
100
|
for (const line of lines) {
|
|
101
101
|
let matches = null;
|
|
@@ -103,7 +103,7 @@ async function parseUnzipStream(chunk, cb) {
|
|
|
103
103
|
const progress = Number(matches[1]);
|
|
104
104
|
const files = Number(matches[2]);
|
|
105
105
|
const path = line.slice(line.indexOf("-") + 1).trim();
|
|
106
|
-
|
|
106
|
+
cb({
|
|
107
107
|
type: "progress",
|
|
108
108
|
data: { progress, path, files },
|
|
109
109
|
});
|
|
@@ -124,7 +124,7 @@ async function unzip(data) {
|
|
|
124
124
|
stderr: { toExitCode: true },
|
|
125
125
|
stdout: {
|
|
126
126
|
...(data.onStream && {
|
|
127
|
-
onData:
|
|
127
|
+
onData: (chunk) => {
|
|
128
128
|
if (data.onStream)
|
|
129
129
|
parseUnzipStream(chunk, data.onStream);
|
|
130
130
|
},
|