@datatruck/cli 0.13.1 → 0.16.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/BackupAction.js +7 -19
- package/Action/RestoreAction.js +4 -12
- package/Command/BackupCommand.js +2 -1
- package/Command/BackupSessionsCommand.js +1 -0
- package/Command/RestoreCommand.js +1 -1
- package/Command/RestoreSessionsCommand.js +1 -0
- package/Entity/StateEntityAbstract.d.ts +2 -5
- package/Error/AppError.d.ts +1 -0
- package/Error/AppError.js +4 -0
- package/Repository/DatatruckRepository.d.ts +2 -0
- package/Repository/DatatruckRepository.js +234 -123
- package/Repository/RepositoryAbstract.d.ts +4 -10
- package/Repository/ResticRepository.js +34 -17
- package/SessionDriver/ConsoleSessionDriver.d.ts +2 -7
- package/SessionDriver/ConsoleSessionDriver.js +51 -24
- package/SessionDriver/SqliteSessionDriver.js +5 -0
- package/SessionManager/BackupSessionManager.d.ts +12 -11
- package/SessionManager/BackupSessionManager.js +20 -5
- package/SessionManager/RestoreSessionManager.d.ts +14 -11
- package/SessionManager/RestoreSessionManager.js +20 -5
- package/SessionManager/SessionManagerAbstract.d.ts +18 -0
- package/SessionManager/SessionManagerAbstract.js +32 -0
- package/Task/GitTask.js +22 -14
- package/Task/MariadbTask.js +9 -4
- package/Task/MysqlDumpTask.d.ts +3 -1
- package/Task/MysqlDumpTask.js +5 -2
- package/Task/PostgresqlDumpTask.d.ts +3 -1
- package/Task/PostgresqlDumpTask.js +2 -2
- package/Task/SqlDumpTaskAbstract.d.ts +3 -1
- package/Task/SqlDumpTaskAbstract.js +55 -13
- package/Task/TaskAbstract.d.ts +3 -9
- package/cli.js +1 -1
- package/config.schema.json +3 -0
- package/migrations/001-initial.sql +6 -30
- package/package.json +1 -1
- package/util/cli-util.d.ts +1 -1
- package/util/cli-util.js +17 -2
- package/util/fs-util.d.ts +25 -21
- package/util/fs-util.js +60 -93
- package/util/math-util.js +2 -0
- package/util/process-util.d.ts +4 -0
- package/util/process-util.js +31 -4
- package/util/progress.d.ts +12 -0
- package/util/progress.js +2 -0
- package/util/string-util.d.ts +1 -0
- package/util/string-util.js +8 -1
- package/util/zip-util.d.ts +64 -20
- package/util/zip-util.js +153 -59
package/Task/MysqlDumpTask.js
CHANGED
|
@@ -70,7 +70,7 @@ class MysqlDumpTask extends SqlDumpTaskAbstract_1.SqlDumpTaskAbstract {
|
|
|
70
70
|
table_name
|
|
71
71
|
`);
|
|
72
72
|
}
|
|
73
|
-
async onExportTables(tableNames, output) {
|
|
73
|
+
async onExportTables(tableNames, output, onProgress) {
|
|
74
74
|
const stream = (0, fs_1.createWriteStream)(output);
|
|
75
75
|
await Promise.all([
|
|
76
76
|
new Promise((resolve, reject) => {
|
|
@@ -83,7 +83,10 @@ class MysqlDumpTask extends SqlDumpTaskAbstract_1.SqlDumpTaskAbstract {
|
|
|
83
83
|
"--skip-add-drop-table=false",
|
|
84
84
|
...tableNames,
|
|
85
85
|
], null, {
|
|
86
|
-
pipe: {
|
|
86
|
+
pipe: {
|
|
87
|
+
stream,
|
|
88
|
+
onWriteProgress: onProgress,
|
|
89
|
+
},
|
|
87
90
|
log: {
|
|
88
91
|
exec: this.verbose,
|
|
89
92
|
stderr: this.verbose,
|
|
@@ -9,7 +9,9 @@ export declare class PostgresqlDumpTask extends SqlDumpTaskAbstract<PostgresqlDu
|
|
|
9
9
|
onCreateDatabase(database: TargetDatabaseType): Promise<void>;
|
|
10
10
|
onExecQuery(query: string): Promise<import("../util/process-util").ExecResultType>;
|
|
11
11
|
onFetchTableNames(database: string): Promise<string[]>;
|
|
12
|
-
onExportTables(tableNames: string[], output: string
|
|
12
|
+
onExportTables(tableNames: string[], output: string, onProgress: (progress: {
|
|
13
|
+
totalBytes: number;
|
|
14
|
+
}) => void): Promise<void>;
|
|
13
15
|
onExportStoredPrograms(): Promise<void>;
|
|
14
16
|
onImport(path: string, database: string): Promise<void>;
|
|
15
17
|
}
|
|
@@ -73,7 +73,7 @@ class PostgresqlDumpTask extends SqlDumpTaskAbstract_1.SqlDumpTaskAbstract {
|
|
|
73
73
|
CONCAT(table_schema, '.', table_name)
|
|
74
74
|
`);
|
|
75
75
|
}
|
|
76
|
-
async onExportTables(tableNames, output) {
|
|
76
|
+
async onExportTables(tableNames, output, onProgress) {
|
|
77
77
|
const stream = (0, fs_1.createWriteStream)(output);
|
|
78
78
|
await Promise.all([
|
|
79
79
|
new Promise((resolve, reject) => {
|
|
@@ -84,7 +84,7 @@ class PostgresqlDumpTask extends SqlDumpTaskAbstract_1.SqlDumpTaskAbstract {
|
|
|
84
84
|
...(await this.buildConnectionArgs()),
|
|
85
85
|
...(tableNames?.flatMap((v) => ["-t", v]) ?? []),
|
|
86
86
|
], null, {
|
|
87
|
-
pipe: { stream:
|
|
87
|
+
pipe: { stream, onWriteProgress: onProgress },
|
|
88
88
|
stderr: {
|
|
89
89
|
toExitCode: true,
|
|
90
90
|
},
|
|
@@ -29,7 +29,9 @@ export declare abstract class SqlDumpTaskAbstract<TConfig extends SqlDumpTaskCon
|
|
|
29
29
|
abstract onDatabaseIsEmpty(databaseName: string): Promise<boolean>;
|
|
30
30
|
abstract onFetchTableNames(database: string): Promise<string[]>;
|
|
31
31
|
abstract onExecQuery(query: string): ReturnType<typeof exec>;
|
|
32
|
-
abstract onExportTables(tableNames: string[], output: string
|
|
32
|
+
abstract onExportTables(tableNames: string[], output: string, onProgress: (data: {
|
|
33
|
+
totalBytes: number;
|
|
34
|
+
}) => void): Promise<void>;
|
|
33
35
|
abstract onExportStoredPrograms(output: string): Promise<void>;
|
|
34
36
|
abstract onImport(path: string, database: string): Promise<void>;
|
|
35
37
|
onBackup(data: BackupDataType): Promise<void>;
|
|
@@ -118,24 +118,61 @@ class SqlDumpTaskAbstract extends TaskAbstract_1.TaskAbstract {
|
|
|
118
118
|
await (0, promises_1.mkdir)(outputPath, { recursive: true });
|
|
119
119
|
if (!this.config.oneFileByTable) {
|
|
120
120
|
const outPath = (0, path_1.join)(outputPath, serializeSqlFile({ database: this.config.database }));
|
|
121
|
-
|
|
121
|
+
data.onProgress({
|
|
122
|
+
relative: {
|
|
123
|
+
description: "Exporting",
|
|
124
|
+
},
|
|
125
|
+
});
|
|
126
|
+
await this.onExportTables(tableNames, outPath, async (progress) => {
|
|
127
|
+
await data.onProgress({
|
|
128
|
+
absolute: {
|
|
129
|
+
description: "Exporting in single file",
|
|
130
|
+
current: progress.totalBytes,
|
|
131
|
+
format: "size",
|
|
132
|
+
},
|
|
133
|
+
});
|
|
134
|
+
});
|
|
122
135
|
}
|
|
123
136
|
else {
|
|
124
137
|
let current = 0;
|
|
125
138
|
for (const tableName of tableNames) {
|
|
126
|
-
data.onProgress({
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
139
|
+
await data.onProgress({
|
|
140
|
+
relative: {
|
|
141
|
+
description: "Exporting",
|
|
142
|
+
payload: tableName,
|
|
143
|
+
},
|
|
144
|
+
absolute: {
|
|
145
|
+
total: tableNames.length,
|
|
146
|
+
current: current,
|
|
147
|
+
percent: (0, math_util_1.progressPercent)(tableNames.length, current),
|
|
148
|
+
},
|
|
131
149
|
});
|
|
132
|
-
current++;
|
|
133
150
|
const outPath = (0, path_1.join)(outputPath, serializeSqlFile({ table: tableName }));
|
|
134
|
-
await this.onExportTables([tableName], outPath)
|
|
151
|
+
await this.onExportTables([tableName], outPath, async (progress) => {
|
|
152
|
+
await data.onProgress({
|
|
153
|
+
relative: {
|
|
154
|
+
description: "Exporting",
|
|
155
|
+
payload: tableName,
|
|
156
|
+
current: progress.totalBytes,
|
|
157
|
+
format: "size",
|
|
158
|
+
},
|
|
159
|
+
absolute: {
|
|
160
|
+
total: tableNames.length,
|
|
161
|
+
current: current,
|
|
162
|
+
percent: (0, math_util_1.progressPercent)(tableNames.length, current),
|
|
163
|
+
},
|
|
164
|
+
});
|
|
165
|
+
});
|
|
166
|
+
current++;
|
|
135
167
|
}
|
|
136
168
|
}
|
|
137
169
|
if (this.config.storedPrograms) {
|
|
138
170
|
const outPath = (0, path_1.join)(outputPath, "stored-programs.sql");
|
|
171
|
+
data.onProgress({
|
|
172
|
+
relative: {
|
|
173
|
+
description: "Exporting storaged programs",
|
|
174
|
+
},
|
|
175
|
+
});
|
|
139
176
|
await this.onExportStoredPrograms(outPath);
|
|
140
177
|
}
|
|
141
178
|
}
|
|
@@ -184,13 +221,18 @@ class SqlDumpTaskAbstract extends TaskAbstract_1.TaskAbstract {
|
|
|
184
221
|
for (const item of items) {
|
|
185
222
|
const path = (0, path_1.join)(restorePath, item.fileName);
|
|
186
223
|
data.onProgress({
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
224
|
+
relative: {
|
|
225
|
+
description: "Importing",
|
|
226
|
+
payload: item.fileName,
|
|
227
|
+
},
|
|
228
|
+
absolute: {
|
|
229
|
+
total: items.length,
|
|
230
|
+
current: current,
|
|
231
|
+
percent: (0, math_util_1.progressPercent)(items.length, current),
|
|
232
|
+
},
|
|
191
233
|
});
|
|
192
|
-
current++;
|
|
193
234
|
await this.onImport(path, database.name);
|
|
235
|
+
current++;
|
|
194
236
|
}
|
|
195
237
|
}
|
|
196
238
|
}
|
package/Task/TaskAbstract.d.ts
CHANGED
|
@@ -2,22 +2,16 @@ import { BackupActionOptionsType } from "../Action/BackupAction";
|
|
|
2
2
|
import { RestoreActionOptionsType } from "../Action/RestoreAction";
|
|
3
3
|
import { PackageConfigType } from "../Config/PackageConfig";
|
|
4
4
|
import { SnapshotType } from "../Repository/RepositoryAbstract";
|
|
5
|
-
|
|
6
|
-
total?: number;
|
|
7
|
-
current?: number;
|
|
8
|
-
percent?: number;
|
|
9
|
-
step?: string;
|
|
10
|
-
stepPercent?: number;
|
|
11
|
-
};
|
|
5
|
+
import { Progress } from "../util/progress";
|
|
12
6
|
export declare type BackupDataType = {
|
|
13
|
-
onProgress: (data:
|
|
7
|
+
onProgress: (data: Progress) => Promise<void>;
|
|
14
8
|
options: BackupActionOptionsType;
|
|
15
9
|
package: PackageConfigType;
|
|
16
10
|
targetPath: string | undefined;
|
|
17
11
|
snapshot: SnapshotType;
|
|
18
12
|
};
|
|
19
13
|
export declare type RestoreDataType = {
|
|
20
|
-
onProgress: (data:
|
|
14
|
+
onProgress: (data: Progress) => Promise<void>;
|
|
21
15
|
options: RestoreActionOptionsType;
|
|
22
16
|
package: PackageConfigType;
|
|
23
17
|
targetPath: string | undefined;
|
package/cli.js
CHANGED
|
@@ -81,7 +81,7 @@ program.usage("dtt");
|
|
|
81
81
|
program.option("-v,--verbose", "Verbose", (_, previous) => previous + 1, 0);
|
|
82
82
|
program.option("-c,--config <path>", "Config path", process.env["DATATRUCK_CONFIG"] ?? (cwd.endsWith(path_1.sep) ? cwd : `${cwd}${path_1.sep}`));
|
|
83
83
|
program.option("--progress <value>", "Progress type (auto, plain, tty)", "auto");
|
|
84
|
-
program.option("--progress-interval <ms>", "Progress interval");
|
|
84
|
+
program.option("--progress-interval <ms>", "Progress interval", Number, 1000);
|
|
85
85
|
program.option("-o,--output-format <format>", "Output format (json, pjson, yaml, table, custom=$, tpl=name)", "table");
|
|
86
86
|
makeCommand(CommandFactory_1.CommandEnum.config).alias("c");
|
|
87
87
|
makeCommand(CommandFactory_1.CommandEnum.init).alias("i");
|
package/config.schema.json
CHANGED
|
@@ -11,11 +11,7 @@ CREATE TABLE "backup_session" (
|
|
|
11
11
|
"endDate" TEXT,
|
|
12
12
|
"state" TEXT,
|
|
13
13
|
"error" TEXT,
|
|
14
|
-
"
|
|
15
|
-
"progressTotal" INTEGER,
|
|
16
|
-
"progressPercent" INTEGER,
|
|
17
|
-
"progressStep" TEXT,
|
|
18
|
-
"progressStepPercent" INTEGER,
|
|
14
|
+
"progress" TEXT,
|
|
19
15
|
|
|
20
16
|
"snapshotId" TEXT NOT NULL,
|
|
21
17
|
"packageName" TEXT NOT NULL,
|
|
@@ -32,11 +28,7 @@ CREATE TABLE "backup_session_task" (
|
|
|
32
28
|
"endDate" TEXT,
|
|
33
29
|
"state" TEXT,
|
|
34
30
|
"error" TEXT,
|
|
35
|
-
"
|
|
36
|
-
"progressTotal" INTEGER,
|
|
37
|
-
"progressPercent" INTEGER,
|
|
38
|
-
"progressStep" TEXT,
|
|
39
|
-
"progressStepPercent" INTEGER,
|
|
31
|
+
"progress" TEXT,
|
|
40
32
|
|
|
41
33
|
"sessionId" INTEGER NOT NULL,
|
|
42
34
|
"taskName" TEXT NOT NULL
|
|
@@ -51,11 +43,7 @@ CREATE TABLE "backup_session_repository" (
|
|
|
51
43
|
"endDate" TEXT,
|
|
52
44
|
"state" TEXT,
|
|
53
45
|
"error" TEXT,
|
|
54
|
-
"
|
|
55
|
-
"progressTotal" INTEGER,
|
|
56
|
-
"progressPercent" INTEGER,
|
|
57
|
-
"progressStep" TEXT,
|
|
58
|
-
"progressStepPercent" INTEGER,
|
|
46
|
+
"progress" TEXT,
|
|
59
47
|
|
|
60
48
|
"sessionId" INTEGER NOT NULL,
|
|
61
49
|
"repositoryName" TEXT NOT NULL,
|
|
@@ -71,11 +59,7 @@ CREATE TABLE "restore_session" (
|
|
|
71
59
|
"endDate" TEXT,
|
|
72
60
|
"state" TEXT,
|
|
73
61
|
"error" TEXT,
|
|
74
|
-
"
|
|
75
|
-
"progressTotal" INTEGER,
|
|
76
|
-
"progressPercent" INTEGER,
|
|
77
|
-
"progressStep" TEXT,
|
|
78
|
-
"progressStepPercent" INTEGER,
|
|
62
|
+
"progress" TEXT,
|
|
79
63
|
|
|
80
64
|
"snapshotId" TEXT NOT NULL,
|
|
81
65
|
"packageName" TEXT NOT NULL -- ,
|
|
@@ -91,11 +75,7 @@ CREATE TABLE "restore_session_task" (
|
|
|
91
75
|
"endDate" TEXT,
|
|
92
76
|
"state" TEXT,
|
|
93
77
|
"error" TEXT,
|
|
94
|
-
"
|
|
95
|
-
"progressTotal" INTEGER,
|
|
96
|
-
"progressPercent" INTEGER,
|
|
97
|
-
"progressStep" TEXT,
|
|
98
|
-
"progressStepPercent" INTEGER,
|
|
78
|
+
"progress" TEXT,
|
|
99
79
|
|
|
100
80
|
"sessionId" INTEGER NOT NULL,
|
|
101
81
|
"taskName" TEXT NOT NULL
|
|
@@ -110,11 +90,7 @@ CREATE TABLE "restore_session_repository" (
|
|
|
110
90
|
"endDate" TEXT,
|
|
111
91
|
"state" TEXT,
|
|
112
92
|
"error" TEXT,
|
|
113
|
-
"
|
|
114
|
-
"progressTotal" INTEGER,
|
|
115
|
-
"progressPercent" INTEGER,
|
|
116
|
-
"progressStep" TEXT,
|
|
117
|
-
"progressStepPercent" INTEGER,
|
|
93
|
+
"progress" TEXT,
|
|
118
94
|
|
|
119
95
|
"sessionId" INTEGER NOT NULL,
|
|
120
96
|
"repositoryName" TEXT NOT NULL,
|
package/package.json
CHANGED
package/util/cli-util.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ export declare const showCursorCommand = "\u001B[?25h";
|
|
|
5
5
|
export declare const clearCommand = "\r\u001B[K";
|
|
6
6
|
export declare const hideCursorCommand = "\u001B[?25l";
|
|
7
7
|
export declare function renderSpinner(counter: number): string;
|
|
8
|
-
export declare function renderProgressBar(progress: number, size?: number): string;
|
|
8
|
+
export declare function renderProgressBar(progress: number, size?: number, subprogress?: number): string;
|
|
9
9
|
export declare function logVars(data: Record<string, any>): void;
|
|
10
10
|
export declare function logExec(command: string, argv?: string[], env?: NodeJS.ProcessEnv, logToStderr?: boolean): void;
|
|
11
11
|
export declare function resultColumn(error: Error | null | string, state?: "started" | "ended"): "❌" | " ? " | "✅";
|
package/util/cli-util.js
CHANGED
|
@@ -20,12 +20,27 @@ function renderSpinner(counter) {
|
|
|
20
20
|
return exports.spinnerChars[counter % (exports.spinnerChars.length - 1)];
|
|
21
21
|
}
|
|
22
22
|
exports.renderSpinner = renderSpinner;
|
|
23
|
-
function renderProgressBar(progress, size = 10) {
|
|
23
|
+
function renderProgressBar(progress, size = 10, subprogress) {
|
|
24
24
|
const completeChar = "\u2588";
|
|
25
25
|
const incompleteChar = "\u2591";
|
|
26
26
|
const completedSize = Math.round((progress * size) / 100);
|
|
27
27
|
const restSize = Math.max(size - completedSize, 0);
|
|
28
|
-
|
|
28
|
+
let result = completeChar.repeat(completedSize) + incompleteChar.repeat(restSize);
|
|
29
|
+
if (typeof subprogress === "number") {
|
|
30
|
+
const subprogressChar = Math.round((subprogress * size) / 100);
|
|
31
|
+
if (subprogressChar === size) {
|
|
32
|
+
result =
|
|
33
|
+
result.slice(0, subprogressChar - 1) +
|
|
34
|
+
chalk_1.default.white(result[Math.max(0, subprogressChar - 1)]);
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
result =
|
|
38
|
+
result.slice(0, subprogressChar) +
|
|
39
|
+
chalk_1.default.white(result[Math.max(0, subprogressChar - 1)]) +
|
|
40
|
+
result.slice(subprogressChar + 1);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return (0, chalk_2.cyan)(result);
|
|
29
44
|
}
|
|
30
45
|
exports.renderProgressBar = renderProgressBar;
|
|
31
46
|
function logVars(data) {
|
package/util/fs-util.d.ts
CHANGED
|
@@ -1,9 +1,20 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
2
|
/// <reference types="node" />
|
|
3
3
|
/// <reference types="node" />
|
|
4
|
-
|
|
4
|
+
/// <reference types="node" />
|
|
5
|
+
import { Dirent, Stats } from "fs";
|
|
6
|
+
import { WriteStream } from "fs";
|
|
5
7
|
import { Interface } from "readline";
|
|
6
8
|
export declare const isWSLSystem: boolean;
|
|
9
|
+
export declare function isEmptyDir(path: string): Promise<boolean>;
|
|
10
|
+
declare type EntryObject = {
|
|
11
|
+
name: string;
|
|
12
|
+
path: string;
|
|
13
|
+
dirent: Dirent;
|
|
14
|
+
stats: Stats;
|
|
15
|
+
};
|
|
16
|
+
export declare function applyPermissions(baseDir: string, permissionsPath: string): Promise<void>;
|
|
17
|
+
export declare function pathIterator(stream: AsyncIterable<string | Buffer>): AsyncIterable<EntryObject>;
|
|
7
18
|
export declare function isLocalDir(path: string): boolean;
|
|
8
19
|
export declare function isDirEmpty(path: string): Promise<boolean>;
|
|
9
20
|
export declare function mkdirIfNotExists(path: string): Promise<string>;
|
|
@@ -28,7 +39,7 @@ export declare function mkTmpDir(prefix: string, id?: string): Promise<string>;
|
|
|
28
39
|
export declare function readPartialFile(path: string, positions: [number, number?]): Promise<string>;
|
|
29
40
|
export declare function checkFile(path: string): Promise<boolean>;
|
|
30
41
|
export declare function checkDir(path: string): Promise<boolean>;
|
|
31
|
-
export declare function readDir(path: string): Promise<string[]>;
|
|
42
|
+
export declare function readDir(path: string, optional?: boolean): Promise<string[]>;
|
|
32
43
|
export declare function forEachFile(dirPath: string, cb: (path: string, dir: boolean) => void, includeDir?: boolean): Promise<void>;
|
|
33
44
|
/**
|
|
34
45
|
* @experimental
|
|
@@ -37,24 +48,7 @@ export declare function fastglobToGitIgnore(patterns: string[], baseDir: string)
|
|
|
37
48
|
export declare function writeGitIgnoreList(options: {
|
|
38
49
|
paths: NodeJS.ReadableStream | string[];
|
|
39
50
|
}): Promise<string>;
|
|
40
|
-
export declare function
|
|
41
|
-
paths: NodeJS.ReadableStream | string[];
|
|
42
|
-
packs?: {
|
|
43
|
-
include: string[];
|
|
44
|
-
exclude?: string[];
|
|
45
|
-
multiple?: boolean;
|
|
46
|
-
}[];
|
|
47
|
-
}): Promise<{
|
|
48
|
-
path: string;
|
|
49
|
-
includedPackPaths: string[];
|
|
50
|
-
excludedPackPaths: string[];
|
|
51
|
-
total: {
|
|
52
|
-
all: number;
|
|
53
|
-
path: number;
|
|
54
|
-
packsPaths: number[];
|
|
55
|
-
multipleStats: Record<string, number>;
|
|
56
|
-
};
|
|
57
|
-
}>;
|
|
51
|
+
export declare function waitForClose(stream: WriteStream): Promise<WriteStream>;
|
|
58
52
|
export declare function copyFileWithStreams(source: string, target: string): Promise<unknown>;
|
|
59
53
|
export declare function updateFileStats(path: string, fileInfo: Stats): Promise<void>;
|
|
60
54
|
export declare function isNotFoundError(error: unknown): boolean;
|
|
@@ -79,6 +73,11 @@ export declare function cpy(options: {
|
|
|
79
73
|
*/
|
|
80
74
|
concurrency?: number;
|
|
81
75
|
skipNotFoundError?: boolean;
|
|
76
|
+
onProgress?: (data: {
|
|
77
|
+
current: number;
|
|
78
|
+
path?: string;
|
|
79
|
+
type?: "start" | "end";
|
|
80
|
+
}) => Promise<boolean | void>;
|
|
82
81
|
onPath?: (data: {
|
|
83
82
|
isDir: boolean;
|
|
84
83
|
entryPath: string;
|
|
@@ -90,4 +89,9 @@ export declare function cpy(options: {
|
|
|
90
89
|
dirs: number;
|
|
91
90
|
};
|
|
92
91
|
}) => Promise<boolean | void>;
|
|
93
|
-
}): Promise<
|
|
92
|
+
}): Promise<{
|
|
93
|
+
paths: number;
|
|
94
|
+
files: number;
|
|
95
|
+
dirs: number;
|
|
96
|
+
}>;
|
|
97
|
+
export {};
|
package/util/fs-util.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.cpy = exports.isNotFoundError = exports.updateFileStats = exports.copyFileWithStreams = exports.
|
|
6
|
+
exports.cpy = exports.isNotFoundError = exports.updateFileStats = exports.copyFileWithStreams = exports.waitForClose = exports.writeGitIgnoreList = exports.fastglobToGitIgnore = exports.forEachFile = exports.readDir = exports.checkDir = exports.checkFile = exports.readPartialFile = exports.mkTmpDir = exports.fastFolderSizeAsync = exports.tmpDir = exports.sessionTmpDir = exports.parentTmpDir = exports.existsFile = exports.findFile = exports.parsePackageFile = exports.parseFile = exports.parseFileExtensions = exports.readdirIfExists = exports.writeJSONFile = exports.existsDir = exports.ensureEmptyDir = exports.mkdirIfNotExists = exports.isDirEmpty = exports.isLocalDir = exports.pathIterator = exports.applyPermissions = exports.isEmptyDir = exports.isWSLSystem = void 0;
|
|
7
7
|
const globalData_1 = __importDefault(require("../globalData"));
|
|
8
8
|
const path_util_1 = require("./path-util");
|
|
9
9
|
const async_1 = require("async");
|
|
@@ -13,13 +13,49 @@ const fast_glob_1 = __importDefault(require("fast-glob"));
|
|
|
13
13
|
const fs_1 = require("fs");
|
|
14
14
|
const fs_2 = require("fs");
|
|
15
15
|
const promises_1 = require("fs/promises");
|
|
16
|
-
const micromatch_1 = require("micromatch");
|
|
17
16
|
const os_1 = require("os");
|
|
18
17
|
const path_1 = require("path");
|
|
19
18
|
const path_2 = require("path");
|
|
20
19
|
const readline_1 = require("readline");
|
|
21
20
|
const util_1 = require("util");
|
|
22
21
|
exports.isWSLSystem = (0, os_1.release)().includes("microsoft-standard-WSL");
|
|
22
|
+
async function isEmptyDir(path) {
|
|
23
|
+
const iterator = await (0, promises_1.opendir)(path);
|
|
24
|
+
let done = false;
|
|
25
|
+
try {
|
|
26
|
+
const next = await iterator[Symbol.asyncIterator]().next();
|
|
27
|
+
done = !!next.done;
|
|
28
|
+
return done;
|
|
29
|
+
}
|
|
30
|
+
finally {
|
|
31
|
+
if (!done) {
|
|
32
|
+
await iterator.close();
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
exports.isEmptyDir = isEmptyDir;
|
|
37
|
+
async function applyPermissions(baseDir, permissionsPath) {
|
|
38
|
+
const singleReader = (0, readline_1.createInterface)({
|
|
39
|
+
input: (0, fs_1.createReadStream)(permissionsPath),
|
|
40
|
+
});
|
|
41
|
+
for await (const line of singleReader) {
|
|
42
|
+
const [rpath, rawUid, rawGui, rawMode] = line.split(":");
|
|
43
|
+
const path = (0, path_1.join)(baseDir, rpath);
|
|
44
|
+
if (!path.startsWith(baseDir)) {
|
|
45
|
+
throw new Error(`Entry path is out of the base dir: (${path}, ${baseDir})`);
|
|
46
|
+
}
|
|
47
|
+
const uid = Number(rawUid);
|
|
48
|
+
const guid = Number(rawGui);
|
|
49
|
+
await (0, promises_1.chown)(path, uid, guid);
|
|
50
|
+
const mode = Number(rawMode);
|
|
51
|
+
await (0, promises_1.chmod)(path, mode);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
exports.applyPermissions = applyPermissions;
|
|
55
|
+
function pathIterator(stream) {
|
|
56
|
+
return stream;
|
|
57
|
+
}
|
|
58
|
+
exports.pathIterator = pathIterator;
|
|
23
59
|
function isLocalDir(path) {
|
|
24
60
|
return /^[\/\.]|([A-Z]:)/i.test(path);
|
|
25
61
|
}
|
|
@@ -59,9 +95,7 @@ async function writeJSONFile(path, json) {
|
|
|
59
95
|
}
|
|
60
96
|
exports.writeJSONFile = writeJSONFile;
|
|
61
97
|
async function readdirIfExists(path) {
|
|
62
|
-
|
|
63
|
-
return [];
|
|
64
|
-
return await readDir(path);
|
|
98
|
+
return await readDir(path, true);
|
|
65
99
|
}
|
|
66
100
|
exports.readdirIfExists = readdirIfExists;
|
|
67
101
|
exports.parseFileExtensions = ["json", "js", "ts", "yaml", "yml"];
|
|
@@ -128,7 +162,7 @@ function sessionTmpDir() {
|
|
|
128
162
|
exports.sessionTmpDir = sessionTmpDir;
|
|
129
163
|
function tmpDir(prefix, id) {
|
|
130
164
|
if (!id)
|
|
131
|
-
id = (0, crypto_1.
|
|
165
|
+
id = (0, crypto_1.randomUUID)().slice(0, 8);
|
|
132
166
|
return (0, path_1.join)(sessionTmpDir(), `${prefix}-${id}`);
|
|
133
167
|
}
|
|
134
168
|
exports.tmpDir = tmpDir;
|
|
@@ -187,13 +221,15 @@ async function checkDir(path) {
|
|
|
187
221
|
}
|
|
188
222
|
}
|
|
189
223
|
exports.checkDir = checkDir;
|
|
190
|
-
async function readDir(path) {
|
|
224
|
+
async function readDir(path, optional) {
|
|
191
225
|
try {
|
|
192
226
|
return await (0, promises_1.readdir)(path);
|
|
193
227
|
}
|
|
194
228
|
catch (anyError) {
|
|
195
229
|
const nodeError = anyError;
|
|
196
230
|
if (nodeError.code === "ENOENT") {
|
|
231
|
+
if (optional)
|
|
232
|
+
return [];
|
|
197
233
|
const error = new Error(nodeError.message);
|
|
198
234
|
error.code = nodeError.code;
|
|
199
235
|
error.errno = nodeError.errno;
|
|
@@ -262,93 +298,14 @@ async function writeGitIgnoreList(options) {
|
|
|
262
298
|
return path;
|
|
263
299
|
}
|
|
264
300
|
exports.writeGitIgnoreList = writeGitIgnoreList;
|
|
265
|
-
async function
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
const multipleStats = {};
|
|
272
|
-
const total = new Array((options.packs?.length || 0) + 1).fill(0);
|
|
273
|
-
await Promise.all([
|
|
274
|
-
...new Array((options.packs?.length || 0) + 1).fill(null).map((_, index) => new Promise((resolve, reject) => {
|
|
275
|
-
const path = (0, path_1.join)(tempDir, `${index}-included.txt`);
|
|
276
|
-
const stream = (0, fs_2.createWriteStream)(path);
|
|
277
|
-
includedPaths.push(path);
|
|
278
|
-
included.push(stream);
|
|
279
|
-
stream.on("close", resolve);
|
|
280
|
-
stream.on("error", reject);
|
|
281
|
-
})),
|
|
282
|
-
...new Array(options.packs?.length || 0).fill(null).map((_, index) => new Promise((resolve, reject) => {
|
|
283
|
-
const path = (0, path_1.join)(tempDir, `${index}-excluded.txt`);
|
|
284
|
-
const stream = (0, fs_2.createWriteStream)(path);
|
|
285
|
-
excludedPaths.push(path);
|
|
286
|
-
excluded.push(stream);
|
|
287
|
-
stream.on("close", resolve);
|
|
288
|
-
stream.on("error", reject);
|
|
289
|
-
})),
|
|
290
|
-
new Promise(async (resolve) => {
|
|
291
|
-
const packDirectories = [];
|
|
292
|
-
for await (const value of options.paths) {
|
|
293
|
-
const entry = value.toString();
|
|
294
|
-
const isDir = entry.endsWith("/");
|
|
295
|
-
const matchEntry = isDir ? entry.slice(0, -1) : entry;
|
|
296
|
-
let packIndex = 1;
|
|
297
|
-
let matches = false;
|
|
298
|
-
for (const pack of options.packs || []) {
|
|
299
|
-
if ((0, micromatch_1.isMatch)(matchEntry, pack.include) &&
|
|
300
|
-
(!pack.exclude || !(0, micromatch_1.isMatch)(matchEntry, pack.exclude))) {
|
|
301
|
-
if (isDir)
|
|
302
|
-
packDirectories.push([packIndex - 1, entry]);
|
|
303
|
-
included[packIndex].write(`${entry}\n`);
|
|
304
|
-
if (!isDir)
|
|
305
|
-
total[packIndex]++;
|
|
306
|
-
matches = true;
|
|
307
|
-
break;
|
|
308
|
-
}
|
|
309
|
-
packIndex++;
|
|
310
|
-
}
|
|
311
|
-
if (!matches) {
|
|
312
|
-
const packDir = packDirectories.find(([, p]) => entry.startsWith(p));
|
|
313
|
-
if (packDir) {
|
|
314
|
-
const [i, v] = packDir;
|
|
315
|
-
const multipleExclude = options.packs?.[i].exclude;
|
|
316
|
-
if (multipleExclude && (0, micromatch_1.isMatch)(matchEntry, multipleExclude)) {
|
|
317
|
-
included[0].write(`${entry}\n`);
|
|
318
|
-
excluded[i].write(`${entry}\n`);
|
|
319
|
-
}
|
|
320
|
-
else {
|
|
321
|
-
if (!multipleStats[v])
|
|
322
|
-
multipleStats[v] = 0;
|
|
323
|
-
multipleStats[v]++;
|
|
324
|
-
}
|
|
325
|
-
}
|
|
326
|
-
else {
|
|
327
|
-
included[0].write(`${entry}\n`);
|
|
328
|
-
}
|
|
329
|
-
if (!isDir)
|
|
330
|
-
total[0]++;
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
|
-
for (const stream of [...included, ...excluded]) {
|
|
334
|
-
stream.end();
|
|
335
|
-
}
|
|
336
|
-
resolve();
|
|
337
|
-
}),
|
|
338
|
-
]);
|
|
339
|
-
return {
|
|
340
|
-
path: includedPaths[0],
|
|
341
|
-
includedPackPaths: includedPaths.slice(1),
|
|
342
|
-
excludedPackPaths: excludedPaths,
|
|
343
|
-
total: {
|
|
344
|
-
all: total.reduce((p, v) => p + v, 0),
|
|
345
|
-
path: total[0],
|
|
346
|
-
packsPaths: total.slice(1),
|
|
347
|
-
multipleStats,
|
|
348
|
-
},
|
|
349
|
-
};
|
|
301
|
+
async function waitForClose(stream) {
|
|
302
|
+
return new Promise(async (resolve, reject) => {
|
|
303
|
+
stream.on("close", resolve);
|
|
304
|
+
stream.on("error", reject);
|
|
305
|
+
return stream;
|
|
306
|
+
});
|
|
350
307
|
}
|
|
351
|
-
exports.
|
|
308
|
+
exports.waitForClose = waitForClose;
|
|
352
309
|
async function copyFileWithStreams(source, target) {
|
|
353
310
|
const r = (0, fs_1.createReadStream)(source);
|
|
354
311
|
const w = (0, fs_2.createWriteStream)(target);
|
|
@@ -391,6 +348,7 @@ async function cpy(options) {
|
|
|
391
348
|
}
|
|
392
349
|
};
|
|
393
350
|
const task = async (rawEntryPath, basePath) => {
|
|
351
|
+
[rawEntryPath] = rawEntryPath.split(":");
|
|
394
352
|
const isDir = rawEntryPath.endsWith("/");
|
|
395
353
|
const entryPath = (0, path_1.normalize)(rawEntryPath);
|
|
396
354
|
const entrySourcePath = (0, path_1.resolve)((0, path_1.join)(basePath, rawEntryPath));
|
|
@@ -411,6 +369,10 @@ async function cpy(options) {
|
|
|
411
369
|
else {
|
|
412
370
|
const dir = (0, path_1.dirname)(entryTargetPath);
|
|
413
371
|
await makeRecursiveDir(dir);
|
|
372
|
+
await options.onProgress?.({
|
|
373
|
+
current: stats.files,
|
|
374
|
+
path: entryPath,
|
|
375
|
+
});
|
|
414
376
|
stats.files++;
|
|
415
377
|
// https://github.com/nodejs/node/issues/44261
|
|
416
378
|
if (exports.isWSLSystem) {
|
|
@@ -471,5 +433,10 @@ async function cpy(options) {
|
|
|
471
433
|
},
|
|
472
434
|
});
|
|
473
435
|
}
|
|
436
|
+
await options.onProgress?.({
|
|
437
|
+
current: stats.files,
|
|
438
|
+
type: "end",
|
|
439
|
+
});
|
|
440
|
+
return stats;
|
|
474
441
|
}
|
|
475
442
|
exports.cpy = cpy;
|
package/util/math-util.js
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.progressPercent = void 0;
|
|
4
4
|
function progressPercent(total, current) {
|
|
5
|
+
if (total === 0 && current === 0)
|
|
6
|
+
return 0;
|
|
5
7
|
return Number(((current / total) * 100).toFixed(2));
|
|
6
8
|
}
|
|
7
9
|
exports.progressPercent = progressPercent;
|
package/util/process-util.d.ts
CHANGED
|
@@ -14,6 +14,9 @@ export interface ExecSettingsInterface {
|
|
|
14
14
|
exec?: boolean;
|
|
15
15
|
pipe?: {
|
|
16
16
|
stream: WriteStream | ReadStream;
|
|
17
|
+
onWriteProgress?: (data: {
|
|
18
|
+
totalBytes: number;
|
|
19
|
+
}) => void;
|
|
17
20
|
onReadProgress?: (data: {
|
|
18
21
|
totalBytes: number;
|
|
19
22
|
currentBytes: number;
|
|
@@ -24,6 +27,7 @@ export interface ExecSettingsInterface {
|
|
|
24
27
|
onSpawn?: (p: ChildProcess) => void;
|
|
25
28
|
stdout?: {
|
|
26
29
|
save?: boolean;
|
|
30
|
+
parseLines?: boolean;
|
|
27
31
|
onData?: (data: string) => void;
|
|
28
32
|
};
|
|
29
33
|
stderr?: {
|