openapi-sdkgen 10.1.0 → 10.1.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/.releaseway/native.json +18 -18
- package/.releaseway/runtime.cjs +306 -144
- package/package.json +1 -1
package/.releaseway/native.json
CHANGED
|
@@ -1,48 +1,48 @@
|
|
|
1
1
|
{
|
|
2
2
|
"repository": "jinyongp/openapi-sdkgen",
|
|
3
|
-
"version": "10.1.
|
|
4
|
-
"tag": "v10.1.
|
|
3
|
+
"version": "10.1.1",
|
|
4
|
+
"tag": "v10.1.1",
|
|
5
5
|
"cacheEnv": "OPENAPI_SDKGEN_CACHE_DIR",
|
|
6
6
|
"targets": {
|
|
7
7
|
"darwin-arm64": {
|
|
8
|
-
"asset": "openapi-sdkgen_10.1.
|
|
8
|
+
"asset": "openapi-sdkgen_10.1.1_darwin_arm64.tar.gz",
|
|
9
9
|
"executable": "openapi-sdkgen",
|
|
10
|
-
"sha256": "
|
|
10
|
+
"sha256": "18e7efeb53d21c7c1b157d8b8ff9472343ba5f3828fa1cd9096f6072ef1f4310"
|
|
11
11
|
},
|
|
12
12
|
"darwin-x64": {
|
|
13
|
-
"asset": "openapi-sdkgen_10.1.
|
|
13
|
+
"asset": "openapi-sdkgen_10.1.1_darwin_amd64.tar.gz",
|
|
14
14
|
"executable": "openapi-sdkgen",
|
|
15
|
-
"sha256": "
|
|
15
|
+
"sha256": "0da26a393f3f92a90ecbc0a3b5cd420726f46cf6b27d647d11d47aa6d9c25544"
|
|
16
16
|
},
|
|
17
17
|
"linux-arm64-gnu": {
|
|
18
|
-
"asset": "openapi-sdkgen_10.1.
|
|
18
|
+
"asset": "openapi-sdkgen_10.1.1_linux_arm64.tar.gz",
|
|
19
19
|
"executable": "openapi-sdkgen",
|
|
20
|
-
"sha256": "
|
|
20
|
+
"sha256": "e107bd4c49bb489885a9ac992ef0c7ebaef97039e815aad0793308694a3d52f9"
|
|
21
21
|
},
|
|
22
22
|
"linux-arm64-musl": {
|
|
23
|
-
"asset": "openapi-sdkgen_10.1.
|
|
23
|
+
"asset": "openapi-sdkgen_10.1.1_linux_arm64.tar.gz",
|
|
24
24
|
"executable": "openapi-sdkgen",
|
|
25
|
-
"sha256": "
|
|
25
|
+
"sha256": "e107bd4c49bb489885a9ac992ef0c7ebaef97039e815aad0793308694a3d52f9"
|
|
26
26
|
},
|
|
27
27
|
"linux-x64-gnu": {
|
|
28
|
-
"asset": "openapi-sdkgen_10.1.
|
|
28
|
+
"asset": "openapi-sdkgen_10.1.1_linux_amd64.tar.gz",
|
|
29
29
|
"executable": "openapi-sdkgen",
|
|
30
|
-
"sha256": "
|
|
30
|
+
"sha256": "9c4e04e87106e8dd0b71b0c5a464fa8c0e1d126dc157b84b6f6ad8313f45a231"
|
|
31
31
|
},
|
|
32
32
|
"linux-x64-musl": {
|
|
33
|
-
"asset": "openapi-sdkgen_10.1.
|
|
33
|
+
"asset": "openapi-sdkgen_10.1.1_linux_amd64.tar.gz",
|
|
34
34
|
"executable": "openapi-sdkgen",
|
|
35
|
-
"sha256": "
|
|
35
|
+
"sha256": "9c4e04e87106e8dd0b71b0c5a464fa8c0e1d126dc157b84b6f6ad8313f45a231"
|
|
36
36
|
},
|
|
37
37
|
"win32-arm64": {
|
|
38
|
-
"asset": "openapi-sdkgen_10.1.
|
|
38
|
+
"asset": "openapi-sdkgen_10.1.1_windows_arm64.zip",
|
|
39
39
|
"executable": "openapi-sdkgen.exe",
|
|
40
|
-
"sha256": "
|
|
40
|
+
"sha256": "45d3ed65b5ee32a5c79f5ebe8a50dc0f90edc467733b95b26c514ffb9dd3ba42"
|
|
41
41
|
},
|
|
42
42
|
"win32-x64": {
|
|
43
|
-
"asset": "openapi-sdkgen_10.1.
|
|
43
|
+
"asset": "openapi-sdkgen_10.1.1_windows_amd64.zip",
|
|
44
44
|
"executable": "openapi-sdkgen.exe",
|
|
45
|
-
"sha256": "
|
|
45
|
+
"sha256": "7e043b2eb8eb0ef7150a2f81411df8a641040ee1ed54fb122754a82d6cfeba95"
|
|
46
46
|
}
|
|
47
47
|
}
|
|
48
48
|
}
|
package/.releaseway/runtime.cjs
CHANGED
|
@@ -531,7 +531,7 @@ var require_yauzl = __commonJS({
|
|
|
531
531
|
var PassThrough = require("stream").PassThrough;
|
|
532
532
|
var Writable = require("stream").Writable;
|
|
533
533
|
var crc32 = typeof zlib.crc32 === "function" ? zlib.crc32 : require_crc32();
|
|
534
|
-
exports2.open =
|
|
534
|
+
exports2.open = open2;
|
|
535
535
|
exports2.fromFd = fromFd;
|
|
536
536
|
exports2.fromBuffer = fromBuffer;
|
|
537
537
|
exports2.fromRandomAccessReader = fromRandomAccessReader;
|
|
@@ -549,7 +549,7 @@ var require_yauzl = __commonJS({
|
|
|
549
549
|
exports2.RandomAccessReader = RandomAccessReader;
|
|
550
550
|
function openPromise(path, options2) {
|
|
551
551
|
return new Promise((resolve2, reject) => {
|
|
552
|
-
|
|
552
|
+
open2(path, { ...options2, lazyEntries: true }, function(err, zipfile) {
|
|
553
553
|
if (err) return reject(err);
|
|
554
554
|
resolve2(zipfile);
|
|
555
555
|
});
|
|
@@ -579,7 +579,7 @@ var require_yauzl = __commonJS({
|
|
|
579
579
|
});
|
|
580
580
|
});
|
|
581
581
|
}
|
|
582
|
-
function
|
|
582
|
+
function open2(path, options2, callback) {
|
|
583
583
|
if (typeof options2 === "function") {
|
|
584
584
|
callback = options2;
|
|
585
585
|
options2 = null;
|
|
@@ -1391,13 +1391,93 @@ var import_node_process3 = __toESM(require("node:process"));
|
|
|
1391
1391
|
|
|
1392
1392
|
// src/native/launcher/cache.ts
|
|
1393
1393
|
var import_node_crypto3 = require("node:crypto");
|
|
1394
|
-
var
|
|
1394
|
+
var import_node_fs8 = require("node:fs");
|
|
1395
|
+
var import_promises3 = require("node:fs/promises");
|
|
1395
1396
|
var import_node_os = require("node:os");
|
|
1396
1397
|
var import_node_path11 = require("node:path");
|
|
1397
1398
|
var import_node_process = __toESM(require("node:process"));
|
|
1398
|
-
|
|
1399
|
+
|
|
1400
|
+
// src/native/limits.ts
|
|
1401
|
+
function nativeLimits(env = process.env) {
|
|
1402
|
+
const read = (suffix, fallback) => {
|
|
1403
|
+
const name = `RELEASEWAY_NATIVE_${suffix}`;
|
|
1404
|
+
const raw = env[name];
|
|
1405
|
+
if (raw === void 0 || raw === "") return fallback;
|
|
1406
|
+
const value = Number(raw);
|
|
1407
|
+
if (!/^\d+$/.test(raw) || !Number.isSafeInteger(value) || value <= 0) {
|
|
1408
|
+
throw new Error(`${name} must be a positive safe integer`);
|
|
1409
|
+
}
|
|
1410
|
+
return value;
|
|
1411
|
+
};
|
|
1412
|
+
return {
|
|
1413
|
+
apiTimeoutMs: read("API_TIMEOUT_MS", 3e4),
|
|
1414
|
+
downloadTimeoutMs: read("DOWNLOAD_TIMEOUT_MS", 24e4),
|
|
1415
|
+
extractTimeoutMs: read("EXTRACT_TIMEOUT_MS", 12e4),
|
|
1416
|
+
maxArchiveBytes: read("MAX_ARCHIVE_BYTES", 1024 ** 3),
|
|
1417
|
+
maxExecutableBytes: read("MAX_EXECUTABLE_BYTES", 1024 ** 3),
|
|
1418
|
+
maxExpandedBytes: read("MAX_EXPANDED_BYTES", 4 * 1024 ** 3)
|
|
1419
|
+
};
|
|
1420
|
+
}
|
|
1421
|
+
async function withDeadline(label, timeoutMs, operation) {
|
|
1422
|
+
const controller = new AbortController();
|
|
1423
|
+
let timer;
|
|
1424
|
+
const expired = new Promise((_2, reject) => {
|
|
1425
|
+
const deadline = performance.now() + timeoutMs;
|
|
1426
|
+
const expire = () => {
|
|
1427
|
+
const remaining = deadline - performance.now();
|
|
1428
|
+
if (remaining > 0) {
|
|
1429
|
+
timer = setTimeout(expire, Math.min(Math.ceil(remaining), 2 ** 31 - 1));
|
|
1430
|
+
return;
|
|
1431
|
+
}
|
|
1432
|
+
const error = new Error(`${label} exceeded ${timeoutMs}ms preparation budget`);
|
|
1433
|
+
controller.abort(error);
|
|
1434
|
+
reject(error);
|
|
1435
|
+
};
|
|
1436
|
+
expire();
|
|
1437
|
+
});
|
|
1438
|
+
try {
|
|
1439
|
+
return await Promise.race([operation(controller.signal), expired]);
|
|
1440
|
+
} finally {
|
|
1441
|
+
clearTimeout(timer);
|
|
1442
|
+
}
|
|
1443
|
+
}
|
|
1444
|
+
async function consumeBoundedResponse(response, maxBytes, signal, consume) {
|
|
1445
|
+
const declared = Number(response.headers.get("content-length"));
|
|
1446
|
+
if (Number.isFinite(declared) && declared > maxBytes) {
|
|
1447
|
+
await response.body?.cancel();
|
|
1448
|
+
throw new Error(`Download exceeds ${maxBytes} byte limit`);
|
|
1449
|
+
}
|
|
1450
|
+
if (!response.body) return;
|
|
1451
|
+
const reader = response.body.getReader();
|
|
1452
|
+
const abort = () => {
|
|
1453
|
+
void reader.cancel(signal.reason).catch(() => {
|
|
1454
|
+
});
|
|
1455
|
+
};
|
|
1456
|
+
signal.addEventListener("abort", abort, { once: true });
|
|
1457
|
+
let total = 0;
|
|
1458
|
+
try {
|
|
1459
|
+
while (true) {
|
|
1460
|
+
signal.throwIfAborted();
|
|
1461
|
+
const { done, value } = await reader.read();
|
|
1462
|
+
signal.throwIfAborted();
|
|
1463
|
+
if (done) break;
|
|
1464
|
+
total += value.byteLength;
|
|
1465
|
+
if (total > maxBytes) throw new Error(`Download exceeds ${maxBytes} byte limit`);
|
|
1466
|
+
await consume(value);
|
|
1467
|
+
}
|
|
1468
|
+
} finally {
|
|
1469
|
+
signal.removeEventListener("abort", abort);
|
|
1470
|
+
await reader.cancel().catch(() => {
|
|
1471
|
+
});
|
|
1472
|
+
reader.releaseLock();
|
|
1473
|
+
}
|
|
1474
|
+
}
|
|
1475
|
+
|
|
1476
|
+
// src/native/launcher/cache.ts
|
|
1477
|
+
var import_promises4 = require("node:timers/promises");
|
|
1399
1478
|
|
|
1400
1479
|
// src/native/launcher/archive.ts
|
|
1480
|
+
var import_node_fs7 = require("node:fs");
|
|
1401
1481
|
var import_node_path10 = require("node:path");
|
|
1402
1482
|
|
|
1403
1483
|
// node_modules/tar/dist/esm/index.min.js
|
|
@@ -4385,44 +4465,73 @@ function normalizeArchivePath(value) {
|
|
|
4385
4465
|
function isTarRegularFile(type) {
|
|
4386
4466
|
return type === "File" || type === "OldFile" || type === "ContiguousFile";
|
|
4387
4467
|
}
|
|
4388
|
-
async function extractTarGzExecutable(archivePath, executablePath) {
|
|
4468
|
+
async function extractTarGzExecutable(archivePath, executablePath, limits = nativeLimits()) {
|
|
4389
4469
|
const wanted = normalizeArchivePath(executablePath);
|
|
4390
4470
|
const seen = /* @__PURE__ */ new Set();
|
|
4391
4471
|
const chunks = [];
|
|
4392
4472
|
let matches = 0;
|
|
4393
4473
|
let scanError;
|
|
4394
|
-
|
|
4395
|
-
|
|
4396
|
-
|
|
4397
|
-
|
|
4398
|
-
|
|
4399
|
-
|
|
4400
|
-
|
|
4401
|
-
|
|
4402
|
-
|
|
4403
|
-
const normalized = normalizeArchivePath(entry.path.replace(/\/$/, ""));
|
|
4404
|
-
if (seen.has(normalized)) {
|
|
4405
|
-
throw new Error(`Duplicate archive entry path: ${normalized}`);
|
|
4406
|
-
}
|
|
4407
|
-
seen.add(normalized);
|
|
4408
|
-
if (entry.type === "Directory") {
|
|
4474
|
+
let expanded = 0;
|
|
4475
|
+
let executableBytes = 0;
|
|
4476
|
+
await withDeadline("Native tar extraction", limits.extractTimeoutMs, (signal) => new Promise((resolve2, reject) => {
|
|
4477
|
+
const input = (0, import_node_fs7.createReadStream)(archivePath);
|
|
4478
|
+
const parser = Ct({
|
|
4479
|
+
strict: true,
|
|
4480
|
+
onentry(entry) {
|
|
4481
|
+
if (scanError) {
|
|
4482
|
+
entry.resume();
|
|
4409
4483
|
return;
|
|
4410
4484
|
}
|
|
4411
|
-
|
|
4412
|
-
|
|
4413
|
-
|
|
4414
|
-
);
|
|
4415
|
-
|
|
4416
|
-
|
|
4417
|
-
|
|
4418
|
-
|
|
4485
|
+
try {
|
|
4486
|
+
expanded += entry.size;
|
|
4487
|
+
if (expanded > limits.maxExpandedBytes) throw new Error("Native archive exceeds expanded byte limit");
|
|
4488
|
+
const normalized = normalizeArchivePath(entry.path.replace(/\/$/, ""));
|
|
4489
|
+
if (seen.has(normalized)) {
|
|
4490
|
+
throw new Error(`Duplicate archive entry path: ${normalized}`);
|
|
4491
|
+
}
|
|
4492
|
+
seen.add(normalized);
|
|
4493
|
+
if (entry.type === "Directory") {
|
|
4494
|
+
return;
|
|
4495
|
+
}
|
|
4496
|
+
if (!isTarRegularFile(entry.type)) {
|
|
4497
|
+
throw new Error(
|
|
4498
|
+
`Unsupported non-regular tar entry ${entry.type}: ${entry.path}`
|
|
4499
|
+
);
|
|
4500
|
+
}
|
|
4501
|
+
if (normalized === wanted) {
|
|
4502
|
+
if (entry.size > limits.maxExecutableBytes) throw new Error("Native executable exceeds byte limit");
|
|
4503
|
+
matches += 1;
|
|
4504
|
+
entry.on("data", (chunk) => {
|
|
4505
|
+
executableBytes += chunk.length;
|
|
4506
|
+
if (executableBytes > limits.maxExecutableBytes) {
|
|
4507
|
+
parser.abort(new Error("Native executable exceeds byte limit"));
|
|
4508
|
+
return;
|
|
4509
|
+
}
|
|
4510
|
+
chunks.push(Buffer.from(chunk));
|
|
4511
|
+
});
|
|
4512
|
+
}
|
|
4513
|
+
} catch (error) {
|
|
4514
|
+
scanError = error instanceof Error ? error : new Error(String(error));
|
|
4515
|
+
entry.resume();
|
|
4516
|
+
parser.abort(scanError);
|
|
4419
4517
|
}
|
|
4420
|
-
} catch (error) {
|
|
4421
|
-
scanError = error instanceof Error ? error : new Error(String(error));
|
|
4422
|
-
entry.resume();
|
|
4423
4518
|
}
|
|
4424
|
-
}
|
|
4425
|
-
|
|
4519
|
+
});
|
|
4520
|
+
const abort = () => parser.abort(signal.reason);
|
|
4521
|
+
signal.addEventListener("abort", abort, { once: true });
|
|
4522
|
+
const finish = (error) => {
|
|
4523
|
+
signal.removeEventListener("abort", abort);
|
|
4524
|
+
input.destroy();
|
|
4525
|
+
if (error) reject(error);
|
|
4526
|
+
else resolve2();
|
|
4527
|
+
};
|
|
4528
|
+
input.on("error", (error) => {
|
|
4529
|
+
parser.abort(error);
|
|
4530
|
+
});
|
|
4531
|
+
parser.on("error", finish);
|
|
4532
|
+
parser.on("end", () => finish());
|
|
4533
|
+
input.pipe(parser);
|
|
4534
|
+
}));
|
|
4426
4535
|
if (scanError) {
|
|
4427
4536
|
throw scanError;
|
|
4428
4537
|
}
|
|
@@ -4464,90 +4573,117 @@ function openZip(path) {
|
|
|
4464
4573
|
});
|
|
4465
4574
|
});
|
|
4466
4575
|
}
|
|
4467
|
-
function readZipEntry(zip, entry) {
|
|
4576
|
+
function readZipEntry(zip, entry, maxBytes, signal) {
|
|
4468
4577
|
return new Promise((resolve2, reject) => {
|
|
4469
4578
|
zip.openReadStream(entry, (error, stream) => {
|
|
4470
4579
|
if (error || !stream) {
|
|
4471
4580
|
reject(error ?? new Error("Failed to read zip entry"));
|
|
4472
4581
|
return;
|
|
4473
4582
|
}
|
|
4583
|
+
if (signal.aborted) {
|
|
4584
|
+
stream.destroy();
|
|
4585
|
+
reject(signal.reason);
|
|
4586
|
+
return;
|
|
4587
|
+
}
|
|
4474
4588
|
const chunks = [];
|
|
4475
|
-
|
|
4589
|
+
let total = 0;
|
|
4590
|
+
const abort = () => stream.destroy(signal.reason);
|
|
4591
|
+
signal.addEventListener("abort", abort, { once: true });
|
|
4592
|
+
stream.on("close", () => signal.removeEventListener("abort", abort));
|
|
4593
|
+
stream.on("data", (chunk) => {
|
|
4594
|
+
total += chunk.length;
|
|
4595
|
+
if (total > maxBytes) stream.destroy(new Error("Native executable exceeds byte limit"));
|
|
4596
|
+
else chunks.push(Buffer.from(chunk));
|
|
4597
|
+
});
|
|
4476
4598
|
stream.on("error", reject);
|
|
4477
4599
|
stream.on("end", () => resolve2(Buffer.concat(chunks)));
|
|
4478
4600
|
});
|
|
4479
4601
|
});
|
|
4480
4602
|
}
|
|
4481
|
-
async function extractZipExecutable(archivePath, executablePath) {
|
|
4482
|
-
|
|
4483
|
-
|
|
4484
|
-
|
|
4485
|
-
|
|
4486
|
-
|
|
4487
|
-
|
|
4488
|
-
let
|
|
4489
|
-
|
|
4490
|
-
|
|
4491
|
-
|
|
4492
|
-
|
|
4493
|
-
|
|
4494
|
-
|
|
4495
|
-
|
|
4496
|
-
|
|
4497
|
-
};
|
|
4498
|
-
zip.on("error", fail);
|
|
4499
|
-
zip.on("entry", async (entry) => {
|
|
4500
|
-
try {
|
|
4501
|
-
const raw = entry.fileName.replace(/\/$/, "");
|
|
4502
|
-
const normalized = normalizeArchivePath(raw);
|
|
4503
|
-
if (seen.has(normalized)) {
|
|
4504
|
-
throw new Error(`Duplicate archive entry path: ${normalized}`);
|
|
4505
|
-
}
|
|
4506
|
-
seen.add(normalized);
|
|
4507
|
-
assertZipEntryType(entry, normalized);
|
|
4508
|
-
const directory = entry.fileName.endsWith("/") || ((zipUnixMode(entry) ?? 0) & 61440) === 16384;
|
|
4509
|
-
if (normalized === wanted) {
|
|
4510
|
-
if (directory) {
|
|
4511
|
-
throw new Error(
|
|
4512
|
-
`Configured native executable is not a regular zip file: ${wanted}`
|
|
4513
|
-
);
|
|
4514
|
-
}
|
|
4515
|
-
matches += 1;
|
|
4516
|
-
match = await readZipEntry(zip, entry);
|
|
4603
|
+
async function extractZipExecutable(archivePath, executablePath, limits = nativeLimits()) {
|
|
4604
|
+
return withDeadline("Native zip extraction", limits.extractTimeoutMs, async (signal) => {
|
|
4605
|
+
const wanted = normalizeArchivePath(executablePath);
|
|
4606
|
+
const zip = await openZip(archivePath);
|
|
4607
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4608
|
+
let match;
|
|
4609
|
+
let matches = 0;
|
|
4610
|
+
let expanded = 0;
|
|
4611
|
+
return new Promise((resolve2, reject) => {
|
|
4612
|
+
let settled = false;
|
|
4613
|
+
const fail = (error) => {
|
|
4614
|
+
if (settled) return;
|
|
4615
|
+
settled = true;
|
|
4616
|
+
try {
|
|
4617
|
+
zip.close();
|
|
4618
|
+
} catch {
|
|
4517
4619
|
}
|
|
4518
|
-
|
|
4519
|
-
}
|
|
4520
|
-
|
|
4521
|
-
|
|
4522
|
-
|
|
4523
|
-
|
|
4524
|
-
if (
|
|
4525
|
-
|
|
4526
|
-
if (matches !== 1 || !match) {
|
|
4527
|
-
reject(
|
|
4528
|
-
new Error(
|
|
4529
|
-
`Native executable ${wanted} must occur exactly once in zip; found ${matches}`
|
|
4530
|
-
)
|
|
4531
|
-
);
|
|
4620
|
+
reject(error);
|
|
4621
|
+
};
|
|
4622
|
+
zip.on("error", fail);
|
|
4623
|
+
const abort = () => fail(signal.reason);
|
|
4624
|
+
signal.addEventListener("abort", abort, { once: true });
|
|
4625
|
+
zip.on("close", () => signal.removeEventListener("abort", abort));
|
|
4626
|
+
if (signal.aborted) {
|
|
4627
|
+
fail(signal.reason);
|
|
4532
4628
|
return;
|
|
4533
4629
|
}
|
|
4534
|
-
|
|
4630
|
+
zip.on("entry", async (entry) => {
|
|
4631
|
+
try {
|
|
4632
|
+
expanded += entry.uncompressedSize;
|
|
4633
|
+
if (expanded > limits.maxExpandedBytes) throw new Error("Native archive exceeds expanded byte limit");
|
|
4634
|
+
const raw = entry.fileName.replace(/\/$/, "");
|
|
4635
|
+
const normalized = normalizeArchivePath(raw);
|
|
4636
|
+
if (seen.has(normalized)) {
|
|
4637
|
+
throw new Error(`Duplicate archive entry path: ${normalized}`);
|
|
4638
|
+
}
|
|
4639
|
+
seen.add(normalized);
|
|
4640
|
+
assertZipEntryType(entry, normalized);
|
|
4641
|
+
const directory = entry.fileName.endsWith("/") || ((zipUnixMode(entry) ?? 0) & 61440) === 16384;
|
|
4642
|
+
if (normalized === wanted) {
|
|
4643
|
+
if (directory) {
|
|
4644
|
+
throw new Error(
|
|
4645
|
+
`Configured native executable is not a regular zip file: ${wanted}`
|
|
4646
|
+
);
|
|
4647
|
+
}
|
|
4648
|
+
matches += 1;
|
|
4649
|
+
if (entry.uncompressedSize > limits.maxExecutableBytes) throw new Error("Native executable exceeds byte limit");
|
|
4650
|
+
match = await readZipEntry(zip, entry, limits.maxExecutableBytes, signal);
|
|
4651
|
+
}
|
|
4652
|
+
zip.readEntry();
|
|
4653
|
+
} catch (error) {
|
|
4654
|
+
fail(error);
|
|
4655
|
+
}
|
|
4656
|
+
});
|
|
4657
|
+
zip.on("end", () => {
|
|
4658
|
+
if (settled) return;
|
|
4659
|
+
settled = true;
|
|
4660
|
+
if (matches !== 1 || !match) {
|
|
4661
|
+
reject(
|
|
4662
|
+
new Error(
|
|
4663
|
+
`Native executable ${wanted} must occur exactly once in zip; found ${matches}`
|
|
4664
|
+
)
|
|
4665
|
+
);
|
|
4666
|
+
return;
|
|
4667
|
+
}
|
|
4668
|
+
resolve2(match);
|
|
4669
|
+
});
|
|
4670
|
+
zip.readEntry();
|
|
4535
4671
|
});
|
|
4536
|
-
zip.readEntry();
|
|
4537
4672
|
});
|
|
4538
4673
|
}
|
|
4539
|
-
async function extractExecutable(archivePath, assetName, executablePath) {
|
|
4674
|
+
async function extractExecutable(archivePath, assetName, executablePath, limits = nativeLimits()) {
|
|
4540
4675
|
if (assetName.endsWith(".tar.gz")) {
|
|
4541
|
-
return extractTarGzExecutable(archivePath, executablePath);
|
|
4676
|
+
return extractTarGzExecutable(archivePath, executablePath, limits);
|
|
4542
4677
|
}
|
|
4543
4678
|
if (assetName.endsWith(".zip")) {
|
|
4544
|
-
return extractZipExecutable(archivePath, executablePath);
|
|
4679
|
+
return extractZipExecutable(archivePath, executablePath, limits);
|
|
4545
4680
|
}
|
|
4546
4681
|
throw new Error(`Unsupported native archive format: ${assetName}`);
|
|
4547
4682
|
}
|
|
4548
4683
|
|
|
4549
4684
|
// src/native/launcher/download.ts
|
|
4550
4685
|
var import_node_crypto2 = require("node:crypto");
|
|
4686
|
+
var import_promises2 = require("node:fs/promises");
|
|
4551
4687
|
function encodeRepository(repository) {
|
|
4552
4688
|
const parts = repository.split("/");
|
|
4553
4689
|
if (parts.length !== 2 || !parts[0] || !parts[1]) {
|
|
@@ -4555,6 +4691,32 @@ function encodeRepository(repository) {
|
|
|
4555
4691
|
}
|
|
4556
4692
|
return parts.map(encodeURIComponent).join("/");
|
|
4557
4693
|
}
|
|
4694
|
+
async function downloadReleaseAssetToFile(repository, tag, asset, destination, expectedSha256, env = process.env, fetchImpl = fetch) {
|
|
4695
|
+
const limits = nativeLimits(env);
|
|
4696
|
+
const file = await (0, import_promises2.open)(destination, "wx", 384);
|
|
4697
|
+
try {
|
|
4698
|
+
await withDeadline("Native asset download", limits.downloadTimeoutMs, async (signal) => {
|
|
4699
|
+
const response = await fetchImpl(releaseAssetUrl(repository, tag, asset), {
|
|
4700
|
+
signal,
|
|
4701
|
+
redirect: "follow",
|
|
4702
|
+
headers: { "User-Agent": "releaseway-npm-actions-native" }
|
|
4703
|
+
});
|
|
4704
|
+
if (!response.ok) {
|
|
4705
|
+
throw new Error(`Failed to download native GitHub Release asset ${asset}: HTTP ${response.status}`);
|
|
4706
|
+
}
|
|
4707
|
+
const hash = (0, import_node_crypto2.createHash)("sha256");
|
|
4708
|
+
await consumeBoundedResponse(response, limits.maxArchiveBytes, signal, async (chunk) => {
|
|
4709
|
+
hash.update(chunk);
|
|
4710
|
+
await file.writeFile(chunk);
|
|
4711
|
+
});
|
|
4712
|
+
if (hash.digest("hex") !== expectedSha256.toLowerCase()) {
|
|
4713
|
+
throw new Error("Native asset SHA-256 digest mismatch");
|
|
4714
|
+
}
|
|
4715
|
+
});
|
|
4716
|
+
} finally {
|
|
4717
|
+
await file.close();
|
|
4718
|
+
}
|
|
4719
|
+
}
|
|
4558
4720
|
function releaseAssetUrl(repository, tag, asset) {
|
|
4559
4721
|
return `https://github.com/${encodeRepository(repository)}/releases/download/${encodeURIComponent(tag)}/${encodeURIComponent(asset)}`;
|
|
4560
4722
|
}
|
|
@@ -4568,25 +4730,16 @@ function verifySha256(bytes, expectedHex) {
|
|
|
4568
4730
|
throw new Error("Native asset SHA-256 digest mismatch");
|
|
4569
4731
|
}
|
|
4570
4732
|
}
|
|
4571
|
-
async function downloadReleaseAsset(repository, tag, asset, fetchImpl = fetch) {
|
|
4572
|
-
const response = await fetchImpl(releaseAssetUrl(repository, tag, asset), {
|
|
4573
|
-
redirect: "follow",
|
|
4574
|
-
headers: {
|
|
4575
|
-
"User-Agent": "releaseway-npm-actions-native"
|
|
4576
|
-
}
|
|
4577
|
-
});
|
|
4578
|
-
if (!response.ok) {
|
|
4579
|
-
throw new Error(
|
|
4580
|
-
`Failed to download native GitHub Release asset ${asset}: HTTP ${response.status}`
|
|
4581
|
-
);
|
|
4582
|
-
}
|
|
4583
|
-
return Buffer.from(await response.arrayBuffer());
|
|
4584
|
-
}
|
|
4585
4733
|
|
|
4586
4734
|
// src/native/launcher/cache.ts
|
|
4587
4735
|
function sha256(bytes) {
|
|
4588
4736
|
return (0, import_node_crypto3.createHash)("sha256").update(bytes).digest("hex");
|
|
4589
4737
|
}
|
|
4738
|
+
async function fileSha256(path) {
|
|
4739
|
+
const hash = (0, import_node_crypto3.createHash)("sha256");
|
|
4740
|
+
for await (const chunk of (0, import_node_fs8.createReadStream)(path)) hash.update(chunk);
|
|
4741
|
+
return hash.digest("hex");
|
|
4742
|
+
}
|
|
4590
4743
|
function archiveFormat(asset) {
|
|
4591
4744
|
if (asset.endsWith(".tar.gz")) {
|
|
4592
4745
|
return "tar.gz";
|
|
@@ -4660,14 +4813,13 @@ function executableCacheKey(executable) {
|
|
|
4660
4813
|
async function validateArchiveCache(directory, target) {
|
|
4661
4814
|
try {
|
|
4662
4815
|
const metadata = JSON.parse(
|
|
4663
|
-
await (0,
|
|
4816
|
+
await (0, import_promises3.readFile)((0, import_node_path11.join)(directory, "archive.json"), "utf8")
|
|
4664
4817
|
);
|
|
4665
4818
|
if (metadata.schema !== 2 || metadata.assetSha256 !== target.sha256 || metadata.archiveFormat !== archiveFormat(target.asset)) {
|
|
4666
4819
|
return void 0;
|
|
4667
4820
|
}
|
|
4668
4821
|
const archive = (0, import_node_path11.join)(directory, "archive.bin");
|
|
4669
|
-
|
|
4670
|
-
if (sha256(bytes) !== target.sha256) {
|
|
4822
|
+
if (await fileSha256(archive) !== target.sha256) {
|
|
4671
4823
|
return void 0;
|
|
4672
4824
|
}
|
|
4673
4825
|
return archive;
|
|
@@ -4678,15 +4830,14 @@ async function validateArchiveCache(directory, target) {
|
|
|
4678
4830
|
async function validateExecutableCache(directory, target, executable) {
|
|
4679
4831
|
try {
|
|
4680
4832
|
const metadata = JSON.parse(
|
|
4681
|
-
await (0,
|
|
4833
|
+
await (0, import_promises3.readFile)((0, import_node_path11.join)(directory, "metadata.json"), "utf8")
|
|
4682
4834
|
);
|
|
4683
4835
|
const executableFile = cachedExecutableName(executable);
|
|
4684
4836
|
if (metadata.schema !== 2 || metadata.assetSha256 !== target.sha256 || metadata.sourceExecutable !== executable || typeof metadata.executableSha256 !== "string" || !/^[0-9a-f]{64}$/.test(metadata.executableSha256) || metadata.executableFile !== executableFile) {
|
|
4685
4837
|
return void 0;
|
|
4686
4838
|
}
|
|
4687
4839
|
const path = (0, import_node_path11.join)(directory, executableFile);
|
|
4688
|
-
|
|
4689
|
-
if (sha256(bytes) !== metadata.executableSha256) {
|
|
4840
|
+
if (await fileSha256(path) !== metadata.executableSha256) {
|
|
4690
4841
|
return void 0;
|
|
4691
4842
|
}
|
|
4692
4843
|
return path;
|
|
@@ -4705,10 +4856,19 @@ async function acquireCacheLock(lockDirectory, validate, label, options2) {
|
|
|
4705
4856
|
return { kind: "cached", value: cached };
|
|
4706
4857
|
}
|
|
4707
4858
|
try {
|
|
4708
|
-
await (0,
|
|
4859
|
+
await (0, import_promises3.mkdir)(lockDirectory);
|
|
4860
|
+
const heartbeat = setInterval(() => {
|
|
4861
|
+
const now = /* @__PURE__ */ new Date();
|
|
4862
|
+
void (0, import_promises3.utimes)(lockDirectory, now, now).catch(() => {
|
|
4863
|
+
});
|
|
4864
|
+
}, Math.max(1, Math.min(1e3, Math.floor(staleMs / 3))));
|
|
4865
|
+
heartbeat.unref();
|
|
4709
4866
|
return {
|
|
4710
4867
|
kind: "owner",
|
|
4711
|
-
release: () =>
|
|
4868
|
+
release: async () => {
|
|
4869
|
+
clearInterval(heartbeat);
|
|
4870
|
+
await (0, import_promises3.rm)(lockDirectory, { recursive: true, force: true });
|
|
4871
|
+
}
|
|
4712
4872
|
};
|
|
4713
4873
|
} catch (error) {
|
|
4714
4874
|
if (!error || typeof error !== "object" || !("code" in error) || String(error.code) !== "EEXIST") {
|
|
@@ -4716,9 +4876,9 @@ async function acquireCacheLock(lockDirectory, validate, label, options2) {
|
|
|
4716
4876
|
}
|
|
4717
4877
|
}
|
|
4718
4878
|
try {
|
|
4719
|
-
const lockStat = await (0,
|
|
4879
|
+
const lockStat = await (0, import_promises3.stat)(lockDirectory);
|
|
4720
4880
|
if (Date.now() - lockStat.mtimeMs > staleMs) {
|
|
4721
|
-
await (0,
|
|
4881
|
+
await (0, import_promises3.rm)(lockDirectory, { recursive: true, force: true });
|
|
4722
4882
|
continue;
|
|
4723
4883
|
}
|
|
4724
4884
|
} catch (error) {
|
|
@@ -4730,25 +4890,25 @@ async function acquireCacheLock(lockDirectory, validate, label, options2) {
|
|
|
4730
4890
|
if (Date.now() - started > timeoutMs) {
|
|
4731
4891
|
throw new Error(`Timed out waiting for native cache lock for ${label}`);
|
|
4732
4892
|
}
|
|
4733
|
-
await (0,
|
|
4893
|
+
await (0, import_promises4.setTimeout)(pollMs);
|
|
4734
4894
|
}
|
|
4735
4895
|
}
|
|
4736
4896
|
async function promoteDirectory(temp, finalDirectory, validate) {
|
|
4737
4897
|
try {
|
|
4738
|
-
await (0,
|
|
4898
|
+
await (0, import_promises3.rename)(temp, finalDirectory);
|
|
4739
4899
|
return validate();
|
|
4740
4900
|
} catch (error) {
|
|
4741
4901
|
if (!error || typeof error !== "object" || !("code" in error) || !["EEXIST", "ENOTEMPTY", "EPERM"].includes(String(error.code))) {
|
|
4742
4902
|
throw error;
|
|
4743
4903
|
}
|
|
4744
|
-
await (0,
|
|
4904
|
+
await (0, import_promises3.rm)(temp, { recursive: true, force: true });
|
|
4745
4905
|
return validate();
|
|
4746
4906
|
}
|
|
4747
4907
|
}
|
|
4748
4908
|
async function prepareNativeArchive(manifest, target, root, options2) {
|
|
4749
4909
|
const directory = (0, import_node_path11.join)(root, target.sha256);
|
|
4750
4910
|
const executables = (0, import_node_path11.join)(directory, "executables");
|
|
4751
|
-
await (0,
|
|
4911
|
+
await (0, import_promises3.mkdir)(executables, { recursive: true });
|
|
4752
4912
|
const validate = () => validateArchiveCache(directory, target);
|
|
4753
4913
|
const existing = await validate();
|
|
4754
4914
|
if (existing) {
|
|
@@ -4768,34 +4928,35 @@ async function prepareNativeArchive(manifest, target, root, options2) {
|
|
|
4768
4928
|
if (afterLock) {
|
|
4769
4929
|
return afterLock;
|
|
4770
4930
|
}
|
|
4771
|
-
await (0,
|
|
4772
|
-
await (0,
|
|
4773
|
-
const temp = await (0,
|
|
4931
|
+
await (0, import_promises3.rm)((0, import_node_path11.join)(directory, "archive.bin"), { force: true });
|
|
4932
|
+
await (0, import_promises3.rm)((0, import_node_path11.join)(directory, "archive.json"), { force: true });
|
|
4933
|
+
const temp = await (0, import_promises3.mkdtemp)(
|
|
4774
4934
|
(0, import_node_path11.join)(root, `.${target.sha256}.archive.tmp-`)
|
|
4775
4935
|
);
|
|
4776
4936
|
try {
|
|
4777
|
-
const download = options2.download ?? ((repository, tag, asset) => downloadReleaseAsset(repository, tag, asset));
|
|
4778
|
-
const archiveBytes = await download(
|
|
4779
|
-
manifest.repository,
|
|
4780
|
-
manifest.tag,
|
|
4781
|
-
target.asset
|
|
4782
|
-
);
|
|
4783
|
-
verifySha256(archiveBytes, target.sha256);
|
|
4784
4937
|
const tempArchive = (0, import_node_path11.join)(temp, "archive.bin");
|
|
4785
4938
|
const tempMetadata = (0, import_node_path11.join)(temp, "archive.json");
|
|
4786
|
-
|
|
4939
|
+
if (options2.download) {
|
|
4940
|
+
const limits = nativeLimits(options2.env);
|
|
4941
|
+
const archiveBytes = await withDeadline("Native asset download", limits.downloadTimeoutMs, () => options2.download(manifest.repository, manifest.tag, target.asset));
|
|
4942
|
+
if (archiveBytes.length > limits.maxArchiveBytes) throw new Error("Native archive exceeds byte limit");
|
|
4943
|
+
verifySha256(archiveBytes, target.sha256);
|
|
4944
|
+
await (0, import_promises3.writeFile)(tempArchive, archiveBytes, { mode: 384 });
|
|
4945
|
+
} else {
|
|
4946
|
+
await downloadReleaseAssetToFile(manifest.repository, manifest.tag, target.asset, tempArchive, target.sha256, options2.env);
|
|
4947
|
+
}
|
|
4787
4948
|
const metadata = {
|
|
4788
4949
|
schema: 2,
|
|
4789
4950
|
assetSha256: target.sha256,
|
|
4790
4951
|
archiveFormat: archiveFormat(target.asset)
|
|
4791
4952
|
};
|
|
4792
|
-
await (0,
|
|
4953
|
+
await (0, import_promises3.writeFile)(
|
|
4793
4954
|
tempMetadata,
|
|
4794
4955
|
JSON.stringify(metadata, null, 2) + "\n",
|
|
4795
4956
|
{ encoding: "utf8", mode: 384 }
|
|
4796
4957
|
);
|
|
4797
|
-
await (0,
|
|
4798
|
-
await (0,
|
|
4958
|
+
await (0, import_promises3.rename)(tempArchive, (0, import_node_path11.join)(directory, "archive.bin"));
|
|
4959
|
+
await (0, import_promises3.rename)(tempMetadata, (0, import_node_path11.join)(directory, "archive.json"));
|
|
4799
4960
|
const promoted = await validate();
|
|
4800
4961
|
if (!promoted) {
|
|
4801
4962
|
throw new Error(
|
|
@@ -4804,7 +4965,7 @@ async function prepareNativeArchive(manifest, target, root, options2) {
|
|
|
4804
4965
|
}
|
|
4805
4966
|
return promoted;
|
|
4806
4967
|
} finally {
|
|
4807
|
-
await (0,
|
|
4968
|
+
await (0, import_promises3.rm)(temp, { recursive: true, force: true });
|
|
4808
4969
|
}
|
|
4809
4970
|
} finally {
|
|
4810
4971
|
await lock.release();
|
|
@@ -4834,19 +4995,20 @@ async function prepareExecutableFromArchive(archivePath, target, root, options2)
|
|
|
4834
4995
|
if (afterLock) {
|
|
4835
4996
|
return afterLock;
|
|
4836
4997
|
}
|
|
4837
|
-
await (0,
|
|
4838
|
-
const temp = await (0,
|
|
4998
|
+
await (0, import_promises3.rm)(finalDirectory, { recursive: true, force: true });
|
|
4999
|
+
const temp = await (0, import_promises3.mkdtemp)((0, import_node_path11.join)(executables, `.${key}.tmp-`));
|
|
4839
5000
|
try {
|
|
4840
5001
|
const executableBytes = await extractExecutable(
|
|
4841
5002
|
archivePath,
|
|
4842
5003
|
target.asset,
|
|
4843
|
-
executable
|
|
5004
|
+
executable,
|
|
5005
|
+
nativeLimits(options2.env)
|
|
4844
5006
|
);
|
|
4845
5007
|
const executableFile = cachedExecutableName(executable);
|
|
4846
5008
|
const executablePath = (0, import_node_path11.join)(temp, executableFile);
|
|
4847
|
-
await (0,
|
|
5009
|
+
await (0, import_promises3.writeFile)(executablePath, executableBytes, { mode: 493 });
|
|
4848
5010
|
if ((options2.platform ?? import_node_process.default.platform) !== "win32") {
|
|
4849
|
-
await (0,
|
|
5011
|
+
await (0, import_promises3.chmod)(executablePath, 493);
|
|
4850
5012
|
}
|
|
4851
5013
|
const metadata = {
|
|
4852
5014
|
schema: 2,
|
|
@@ -4855,7 +5017,7 @@ async function prepareExecutableFromArchive(archivePath, target, root, options2)
|
|
|
4855
5017
|
executableSha256: sha256(executableBytes),
|
|
4856
5018
|
executableFile
|
|
4857
5019
|
};
|
|
4858
|
-
await (0,
|
|
5020
|
+
await (0, import_promises3.writeFile)(
|
|
4859
5021
|
(0, import_node_path11.join)(temp, "metadata.json"),
|
|
4860
5022
|
JSON.stringify(metadata, null, 2) + "\n",
|
|
4861
5023
|
{ encoding: "utf8", mode: 384 }
|
|
@@ -4872,7 +5034,7 @@ async function prepareExecutableFromArchive(archivePath, target, root, options2)
|
|
|
4872
5034
|
}
|
|
4873
5035
|
return promoted;
|
|
4874
5036
|
} finally {
|
|
4875
|
-
await (0,
|
|
5037
|
+
await (0, import_promises3.rm)(temp, { recursive: true, force: true });
|
|
4876
5038
|
}
|
|
4877
5039
|
} finally {
|
|
4878
5040
|
await lock.release();
|
|
@@ -4884,7 +5046,7 @@ async function prepareNativeExecutable(manifest, target, options2 = {}) {
|
|
|
4884
5046
|
platform: options2.platform,
|
|
4885
5047
|
home: options2.home
|
|
4886
5048
|
});
|
|
4887
|
-
await (0,
|
|
5049
|
+
await (0, import_promises3.mkdir)(root, { recursive: true });
|
|
4888
5050
|
const archivePath = await prepareNativeArchive(
|
|
4889
5051
|
manifest,
|
|
4890
5052
|
target,
|
|
@@ -4900,7 +5062,7 @@ async function prepareNativeExecutable(manifest, target, options2 = {}) {
|
|
|
4900
5062
|
}
|
|
4901
5063
|
|
|
4902
5064
|
// src/native/launcher/manifest.ts
|
|
4903
|
-
var
|
|
5065
|
+
var import_promises5 = require("node:fs/promises");
|
|
4904
5066
|
|
|
4905
5067
|
// src/native/validate.ts
|
|
4906
5068
|
var SUPPORTED_NATIVE_TARGETS = /* @__PURE__ */ new Set([
|
|
@@ -4986,7 +5148,7 @@ function parseNativeManifest(value) {
|
|
|
4986
5148
|
}
|
|
4987
5149
|
async function loadNativeManifest(manifestPath) {
|
|
4988
5150
|
try {
|
|
4989
|
-
const source = await (0,
|
|
5151
|
+
const source = await (0, import_promises5.readFile)(manifestPath, "utf8");
|
|
4990
5152
|
return parseNativeManifest(JSON.parse(source));
|
|
4991
5153
|
} catch (error) {
|
|
4992
5154
|
throw new Error(
|