@datatruck/cli 0.29.1 → 0.30.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/Action/BackupAction.d.ts +2 -4
- package/Action/BackupAction.js +3 -11
- package/Action/CopyAction.d.ts +2 -2
- package/Action/CopyAction.js +1 -2
- package/Action/RestoreAction.d.ts +2 -3
- package/Action/RestoreAction.js +1 -2
- package/CHANGELOG.md +22 -0
- package/Command/BackupCommand.js +0 -1
- package/Command/CleanCacheCommand.js +2 -5
- package/Command/CommandAbstract.d.ts +2 -2
- package/Command/CopyCommand.js +0 -1
- package/Command/RestoreCommand.js +0 -1
- package/Command/SnapshotsCommand.js +2 -5
- package/Config/Config.js +22 -0
- package/Repository/DatatruckRepository.js +43 -15
- package/cli.js +6 -8
- package/config.schema.json +34 -0
- package/package.json +1 -2
- package/utils/bytes.d.ts +2 -0
- package/utils/bytes.js +29 -0
- package/utils/datatruck/client.d.ts +6 -1
- package/utils/datatruck/client.js +6 -5
- package/utils/datatruck/server.d.ts +10 -0
- package/utils/datatruck/server.js +30 -11
- package/utils/exit.d.ts +6 -0
- package/utils/exit.js +56 -0
- package/utils/fs.d.ts +3 -0
- package/utils/fs.js +24 -6
- package/utils/http.d.ts +2 -0
- package/utils/http.js +21 -2
- package/utils/list.d.ts +2 -0
- package/utils/list.js +18 -3
- package/utils/process.d.ts +1 -3
- package/utils/process.js +39 -54
- package/utils/progress.d.ts +23 -11
- package/utils/progress.js +25 -17
- package/utils/tar.d.ts +2 -6
- package/utils/virtual-fs.d.ts +7 -1
- package/utils/virtual-fs.js +3 -0
|
@@ -4,6 +4,7 @@ exports.createDatatruckServer = exports.headerKey = void 0;
|
|
|
4
4
|
const http_1 = require("../http");
|
|
5
5
|
const virtual_fs_1 = require("../virtual-fs");
|
|
6
6
|
const fs_1 = require("fs");
|
|
7
|
+
const promises_1 = require("fs/promises");
|
|
7
8
|
const http_2 = require("http");
|
|
8
9
|
function parseUrl(inUrl) {
|
|
9
10
|
const url = new URL(`http://127.0.0.1${inUrl}`);
|
|
@@ -23,14 +24,26 @@ exports.headerKey = {
|
|
|
23
24
|
user: "x-dtt-user",
|
|
24
25
|
password: "x-dtt-password",
|
|
25
26
|
};
|
|
26
|
-
function validateRequest(req,
|
|
27
|
-
const
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
27
|
+
function validateRequest(req, options) {
|
|
28
|
+
const list = options.allowlist;
|
|
29
|
+
if (list && (list.enabled ?? true) && list.remoteAddresses) {
|
|
30
|
+
const remoteAddress = getRemoteAddress(req, options);
|
|
31
|
+
if (!remoteAddress || !list.remoteAddresses.includes(remoteAddress))
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
const name = req.headers[exports.headerKey.user]?.toString().trim();
|
|
35
|
+
const password = req.headers[exports.headerKey.password]?.toString().trim();
|
|
36
|
+
if (!name?.length || !password?.length)
|
|
37
|
+
return;
|
|
38
|
+
return (options.users?.some((user) => user.name === name && user.password === password) || false);
|
|
33
39
|
}
|
|
40
|
+
const getRemoteAddress = (req, options) => {
|
|
41
|
+
return ((options.trustProxy
|
|
42
|
+
? options.trustProxy === true
|
|
43
|
+
? req.headers["x-real-ip"]?.toString()
|
|
44
|
+
: req.headers[options.trustProxy.remoteAddressHeader]?.toString()
|
|
45
|
+
: undefined) ?? req.socket.remoteAddress);
|
|
46
|
+
};
|
|
34
47
|
function createDatatruckServer(options) {
|
|
35
48
|
const log = options.log ?? true;
|
|
36
49
|
return (0, http_2.createServer)(async (req, res) => {
|
|
@@ -38,7 +51,7 @@ function createDatatruckServer(options) {
|
|
|
38
51
|
if (req.url === "/" || req.url === "/favicon.ico") {
|
|
39
52
|
return res.end();
|
|
40
53
|
}
|
|
41
|
-
else if (!validateRequest(req, options
|
|
54
|
+
else if (!validateRequest(req, options)) {
|
|
42
55
|
res.statusCode = 401;
|
|
43
56
|
return res.end();
|
|
44
57
|
}
|
|
@@ -50,7 +63,8 @@ function createDatatruckServer(options) {
|
|
|
50
63
|
const { action, params } = parseUrl(req.url);
|
|
51
64
|
if (action === "upload") {
|
|
52
65
|
const [target] = params;
|
|
53
|
-
const
|
|
66
|
+
const path = fs.resolvePath(target);
|
|
67
|
+
const file = (0, fs_1.createWriteStream)(path);
|
|
54
68
|
req.pipe(file);
|
|
55
69
|
await new Promise((resolve, reject) => {
|
|
56
70
|
req.on("error", reject);
|
|
@@ -60,7 +74,10 @@ function createDatatruckServer(options) {
|
|
|
60
74
|
}
|
|
61
75
|
else if (action === "download") {
|
|
62
76
|
const [target] = params;
|
|
63
|
-
const
|
|
77
|
+
const path = fs.resolvePath(target);
|
|
78
|
+
const file = (0, fs_1.createReadStream)(path);
|
|
79
|
+
const fileStat = await (0, promises_1.stat)(path);
|
|
80
|
+
res.setHeader("Content-Length", fileStat.size);
|
|
64
81
|
file.pipe(res);
|
|
65
82
|
await new Promise((resolve, reject) => {
|
|
66
83
|
req.on("error", reject);
|
|
@@ -82,14 +99,16 @@ function createDatatruckServer(options) {
|
|
|
82
99
|
if (json !== undefined)
|
|
83
100
|
res.write(JSON.stringify(json));
|
|
84
101
|
}
|
|
85
|
-
res.end();
|
|
86
102
|
if (log)
|
|
87
103
|
console.info(`<${action}`);
|
|
104
|
+
res.end();
|
|
88
105
|
}
|
|
89
106
|
catch (error) {
|
|
90
107
|
if (log)
|
|
91
108
|
console.error(`<${req.url}`, error);
|
|
92
109
|
res.statusCode = 500;
|
|
110
|
+
res.statusMessage = error.message;
|
|
111
|
+
res.end();
|
|
93
112
|
}
|
|
94
113
|
});
|
|
95
114
|
}
|
package/utils/exit.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
type EventNameType = "exit" | "SIGINT" | "SIGUSR1" | "SIGUSR2" | "SIGTERM" | "uncaughtException";
|
|
2
|
+
export declare function triggerExitEvent(eventName: EventNameType, ...args: any[]): void;
|
|
3
|
+
export declare function enableExitEvents(): void;
|
|
4
|
+
export declare function disableExitEvents(): void;
|
|
5
|
+
export declare function onExit(cb: (eventName: EventNameType, ...args: any[]) => void, priority?: number): () => boolean;
|
|
6
|
+
export {};
|
package/utils/exit.js
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.onExit = exports.disableExitEvents = exports.enableExitEvents = exports.triggerExitEvent = void 0;
|
|
4
|
+
const eventNames = [
|
|
5
|
+
`exit`,
|
|
6
|
+
`SIGINT`,
|
|
7
|
+
`SIGUSR1`,
|
|
8
|
+
`SIGUSR2`,
|
|
9
|
+
`uncaughtException`,
|
|
10
|
+
`SIGTERM`,
|
|
11
|
+
];
|
|
12
|
+
const listeners = new Set();
|
|
13
|
+
const disableExitDisposes = new Set();
|
|
14
|
+
function triggerExitEvent(eventName, ...args) {
|
|
15
|
+
if (!disableExitDisposes.size) {
|
|
16
|
+
process.emit(eventName, ...args);
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
try {
|
|
20
|
+
disableExitEvents();
|
|
21
|
+
const items = [...listeners].sort((a, b) => b.priority - a.priority);
|
|
22
|
+
for (const { cb } of items) {
|
|
23
|
+
try {
|
|
24
|
+
cb(eventName, ...args);
|
|
25
|
+
}
|
|
26
|
+
catch (_) { }
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
catch (_) {
|
|
30
|
+
process.exit(5);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
exports.triggerExitEvent = triggerExitEvent;
|
|
34
|
+
function enableExitEvents() {
|
|
35
|
+
disableExitEvents();
|
|
36
|
+
for (const eventName of eventNames) {
|
|
37
|
+
const listener = (...args) => triggerExitEvent(eventName, ...args);
|
|
38
|
+
process.on(eventName, listener);
|
|
39
|
+
disableExitDisposes.add(() => process.off(eventName, listener));
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
exports.enableExitEvents = enableExitEvents;
|
|
43
|
+
function disableExitEvents() {
|
|
44
|
+
for (const dispose of disableExitDisposes)
|
|
45
|
+
dispose();
|
|
46
|
+
disableExitDisposes.clear();
|
|
47
|
+
}
|
|
48
|
+
exports.disableExitEvents = disableExitEvents;
|
|
49
|
+
function onExit(cb, priority) {
|
|
50
|
+
if (!disableExitDisposes.size)
|
|
51
|
+
enableExitEvents();
|
|
52
|
+
const listener = { cb, priority: priority ?? 0 };
|
|
53
|
+
listeners.add(listener);
|
|
54
|
+
return () => listeners.delete(listener);
|
|
55
|
+
}
|
|
56
|
+
exports.onExit = onExit;
|
package/utils/fs.d.ts
CHANGED
|
@@ -133,4 +133,7 @@ export declare function fetchDiskStats(path: string): Promise<DiskStats>;
|
|
|
133
133
|
export declare function checkFreeDiskSpace(stat: DiskStats, inSize: string | number): Promise<void>;
|
|
134
134
|
export declare function ensureFreeDiskSpace(input: string[] | DiskStats, inSize: number | string): Promise<void>;
|
|
135
135
|
export declare function groupFiles(inFiles: string[], suffixes?: string[], gzSuffix?: string): [string[], Record<string, string>];
|
|
136
|
+
export declare function asFile(input: string | {
|
|
137
|
+
path: string;
|
|
138
|
+
}): Promise<[string, (() => Promise<void>) | undefined]>;
|
|
136
139
|
export {};
|
package/utils/fs.js
CHANGED
|
@@ -3,12 +3,13 @@ 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.groupFiles = exports.ensureFreeDiskSpace = exports.checkFreeDiskSpace = exports.fetchDiskStats = exports.initEmptyDir = exports.tryRm = exports.safeRename = exports.fetchData = exports.countFileLines = exports.createWriteStreamPool = exports.createFileScanner = exports.createProgress = exports.cpy = exports.isNotFoundError = exports.updateFileStats = exports.copyFileWithStreams = exports.waitForClose = exports.writeGitIgnoreList = exports.fastglobToGitIgnore = exports.forEachFile = exports.readDir = exports.readPartialFile = exports.fastFolderSizeAsync = exports.findFile = exports.parsePackageFile = exports.parseFile = exports.parseFileExtensions = exports.writeJSONFile = exports.existsFile = exports.existsDir = exports.safeStat = exports.ensureExistsDir = exports.ensureSingleFile = exports.ensureEmptyDir = exports.mkdirIfNotExists = exports.isLocalDir = exports.isEmptyDir = exports.isWSLSystem = void 0;
|
|
6
|
+
exports.asFile = exports.groupFiles = exports.ensureFreeDiskSpace = exports.checkFreeDiskSpace = exports.fetchDiskStats = exports.initEmptyDir = exports.tryRm = exports.safeRename = exports.fetchData = exports.countFileLines = exports.createWriteStreamPool = exports.createFileScanner = exports.createProgress = exports.cpy = exports.isNotFoundError = exports.updateFileStats = exports.copyFileWithStreams = exports.waitForClose = exports.writeGitIgnoreList = exports.fastglobToGitIgnore = exports.forEachFile = exports.readDir = exports.readPartialFile = exports.fastFolderSizeAsync = exports.findFile = exports.parsePackageFile = exports.parseFile = exports.parseFileExtensions = exports.writeJSONFile = exports.existsFile = exports.existsDir = exports.safeStat = exports.ensureExistsDir = exports.ensureSingleFile = exports.ensureEmptyDir = exports.mkdirIfNotExists = exports.isLocalDir = exports.isEmptyDir = exports.isWSLSystem = void 0;
|
|
7
|
+
const bytes_1 = require("./bytes");
|
|
7
8
|
const math_1 = require("./math");
|
|
8
9
|
const path_1 = require("./path");
|
|
9
10
|
const string_1 = require("./string");
|
|
11
|
+
const temp_1 = require("./temp");
|
|
10
12
|
const async_1 = require("async");
|
|
11
|
-
const bytes_1 = __importDefault(require("bytes"));
|
|
12
13
|
const fast_folder_size_1 = __importDefault(require("fast-folder-size"));
|
|
13
14
|
const fast_glob_1 = __importDefault(require("fast-glob"));
|
|
14
15
|
const fs_1 = require("fs");
|
|
@@ -238,7 +239,7 @@ async function writeGitIgnoreList(options) {
|
|
|
238
239
|
}
|
|
239
240
|
exports.writeGitIgnoreList = writeGitIgnoreList;
|
|
240
241
|
async function waitForClose(stream) {
|
|
241
|
-
return new Promise(
|
|
242
|
+
return new Promise((resolve, reject) => {
|
|
242
243
|
stream.on("close", resolve);
|
|
243
244
|
stream.on("error", reject);
|
|
244
245
|
return stream;
|
|
@@ -566,10 +567,10 @@ async function fetchDiskStats(path) {
|
|
|
566
567
|
}
|
|
567
568
|
exports.fetchDiskStats = fetchDiskStats;
|
|
568
569
|
async function checkFreeDiskSpace(stat, inSize) {
|
|
569
|
-
const humanSize = typeof inSize === "number" ? (0, bytes_1.
|
|
570
|
-
const size = bytes_1.
|
|
570
|
+
const humanSize = typeof inSize === "number" ? (0, bytes_1.formatBytes)(inSize) : inSize;
|
|
571
|
+
const size = typeof inSize === "number" ? inSize : (0, bytes_1.parseSize)(inSize);
|
|
571
572
|
if (stat.free < size)
|
|
572
|
-
throw new Error(`Free disk space is less than ${humanSize}: ${(0, bytes_1.
|
|
573
|
+
throw new Error(`Free disk space is less than ${humanSize}: ${(0, bytes_1.formatBytes)(stat.free)}/${(0, bytes_1.formatBytes)(stat.total)}`);
|
|
573
574
|
}
|
|
574
575
|
exports.checkFreeDiskSpace = checkFreeDiskSpace;
|
|
575
576
|
async function ensureFreeDiskSpace(input, inSize) {
|
|
@@ -608,3 +609,20 @@ function groupFiles(inFiles, suffixes, gzSuffix = ".tar.gz") {
|
|
|
608
609
|
return [Object.keys(grouped), compressed];
|
|
609
610
|
}
|
|
610
611
|
exports.groupFiles = groupFiles;
|
|
612
|
+
async function asFile(input) {
|
|
613
|
+
if (typeof input === "string") {
|
|
614
|
+
const dir = await (0, temp_1.mkTmpDir)("text-as-file");
|
|
615
|
+
const path = (0, path_2.join)(dir, "contents.txt");
|
|
616
|
+
await (0, promises_1.writeFile)(path, input);
|
|
617
|
+
return [
|
|
618
|
+
path,
|
|
619
|
+
async () => {
|
|
620
|
+
await (0, promises_1.rm)(dir, { recursive: true });
|
|
621
|
+
},
|
|
622
|
+
];
|
|
623
|
+
}
|
|
624
|
+
else {
|
|
625
|
+
return [input.path, undefined];
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
exports.asFile = asFile;
|
package/utils/http.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
|
+
import { BasicProgress } from "./progress";
|
|
2
3
|
import { IncomingMessage, Server } from "http";
|
|
3
4
|
export declare function closeServer(server: Server): Promise<void>;
|
|
4
5
|
export declare function readRequestData(req: IncomingMessage): Promise<string | undefined>;
|
|
@@ -14,6 +15,7 @@ export declare function downloadFile(url: string, output: string, options?: {
|
|
|
14
15
|
headers?: Record<string, string>;
|
|
15
16
|
query?: Record<string, string>;
|
|
16
17
|
timeout?: number;
|
|
18
|
+
onProgress?: (progress: BasicProgress) => void;
|
|
17
19
|
}): Promise<void>;
|
|
18
20
|
export declare function uploadFile(url: string, path: string, options?: {
|
|
19
21
|
headers?: Record<string, string>;
|
package/utils/http.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.uploadFile = exports.downloadFile = exports.post = exports.fetchJson = exports.readRequestData = exports.closeServer = void 0;
|
|
4
|
+
const math_1 = require("./math");
|
|
4
5
|
const fs_1 = require("fs");
|
|
5
6
|
const promises_1 = require("fs/promises");
|
|
6
7
|
const http_1 = require("http");
|
|
@@ -89,16 +90,34 @@ async function post(url, data, options = {}) {
|
|
|
89
90
|
}
|
|
90
91
|
exports.post = post;
|
|
91
92
|
async function downloadFile(url, output, options = {}) {
|
|
92
|
-
const timeout = options.timeout ?? 3600 *
|
|
93
|
+
const timeout = options.timeout ?? 3600 * 1000; // 60m
|
|
93
94
|
const file = (0, fs_1.createWriteStream)(output);
|
|
94
95
|
await new Promise((resolve, reject) => {
|
|
95
96
|
const req = request(href(url, options.query), {
|
|
96
97
|
headers: options.headers,
|
|
97
98
|
}, (res) => {
|
|
99
|
+
const contentLength = res.headers["content-length"] ?? "";
|
|
100
|
+
if (!/^\d+$/.test(contentLength))
|
|
101
|
+
return reject(new Error(`Invalid 'content-length': ${contentLength}`));
|
|
102
|
+
const total = Number(contentLength);
|
|
103
|
+
let current = 0;
|
|
98
104
|
if (res.statusCode === 200) {
|
|
105
|
+
if (options.onProgress) {
|
|
106
|
+
res.on("data", (chunk) => {
|
|
107
|
+
current += chunk.byteLength;
|
|
108
|
+
options.onProgress({
|
|
109
|
+
percent: (0, math_1.progressPercent)(total, current),
|
|
110
|
+
current,
|
|
111
|
+
total,
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
}
|
|
99
115
|
res
|
|
100
116
|
.on("error", async (error) => {
|
|
101
|
-
|
|
117
|
+
try {
|
|
118
|
+
file.destroy();
|
|
119
|
+
}
|
|
120
|
+
catch (_) { }
|
|
102
121
|
try {
|
|
103
122
|
await (0, promises_1.unlink)(output);
|
|
104
123
|
}
|
package/utils/list.d.ts
CHANGED
|
@@ -46,6 +46,7 @@ export declare class Listr3<T extends Listr3Context> extends Listr<void, "defaul
|
|
|
46
46
|
};
|
|
47
47
|
readonly resultMap: Record<string, Listr3TaskResult<T>>;
|
|
48
48
|
readonly resultList: Listr3TaskResult<T>[];
|
|
49
|
+
readonly logger: List3Logger;
|
|
49
50
|
protected execTimer: Timer;
|
|
50
51
|
constructor($options: {
|
|
51
52
|
streams?: Streams;
|
|
@@ -59,6 +60,7 @@ export declare class Listr3<T extends Listr3Context> extends Listr<void, "defaul
|
|
|
59
60
|
add(tasks: ListrTask<void, ListrGetRendererClassFromValue<"default">> | ListrTask<void, ListrGetRendererClassFromValue<"default">>[]): this;
|
|
60
61
|
getSummaryResult(): SummaryResult;
|
|
61
62
|
getResult(): (SummaryResult | Listr3TaskResult<T>)[];
|
|
63
|
+
protected release(): void;
|
|
62
64
|
exec(): Promise<(Listr3TaskResult<T> | SummaryResult)[]>;
|
|
63
65
|
}
|
|
64
66
|
export {};
|
package/utils/list.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.Listr3 = exports.List3Logger = void 0;
|
|
4
4
|
const date_1 = require("./date");
|
|
5
|
+
const exit_1 = require("./exit");
|
|
5
6
|
const stream_1 = require("./stream");
|
|
6
7
|
const listr2_1 = require("listr2");
|
|
7
8
|
class List3Logger extends listr2_1.ListrLogger {
|
|
@@ -17,8 +18,10 @@ class Listr3 extends listr2_1.Listr {
|
|
|
17
18
|
$options;
|
|
18
19
|
resultMap = {};
|
|
19
20
|
resultList = [];
|
|
21
|
+
logger;
|
|
20
22
|
execTimer;
|
|
21
23
|
constructor($options) {
|
|
24
|
+
const logger = new List3Logger();
|
|
22
25
|
super([], {
|
|
23
26
|
renderer: "default",
|
|
24
27
|
collectErrors: "minimal",
|
|
@@ -27,12 +30,12 @@ class Listr3 extends listr2_1.Listr {
|
|
|
27
30
|
}),
|
|
28
31
|
fallbackRenderer: "simple",
|
|
29
32
|
fallbackRendererOptions: {
|
|
30
|
-
logger:
|
|
33
|
+
logger: logger,
|
|
31
34
|
timestamp: listr2_1.PRESET_TIMESTAMP,
|
|
32
35
|
timer: listr2_1.PRESET_TIMER,
|
|
33
36
|
},
|
|
34
37
|
rendererOptions: {
|
|
35
|
-
logger:
|
|
38
|
+
logger: logger,
|
|
36
39
|
collapseSubtasks: false,
|
|
37
40
|
collapseErrors: false,
|
|
38
41
|
timer: listr2_1.PRESET_TIMER,
|
|
@@ -40,6 +43,7 @@ class Listr3 extends listr2_1.Listr {
|
|
|
40
43
|
});
|
|
41
44
|
this.$options = $options;
|
|
42
45
|
this.execTimer = (0, date_1.createTimer)();
|
|
46
|
+
this.logger = logger;
|
|
43
47
|
}
|
|
44
48
|
serializeKeyIndex(keyIndex) {
|
|
45
49
|
return typeof keyIndex !== "undefined"
|
|
@@ -127,7 +131,18 @@ class Listr3 extends listr2_1.Listr {
|
|
|
127
131
|
getResult() {
|
|
128
132
|
return [...this.resultList, this.getSummaryResult()];
|
|
129
133
|
}
|
|
134
|
+
release() {
|
|
135
|
+
for (const task of this.tasks)
|
|
136
|
+
if (task.isPending())
|
|
137
|
+
task.state$ = listr2_1.ListrTaskState.FAILED;
|
|
138
|
+
this["renderer"].end(new Error("Interrupted."));
|
|
139
|
+
}
|
|
130
140
|
async exec() {
|
|
141
|
+
const dispose = (0, exit_1.onExit)(() => {
|
|
142
|
+
this.$options.progressManager?.dispose();
|
|
143
|
+
this.execTimer.reset();
|
|
144
|
+
this.release();
|
|
145
|
+
}, 1);
|
|
131
146
|
try {
|
|
132
147
|
this.$options.progressManager?.start();
|
|
133
148
|
this.execTimer.reset();
|
|
@@ -138,7 +153,7 @@ class Listr3 extends listr2_1.Listr {
|
|
|
138
153
|
throw error;
|
|
139
154
|
}
|
|
140
155
|
finally {
|
|
141
|
-
|
|
156
|
+
dispose();
|
|
142
157
|
}
|
|
143
158
|
}
|
|
144
159
|
}
|
package/utils/process.d.ts
CHANGED
|
@@ -30,7 +30,7 @@ export interface ExecSettingsInterface {
|
|
|
30
30
|
stream: Readable;
|
|
31
31
|
};
|
|
32
32
|
log?: ExecLogSettingsType | boolean;
|
|
33
|
-
onSpawn?: (p: ChildProcess) =>
|
|
33
|
+
onSpawn?: (p: ChildProcess) => void | undefined;
|
|
34
34
|
stdout?: {
|
|
35
35
|
save?: boolean;
|
|
36
36
|
parseLines?: boolean | "skip-empty";
|
|
@@ -103,6 +103,4 @@ export declare function createProcess<O1 extends boolean, O2 extends boolean>(co
|
|
|
103
103
|
stderr: string;
|
|
104
104
|
} : {})>;
|
|
105
105
|
export declare function exec(command: string, argv?: string[], options?: SpawnOptions | null, settings?: ExecSettingsInterface): Promise<ExecResultType>;
|
|
106
|
-
type EventNameType = "exit" | "SIGINT" | "SIGUSR1" | "SIGUSR2" | "SIGTERM" | "uncaughtException";
|
|
107
|
-
export declare function onExit(cb: (eventName: EventNameType, ...args: any[]) => void): void;
|
|
108
106
|
export {};
|
package/utils/process.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.
|
|
6
|
+
exports.exec = exports.createProcess = exports.logProcessExec = exports.waitForClose = exports.parseStreamData = exports.logExecStderr = exports.logExecStdout = void 0;
|
|
7
7
|
const cli_1 = require("./cli");
|
|
8
8
|
const fs_1 = require("./fs");
|
|
9
9
|
const math_1 = require("./math");
|
|
@@ -179,46 +179,45 @@ async function exec(command, argv = [], options = null, settings = {}) {
|
|
|
179
179
|
else if (settings.log) {
|
|
180
180
|
log = settings.log;
|
|
181
181
|
}
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
pipe.
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
currentBytes: currentBytes,
|
|
202
|
-
progress: (0, math_1.progressPercent)(totalBytes, currentBytes),
|
|
203
|
-
});
|
|
182
|
+
if (log.exec)
|
|
183
|
+
logProcessExec(command, argv, {
|
|
184
|
+
env: options?.env,
|
|
185
|
+
envNames: log.envNames,
|
|
186
|
+
pipe: pipe?.stream,
|
|
187
|
+
toStderr: log.allToStderr,
|
|
188
|
+
});
|
|
189
|
+
if (typeof options?.cwd === "string" && !(await (0, fs_1.existsDir)(options.cwd)))
|
|
190
|
+
throw new Error(`Current working directory does not exist: ${options.cwd}`);
|
|
191
|
+
if (pipe?.stream instanceof fs_2.ReadStream && "onReadProgress" in pipe) {
|
|
192
|
+
const fileInfo = await (0, promises_1.stat)(pipe.stream.path);
|
|
193
|
+
const totalBytes = fileInfo.size;
|
|
194
|
+
let currentBytes = 0;
|
|
195
|
+
pipe.stream.on("data", (data) => {
|
|
196
|
+
currentBytes += data.length;
|
|
197
|
+
pipe.onReadProgress?.({
|
|
198
|
+
totalBytes: totalBytes,
|
|
199
|
+
currentBytes: currentBytes,
|
|
200
|
+
progress: (0, math_1.progressPercent)(totalBytes, currentBytes),
|
|
204
201
|
});
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
const p = (0, child_process_1.spawn)(command, argv, options ?? {});
|
|
205
|
+
settings.onSpawn?.(p);
|
|
206
|
+
let spawnError;
|
|
207
|
+
const spawnData = {
|
|
208
|
+
stdout: "",
|
|
209
|
+
stderr: "",
|
|
210
|
+
exitCode: 0,
|
|
211
|
+
};
|
|
212
|
+
let finishListeners = 1;
|
|
213
|
+
if (pipe?.stream instanceof fs_2.WriteStream)
|
|
214
|
+
finishListeners++;
|
|
215
|
+
if (settings.stdout?.parseLines)
|
|
216
|
+
finishListeners++;
|
|
217
|
+
if (settings.stderr?.parseLines)
|
|
218
|
+
finishListeners++;
|
|
219
|
+
let streamError;
|
|
220
|
+
return new Promise((resolve, reject) => {
|
|
222
221
|
const tryFinish = () => {
|
|
223
222
|
if (!--finishListeners)
|
|
224
223
|
finish();
|
|
@@ -336,17 +335,3 @@ async function exec(command, argv = [], options = null, settings = {}) {
|
|
|
336
335
|
});
|
|
337
336
|
}
|
|
338
337
|
exports.exec = exec;
|
|
339
|
-
const eventNames = [
|
|
340
|
-
`exit`,
|
|
341
|
-
`SIGINT`,
|
|
342
|
-
`SIGUSR1`,
|
|
343
|
-
`SIGUSR2`,
|
|
344
|
-
`uncaughtException`,
|
|
345
|
-
`SIGTERM`,
|
|
346
|
-
];
|
|
347
|
-
function onExit(cb) {
|
|
348
|
-
for (const eventName of eventNames) {
|
|
349
|
-
process.on(eventName, (...args) => cb(eventName, ...args));
|
|
350
|
-
}
|
|
351
|
-
}
|
|
352
|
-
exports.onExit = onExit;
|
package/utils/progress.d.ts
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
2
|
import { Timer } from "./date";
|
|
3
|
+
export type BasicProgress = {
|
|
4
|
+
percent: number;
|
|
5
|
+
current: number;
|
|
6
|
+
total: number;
|
|
7
|
+
};
|
|
3
8
|
export type ProgressStats = {
|
|
4
9
|
percent?: number;
|
|
5
10
|
total?: number;
|
|
@@ -12,29 +17,36 @@ export type Progress = {
|
|
|
12
17
|
absolute?: ProgressStats;
|
|
13
18
|
relative?: ProgressStats;
|
|
14
19
|
};
|
|
20
|
+
export type ProgressTty = "auto" | boolean;
|
|
21
|
+
export type ProgressMode = "auto" | "interval" | `interval:${number}` | boolean;
|
|
15
22
|
export declare class ProgressManager {
|
|
16
23
|
readonly options: {
|
|
17
24
|
verbose?: boolean;
|
|
18
25
|
/**
|
|
19
|
-
* @default
|
|
26
|
+
* @default false
|
|
27
|
+
*/
|
|
28
|
+
tty?: ProgressTty;
|
|
29
|
+
/**
|
|
30
|
+
* @default "interval"
|
|
20
31
|
*/
|
|
21
|
-
|
|
22
|
-
enabled?: boolean | "auto" | "interval";
|
|
23
|
-
interval?: number;
|
|
32
|
+
mode?: ProgressMode;
|
|
24
33
|
};
|
|
25
34
|
protected timer: Timer;
|
|
26
35
|
protected interval: Timer | undefined;
|
|
27
|
-
protected
|
|
28
|
-
|
|
29
|
-
readonly
|
|
36
|
+
protected intervalMs: number;
|
|
37
|
+
protected keydownListener: ((data: Buffer | undefined) => void) | undefined;
|
|
38
|
+
readonly tty: Exclude<ProgressTty, "auto">;
|
|
39
|
+
readonly mode: Exclude<ProgressMode, "auto" | `interval:${number}`>;
|
|
30
40
|
constructor(options: {
|
|
31
41
|
verbose?: boolean;
|
|
32
42
|
/**
|
|
33
|
-
* @default
|
|
43
|
+
* @default false
|
|
44
|
+
*/
|
|
45
|
+
tty?: ProgressTty;
|
|
46
|
+
/**
|
|
47
|
+
* @default "interval"
|
|
34
48
|
*/
|
|
35
|
-
|
|
36
|
-
enabled?: boolean | "auto" | "interval";
|
|
37
|
-
interval?: number;
|
|
49
|
+
mode?: ProgressMode;
|
|
38
50
|
});
|
|
39
51
|
elapsed(): number;
|
|
40
52
|
start(): void;
|
package/utils/progress.js
CHANGED
|
@@ -1,21 +1,20 @@
|
|
|
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
3
|
exports.renderProgressStats = exports.renderProgress = exports.ProgressManager = void 0;
|
|
4
|
+
const bytes_1 = require("./bytes");
|
|
7
5
|
const cli_1 = require("./cli");
|
|
8
6
|
const date_1 = require("./date");
|
|
9
|
-
const
|
|
7
|
+
const exit_1 = require("./exit");
|
|
10
8
|
const chalk_1 = require("chalk");
|
|
11
9
|
const readline_1 = require("readline");
|
|
12
10
|
class ProgressManager {
|
|
13
11
|
options;
|
|
14
12
|
timer = (0, date_1.createTimer)();
|
|
15
13
|
interval = (0, date_1.createTimer)();
|
|
14
|
+
intervalMs;
|
|
16
15
|
keydownListener;
|
|
17
16
|
tty;
|
|
18
|
-
|
|
17
|
+
mode;
|
|
19
18
|
constructor(options) {
|
|
20
19
|
this.options = options;
|
|
21
20
|
this.tty =
|
|
@@ -24,12 +23,21 @@ class ProgressManager {
|
|
|
24
23
|
? false
|
|
25
24
|
: process.stdout.isTTY
|
|
26
25
|
: !!options.tty;
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
?
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
26
|
+
const mode = options.mode === "auto"
|
|
27
|
+
? this.tty
|
|
28
|
+
? `interval:${300}`
|
|
29
|
+
: "interval"
|
|
30
|
+
: options.mode ?? "interval";
|
|
31
|
+
this.intervalMs = 1000;
|
|
32
|
+
if (typeof mode === "string" && mode.startsWith("interval:")) {
|
|
33
|
+
const [, ms] = mode.split(":");
|
|
34
|
+
this.mode = "interval";
|
|
35
|
+
if (/^\d+$/.test(ms))
|
|
36
|
+
this.intervalMs = Number(ms);
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
this.mode = mode;
|
|
40
|
+
}
|
|
33
41
|
}
|
|
34
42
|
elapsed() {
|
|
35
43
|
return this.timer.elapsed();
|
|
@@ -41,10 +49,10 @@ class ProgressManager {
|
|
|
41
49
|
process.stdin?.resume();
|
|
42
50
|
process.stdin?.setEncoding("utf8");
|
|
43
51
|
process.stdin?.on("keypress", (this.keydownListener = (inKey) => {
|
|
44
|
-
const key = inKey
|
|
52
|
+
const key = inKey?.toString() || "";
|
|
45
53
|
if (key === "\u0003") {
|
|
46
54
|
process.stdin.setRawMode?.(false);
|
|
47
|
-
|
|
55
|
+
(0, exit_1.triggerExitEvent)("SIGINT");
|
|
48
56
|
}
|
|
49
57
|
else if (/^(\r\n)|\r|\n$/.test(key)) {
|
|
50
58
|
this.interval = undefined;
|
|
@@ -59,11 +67,11 @@ class ProgressManager {
|
|
|
59
67
|
}
|
|
60
68
|
}
|
|
61
69
|
update(progress, cb) {
|
|
62
|
-
if (!this.
|
|
70
|
+
if (!this.mode)
|
|
63
71
|
return;
|
|
64
|
-
if (this.
|
|
72
|
+
if (this.mode === "interval") {
|
|
65
73
|
if (this.interval) {
|
|
66
|
-
if (!this.interval.reset(this.
|
|
74
|
+
if (!this.interval.reset(this.intervalMs))
|
|
67
75
|
return;
|
|
68
76
|
}
|
|
69
77
|
else {
|
|
@@ -89,7 +97,7 @@ function renderProgressStats(stats, progressBar) {
|
|
|
89
97
|
text.push(`${stats.percent.toFixed(2).padStart(5, " ")}%`);
|
|
90
98
|
}
|
|
91
99
|
if (typeof stats.current === "number" || typeof stats.total === "number") {
|
|
92
|
-
const format = (value) => stats.format === "size" ? (0, bytes_1.
|
|
100
|
+
const format = (value) => stats.format === "size" ? (0, bytes_1.formatBytes)(value) : value.toString();
|
|
93
101
|
const pad = 8;
|
|
94
102
|
let values = [];
|
|
95
103
|
if (typeof stats.current === "number" && typeof stats.total === "number") {
|
package/utils/tar.d.ts
CHANGED
|
@@ -1,12 +1,8 @@
|
|
|
1
|
+
import { BasicProgress } from "./progress";
|
|
1
2
|
import type { JSONSchema7 } from "json-schema";
|
|
2
|
-
export type Progress = {
|
|
3
|
-
percent: number;
|
|
4
|
-
current: number;
|
|
5
|
-
total: number;
|
|
6
|
-
};
|
|
7
3
|
export type TarEntry = {
|
|
8
4
|
path: string;
|
|
9
|
-
progress:
|
|
5
|
+
progress: BasicProgress;
|
|
10
6
|
};
|
|
11
7
|
export type CoresOptions = number | {
|
|
12
8
|
percent: number;
|