@datatruck/cli 0.33.0 → 0.34.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/config.schema.json +3 -0
- package/lib/actions/BackupAction.d.ts +8 -2
- package/lib/actions/BackupAction.js +22 -8
- package/lib/actions/CopyAction.d.ts +4 -1
- package/lib/actions/CopyAction.js +15 -4
- package/lib/commands/BackupCommand.d.ts +2 -0
- package/lib/repositories/DatatruckRepository.d.ts +6 -2
- package/lib/repositories/DatatruckRepository.js +11 -3
- package/lib/repositories/GitRepository.d.ts +6 -2
- package/lib/repositories/GitRepository.js +8 -3
- package/lib/repositories/RepositoryAbstract.d.ts +6 -2
- package/lib/repositories/ResticRepository.d.ts +6 -2
- package/lib/repositories/ResticRepository.js +11 -5
- package/lib/tasks/GitTask.js +32 -50
- package/lib/tasks/MariadbTask.js +43 -56
- package/lib/tasks/MssqlTask.js +5 -11
- package/lib/tasks/MysqlDumpTask.js +4 -4
- package/lib/tasks/PostgresqlDumpTask.d.ts +1 -1
- package/lib/tasks/PostgresqlDumpTask.js +9 -32
- package/lib/tasks/SqlDumpTaskAbstract.d.ts +1 -2
- package/lib/tasks/SqlDumpTaskAbstract.js +1 -1
- package/lib/utils/Git.d.ts +9 -7
- package/lib/utils/Git.js +30 -29
- package/lib/utils/Restic.d.ts +11 -11
- package/lib/utils/Restic.js +72 -90
- package/lib/utils/async-process.d.ts +66 -0
- package/lib/utils/async-process.js +237 -0
- package/lib/utils/async.d.ts +3 -5
- package/lib/utils/async.js +2 -2
- package/lib/utils/datatruck/client.d.ts +3 -1
- package/lib/utils/datatruck/client.js +1 -1
- package/lib/utils/datatruck/config-type.d.ts +1 -0
- package/lib/utils/datatruck/cron-server.js +2 -2
- package/lib/utils/datatruck/report-list.d.ts +2 -0
- package/lib/utils/datatruck/report-list.js +1 -1
- package/lib/utils/fs.d.ts +7 -2
- package/lib/utils/fs.js +0 -1
- package/lib/utils/http.d.ts +3 -1
- package/lib/utils/http.js +6 -1
- package/lib/utils/mysql.d.ts +8 -10
- package/lib/utils/mysql.js +60 -79
- package/lib/utils/process.d.ts +3 -92
- package/lib/utils/process.js +7 -311
- package/lib/utils/spawnSteps.js +9 -10
- package/lib/utils/tar.js +29 -49
- package/lib/utils/virtual-fs.d.ts +6 -2
- package/lib/utils/virtual-fs.js +4 -1
- package/package.json +2 -2
package/lib/utils/Restic.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.Restic = void 0;
|
|
4
|
+
const async_process_1 = require("./async-process");
|
|
4
5
|
const fs_1 = require("./fs");
|
|
5
|
-
const process_1 = require("./process");
|
|
6
6
|
const string_1 = require("./string");
|
|
7
7
|
const promises_1 = require("fs/promises");
|
|
8
8
|
const path_1 = require("path");
|
|
@@ -28,14 +28,11 @@ class Restic {
|
|
|
28
28
|
}
|
|
29
29
|
return `${input.backend}:${(0, string_1.formatUri)({ ...input, password: input.password }, hidePassword)}`;
|
|
30
30
|
}
|
|
31
|
-
|
|
32
|
-
return
|
|
31
|
+
createProcess(args, options) {
|
|
32
|
+
return new async_process_1.AsyncProcess("restic", args, {
|
|
33
33
|
stdio: ["ignore", "pipe", "pipe"],
|
|
34
34
|
env: { ...process.env, ...this.options.env },
|
|
35
|
-
|
|
36
|
-
}, {
|
|
37
|
-
stderr: { toExitCode: true },
|
|
38
|
-
log: this.options.log
|
|
35
|
+
$log: this.options.log
|
|
39
36
|
? {
|
|
40
37
|
exec: true,
|
|
41
38
|
stdout: true,
|
|
@@ -49,17 +46,22 @@ class Restic {
|
|
|
49
46
|
],
|
|
50
47
|
}
|
|
51
48
|
: {},
|
|
52
|
-
...(
|
|
49
|
+
...(options ?? {}),
|
|
53
50
|
});
|
|
54
51
|
}
|
|
52
|
+
async exec(args, options) {
|
|
53
|
+
return await this.createProcess(args, options).waitForClose();
|
|
54
|
+
}
|
|
55
|
+
async stdout(args, options) {
|
|
56
|
+
return await this.createProcess(args, options).stdout.fetch();
|
|
57
|
+
}
|
|
55
58
|
async checkRepository() {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
});
|
|
59
|
-
return result.exitCode === 0;
|
|
59
|
+
return ((await this.exec(["cat", "config"], {
|
|
60
|
+
$exitCode: false,
|
|
61
|
+
})) === 0);
|
|
60
62
|
}
|
|
61
63
|
async forget(options) {
|
|
62
|
-
|
|
64
|
+
await this.exec([
|
|
63
65
|
"forget",
|
|
64
66
|
...(options.keepLast ? ["--keep-last", options.keepLast.toString()] : []),
|
|
65
67
|
...(options.keepHourly
|
|
@@ -87,66 +89,46 @@ class Restic {
|
|
|
87
89
|
...(options.prune ? ["--prune"] : []),
|
|
88
90
|
...(options.snapshotId ? [options.snapshotId] : []),
|
|
89
91
|
]);
|
|
90
|
-
return result.stdout;
|
|
91
92
|
}
|
|
92
93
|
async snapshots(options) {
|
|
93
|
-
const
|
|
94
|
+
const json = await this.stdout([
|
|
94
95
|
"snapshots",
|
|
95
96
|
...(options.tags?.flatMap((tag) => [`--tag`, tag]) ?? []),
|
|
96
97
|
...(options.json ? ["--json"] : []),
|
|
97
98
|
...(options.paths?.flatMap((path) => ["--path", path]) ?? []),
|
|
98
99
|
...(options.latest ? ["--latest", options.latest.toString()] : []),
|
|
99
100
|
...(options.snapshotIds || []),
|
|
100
|
-
]
|
|
101
|
-
|
|
102
|
-
});
|
|
103
|
-
return JSON.parse(result.stdout);
|
|
104
|
-
}
|
|
105
|
-
async checkBackupSetPathSupport() {
|
|
106
|
-
/*const result = await this.exec(["backup", "--set-path"], {
|
|
107
|
-
onExitCodeError: () => false,
|
|
108
|
-
stderr: { save: true },
|
|
109
|
-
});
|
|
110
|
-
return result.stderr.includes("flag needs an argument");*/
|
|
111
|
-
return false;
|
|
101
|
+
]);
|
|
102
|
+
return JSON.parse(json);
|
|
112
103
|
}
|
|
113
104
|
async backup(options) {
|
|
114
|
-
const exec = async () => await this.exec([
|
|
115
|
-
"backup",
|
|
116
|
-
"--json",
|
|
117
|
-
...(options.exclude?.flatMap((v) => ["-e", v]) ?? []),
|
|
118
|
-
...(options.excludeFile?.flatMap((v) => ["--exclude-file", v]) ?? []),
|
|
119
|
-
...(options.tags?.flatMap((v) => ["--tag", v]) ?? []),
|
|
120
|
-
...(options.setPaths?.flatMap((v) => ["--set-path", v]) ?? []),
|
|
121
|
-
...(options.parent ? ["--parent", options.parent] : []),
|
|
122
|
-
...options.paths,
|
|
123
|
-
], {
|
|
124
|
-
stderr: {
|
|
125
|
-
toExitCode: true,
|
|
126
|
-
},
|
|
127
|
-
stdout: {
|
|
128
|
-
...(options.onStream && {
|
|
129
|
-
onData: (data) => {
|
|
130
|
-
for (const rawLine of data.split("\n")) {
|
|
131
|
-
const line = rawLine.trim();
|
|
132
|
-
if (line.startsWith("{") && line.endsWith("}")) {
|
|
133
|
-
let parsedLine;
|
|
134
|
-
try {
|
|
135
|
-
parsedLine = JSON.parse(line);
|
|
136
|
-
}
|
|
137
|
-
catch (error) { }
|
|
138
|
-
if (parsedLine)
|
|
139
|
-
options.onStream?.(parsedLine);
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
},
|
|
143
|
-
}),
|
|
144
|
-
},
|
|
145
|
-
}, {
|
|
146
|
-
cwd: options.cwd,
|
|
147
|
-
});
|
|
148
105
|
try {
|
|
149
|
-
|
|
106
|
+
const backup = this.createProcess([
|
|
107
|
+
"backup",
|
|
108
|
+
"--json",
|
|
109
|
+
...(options.exclude?.flatMap((v) => ["-e", v]) ?? []),
|
|
110
|
+
...(options.excludeFile?.flatMap((v) => ["--exclude-file", v]) ?? []),
|
|
111
|
+
...(options.tags?.flatMap((v) => ["--tag", v]) ?? []),
|
|
112
|
+
...(options.setPaths?.flatMap((v) => ["--set-path", v]) ?? []),
|
|
113
|
+
...(options.parent ? ["--parent", options.parent] : []),
|
|
114
|
+
...options.paths,
|
|
115
|
+
], { cwd: options.cwd });
|
|
116
|
+
if (options.onStream) {
|
|
117
|
+
await backup.stdout.parseLines((line) => {
|
|
118
|
+
if (line.startsWith("{") && line.endsWith("}")) {
|
|
119
|
+
let parsedLine;
|
|
120
|
+
try {
|
|
121
|
+
parsedLine = JSON.parse(line);
|
|
122
|
+
}
|
|
123
|
+
catch (error) { }
|
|
124
|
+
if (parsedLine)
|
|
125
|
+
options.onStream?.(parsedLine);
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
await backup.waitForClose();
|
|
131
|
+
}
|
|
150
132
|
}
|
|
151
133
|
catch (error) {
|
|
152
134
|
if (options.allowEmptySnapshot &&
|
|
@@ -169,20 +151,17 @@ class Restic {
|
|
|
169
151
|
}
|
|
170
152
|
}
|
|
171
153
|
async copy(options) {
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
}),
|
|
184
|
-
},
|
|
185
|
-
});
|
|
154
|
+
const copy = this.createProcess(["copy", "--json", options.id]);
|
|
155
|
+
if (options.onStream) {
|
|
156
|
+
await copy.stdout.parseLines((line) => {
|
|
157
|
+
if (line.startsWith("{") && line.endsWith("}")) {
|
|
158
|
+
options.onStream?.(JSON.parse(line));
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
else {
|
|
163
|
+
await copy.waitForClose();
|
|
164
|
+
}
|
|
186
165
|
}
|
|
187
166
|
async restore(options) {
|
|
188
167
|
let progressTimeout;
|
|
@@ -207,20 +186,23 @@ class Restic {
|
|
|
207
186
|
if (typeof progressInterval === "number")
|
|
208
187
|
progressRutine();
|
|
209
188
|
try {
|
|
210
|
-
const result =
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
}
|
|
189
|
+
const result = this.createProcess([
|
|
190
|
+
"restore",
|
|
191
|
+
"--json",
|
|
192
|
+
options.id,
|
|
193
|
+
"--target",
|
|
194
|
+
options.target,
|
|
195
|
+
]);
|
|
196
|
+
if (options.onStream) {
|
|
197
|
+
await result.stdout.parseLines((line) => {
|
|
198
|
+
if (line.startsWith("{") && line.endsWith("}")) {
|
|
199
|
+
options.onStream?.(JSON.parse(line));
|
|
200
|
+
}
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
else {
|
|
204
|
+
await result.waitForClose();
|
|
205
|
+
}
|
|
224
206
|
if (snapshots.at(0)?.tags?.includes(emptySnapshotTag))
|
|
225
207
|
await (0, promises_1.rm)((0, path_1.join)(options.target, `.${emptySnapshotTag}`));
|
|
226
208
|
return result;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
/// <reference types="node" />
|
|
3
|
+
/// <reference types="node" />
|
|
4
|
+
/// <reference types="node" />
|
|
5
|
+
/// <reference types="node" />
|
|
6
|
+
import { ChildProcess, SpawnOptions } from "child_process";
|
|
7
|
+
import { ReadStream } from "fs";
|
|
8
|
+
import { Readable, Writable } from "stream";
|
|
9
|
+
export type AsyncProcessOptions = SpawnOptions & {
|
|
10
|
+
$log?: AsyncProcessLog | boolean;
|
|
11
|
+
$controller?: AbortController;
|
|
12
|
+
$exitCode?: ExitCode;
|
|
13
|
+
};
|
|
14
|
+
type ExitCode = ExitCodeValue | ((code: number) => ExitCodeValue | void | undefined);
|
|
15
|
+
type ExitCodeValue = Error | string | number | boolean;
|
|
16
|
+
type AsyncProcessArgv = (string | number)[] | undefined;
|
|
17
|
+
declare class StdIn {
|
|
18
|
+
readonly process: AsyncProcess;
|
|
19
|
+
readonly writable: Writable;
|
|
20
|
+
constructor(process: AsyncProcess, writable: Writable);
|
|
21
|
+
pipe(source: string | ReadStream, onProgress?: (data: {
|
|
22
|
+
totalBytes: number;
|
|
23
|
+
currentBytes: number;
|
|
24
|
+
progress: number;
|
|
25
|
+
}) => void): Promise<void>;
|
|
26
|
+
}
|
|
27
|
+
declare class Std {
|
|
28
|
+
protected type: "stdout" | "stderr";
|
|
29
|
+
readonly process: AsyncProcess;
|
|
30
|
+
readonly readable: Readable;
|
|
31
|
+
constructor(type: "stdout" | "stderr", process: AsyncProcess, readable: Readable);
|
|
32
|
+
onData(cb: (chunk: Buffer) => void): void;
|
|
33
|
+
fetch(): Promise<string>;
|
|
34
|
+
parseLines(cb: (line: string, total: number) => void): Promise<number>;
|
|
35
|
+
pipe(out: string | NodeJS.WritableStream | StdIn, onProgress?: (data: {
|
|
36
|
+
totalBytes: number;
|
|
37
|
+
}) => void): Promise<void>;
|
|
38
|
+
}
|
|
39
|
+
export type AsyncProcessLog = {
|
|
40
|
+
colorize?: boolean;
|
|
41
|
+
exec?: boolean;
|
|
42
|
+
stdout?: boolean;
|
|
43
|
+
stderr?: boolean;
|
|
44
|
+
allToStderr?: boolean;
|
|
45
|
+
envNames?: string[];
|
|
46
|
+
};
|
|
47
|
+
export declare class AsyncProcess {
|
|
48
|
+
protected command: string;
|
|
49
|
+
protected argv: AsyncProcessArgv;
|
|
50
|
+
protected options: AsyncProcessOptions;
|
|
51
|
+
readonly child: ChildProcess;
|
|
52
|
+
readonly stdout: Std;
|
|
53
|
+
readonly stderr: Std;
|
|
54
|
+
readonly stdin: StdIn;
|
|
55
|
+
protected log: AsyncProcessLog | undefined;
|
|
56
|
+
protected controller: AbortController;
|
|
57
|
+
private lastStdError;
|
|
58
|
+
constructor(command: string, argv: AsyncProcessArgv, options?: AsyncProcessOptions);
|
|
59
|
+
static exec(command: string, argv: AsyncProcessArgv, options?: AsyncProcessOptions): Promise<number>;
|
|
60
|
+
static stdout(command: string, argv: AsyncProcessArgv, options?: AsyncProcessOptions): Promise<string>;
|
|
61
|
+
private installLog;
|
|
62
|
+
private installAbortController;
|
|
63
|
+
private resolveExitCode;
|
|
64
|
+
waitForClose(): Promise<number>;
|
|
65
|
+
}
|
|
66
|
+
export {};
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AsyncProcess = void 0;
|
|
4
|
+
const cli_1 = require("./cli");
|
|
5
|
+
const fs_1 = require("./fs");
|
|
6
|
+
const math_1 = require("./math");
|
|
7
|
+
const process_1 = require("./process");
|
|
8
|
+
const child_process_1 = require("child_process");
|
|
9
|
+
const fs_2 = require("fs");
|
|
10
|
+
const promises_1 = require("fs/promises");
|
|
11
|
+
const readline_1 = require("readline");
|
|
12
|
+
function resolveLogOptions(log) {
|
|
13
|
+
return log === true
|
|
14
|
+
? { exec: {}, stdout: {}, stderr: {} }
|
|
15
|
+
: log || undefined;
|
|
16
|
+
}
|
|
17
|
+
function ensureDir(cwd) {
|
|
18
|
+
if (typeof cwd === "string") {
|
|
19
|
+
let isDir = false;
|
|
20
|
+
try {
|
|
21
|
+
isDir = (0, fs_2.statSync)(cwd).isDirectory();
|
|
22
|
+
}
|
|
23
|
+
catch (error) { }
|
|
24
|
+
if (!isDir)
|
|
25
|
+
throw new Error(`Current working directory does not exist: ${cwd}`);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
class StdIn {
|
|
29
|
+
process;
|
|
30
|
+
writable;
|
|
31
|
+
constructor(process, writable) {
|
|
32
|
+
this.process = process;
|
|
33
|
+
this.writable = writable;
|
|
34
|
+
}
|
|
35
|
+
async pipe(source, onProgress) {
|
|
36
|
+
if (this.process["log"]?.exec) {
|
|
37
|
+
const path = source instanceof fs_2.ReadStream
|
|
38
|
+
? "path" in source
|
|
39
|
+
? source.path
|
|
40
|
+
: "&readableStream"
|
|
41
|
+
: source;
|
|
42
|
+
(0, cli_1.logExec)(`[${this.process.child.pid || 0}] < ${path}`);
|
|
43
|
+
}
|
|
44
|
+
const stream = typeof source === "string" ? (0, fs_2.createReadStream)(source) : source;
|
|
45
|
+
const streamPath = stream.path.toString();
|
|
46
|
+
if (onProgress) {
|
|
47
|
+
const fileInfo = await (0, promises_1.stat)(streamPath);
|
|
48
|
+
const totalBytes = fileInfo.size;
|
|
49
|
+
let currentBytes = 0;
|
|
50
|
+
stream.on("data", (data) => {
|
|
51
|
+
currentBytes += data.length;
|
|
52
|
+
onProgress?.({
|
|
53
|
+
totalBytes: totalBytes,
|
|
54
|
+
currentBytes: currentBytes,
|
|
55
|
+
progress: (0, math_1.progressPercent)(totalBytes, currentBytes),
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
stream.pipe(this.writable);
|
|
60
|
+
await Promise.all([
|
|
61
|
+
this.process.waitForClose(),
|
|
62
|
+
(0, fs_1.waitForClose)(stream),
|
|
63
|
+
(0, fs_1.waitForClose)(this.writable),
|
|
64
|
+
]);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
class Std {
|
|
68
|
+
type;
|
|
69
|
+
process;
|
|
70
|
+
readable;
|
|
71
|
+
constructor(type, process, readable) {
|
|
72
|
+
this.type = type;
|
|
73
|
+
this.process = process;
|
|
74
|
+
this.readable = readable;
|
|
75
|
+
}
|
|
76
|
+
onData(cb) {
|
|
77
|
+
this.readable.on("data", cb);
|
|
78
|
+
}
|
|
79
|
+
async fetch() {
|
|
80
|
+
let data = "";
|
|
81
|
+
this.onData((chunk) => (data += chunk));
|
|
82
|
+
await this.process.waitForClose();
|
|
83
|
+
return data.trim();
|
|
84
|
+
}
|
|
85
|
+
async parseLines(cb) {
|
|
86
|
+
let total = 0;
|
|
87
|
+
const parser = (0, readline_1.createInterface)({ input: this.readable });
|
|
88
|
+
parser.on("line", (inLine) => {
|
|
89
|
+
const line = inLine.toString().trim();
|
|
90
|
+
if (line.length)
|
|
91
|
+
cb(line, ++total);
|
|
92
|
+
});
|
|
93
|
+
await Promise.all([this.process.waitForClose(), (0, fs_1.waitForClose)(parser)]);
|
|
94
|
+
return total;
|
|
95
|
+
}
|
|
96
|
+
async pipe(out, onProgress) {
|
|
97
|
+
if (this.process["log"]?.exec) {
|
|
98
|
+
const path = out instanceof StdIn
|
|
99
|
+
? `${[out.process.child.pid || 0]}`
|
|
100
|
+
: typeof out === "string"
|
|
101
|
+
? out
|
|
102
|
+
: "path" in out
|
|
103
|
+
? out.path
|
|
104
|
+
: "&writableStream";
|
|
105
|
+
(0, cli_1.logExec)(`[${this.process.child.pid || 0}] ${this.type === "stderr" ? 2 : ""}> ${path}`);
|
|
106
|
+
}
|
|
107
|
+
if (onProgress) {
|
|
108
|
+
let totalBytes = 0;
|
|
109
|
+
this.readable.on("data", (chunk) => {
|
|
110
|
+
totalBytes += chunk.length;
|
|
111
|
+
onProgress({ totalBytes });
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
const stream = out instanceof StdIn
|
|
115
|
+
? out.writable
|
|
116
|
+
: typeof out === "string"
|
|
117
|
+
? (0, fs_2.createWriteStream)(out)
|
|
118
|
+
: out;
|
|
119
|
+
this.readable.pipe(stream);
|
|
120
|
+
await Promise.all([
|
|
121
|
+
this.process.waitForClose().catch((error) => {
|
|
122
|
+
if ("destroy" in stream)
|
|
123
|
+
stream.destroy();
|
|
124
|
+
return Promise.reject(error);
|
|
125
|
+
}),
|
|
126
|
+
(0, fs_1.waitForClose)(stream),
|
|
127
|
+
(0, fs_1.waitForClose)(this.readable),
|
|
128
|
+
]);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
class AsyncProcess {
|
|
132
|
+
command;
|
|
133
|
+
argv;
|
|
134
|
+
options;
|
|
135
|
+
child;
|
|
136
|
+
stdout;
|
|
137
|
+
stderr;
|
|
138
|
+
stdin;
|
|
139
|
+
log;
|
|
140
|
+
controller;
|
|
141
|
+
lastStdError;
|
|
142
|
+
constructor(command, argv, options = {}) {
|
|
143
|
+
this.command = command;
|
|
144
|
+
this.argv = argv;
|
|
145
|
+
this.options = options;
|
|
146
|
+
const { $log, $controller, ...otherOptions } = options;
|
|
147
|
+
this.log = resolveLogOptions($log);
|
|
148
|
+
this.controller = $controller || new AbortController();
|
|
149
|
+
if (this.log?.exec)
|
|
150
|
+
(0, process_1.logProcess)(command, argv || [], this.log.exec === true ? {} : this.log.exec);
|
|
151
|
+
if (typeof options.cwd === "string")
|
|
152
|
+
ensureDir(options.cwd);
|
|
153
|
+
this.child = (0, child_process_1.spawn)(command, argv?.map(String) || [], otherOptions ?? {});
|
|
154
|
+
if (this.log)
|
|
155
|
+
this.installLog(this.log);
|
|
156
|
+
this.installAbortController(this.controller);
|
|
157
|
+
this.stdout = new Std("stdout", this, this.child.stdout);
|
|
158
|
+
this.stderr = new Std("stderr", this, this.child.stderr);
|
|
159
|
+
this.stdin = new StdIn(this, this.child.stdin);
|
|
160
|
+
}
|
|
161
|
+
static async exec(command, argv, options = {}) {
|
|
162
|
+
const p = new AsyncProcess(command, argv, options);
|
|
163
|
+
return await p.waitForClose();
|
|
164
|
+
}
|
|
165
|
+
static async stdout(command, argv, options = {}) {
|
|
166
|
+
const p = new AsyncProcess(command, argv, options);
|
|
167
|
+
return await p.stdout.fetch();
|
|
168
|
+
}
|
|
169
|
+
installLog(log) {
|
|
170
|
+
if (log.stdout)
|
|
171
|
+
this.child.stdout?.on("data", (chunk) => (0, process_1.logStdout)({
|
|
172
|
+
data: chunk.toString(),
|
|
173
|
+
colorize: log.colorize,
|
|
174
|
+
stderr: log.allToStderr,
|
|
175
|
+
}));
|
|
176
|
+
if (log.stderr)
|
|
177
|
+
this.child.stderr?.on("data", (chunk) => (0, process_1.logStdout)({
|
|
178
|
+
data: chunk.toString(),
|
|
179
|
+
colorize: log.colorize,
|
|
180
|
+
stderr: log.allToStderr,
|
|
181
|
+
}));
|
|
182
|
+
}
|
|
183
|
+
installAbortController(controller) {
|
|
184
|
+
if (controller.signal.aborted) {
|
|
185
|
+
this.child.kill();
|
|
186
|
+
}
|
|
187
|
+
else {
|
|
188
|
+
controller.signal.addEventListener("abort", () => {
|
|
189
|
+
this.child.kill();
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
resolveExitCode(inExitCode) {
|
|
194
|
+
let exitCode = inExitCode ?? 32;
|
|
195
|
+
if (!exitCode)
|
|
196
|
+
return exitCode;
|
|
197
|
+
let result = this.options.$exitCode ?? true;
|
|
198
|
+
let message = inExitCode === null ? "Process killed" : `Process exit code: ${exitCode}`;
|
|
199
|
+
if (typeof result === "function")
|
|
200
|
+
result = result(exitCode);
|
|
201
|
+
if (typeof result === "string") {
|
|
202
|
+
message = result;
|
|
203
|
+
}
|
|
204
|
+
else if (typeof result === "number") {
|
|
205
|
+
exitCode = result;
|
|
206
|
+
}
|
|
207
|
+
else if (result instanceof Error) {
|
|
208
|
+
return result;
|
|
209
|
+
}
|
|
210
|
+
else if (result === false) {
|
|
211
|
+
return exitCode;
|
|
212
|
+
}
|
|
213
|
+
return new Error(message, {
|
|
214
|
+
cause: {
|
|
215
|
+
command: this.command,
|
|
216
|
+
argv: this.argv,
|
|
217
|
+
exitCode,
|
|
218
|
+
lastStdError: this.lastStdError?.toString().trim().slice(0, 255),
|
|
219
|
+
},
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
async waitForClose() {
|
|
223
|
+
if (!this.lastStdError) {
|
|
224
|
+
this.lastStdError = Buffer.from([]);
|
|
225
|
+
this.child.stderr?.on("data", (chunk) => {
|
|
226
|
+
this.lastStdError = chunk;
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
return new Promise((resolve, reject) => {
|
|
230
|
+
this.child.on("error", reject).on("close", (exitCode) => {
|
|
231
|
+
const result = this.resolveExitCode(exitCode);
|
|
232
|
+
typeof result === "number" ? resolve(result) : reject(result);
|
|
233
|
+
});
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
exports.AsyncProcess = AsyncProcess;
|
package/lib/utils/async.d.ts
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
};
|
|
4
|
-
type ItemBuffer<T> = Map<T, ControllerItem>;
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
type ItemBuffer<T> = Map<T, AbortController>;
|
|
5
3
|
export declare function runParallel<T>(options: {
|
|
6
4
|
items: T[];
|
|
7
5
|
concurrency: number;
|
|
@@ -13,7 +11,7 @@ export declare function runParallel<T>(options: {
|
|
|
13
11
|
onItem: (data: {
|
|
14
12
|
item: T;
|
|
15
13
|
index: number;
|
|
16
|
-
controller:
|
|
14
|
+
controller: AbortController;
|
|
17
15
|
}) => Promise<void> | void;
|
|
18
16
|
onFinished?: () => Promise<void> | void;
|
|
19
17
|
}): Promise<void>;
|
package/lib/utils/async.js
CHANGED
|
@@ -9,7 +9,7 @@ async function runParallel(options) {
|
|
|
9
9
|
await promise_pool_1.PromisePool.for(options.items)
|
|
10
10
|
.withConcurrency(options.concurrency)
|
|
11
11
|
.process(async (item, index, pool) => {
|
|
12
|
-
const controller =
|
|
12
|
+
const controller = new AbortController();
|
|
13
13
|
buffer.set(item, controller);
|
|
14
14
|
try {
|
|
15
15
|
await options.onChange({
|
|
@@ -29,7 +29,7 @@ async function runParallel(options) {
|
|
|
29
29
|
pool.stop();
|
|
30
30
|
for (const [, $controller] of buffer.entries())
|
|
31
31
|
try {
|
|
32
|
-
$controller.
|
|
32
|
+
$controller.abort();
|
|
33
33
|
}
|
|
34
34
|
catch (_) { }
|
|
35
35
|
}
|
|
@@ -23,7 +23,9 @@ export declare class RemoteFs extends AbstractFs {
|
|
|
23
23
|
download(source: string, target: string, options?: {
|
|
24
24
|
timeout?: number;
|
|
25
25
|
onProgress?: (progress: BasicProgress) => void;
|
|
26
|
-
}): Promise<
|
|
26
|
+
}): Promise<{
|
|
27
|
+
bytes: number;
|
|
28
|
+
}>;
|
|
27
29
|
}
|
|
28
30
|
export declare function isRemoteBackend(backend: string): boolean;
|
|
29
31
|
export declare function createFs(backend: string): AbstractFs;
|
|
@@ -80,7 +80,7 @@ class RemoteFs extends virtual_fs_1.AbstractFs {
|
|
|
80
80
|
});
|
|
81
81
|
}
|
|
82
82
|
async download(source, target, options = {}) {
|
|
83
|
-
await (0, http_1.downloadFile)(`${this.url}/download`, target, {
|
|
83
|
+
return await (0, http_1.downloadFile)(`${this.url}/download`, target, {
|
|
84
84
|
...options,
|
|
85
85
|
headers: this.headers,
|
|
86
86
|
query: { params: JSON.stringify([source]) },
|
|
@@ -9,6 +9,7 @@ import type { DatatruckRepositoryServerOptions } from "./repository-server";
|
|
|
9
9
|
export { RepositoryConfig, RepositoryConfigEnabledAction, TaskConfig };
|
|
10
10
|
export type Config = {
|
|
11
11
|
$schema?: string;
|
|
12
|
+
hostname?: string;
|
|
12
13
|
tempDir?: string;
|
|
13
14
|
minFreeDiskSpace?: string | number;
|
|
14
15
|
repositories: RepositoryConfig[];
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.createCronServer = void 0;
|
|
4
4
|
const ConfigAction_1 = require("../../actions/ConfigAction");
|
|
5
|
+
const async_process_1 = require("../async-process");
|
|
5
6
|
const cli_1 = require("../cli");
|
|
6
7
|
const cron_1 = require("../cron");
|
|
7
|
-
const process_1 = require("../process");
|
|
8
8
|
const string_1 = require("../string");
|
|
9
9
|
const watcher_1 = require("../watcher");
|
|
10
10
|
const command_1 = require("./command");
|
|
@@ -33,7 +33,7 @@ function createCronServer(options, config) {
|
|
|
33
33
|
const command = new Command({ config: { packages: [], repositories: [] } }, {});
|
|
34
34
|
const cliOptions = (0, cli_1.stringifyOptions)(command.optionsConfig(), action.options);
|
|
35
35
|
const [node, bin] = process.argv;
|
|
36
|
-
await
|
|
36
|
+
await async_process_1.AsyncProcess.exec(node, [bin, "-c", config.configPath, action.name, ...cliOptions], { $log: config.verbose });
|
|
37
37
|
if (config.log)
|
|
38
38
|
console.info(`< [job] ${index} - ${action.name}`);
|
|
39
39
|
}
|
|
@@ -6,6 +6,8 @@ export type ReportListTaskContext = {
|
|
|
6
6
|
};
|
|
7
7
|
};
|
|
8
8
|
export declare function createReportListTasks<T extends ReportListTaskContext>(list: Listr3<T>, options: {
|
|
9
|
+
action: string;
|
|
10
|
+
hostname: string;
|
|
9
11
|
reports: DatatruckReportConfig[];
|
|
10
12
|
onMessage: (result: (List3SummaryResult | Listr3TaskResult<T>)[], report: DatatruckReportConfig) => string;
|
|
11
13
|
verbose?: boolean;
|
|
@@ -41,7 +41,7 @@ function createReportListTasks(list, options) {
|
|
|
41
41
|
else if ((0, reportSteps_1.isReportStep)(report.run)) {
|
|
42
42
|
await (0, reportSteps_1.runReportSteps)(report.run, {
|
|
43
43
|
data: {
|
|
44
|
-
title:
|
|
44
|
+
title: `[${options.hostname}] DTT ${options.action}`,
|
|
45
45
|
message,
|
|
46
46
|
success,
|
|
47
47
|
},
|
package/lib/utils/fs.d.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
/// <reference types="node" />
|
|
4
4
|
import { Progress } from "./progress";
|
|
5
5
|
import { Entry, Options } from "fast-glob";
|
|
6
|
-
import {
|
|
6
|
+
import { Stats } from "fs";
|
|
7
7
|
import { WriteStream } from "fs";
|
|
8
8
|
import { Interface } from "readline";
|
|
9
9
|
export declare const isWSLSystem: boolean;
|
|
@@ -38,7 +38,12 @@ export declare function writeGitIgnoreList(options: {
|
|
|
38
38
|
paths: NodeJS.ReadableStream | string[];
|
|
39
39
|
outDir: string;
|
|
40
40
|
}): Promise<string>;
|
|
41
|
-
export declare function waitForClose(stream:
|
|
41
|
+
export declare function waitForClose(stream: {
|
|
42
|
+
on(event: "close", cb: (...args: any[]) => any): any;
|
|
43
|
+
} | {
|
|
44
|
+
on(event: "close", cb: (...args: any[]) => any): any;
|
|
45
|
+
on(event: "error", cb: (...args: any[]) => any): any;
|
|
46
|
+
}): Promise<void>;
|
|
42
47
|
export declare function copyFileWithStreams(source: string, target: string): Promise<unknown>;
|
|
43
48
|
export declare function updateFileStats(path: string, fileInfo: Stats): Promise<void>;
|
|
44
49
|
export declare function isNotFoundError(error: unknown): boolean;
|
package/lib/utils/fs.js
CHANGED
package/lib/utils/http.d.ts
CHANGED
|
@@ -22,7 +22,9 @@ export declare function downloadFile(url: string, output: string, options?: {
|
|
|
22
22
|
query?: Record<string, string>;
|
|
23
23
|
timeout?: number;
|
|
24
24
|
onProgress?: (progress: BasicProgress) => void;
|
|
25
|
-
}): Promise<
|
|
25
|
+
}): Promise<{
|
|
26
|
+
bytes: number;
|
|
27
|
+
}>;
|
|
26
28
|
export declare function uploadFile(url: string, path: string, options?: {
|
|
27
29
|
headers?: Record<string, string>;
|
|
28
30
|
query?: Record<string, string>;
|