@datatruck/cli 0.29.1 → 0.30.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 +1 -1
- package/CHANGELOG.md +14 -0
- package/Command/CleanCacheCommand.js +2 -5
- package/Command/SnapshotsCommand.js +2 -5
- package/Config/Config.js +22 -0
- package/Repository/DatatruckRepository.js +41 -15
- package/cli.js +2 -2
- 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 +17 -1
- 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 +6 -1
- package/utils/progress.js +5 -7
- package/utils/tar.d.ts +2 -6
- package/utils/virtual-fs.d.ts +7 -1
- package/utils/virtual-fs.js +3 -0
package/Action/BackupAction.js
CHANGED
|
@@ -153,7 +153,7 @@ class BackupAction {
|
|
|
153
153
|
.map((item) => [
|
|
154
154
|
(0, cli_1.renderResult)(item.error),
|
|
155
155
|
renderTitle(item, true),
|
|
156
|
-
renderData(item, true),
|
|
156
|
+
renderData(item, true, result),
|
|
157
157
|
(0, date_1.duration)(item.elapsed),
|
|
158
158
|
(0, cli_1.renderError)(item.error, options.verbose),
|
|
159
159
|
]),
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# @datatruck/cli
|
|
2
2
|
|
|
3
|
+
## 0.30.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [`e28b12d`](https://github.com/swordev/datatruck/commit/e28b12d08c36844317e506552443825ab3333139) Thanks [@juanrgm](https://github.com/juanrgm)! - Add `allowlist` option to the datatruck server
|
|
8
|
+
|
|
9
|
+
### Patch Changes
|
|
10
|
+
|
|
11
|
+
- [`2f63e67`](https://github.com/swordev/datatruck/commit/2f63e67ee532892fda3a9d06e46336a42834e5ed) Thanks [@juanrgm](https://github.com/juanrgm)! - Add download progress in datatruck repository
|
|
12
|
+
|
|
13
|
+
- [`258e933`](https://github.com/swordev/datatruck/commit/258e93385d9b6f93a435a28a2b26e53ea1763755) Thanks [@juanrgm](https://github.com/juanrgm)! - Copy backups safely
|
|
14
|
+
|
|
15
|
+
- [`82b4c67`](https://github.com/swordev/datatruck/commit/82b4c67c55eee4f40753b48eb91345e6d6789f72) Thanks [@juanrgm](https://github.com/juanrgm)! - Fix exit event
|
|
16
|
+
|
|
3
17
|
## 0.29.1
|
|
4
18
|
|
|
5
19
|
### Patch Changes
|
|
@@ -1,13 +1,10 @@
|
|
|
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.CleanCacheCommand = void 0;
|
|
7
4
|
const CleanCacheAction_1 = require("../Action/CleanCacheAction");
|
|
8
5
|
const DataFormat_1 = require("../utils/DataFormat");
|
|
6
|
+
const bytes_1 = require("../utils/bytes");
|
|
9
7
|
const CommandAbstract_1 = require("./CommandAbstract");
|
|
10
|
-
const bytes_1 = __importDefault(require("bytes"));
|
|
11
8
|
class CleanCacheCommand extends CommandAbstract_1.CommandAbstract {
|
|
12
9
|
onOptions() {
|
|
13
10
|
return this.returnsOptions({});
|
|
@@ -29,7 +26,7 @@ class CleanCacheCommand extends CommandAbstract_1.CommandAbstract {
|
|
|
29
26
|
value: "Freed disk space",
|
|
30
27
|
},
|
|
31
28
|
],
|
|
32
|
-
rows: () => [[result.path, (0, bytes_1.
|
|
29
|
+
rows: () => [[result.path, (0, bytes_1.formatBytes)(result.freedSize)]],
|
|
33
30
|
},
|
|
34
31
|
});
|
|
35
32
|
await cleanCache.exec();
|
|
@@ -1,15 +1,12 @@
|
|
|
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.SnapshotsCommand = void 0;
|
|
7
4
|
const ConfigAction_1 = require("../Action/ConfigAction");
|
|
8
5
|
const SnapshotsAction_1 = require("../Action/SnapshotsAction");
|
|
9
6
|
const DataFormat_1 = require("../utils/DataFormat");
|
|
7
|
+
const bytes_1 = require("../utils/bytes");
|
|
10
8
|
const string_1 = require("../utils/string");
|
|
11
9
|
const CommandAbstract_1 = require("./CommandAbstract");
|
|
12
|
-
const bytes_1 = __importDefault(require("bytes"));
|
|
13
10
|
class SnapshotsCommand extends CommandAbstract_1.CommandAbstract {
|
|
14
11
|
onOptions() {
|
|
15
12
|
const groupByValues = [
|
|
@@ -139,7 +136,7 @@ class SnapshotsCommand extends CommandAbstract_1.CommandAbstract {
|
|
|
139
136
|
item.date.replace("T", " ").replace("Z", ""),
|
|
140
137
|
item.packageName,
|
|
141
138
|
item.packageTaskName || "",
|
|
142
|
-
(0, bytes_1.
|
|
139
|
+
(0, bytes_1.formatBytes)(item.size),
|
|
143
140
|
item.repositoryName,
|
|
144
141
|
item.repositoryType,
|
|
145
142
|
]),
|
package/Config/Config.js
CHANGED
|
@@ -39,6 +39,7 @@ exports.configDefinition = {
|
|
|
39
39
|
additionalProperties: false,
|
|
40
40
|
properties: {
|
|
41
41
|
path: { type: "string" },
|
|
42
|
+
log: { type: "boolean" },
|
|
42
43
|
users: {
|
|
43
44
|
type: "array",
|
|
44
45
|
items: {
|
|
@@ -58,6 +59,27 @@ exports.configDefinition = {
|
|
|
58
59
|
address: { type: "string" },
|
|
59
60
|
},
|
|
60
61
|
},
|
|
62
|
+
trustProxy: {
|
|
63
|
+
anyOf: [
|
|
64
|
+
{ type: "boolean" },
|
|
65
|
+
{
|
|
66
|
+
type: "object",
|
|
67
|
+
additionalProperties: false,
|
|
68
|
+
required: ["remoteAddressHeader"],
|
|
69
|
+
properties: {
|
|
70
|
+
remoteAddressHeader: { type: "string" },
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
],
|
|
74
|
+
},
|
|
75
|
+
allowlist: {
|
|
76
|
+
type: "object",
|
|
77
|
+
additionalProperties: false,
|
|
78
|
+
properties: {
|
|
79
|
+
enabled: { type: "boolean" },
|
|
80
|
+
remoteAddreses: (0, DefinitionEnum_1.makeRef)(DefinitionEnum_1.DefinitionEnum.stringListUtil),
|
|
81
|
+
},
|
|
82
|
+
},
|
|
61
83
|
},
|
|
62
84
|
},
|
|
63
85
|
prunePolicy: (0, DefinitionEnum_1.makeRef)(DefinitionEnum_1.DefinitionEnum.prunePolicy),
|
|
@@ -58,7 +58,9 @@ class DatatruckRepository extends RepositoryAbstract_1.RepositoryAbstract {
|
|
|
58
58
|
static zipBasenameTpl = `.*.dd.tar.gz`;
|
|
59
59
|
static buildSnapshotName(snapshot, pkg) {
|
|
60
60
|
const date = snapshot.date.replace(/:/g, "-");
|
|
61
|
-
const pkgName = encodeURIComponent(pkg.name)
|
|
61
|
+
const pkgName = encodeURIComponent(pkg.name)
|
|
62
|
+
.replace(/%40/g, "@")
|
|
63
|
+
.replace("_", "%5F");
|
|
62
64
|
const snapshotShortId = snapshot.id.slice(0, 8);
|
|
63
65
|
return `${date}_${pkgName}_${snapshotShortId}`;
|
|
64
66
|
}
|
|
@@ -260,38 +262,62 @@ class DatatruckRepository extends RepositoryAbstract_1.RepositoryAbstract {
|
|
|
260
262
|
const snapshotName = DatatruckRepository.buildSnapshotName(data.snapshot, data.package);
|
|
261
263
|
if (data.options.verbose)
|
|
262
264
|
(0, cli_1.logExec)(`Copying backup files to ${data.mirrorRepositoryConfig.backend}`);
|
|
263
|
-
|
|
264
|
-
await targetFs.
|
|
265
|
+
const tmpSnapshotName = `${snapshotName}_tmp`;
|
|
266
|
+
if (await targetFs.existsDir(snapshotName))
|
|
267
|
+
await targetFs.ensureEmptyDir(snapshotName);
|
|
268
|
+
try {
|
|
269
|
+
await targetFs.rmAll(tmpSnapshotName);
|
|
270
|
+
}
|
|
271
|
+
catch (_) { }
|
|
272
|
+
await targetFs.mkdir(tmpSnapshotName);
|
|
273
|
+
await targetFs.ensureEmptyDir(tmpSnapshotName);
|
|
265
274
|
const entries = await sourceFs.readdir(snapshotName);
|
|
266
275
|
const total = entries.length;
|
|
267
276
|
let current = 0;
|
|
268
277
|
for (const entry of entries) {
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
});
|
|
278
|
+
const absolute = {
|
|
279
|
+
current,
|
|
280
|
+
description: "Copying",
|
|
281
|
+
payload: entry,
|
|
282
|
+
total,
|
|
283
|
+
percent: (0, math_1.progressPercent)(total, current),
|
|
284
|
+
};
|
|
285
|
+
data.onProgress({ absolute });
|
|
278
286
|
current++;
|
|
279
287
|
const sourceEntry = `${snapshotName}/${entry}`;
|
|
288
|
+
const targetEntry = `${tmpSnapshotName}/${entry}`;
|
|
280
289
|
if (targetFs.isLocal()) {
|
|
281
|
-
await sourceFs.download(sourceEntry, targetFs.resolvePath(
|
|
290
|
+
await sourceFs.download(sourceEntry, targetFs.resolvePath(targetEntry), {
|
|
291
|
+
onProgress: (progress) => data.onProgress({
|
|
292
|
+
absolute,
|
|
293
|
+
relative: {
|
|
294
|
+
description: "Downloading",
|
|
295
|
+
...progress,
|
|
296
|
+
},
|
|
297
|
+
}),
|
|
298
|
+
});
|
|
282
299
|
}
|
|
283
300
|
else {
|
|
284
301
|
const tempDir = await (0, temp_1.mkTmpDir)(exports.datatruckRepositoryName, "repo", "remote-copy", entry);
|
|
285
302
|
const tempFile = (0, path_1.join)(tempDir, entry);
|
|
286
303
|
try {
|
|
287
|
-
await sourceFs.download(sourceEntry, tempFile
|
|
288
|
-
|
|
304
|
+
await sourceFs.download(sourceEntry, tempFile, {
|
|
305
|
+
onProgress: (progress) => data.onProgress({
|
|
306
|
+
absolute,
|
|
307
|
+
relative: {
|
|
308
|
+
description: "Downloading",
|
|
309
|
+
...progress,
|
|
310
|
+
},
|
|
311
|
+
}),
|
|
312
|
+
});
|
|
313
|
+
await targetFs.upload(tempFile, targetEntry);
|
|
289
314
|
}
|
|
290
315
|
finally {
|
|
291
316
|
await (0, fs_1.tryRm)(tempFile);
|
|
292
317
|
}
|
|
293
318
|
}
|
|
294
319
|
}
|
|
320
|
+
await targetFs.rename(tmpSnapshotName, snapshotName);
|
|
295
321
|
}
|
|
296
322
|
async restore(data) {
|
|
297
323
|
const fs = (0, client_1.createFs)(this.config.backend);
|
package/cli.js
CHANGED
|
@@ -9,8 +9,8 @@ const AppError_1 = require("./Error/AppError");
|
|
|
9
9
|
const CommandFactory_1 = require("./Factory/CommandFactory");
|
|
10
10
|
const globalData_1 = __importDefault(require("./globalData"));
|
|
11
11
|
const cli_1 = require("./utils/cli");
|
|
12
|
+
const exit_1 = require("./utils/exit");
|
|
12
13
|
const fs_1 = require("./utils/fs");
|
|
13
|
-
const process_1 = require("./utils/process");
|
|
14
14
|
const string_1 = require("./utils/string");
|
|
15
15
|
const temp_1 = require("./utils/temp");
|
|
16
16
|
const chalk_1 = require("chalk");
|
|
@@ -112,7 +112,7 @@ exports.buildArgs = buildArgs;
|
|
|
112
112
|
function parseArgs(args) {
|
|
113
113
|
program.parse(args);
|
|
114
114
|
const verbose = getGlobalOptions().verbose;
|
|
115
|
-
(0,
|
|
115
|
+
(0, exit_1.onExit)((eventName, error) => {
|
|
116
116
|
if (eventName !== "exit") {
|
|
117
117
|
process.stdout.write(cli_1.showCursorCommand);
|
|
118
118
|
console.info(`\nClosing... (reason: ${eventName})`);
|
package/config.schema.json
CHANGED
|
@@ -1051,6 +1051,9 @@
|
|
|
1051
1051
|
"path": {
|
|
1052
1052
|
"type": "string"
|
|
1053
1053
|
},
|
|
1054
|
+
"log": {
|
|
1055
|
+
"type": "boolean"
|
|
1056
|
+
},
|
|
1054
1057
|
"users": {
|
|
1055
1058
|
"type": "array",
|
|
1056
1059
|
"items": {
|
|
@@ -1077,6 +1080,37 @@
|
|
|
1077
1080
|
"type": "string"
|
|
1078
1081
|
}
|
|
1079
1082
|
}
|
|
1083
|
+
},
|
|
1084
|
+
"trustProxy": {
|
|
1085
|
+
"anyOf": [
|
|
1086
|
+
{
|
|
1087
|
+
"type": "boolean"
|
|
1088
|
+
},
|
|
1089
|
+
{
|
|
1090
|
+
"type": "object",
|
|
1091
|
+
"additionalProperties": false,
|
|
1092
|
+
"required": [
|
|
1093
|
+
"remoteAddressHeader"
|
|
1094
|
+
],
|
|
1095
|
+
"properties": {
|
|
1096
|
+
"remoteAddressHeader": {
|
|
1097
|
+
"type": "string"
|
|
1098
|
+
}
|
|
1099
|
+
}
|
|
1100
|
+
}
|
|
1101
|
+
]
|
|
1102
|
+
},
|
|
1103
|
+
"allowlist": {
|
|
1104
|
+
"type": "object",
|
|
1105
|
+
"additionalProperties": false,
|
|
1106
|
+
"properties": {
|
|
1107
|
+
"enabled": {
|
|
1108
|
+
"type": "boolean"
|
|
1109
|
+
},
|
|
1110
|
+
"remoteAddreses": {
|
|
1111
|
+
"$ref": "#/definitions/stringlist-util"
|
|
1112
|
+
}
|
|
1113
|
+
}
|
|
1080
1114
|
}
|
|
1081
1115
|
}
|
|
1082
1116
|
},
|
package/package.json
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@datatruck/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.30.0",
|
|
4
4
|
"dependencies": {
|
|
5
5
|
"@supercharge/promise-pool": "^3.1.0",
|
|
6
6
|
"ajv": "^8.12.0",
|
|
7
7
|
"async": "^3.2.4",
|
|
8
|
-
"bytes": "^3.1.2",
|
|
9
8
|
"chalk": "^4.1.2",
|
|
10
9
|
"commander": "^11.0.0",
|
|
11
10
|
"dayjs": "^1.11.10",
|
package/utils/bytes.d.ts
ADDED
package/utils/bytes.js
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.parseSize = exports.formatBytes = void 0;
|
|
4
|
+
const units = ["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
|
|
5
|
+
const sizeRegex = new RegExp(`^(\\d+(?:\\.\\d+)?)\\s*(${units.join("|")})$`, "i");
|
|
6
|
+
function formatBytes(bytes) {
|
|
7
|
+
let u = 0;
|
|
8
|
+
let n = bytes;
|
|
9
|
+
if (bytes < 0n)
|
|
10
|
+
throw new Error(`Invalid bytes: ${bytes.toString()}`);
|
|
11
|
+
while (n >= 1024n && ++u)
|
|
12
|
+
n = n / 1024;
|
|
13
|
+
return Number(n).toFixed(n < 10 && u > 0 ? 1 : 0) + units[u];
|
|
14
|
+
}
|
|
15
|
+
exports.formatBytes = formatBytes;
|
|
16
|
+
function parseSize(size) {
|
|
17
|
+
const matches = sizeRegex.exec(size);
|
|
18
|
+
if (!matches)
|
|
19
|
+
throw new Error(`Invalid size: ${size}`);
|
|
20
|
+
const [, value, unit] = matches;
|
|
21
|
+
const unitIndex = units.findIndex((v) => v === unit.toUpperCase());
|
|
22
|
+
if (unitIndex === -1)
|
|
23
|
+
throw new Error(`Unit not found: ${unit}`);
|
|
24
|
+
let result = Number(value);
|
|
25
|
+
for (let i = 0; i < unitIndex; ++i)
|
|
26
|
+
result *= 1024;
|
|
27
|
+
return result;
|
|
28
|
+
}
|
|
29
|
+
exports.parseSize = parseSize;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { DiskStats } from "../fs";
|
|
2
|
+
import { BasicProgress } from "../progress";
|
|
2
3
|
import { AbstractFs, FsOptions } from "../virtual-fs";
|
|
3
4
|
export declare class RemoteFs extends AbstractFs {
|
|
4
5
|
readonly options: FsOptions;
|
|
@@ -9,6 +10,7 @@ export declare class RemoteFs extends AbstractFs {
|
|
|
9
10
|
protected fetchJson(name: string, params: any[]): Promise<any>;
|
|
10
11
|
protected post(name: string, params: any[], data: string): Promise<void>;
|
|
11
12
|
existsDir(path: string): Promise<any>;
|
|
13
|
+
rename(source: string, target: string): Promise<any>;
|
|
12
14
|
mkdir(path: string): Promise<any>;
|
|
13
15
|
readFile(path: string): Promise<any>;
|
|
14
16
|
readdir(path: string): Promise<any>;
|
|
@@ -18,7 +20,10 @@ export declare class RemoteFs extends AbstractFs {
|
|
|
18
20
|
rmAll(path: string): Promise<void>;
|
|
19
21
|
fetchDiskStats(path: string): Promise<DiskStats>;
|
|
20
22
|
upload(source: string, target: string): Promise<void>;
|
|
21
|
-
download(source: string, target: string,
|
|
23
|
+
download(source: string, target: string, options?: {
|
|
24
|
+
timeout?: number;
|
|
25
|
+
onProgress?: (progress: BasicProgress) => void;
|
|
26
|
+
}): Promise<void>;
|
|
22
27
|
}
|
|
23
28
|
export declare function isRemoteBackend(backend: string): boolean;
|
|
24
29
|
export declare function createFs(backend: string): AbstractFs;
|
|
@@ -44,6 +44,9 @@ class RemoteFs extends virtual_fs_1.AbstractFs {
|
|
|
44
44
|
async existsDir(path) {
|
|
45
45
|
return await this.fetchJson("existsDir", [path]);
|
|
46
46
|
}
|
|
47
|
+
async rename(source, target) {
|
|
48
|
+
return await this.fetchJson("rename", [source, target]);
|
|
49
|
+
}
|
|
47
50
|
async mkdir(path) {
|
|
48
51
|
return await this.fetchJson("mkdir", [path]);
|
|
49
52
|
}
|
|
@@ -76,13 +79,11 @@ class RemoteFs extends virtual_fs_1.AbstractFs {
|
|
|
76
79
|
},
|
|
77
80
|
});
|
|
78
81
|
}
|
|
79
|
-
async download(source, target,
|
|
82
|
+
async download(source, target, options = {}) {
|
|
80
83
|
await (0, http_1.downloadFile)(`${this.url}/download`, target, {
|
|
81
|
-
|
|
84
|
+
...options,
|
|
82
85
|
headers: this.headers,
|
|
83
|
-
query: {
|
|
84
|
-
params: JSON.stringify([source]),
|
|
85
|
-
},
|
|
86
|
+
query: { params: JSON.stringify([source]) },
|
|
86
87
|
});
|
|
87
88
|
}
|
|
88
89
|
}
|
|
@@ -12,6 +12,16 @@ export type DatatruckServerOptions = {
|
|
|
12
12
|
address?: string;
|
|
13
13
|
};
|
|
14
14
|
users?: User[];
|
|
15
|
+
trustProxy?: true | {
|
|
16
|
+
remoteAddressHeader: string;
|
|
17
|
+
};
|
|
18
|
+
allowlist?: {
|
|
19
|
+
/**
|
|
20
|
+
* @default true
|
|
21
|
+
*/
|
|
22
|
+
enabled?: boolean;
|
|
23
|
+
remoteAddreses?: string[];
|
|
24
|
+
};
|
|
15
25
|
};
|
|
16
26
|
export declare const headerKey: {
|
|
17
27
|
user: string;
|
|
@@ -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.remoteAddreses) {
|
|
30
|
+
const remoteAddress = getRemoteAddress(req, options);
|
|
31
|
+
if (!remoteAddress || list.remoteAddreses.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,13 +90,28 @@ 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
|
file.destroy();
|
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;
|
|
@@ -24,7 +29,7 @@ export declare class ProgressManager {
|
|
|
24
29
|
};
|
|
25
30
|
protected timer: Timer;
|
|
26
31
|
protected interval: Timer | undefined;
|
|
27
|
-
protected keydownListener: ((data: Buffer) => void) | undefined;
|
|
32
|
+
protected keydownListener: ((data: Buffer | undefined) => void) | undefined;
|
|
28
33
|
readonly tty: boolean;
|
|
29
34
|
readonly enabled: boolean | "interval";
|
|
30
35
|
constructor(options: {
|
package/utils/progress.js
CHANGED
|
@@ -1,12 +1,10 @@
|
|
|
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 {
|
|
@@ -41,10 +39,10 @@ class ProgressManager {
|
|
|
41
39
|
process.stdin?.resume();
|
|
42
40
|
process.stdin?.setEncoding("utf8");
|
|
43
41
|
process.stdin?.on("keypress", (this.keydownListener = (inKey) => {
|
|
44
|
-
const key = inKey
|
|
42
|
+
const key = inKey?.toString() || "";
|
|
45
43
|
if (key === "\u0003") {
|
|
46
44
|
process.stdin.setRawMode?.(false);
|
|
47
|
-
|
|
45
|
+
(0, exit_1.triggerExitEvent)("SIGINT");
|
|
48
46
|
}
|
|
49
47
|
else if (/^(\r\n)|\r|\n$/.test(key)) {
|
|
50
48
|
this.interval = undefined;
|
|
@@ -89,7 +87,7 @@ function renderProgressStats(stats, progressBar) {
|
|
|
89
87
|
text.push(`${stats.percent.toFixed(2).padStart(5, " ")}%`);
|
|
90
88
|
}
|
|
91
89
|
if (typeof stats.current === "number" || typeof stats.total === "number") {
|
|
92
|
-
const format = (value) => stats.format === "size" ? (0, bytes_1.
|
|
90
|
+
const format = (value) => stats.format === "size" ? (0, bytes_1.formatBytes)(value) : value.toString();
|
|
93
91
|
const pad = 8;
|
|
94
92
|
let values = [];
|
|
95
93
|
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;
|
package/utils/virtual-fs.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { DiskStats } from "./fs";
|
|
2
|
+
import { BasicProgress } from "./progress";
|
|
2
3
|
export declare function resolvePath(path: string): string;
|
|
3
4
|
export type FsOptions = {
|
|
4
5
|
backend: string;
|
|
@@ -10,6 +11,7 @@ export declare abstract class AbstractFs {
|
|
|
10
11
|
abstract isLocal(): boolean;
|
|
11
12
|
isRemote(): boolean;
|
|
12
13
|
abstract existsDir(path: string): Promise<boolean>;
|
|
14
|
+
abstract rename(source: string, target: string): Promise<void>;
|
|
13
15
|
abstract mkdir(path: string): Promise<void>;
|
|
14
16
|
abstract readFile(path: string): Promise<string>;
|
|
15
17
|
abstract rmAll(path: string): Promise<void>;
|
|
@@ -18,12 +20,16 @@ export declare abstract class AbstractFs {
|
|
|
18
20
|
abstract ensureEmptyDir(path: string): Promise<void>;
|
|
19
21
|
abstract writeFile(path: string, contents: string): Promise<void>;
|
|
20
22
|
abstract upload(source: string, target: string): Promise<void>;
|
|
21
|
-
abstract download(source: string, target: string
|
|
23
|
+
abstract download(source: string, target: string, options?: {
|
|
24
|
+
timeout?: number;
|
|
25
|
+
onProgress?: (progress: BasicProgress) => void;
|
|
26
|
+
}): Promise<void>;
|
|
22
27
|
abstract fetchDiskStats(source: string): Promise<DiskStats>;
|
|
23
28
|
}
|
|
24
29
|
export declare class LocalFs extends AbstractFs {
|
|
25
30
|
isLocal(): boolean;
|
|
26
31
|
existsDir(path: string): Promise<boolean>;
|
|
32
|
+
rename(source: string, target: string): Promise<void>;
|
|
27
33
|
mkdir(path: string): Promise<void>;
|
|
28
34
|
ensureEmptyDir(path: string): Promise<void>;
|
|
29
35
|
readFile(path: string): Promise<string>;
|
package/utils/virtual-fs.js
CHANGED
|
@@ -29,6 +29,9 @@ class LocalFs extends AbstractFs {
|
|
|
29
29
|
async existsDir(path) {
|
|
30
30
|
return (0, fs_1.existsDir)(this.resolvePath(path));
|
|
31
31
|
}
|
|
32
|
+
async rename(source, target) {
|
|
33
|
+
await (0, promises_1.rename)(this.resolvePath(source), this.resolvePath(target));
|
|
34
|
+
}
|
|
32
35
|
async mkdir(path) {
|
|
33
36
|
await (0, fs_1.mkdirIfNotExists)(this.resolvePath(path));
|
|
34
37
|
}
|