@datatruck/cli 0.19.0 → 0.21.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Action/RestoreAction.js +1 -1
- package/JsonSchema/DefinitionEnum.d.ts +2 -1
- package/JsonSchema/DefinitionEnum.js +1 -0
- package/JsonSchema/JsonSchema.js +2 -0
- package/Repository/DatatruckRepository.d.ts +4 -3
- package/Repository/DatatruckRepository.js +27 -14
- package/Task/GitTask.js +2 -2
- package/Task/MariadbTask.js +1 -0
- package/Task/SqlDumpTaskAbstract.js +1 -1
- package/config.schema.json +53 -2
- package/package.json +2 -3
- package/utils/Git.js +1 -1
- package/utils/datatruck/paths.js +5 -1
- package/utils/fs.d.ts +20 -15
- package/utils/fs.js +84 -64
- package/utils/process.d.ts +1 -1
- package/utils/process.js +17 -8
- package/utils/string.d.ts +0 -2
- package/utils/string.js +1 -9
- package/utils/tar.d.ts +24 -2
- package/utils/tar.js +182 -63
package/Action/RestoreAction.js
CHANGED
|
@@ -149,7 +149,7 @@ class RestoreAction {
|
|
|
149
149
|
if (typeof pkg.restorePath !== "string")
|
|
150
150
|
throw new AppError_1.AppError("Restore path is not defined");
|
|
151
151
|
await (0, fs_1.mkdirIfNotExists)(pkg.restorePath);
|
|
152
|
-
if (!(await (0, fs_1.
|
|
152
|
+
if (!(await (0, fs_1.isEmptyDir)(pkg.restorePath)))
|
|
153
153
|
throw new AppError_1.AppError(`Restore path is not empty: ${pkg.restorePath}`);
|
|
154
154
|
if (this.options.verbose)
|
|
155
155
|
(0, cli_1.logExec)(`restorePath=${pkg.restorePath}`);
|
|
@@ -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 = {
|
|
@@ -11,17 +12,17 @@ export type MetaDataType = {
|
|
|
11
12
|
};
|
|
12
13
|
export type DatatruckRepositoryConfigType = {
|
|
13
14
|
outPath: string;
|
|
14
|
-
compress?: boolean;
|
|
15
|
+
compress?: boolean | CompressOptions;
|
|
15
16
|
};
|
|
16
17
|
type PackObject = {
|
|
17
18
|
name?: string;
|
|
18
|
-
compress?: boolean;
|
|
19
|
+
compress?: boolean | CompressOptions;
|
|
19
20
|
include: string[];
|
|
20
21
|
exclude?: string[];
|
|
21
22
|
onePackByResult?: boolean;
|
|
22
23
|
};
|
|
23
24
|
export type DatatruckPackageRepositoryConfigType = {
|
|
24
|
-
compress?: boolean;
|
|
25
|
+
compress?: boolean | CompressOptions;
|
|
25
26
|
packs?: PackObject[];
|
|
26
27
|
};
|
|
27
28
|
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" },
|
|
@@ -98,13 +103,13 @@ class DatatruckRepository extends RepositoryAbstract_1.RepositoryAbstract {
|
|
|
98
103
|
const snapshotPath = (0, path_1.join)(this.config.outPath, snapshotName);
|
|
99
104
|
if (data.options.verbose)
|
|
100
105
|
(0, cli_1.logExec)(`Deleting ${snapshotPath}`);
|
|
101
|
-
if (await (0, fs_1.
|
|
106
|
+
if (await (0, fs_1.existsDir)(snapshotPath))
|
|
102
107
|
await (0, promises_1.rm)(snapshotPath, {
|
|
103
108
|
recursive: true,
|
|
104
109
|
});
|
|
105
110
|
}
|
|
106
111
|
async onSnapshots(data) {
|
|
107
|
-
if (!(await (0, fs_1.
|
|
112
|
+
if (!(await (0, fs_1.existsDir)(this.config.outPath)))
|
|
108
113
|
throw new Error(`Repository (${this.repository.name}) out path does not exist: ${this.config.outPath}`);
|
|
109
114
|
const snapshotNames = await (0, fs_1.readDir)(this.config.outPath);
|
|
110
115
|
const snapshots = [];
|
|
@@ -175,49 +180,56 @@ class DatatruckRepository extends RepositoryAbstract_1.RepositoryAbstract {
|
|
|
175
180
|
const configPacks = (data.packageConfig?.packs ?? []).map((p) => ({
|
|
176
181
|
...p,
|
|
177
182
|
compress: p.compress ?? data.packageConfig?.compress ?? this.config.compress,
|
|
178
|
-
includeResult: [],
|
|
179
183
|
}));
|
|
180
184
|
const defaultsPack = {
|
|
181
185
|
name: "defaults",
|
|
182
186
|
compress: data.packageConfig?.compress ?? this.config.compress,
|
|
183
187
|
include: [],
|
|
184
|
-
includeResult: [],
|
|
185
188
|
};
|
|
186
189
|
const packs = [...configPacks, defaultsPack];
|
|
190
|
+
const defaultsPackIndex = packs.findIndex((p) => p === defaultsPack);
|
|
191
|
+
const stream = (0, fs_1.createWriteStreamPool)({
|
|
192
|
+
path: await this.mkTmpDir("files"),
|
|
193
|
+
onStreamPath: (key) => `files-${key}.txt`,
|
|
194
|
+
});
|
|
187
195
|
await scanner.start(async (entry) => {
|
|
188
196
|
if (entry.dirent.isDirectory() &&
|
|
189
197
|
!(await (0, fs_1.isEmptyDir)((0, path_1.join)(sourcePath, entry.path))))
|
|
190
198
|
return false;
|
|
191
|
-
|
|
199
|
+
let packIndex = configPacks.findIndex((pack) => (0, string_1.checkPath)(entry.path, pack.include, pack.exclude));
|
|
200
|
+
if (packIndex === -1)
|
|
201
|
+
packIndex = defaultsPackIndex;
|
|
202
|
+
const pack = packs[packIndex];
|
|
192
203
|
if (pack.onePackByResult) {
|
|
193
204
|
const subname = (0, path_1.basename)(entry.path);
|
|
194
205
|
packs.push({
|
|
195
206
|
...pack,
|
|
196
207
|
name: pack.name ? `${pack.name}-${subname}` : subname,
|
|
197
|
-
includeResult: [entry.path],
|
|
198
208
|
});
|
|
209
|
+
packIndex = packs.length - 1;
|
|
199
210
|
}
|
|
200
|
-
|
|
201
|
-
pack.includeResult.push(entry.path);
|
|
202
|
-
}
|
|
211
|
+
stream.writeLine(packIndex, entry.path);
|
|
203
212
|
return true;
|
|
204
213
|
});
|
|
214
|
+
await stream.end();
|
|
205
215
|
let packIndex = 0;
|
|
206
216
|
for (const pack of packs) {
|
|
207
|
-
const packBasename = [`pack`,
|
|
217
|
+
const packBasename = [`pack`, packIndex.toString(), pack.name]
|
|
208
218
|
.filter((v) => typeof v === "string")
|
|
209
219
|
.map((v) => encodeURIComponent(v.toString().replace(/[\\/]/g, "-")))
|
|
210
220
|
.join("-");
|
|
211
221
|
const ext = pack.compress ? `.tar.gz` : `.tar`;
|
|
212
|
-
|
|
222
|
+
const includeList = stream.path(packIndex);
|
|
223
|
+
if (includeList)
|
|
213
224
|
await (0, tar_1.createTar)({
|
|
214
225
|
compress: pack.compress,
|
|
215
226
|
verbose: data.options.verbose,
|
|
216
|
-
|
|
227
|
+
includeList,
|
|
217
228
|
path: sourcePath,
|
|
218
229
|
output: (0, path_1.join)(outPath, packBasename) + ext,
|
|
219
230
|
onEntry: async (data) => await scanner.progress(pack.compress ? "Compressing" : "Packing", data.path),
|
|
220
231
|
});
|
|
232
|
+
packIndex++;
|
|
221
233
|
}
|
|
222
234
|
await scanner.end();
|
|
223
235
|
// Meta
|
|
@@ -316,6 +328,7 @@ class DatatruckRepository extends RepositoryAbstract_1.RepositoryAbstract {
|
|
|
316
328
|
await (0, tar_1.extractTar)({
|
|
317
329
|
input: tarFile,
|
|
318
330
|
output: restorePath,
|
|
331
|
+
decompress: tarFile.endsWith(".tar.gz"),
|
|
319
332
|
verbose: data.options.verbose,
|
|
320
333
|
onEntry: async (data) => await scanner.progress(tarFile.endsWith(".tar.gz") ? "Extracting" : "Unpacking", data.path),
|
|
321
334
|
});
|
package/Task/GitTask.js
CHANGED
|
@@ -231,14 +231,14 @@ class GitTask extends TaskAbstract_1.TaskAbstract {
|
|
|
231
231
|
await incrementProgress();
|
|
232
232
|
// Config
|
|
233
233
|
const configPath = (0, path_1.join)(targetPath, "repo.config");
|
|
234
|
-
if (await (0, fs_1.
|
|
234
|
+
if (await (0, fs_1.existsFile)(configPath)) {
|
|
235
235
|
await (0, promises_1.copyFile)(configPath, (0, path_1.join)(restorePath, ".git", "config"));
|
|
236
236
|
await incrementProgress();
|
|
237
237
|
}
|
|
238
238
|
// ls-files
|
|
239
239
|
for (const name of ["untracked", "modified", "ignored"]) {
|
|
240
240
|
const sourcePath = (0, path_1.join)(targetPath, `repo.${name}`);
|
|
241
|
-
if (await (0, fs_1.
|
|
241
|
+
if (await (0, fs_1.existsDir)(sourcePath)) {
|
|
242
242
|
if (data.options.verbose)
|
|
243
243
|
(0, cli_1.logExec)(`Copying ${name} files to ${restorePath}`);
|
|
244
244
|
await (0, fs_1.cpy)({
|
package/Task/MariadbTask.js
CHANGED
|
@@ -115,7 +115,7 @@ class SqlDumpTaskAbstract extends TaskAbstract_1.TaskAbstract {
|
|
|
115
115
|
return true;
|
|
116
116
|
});
|
|
117
117
|
(0, assert_1.ok)(typeof outputPath === "string");
|
|
118
|
-
if (!(await (0, fs_1.
|
|
118
|
+
if (!(await (0, fs_1.existsDir)(outputPath)))
|
|
119
119
|
await (0, promises_1.mkdir)(outputPath, { recursive: true });
|
|
120
120
|
if (!this.config.oneFileByTable) {
|
|
121
121
|
const outPath = (0, path_1.join)(outputPath, serializeSqlFile({ database: this.config.database }));
|
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
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@datatruck/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.21.0",
|
|
4
4
|
"dependencies": {
|
|
5
5
|
"ajv": "^8.12.0",
|
|
6
6
|
"async": "^3.2.4",
|
|
@@ -13,8 +13,7 @@
|
|
|
13
13
|
"micromatch": "^4.0.5",
|
|
14
14
|
"pretty-bytes": "^5.6.0",
|
|
15
15
|
"sqlite": "^5.0.1",
|
|
16
|
-
"sqlite3": "^5.1.6"
|
|
17
|
-
"tar": "^6.2.0"
|
|
16
|
+
"sqlite3": "^5.1.6"
|
|
18
17
|
},
|
|
19
18
|
"optionalDependencies": {
|
|
20
19
|
"ts-node": "^10.9.1",
|
package/utils/Git.js
CHANGED
|
@@ -16,7 +16,7 @@ class Git {
|
|
|
16
16
|
}
|
|
17
17
|
async canBeInit(repo) {
|
|
18
18
|
return ((0, fs_1.isLocalDir)(repo) &&
|
|
19
|
-
(!(await (0, fs_1.
|
|
19
|
+
(!(await (0, fs_1.existsDir)(repo)) || !(await (0, fs_1.readDir)(repo)).length));
|
|
20
20
|
}
|
|
21
21
|
async clone(options) {
|
|
22
22
|
return await this.exec([
|
package/utils/datatruck/paths.js
CHANGED
|
@@ -9,7 +9,11 @@ async function parsePaths(values, options) {
|
|
|
9
9
|
paths.push(value);
|
|
10
10
|
}
|
|
11
11
|
else if (value.type === "spawn") {
|
|
12
|
-
const spawnResult = await (0, process_1.exec)(value.command, value.args, { cwd: options.cwd }, {
|
|
12
|
+
const spawnResult = await (0, process_1.exec)(value.command, value.args, { cwd: options.cwd }, {
|
|
13
|
+
log: options.verbose,
|
|
14
|
+
stderr: { save: true },
|
|
15
|
+
stdout: { save: true },
|
|
16
|
+
});
|
|
13
17
|
const spawnFiles = [spawnResult.stderr, spawnResult.stdout].flatMap((text) => text
|
|
14
18
|
.split(/\r?\n/)
|
|
15
19
|
.map((v) => v.trim())
|
package/utils/fs.d.ts
CHANGED
|
@@ -1,29 +1,20 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
2
|
/// <reference types="node" />
|
|
3
3
|
/// <reference types="node" />
|
|
4
|
-
/// <reference types="node" />
|
|
5
4
|
import { Progress } from "./progress";
|
|
6
5
|
import { Entry, Options } from "fast-glob";
|
|
7
|
-
import {
|
|
6
|
+
import { ReadStream, Stats } from "fs";
|
|
8
7
|
import { WriteStream } from "fs";
|
|
9
8
|
import { Interface } from "readline";
|
|
10
9
|
export declare const isWSLSystem: boolean;
|
|
11
10
|
export declare function isEmptyDir(path: string): Promise<boolean>;
|
|
12
|
-
type EntryObject = {
|
|
13
|
-
name: string;
|
|
14
|
-
path: string;
|
|
15
|
-
dirent: Dirent;
|
|
16
|
-
stats: Stats;
|
|
17
|
-
};
|
|
18
|
-
export declare function applyPermissions(baseDir: string, permissionsPath: string): Promise<void>;
|
|
19
|
-
export declare function pathIterator(stream: AsyncIterable<string | Buffer>): AsyncIterable<EntryObject>;
|
|
20
11
|
export declare function isLocalDir(path: string): boolean;
|
|
21
|
-
export declare function isDirEmpty(path: string): Promise<boolean>;
|
|
22
12
|
export declare function mkdirIfNotExists(path: string): Promise<string>;
|
|
23
13
|
export declare function ensureEmptyDir(path: string): Promise<void>;
|
|
14
|
+
export declare function safeStat(path: string): Promise<Stats | undefined>;
|
|
24
15
|
export declare function existsDir(path: string): Promise<boolean>;
|
|
16
|
+
export declare function existsFile(path: string): Promise<boolean>;
|
|
25
17
|
export declare function writeJSONFile<T = any>(path: string, json: T): Promise<void>;
|
|
26
|
-
export declare function readdirIfExists(path: string): Promise<string[]>;
|
|
27
18
|
export declare const parseFileExtensions: string[];
|
|
28
19
|
export declare function parseFile(path: string, jsKey?: string): Promise<any>;
|
|
29
20
|
export declare function parsePackageFile(): {
|
|
@@ -32,7 +23,6 @@ export declare function parsePackageFile(): {
|
|
|
32
23
|
description: string;
|
|
33
24
|
};
|
|
34
25
|
export declare function findFile(sourcePath: string, baseName: string, extensions: string[], errorMessage?: string): Promise<string>;
|
|
35
|
-
export declare function existsFile(path: string): Promise<boolean>;
|
|
36
26
|
export declare function parentTmpDir(): string;
|
|
37
27
|
export declare function sessionTmpDir(): string;
|
|
38
28
|
export declare function isTmpDir(path: string): boolean;
|
|
@@ -41,8 +31,6 @@ export declare function tmpDir(prefix: string, id?: string): string;
|
|
|
41
31
|
export declare function fastFolderSizeAsync(path: string): Promise<number>;
|
|
42
32
|
export declare function mkTmpDir(prefix: string, id?: string): Promise<string>;
|
|
43
33
|
export declare function readPartialFile(path: string, positions: [number, number?]): Promise<string>;
|
|
44
|
-
export declare function checkFile(path: string): Promise<boolean>;
|
|
45
|
-
export declare function checkDir(path: string): Promise<boolean>;
|
|
46
34
|
export declare function readDir(path: string, optional?: boolean): Promise<string[]>;
|
|
47
35
|
export declare function forEachFile(dirPath: string, cb: (path: string, dir: boolean) => void, includeDir?: boolean): Promise<void>;
|
|
48
36
|
/**
|
|
@@ -112,4 +100,21 @@ export declare function createFileScanner(options: {
|
|
|
112
100
|
end: () => Promise<void>;
|
|
113
101
|
start: (cb?: ((entry: Required<Entry>) => any) | undefined) => Promise<void>;
|
|
114
102
|
}>;
|
|
103
|
+
type StreamItem = {
|
|
104
|
+
key: string;
|
|
105
|
+
stream: WriteStream;
|
|
106
|
+
finished: boolean;
|
|
107
|
+
error?: Error;
|
|
108
|
+
written?: boolean;
|
|
109
|
+
};
|
|
110
|
+
export declare function createWriteStreamPool(options: {
|
|
111
|
+
path: string;
|
|
112
|
+
onStreamPath?: (key: string) => string;
|
|
113
|
+
}): {
|
|
114
|
+
pool: Record<string, StreamItem>;
|
|
115
|
+
path(key: string | number): string | undefined;
|
|
116
|
+
writeLine(key: string | number, v: string): boolean;
|
|
117
|
+
end(): Promise<void>;
|
|
118
|
+
};
|
|
119
|
+
export declare function countFileLines(path: string): Promise<number>;
|
|
115
120
|
export {};
|
package/utils/fs.js
CHANGED
|
@@ -3,7 +3,7 @@ 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.createFileScanner = exports.cpy = exports.isNotFoundError = exports.updateFileStats = exports.copyFileWithStreams = exports.waitForClose = exports.writeGitIgnoreList = exports.fastglobToGitIgnore = exports.forEachFile = exports.readDir = exports.
|
|
6
|
+
exports.countFileLines = exports.createWriteStreamPool = exports.createFileScanner = exports.cpy = exports.isNotFoundError = exports.updateFileStats = exports.copyFileWithStreams = exports.waitForClose = exports.writeGitIgnoreList = exports.fastglobToGitIgnore = exports.forEachFile = exports.readDir = exports.readPartialFile = exports.mkTmpDir = exports.fastFolderSizeAsync = exports.tmpDir = exports.rmTmpDir = exports.isTmpDir = exports.sessionTmpDir = exports.parentTmpDir = exports.findFile = exports.parsePackageFile = exports.parseFile = exports.parseFileExtensions = exports.writeJSONFile = exports.existsFile = exports.existsDir = exports.safeStat = exports.ensureEmptyDir = exports.mkdirIfNotExists = exports.isLocalDir = exports.isEmptyDir = exports.isWSLSystem = void 0;
|
|
7
7
|
const globalData_1 = __importDefault(require("../globalData"));
|
|
8
8
|
const math_1 = require("./math");
|
|
9
9
|
const path_1 = require("./path");
|
|
@@ -35,37 +35,13 @@ async function isEmptyDir(path) {
|
|
|
35
35
|
}
|
|
36
36
|
}
|
|
37
37
|
exports.isEmptyDir = isEmptyDir;
|
|
38
|
-
async function applyPermissions(baseDir, permissionsPath) {
|
|
39
|
-
const singleReader = (0, readline_1.createInterface)({
|
|
40
|
-
input: (0, fs_1.createReadStream)(permissionsPath),
|
|
41
|
-
});
|
|
42
|
-
for await (const line of singleReader) {
|
|
43
|
-
const [rpath, rawUid, rawGui, rawMode] = line.split(":");
|
|
44
|
-
const path = (0, path_2.join)(baseDir, rpath);
|
|
45
|
-
if (!path.startsWith(baseDir)) {
|
|
46
|
-
throw new Error(`Entry path is out of the base dir: (${path}, ${baseDir})`);
|
|
47
|
-
}
|
|
48
|
-
const uid = Number(rawUid);
|
|
49
|
-
const guid = Number(rawGui);
|
|
50
|
-
await (0, promises_1.chown)(path, uid, guid);
|
|
51
|
-
const mode = Number(rawMode);
|
|
52
|
-
await (0, promises_1.chmod)(path, mode);
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
exports.applyPermissions = applyPermissions;
|
|
56
38
|
function pathIterator(stream) {
|
|
57
39
|
return stream;
|
|
58
40
|
}
|
|
59
|
-
exports.pathIterator = pathIterator;
|
|
60
41
|
function isLocalDir(path) {
|
|
61
42
|
return /^[\/\.]|([A-Z]:)/i.test(path);
|
|
62
43
|
}
|
|
63
44
|
exports.isLocalDir = isLocalDir;
|
|
64
|
-
async function isDirEmpty(path) {
|
|
65
|
-
const files = await readDir(path);
|
|
66
|
-
return !files.length;
|
|
67
|
-
}
|
|
68
|
-
exports.isDirEmpty = isDirEmpty;
|
|
69
45
|
async function mkdirIfNotExists(path) {
|
|
70
46
|
await (0, promises_1.mkdir)(path, {
|
|
71
47
|
recursive: true,
|
|
@@ -74,28 +50,29 @@ async function mkdirIfNotExists(path) {
|
|
|
74
50
|
}
|
|
75
51
|
exports.mkdirIfNotExists = mkdirIfNotExists;
|
|
76
52
|
async function ensureEmptyDir(path) {
|
|
77
|
-
if (!(await
|
|
53
|
+
if (!(await isEmptyDir(path)))
|
|
78
54
|
throw new Error(`Dir is not empty: ${path}`);
|
|
79
55
|
}
|
|
80
56
|
exports.ensureEmptyDir = ensureEmptyDir;
|
|
81
|
-
async function
|
|
57
|
+
async function safeStat(path) {
|
|
82
58
|
try {
|
|
83
|
-
|
|
84
|
-
return info.isDirectory();
|
|
85
|
-
}
|
|
86
|
-
catch (e) {
|
|
87
|
-
return false;
|
|
59
|
+
return await (0, promises_1.stat)(path);
|
|
88
60
|
}
|
|
61
|
+
catch (e) { }
|
|
62
|
+
}
|
|
63
|
+
exports.safeStat = safeStat;
|
|
64
|
+
async function existsDir(path) {
|
|
65
|
+
return (await safeStat(path))?.isDirectory() ?? false;
|
|
89
66
|
}
|
|
90
67
|
exports.existsDir = existsDir;
|
|
68
|
+
async function existsFile(path) {
|
|
69
|
+
return (await safeStat(path))?.isFile() ?? false;
|
|
70
|
+
}
|
|
71
|
+
exports.existsFile = existsFile;
|
|
91
72
|
async function writeJSONFile(path, json) {
|
|
92
73
|
await (0, promises_1.writeFile)(path, JSON.stringify(json));
|
|
93
74
|
}
|
|
94
75
|
exports.writeJSONFile = writeJSONFile;
|
|
95
|
-
async function readdirIfExists(path) {
|
|
96
|
-
return await readDir(path, true);
|
|
97
|
-
}
|
|
98
|
-
exports.readdirIfExists = readdirIfExists;
|
|
99
76
|
exports.parseFileExtensions = ["json", "js", "ts", "yaml", "yml"];
|
|
100
77
|
async function parseFile(path, jsKey) {
|
|
101
78
|
if (!(0, path_3.isAbsolute)(path))
|
|
@@ -140,16 +117,6 @@ async function findFile(sourcePath, baseName, extensions, errorMessage = "Path n
|
|
|
140
117
|
return path;
|
|
141
118
|
}
|
|
142
119
|
exports.findFile = findFile;
|
|
143
|
-
async function existsFile(path) {
|
|
144
|
-
try {
|
|
145
|
-
const info = await (0, promises_1.stat)(path);
|
|
146
|
-
return info.isFile();
|
|
147
|
-
}
|
|
148
|
-
catch (e) {
|
|
149
|
-
return false;
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
exports.existsFile = existsFile;
|
|
153
120
|
function parentTmpDir() {
|
|
154
121
|
return (0, path_2.join)(globalData_1.default.tempDir, "datatruck-temp");
|
|
155
122
|
}
|
|
@@ -219,24 +186,6 @@ async function readPartialFile(path, positions) {
|
|
|
219
186
|
});
|
|
220
187
|
}
|
|
221
188
|
exports.readPartialFile = readPartialFile;
|
|
222
|
-
async function checkFile(path) {
|
|
223
|
-
try {
|
|
224
|
-
return (await (0, promises_1.stat)(path)).isFile();
|
|
225
|
-
}
|
|
226
|
-
catch (e) {
|
|
227
|
-
return false;
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
exports.checkFile = checkFile;
|
|
231
|
-
async function checkDir(path) {
|
|
232
|
-
try {
|
|
233
|
-
return (await (0, promises_1.stat)(path)).isDirectory();
|
|
234
|
-
}
|
|
235
|
-
catch (e) {
|
|
236
|
-
return false;
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
exports.checkDir = checkDir;
|
|
240
189
|
async function readDir(path, optional) {
|
|
241
190
|
try {
|
|
242
191
|
return await (0, promises_1.readdir)(path);
|
|
@@ -509,3 +458,74 @@ async function createFileScanner(options) {
|
|
|
509
458
|
return object;
|
|
510
459
|
}
|
|
511
460
|
exports.createFileScanner = createFileScanner;
|
|
461
|
+
function createWriteStreamPool(options) {
|
|
462
|
+
const pool = {};
|
|
463
|
+
const create = (key) => {
|
|
464
|
+
const item = {
|
|
465
|
+
key,
|
|
466
|
+
stream: (0, fs_2.createWriteStream)((0, path_2.join)(options.path, options.onStreamPath ? options.onStreamPath(key) : key)),
|
|
467
|
+
finished: false,
|
|
468
|
+
};
|
|
469
|
+
item.stream
|
|
470
|
+
.once("error", (error) => {
|
|
471
|
+
item.finished = true;
|
|
472
|
+
item.error = error;
|
|
473
|
+
})
|
|
474
|
+
.once("close", () => (item.finished = true));
|
|
475
|
+
return (pool[item.key] = item);
|
|
476
|
+
};
|
|
477
|
+
return {
|
|
478
|
+
pool,
|
|
479
|
+
path(key) {
|
|
480
|
+
const item = pool[key];
|
|
481
|
+
if (!item)
|
|
482
|
+
return;
|
|
483
|
+
if (typeof item.stream.path !== "string")
|
|
484
|
+
throw new Error(`Stream path is not defined: ${key}`);
|
|
485
|
+
return item.stream.path;
|
|
486
|
+
},
|
|
487
|
+
writeLine(key, v) {
|
|
488
|
+
const item = pool[key] || create(key.toString());
|
|
489
|
+
if (item.finished) {
|
|
490
|
+
return false;
|
|
491
|
+
}
|
|
492
|
+
else if (item.written) {
|
|
493
|
+
return item.stream.write(`\n${v}`);
|
|
494
|
+
}
|
|
495
|
+
else {
|
|
496
|
+
item.written = true;
|
|
497
|
+
return item.stream.write(`${v}`);
|
|
498
|
+
}
|
|
499
|
+
},
|
|
500
|
+
async end() {
|
|
501
|
+
const items = Object.values(pool);
|
|
502
|
+
for (const item of items)
|
|
503
|
+
if (!item.finished)
|
|
504
|
+
item.stream.end();
|
|
505
|
+
const itemWithErrors = items.filter((v) => v.error);
|
|
506
|
+
if (itemWithErrors.length) {
|
|
507
|
+
const keys = itemWithErrors.map((item) => item.key);
|
|
508
|
+
throw new AggregateError(itemWithErrors.map((item) => item.error), `Streams faileds: ${keys.join(", ")}`);
|
|
509
|
+
}
|
|
510
|
+
await Promise.all(items
|
|
511
|
+
.filter((item) => !item.finished)
|
|
512
|
+
.map((item) => waitForClose(item.stream)));
|
|
513
|
+
},
|
|
514
|
+
};
|
|
515
|
+
}
|
|
516
|
+
exports.createWriteStreamPool = createWriteStreamPool;
|
|
517
|
+
function countFileLines(path) {
|
|
518
|
+
let lines = 0;
|
|
519
|
+
const rl = (0, readline_1.createInterface)({
|
|
520
|
+
input: (0, fs_1.createReadStream)(path),
|
|
521
|
+
});
|
|
522
|
+
return new Promise((resolve, reject) => {
|
|
523
|
+
rl.on("line", (line) => {
|
|
524
|
+
if (!line.length)
|
|
525
|
+
lines++;
|
|
526
|
+
});
|
|
527
|
+
rl.on("close", () => resolve(lines));
|
|
528
|
+
rl.on("error", reject);
|
|
529
|
+
});
|
|
530
|
+
}
|
|
531
|
+
exports.countFileLines = countFileLines;
|
package/utils/process.d.ts
CHANGED
package/utils/process.js
CHANGED
|
@@ -188,7 +188,7 @@ async function exec(command, argv = [], options = null, settings = {}) {
|
|
|
188
188
|
toStderr: log.allToStderr,
|
|
189
189
|
});
|
|
190
190
|
}
|
|
191
|
-
if (typeof options?.cwd === "string" && !(await (0, fs_1.
|
|
191
|
+
if (typeof options?.cwd === "string" && !(await (0, fs_1.existsDir)(options.cwd)))
|
|
192
192
|
throw new Error(`Current working directory does not exist: ${options.cwd}`);
|
|
193
193
|
if (pipe?.stream instanceof fs_2.ReadStream && "onReadProgress" in pipe) {
|
|
194
194
|
const fileInfo = await (0, promises_1.stat)(pipe.stream.path);
|
|
@@ -225,13 +225,13 @@ async function exec(command, argv = [], options = null, settings = {}) {
|
|
|
225
225
|
if (spawnData.exitCode) {
|
|
226
226
|
let exitCodeError;
|
|
227
227
|
if (settings.stderr?.toExitCode) {
|
|
228
|
-
exitCodeError = new Error(`Exit code ${spawnData.exitCode}: ${spawnData.stderr
|
|
228
|
+
exitCodeError = new Error(`Exit code ${spawnData.exitCode}: ${command} ${argv.join(" ")} | ${spawnData.stderr
|
|
229
229
|
.split(/\r?\n/g)
|
|
230
230
|
.filter((v) => !!v.length)
|
|
231
231
|
.join(" | ")}`);
|
|
232
232
|
}
|
|
233
233
|
else {
|
|
234
|
-
exitCodeError = new Error(`Exit code
|
|
234
|
+
exitCodeError = new Error(`Exit code ${spawnData.exitCode}: ${command} ${argv.join(" ")}`);
|
|
235
235
|
}
|
|
236
236
|
const exitCodeErrorResult = settings.onExitCodeError?.(spawnData, exitCodeError);
|
|
237
237
|
if (exitCodeErrorResult instanceof Error) {
|
|
@@ -286,17 +286,24 @@ async function exec(command, argv = [], options = null, settings = {}) {
|
|
|
286
286
|
if (!p.stdout)
|
|
287
287
|
throw new Error(`stdout is not defined`);
|
|
288
288
|
const parseLines = settings.stdout?.parseLines;
|
|
289
|
-
const
|
|
289
|
+
const skipEmptyLines = parseLines === "skip-empty";
|
|
290
|
+
const onData = (inData) => {
|
|
291
|
+
let data = inData.toString();
|
|
292
|
+
if (parseLines) {
|
|
293
|
+
if (skipEmptyLines && !data.trim().length)
|
|
294
|
+
return;
|
|
295
|
+
data = `${inData}\n`;
|
|
296
|
+
}
|
|
290
297
|
if (log.stdout)
|
|
291
298
|
logExecStdout({
|
|
292
|
-
data
|
|
299
|
+
data,
|
|
293
300
|
stderr: log.allToStderr,
|
|
294
301
|
colorize: log.colorize,
|
|
295
302
|
});
|
|
296
303
|
if (settings.stdout?.save)
|
|
297
|
-
spawnData.stdout += data
|
|
304
|
+
spawnData.stdout += data;
|
|
298
305
|
if (settings.stdout?.onData)
|
|
299
|
-
settings.stdout.onData(
|
|
306
|
+
settings.stdout.onData(inData.toString());
|
|
300
307
|
};
|
|
301
308
|
if (parseLines) {
|
|
302
309
|
const rl = (0, readline_1.createInterface)({
|
|
@@ -305,7 +312,9 @@ async function exec(command, argv = [], options = null, settings = {}) {
|
|
|
305
312
|
rl.on("line", onData);
|
|
306
313
|
rl.on("close", tryFinish);
|
|
307
314
|
}
|
|
308
|
-
else
|
|
315
|
+
else if (log.stdout ||
|
|
316
|
+
settings.stdout?.save ||
|
|
317
|
+
settings.stdout?.onData) {
|
|
309
318
|
p.stdout.on("data", onData);
|
|
310
319
|
}
|
|
311
320
|
}
|
package/utils/string.d.ts
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
1
|
export declare function serialize(message: string, data?: Object): string;
|
|
2
|
-
export declare function ucfirst(value: string): string;
|
|
3
|
-
export declare function lcfirst(value: string): string;
|
|
4
2
|
export declare function snakeCase(value: string, char?: string): string;
|
|
5
3
|
export declare function render(subject: string, vars: Record<string, string | undefined>): string;
|
|
6
4
|
export declare function parseStringList(value: string, validValues?: string[]): string[];
|
package/utils/string.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.formatDateTime = exports.checkMatch = exports.checkPath = exports.makePathPatterns = exports.formatSeconds = exports.formatUri = exports.parseStringList = exports.render = exports.snakeCase = exports.
|
|
3
|
+
exports.formatDateTime = exports.checkMatch = exports.checkPath = exports.makePathPatterns = exports.formatSeconds = exports.formatUri = exports.parseStringList = exports.render = exports.snakeCase = exports.serialize = void 0;
|
|
4
4
|
const AppError_1 = require("../Error/AppError");
|
|
5
5
|
const micromatch_1 = require("micromatch");
|
|
6
6
|
function serialize(message, data) {
|
|
@@ -9,14 +9,6 @@ function serialize(message, data) {
|
|
|
9
9
|
return message;
|
|
10
10
|
}
|
|
11
11
|
exports.serialize = serialize;
|
|
12
|
-
function ucfirst(value) {
|
|
13
|
-
return value.charAt(0).toUpperCase() + value.slice(1);
|
|
14
|
-
}
|
|
15
|
-
exports.ucfirst = ucfirst;
|
|
16
|
-
function lcfirst(value) {
|
|
17
|
-
return value.charAt(0).toLowerCase() + value.slice(1);
|
|
18
|
-
}
|
|
19
|
-
exports.lcfirst = lcfirst;
|
|
20
12
|
function snakeCase(value, char = "_") {
|
|
21
13
|
return value.replace(/[A-Z]/g, (letter) => `${char}${letter.toLowerCase()}`);
|
|
22
14
|
}
|
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,26 +8,47 @@ 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
|
-
|
|
15
|
-
compress?: boolean;
|
|
31
|
+
includeList: string;
|
|
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;
|
|
39
|
+
decompress?: boolean | DecompressOptions;
|
|
22
40
|
total?: number;
|
|
23
41
|
onEntry?: (entry: TarEntry) => void;
|
|
24
42
|
}
|
|
43
|
+
export declare const compressDefinition: JSONSchema7;
|
|
44
|
+
export type TarVendor = "busybox" | "bsdtar" | "gnu";
|
|
45
|
+
export declare function getTarVendor(cache?: boolean, log?: boolean): Promise<TarVendor | null>;
|
|
25
46
|
export type ListTarOptions = {
|
|
26
47
|
input: string;
|
|
27
48
|
onEntry?: (entry: Pick<TarEntry, "path">) => void;
|
|
28
49
|
verbose?: boolean;
|
|
29
50
|
};
|
|
30
51
|
export declare function listTar(options: ListTarOptions): Promise<number>;
|
|
52
|
+
export declare function checkPigzLib(cache?: boolean): Promise<boolean>;
|
|
31
53
|
export declare function createTar(options: CreateTarOptions): Promise<void>;
|
|
32
54
|
export declare function extractTar(options: ExtractOptions): Promise<void>;
|
package/utils/tar.js
CHANGED
|
@@ -1,91 +1,210 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.extractTar = exports.createTar = exports.listTar = void 0;
|
|
3
|
+
exports.extractTar = exports.createTar = exports.checkPigzLib = exports.listTar = exports.getTarVendor = exports.compressDefinition = void 0;
|
|
7
4
|
const cli_1 = require("./cli");
|
|
8
5
|
const fs_1 = require("./fs");
|
|
9
6
|
const math_1 = require("./math");
|
|
10
|
-
const
|
|
7
|
+
const process_1 = require("./process");
|
|
11
8
|
const promises_1 = require("fs/promises");
|
|
12
|
-
const
|
|
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
|
+
};
|
|
27
|
+
let tarVendor;
|
|
28
|
+
async function getTarVendor(cache = true, log = false) {
|
|
29
|
+
if (cache && typeof tarVendor !== "undefined")
|
|
30
|
+
return tarVendor;
|
|
31
|
+
const p = await (0, process_1.exec)("tar", ["--help"], {}, {
|
|
32
|
+
log,
|
|
33
|
+
stdout: {
|
|
34
|
+
save: true,
|
|
35
|
+
},
|
|
36
|
+
});
|
|
37
|
+
const find = () => {
|
|
38
|
+
if (p.stdout.includes("BusyBox")) {
|
|
39
|
+
return "busybox";
|
|
40
|
+
}
|
|
41
|
+
else if (p.stdout.includes("bsdtar")) {
|
|
42
|
+
return "bsdtar";
|
|
43
|
+
}
|
|
44
|
+
else if (p.stdout.includes("GNU")) {
|
|
45
|
+
return "gnu";
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
return (tarVendor = find());
|
|
52
|
+
}
|
|
53
|
+
exports.getTarVendor = getTarVendor;
|
|
13
54
|
async function listTar(options) {
|
|
14
|
-
if (options.verbose)
|
|
15
|
-
(0, cli_1.logExec)("tar", ["-ztvf", options.input]);
|
|
16
55
|
let total = 0;
|
|
17
|
-
await
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
56
|
+
await (0, process_1.exec)("tar", ["-tf", toLocalPath(options.input), "--force-local"], {}, {
|
|
57
|
+
log: options.verbose,
|
|
58
|
+
stdout: {
|
|
59
|
+
parseLines: "skip-empty",
|
|
60
|
+
onData: (path) => {
|
|
61
|
+
options.onEntry?.({ path });
|
|
62
|
+
total++;
|
|
63
|
+
},
|
|
22
64
|
},
|
|
23
65
|
});
|
|
24
66
|
return total;
|
|
25
67
|
}
|
|
26
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
|
+
}
|
|
27
92
|
async function createTar(options) {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
let total = options.include.length;
|
|
35
|
-
if (!total)
|
|
36
|
-
throw new Error("'include' option is empty");
|
|
93
|
+
const vendor = await getTarVendor(true, options.verbose);
|
|
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
|
+
}));
|
|
37
99
|
let current = 0;
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
100
|
+
const env = {
|
|
101
|
+
...(compress?.cores === 1 &&
|
|
102
|
+
compress.level && {
|
|
103
|
+
GZIP_OPT: compress.level.toString(),
|
|
104
|
+
}),
|
|
105
|
+
};
|
|
106
|
+
await (0, process_1.exec)("tar", [
|
|
107
|
+
"-C",
|
|
108
|
+
toLocalPath(options.path),
|
|
109
|
+
compress?.cores === 1 ? "-czvf" : "-cvf",
|
|
110
|
+
toLocalPath(options.output),
|
|
111
|
+
"-T",
|
|
112
|
+
toLocalPath(options.includeList),
|
|
113
|
+
"--ignore-failed-read",
|
|
114
|
+
"--force-local",
|
|
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,
|
|
136
|
+
},
|
|
137
|
+
}, {
|
|
138
|
+
log: options.verbose ? { envNames: Object.keys(env) } : false,
|
|
139
|
+
stderr: { toExitCode: true },
|
|
140
|
+
stdout: {
|
|
141
|
+
parseLines: "skip-empty",
|
|
142
|
+
onData: (line) => {
|
|
143
|
+
current++;
|
|
144
|
+
const path = vendor === "bsdtar" ? line.slice(2) : line;
|
|
145
|
+
options.onEntry?.({
|
|
146
|
+
path,
|
|
147
|
+
progress: {
|
|
148
|
+
total,
|
|
149
|
+
current,
|
|
150
|
+
percent: (0, math_1.progressPercent)(total, current),
|
|
151
|
+
},
|
|
152
|
+
});
|
|
153
|
+
},
|
|
53
154
|
},
|
|
54
|
-
}, options.include);
|
|
55
|
-
const outStream = (0, fs_2.createWriteStream)(options.output);
|
|
56
|
-
await new Promise((resolve, reject) => {
|
|
57
|
-
inStream.on("error", reject);
|
|
58
|
-
outStream.on("error", reject);
|
|
59
|
-
inStream.pipe(outStream);
|
|
60
|
-
outStream.on("close", resolve);
|
|
61
155
|
});
|
|
62
|
-
await progressPromise;
|
|
63
156
|
}
|
|
64
157
|
exports.createTar = createTar;
|
|
158
|
+
/**
|
|
159
|
+
* Fix `tar.exe: Option --force-local is not supported`
|
|
160
|
+
*/
|
|
161
|
+
function toLocalPath(path) {
|
|
162
|
+
return (0, os_1.platform)() === "win32" ? path.replace(/\\/g, "/") : path;
|
|
163
|
+
}
|
|
65
164
|
async function extractTar(options) {
|
|
66
165
|
let total = options.total ??
|
|
67
166
|
(await listTar({ input: options.input, verbose: options.verbose }));
|
|
68
|
-
if (options.verbose)
|
|
69
|
-
(0, cli_1.logExec)("tar", ["-xzvfp", options.input, "-C", options.output]);
|
|
167
|
+
if (options.verbose)
|
|
70
168
|
(0, cli_1.logExec)("mkdir", ["-p", options.output]);
|
|
71
|
-
}
|
|
72
|
-
let current = 0;
|
|
73
169
|
await (0, promises_1.mkdir)(options.output, { recursive: true });
|
|
74
170
|
await (0, fs_1.ensureEmptyDir)(options.output);
|
|
75
|
-
await
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
171
|
+
const decompress = await ifX(options.decompress, async (decompress) => ({
|
|
172
|
+
...decompress,
|
|
173
|
+
cores: await resolveCores(decompress.cores),
|
|
174
|
+
}));
|
|
175
|
+
let current = 0;
|
|
176
|
+
await (0, process_1.exec)("tar", [
|
|
177
|
+
decompress?.cores === 1 ? "-xzvpf" : "-xvpf",
|
|
178
|
+
toLocalPath(options.input),
|
|
179
|
+
"-C",
|
|
180
|
+
toLocalPath(options.output),
|
|
181
|
+
"--force-local",
|
|
182
|
+
...(decompress && decompress.cores > 1
|
|
183
|
+
? ["-I", `"pigz -p ${decompress.cores}"`]
|
|
184
|
+
: []),
|
|
185
|
+
], {
|
|
186
|
+
...(decompress &&
|
|
187
|
+
decompress.cores > 1 && {
|
|
188
|
+
shell: true,
|
|
189
|
+
}),
|
|
190
|
+
}, {
|
|
191
|
+
log: options.verbose,
|
|
192
|
+
stderr: {
|
|
193
|
+
toExitCode: true,
|
|
194
|
+
},
|
|
195
|
+
stdout: {
|
|
196
|
+
parseLines: "skip-empty",
|
|
197
|
+
onData: (path) => {
|
|
198
|
+
current++;
|
|
199
|
+
options.onEntry?.({
|
|
200
|
+
path,
|
|
201
|
+
progress: {
|
|
202
|
+
total,
|
|
203
|
+
current,
|
|
204
|
+
percent: (0, math_1.progressPercent)(total, current),
|
|
205
|
+
},
|
|
206
|
+
});
|
|
207
|
+
},
|
|
89
208
|
},
|
|
90
209
|
});
|
|
91
210
|
}
|