@mastra/docker 0.11.1-alpha.0 → 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 +340 -12
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +340 -12
- 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/sandbox/process-manager.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 +5 -6
package/dist/index.js
CHANGED
|
@@ -279,10 +279,11 @@ var DockerProcessHandle = class extends ProcessHandle {
|
|
|
279
279
|
"sh",
|
|
280
280
|
this._pgidFile
|
|
281
281
|
],
|
|
282
|
-
AttachStdout:
|
|
283
|
-
AttachStderr:
|
|
282
|
+
AttachStdout: true,
|
|
283
|
+
AttachStderr: true
|
|
284
284
|
});
|
|
285
285
|
const killStream = await killExec.start({});
|
|
286
|
+
killStream.resume();
|
|
286
287
|
try {
|
|
287
288
|
let killInfo = await killExec.inspect();
|
|
288
289
|
while (killInfo.Running) {
|
|
@@ -581,6 +582,7 @@ var DockerSandbox = class DockerSandbox extends MastraSandbox {
|
|
|
581
582
|
/** Image the container boots from; rewritten by a template resolution on `start()`. */
|
|
582
583
|
_image;
|
|
583
584
|
_templateSpec;
|
|
585
|
+
_onTemplateBuildProgress;
|
|
584
586
|
_workingDirectoryWasSet;
|
|
585
587
|
_command;
|
|
586
588
|
_env;
|
|
@@ -624,6 +626,7 @@ var DockerSandbox = class DockerSandbox extends MastraSandbox {
|
|
|
624
626
|
this._containerName = sanitizeContainerName(options.name ?? this.id);
|
|
625
627
|
if (options.image !== void 0 && options.template !== void 0) throw new TypeError("DockerSandbox: `image` and `template` are mutually exclusive");
|
|
626
628
|
this._templateSpec = options.template;
|
|
629
|
+
this._onTemplateBuildProgress = options.onTemplateBuildProgress;
|
|
627
630
|
this._image = options.image ?? "node:22-slim";
|
|
628
631
|
this._workingDirectoryWasSet = options.workingDirectory !== void 0 || options.workingDir !== void 0;
|
|
629
632
|
this._command = options.command ?? ["sleep", "infinity"];
|
|
@@ -983,7 +986,8 @@ var DockerSandbox = class DockerSandbox extends MastraSandbox {
|
|
|
983
986
|
throwIfAborted(abortSignal, "start Docker sandbox");
|
|
984
987
|
const result = await template.build({
|
|
985
988
|
docker: this._docker,
|
|
986
|
-
abortSignal
|
|
989
|
+
abortSignal,
|
|
990
|
+
...this._onTemplateBuildProgress && { onProgress: this._onTemplateBuildProgress }
|
|
987
991
|
});
|
|
988
992
|
if (result.status !== "ready") throw new SandboxError(`Docker template build failed: ${result.error ?? "unknown error"}`, "START_FAILED", {
|
|
989
993
|
templateId: result.templateId,
|
|
@@ -1336,6 +1340,287 @@ const dockerSandboxProvider = {
|
|
|
1336
1340
|
createSandbox: (config) => new DockerSandbox(config)
|
|
1337
1341
|
};
|
|
1338
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
|
|
1339
1624
|
//#region src/template/build-session.ts
|
|
1340
1625
|
/**
|
|
1341
1626
|
* BuildKit session that serves build secrets to the daemon.
|
|
@@ -1872,20 +2157,29 @@ var DockerTemplate = class DockerTemplate {
|
|
|
1872
2157
|
* `options.secrets`, the template's `secrets` option, or `process.env`.
|
|
1873
2158
|
*/
|
|
1874
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");
|
|
1875
2162
|
throwIfAborted(options.abortSignal, "build Docker template");
|
|
1876
2163
|
const isPlain = !options.force && options.secrets === void 0 && options.docker === void 0;
|
|
1877
|
-
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);
|
|
1878
2165
|
const previous = this.#queueTail?.promise.catch(() => void 0) ?? Promise.resolve();
|
|
1879
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
|
+
};
|
|
1880
2173
|
const run = {
|
|
1881
2174
|
controller,
|
|
1882
2175
|
waiters: 0,
|
|
2176
|
+
listeners,
|
|
1883
2177
|
promise: previous.then(() => {
|
|
1884
2178
|
throwIfAborted(controller.signal, "build Docker template");
|
|
1885
2179
|
return this.#build({
|
|
1886
2180
|
...options,
|
|
1887
2181
|
abortSignal: controller.signal
|
|
1888
|
-
});
|
|
2182
|
+
}, emit, maxOutputBytes);
|
|
1889
2183
|
})
|
|
1890
2184
|
};
|
|
1891
2185
|
this.#queueTail = run;
|
|
@@ -1894,20 +2188,23 @@ var DockerTemplate = class DockerTemplate {
|
|
|
1894
2188
|
if (this.#queueTail === run) this.#queueTail = void 0;
|
|
1895
2189
|
if (this.#plainBuild === run) this.#plainBuild = void 0;
|
|
1896
2190
|
}).catch(() => void 0);
|
|
1897
|
-
return this.#waitForBuild(run, options.abortSignal);
|
|
2191
|
+
return this.#waitForBuild(run, options.abortSignal, options.onProgress);
|
|
1898
2192
|
}
|
|
1899
|
-
#waitForBuild(run, abortSignal) {
|
|
2193
|
+
#waitForBuild(run, abortSignal, onProgress) {
|
|
1900
2194
|
run.waiters += 1;
|
|
2195
|
+
const listener = onProgress && ((event) => onProgress(event));
|
|
2196
|
+
if (listener) run.listeners.add(listener);
|
|
1901
2197
|
let settled = false;
|
|
1902
2198
|
const release = (aborted) => {
|
|
1903
2199
|
if (settled) return;
|
|
1904
2200
|
settled = true;
|
|
2201
|
+
if (listener) run.listeners.delete(listener);
|
|
1905
2202
|
run.waiters -= 1;
|
|
1906
2203
|
if (aborted && run.waiters === 0) run.controller.abort();
|
|
1907
2204
|
};
|
|
1908
2205
|
return waitForAbortable(run.promise, abortSignal, "build Docker template", () => release(true)).finally(() => release(false));
|
|
1909
2206
|
}
|
|
1910
|
-
async #build(options) {
|
|
2207
|
+
async #build(options, emit, maxOutputBytes) {
|
|
1911
2208
|
const docker = options.docker ?? this.#getDocker();
|
|
1912
2209
|
const tag = this.templateId;
|
|
1913
2210
|
const { abortSignal } = options;
|
|
@@ -1916,6 +2213,10 @@ var DockerTemplate = class DockerTemplate {
|
|
|
1916
2213
|
await docker.getImage(tag).inspect();
|
|
1917
2214
|
throwIfAborted(abortSignal, "build Docker template");
|
|
1918
2215
|
this.#built = true;
|
|
2216
|
+
emit({
|
|
2217
|
+
type: "reused",
|
|
2218
|
+
templateId: tag
|
|
2219
|
+
});
|
|
1919
2220
|
return {
|
|
1920
2221
|
status: "ready",
|
|
1921
2222
|
templateId: tag
|
|
@@ -1928,12 +2229,19 @@ var DockerTemplate = class DockerTemplate {
|
|
|
1928
2229
|
const context = pack();
|
|
1929
2230
|
context.entry({ name: "Dockerfile" }, this.dockerfile);
|
|
1930
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
|
+
});
|
|
1931
2239
|
try {
|
|
1932
2240
|
const nocache = options.force === true;
|
|
1933
2241
|
if (secrets) {
|
|
1934
2242
|
const { stream, session } = await this.#buildWithSecrets(docker, context, tag, secrets, nocache, abortSignal);
|
|
1935
2243
|
try {
|
|
1936
|
-
await this.#followBuild(docker, stream, abortSignal);
|
|
2244
|
+
await this.#followBuild(docker, stream, normalizer, abortSignal);
|
|
1937
2245
|
} finally {
|
|
1938
2246
|
session.close();
|
|
1939
2247
|
}
|
|
@@ -1951,18 +2259,31 @@ var DockerTemplate = class DockerTemplate {
|
|
|
1951
2259
|
}), abortSignal, "build Docker template", () => {
|
|
1952
2260
|
context.destroy?.();
|
|
1953
2261
|
});
|
|
1954
|
-
await this.#followBuild(docker, stream, abortSignal);
|
|
2262
|
+
await this.#followBuild(docker, stream, normalizer, abortSignal);
|
|
1955
2263
|
}
|
|
1956
2264
|
} catch (error) {
|
|
1957
2265
|
this.#built = false;
|
|
1958
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;
|
|
1959
2272
|
return {
|
|
1960
2273
|
status: "failed",
|
|
1961
2274
|
templateId: tag,
|
|
1962
|
-
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
|
|
1963
2279
|
};
|
|
1964
2280
|
}
|
|
2281
|
+
normalizer.finish();
|
|
1965
2282
|
this.#built = true;
|
|
2283
|
+
emit({
|
|
2284
|
+
type: "build-complete",
|
|
2285
|
+
status: "ready"
|
|
2286
|
+
});
|
|
1966
2287
|
return {
|
|
1967
2288
|
status: "ready",
|
|
1968
2289
|
templateId: tag
|
|
@@ -2040,7 +2361,7 @@ var DockerTemplate = class DockerTemplate {
|
|
|
2040
2361
|
session
|
|
2041
2362
|
};
|
|
2042
2363
|
}
|
|
2043
|
-
#followBuild(docker, stream, abortSignal) {
|
|
2364
|
+
#followBuild(docker, stream, normalizer, abortSignal) {
|
|
2044
2365
|
return new Promise((resolve, reject) => {
|
|
2045
2366
|
let settled = false;
|
|
2046
2367
|
const finish = (error) => {
|
|
@@ -2063,6 +2384,11 @@ var DockerTemplate = class DockerTemplate {
|
|
|
2063
2384
|
finish(err);
|
|
2064
2385
|
return;
|
|
2065
2386
|
}
|
|
2387
|
+
const reported = normalizer.failure();
|
|
2388
|
+
if (reported) {
|
|
2389
|
+
finish(new Error(reported.message));
|
|
2390
|
+
return;
|
|
2391
|
+
}
|
|
2066
2392
|
const failure = output?.find((entry) => entry && (entry.error !== void 0 || entry.errorDetail !== void 0));
|
|
2067
2393
|
if (failure) {
|
|
2068
2394
|
const detail = failure.errorDetail;
|
|
@@ -2070,6 +2396,8 @@ var DockerTemplate = class DockerTemplate {
|
|
|
2070
2396
|
return;
|
|
2071
2397
|
}
|
|
2072
2398
|
finish();
|
|
2399
|
+
}, (event) => {
|
|
2400
|
+
if (!settled) normalizer.handle(event);
|
|
2073
2401
|
});
|
|
2074
2402
|
});
|
|
2075
2403
|
}
|