@datatruck/cli 0.19.0 → 0.20.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/Repository/DatatruckRepository.js +20 -12
- package/Task/GitTask.js +2 -2
- package/Task/MariadbTask.js +1 -0
- package/Task/SqlDumpTaskAbstract.js +1 -1
- 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 +4 -1
- package/utils/tar.js +98 -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}`);
|
|
@@ -98,13 +98,13 @@ class DatatruckRepository extends RepositoryAbstract_1.RepositoryAbstract {
|
|
|
98
98
|
const snapshotPath = (0, path_1.join)(this.config.outPath, snapshotName);
|
|
99
99
|
if (data.options.verbose)
|
|
100
100
|
(0, cli_1.logExec)(`Deleting ${snapshotPath}`);
|
|
101
|
-
if (await (0, fs_1.
|
|
101
|
+
if (await (0, fs_1.existsDir)(snapshotPath))
|
|
102
102
|
await (0, promises_1.rm)(snapshotPath, {
|
|
103
103
|
recursive: true,
|
|
104
104
|
});
|
|
105
105
|
}
|
|
106
106
|
async onSnapshots(data) {
|
|
107
|
-
if (!(await (0, fs_1.
|
|
107
|
+
if (!(await (0, fs_1.existsDir)(this.config.outPath)))
|
|
108
108
|
throw new Error(`Repository (${this.repository.name}) out path does not exist: ${this.config.outPath}`);
|
|
109
109
|
const snapshotNames = await (0, fs_1.readDir)(this.config.outPath);
|
|
110
110
|
const snapshots = [];
|
|
@@ -175,49 +175,56 @@ class DatatruckRepository extends RepositoryAbstract_1.RepositoryAbstract {
|
|
|
175
175
|
const configPacks = (data.packageConfig?.packs ?? []).map((p) => ({
|
|
176
176
|
...p,
|
|
177
177
|
compress: p.compress ?? data.packageConfig?.compress ?? this.config.compress,
|
|
178
|
-
includeResult: [],
|
|
179
178
|
}));
|
|
180
179
|
const defaultsPack = {
|
|
181
180
|
name: "defaults",
|
|
182
181
|
compress: data.packageConfig?.compress ?? this.config.compress,
|
|
183
182
|
include: [],
|
|
184
|
-
includeResult: [],
|
|
185
183
|
};
|
|
186
184
|
const packs = [...configPacks, defaultsPack];
|
|
185
|
+
const defaultsPackIndex = packs.findIndex((p) => p === defaultsPack);
|
|
186
|
+
const stream = (0, fs_1.createWriteStreamPool)({
|
|
187
|
+
path: await this.mkTmpDir("files"),
|
|
188
|
+
onStreamPath: (key) => `files-${key}.txt`,
|
|
189
|
+
});
|
|
187
190
|
await scanner.start(async (entry) => {
|
|
188
191
|
if (entry.dirent.isDirectory() &&
|
|
189
192
|
!(await (0, fs_1.isEmptyDir)((0, path_1.join)(sourcePath, entry.path))))
|
|
190
193
|
return false;
|
|
191
|
-
|
|
194
|
+
let packIndex = configPacks.findIndex((pack) => (0, string_1.checkPath)(entry.path, pack.include, pack.exclude));
|
|
195
|
+
if (packIndex === -1)
|
|
196
|
+
packIndex = defaultsPackIndex;
|
|
197
|
+
const pack = packs[packIndex];
|
|
192
198
|
if (pack.onePackByResult) {
|
|
193
199
|
const subname = (0, path_1.basename)(entry.path);
|
|
194
200
|
packs.push({
|
|
195
201
|
...pack,
|
|
196
202
|
name: pack.name ? `${pack.name}-${subname}` : subname,
|
|
197
|
-
includeResult: [entry.path],
|
|
198
203
|
});
|
|
204
|
+
packIndex = packs.length - 1;
|
|
199
205
|
}
|
|
200
|
-
|
|
201
|
-
pack.includeResult.push(entry.path);
|
|
202
|
-
}
|
|
206
|
+
stream.writeLine(packIndex, entry.path);
|
|
203
207
|
return true;
|
|
204
208
|
});
|
|
209
|
+
await stream.end();
|
|
205
210
|
let packIndex = 0;
|
|
206
211
|
for (const pack of packs) {
|
|
207
|
-
const packBasename = [`pack`,
|
|
212
|
+
const packBasename = [`pack`, packIndex.toString(), pack.name]
|
|
208
213
|
.filter((v) => typeof v === "string")
|
|
209
214
|
.map((v) => encodeURIComponent(v.toString().replace(/[\\/]/g, "-")))
|
|
210
215
|
.join("-");
|
|
211
216
|
const ext = pack.compress ? `.tar.gz` : `.tar`;
|
|
212
|
-
|
|
217
|
+
const includeList = stream.path(packIndex);
|
|
218
|
+
if (includeList)
|
|
213
219
|
await (0, tar_1.createTar)({
|
|
214
220
|
compress: pack.compress,
|
|
215
221
|
verbose: data.options.verbose,
|
|
216
|
-
|
|
222
|
+
includeList,
|
|
217
223
|
path: sourcePath,
|
|
218
224
|
output: (0, path_1.join)(outPath, packBasename) + ext,
|
|
219
225
|
onEntry: async (data) => await scanner.progress(pack.compress ? "Compressing" : "Packing", data.path),
|
|
220
226
|
});
|
|
227
|
+
packIndex++;
|
|
221
228
|
}
|
|
222
229
|
await scanner.end();
|
|
223
230
|
// Meta
|
|
@@ -316,6 +323,7 @@ class DatatruckRepository extends RepositoryAbstract_1.RepositoryAbstract {
|
|
|
316
323
|
await (0, tar_1.extractTar)({
|
|
317
324
|
input: tarFile,
|
|
318
325
|
output: restorePath,
|
|
326
|
+
uncompress: tarFile.endsWith(".tar.gz"),
|
|
319
327
|
verbose: data.options.verbose,
|
|
320
328
|
onEntry: async (data) => await scanner.progress(tarFile.endsWith(".tar.gz") ? "Extracting" : "Unpacking", data.path),
|
|
321
329
|
});
|
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@datatruck/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.20.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
|
@@ -11,7 +11,7 @@ export interface CreateTarOptions {
|
|
|
11
11
|
path: string;
|
|
12
12
|
verbose?: boolean;
|
|
13
13
|
output: string;
|
|
14
|
-
|
|
14
|
+
includeList: string;
|
|
15
15
|
compress?: boolean;
|
|
16
16
|
onEntry?: (entry: TarEntry) => void;
|
|
17
17
|
}
|
|
@@ -19,9 +19,12 @@ export interface ExtractOptions {
|
|
|
19
19
|
input: string;
|
|
20
20
|
output: string;
|
|
21
21
|
verbose?: boolean;
|
|
22
|
+
uncompress?: boolean;
|
|
22
23
|
total?: number;
|
|
23
24
|
onEntry?: (entry: TarEntry) => void;
|
|
24
25
|
}
|
|
26
|
+
export type TarVendor = "busybox" | "bsdtar" | "gnu";
|
|
27
|
+
export declare function getTarVendor(cache?: boolean, log?: boolean): Promise<TarVendor | null>;
|
|
25
28
|
export type ListTarOptions = {
|
|
26
29
|
input: string;
|
|
27
30
|
onEntry?: (entry: Pick<TarEntry, "path">) => void;
|
package/utils/tar.js
CHANGED
|
@@ -1,91 +1,126 @@
|
|
|
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.listTar = exports.getTarVendor = 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
|
+
let tarVendor;
|
|
11
|
+
async function getTarVendor(cache = true, log = false) {
|
|
12
|
+
if (cache && typeof tarVendor !== "undefined")
|
|
13
|
+
return tarVendor;
|
|
14
|
+
const p = await (0, process_1.exec)("tar", ["--help"], {}, {
|
|
15
|
+
log,
|
|
16
|
+
stdout: {
|
|
17
|
+
save: true,
|
|
18
|
+
},
|
|
19
|
+
});
|
|
20
|
+
const find = () => {
|
|
21
|
+
if (p.stdout.includes("BusyBox")) {
|
|
22
|
+
return "busybox";
|
|
23
|
+
}
|
|
24
|
+
else if (p.stdout.includes("bsdtar")) {
|
|
25
|
+
return "bsdtar";
|
|
26
|
+
}
|
|
27
|
+
else if (p.stdout.includes("GNU")) {
|
|
28
|
+
return "gnu";
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
return (tarVendor = find());
|
|
35
|
+
}
|
|
36
|
+
exports.getTarVendor = getTarVendor;
|
|
13
37
|
async function listTar(options) {
|
|
14
|
-
if (options.verbose)
|
|
15
|
-
(0, cli_1.logExec)("tar", ["-ztvf", options.input]);
|
|
16
38
|
let total = 0;
|
|
17
|
-
await
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
39
|
+
await (0, process_1.exec)("tar", ["-tf", toLocalPath(options.input), "--force-local"], {}, {
|
|
40
|
+
log: options.verbose,
|
|
41
|
+
stdout: {
|
|
42
|
+
parseLines: "skip-empty",
|
|
43
|
+
onData: (path) => {
|
|
44
|
+
options.onEntry?.({ path });
|
|
45
|
+
total++;
|
|
46
|
+
},
|
|
22
47
|
},
|
|
23
48
|
});
|
|
24
49
|
return total;
|
|
25
50
|
}
|
|
26
51
|
exports.listTar = listTar;
|
|
27
52
|
async function createTar(options) {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
options.compress ? "-czvf" : "-cvf",
|
|
31
|
-
options.output,
|
|
32
|
-
options.path,
|
|
33
|
-
]);
|
|
34
|
-
let total = options.include.length;
|
|
35
|
-
if (!total)
|
|
36
|
-
throw new Error("'include' option is empty");
|
|
53
|
+
const vendor = await getTarVendor(true, options.verbose);
|
|
54
|
+
const total = await (0, fs_1.countFileLines)(options.includeList);
|
|
37
55
|
let current = 0;
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
56
|
+
await (0, process_1.exec)("tar", [
|
|
57
|
+
"-C",
|
|
58
|
+
toLocalPath(options.path),
|
|
59
|
+
options.compress ? "-czvf" : "-cvf",
|
|
60
|
+
toLocalPath(options.output),
|
|
61
|
+
"-T",
|
|
62
|
+
toLocalPath(options.includeList),
|
|
63
|
+
"--ignore-failed-read",
|
|
64
|
+
"--force-local",
|
|
65
|
+
], {}, {
|
|
66
|
+
log: options.verbose,
|
|
67
|
+
stderr: { toExitCode: true },
|
|
68
|
+
stdout: {
|
|
69
|
+
parseLines: "skip-empty",
|
|
70
|
+
onData: (line) => {
|
|
71
|
+
current++;
|
|
72
|
+
const path = vendor === "bsdtar" ? line.slice(2) : line;
|
|
73
|
+
options.onEntry?.({
|
|
74
|
+
path,
|
|
75
|
+
progress: {
|
|
76
|
+
total,
|
|
77
|
+
current,
|
|
78
|
+
percent: (0, math_1.progressPercent)(total, current),
|
|
79
|
+
},
|
|
80
|
+
});
|
|
81
|
+
},
|
|
53
82
|
},
|
|
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
83
|
});
|
|
62
|
-
await progressPromise;
|
|
63
84
|
}
|
|
64
85
|
exports.createTar = createTar;
|
|
86
|
+
/**
|
|
87
|
+
* Fix `tar.exe: Option --force-local is not supported`
|
|
88
|
+
*/
|
|
89
|
+
function toLocalPath(path) {
|
|
90
|
+
return (0, os_1.platform)() === "win32" ? path.replace(/\\/g, "/") : path;
|
|
91
|
+
}
|
|
65
92
|
async function extractTar(options) {
|
|
66
93
|
let total = options.total ??
|
|
67
94
|
(await listTar({ input: options.input, verbose: options.verbose }));
|
|
68
|
-
if (options.verbose)
|
|
69
|
-
(0, cli_1.logExec)("tar", ["-xzvfp", options.input, "-C", options.output]);
|
|
95
|
+
if (options.verbose)
|
|
70
96
|
(0, cli_1.logExec)("mkdir", ["-p", options.output]);
|
|
71
|
-
}
|
|
72
|
-
let current = 0;
|
|
73
97
|
await (0, promises_1.mkdir)(options.output, { recursive: true });
|
|
74
98
|
await (0, fs_1.ensureEmptyDir)(options.output);
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
99
|
+
let current = 0;
|
|
100
|
+
await (0, process_1.exec)("tar", [
|
|
101
|
+
options.uncompress ? "-xzvpf" : "-xvpf",
|
|
102
|
+
toLocalPath(options.input),
|
|
103
|
+
"-C",
|
|
104
|
+
toLocalPath(options.output),
|
|
105
|
+
"--force-local",
|
|
106
|
+
], {}, {
|
|
107
|
+
log: options.verbose,
|
|
108
|
+
stderr: {
|
|
109
|
+
toExitCode: true,
|
|
110
|
+
},
|
|
111
|
+
stdout: {
|
|
112
|
+
parseLines: "skip-empty",
|
|
113
|
+
onData: (path) => {
|
|
114
|
+
current++;
|
|
115
|
+
options.onEntry?.({
|
|
116
|
+
path,
|
|
117
|
+
progress: {
|
|
118
|
+
total,
|
|
119
|
+
current,
|
|
120
|
+
percent: (0, math_1.progressPercent)(total, current),
|
|
121
|
+
},
|
|
122
|
+
});
|
|
123
|
+
},
|
|
89
124
|
},
|
|
90
125
|
});
|
|
91
126
|
}
|