@validate.qa/runner 1.0.96 → 1.0.98
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/dist/{chunk-2BYRCDXC.mjs → chunk-J4ST6MFT.mjs} +196 -64
- package/dist/cli.js +3288 -723
- package/dist/cli.mjs +3095 -662
- package/dist/tar-extract-child.js +251 -104
- package/dist/tar-extract-child.mjs +41 -26
- package/package.json +6 -5
|
@@ -25,6 +25,58 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
25
25
|
mod
|
|
26
26
|
));
|
|
27
27
|
|
|
28
|
+
// ../runner-core/dist/services/isolated-child-host.js
|
|
29
|
+
var require_isolated_child_host = __commonJS({
|
|
30
|
+
"../runner-core/dist/services/isolated-child-host.js"(exports2) {
|
|
31
|
+
"use strict";
|
|
32
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
33
|
+
exports2.serveIsolatedChild = serveIsolatedChild;
|
|
34
|
+
function runFault(fault) {
|
|
35
|
+
switch (fault) {
|
|
36
|
+
case "throw-async":
|
|
37
|
+
setImmediate(() => {
|
|
38
|
+
throw new Error("injected isolated child crash");
|
|
39
|
+
});
|
|
40
|
+
return new Promise(() => {
|
|
41
|
+
});
|
|
42
|
+
case "abort":
|
|
43
|
+
process.abort();
|
|
44
|
+
break;
|
|
45
|
+
case "hang":
|
|
46
|
+
setInterval(() => {
|
|
47
|
+
}, 1e3);
|
|
48
|
+
return new Promise(() => {
|
|
49
|
+
});
|
|
50
|
+
case "oom": {
|
|
51
|
+
const hold = [];
|
|
52
|
+
for (; ; )
|
|
53
|
+
hold.push(new Array(1e6).fill(hold.length));
|
|
54
|
+
}
|
|
55
|
+
case "report-env":
|
|
56
|
+
return Promise.resolve({ ok: true, env: { ...process.env } });
|
|
57
|
+
default:
|
|
58
|
+
break;
|
|
59
|
+
}
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
function serveIsolatedChild(markerEnv, handle) {
|
|
63
|
+
if (typeof process.send !== "function" || process.env[markerEnv] !== "1")
|
|
64
|
+
return;
|
|
65
|
+
const reply = (message) => {
|
|
66
|
+
process.send?.(message, () => process.exit(0));
|
|
67
|
+
};
|
|
68
|
+
process.on("disconnect", () => process.exit(1));
|
|
69
|
+
process.once("message", async (request) => {
|
|
70
|
+
try {
|
|
71
|
+
reply(await (runFault(request.fault) ?? handle(request.job)));
|
|
72
|
+
} catch (err) {
|
|
73
|
+
reply({ ok: false, message: err instanceof Error ? err.message : String(err) });
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
|
|
28
80
|
// ../../node_modules/adm-zip/util/constants.js
|
|
29
81
|
var require_constants = __commonJS({
|
|
30
82
|
"../../node_modules/adm-zip/util/constants.js"(exports2, module2) {
|
|
@@ -8915,11 +8967,22 @@ var require_tar_isolated = __commonJS({
|
|
|
8915
8967
|
"../runner-core/dist/services/tar-isolated.js"(exports2) {
|
|
8916
8968
|
"use strict";
|
|
8917
8969
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
8918
|
-
exports2.TAR_ISOLATED_MAX_CHILDREN_PER_TENANT = exports2.TAR_ISOLATED_MAX_CHILDREN = exports2.TarIsolationError = exports2.TarIsolationUnavailableError = exports2.TAR_ISOLATION_UNAVAILABLE_MESSAGE = exports2.TAR_CHILD_MARKER_ENV = exports2.TAR_ISOLATED_MAX_OLD_SPACE_MB = exports2.TAR_ISOLATED_TIMEOUT_MS = void 0;
|
|
8970
|
+
exports2.IsolationSlots = exports2.SlotPool = exports2.ISOLATED_MIN_CHILD_HEAP_MB = exports2.ISOLATED_MEMORY_MARGIN_MB = exports2.TAR_ISOLATED_MAX_CHILDREN_PER_TENANT = exports2.TAR_ISOLATED_MAX_CHILDREN = exports2.ISOLATED_MAX_CHILDREN = exports2.TarIsolationError = exports2.TarIsolationUnavailableError = exports2.TAR_ISOLATION_UNAVAILABLE_MESSAGE = exports2.TAR_CHILD_MARKER_ENV = exports2.TAR_ISOLATED_MAX_OLD_SPACE_MB = exports2.TAR_ISOLATED_TIMEOUT_MS = void 0;
|
|
8971
|
+
exports2.isolatedChildEnv = isolatedChildEnv;
|
|
8919
8972
|
exports2.tarChildEnv = tarChildEnv;
|
|
8973
|
+
exports2.resolveIsolatedChildEntry = resolveIsolatedChildEntry;
|
|
8920
8974
|
exports2.resolveTarChildEntry = resolveTarChildEntry;
|
|
8975
|
+
exports2.deriveIsolatedChildBudget = deriveIsolatedChildBudget;
|
|
8976
|
+
exports2.deriveMaxIsolatedChildren = deriveMaxIsolatedChildren;
|
|
8977
|
+
exports2.effectiveChildHeapMb = effectiveChildHeapMb;
|
|
8978
|
+
exports2.envHeapCap = envHeapCap;
|
|
8979
|
+
exports2.readMemoryLimitBytes = readMemoryLimitBytes;
|
|
8980
|
+
exports2.isolatedChildBudget = isolatedChildBudget;
|
|
8981
|
+
exports2.maxIsolatedChildren = maxIsolatedChildren;
|
|
8921
8982
|
exports2.tarIsolationLoad = tarIsolationLoad;
|
|
8983
|
+
exports2.isolationLoad = isolationLoad;
|
|
8922
8984
|
exports2.runTarJobIsolated = runTarJobIsolated;
|
|
8985
|
+
exports2.runIsolatedJob = runIsolatedJob;
|
|
8923
8986
|
exports2.extractTarGzIsolated = extractTarGzIsolated;
|
|
8924
8987
|
exports2.extractZipIsolated = extractZipIsolated;
|
|
8925
8988
|
exports2.slimRepoArchiveIsolated = slimRepoArchiveIsolated;
|
|
@@ -8927,8 +8990,10 @@ var require_tar_isolated = __commonJS({
|
|
|
8927
8990
|
var node_child_process_1 = require("child_process");
|
|
8928
8991
|
var node_fs_1 = require("fs");
|
|
8929
8992
|
var promises_1 = require("fs/promises");
|
|
8993
|
+
var node_os_1 = require("os");
|
|
8930
8994
|
var node_path_1 = require("path");
|
|
8931
8995
|
var node_url_1 = require("url");
|
|
8996
|
+
var node_v8_1 = require("v8");
|
|
8932
8997
|
var repo_acquire_js_1 = require_repo_acquire();
|
|
8933
8998
|
exports2.TAR_ISOLATED_TIMEOUT_MS = 5 * 60 * 1e3;
|
|
8934
8999
|
exports2.TAR_ISOLATED_MAX_OLD_SPACE_MB = 512;
|
|
@@ -8957,23 +9022,26 @@ var require_tar_isolated = __commonJS({
|
|
|
8957
9022
|
const n = Number(process.env[name]);
|
|
8958
9023
|
return Number.isSafeInteger(n) && n > 0 ? n : fallback;
|
|
8959
9024
|
}
|
|
8960
|
-
function
|
|
9025
|
+
function isolatedChildEnv(markerEnv, source = process.env) {
|
|
8961
9026
|
const env = {};
|
|
8962
9027
|
for (const key of CHILD_ENV_ALLOWLIST) {
|
|
8963
9028
|
const value = source[key];
|
|
8964
9029
|
if (typeof value === "string")
|
|
8965
9030
|
env[key] = value;
|
|
8966
9031
|
}
|
|
8967
|
-
env[
|
|
9032
|
+
env[markerEnv] = "1";
|
|
8968
9033
|
return env;
|
|
8969
9034
|
}
|
|
8970
|
-
function
|
|
8971
|
-
|
|
9035
|
+
function tarChildEnv(source = process.env) {
|
|
9036
|
+
return isolatedChildEnv(exports2.TAR_CHILD_MARKER_ENV, source);
|
|
9037
|
+
}
|
|
9038
|
+
function resolveIsolatedChildEntry(dir, basename) {
|
|
9039
|
+
if (typeof dir !== "string")
|
|
8972
9040
|
return null;
|
|
8973
|
-
const js = (0, node_path_1.join)(
|
|
9041
|
+
const js = (0, node_path_1.join)(dir, `${basename}.js`);
|
|
8974
9042
|
if ((0, node_fs_1.existsSync)(js))
|
|
8975
9043
|
return { file: js, execArgv: [] };
|
|
8976
|
-
const ts = (0, node_path_1.join)(
|
|
9044
|
+
const ts = (0, node_path_1.join)(dir, `${basename}.ts`);
|
|
8977
9045
|
if (!(0, node_fs_1.existsSync)(ts))
|
|
8978
9046
|
return null;
|
|
8979
9047
|
try {
|
|
@@ -8982,10 +9050,76 @@ var require_tar_isolated = __commonJS({
|
|
|
8982
9050
|
return null;
|
|
8983
9051
|
}
|
|
8984
9052
|
}
|
|
8985
|
-
|
|
9053
|
+
function resolveTarChildEntry() {
|
|
9054
|
+
return resolveIsolatedChildEntry(typeof __dirname === "string" ? __dirname : void 0, CHILD_BASENAME);
|
|
9055
|
+
}
|
|
9056
|
+
exports2.ISOLATED_MAX_CHILDREN = 2;
|
|
9057
|
+
exports2.TAR_ISOLATED_MAX_CHILDREN = exports2.ISOLATED_MAX_CHILDREN;
|
|
8986
9058
|
exports2.TAR_ISOLATED_MAX_CHILDREN_PER_TENANT = 1;
|
|
8987
|
-
|
|
8988
|
-
|
|
9059
|
+
exports2.ISOLATED_MEMORY_MARGIN_MB = 256;
|
|
9060
|
+
exports2.ISOLATED_MIN_CHILD_HEAP_MB = 256;
|
|
9061
|
+
var MB = 1024 * 1024;
|
|
9062
|
+
function deriveIsolatedChildBudget(input) {
|
|
9063
|
+
const freeMb = (input.memoryLimitBytes - input.heapLimitBytes) / MB - (input.marginMb ?? exports2.ISOLATED_MEMORY_MARGIN_MB);
|
|
9064
|
+
const fit = Math.floor(freeMb / Math.max(1, input.childHeapMb));
|
|
9065
|
+
if (Number.isFinite(fit) && fit >= 1)
|
|
9066
|
+
return { children: Math.min(exports2.ISOLATED_MAX_CHILDREN, fit), heapLimitMb: null };
|
|
9067
|
+
return { children: 1, heapLimitMb: Math.max(exports2.ISOLATED_MIN_CHILD_HEAP_MB, Number.isFinite(freeMb) ? Math.floor(freeMb) : 0) };
|
|
9068
|
+
}
|
|
9069
|
+
function deriveMaxIsolatedChildren(input) {
|
|
9070
|
+
return deriveIsolatedChildBudget(input).children;
|
|
9071
|
+
}
|
|
9072
|
+
function effectiveChildHeapMb(cap, budget) {
|
|
9073
|
+
if (cap.fromEnv || budget.heapLimitMb === null)
|
|
9074
|
+
return cap.mb;
|
|
9075
|
+
return Math.min(cap.mb, budget.heapLimitMb);
|
|
9076
|
+
}
|
|
9077
|
+
function envHeapCap(name, fallback) {
|
|
9078
|
+
const n = Number(process.env[name]);
|
|
9079
|
+
return Number.isSafeInteger(n) && n > 0 ? { mb: n, fromEnv: true } : { mb: fallback, fromEnv: false };
|
|
9080
|
+
}
|
|
9081
|
+
function readMemoryLimitBytes(read = (path) => (0, node_fs_1.readFileSync)(path, "utf8"), total = node_os_1.totalmem) {
|
|
9082
|
+
try {
|
|
9083
|
+
const raw = read("/sys/fs/cgroup/memory.max").trim();
|
|
9084
|
+
const limit = Number(raw);
|
|
9085
|
+
if (raw !== "max" && Number.isSafeInteger(limit) && limit > 0)
|
|
9086
|
+
return Math.min(limit, total());
|
|
9087
|
+
} catch {
|
|
9088
|
+
}
|
|
9089
|
+
return total();
|
|
9090
|
+
}
|
|
9091
|
+
var memoryLimitCache;
|
|
9092
|
+
var childHeapCaps = /* @__PURE__ */ new Set();
|
|
9093
|
+
function isolatedChildBudget() {
|
|
9094
|
+
const override = Number(process.env.ISOLATED_CHILDREN_MAX ?? process.env.REPO_ARCHIVE_EXTRACT_MAX_CHILDREN);
|
|
9095
|
+
if (Number.isSafeInteger(override) && override > 0)
|
|
9096
|
+
return { children: override, heapLimitMb: null };
|
|
9097
|
+
memoryLimitCache ??= readMemoryLimitBytes();
|
|
9098
|
+
let childHeapMb = 0;
|
|
9099
|
+
for (const cap of childHeapCaps)
|
|
9100
|
+
childHeapMb = Math.max(childHeapMb, cap().mb);
|
|
9101
|
+
return deriveIsolatedChildBudget({
|
|
9102
|
+
memoryLimitBytes: memoryLimitCache,
|
|
9103
|
+
heapLimitBytes: (0, node_v8_1.getHeapStatistics)().heap_size_limit,
|
|
9104
|
+
childHeapMb: childHeapMb || exports2.TAR_ISOLATED_MAX_OLD_SPACE_MB
|
|
9105
|
+
});
|
|
9106
|
+
}
|
|
9107
|
+
function maxIsolatedChildren() {
|
|
9108
|
+
return isolatedChildBudget().children;
|
|
9109
|
+
}
|
|
9110
|
+
var heapReductionLogged = false;
|
|
9111
|
+
function childHeapMbFor(slots, log) {
|
|
9112
|
+
const cap = slots.heapCap();
|
|
9113
|
+
const budget = isolatedChildBudget();
|
|
9114
|
+
const mb = effectiveChildHeapMb(cap, budget);
|
|
9115
|
+
if (mb < cap.mb && !heapReductionLogged) {
|
|
9116
|
+
heapReductionLogged = true;
|
|
9117
|
+
const floored = budget.heapLimitMb === exports2.ISOLATED_MIN_CHILD_HEAP_MB ? ` (the ${exports2.ISOLATED_MIN_CHILD_HEAP_MB} MB floor: the ${exports2.ISOLATED_MEMORY_MARGIN_MB} MB margin is not fully kept)` : "";
|
|
9118
|
+
const line = `[isolated-child] isolated child heap reduced to ${mb} MB to fit memory${floored}; set ISOLATED_CHILDREN_MAX or the kind's heap env knob to override`;
|
|
9119
|
+
console.warn(line);
|
|
9120
|
+
log?.(line);
|
|
9121
|
+
}
|
|
9122
|
+
return mb;
|
|
8989
9123
|
}
|
|
8990
9124
|
var SlotPool = class {
|
|
8991
9125
|
capacity;
|
|
@@ -9027,31 +9161,59 @@ var require_tar_isolated = __commonJS({
|
|
|
9027
9161
|
this.inUse -= 1;
|
|
9028
9162
|
}
|
|
9029
9163
|
};
|
|
9030
|
-
|
|
9031
|
-
var
|
|
9032
|
-
|
|
9033
|
-
|
|
9034
|
-
|
|
9035
|
-
|
|
9036
|
-
|
|
9037
|
-
|
|
9038
|
-
|
|
9039
|
-
|
|
9040
|
-
|
|
9041
|
-
|
|
9042
|
-
|
|
9043
|
-
|
|
9044
|
-
|
|
9164
|
+
exports2.SlotPool = SlotPool;
|
|
9165
|
+
var processSlots = new SlotPool(maxIsolatedChildren);
|
|
9166
|
+
var IsolationSlots = class {
|
|
9167
|
+
perTenant;
|
|
9168
|
+
heapCap;
|
|
9169
|
+
global = processSlots;
|
|
9170
|
+
/** Per-tenant pools, created on demand and dropped when idle. */
|
|
9171
|
+
tenants = /* @__PURE__ */ new Map();
|
|
9172
|
+
/** This kind's children still running. */
|
|
9173
|
+
live = /* @__PURE__ */ new Set();
|
|
9174
|
+
constructor(perTenant, heapCap) {
|
|
9175
|
+
this.perTenant = perTenant;
|
|
9176
|
+
this.heapCap = heapCap;
|
|
9177
|
+
childHeapCaps.add(heapCap);
|
|
9178
|
+
}
|
|
9179
|
+
tenantPool(key) {
|
|
9180
|
+
let pool = this.tenants.get(key);
|
|
9181
|
+
if (!pool) {
|
|
9182
|
+
pool = new SlotPool(() => this.perTenant);
|
|
9183
|
+
this.tenants.set(key, pool);
|
|
9184
|
+
}
|
|
9185
|
+
return pool;
|
|
9186
|
+
}
|
|
9187
|
+
/** Drop an idle tenant pool (after a release, or a wait that gave up). */
|
|
9188
|
+
forgetIfIdle(key, pool) {
|
|
9189
|
+
if (pool.inUse === 0 && pool.queued === 0 && this.tenants.get(key) === pool)
|
|
9190
|
+
this.tenants.delete(key);
|
|
9191
|
+
}
|
|
9192
|
+
releaseTenant(key, pool) {
|
|
9193
|
+
pool.release();
|
|
9194
|
+
this.forgetIfIdle(key, pool);
|
|
9195
|
+
}
|
|
9196
|
+
/** This kind's live children, process-wide slots held and waiting, and this kind's jobs waiting behind their own tenant. */
|
|
9197
|
+
load() {
|
|
9198
|
+
let tenantQueued = 0;
|
|
9199
|
+
for (const pool of this.tenants.values())
|
|
9200
|
+
tenantQueued += pool.queued;
|
|
9201
|
+
return { children: this.live.size, slots: this.global.inUse, queued: this.global.queued, tenantQueued };
|
|
9202
|
+
}
|
|
9203
|
+
};
|
|
9204
|
+
exports2.IsolationSlots = IsolationSlots;
|
|
9205
|
+
var tarSlots = new IsolationSlots(exports2.TAR_ISOLATED_MAX_CHILDREN_PER_TENANT, () => envHeapCap("REPO_ARCHIVE_EXTRACT_MAX_OLD_SPACE_MB", exports2.TAR_ISOLATED_MAX_OLD_SPACE_MB));
|
|
9045
9206
|
function tarIsolationLoad() {
|
|
9046
|
-
|
|
9047
|
-
|
|
9048
|
-
|
|
9049
|
-
return { children: liveChildren.size, slots:
|
|
9207
|
+
return tarSlots.load();
|
|
9208
|
+
}
|
|
9209
|
+
function isolationLoad() {
|
|
9210
|
+
return { children: liveChildren.size, slots: processSlots.inUse, queued: processSlots.queued };
|
|
9050
9211
|
}
|
|
9051
9212
|
var liveChildren = /* @__PURE__ */ new Set();
|
|
9052
9213
|
var exitHookInstalled = false;
|
|
9053
|
-
function trackChild(child) {
|
|
9214
|
+
function trackChild(child, slots) {
|
|
9054
9215
|
liveChildren.add(child);
|
|
9216
|
+
slots.live.add(child);
|
|
9055
9217
|
if (exitHookInstalled)
|
|
9056
9218
|
return;
|
|
9057
9219
|
exitHookInstalled = true;
|
|
@@ -9060,6 +9222,23 @@ var require_tar_isolated = __commonJS({
|
|
|
9060
9222
|
c.kill("SIGKILL");
|
|
9061
9223
|
});
|
|
9062
9224
|
}
|
|
9225
|
+
function untrackChild(child, slots) {
|
|
9226
|
+
liveChildren.delete(child);
|
|
9227
|
+
slots.live.delete(child);
|
|
9228
|
+
}
|
|
9229
|
+
var TAR_CHILD_SPEC = {
|
|
9230
|
+
name: CHILD_BASENAME,
|
|
9231
|
+
dir: typeof __dirname === "string" ? __dirname : void 0,
|
|
9232
|
+
resolveEntry: resolveTarChildEntry,
|
|
9233
|
+
markerEnv: exports2.TAR_CHILD_MARKER_ENV,
|
|
9234
|
+
slots: tarSlots,
|
|
9235
|
+
defaultTimeoutMs: () => positiveIntEnv("REPO_ARCHIVE_EXTRACT_TIMEOUT_MS", exports2.TAR_ISOLATED_TIMEOUT_MS),
|
|
9236
|
+
failureMessage: (subject, detail) => `CONFIG_ISSUE: The ${subject} couldn't be unpacked (${detail}).`,
|
|
9237
|
+
workerName: "archive extractor",
|
|
9238
|
+
childName: "extractor",
|
|
9239
|
+
jobName: "extraction",
|
|
9240
|
+
logPrefix: "[tar-isolated]"
|
|
9241
|
+
};
|
|
9063
9242
|
function jobCwd(job) {
|
|
9064
9243
|
switch (job.op) {
|
|
9065
9244
|
case "extract":
|
|
@@ -9073,58 +9252,64 @@ var require_tar_isolated = __commonJS({
|
|
|
9073
9252
|
}
|
|
9074
9253
|
}
|
|
9075
9254
|
function runTarJobIsolated(job, subject, options = {}) {
|
|
9076
|
-
return runJob(job, subject, options, true);
|
|
9255
|
+
return runJob(TAR_CHILD_SPEC, job, subject, { ...options, cwd: jobCwd(job) }, true);
|
|
9256
|
+
}
|
|
9257
|
+
function runIsolatedJob(spec, job, subject, options = {}) {
|
|
9258
|
+
return runJob(spec, job, subject, options, true);
|
|
9077
9259
|
}
|
|
9078
|
-
async function runJob(job, subject, options, limited) {
|
|
9079
|
-
const entry = options.testChildFile === void 0 ?
|
|
9260
|
+
async function runJob(spec, job, subject, options, limited) {
|
|
9261
|
+
const entry = options.testChildFile === void 0 ? spec.resolveEntry() : options.testChildFile && (0, node_fs_1.existsSync)(options.testChildFile) ? { file: options.testChildFile, execArgv: [] } : null;
|
|
9080
9262
|
if (!entry)
|
|
9081
|
-
throw new TarIsolationUnavailableError(`${
|
|
9082
|
-
const
|
|
9263
|
+
throw new TarIsolationUnavailableError(`${spec.name}.js was not found beside ${spec.dir ?? "this module"}`);
|
|
9264
|
+
const jobTitle = spec.jobName.charAt(0).toUpperCase() + spec.jobName.slice(1);
|
|
9265
|
+
const abortError = () => Object.assign(new Error(`${jobTitle} of the ${subject} was stopped`), { name: "AbortError" });
|
|
9083
9266
|
if (options.signal?.aborted)
|
|
9084
9267
|
throw abortError();
|
|
9085
|
-
const timeoutMs = options.timeoutMs ??
|
|
9086
|
-
const maxOldSpaceMb = options.maxOldSpaceMb ??
|
|
9087
|
-
const failure = (reason, detail) => new TarIsolationError(reason,
|
|
9268
|
+
const timeoutMs = options.timeoutMs ?? spec.defaultTimeoutMs();
|
|
9269
|
+
const maxOldSpaceMb = options.maxOldSpaceMb ?? childHeapMbFor(spec.slots, options.log);
|
|
9270
|
+
const failure = (reason, detail) => new TarIsolationError(reason, `${spec.failureMessage(subject, detail)}${options.hint ? ` ${options.hint}` : ""}`);
|
|
9088
9271
|
if (!limited)
|
|
9089
|
-
return runChild(job, entry, timeoutMs, maxOldSpaceMb, options, failure, abortError);
|
|
9090
|
-
const
|
|
9272
|
+
return runChild(spec, job, entry, timeoutMs, maxOldSpaceMb, options, failure, abortError);
|
|
9273
|
+
const queueMs = options.queueTimeoutMs ?? timeoutMs;
|
|
9274
|
+
const deadline = Date.now() + queueMs;
|
|
9091
9275
|
const queueTimeout = () => {
|
|
9092
|
-
const
|
|
9093
|
-
|
|
9276
|
+
const detail = `no ${spec.workerName} was free within ${Math.round(queueMs / 1e3)} s`;
|
|
9277
|
+
const err = spec.busyMessage ? new TarIsolationError("timeout", spec.busyMessage(subject, detail)) : failure("timeout", detail);
|
|
9278
|
+
options.log?.(`${spec.logPrefix} ${err.message}`);
|
|
9094
9279
|
return err;
|
|
9095
9280
|
};
|
|
9096
|
-
const
|
|
9281
|
+
const { slots } = spec;
|
|
9282
|
+
const tenant = options.tenantKey ? { key: options.tenantKey, pool: slots.tenantPool(options.tenantKey) } : null;
|
|
9097
9283
|
if (tenant) {
|
|
9098
|
-
const got = await tenant.pool.acquire(
|
|
9284
|
+
const got = await tenant.pool.acquire(queueMs, options.signal);
|
|
9099
9285
|
if (got !== "ok") {
|
|
9100
|
-
|
|
9101
|
-
tenantSlots.delete(tenant.key);
|
|
9286
|
+
slots.forgetIfIdle(tenant.key, tenant.pool);
|
|
9102
9287
|
throw got === "aborted" ? abortError() : queueTimeout();
|
|
9103
9288
|
}
|
|
9104
9289
|
}
|
|
9105
9290
|
let holdsGlobal = false;
|
|
9106
9291
|
try {
|
|
9107
|
-
const got = await
|
|
9292
|
+
const got = await slots.global.acquire(deadline - Date.now(), options.signal);
|
|
9108
9293
|
if (got !== "ok")
|
|
9109
9294
|
throw got === "aborted" ? abortError() : queueTimeout();
|
|
9110
9295
|
holdsGlobal = true;
|
|
9111
9296
|
if (options.signal?.aborted)
|
|
9112
9297
|
throw abortError();
|
|
9113
|
-
return await runChild(job, entry, timeoutMs, maxOldSpaceMb, options, failure, abortError);
|
|
9298
|
+
return await runChild(spec, job, entry, timeoutMs, maxOldSpaceMb, options, failure, abortError);
|
|
9114
9299
|
} finally {
|
|
9115
9300
|
if (holdsGlobal)
|
|
9116
|
-
|
|
9301
|
+
slots.global.release();
|
|
9117
9302
|
if (tenant)
|
|
9118
|
-
releaseTenant(tenant.key, tenant.pool);
|
|
9303
|
+
slots.releaseTenant(tenant.key, tenant.pool);
|
|
9119
9304
|
}
|
|
9120
9305
|
}
|
|
9121
|
-
function runChild(job, entry, timeoutMs, maxOldSpaceMb, options, failure, abortError) {
|
|
9306
|
+
function runChild(spec, job, entry, timeoutMs, maxOldSpaceMb, options, failure, abortError) {
|
|
9122
9307
|
return new Promise((resolvePromise, rejectPromise) => {
|
|
9123
9308
|
let child;
|
|
9124
9309
|
try {
|
|
9125
9310
|
child = (0, node_child_process_1.spawn)(process.execPath, [...entry.execArgv, `--max-old-space-size=${maxOldSpaceMb}`, entry.file], {
|
|
9126
|
-
cwd:
|
|
9127
|
-
env:
|
|
9311
|
+
cwd: options.cwd,
|
|
9312
|
+
env: isolatedChildEnv(spec.markerEnv),
|
|
9128
9313
|
stdio: ["ignore", "ignore", "pipe", "ipc"],
|
|
9129
9314
|
serialization: "json",
|
|
9130
9315
|
windowsHide: true
|
|
@@ -9133,7 +9318,7 @@ var require_tar_isolated = __commonJS({
|
|
|
9133
9318
|
rejectPromise(new TarIsolationUnavailableError(err instanceof Error ? err.message : String(err)));
|
|
9134
9319
|
return;
|
|
9135
9320
|
}
|
|
9136
|
-
trackChild(child);
|
|
9321
|
+
trackChild(child, spec.slots);
|
|
9137
9322
|
let started = false;
|
|
9138
9323
|
let timedOut = false;
|
|
9139
9324
|
let aborted = false;
|
|
@@ -9157,7 +9342,7 @@ var require_tar_isolated = __commonJS({
|
|
|
9157
9342
|
settled = true;
|
|
9158
9343
|
clearTimeout(timer);
|
|
9159
9344
|
options.signal?.removeEventListener("abort", onAbort);
|
|
9160
|
-
|
|
9345
|
+
untrackChild(child, spec.slots);
|
|
9161
9346
|
fn();
|
|
9162
9347
|
};
|
|
9163
9348
|
child.stderr?.setEncoding("utf8");
|
|
@@ -9200,18 +9385,18 @@ var require_tar_isolated = __commonJS({
|
|
|
9200
9385
|
}
|
|
9201
9386
|
const tail = stderr.trim();
|
|
9202
9387
|
if (tail)
|
|
9203
|
-
options.log?.(
|
|
9388
|
+
options.log?.(`${spec.logPrefix} ${spec.childName} stderr: ${tail.replace(/\s+/g, " ").slice(-1e3)}`);
|
|
9204
9389
|
let err;
|
|
9205
9390
|
if (timedOut) {
|
|
9206
|
-
err = failure("timeout",
|
|
9391
|
+
err = failure("timeout", `${spec.jobName} took longer than ${Math.round(timeoutMs / 1e3)} s and was stopped`);
|
|
9207
9392
|
} else if (outOfMemory) {
|
|
9208
|
-
err = failure("oom",
|
|
9393
|
+
err = failure("oom", `${spec.jobName} ran out of memory (${maxOldSpaceMb} MB heap cap)`);
|
|
9209
9394
|
} else if (signal) {
|
|
9210
|
-
err = failure("signal", `the
|
|
9395
|
+
err = failure("signal", `the ${spec.childName} was killed by ${signal}`);
|
|
9211
9396
|
} else {
|
|
9212
|
-
err = failure("crash", `the
|
|
9397
|
+
err = failure("crash", `the ${spec.childName} crashed (exit code ${code ?? "unknown"})`);
|
|
9213
9398
|
}
|
|
9214
|
-
options.log?.(
|
|
9399
|
+
options.log?.(`${spec.logPrefix} ${err.message}`);
|
|
9215
9400
|
rejectPromise(err);
|
|
9216
9401
|
});
|
|
9217
9402
|
});
|
|
@@ -9269,7 +9454,7 @@ var require_tar_isolated = __commonJS({
|
|
|
9269
9454
|
}
|
|
9270
9455
|
async function probeTarIsolation(options = {}) {
|
|
9271
9456
|
try {
|
|
9272
|
-
await runJob({ op: "ping" }, "archive", { timeoutMs: options.timeoutMs ?? 3e4 }, false);
|
|
9457
|
+
await runJob(TAR_CHILD_SPEC, { op: "ping" }, "archive", { timeoutMs: options.timeoutMs ?? 3e4 }, false);
|
|
9273
9458
|
return { ok: true, detail: `archives are extracted in a separate process (${resolveTarChildEntry()?.file ?? "unknown"})` };
|
|
9274
9459
|
} catch (err) {
|
|
9275
9460
|
return { ok: false, detail: err instanceof Error ? err.message : String(err) };
|
|
@@ -9575,7 +9760,6 @@ var require_repo_acquire = __commonJS({
|
|
|
9575
9760
|
var MAX_TAR_BYTES_AFTER_END = 1024 * 1024;
|
|
9576
9761
|
var TAR_OVERHEAD_PER_ENTRY = 4096;
|
|
9577
9762
|
var TAR_OVERHEAD_BASE = 16 * 1024 * 1024;
|
|
9578
|
-
var TAR_COUNTED_TYPES = /* @__PURE__ */ new Set(["File", "OldFile", "ContiguousFile", "Directory", "GNUDumpDir"]);
|
|
9579
9763
|
function refuseNestedGzip() {
|
|
9580
9764
|
let head = Buffer.alloc(0);
|
|
9581
9765
|
return new node_stream_1.Transform({
|
|
@@ -9639,7 +9823,7 @@ var require_repo_acquire = __commonJS({
|
|
|
9639
9823
|
bad = new Error(`Tar entry too large: ${shownPath} (declared ${size} bytes > ${limits.maxFileBytes} cap)`);
|
|
9640
9824
|
} else if ((declaredTotal += size) > limits.maxTotalBytes) {
|
|
9641
9825
|
bad = new Error(`Tar total uncompressed size exceeds ${limits.maxTotalBytes} cap (tar-bomb guard)`);
|
|
9642
|
-
} else if (
|
|
9826
|
+
} else if (++entries > limits.maxEntries) {
|
|
9643
9827
|
bad = new Error(`Tar has too many entries: > ${limits.maxEntries} cap`);
|
|
9644
9828
|
}
|
|
9645
9829
|
if (bad)
|
|
@@ -10052,36 +10236,11 @@ var require_tar_extract_child = __commonJS({
|
|
|
10052
10236
|
"use strict";
|
|
10053
10237
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
10054
10238
|
var promises_1 = require("fs/promises");
|
|
10239
|
+
var isolated_child_host_js_1 = require_isolated_child_host();
|
|
10055
10240
|
var repo_acquire_js_1 = require_repo_acquire();
|
|
10056
10241
|
var repo_snapshot_slim_js_1 = require_repo_snapshot_slim();
|
|
10057
10242
|
var tar_isolated_js_1 = require_tar_isolated();
|
|
10058
|
-
async function handle(
|
|
10059
|
-
switch (request.fault) {
|
|
10060
|
-
case "throw-async":
|
|
10061
|
-
setImmediate(() => {
|
|
10062
|
-
throw new Error("injected tar child crash");
|
|
10063
|
-
});
|
|
10064
|
-
return new Promise(() => {
|
|
10065
|
-
});
|
|
10066
|
-
case "abort":
|
|
10067
|
-
process.abort();
|
|
10068
|
-
break;
|
|
10069
|
-
case "hang":
|
|
10070
|
-
setInterval(() => {
|
|
10071
|
-
}, 1e3);
|
|
10072
|
-
return new Promise(() => {
|
|
10073
|
-
});
|
|
10074
|
-
case "oom": {
|
|
10075
|
-
const hold = [];
|
|
10076
|
-
for (; ; )
|
|
10077
|
-
hold.push(new Array(1e6).fill(hold.length));
|
|
10078
|
-
}
|
|
10079
|
-
case "report-env":
|
|
10080
|
-
return { ok: true, env: { ...process.env } };
|
|
10081
|
-
default:
|
|
10082
|
-
break;
|
|
10083
|
-
}
|
|
10084
|
-
const { job } = request;
|
|
10243
|
+
async function handle(job) {
|
|
10085
10244
|
switch (job.op) {
|
|
10086
10245
|
case "ping":
|
|
10087
10246
|
return { ok: true };
|
|
@@ -10099,19 +10258,7 @@ var require_tar_extract_child = __commonJS({
|
|
|
10099
10258
|
return { ok: false, message: "Unknown isolated tar job" };
|
|
10100
10259
|
}
|
|
10101
10260
|
}
|
|
10102
|
-
|
|
10103
|
-
process.send?.(message, () => process.exit(0));
|
|
10104
|
-
}
|
|
10105
|
-
if (typeof process.send === "function" && process.env[tar_isolated_js_1.TAR_CHILD_MARKER_ENV] === "1") {
|
|
10106
|
-
process.on("disconnect", () => process.exit(1));
|
|
10107
|
-
process.once("message", async (request) => {
|
|
10108
|
-
try {
|
|
10109
|
-
reply(await handle(request));
|
|
10110
|
-
} catch (err) {
|
|
10111
|
-
reply({ ok: false, message: err instanceof Error ? err.message : String(err) });
|
|
10112
|
-
}
|
|
10113
|
-
});
|
|
10114
|
-
}
|
|
10261
|
+
(0, isolated_child_host_js_1.serveIsolatedChild)(tar_isolated_js_1.TAR_CHILD_MARKER_ENV, handle);
|
|
10115
10262
|
}
|
|
10116
10263
|
});
|
|
10117
10264
|
|
|
@@ -5,22 +5,19 @@ import {
|
|
|
5
5
|
require_repo_acquire,
|
|
6
6
|
require_repo_snapshot_slim,
|
|
7
7
|
require_tar_isolated
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-J4ST6MFT.mjs";
|
|
9
9
|
|
|
10
|
-
// ../runner-core/dist/services/
|
|
11
|
-
var
|
|
12
|
-
"../runner-core/dist/services/
|
|
10
|
+
// ../runner-core/dist/services/isolated-child-host.js
|
|
11
|
+
var require_isolated_child_host = __commonJS({
|
|
12
|
+
"../runner-core/dist/services/isolated-child-host.js"(exports) {
|
|
13
13
|
"use strict";
|
|
14
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
var tar_isolated_js_1 = require_tar_isolated();
|
|
19
|
-
async function handle(request) {
|
|
20
|
-
switch (request.fault) {
|
|
15
|
+
exports.serveIsolatedChild = serveIsolatedChild;
|
|
16
|
+
function runFault(fault) {
|
|
17
|
+
switch (fault) {
|
|
21
18
|
case "throw-async":
|
|
22
19
|
setImmediate(() => {
|
|
23
|
-
throw new Error("injected
|
|
20
|
+
throw new Error("injected isolated child crash");
|
|
24
21
|
});
|
|
25
22
|
return new Promise(() => {
|
|
26
23
|
});
|
|
@@ -38,11 +35,41 @@ var require_tar_extract_child = __commonJS({
|
|
|
38
35
|
hold.push(new Array(1e6).fill(hold.length));
|
|
39
36
|
}
|
|
40
37
|
case "report-env":
|
|
41
|
-
return { ok: true, env: { ...process.env } };
|
|
38
|
+
return Promise.resolve({ ok: true, env: { ...process.env } });
|
|
42
39
|
default:
|
|
43
40
|
break;
|
|
44
41
|
}
|
|
45
|
-
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
function serveIsolatedChild(markerEnv, handle) {
|
|
45
|
+
if (typeof process.send !== "function" || process.env[markerEnv] !== "1")
|
|
46
|
+
return;
|
|
47
|
+
const reply = (message) => {
|
|
48
|
+
process.send?.(message, () => process.exit(0));
|
|
49
|
+
};
|
|
50
|
+
process.on("disconnect", () => process.exit(1));
|
|
51
|
+
process.once("message", async (request) => {
|
|
52
|
+
try {
|
|
53
|
+
reply(await (runFault(request.fault) ?? handle(request.job)));
|
|
54
|
+
} catch (err) {
|
|
55
|
+
reply({ ok: false, message: err instanceof Error ? err.message : String(err) });
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
// ../runner-core/dist/services/tar-extract-child.js
|
|
63
|
+
var require_tar_extract_child = __commonJS({
|
|
64
|
+
"../runner-core/dist/services/tar-extract-child.js"(exports) {
|
|
65
|
+
"use strict";
|
|
66
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
67
|
+
var promises_1 = __require("fs/promises");
|
|
68
|
+
var isolated_child_host_js_1 = require_isolated_child_host();
|
|
69
|
+
var repo_acquire_js_1 = require_repo_acquire();
|
|
70
|
+
var repo_snapshot_slim_js_1 = require_repo_snapshot_slim();
|
|
71
|
+
var tar_isolated_js_1 = require_tar_isolated();
|
|
72
|
+
async function handle(job) {
|
|
46
73
|
switch (job.op) {
|
|
47
74
|
case "ping":
|
|
48
75
|
return { ok: true };
|
|
@@ -60,19 +87,7 @@ var require_tar_extract_child = __commonJS({
|
|
|
60
87
|
return { ok: false, message: "Unknown isolated tar job" };
|
|
61
88
|
}
|
|
62
89
|
}
|
|
63
|
-
|
|
64
|
-
process.send?.(message, () => process.exit(0));
|
|
65
|
-
}
|
|
66
|
-
if (typeof process.send === "function" && process.env[tar_isolated_js_1.TAR_CHILD_MARKER_ENV] === "1") {
|
|
67
|
-
process.on("disconnect", () => process.exit(1));
|
|
68
|
-
process.once("message", async (request) => {
|
|
69
|
-
try {
|
|
70
|
-
reply(await handle(request));
|
|
71
|
-
} catch (err) {
|
|
72
|
-
reply({ ok: false, message: err instanceof Error ? err.message : String(err) });
|
|
73
|
-
}
|
|
74
|
-
});
|
|
75
|
-
}
|
|
90
|
+
(0, isolated_child_host_js_1.serveIsolatedChild)(tar_isolated_js_1.TAR_CHILD_MARKER_ENV, handle);
|
|
76
91
|
}
|
|
77
92
|
});
|
|
78
93
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@validate.qa/runner",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.98",
|
|
4
4
|
"description": "validate local test runner - execute Playwright tests on your machine, connected to validate.qa cloud.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"validate-runner": "dist/cli.js"
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
"scripts": {
|
|
9
9
|
"build": "tsup",
|
|
10
10
|
"dev": "tsx src/cli.ts",
|
|
11
|
-
"test": "node --import tsx --test src/retry-on-429.test.ts src/version-check.test.ts src/poll-interval.test.ts src/services/auth-state.test.ts src/run-no-longer-active.test.ts",
|
|
11
|
+
"test": "node --import tsx --test src/retry-on-429.test.ts src/version-check.test.ts src/poll-interval.test.ts src/services/auth-state.test.ts src/run-no-longer-active.test.ts src/heartbeat-active-runs.test.ts src/result-post-structure-413.test.ts src/result-shed-routes-413.test.ts src/result-media-stale-epoch.test.ts",
|
|
12
12
|
"test:mobile": "node --import tsx --test --test-concurrency=2 src/runner.mobile-ai-env.test.ts src/mobile-device-queue.test.ts src/device-lease.test.ts src/runner-watchdog.test.ts src/result-payload-budget.test.ts src/result-media-upload.test.ts src/services/appium-*.test.ts src/services/*mobile*.test.ts src/services/mobile/*.test.ts",
|
|
13
13
|
"smoke:installed": "node scripts/installed-run-smoke.mjs",
|
|
14
14
|
"prepublishOnly": "npm run build"
|
|
@@ -26,13 +26,14 @@
|
|
|
26
26
|
],
|
|
27
27
|
"dependencies": {
|
|
28
28
|
"@modelcontextprotocol/sdk": "^1.6.1",
|
|
29
|
-
"@playwright/
|
|
30
|
-
"@playwright/test": "1.59.0-alpha-1771104257000",
|
|
29
|
+
"@playwright/test": "1.64.1",
|
|
31
30
|
"axe-core": "4.14.0",
|
|
32
31
|
"commander": "^13.1.0",
|
|
33
32
|
"dotenv": "^16.4.5",
|
|
34
33
|
"mockttp": "^4.4.2",
|
|
35
|
-
"openai": "^4.73.0"
|
|
34
|
+
"openai": "^4.73.0",
|
|
35
|
+
"playwright": "1.64.1",
|
|
36
|
+
"playwright-core": "1.64.1"
|
|
36
37
|
},
|
|
37
38
|
"devDependencies": {
|
|
38
39
|
"@types/node": "^24.10.1",
|