@datatruck/cli 0.20.0 → 0.21.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/JsonSchema/DefinitionEnum.d.ts +2 -1
- package/JsonSchema/DefinitionEnum.js +1 -0
- package/JsonSchema/JsonSchema.js +2 -0
- package/Repository/DatatruckRepository.d.ts +7 -3
- package/Repository/DatatruckRepository.js +37 -14
- package/Task/MariadbTask.js +1 -1
- package/Task/MysqlDumpTask.d.ts +1 -1
- package/Task/MysqlDumpTask.js +5 -5
- package/Task/PostgresqlDumpTask.js +1 -1
- package/config.schema.json +53 -2
- package/package.json +1 -1
- package/utils/fs.d.ts +3 -1
- package/utils/fs.js +9 -2
- package/utils/tar.d.ts +21 -2
- package/utils/tar.js +128 -39
|
@@ -19,7 +19,8 @@ export declare enum DefinitionEnum {
|
|
|
19
19
|
sqlDumpTask = "sqldump-task",
|
|
20
20
|
stringListUtil = "stringlist-util",
|
|
21
21
|
prunePolicy = "prune-policy",
|
|
22
|
-
pathsObject = "paths-object"
|
|
22
|
+
pathsObject = "paths-object",
|
|
23
|
+
compressUtil = "compress-util"
|
|
23
24
|
}
|
|
24
25
|
export declare function makeRef(type: DefinitionEnum, subType?: string): {
|
|
25
26
|
$ref: string;
|
|
@@ -24,6 +24,7 @@ var DefinitionEnum;
|
|
|
24
24
|
DefinitionEnum["stringListUtil"] = "stringlist-util";
|
|
25
25
|
DefinitionEnum["prunePolicy"] = "prune-policy";
|
|
26
26
|
DefinitionEnum["pathsObject"] = "paths-object";
|
|
27
|
+
DefinitionEnum["compressUtil"] = "compress-util";
|
|
27
28
|
})(DefinitionEnum || (exports.DefinitionEnum = DefinitionEnum = {}));
|
|
28
29
|
function makeRef(type, subType) {
|
|
29
30
|
return {
|
package/JsonSchema/JsonSchema.js
CHANGED
|
@@ -17,6 +17,7 @@ const MysqlDumpTask_1 = require("../Task/MysqlDumpTask");
|
|
|
17
17
|
const PostgresqlDumpTask_1 = require("../Task/PostgresqlDumpTask");
|
|
18
18
|
const ScriptTask_1 = require("../Task/ScriptTask");
|
|
19
19
|
const SqlDumpTaskAbstract_1 = require("../Task/SqlDumpTaskAbstract");
|
|
20
|
+
const tar_1 = require("../utils/tar");
|
|
20
21
|
const DefinitionEnum_1 = require("./DefinitionEnum");
|
|
21
22
|
exports.definitions = {
|
|
22
23
|
[DefinitionEnum_1.DefinitionEnum.stringListUtil]: {
|
|
@@ -45,6 +46,7 @@ exports.definitions = {
|
|
|
45
46
|
[DefinitionEnum_1.DefinitionEnum.config]: Config_1.configDefinition,
|
|
46
47
|
[DefinitionEnum_1.DefinitionEnum.prunePolicy]: PrunePolicyConfig_1.prunePolicyConfigDefinition,
|
|
47
48
|
[DefinitionEnum_1.DefinitionEnum.pathsObject]: PackageConfig_1.pathsObjectDefinition,
|
|
49
|
+
[DefinitionEnum_1.DefinitionEnum.compressUtil]: tar_1.compressDefinition,
|
|
48
50
|
};
|
|
49
51
|
for (const key in exports.definitions) {
|
|
50
52
|
const schemaKey = key;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { CompressOptions } from "../utils/tar";
|
|
1
2
|
import { RepositoryAbstract, BackupDataType, InitDataType, RestoreDataType, SnapshotsDataType, SnapshotResultType, PruneDataType, CopyBackupType } from "./RepositoryAbstract";
|
|
2
3
|
import type { JSONSchema7 } from "json-schema";
|
|
3
4
|
export type MetaDataType = {
|
|
@@ -8,20 +9,23 @@ export type MetaDataType = {
|
|
|
8
9
|
tags: string[];
|
|
9
10
|
version: string;
|
|
10
11
|
size: number;
|
|
12
|
+
tarStats?: Record<string, {
|
|
13
|
+
files: number;
|
|
14
|
+
}>;
|
|
11
15
|
};
|
|
12
16
|
export type DatatruckRepositoryConfigType = {
|
|
13
17
|
outPath: string;
|
|
14
|
-
compress?: boolean;
|
|
18
|
+
compress?: boolean | CompressOptions;
|
|
15
19
|
};
|
|
16
20
|
type PackObject = {
|
|
17
21
|
name?: string;
|
|
18
|
-
compress?: boolean;
|
|
22
|
+
compress?: boolean | CompressOptions;
|
|
19
23
|
include: string[];
|
|
20
24
|
exclude?: string[];
|
|
21
25
|
onePackByResult?: boolean;
|
|
22
26
|
};
|
|
23
27
|
export type DatatruckPackageRepositoryConfigType = {
|
|
24
|
-
compress?: boolean;
|
|
28
|
+
compress?: boolean | CompressOptions;
|
|
25
29
|
packs?: PackObject[];
|
|
26
30
|
};
|
|
27
31
|
export declare const datatruckRepositoryName = "datatruck";
|
|
@@ -20,7 +20,9 @@ exports.datatruckRepositoryDefinition = {
|
|
|
20
20
|
additionalProperties: false,
|
|
21
21
|
properties: {
|
|
22
22
|
outPath: { type: "string" },
|
|
23
|
-
compress: {
|
|
23
|
+
compress: {
|
|
24
|
+
anyOf: [{ type: "boolean" }, (0, DefinitionEnum_1.makeRef)(DefinitionEnum_1.DefinitionEnum.compressUtil)],
|
|
25
|
+
},
|
|
24
26
|
},
|
|
25
27
|
};
|
|
26
28
|
exports.datatruckPackageRepositoryDefinition = {
|
|
@@ -28,7 +30,7 @@ exports.datatruckPackageRepositoryDefinition = {
|
|
|
28
30
|
additionalProperties: false,
|
|
29
31
|
properties: {
|
|
30
32
|
compress: {
|
|
31
|
-
type: "boolean",
|
|
33
|
+
anyOf: [{ type: "boolean" }, (0, DefinitionEnum_1.makeRef)(DefinitionEnum_1.DefinitionEnum.compressUtil)],
|
|
32
34
|
},
|
|
33
35
|
packs: {
|
|
34
36
|
type: "array",
|
|
@@ -38,6 +40,9 @@ exports.datatruckPackageRepositoryDefinition = {
|
|
|
38
40
|
required: ["include"],
|
|
39
41
|
properties: {
|
|
40
42
|
name: { type: "string" },
|
|
43
|
+
compress: {
|
|
44
|
+
anyOf: [{ type: "boolean" }, (0, DefinitionEnum_1.makeRef)(DefinitionEnum_1.DefinitionEnum.compressUtil)],
|
|
45
|
+
},
|
|
41
46
|
include: (0, DefinitionEnum_1.makeRef)(DefinitionEnum_1.DefinitionEnum.stringListUtil),
|
|
42
47
|
exclude: (0, DefinitionEnum_1.makeRef)(DefinitionEnum_1.DefinitionEnum.stringListUtil),
|
|
43
48
|
onePackByResult: { type: "boolean" },
|
|
@@ -208,22 +213,26 @@ class DatatruckRepository extends RepositoryAbstract_1.RepositoryAbstract {
|
|
|
208
213
|
});
|
|
209
214
|
await stream.end();
|
|
210
215
|
let packIndex = 0;
|
|
216
|
+
const tarStats = {};
|
|
211
217
|
for (const pack of packs) {
|
|
212
218
|
const packBasename = [`pack`, packIndex.toString(), pack.name]
|
|
213
219
|
.filter((v) => typeof v === "string")
|
|
214
220
|
.map((v) => encodeURIComponent(v.toString().replace(/[\\/]/g, "-")))
|
|
215
|
-
.join("-");
|
|
216
|
-
const ext = pack.compress ? `.tar.gz` : `.tar`;
|
|
221
|
+
.join("-") + (pack.compress ? `.tar.gz` : `.tar`);
|
|
217
222
|
const includeList = stream.path(packIndex);
|
|
218
|
-
if (includeList)
|
|
223
|
+
if (includeList) {
|
|
224
|
+
tarStats[packBasename] = {
|
|
225
|
+
files: stream.lines(packIndex),
|
|
226
|
+
};
|
|
219
227
|
await (0, tar_1.createTar)({
|
|
220
228
|
compress: pack.compress,
|
|
221
229
|
verbose: data.options.verbose,
|
|
222
230
|
includeList,
|
|
223
231
|
path: sourcePath,
|
|
224
|
-
output: (0, path_1.join)(outPath, packBasename)
|
|
232
|
+
output: (0, path_1.join)(outPath, packBasename),
|
|
225
233
|
onEntry: async (data) => await scanner.progress(pack.compress ? "Compressing" : "Packing", data.path),
|
|
226
234
|
});
|
|
235
|
+
}
|
|
227
236
|
packIndex++;
|
|
228
237
|
}
|
|
229
238
|
await scanner.end();
|
|
@@ -238,6 +247,7 @@ class DatatruckRepository extends RepositoryAbstract_1.RepositoryAbstract {
|
|
|
238
247
|
task: data.package.task?.name,
|
|
239
248
|
version: nodePkg.version,
|
|
240
249
|
size: await (0, fs_1.fastFolderSizeAsync)(outPath),
|
|
250
|
+
tarStats,
|
|
241
251
|
};
|
|
242
252
|
if (data.options.verbose)
|
|
243
253
|
(0, cli_1.logExec)(`Writing metadata into ${metaPath}`);
|
|
@@ -293,6 +303,8 @@ class DatatruckRepository extends RepositoryAbstract_1.RepositoryAbstract {
|
|
|
293
303
|
packageName: data.package.name,
|
|
294
304
|
});
|
|
295
305
|
const sourcePath = (0, path_1.join)(this.config.outPath, snapshotName);
|
|
306
|
+
const metaPath = (0, path_1.join)(sourcePath, "meta.json");
|
|
307
|
+
const meta = await DatatruckRepository.parseMetaData(metaPath);
|
|
296
308
|
const scanner = await (0, fs_1.createFileScanner)({
|
|
297
309
|
onProgress: data.onProgress,
|
|
298
310
|
glob: {
|
|
@@ -301,29 +313,40 @@ class DatatruckRepository extends RepositoryAbstract_1.RepositoryAbstract {
|
|
|
301
313
|
},
|
|
302
314
|
});
|
|
303
315
|
const tarFiles = [];
|
|
316
|
+
const tarStats = meta?.tarStats || {};
|
|
304
317
|
await scanner.start(async (entry) => {
|
|
305
318
|
const path = (0, path_1.join)(sourcePath, entry.name);
|
|
306
319
|
const isTar = entry.name.endsWith(".tar");
|
|
307
320
|
const isTarGz = entry.name.endsWith(".tar.gz");
|
|
308
321
|
if (isTar || isTarGz) {
|
|
309
322
|
tarFiles.push(path);
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
}
|
|
316
|
-
|
|
323
|
+
if (typeof tarStats[entry.name]?.files === "number") {
|
|
324
|
+
scanner.total += tarStats[entry.name].files;
|
|
325
|
+
}
|
|
326
|
+
else {
|
|
327
|
+
scanner.progress("Scanning", entry.name, false);
|
|
328
|
+
const selfTarStats = (tarStats[entry.name] = { files: 0 });
|
|
329
|
+
await (0, tar_1.listTar)({
|
|
330
|
+
input: path,
|
|
331
|
+
verbose: data.options.verbose,
|
|
332
|
+
onEntry: () => {
|
|
333
|
+
scanner.total++;
|
|
334
|
+
selfTarStats.files++;
|
|
335
|
+
},
|
|
336
|
+
});
|
|
337
|
+
}
|
|
317
338
|
}
|
|
318
339
|
return false;
|
|
319
340
|
});
|
|
320
341
|
if (data.options.verbose)
|
|
321
342
|
(0, cli_1.logExec)(`Unpacking files to ${restorePath}`);
|
|
322
343
|
for (const tarFile of tarFiles) {
|
|
344
|
+
const entryName = (0, path_1.basename)(tarFile);
|
|
323
345
|
await (0, tar_1.extractTar)({
|
|
346
|
+
total: tarStats[entryName].files,
|
|
324
347
|
input: tarFile,
|
|
325
348
|
output: restorePath,
|
|
326
|
-
|
|
349
|
+
decompress: tarFile.endsWith(".tar.gz"),
|
|
327
350
|
verbose: data.options.verbose,
|
|
328
351
|
onEntry: async (data) => await scanner.progress(tarFile.endsWith(".tar.gz") ? "Extracting" : "Unpacking", data.path),
|
|
329
352
|
});
|
package/Task/MariadbTask.js
CHANGED
|
@@ -258,7 +258,7 @@ class MariadbTask extends TaskAbstract_1.TaskAbstract {
|
|
|
258
258
|
});
|
|
259
259
|
await (0, tar_1.extractTar)({
|
|
260
260
|
input: (0, path_1.join)(restorePath, zipFile),
|
|
261
|
-
|
|
261
|
+
decompress: true,
|
|
262
262
|
output: restorePath,
|
|
263
263
|
verbose: this.verbose,
|
|
264
264
|
async onEntry(item) {
|
package/Task/MysqlDumpTask.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ export declare const mysqlDumpTaskName = "mysql-dump";
|
|
|
4
4
|
export type MysqlDumpTaskConfigType = {} & SqlDumpTaskConfigType;
|
|
5
5
|
export declare const mysqlDumpTaskDefinition: JSONSchema7;
|
|
6
6
|
export declare class MysqlDumpTask extends SqlDumpTaskAbstract<MysqlDumpTaskConfigType> {
|
|
7
|
-
buildConnectionArgs(database?:
|
|
7
|
+
buildConnectionArgs(database?: string): Promise<string[]>;
|
|
8
8
|
onDatabaseIsEmpty(name: string): Promise<boolean>;
|
|
9
9
|
onCreateDatabase(database: TargetDatabaseType): Promise<void>;
|
|
10
10
|
onExecQuery(query: string): Promise<import("../utils/process").ExecResultType>;
|
package/Task/MysqlDumpTask.js
CHANGED
|
@@ -13,14 +13,14 @@ exports.mysqlDumpTaskDefinition = {
|
|
|
13
13
|
allOf: [(0, DefinitionEnum_1.makeRef)(DefinitionEnum_1.DefinitionEnum.sqlDumpTask)],
|
|
14
14
|
};
|
|
15
15
|
class MysqlDumpTask extends SqlDumpTaskAbstract_1.SqlDumpTaskAbstract {
|
|
16
|
-
async buildConnectionArgs(database
|
|
16
|
+
async buildConnectionArgs(database) {
|
|
17
17
|
const password = await this.fetchPassword();
|
|
18
18
|
return [
|
|
19
19
|
`--host=${this.config.hostname}`,
|
|
20
20
|
...(this.config.port ? [`--port=${this.config.port}`] : []),
|
|
21
21
|
`--user=${this.config.username}`,
|
|
22
22
|
`--password=${password ?? ""}`,
|
|
23
|
-
...(database
|
|
23
|
+
...(database ? [database] : []),
|
|
24
24
|
];
|
|
25
25
|
}
|
|
26
26
|
async onDatabaseIsEmpty(name) {
|
|
@@ -78,7 +78,7 @@ class MysqlDumpTask extends SqlDumpTaskAbstract_1.SqlDumpTaskAbstract {
|
|
|
78
78
|
stream.on("error", reject);
|
|
79
79
|
}),
|
|
80
80
|
await (0, process_1.exec)("mysqldump", [
|
|
81
|
-
...(await this.buildConnectionArgs()),
|
|
81
|
+
...(await this.buildConnectionArgs(this.config.database)),
|
|
82
82
|
"--lock-tables=false",
|
|
83
83
|
"--skip-add-drop-table=false",
|
|
84
84
|
...tableNames,
|
|
@@ -120,7 +120,7 @@ class MysqlDumpTask extends SqlDumpTaskAbstract_1.SqlDumpTaskAbstract {
|
|
|
120
120
|
stream.on("error", reject);
|
|
121
121
|
}),
|
|
122
122
|
await (0, process_1.exec)("mysqldump", [
|
|
123
|
-
...(await this.buildConnectionArgs()),
|
|
123
|
+
...(await this.buildConnectionArgs(this.config.database)),
|
|
124
124
|
"--lock-tables=false",
|
|
125
125
|
"--routines",
|
|
126
126
|
"--events",
|
|
@@ -143,7 +143,7 @@ class MysqlDumpTask extends SqlDumpTaskAbstract_1.SqlDumpTaskAbstract {
|
|
|
143
143
|
]);
|
|
144
144
|
}
|
|
145
145
|
async onImport(path, database) {
|
|
146
|
-
await (0, process_1.exec)("mysql",
|
|
146
|
+
await (0, process_1.exec)("mysql", await this.buildConnectionArgs(database), null, {
|
|
147
147
|
pipe: {
|
|
148
148
|
stream: (0, fs_3.createReadStream)(path),
|
|
149
149
|
onReadProgress: (data) => {
|
|
@@ -81,7 +81,7 @@ class PostgresqlDumpTask extends SqlDumpTaskAbstract_1.SqlDumpTaskAbstract {
|
|
|
81
81
|
stream.on("error", reject);
|
|
82
82
|
}),
|
|
83
83
|
(0, process_1.exec)("pg_dump", [
|
|
84
|
-
...(await this.buildConnectionArgs()),
|
|
84
|
+
...(await this.buildConnectionArgs(this.config.database)),
|
|
85
85
|
...(tableNames?.flatMap((v) => ["-t", v]) ?? []),
|
|
86
86
|
], null, {
|
|
87
87
|
pipe: { stream, onWriteProgress: onProgress },
|
package/config.schema.json
CHANGED
|
@@ -449,7 +449,14 @@
|
|
|
449
449
|
"type": "string"
|
|
450
450
|
},
|
|
451
451
|
"compress": {
|
|
452
|
-
"
|
|
452
|
+
"anyOf": [
|
|
453
|
+
{
|
|
454
|
+
"type": "boolean"
|
|
455
|
+
},
|
|
456
|
+
{
|
|
457
|
+
"$ref": "#/definitions/compress-util"
|
|
458
|
+
}
|
|
459
|
+
]
|
|
453
460
|
}
|
|
454
461
|
}
|
|
455
462
|
},
|
|
@@ -458,7 +465,14 @@
|
|
|
458
465
|
"additionalProperties": false,
|
|
459
466
|
"properties": {
|
|
460
467
|
"compress": {
|
|
461
|
-
"
|
|
468
|
+
"anyOf": [
|
|
469
|
+
{
|
|
470
|
+
"type": "boolean"
|
|
471
|
+
},
|
|
472
|
+
{
|
|
473
|
+
"$ref": "#/definitions/compress-util"
|
|
474
|
+
}
|
|
475
|
+
]
|
|
462
476
|
},
|
|
463
477
|
"packs": {
|
|
464
478
|
"type": "array",
|
|
@@ -472,6 +486,16 @@
|
|
|
472
486
|
"name": {
|
|
473
487
|
"type": "string"
|
|
474
488
|
},
|
|
489
|
+
"compress": {
|
|
490
|
+
"anyOf": [
|
|
491
|
+
{
|
|
492
|
+
"type": "boolean"
|
|
493
|
+
},
|
|
494
|
+
{
|
|
495
|
+
"$ref": "#/definitions/compress-util"
|
|
496
|
+
}
|
|
497
|
+
]
|
|
498
|
+
},
|
|
475
499
|
"include": {
|
|
476
500
|
"$ref": "#/definitions/stringlist-util"
|
|
477
501
|
},
|
|
@@ -975,6 +999,33 @@
|
|
|
975
999
|
}
|
|
976
1000
|
]
|
|
977
1001
|
},
|
|
1002
|
+
"compress-util": {
|
|
1003
|
+
"type": "object",
|
|
1004
|
+
"additionalProperties": false,
|
|
1005
|
+
"properties": {
|
|
1006
|
+
"level": {
|
|
1007
|
+
"type": "integer"
|
|
1008
|
+
},
|
|
1009
|
+
"cores": {
|
|
1010
|
+
"anyOf": [
|
|
1011
|
+
{
|
|
1012
|
+
"type": "integer"
|
|
1013
|
+
},
|
|
1014
|
+
{
|
|
1015
|
+
"type": "object",
|
|
1016
|
+
"required": [
|
|
1017
|
+
"percent"
|
|
1018
|
+
],
|
|
1019
|
+
"properties": {
|
|
1020
|
+
"percent": {
|
|
1021
|
+
"type": "integer"
|
|
1022
|
+
}
|
|
1023
|
+
}
|
|
1024
|
+
}
|
|
1025
|
+
]
|
|
1026
|
+
}
|
|
1027
|
+
}
|
|
1028
|
+
},
|
|
978
1029
|
"script-task_step": {
|
|
979
1030
|
"type": "object",
|
|
980
1031
|
"required": [
|
package/package.json
CHANGED
package/utils/fs.d.ts
CHANGED
|
@@ -96,12 +96,13 @@ export declare function createFileScanner(options: {
|
|
|
96
96
|
disposed: boolean;
|
|
97
97
|
total: number;
|
|
98
98
|
current: number;
|
|
99
|
-
progress: (description: string, path?: string) => Promise<void>;
|
|
99
|
+
progress: (description: string, path?: string, increment?: boolean) => Promise<void>;
|
|
100
100
|
end: () => Promise<void>;
|
|
101
101
|
start: (cb?: ((entry: Required<Entry>) => any) | undefined) => Promise<void>;
|
|
102
102
|
}>;
|
|
103
103
|
type StreamItem = {
|
|
104
104
|
key: string;
|
|
105
|
+
lines: number;
|
|
105
106
|
stream: WriteStream;
|
|
106
107
|
finished: boolean;
|
|
107
108
|
error?: Error;
|
|
@@ -113,6 +114,7 @@ export declare function createWriteStreamPool(options: {
|
|
|
113
114
|
}): {
|
|
114
115
|
pool: Record<string, StreamItem>;
|
|
115
116
|
path(key: string | number): string | undefined;
|
|
117
|
+
lines(key: string | number): number;
|
|
116
118
|
writeLine(key: string | number, v: string): boolean;
|
|
117
119
|
end(): Promise<void>;
|
|
118
120
|
};
|
package/utils/fs.js
CHANGED
|
@@ -410,10 +410,10 @@ async function createFileScanner(options) {
|
|
|
410
410
|
disposed: false,
|
|
411
411
|
total: 0,
|
|
412
412
|
current: 0,
|
|
413
|
-
progress: async (description, path) => {
|
|
413
|
+
progress: async (description, path, increment = true) => {
|
|
414
414
|
if (object.disposed)
|
|
415
415
|
return;
|
|
416
|
-
if (path)
|
|
416
|
+
if (path && increment)
|
|
417
417
|
object.current++;
|
|
418
418
|
await options.onProgress({
|
|
419
419
|
relative: {
|
|
@@ -463,6 +463,7 @@ function createWriteStreamPool(options) {
|
|
|
463
463
|
const create = (key) => {
|
|
464
464
|
const item = {
|
|
465
465
|
key,
|
|
466
|
+
lines: 0,
|
|
466
467
|
stream: (0, fs_2.createWriteStream)((0, path_2.join)(options.path, options.onStreamPath ? options.onStreamPath(key) : key)),
|
|
467
468
|
finished: false,
|
|
468
469
|
};
|
|
@@ -484,16 +485,22 @@ function createWriteStreamPool(options) {
|
|
|
484
485
|
throw new Error(`Stream path is not defined: ${key}`);
|
|
485
486
|
return item.stream.path;
|
|
486
487
|
},
|
|
488
|
+
lines(key) {
|
|
489
|
+
const item = pool[key];
|
|
490
|
+
return item?.lines;
|
|
491
|
+
},
|
|
487
492
|
writeLine(key, v) {
|
|
488
493
|
const item = pool[key] || create(key.toString());
|
|
489
494
|
if (item.finished) {
|
|
490
495
|
return false;
|
|
491
496
|
}
|
|
492
497
|
else if (item.written) {
|
|
498
|
+
item.lines++;
|
|
493
499
|
return item.stream.write(`\n${v}`);
|
|
494
500
|
}
|
|
495
501
|
else {
|
|
496
502
|
item.written = true;
|
|
503
|
+
item.lines++;
|
|
497
504
|
return item.stream.write(`${v}`);
|
|
498
505
|
}
|
|
499
506
|
},
|
package/utils/tar.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { JSONSchema7 } from "json-schema";
|
|
1
2
|
export type Progress = {
|
|
2
3
|
percent: number;
|
|
3
4
|
current: number;
|
|
@@ -7,22 +8,39 @@ export type TarEntry = {
|
|
|
7
8
|
path: string;
|
|
8
9
|
progress: Progress;
|
|
9
10
|
};
|
|
11
|
+
export type CoresOptions = number | {
|
|
12
|
+
percent: number;
|
|
13
|
+
};
|
|
14
|
+
export type CompressOptions = {
|
|
15
|
+
level?: number;
|
|
16
|
+
/**
|
|
17
|
+
* @default {percent:50}
|
|
18
|
+
*/
|
|
19
|
+
cores?: CoresOptions;
|
|
20
|
+
};
|
|
21
|
+
export type DecompressOptions = {
|
|
22
|
+
/**
|
|
23
|
+
* @default {percent:50}
|
|
24
|
+
*/
|
|
25
|
+
cores?: CoresOptions;
|
|
26
|
+
};
|
|
10
27
|
export interface CreateTarOptions {
|
|
11
28
|
path: string;
|
|
12
29
|
verbose?: boolean;
|
|
13
30
|
output: string;
|
|
14
31
|
includeList: string;
|
|
15
|
-
compress?: boolean;
|
|
32
|
+
compress?: boolean | CompressOptions;
|
|
16
33
|
onEntry?: (entry: TarEntry) => void;
|
|
17
34
|
}
|
|
18
35
|
export interface ExtractOptions {
|
|
19
36
|
input: string;
|
|
20
37
|
output: string;
|
|
21
38
|
verbose?: boolean;
|
|
22
|
-
|
|
39
|
+
decompress?: boolean | DecompressOptions;
|
|
23
40
|
total?: number;
|
|
24
41
|
onEntry?: (entry: TarEntry) => void;
|
|
25
42
|
}
|
|
43
|
+
export declare const compressDefinition: JSONSchema7;
|
|
26
44
|
export type TarVendor = "busybox" | "bsdtar" | "gnu";
|
|
27
45
|
export declare function getTarVendor(cache?: boolean, log?: boolean): Promise<TarVendor | null>;
|
|
28
46
|
export type ListTarOptions = {
|
|
@@ -31,5 +49,6 @@ export type ListTarOptions = {
|
|
|
31
49
|
verbose?: boolean;
|
|
32
50
|
};
|
|
33
51
|
export declare function listTar(options: ListTarOptions): Promise<number>;
|
|
52
|
+
export declare function checkPigzLib(cache?: boolean): Promise<boolean>;
|
|
34
53
|
export declare function createTar(options: CreateTarOptions): Promise<void>;
|
|
35
54
|
export declare function extractTar(options: ExtractOptions): Promise<void>;
|
package/utils/tar.js
CHANGED
|
@@ -1,12 +1,29 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.extractTar = exports.createTar = exports.listTar = exports.getTarVendor = void 0;
|
|
3
|
+
exports.extractTar = exports.createTar = exports.checkPigzLib = exports.listTar = exports.getTarVendor = exports.compressDefinition = void 0;
|
|
4
4
|
const cli_1 = require("./cli");
|
|
5
5
|
const fs_1 = require("./fs");
|
|
6
6
|
const math_1 = require("./math");
|
|
7
7
|
const process_1 = require("./process");
|
|
8
8
|
const promises_1 = require("fs/promises");
|
|
9
9
|
const os_1 = require("os");
|
|
10
|
+
exports.compressDefinition = {
|
|
11
|
+
type: "object",
|
|
12
|
+
additionalProperties: false,
|
|
13
|
+
properties: {
|
|
14
|
+
level: { type: "integer" },
|
|
15
|
+
cores: {
|
|
16
|
+
anyOf: [
|
|
17
|
+
{ type: "integer" },
|
|
18
|
+
{
|
|
19
|
+
type: "object",
|
|
20
|
+
required: ["percent"],
|
|
21
|
+
properties: { percent: { type: "integer" } },
|
|
22
|
+
},
|
|
23
|
+
],
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
};
|
|
10
27
|
let tarVendor;
|
|
11
28
|
async function getTarVendor(cache = true, log = false) {
|
|
12
29
|
if (cache && typeof tarVendor !== "undefined")
|
|
@@ -49,37 +66,94 @@ async function listTar(options) {
|
|
|
49
66
|
return total;
|
|
50
67
|
}
|
|
51
68
|
exports.listTar = listTar;
|
|
69
|
+
let pigzLib;
|
|
70
|
+
async function checkPigzLib(cache = true) {
|
|
71
|
+
if (cache && pigzLib !== undefined)
|
|
72
|
+
return pigzLib;
|
|
73
|
+
try {
|
|
74
|
+
return !(await (0, process_1.exec)("pigz", ["-V"])).exitCode;
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
exports.checkPigzLib = checkPigzLib;
|
|
81
|
+
async function resolveCores(input) {
|
|
82
|
+
if (!(await checkPigzLib()))
|
|
83
|
+
return 1;
|
|
84
|
+
const total = (0, os_1.cpus)().length;
|
|
85
|
+
return Math.min(total, typeof input === "number"
|
|
86
|
+
? input
|
|
87
|
+
: Math.max(0, Math.round(((input?.percent || 50) * total) / 100)));
|
|
88
|
+
}
|
|
89
|
+
async function ifX(input, cb) {
|
|
90
|
+
return input ? await cb((input === true ? {} : input)) : undefined;
|
|
91
|
+
}
|
|
52
92
|
async function createTar(options) {
|
|
53
93
|
const vendor = await getTarVendor(true, options.verbose);
|
|
54
94
|
const total = await (0, fs_1.countFileLines)(options.includeList);
|
|
95
|
+
const compress = await ifX(options.compress, async (compress) => ({
|
|
96
|
+
...compress,
|
|
97
|
+
cores: await resolveCores(compress.cores),
|
|
98
|
+
}));
|
|
55
99
|
let current = 0;
|
|
100
|
+
const env = {
|
|
101
|
+
...(compress?.cores === 1 &&
|
|
102
|
+
compress.level && {
|
|
103
|
+
GZIP_OPT: compress.level.toString(),
|
|
104
|
+
}),
|
|
105
|
+
};
|
|
56
106
|
await (0, process_1.exec)("tar", [
|
|
57
107
|
"-C",
|
|
58
108
|
toLocalPath(options.path),
|
|
59
|
-
|
|
109
|
+
compress?.cores === 1 ? "-czvf" : "-cvf",
|
|
60
110
|
toLocalPath(options.output),
|
|
61
111
|
"-T",
|
|
62
112
|
toLocalPath(options.includeList),
|
|
63
113
|
"--ignore-failed-read",
|
|
64
114
|
"--force-local",
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
115
|
+
...(compress && compress.cores > 1
|
|
116
|
+
? [
|
|
117
|
+
"-I",
|
|
118
|
+
`"${[
|
|
119
|
+
"pigz",
|
|
120
|
+
"-r",
|
|
121
|
+
!!compress.level && `-${compress.level}`,
|
|
122
|
+
`-p ${compress.cores}`,
|
|
123
|
+
]
|
|
124
|
+
.filter(Boolean)
|
|
125
|
+
.join(" ")}"`,
|
|
126
|
+
]
|
|
127
|
+
: []),
|
|
128
|
+
], {
|
|
129
|
+
...(compress &&
|
|
130
|
+
compress.cores > 1 && {
|
|
131
|
+
shell: true,
|
|
132
|
+
}),
|
|
133
|
+
env: {
|
|
134
|
+
...process.env,
|
|
135
|
+
...env,
|
|
82
136
|
},
|
|
137
|
+
}, {
|
|
138
|
+
log: options.verbose ? { envNames: Object.keys(env) } : false,
|
|
139
|
+
stderr: { toExitCode: true },
|
|
140
|
+
stdout: options.onEntry
|
|
141
|
+
? {
|
|
142
|
+
parseLines: "skip-empty",
|
|
143
|
+
onData: (line) => {
|
|
144
|
+
current++;
|
|
145
|
+
const path = vendor === "bsdtar" ? line.slice(2) : line;
|
|
146
|
+
options.onEntry?.({
|
|
147
|
+
path,
|
|
148
|
+
progress: {
|
|
149
|
+
total,
|
|
150
|
+
current,
|
|
151
|
+
percent: (0, math_1.progressPercent)(total, current),
|
|
152
|
+
},
|
|
153
|
+
});
|
|
154
|
+
},
|
|
155
|
+
}
|
|
156
|
+
: undefined,
|
|
83
157
|
});
|
|
84
158
|
}
|
|
85
159
|
exports.createTar = createTar;
|
|
@@ -92,36 +166,51 @@ function toLocalPath(path) {
|
|
|
92
166
|
async function extractTar(options) {
|
|
93
167
|
let total = options.total ??
|
|
94
168
|
(await listTar({ input: options.input, verbose: options.verbose }));
|
|
95
|
-
if (options.
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
169
|
+
if (!(await (0, fs_1.existsDir)(options.output))) {
|
|
170
|
+
if (options.verbose)
|
|
171
|
+
(0, cli_1.logExec)("mkdir", ["-p", options.output]);
|
|
172
|
+
await (0, promises_1.mkdir)(options.output, { recursive: true });
|
|
173
|
+
}
|
|
174
|
+
const decompress = await ifX(options.decompress, async (decompress) => ({
|
|
175
|
+
...decompress,
|
|
176
|
+
cores: await resolveCores(decompress.cores),
|
|
177
|
+
}));
|
|
99
178
|
let current = 0;
|
|
100
179
|
await (0, process_1.exec)("tar", [
|
|
101
|
-
|
|
180
|
+
decompress?.cores === 1 ? "-xzvpf" : "-xvpf",
|
|
102
181
|
toLocalPath(options.input),
|
|
103
182
|
"-C",
|
|
104
183
|
toLocalPath(options.output),
|
|
105
184
|
"--force-local",
|
|
106
|
-
|
|
185
|
+
...(decompress && decompress.cores > 1
|
|
186
|
+
? ["-I", `"pigz -p ${decompress.cores}"`]
|
|
187
|
+
: []),
|
|
188
|
+
], {
|
|
189
|
+
...(decompress &&
|
|
190
|
+
decompress.cores > 1 && {
|
|
191
|
+
shell: true,
|
|
192
|
+
}),
|
|
193
|
+
}, {
|
|
107
194
|
log: options.verbose,
|
|
108
195
|
stderr: {
|
|
109
196
|
toExitCode: true,
|
|
110
197
|
},
|
|
111
|
-
stdout:
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
198
|
+
stdout: options.onEntry
|
|
199
|
+
? {
|
|
200
|
+
parseLines: "skip-empty",
|
|
201
|
+
onData: (path) => {
|
|
202
|
+
current++;
|
|
203
|
+
options.onEntry?.({
|
|
204
|
+
path,
|
|
205
|
+
progress: {
|
|
206
|
+
total,
|
|
207
|
+
current,
|
|
208
|
+
percent: (0, math_1.progressPercent)(total, current),
|
|
209
|
+
},
|
|
210
|
+
});
|
|
211
|
+
},
|
|
212
|
+
}
|
|
213
|
+
: undefined,
|
|
125
214
|
});
|
|
126
215
|
}
|
|
127
216
|
exports.extractTar = extractTar;
|