@mastra/docker 0.11.1-alpha.1 → 0.12.0-alpha.2
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/README.md +37 -0
- package/dist/index.cjs +337 -10
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +337 -10
- package/dist/index.js.map +1 -1
- package/dist/sandbox/index.d.ts +8 -0
- package/dist/sandbox/index.d.ts.map +1 -1
- package/dist/template/build-progress.d.ts +92 -0
- package/dist/template/build-progress.d.ts.map +1 -0
- package/dist/template/build-session.d.ts.map +1 -1
- package/dist/template/index.d.ts +1 -0
- package/dist/template/index.d.ts.map +1 -1
- package/dist/template/template.d.ts +32 -2
- package/dist/template/template.d.ts.map +1 -1
- package/package.json +4 -5
package/dist/index.js
CHANGED
|
@@ -582,6 +582,7 @@ var DockerSandbox = class DockerSandbox extends MastraSandbox {
|
|
|
582
582
|
/** Image the container boots from; rewritten by a template resolution on `start()`. */
|
|
583
583
|
_image;
|
|
584
584
|
_templateSpec;
|
|
585
|
+
_onTemplateBuildProgress;
|
|
585
586
|
_workingDirectoryWasSet;
|
|
586
587
|
_command;
|
|
587
588
|
_env;
|
|
@@ -625,6 +626,7 @@ var DockerSandbox = class DockerSandbox extends MastraSandbox {
|
|
|
625
626
|
this._containerName = sanitizeContainerName(options.name ?? this.id);
|
|
626
627
|
if (options.image !== void 0 && options.template !== void 0) throw new TypeError("DockerSandbox: `image` and `template` are mutually exclusive");
|
|
627
628
|
this._templateSpec = options.template;
|
|
629
|
+
this._onTemplateBuildProgress = options.onTemplateBuildProgress;
|
|
628
630
|
this._image = options.image ?? "node:22-slim";
|
|
629
631
|
this._workingDirectoryWasSet = options.workingDirectory !== void 0 || options.workingDir !== void 0;
|
|
630
632
|
this._command = options.command ?? ["sleep", "infinity"];
|
|
@@ -984,7 +986,8 @@ var DockerSandbox = class DockerSandbox extends MastraSandbox {
|
|
|
984
986
|
throwIfAborted(abortSignal, "start Docker sandbox");
|
|
985
987
|
const result = await template.build({
|
|
986
988
|
docker: this._docker,
|
|
987
|
-
abortSignal
|
|
989
|
+
abortSignal,
|
|
990
|
+
...this._onTemplateBuildProgress && { onProgress: this._onTemplateBuildProgress }
|
|
988
991
|
});
|
|
989
992
|
if (result.status !== "ready") throw new SandboxError(`Docker template build failed: ${result.error ?? "unknown error"}`, "START_FAILED", {
|
|
990
993
|
templateId: result.templateId,
|
|
@@ -1337,6 +1340,287 @@ const dockerSandboxProvider = {
|
|
|
1337
1340
|
createSandbox: (config) => new DockerSandbox(config)
|
|
1338
1341
|
};
|
|
1339
1342
|
//#endregion
|
|
1343
|
+
//#region src/template/build-progress.ts
|
|
1344
|
+
/**
|
|
1345
|
+
* Build progress for `DockerTemplate.build()`.
|
|
1346
|
+
*
|
|
1347
|
+
* The daemon reports build progress as newline-delimited JSON. The legacy
|
|
1348
|
+
* builder sends plain text (`{stream: "Step 2/5 : RUN ..."}`); BuildKit sends
|
|
1349
|
+
* `{id: 'moby.buildkit.trace', aux: <base64 StatusResponse protobuf>}`. This
|
|
1350
|
+
* module normalizes both into {@link DockerTemplateBuildEvent}s and keeps a
|
|
1351
|
+
* bounded tail of raw output for failure diagnostics.
|
|
1352
|
+
*/
|
|
1353
|
+
const DEFAULT_MAX_OUTPUT_BYTES = 64 * 1024;
|
|
1354
|
+
/** Keeps at most `maxBytes` (UTF-8) of the most recent text. */
|
|
1355
|
+
var BoundedTail = class {
|
|
1356
|
+
#maxBytes;
|
|
1357
|
+
#chunks = [];
|
|
1358
|
+
#bytes = 0;
|
|
1359
|
+
#truncated = false;
|
|
1360
|
+
constructor(maxBytes) {
|
|
1361
|
+
this.#maxBytes = maxBytes;
|
|
1362
|
+
}
|
|
1363
|
+
get truncated() {
|
|
1364
|
+
return this.#truncated;
|
|
1365
|
+
}
|
|
1366
|
+
push(text) {
|
|
1367
|
+
if (text.length === 0) return;
|
|
1368
|
+
let chunk = Buffer.from(text, "utf8");
|
|
1369
|
+
if (chunk.length > this.#maxBytes) {
|
|
1370
|
+
chunk = chunk.subarray(chunk.length - this.#maxBytes);
|
|
1371
|
+
this.#chunks = [];
|
|
1372
|
+
this.#bytes = 0;
|
|
1373
|
+
this.#truncated = true;
|
|
1374
|
+
}
|
|
1375
|
+
this.#chunks.push(chunk);
|
|
1376
|
+
this.#bytes += chunk.length;
|
|
1377
|
+
while (this.#bytes > this.#maxBytes) {
|
|
1378
|
+
const head = this.#chunks[0];
|
|
1379
|
+
const excess = this.#bytes - this.#maxBytes;
|
|
1380
|
+
this.#truncated = true;
|
|
1381
|
+
if (head.length <= excess) {
|
|
1382
|
+
this.#chunks.shift();
|
|
1383
|
+
this.#bytes -= head.length;
|
|
1384
|
+
} else {
|
|
1385
|
+
this.#chunks[0] = head.subarray(excess);
|
|
1386
|
+
this.#bytes -= excess;
|
|
1387
|
+
}
|
|
1388
|
+
}
|
|
1389
|
+
}
|
|
1390
|
+
toString() {
|
|
1391
|
+
let text = Buffer.concat(this.#chunks).toString("utf8");
|
|
1392
|
+
if (this.#truncated) text = text.replace(/^\uFFFD+/, "");
|
|
1393
|
+
return text;
|
|
1394
|
+
}
|
|
1395
|
+
};
|
|
1396
|
+
function readFields(buffer) {
|
|
1397
|
+
const fields = [];
|
|
1398
|
+
let offset = 0;
|
|
1399
|
+
const readVarint = () => {
|
|
1400
|
+
let result = 0;
|
|
1401
|
+
let shift = 0;
|
|
1402
|
+
for (;;) {
|
|
1403
|
+
if (offset >= buffer.length) throw new Error("truncated varint");
|
|
1404
|
+
const byte = buffer[offset++];
|
|
1405
|
+
result += (byte & 127) * 2 ** shift;
|
|
1406
|
+
if ((byte & 128) === 0) return result;
|
|
1407
|
+
shift += 7;
|
|
1408
|
+
}
|
|
1409
|
+
};
|
|
1410
|
+
while (offset < buffer.length) {
|
|
1411
|
+
const key = readVarint();
|
|
1412
|
+
const field = Math.floor(key / 8);
|
|
1413
|
+
const wireType = key % 8;
|
|
1414
|
+
switch (wireType) {
|
|
1415
|
+
case 0:
|
|
1416
|
+
fields.push({
|
|
1417
|
+
field,
|
|
1418
|
+
value: readVarint()
|
|
1419
|
+
});
|
|
1420
|
+
break;
|
|
1421
|
+
case 1:
|
|
1422
|
+
offset += 8;
|
|
1423
|
+
break;
|
|
1424
|
+
case 5:
|
|
1425
|
+
offset += 4;
|
|
1426
|
+
break;
|
|
1427
|
+
case 2: {
|
|
1428
|
+
const length = readVarint();
|
|
1429
|
+
if (offset + length > buffer.length) throw new Error("truncated field");
|
|
1430
|
+
fields.push({
|
|
1431
|
+
field,
|
|
1432
|
+
value: buffer.subarray(offset, offset + length)
|
|
1433
|
+
});
|
|
1434
|
+
offset += length;
|
|
1435
|
+
break;
|
|
1436
|
+
}
|
|
1437
|
+
default: throw new Error(`unsupported protobuf wire type ${wireType}`);
|
|
1438
|
+
}
|
|
1439
|
+
}
|
|
1440
|
+
return fields;
|
|
1441
|
+
}
|
|
1442
|
+
/** @internal exported for tests. Throws on malformed input. */
|
|
1443
|
+
function decodeBuildkitStatus(aux) {
|
|
1444
|
+
const vertexes = [];
|
|
1445
|
+
const logs = [];
|
|
1446
|
+
for (const { field, value } of readFields(Buffer.from(aux, "base64"))) {
|
|
1447
|
+
if (!Buffer.isBuffer(value)) continue;
|
|
1448
|
+
if (field === 1) {
|
|
1449
|
+
const vertex = {
|
|
1450
|
+
digest: "",
|
|
1451
|
+
cached: false,
|
|
1452
|
+
started: false,
|
|
1453
|
+
completed: false
|
|
1454
|
+
};
|
|
1455
|
+
for (const f of readFields(value)) if (f.field === 1 && Buffer.isBuffer(f.value)) vertex.digest = f.value.toString("utf8");
|
|
1456
|
+
else if (f.field === 3 && Buffer.isBuffer(f.value)) vertex.name = f.value.toString("utf8");
|
|
1457
|
+
else if (f.field === 4) vertex.cached = f.value !== 0;
|
|
1458
|
+
else if (f.field === 5) vertex.started = true;
|
|
1459
|
+
else if (f.field === 6) vertex.completed = true;
|
|
1460
|
+
else if (f.field === 7 && Buffer.isBuffer(f.value) && f.value.length > 0) vertex.error = f.value.toString("utf8");
|
|
1461
|
+
if (vertex.digest) vertexes.push(vertex);
|
|
1462
|
+
} else if (field === 3) {
|
|
1463
|
+
let vertex = "";
|
|
1464
|
+
let data = Buffer.alloc(0);
|
|
1465
|
+
for (const f of readFields(value)) if (f.field === 1 && Buffer.isBuffer(f.value)) vertex = f.value.toString("utf8");
|
|
1466
|
+
else if (f.field === 4 && Buffer.isBuffer(f.value)) data = f.value;
|
|
1467
|
+
logs.push({
|
|
1468
|
+
vertex,
|
|
1469
|
+
data
|
|
1470
|
+
});
|
|
1471
|
+
}
|
|
1472
|
+
}
|
|
1473
|
+
return {
|
|
1474
|
+
vertexes,
|
|
1475
|
+
logs
|
|
1476
|
+
};
|
|
1477
|
+
}
|
|
1478
|
+
const LEGACY_STEP = /^Step (\d+)\/(\d+) : (.*)$/;
|
|
1479
|
+
function createBuildEventNormalizer(emit, tail) {
|
|
1480
|
+
let failure;
|
|
1481
|
+
let failedStep;
|
|
1482
|
+
let legacyStep;
|
|
1483
|
+
let legacyCached;
|
|
1484
|
+
let pending = "";
|
|
1485
|
+
const vertexes = /* @__PURE__ */ new Map();
|
|
1486
|
+
const decoders = /* @__PURE__ */ new Map();
|
|
1487
|
+
const output = (text, stepId) => {
|
|
1488
|
+
if (!text) return;
|
|
1489
|
+
tail.push(text);
|
|
1490
|
+
emit({
|
|
1491
|
+
type: "output",
|
|
1492
|
+
...stepId !== void 0 && { stepId },
|
|
1493
|
+
text
|
|
1494
|
+
});
|
|
1495
|
+
};
|
|
1496
|
+
const completeLegacyStep = (error) => {
|
|
1497
|
+
if (!legacyStep) return;
|
|
1498
|
+
const step = {
|
|
1499
|
+
...legacyStep,
|
|
1500
|
+
...legacyCached !== void 0 && { cached: legacyCached }
|
|
1501
|
+
};
|
|
1502
|
+
if (error !== void 0) failedStep = step;
|
|
1503
|
+
emit({
|
|
1504
|
+
type: "step-complete",
|
|
1505
|
+
step,
|
|
1506
|
+
...error !== void 0 && { error }
|
|
1507
|
+
});
|
|
1508
|
+
legacyStep = void 0;
|
|
1509
|
+
legacyCached = void 0;
|
|
1510
|
+
};
|
|
1511
|
+
const handleLegacyLine = (line) => {
|
|
1512
|
+
const match = LEGACY_STEP.exec(line.replace(/\r?\n$/, ""));
|
|
1513
|
+
if (match) {
|
|
1514
|
+
completeLegacyStep();
|
|
1515
|
+
legacyStep = {
|
|
1516
|
+
id: match[1],
|
|
1517
|
+
name: match[3],
|
|
1518
|
+
index: Number(match[1]),
|
|
1519
|
+
total: Number(match[2])
|
|
1520
|
+
};
|
|
1521
|
+
emit({
|
|
1522
|
+
type: "step-start",
|
|
1523
|
+
step: { ...legacyStep }
|
|
1524
|
+
});
|
|
1525
|
+
return;
|
|
1526
|
+
}
|
|
1527
|
+
if (legacyStep) {
|
|
1528
|
+
const trimmed = line.trim();
|
|
1529
|
+
if (trimmed === "---> Using cache") legacyCached = true;
|
|
1530
|
+
else if (trimmed.startsWith("---> Running in ") && legacyCached === void 0) legacyCached = false;
|
|
1531
|
+
}
|
|
1532
|
+
output(line, legacyStep?.id);
|
|
1533
|
+
};
|
|
1534
|
+
const handleLegacyStream = (text) => {
|
|
1535
|
+
pending += text;
|
|
1536
|
+
let newline = pending.indexOf("\n");
|
|
1537
|
+
while (newline !== -1) {
|
|
1538
|
+
handleLegacyLine(pending.slice(0, newline + 1));
|
|
1539
|
+
pending = pending.slice(newline + 1);
|
|
1540
|
+
newline = pending.indexOf("\n");
|
|
1541
|
+
}
|
|
1542
|
+
};
|
|
1543
|
+
const flushLegacy = () => {
|
|
1544
|
+
if (pending) {
|
|
1545
|
+
handleLegacyLine(pending);
|
|
1546
|
+
pending = "";
|
|
1547
|
+
}
|
|
1548
|
+
};
|
|
1549
|
+
const handleBuildkit = (aux) => {
|
|
1550
|
+
const { vertexes: updates, logs } = decodeBuildkitStatus(aux);
|
|
1551
|
+
for (const update of updates) {
|
|
1552
|
+
let state = vertexes.get(update.digest);
|
|
1553
|
+
if (!state) {
|
|
1554
|
+
state = {
|
|
1555
|
+
step: {
|
|
1556
|
+
id: update.digest,
|
|
1557
|
+
name: update.name ?? ""
|
|
1558
|
+
},
|
|
1559
|
+
started: false,
|
|
1560
|
+
completed: false
|
|
1561
|
+
};
|
|
1562
|
+
vertexes.set(update.digest, state);
|
|
1563
|
+
}
|
|
1564
|
+
if (update.name && !state.step.name) state.step.name = update.name;
|
|
1565
|
+
if (update.cached) state.step.cached = true;
|
|
1566
|
+
if (state.step.name.startsWith("[internal]")) continue;
|
|
1567
|
+
if (!state.started && (update.started || update.completed || update.cached || update.error)) {
|
|
1568
|
+
state.started = true;
|
|
1569
|
+
emit({
|
|
1570
|
+
type: "step-start",
|
|
1571
|
+
step: { ...state.step }
|
|
1572
|
+
});
|
|
1573
|
+
}
|
|
1574
|
+
if (!state.completed && (update.completed || update.error)) {
|
|
1575
|
+
state.completed = true;
|
|
1576
|
+
if (state.step.cached === void 0) state.step.cached = false;
|
|
1577
|
+
const step = { ...state.step };
|
|
1578
|
+
if (update.error) failedStep = step;
|
|
1579
|
+
emit({
|
|
1580
|
+
type: "step-complete",
|
|
1581
|
+
step,
|
|
1582
|
+
...update.error && { error: update.error }
|
|
1583
|
+
});
|
|
1584
|
+
}
|
|
1585
|
+
}
|
|
1586
|
+
for (const log of logs) {
|
|
1587
|
+
if (vertexes.get(log.vertex)?.step.name.startsWith("[internal]")) continue;
|
|
1588
|
+
let decoder = decoders.get(log.vertex);
|
|
1589
|
+
if (!decoder) {
|
|
1590
|
+
decoder = new StringDecoder("utf8");
|
|
1591
|
+
decoders.set(log.vertex, decoder);
|
|
1592
|
+
}
|
|
1593
|
+
output(decoder.write(log.data), log.vertex || void 0);
|
|
1594
|
+
}
|
|
1595
|
+
};
|
|
1596
|
+
const handleError = (raw) => {
|
|
1597
|
+
const detail = raw.errorDetail;
|
|
1598
|
+
const message = String(detail?.message ?? raw.error ?? "docker build failed");
|
|
1599
|
+
flushLegacy();
|
|
1600
|
+
completeLegacyStep(message);
|
|
1601
|
+
if (!failure) failure = {
|
|
1602
|
+
message,
|
|
1603
|
+
...failedStep && { step: failedStep }
|
|
1604
|
+
};
|
|
1605
|
+
};
|
|
1606
|
+
return {
|
|
1607
|
+
handle(raw) {
|
|
1608
|
+
if (!raw || typeof raw !== "object") return;
|
|
1609
|
+
const event = raw;
|
|
1610
|
+
try {
|
|
1611
|
+
if (event.error !== void 0 || event.errorDetail !== void 0) handleError(event);
|
|
1612
|
+
else if (event.id === "moby.buildkit.trace" && typeof event.aux === "string") handleBuildkit(event.aux);
|
|
1613
|
+
else if (typeof event.stream === "string") handleLegacyStream(event.stream);
|
|
1614
|
+
} catch {}
|
|
1615
|
+
},
|
|
1616
|
+
finish() {
|
|
1617
|
+
flushLegacy();
|
|
1618
|
+
completeLegacyStep();
|
|
1619
|
+
},
|
|
1620
|
+
failure: () => failure
|
|
1621
|
+
};
|
|
1622
|
+
}
|
|
1623
|
+
//#endregion
|
|
1340
1624
|
//#region src/template/build-session.ts
|
|
1341
1625
|
/**
|
|
1342
1626
|
* BuildKit session that serves build secrets to the daemon.
|
|
@@ -1873,20 +2157,29 @@ var DockerTemplate = class DockerTemplate {
|
|
|
1873
2157
|
* `options.secrets`, the template's `secrets` option, or `process.env`.
|
|
1874
2158
|
*/
|
|
1875
2159
|
async build(options = {}) {
|
|
2160
|
+
const { maxOutputBytes = DEFAULT_MAX_OUTPUT_BYTES } = options;
|
|
2161
|
+
if (!Number.isSafeInteger(maxOutputBytes) || maxOutputBytes <= 0) throw new RangeError("maxOutputBytes must be a positive integer");
|
|
1876
2162
|
throwIfAborted(options.abortSignal, "build Docker template");
|
|
1877
2163
|
const isPlain = !options.force && options.secrets === void 0 && options.docker === void 0;
|
|
1878
|
-
if (isPlain && this.#plainBuild) return this.#waitForBuild(this.#plainBuild, options.abortSignal);
|
|
2164
|
+
if (isPlain && this.#plainBuild) return this.#waitForBuild(this.#plainBuild, options.abortSignal, options.onProgress);
|
|
1879
2165
|
const previous = this.#queueTail?.promise.catch(() => void 0) ?? Promise.resolve();
|
|
1880
2166
|
const controller = new AbortController();
|
|
2167
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
2168
|
+
const emit = (event) => {
|
|
2169
|
+
for (const listener of [...listeners]) try {
|
|
2170
|
+
listener(event);
|
|
2171
|
+
} catch {}
|
|
2172
|
+
};
|
|
1881
2173
|
const run = {
|
|
1882
2174
|
controller,
|
|
1883
2175
|
waiters: 0,
|
|
2176
|
+
listeners,
|
|
1884
2177
|
promise: previous.then(() => {
|
|
1885
2178
|
throwIfAborted(controller.signal, "build Docker template");
|
|
1886
2179
|
return this.#build({
|
|
1887
2180
|
...options,
|
|
1888
2181
|
abortSignal: controller.signal
|
|
1889
|
-
});
|
|
2182
|
+
}, emit, maxOutputBytes);
|
|
1890
2183
|
})
|
|
1891
2184
|
};
|
|
1892
2185
|
this.#queueTail = run;
|
|
@@ -1895,20 +2188,23 @@ var DockerTemplate = class DockerTemplate {
|
|
|
1895
2188
|
if (this.#queueTail === run) this.#queueTail = void 0;
|
|
1896
2189
|
if (this.#plainBuild === run) this.#plainBuild = void 0;
|
|
1897
2190
|
}).catch(() => void 0);
|
|
1898
|
-
return this.#waitForBuild(run, options.abortSignal);
|
|
2191
|
+
return this.#waitForBuild(run, options.abortSignal, options.onProgress);
|
|
1899
2192
|
}
|
|
1900
|
-
#waitForBuild(run, abortSignal) {
|
|
2193
|
+
#waitForBuild(run, abortSignal, onProgress) {
|
|
1901
2194
|
run.waiters += 1;
|
|
2195
|
+
const listener = onProgress && ((event) => onProgress(event));
|
|
2196
|
+
if (listener) run.listeners.add(listener);
|
|
1902
2197
|
let settled = false;
|
|
1903
2198
|
const release = (aborted) => {
|
|
1904
2199
|
if (settled) return;
|
|
1905
2200
|
settled = true;
|
|
2201
|
+
if (listener) run.listeners.delete(listener);
|
|
1906
2202
|
run.waiters -= 1;
|
|
1907
2203
|
if (aborted && run.waiters === 0) run.controller.abort();
|
|
1908
2204
|
};
|
|
1909
2205
|
return waitForAbortable(run.promise, abortSignal, "build Docker template", () => release(true)).finally(() => release(false));
|
|
1910
2206
|
}
|
|
1911
|
-
async #build(options) {
|
|
2207
|
+
async #build(options, emit, maxOutputBytes) {
|
|
1912
2208
|
const docker = options.docker ?? this.#getDocker();
|
|
1913
2209
|
const tag = this.templateId;
|
|
1914
2210
|
const { abortSignal } = options;
|
|
@@ -1917,6 +2213,10 @@ var DockerTemplate = class DockerTemplate {
|
|
|
1917
2213
|
await docker.getImage(tag).inspect();
|
|
1918
2214
|
throwIfAborted(abortSignal, "build Docker template");
|
|
1919
2215
|
this.#built = true;
|
|
2216
|
+
emit({
|
|
2217
|
+
type: "reused",
|
|
2218
|
+
templateId: tag
|
|
2219
|
+
});
|
|
1920
2220
|
return {
|
|
1921
2221
|
status: "ready",
|
|
1922
2222
|
templateId: tag
|
|
@@ -1929,12 +2229,19 @@ var DockerTemplate = class DockerTemplate {
|
|
|
1929
2229
|
const context = pack();
|
|
1930
2230
|
context.entry({ name: "Dockerfile" }, this.dockerfile);
|
|
1931
2231
|
context.finalize();
|
|
2232
|
+
const tail = new BoundedTail(maxOutputBytes);
|
|
2233
|
+
const normalizer = createBuildEventNormalizer(emit, tail);
|
|
2234
|
+
emit({
|
|
2235
|
+
type: "build-start",
|
|
2236
|
+
templateId: tag,
|
|
2237
|
+
builder: secrets ? "buildkit" : "legacy"
|
|
2238
|
+
});
|
|
1932
2239
|
try {
|
|
1933
2240
|
const nocache = options.force === true;
|
|
1934
2241
|
if (secrets) {
|
|
1935
2242
|
const { stream, session } = await this.#buildWithSecrets(docker, context, tag, secrets, nocache, abortSignal);
|
|
1936
2243
|
try {
|
|
1937
|
-
await this.#followBuild(docker, stream, abortSignal);
|
|
2244
|
+
await this.#followBuild(docker, stream, normalizer, abortSignal);
|
|
1938
2245
|
} finally {
|
|
1939
2246
|
session.close();
|
|
1940
2247
|
}
|
|
@@ -1952,18 +2259,31 @@ var DockerTemplate = class DockerTemplate {
|
|
|
1952
2259
|
}), abortSignal, "build Docker template", () => {
|
|
1953
2260
|
context.destroy?.();
|
|
1954
2261
|
});
|
|
1955
|
-
await this.#followBuild(docker, stream, abortSignal);
|
|
2262
|
+
await this.#followBuild(docker, stream, normalizer, abortSignal);
|
|
1956
2263
|
}
|
|
1957
2264
|
} catch (error) {
|
|
1958
2265
|
this.#built = false;
|
|
1959
2266
|
if (abortSignal?.aborted) throw normalizeAbortError(error, "build Docker template");
|
|
2267
|
+
emit({
|
|
2268
|
+
type: "build-complete",
|
|
2269
|
+
status: "failed"
|
|
2270
|
+
});
|
|
2271
|
+
const failedStep = normalizer.failure()?.step;
|
|
1960
2272
|
return {
|
|
1961
2273
|
status: "failed",
|
|
1962
2274
|
templateId: tag,
|
|
1963
|
-
error: error instanceof Error ? error.message : String(error)
|
|
2275
|
+
error: error instanceof Error ? error.message : String(error),
|
|
2276
|
+
...failedStep && { failedStep },
|
|
2277
|
+
output: tail.toString(),
|
|
2278
|
+
outputTruncated: tail.truncated
|
|
1964
2279
|
};
|
|
1965
2280
|
}
|
|
2281
|
+
normalizer.finish();
|
|
1966
2282
|
this.#built = true;
|
|
2283
|
+
emit({
|
|
2284
|
+
type: "build-complete",
|
|
2285
|
+
status: "ready"
|
|
2286
|
+
});
|
|
1967
2287
|
return {
|
|
1968
2288
|
status: "ready",
|
|
1969
2289
|
templateId: tag
|
|
@@ -2041,7 +2361,7 @@ var DockerTemplate = class DockerTemplate {
|
|
|
2041
2361
|
session
|
|
2042
2362
|
};
|
|
2043
2363
|
}
|
|
2044
|
-
#followBuild(docker, stream, abortSignal) {
|
|
2364
|
+
#followBuild(docker, stream, normalizer, abortSignal) {
|
|
2045
2365
|
return new Promise((resolve, reject) => {
|
|
2046
2366
|
let settled = false;
|
|
2047
2367
|
const finish = (error) => {
|
|
@@ -2064,6 +2384,11 @@ var DockerTemplate = class DockerTemplate {
|
|
|
2064
2384
|
finish(err);
|
|
2065
2385
|
return;
|
|
2066
2386
|
}
|
|
2387
|
+
const reported = normalizer.failure();
|
|
2388
|
+
if (reported) {
|
|
2389
|
+
finish(new Error(reported.message));
|
|
2390
|
+
return;
|
|
2391
|
+
}
|
|
2067
2392
|
const failure = output?.find((entry) => entry && (entry.error !== void 0 || entry.errorDetail !== void 0));
|
|
2068
2393
|
if (failure) {
|
|
2069
2394
|
const detail = failure.errorDetail;
|
|
@@ -2071,6 +2396,8 @@ var DockerTemplate = class DockerTemplate {
|
|
|
2071
2396
|
return;
|
|
2072
2397
|
}
|
|
2073
2398
|
finish();
|
|
2399
|
+
}, (event) => {
|
|
2400
|
+
if (!settled) normalizer.handle(event);
|
|
2074
2401
|
});
|
|
2075
2402
|
});
|
|
2076
2403
|
}
|