@mastra/docker 0.9.0-alpha.0 → 0.9.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 +8 -0
- package/dist/abort.d.ts +7 -0
- package/dist/abort.d.ts.map +1 -0
- package/dist/index.cjs +211 -47
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +211 -47
- package/dist/index.js.map +1 -1
- package/dist/sandbox/index.d.ts +8 -3
- package/dist/sandbox/index.d.ts.map +1 -1
- package/dist/sandbox/kill-helper-exit.fixture.d.ts +2 -0
- package/dist/sandbox/kill-helper-exit.fixture.d.ts.map +1 -0
- package/dist/sandbox/process-manager.d.ts.map +1 -1
- package/dist/template/build-session.d.ts +1 -1
- package/dist/template/build-session.d.ts.map +1 -1
- package/dist/template/index.d.ts +1 -1
- package/dist/template/index.d.ts.map +1 -1
- package/dist/template/repo-template.d.ts +8 -4
- package/dist/template/repo-template.d.ts.map +1 -1
- package/dist/template/template.d.ts +2 -0
- package/dist/template/template.d.ts.map +1 -1
- package/package.json +4 -4
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { DockerSandbox, type DockerSandboxOptions, type DockerTemplateSpec } from './sandbox/index.js';
|
|
1
|
+
export { DockerSandbox, type DockerSandboxOptions, type DockerSandboxStartOptions, type DockerTemplateSpec, } from './sandbox/index.js';
|
|
2
2
|
export { DockerProcessManager } from './sandbox/process-manager.js';
|
|
3
3
|
export { dockerSandboxProvider } from './provider.js';
|
|
4
4
|
export * from './template/index.js';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,aAAa,EACb,KAAK,oBAAoB,EACzB,KAAK,yBAAyB,EAC9B,KAAK,kBAAkB,GACxB,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AACjE,OAAO,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AACnD,cAAc,YAAY,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -7,6 +7,38 @@ import { createHash, randomUUID } from "crypto";
|
|
|
7
7
|
import posixPath from "path/posix";
|
|
8
8
|
import { Server, ServerCredentials } from "@grpc/grpc-js";
|
|
9
9
|
import { execFile } from "child_process";
|
|
10
|
+
//#region src/abort.ts
|
|
11
|
+
function createAbortError(signal, operation) {
|
|
12
|
+
return new SandboxAbortError(operation, signal.reason);
|
|
13
|
+
}
|
|
14
|
+
function throwIfAborted(signal, operation) {
|
|
15
|
+
if (signal?.aborted) throw createAbortError(signal, operation);
|
|
16
|
+
}
|
|
17
|
+
function isAbortError(error) {
|
|
18
|
+
return error instanceof SandboxAbortError || error instanceof Error && (error.name === "AbortError" || "code" in error && error.code === "ABORT_ERR");
|
|
19
|
+
}
|
|
20
|
+
function normalizeAbortError(error, operation) {
|
|
21
|
+
return error instanceof SandboxAbortError ? error : isAbortError(error) ? new SandboxAbortError(operation, error) : error;
|
|
22
|
+
}
|
|
23
|
+
function waitForAbortable(promise, signal, operation, onAbort) {
|
|
24
|
+
throwIfAborted(signal, operation);
|
|
25
|
+
if (!signal) return promise;
|
|
26
|
+
return new Promise((resolve, reject) => {
|
|
27
|
+
const abort = () => {
|
|
28
|
+
onAbort?.();
|
|
29
|
+
reject(new SandboxAbortError(operation, signal.reason));
|
|
30
|
+
};
|
|
31
|
+
signal.addEventListener("abort", abort, { once: true });
|
|
32
|
+
promise.then((value) => {
|
|
33
|
+
signal.removeEventListener("abort", abort);
|
|
34
|
+
resolve(value);
|
|
35
|
+
}, (error) => {
|
|
36
|
+
signal.removeEventListener("abort", abort);
|
|
37
|
+
reject(normalizeAbortError(error, operation));
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
//#endregion
|
|
10
42
|
//#region src/sandbox/process-manager.ts
|
|
11
43
|
/**
|
|
12
44
|
* Docker Process Manager
|
|
@@ -179,16 +211,20 @@ var DockerProcessHandle = class extends ProcessHandle {
|
|
|
179
211
|
AttachStdout: false,
|
|
180
212
|
AttachStderr: false
|
|
181
213
|
});
|
|
182
|
-
await killExec.start({});
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
214
|
+
const killStream = await killExec.start({});
|
|
215
|
+
try {
|
|
216
|
+
let killInfo = await killExec.inspect();
|
|
217
|
+
while (killInfo.Running) {
|
|
218
|
+
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
219
|
+
killInfo = await killExec.inspect();
|
|
220
|
+
}
|
|
221
|
+
if (killInfo.ExitCode !== 0) throw new Error(`kill helper exited with code ${killInfo.ExitCode}`);
|
|
222
|
+
this._killed = true;
|
|
223
|
+
this._destroyStream();
|
|
224
|
+
return true;
|
|
225
|
+
} finally {
|
|
226
|
+
killStream.destroy();
|
|
187
227
|
}
|
|
188
|
-
if (killInfo.ExitCode !== 0) throw new Error(`kill helper exited with code ${killInfo.ExitCode}`);
|
|
189
|
-
this._killed = true;
|
|
190
|
-
this._destroyStream();
|
|
191
|
-
return true;
|
|
192
228
|
} catch (error) {
|
|
193
229
|
const msg = error instanceof Error ? error.message.toLowerCase() : "";
|
|
194
230
|
if (!msg.includes("no such process") && !msg.includes("esrch")) console.warn(`[DockerProcessManager] kill(${this.pid}) failed unexpectedly:`, error);
|
|
@@ -245,6 +281,7 @@ var DockerProcessManager = class extends SandboxProcessManager {
|
|
|
245
281
|
const container = this.container;
|
|
246
282
|
const pgidFile = `${PROC_DIR}/${randomUUID()}`;
|
|
247
283
|
const envArray = Object.entries({ ...options.env }).filter((entry) => entry[1] !== void 0).map(([k, v]) => `${k}=${v}`);
|
|
284
|
+
const attachStdin = options.stdinMode !== "ignore";
|
|
248
285
|
const exec = await container.exec({
|
|
249
286
|
Cmd: [
|
|
250
287
|
"sh",
|
|
@@ -256,17 +293,17 @@ var DockerProcessManager = class extends SandboxProcessManager {
|
|
|
256
293
|
],
|
|
257
294
|
AttachStdout: true,
|
|
258
295
|
AttachStderr: true,
|
|
259
|
-
AttachStdin:
|
|
296
|
+
AttachStdin: attachStdin,
|
|
260
297
|
Tty: false,
|
|
261
298
|
Env: envArray.length > 0 ? envArray : void 0,
|
|
262
299
|
WorkingDir: options.cwd
|
|
263
300
|
});
|
|
264
301
|
const stream = await exec.start({
|
|
265
302
|
hijack: true,
|
|
266
|
-
stdin:
|
|
303
|
+
stdin: attachStdin
|
|
267
304
|
});
|
|
268
305
|
const startTime = Date.now();
|
|
269
|
-
const handle = new DockerProcessHandle(exec, container, startTime, stream, pgidFile, options);
|
|
306
|
+
const handle = new DockerProcessHandle(exec, container, startTime, attachStdin ? stream : null, pgidFile, options);
|
|
270
307
|
handle._setExecStream(stream);
|
|
271
308
|
const waitPromise = new Promise((resolve) => {
|
|
272
309
|
const buffer = [];
|
|
@@ -539,7 +576,9 @@ var DockerSandbox = class DockerSandbox extends MastraSandbox {
|
|
|
539
576
|
if (!this._container) throw new SandboxNotReadyError(this.id);
|
|
540
577
|
return this._container;
|
|
541
578
|
}
|
|
542
|
-
async start() {
|
|
579
|
+
async start(options = {}) {
|
|
580
|
+
const { abortSignal } = options;
|
|
581
|
+
throwIfAborted(abortSignal, "start Docker sandbox");
|
|
543
582
|
this.logger.debug(`${LOG_PREFIX} Starting sandbox ${this.id}...`);
|
|
544
583
|
const existing = await this._findExistingContainer();
|
|
545
584
|
if (existing) {
|
|
@@ -560,7 +599,7 @@ var DockerSandbox = class DockerSandbox extends MastraSandbox {
|
|
|
560
599
|
return;
|
|
561
600
|
}
|
|
562
601
|
this._warnOnPrivilegedHardeningConflict(this._privileged);
|
|
563
|
-
await this._resolveTemplate();
|
|
602
|
+
await this._resolveTemplate(abortSignal);
|
|
564
603
|
await this._ensureImage();
|
|
565
604
|
const envArray = Object.entries(this._env).map(([k, v]) => `${k}=${v}`);
|
|
566
605
|
const binds = Object.entries(this._volumes).map(([host, container]) => `${host}:${container}`);
|
|
@@ -820,10 +859,20 @@ var DockerSandbox = class DockerSandbox extends MastraSandbox {
|
|
|
820
859
|
* creates a container, so a resolver-form template re-resolves each time.
|
|
821
860
|
* Adopts the template's workdir unless the sandbox was given one explicitly.
|
|
822
861
|
*/
|
|
823
|
-
async _resolveTemplate() {
|
|
862
|
+
async _resolveTemplate(abortSignal) {
|
|
824
863
|
if (!this._templateSpec) return;
|
|
825
|
-
|
|
826
|
-
|
|
864
|
+
throwIfAborted(abortSignal, "start Docker sandbox");
|
|
865
|
+
let template;
|
|
866
|
+
try {
|
|
867
|
+
template = typeof this._templateSpec === "function" ? await this._templateSpec({ abortSignal }) : this._templateSpec;
|
|
868
|
+
} catch (error) {
|
|
869
|
+
throw normalizeAbortError(error, "start Docker sandbox");
|
|
870
|
+
}
|
|
871
|
+
throwIfAborted(abortSignal, "start Docker sandbox");
|
|
872
|
+
const result = await template.build({
|
|
873
|
+
docker: this._docker,
|
|
874
|
+
abortSignal
|
|
875
|
+
});
|
|
827
876
|
if (result.status !== "ready") throw new SandboxError(`Docker template build failed: ${result.error ?? "unknown error"}`, "START_FAILED", {
|
|
828
877
|
templateId: result.templateId,
|
|
829
878
|
reason: "template_build_failed"
|
|
@@ -1196,9 +1245,17 @@ const AUTH_CREDENTIALS_METHOD = "/moby.filesync.v1.Auth/Credentials";
|
|
|
1196
1245
|
* Open a session that answers `GetSecret` from `secrets`. The returned id must
|
|
1197
1246
|
* be sent as the `session` query parameter of the build request.
|
|
1198
1247
|
*/
|
|
1199
|
-
function openBuildSession(docker, secrets) {
|
|
1248
|
+
function openBuildSession(docker, secrets, abortSignal) {
|
|
1249
|
+
throwIfAborted(abortSignal, "build Docker template");
|
|
1200
1250
|
const id = randomUUID();
|
|
1201
1251
|
return new Promise((resolve, reject) => {
|
|
1252
|
+
let settled = false;
|
|
1253
|
+
const abort = () => {
|
|
1254
|
+
if (settled || !abortSignal) return;
|
|
1255
|
+
settled = true;
|
|
1256
|
+
reject(createAbortError(abortSignal, "build Docker template"));
|
|
1257
|
+
};
|
|
1258
|
+
abortSignal?.addEventListener("abort", abort, { once: true });
|
|
1202
1259
|
docker.modem.dial({
|
|
1203
1260
|
method: "POST",
|
|
1204
1261
|
path: "/session",
|
|
@@ -1214,8 +1271,14 @@ function openBuildSession(docker, secrets) {
|
|
|
1214
1271
|
500: "server error"
|
|
1215
1272
|
}
|
|
1216
1273
|
}, (err, socket) => {
|
|
1274
|
+
if (settled) {
|
|
1275
|
+
socket?.end?.();
|
|
1276
|
+
return;
|
|
1277
|
+
}
|
|
1278
|
+
settled = true;
|
|
1279
|
+
abortSignal?.removeEventListener("abort", abort);
|
|
1217
1280
|
if (err) {
|
|
1218
|
-
reject(err);
|
|
1281
|
+
reject(abortSignal?.aborted ? createAbortError(abortSignal, "build Docker template") : err);
|
|
1219
1282
|
return;
|
|
1220
1283
|
}
|
|
1221
1284
|
const server = new Server();
|
|
@@ -1536,7 +1599,8 @@ var DockerTemplate = class DockerTemplate {
|
|
|
1536
1599
|
#secrets;
|
|
1537
1600
|
#docker;
|
|
1538
1601
|
#built = false;
|
|
1539
|
-
#
|
|
1602
|
+
#queueTail;
|
|
1603
|
+
#plainBuild;
|
|
1540
1604
|
constructor(options = {}, state) {
|
|
1541
1605
|
this.#baseImage = state ? state.baseImage : validateString(options.baseImage ?? "node:22-slim", "baseImage");
|
|
1542
1606
|
this.#operations = state?.operations ?? [];
|
|
@@ -1683,19 +1747,49 @@ var DockerTemplate = class DockerTemplate {
|
|
|
1683
1747
|
* `options.secrets`, the template's `secrets` option, or `process.env`.
|
|
1684
1748
|
*/
|
|
1685
1749
|
async build(options = {}) {
|
|
1686
|
-
|
|
1687
|
-
const
|
|
1688
|
-
this.#
|
|
1689
|
-
|
|
1690
|
-
|
|
1750
|
+
throwIfAborted(options.abortSignal, "build Docker template");
|
|
1751
|
+
const isPlain = !options.force && options.secrets === void 0 && options.docker === void 0;
|
|
1752
|
+
if (isPlain && this.#plainBuild) return this.#waitForBuild(this.#plainBuild, options.abortSignal);
|
|
1753
|
+
const previous = this.#queueTail?.promise.catch(() => void 0) ?? Promise.resolve();
|
|
1754
|
+
const controller = new AbortController();
|
|
1755
|
+
const run = {
|
|
1756
|
+
controller,
|
|
1757
|
+
waiters: 0,
|
|
1758
|
+
promise: previous.then(() => {
|
|
1759
|
+
throwIfAborted(controller.signal, "build Docker template");
|
|
1760
|
+
return this.#build({
|
|
1761
|
+
...options,
|
|
1762
|
+
abortSignal: controller.signal
|
|
1763
|
+
});
|
|
1764
|
+
})
|
|
1765
|
+
};
|
|
1766
|
+
this.#queueTail = run;
|
|
1767
|
+
if (isPlain) this.#plainBuild = run;
|
|
1768
|
+
run.promise.finally(() => {
|
|
1769
|
+
if (this.#queueTail === run) this.#queueTail = void 0;
|
|
1770
|
+
if (this.#plainBuild === run) this.#plainBuild = void 0;
|
|
1691
1771
|
}).catch(() => void 0);
|
|
1692
|
-
return run;
|
|
1772
|
+
return this.#waitForBuild(run, options.abortSignal);
|
|
1773
|
+
}
|
|
1774
|
+
#waitForBuild(run, abortSignal) {
|
|
1775
|
+
run.waiters += 1;
|
|
1776
|
+
let settled = false;
|
|
1777
|
+
const release = (aborted) => {
|
|
1778
|
+
if (settled) return;
|
|
1779
|
+
settled = true;
|
|
1780
|
+
run.waiters -= 1;
|
|
1781
|
+
if (aborted && run.waiters === 0) run.controller.abort();
|
|
1782
|
+
};
|
|
1783
|
+
return waitForAbortable(run.promise, abortSignal, "build Docker template", () => release(true)).finally(() => release(false));
|
|
1693
1784
|
}
|
|
1694
1785
|
async #build(options) {
|
|
1695
1786
|
const docker = options.docker ?? this.#getDocker();
|
|
1696
1787
|
const tag = this.templateId;
|
|
1788
|
+
const { abortSignal } = options;
|
|
1789
|
+
throwIfAborted(abortSignal, "build Docker template");
|
|
1697
1790
|
if (!options.force) try {
|
|
1698
1791
|
await docker.getImage(tag).inspect();
|
|
1792
|
+
throwIfAborted(abortSignal, "build Docker template");
|
|
1699
1793
|
this.#built = true;
|
|
1700
1794
|
return {
|
|
1701
1795
|
status: "ready",
|
|
@@ -1705,24 +1799,37 @@ var DockerTemplate = class DockerTemplate {
|
|
|
1705
1799
|
if (!isImageNotFoundError(error)) throw error;
|
|
1706
1800
|
}
|
|
1707
1801
|
const secrets = await this.#resolveSecrets(options.secrets);
|
|
1802
|
+
throwIfAborted(abortSignal, "build Docker template");
|
|
1708
1803
|
const context = pack();
|
|
1709
1804
|
context.entry({ name: "Dockerfile" }, this.dockerfile);
|
|
1710
1805
|
context.finalize();
|
|
1711
1806
|
try {
|
|
1712
1807
|
const nocache = options.force === true;
|
|
1713
1808
|
if (secrets) {
|
|
1714
|
-
const { stream, session } = await this.#buildWithSecrets(docker, context, tag, secrets, nocache);
|
|
1809
|
+
const { stream, session } = await this.#buildWithSecrets(docker, context, tag, secrets, nocache, abortSignal);
|
|
1715
1810
|
try {
|
|
1716
|
-
await this.#followBuild(docker, stream);
|
|
1811
|
+
await this.#followBuild(docker, stream, abortSignal);
|
|
1717
1812
|
} finally {
|
|
1718
1813
|
session.close();
|
|
1719
1814
|
}
|
|
1720
|
-
} else
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1815
|
+
} else {
|
|
1816
|
+
const stream = await waitForAbortable(docker.buildImage(context, {
|
|
1817
|
+
t: tag,
|
|
1818
|
+
nocache
|
|
1819
|
+
}).then((stream) => {
|
|
1820
|
+
if (abortSignal?.aborted) {
|
|
1821
|
+
stream.destroy?.();
|
|
1822
|
+
throw createAbortError(abortSignal, "build Docker template");
|
|
1823
|
+
}
|
|
1824
|
+
return stream;
|
|
1825
|
+
}), abortSignal, "build Docker template", () => {
|
|
1826
|
+
context.destroy?.();
|
|
1827
|
+
});
|
|
1828
|
+
await this.#followBuild(docker, stream, abortSignal);
|
|
1829
|
+
}
|
|
1724
1830
|
} catch (error) {
|
|
1725
1831
|
this.#built = false;
|
|
1832
|
+
if (abortSignal?.aborted) throw normalizeAbortError(error, "build Docker template");
|
|
1726
1833
|
return {
|
|
1727
1834
|
status: "failed",
|
|
1728
1835
|
templateId: tag,
|
|
@@ -1753,12 +1860,35 @@ var DockerTemplate = class DockerTemplate {
|
|
|
1753
1860
|
* directly because `docker.buildImage` replaces any session id with its own
|
|
1754
1861
|
* auth-only session when `version` is `'2'`.
|
|
1755
1862
|
*/
|
|
1756
|
-
async #buildWithSecrets(docker, context, tag, secrets, nocache) {
|
|
1757
|
-
|
|
1863
|
+
async #buildWithSecrets(docker, context, tag, secrets, nocache, abortSignal) {
|
|
1864
|
+
throwIfAborted(abortSignal, "build Docker template");
|
|
1865
|
+
const session = await openBuildSession(docker, secrets, abortSignal);
|
|
1758
1866
|
let stream;
|
|
1759
1867
|
try {
|
|
1760
1868
|
stream = await new Promise((resolve, reject) => {
|
|
1761
|
-
|
|
1869
|
+
let settled = false;
|
|
1870
|
+
let request;
|
|
1871
|
+
const finish = (error, data) => {
|
|
1872
|
+
if (settled) {
|
|
1873
|
+
if (data) data.destroy?.();
|
|
1874
|
+
return;
|
|
1875
|
+
}
|
|
1876
|
+
settled = true;
|
|
1877
|
+
abortSignal?.removeEventListener("abort", abort);
|
|
1878
|
+
error === void 0 ? resolve(data) : reject(error);
|
|
1879
|
+
};
|
|
1880
|
+
const abort = () => {
|
|
1881
|
+
context.destroy?.();
|
|
1882
|
+
request?.destroy?.();
|
|
1883
|
+
session.close();
|
|
1884
|
+
if (abortSignal) finish(createAbortError(abortSignal, "build Docker template"));
|
|
1885
|
+
};
|
|
1886
|
+
abortSignal?.addEventListener("abort", abort, { once: true });
|
|
1887
|
+
if (abortSignal?.aborted) {
|
|
1888
|
+
abort();
|
|
1889
|
+
return;
|
|
1890
|
+
}
|
|
1891
|
+
request = docker.modem.dial({
|
|
1762
1892
|
path: "/build?",
|
|
1763
1893
|
method: "POST",
|
|
1764
1894
|
file: context,
|
|
@@ -1773,7 +1903,7 @@ var DockerTemplate = class DockerTemplate {
|
|
|
1773
1903
|
200: true,
|
|
1774
1904
|
500: "server error"
|
|
1775
1905
|
}
|
|
1776
|
-
}, (err, data) => err
|
|
1906
|
+
}, (err, data) => finish(err ?? void 0, data));
|
|
1777
1907
|
});
|
|
1778
1908
|
} catch (error) {
|
|
1779
1909
|
session.close();
|
|
@@ -1784,20 +1914,36 @@ var DockerTemplate = class DockerTemplate {
|
|
|
1784
1914
|
session
|
|
1785
1915
|
};
|
|
1786
1916
|
}
|
|
1787
|
-
#followBuild(docker, stream) {
|
|
1917
|
+
#followBuild(docker, stream, abortSignal) {
|
|
1788
1918
|
return new Promise((resolve, reject) => {
|
|
1919
|
+
let settled = false;
|
|
1920
|
+
const finish = (error) => {
|
|
1921
|
+
if (settled) return;
|
|
1922
|
+
settled = true;
|
|
1923
|
+
abortSignal?.removeEventListener("abort", abort);
|
|
1924
|
+
error === void 0 ? resolve() : reject(error);
|
|
1925
|
+
};
|
|
1926
|
+
const abort = () => {
|
|
1927
|
+
stream.destroy?.();
|
|
1928
|
+
if (abortSignal) finish(createAbortError(abortSignal, "build Docker template"));
|
|
1929
|
+
};
|
|
1930
|
+
abortSignal?.addEventListener("abort", abort, { once: true });
|
|
1931
|
+
if (abortSignal?.aborted) {
|
|
1932
|
+
abort();
|
|
1933
|
+
return;
|
|
1934
|
+
}
|
|
1789
1935
|
docker.modem.followProgress(stream, (err, output) => {
|
|
1790
1936
|
if (err) {
|
|
1791
|
-
|
|
1937
|
+
finish(err);
|
|
1792
1938
|
return;
|
|
1793
1939
|
}
|
|
1794
1940
|
const failure = output?.find((entry) => entry && (entry.error !== void 0 || entry.errorDetail !== void 0));
|
|
1795
1941
|
if (failure) {
|
|
1796
1942
|
const detail = failure.errorDetail;
|
|
1797
|
-
|
|
1943
|
+
finish(new Error(String(detail?.message ?? failure.error ?? "docker build failed")));
|
|
1798
1944
|
return;
|
|
1799
1945
|
}
|
|
1800
|
-
|
|
1946
|
+
finish();
|
|
1801
1947
|
});
|
|
1802
1948
|
});
|
|
1803
1949
|
}
|
|
@@ -1946,16 +2092,31 @@ function createDockerRepoTemplate(options) {
|
|
|
1946
2092
|
if (options.ref !== void 0 && !REF_PATTERN.test(options.ref)) throw new Error(`Invalid ref '${options.ref}': expected a git ref name`);
|
|
1947
2093
|
const workingDirectory = trimTrailingSlashes(options.workingDirectory ?? DEFAULT_WORKING_DIRECTORY);
|
|
1948
2094
|
if (!workingDirectory.startsWith("/")) throw new Error(`workingDirectory must be an absolute path, got '${options.workingDirectory}'`);
|
|
1949
|
-
return () => resolveRepoTemplate(options, workingDirectory);
|
|
2095
|
+
return (resolveOptions) => resolveRepoTemplate(options, workingDirectory, resolveOptions);
|
|
1950
2096
|
}
|
|
1951
|
-
async function resolveRepoTemplate(options, workingDirectory) {
|
|
1952
|
-
const
|
|
2097
|
+
async function resolveRepoTemplate(options, workingDirectory, resolveOptions = {}) {
|
|
2098
|
+
const { abortSignal } = resolveOptions;
|
|
2099
|
+
throwIfAborted(abortSignal, "resolve Docker repository template");
|
|
2100
|
+
let access;
|
|
2101
|
+
try {
|
|
2102
|
+
access = await options.getRepositoryAccess(resolveOptions);
|
|
2103
|
+
} catch (error) {
|
|
2104
|
+
throw normalizeAbortError(error, "resolve Docker repository template");
|
|
2105
|
+
}
|
|
2106
|
+
throwIfAborted(abortSignal, "resolve Docker repository template");
|
|
1953
2107
|
const cloneUrl = access?.cloneUrl;
|
|
1954
2108
|
if (!cloneUrl) throw new Error("Repo template has no clone URL: repository access returned none.");
|
|
1955
2109
|
assertCloneUrl(cloneUrl);
|
|
1956
2110
|
const token = access?.authorization?.token;
|
|
1957
|
-
|
|
1958
|
-
|
|
2111
|
+
let buildEnv;
|
|
2112
|
+
try {
|
|
2113
|
+
buildEnv = typeof options.buildEnv === "function" ? await options.buildEnv(resolveOptions) : options.buildEnv;
|
|
2114
|
+
} catch (error) {
|
|
2115
|
+
throw normalizeAbortError(error, "resolve Docker repository template");
|
|
2116
|
+
}
|
|
2117
|
+
throwIfAborted(abortSignal, "resolve Docker repository template");
|
|
2118
|
+
const sha = await resolveHead(cloneUrl, options.ref, token, abortSignal);
|
|
2119
|
+
throwIfAborted(abortSignal, "resolve Docker repository template");
|
|
1959
2120
|
if (!sha) throw new Error(`Could not resolve ${options.ref ?? "HEAD"} of ${cloneUrl} with git ls-remote; check the ref, the credential and network access`);
|
|
1960
2121
|
return buildRepoTemplate({
|
|
1961
2122
|
cloneUrl,
|
|
@@ -2004,7 +2165,8 @@ function buildRepoTemplate(inputs) {
|
|
|
2004
2165
|
* yields undefined; the caller decides how to surface it.
|
|
2005
2166
|
* @internal exported for tests.
|
|
2006
2167
|
*/
|
|
2007
|
-
async function resolveHead(cloneUrl, ref, token) {
|
|
2168
|
+
async function resolveHead(cloneUrl, ref, token, abortSignal) {
|
|
2169
|
+
throwIfAborted(abortSignal, "resolve Docker repository template");
|
|
2008
2170
|
if (ref && FULL_SHA_PATTERN.test(ref)) return ref.toLowerCase();
|
|
2009
2171
|
try {
|
|
2010
2172
|
const authEnv = token ? {
|
|
@@ -2019,6 +2181,7 @@ async function resolveHead(cloneUrl, ref, token) {
|
|
|
2019
2181
|
ref ?? "HEAD"
|
|
2020
2182
|
], {
|
|
2021
2183
|
timeout: 1e4,
|
|
2184
|
+
signal: abortSignal,
|
|
2022
2185
|
env: {
|
|
2023
2186
|
...process.env,
|
|
2024
2187
|
...authEnv,
|
|
@@ -2028,7 +2191,8 @@ async function resolveHead(cloneUrl, ref, token) {
|
|
|
2028
2191
|
const lines = stdout.split("\n").filter(Boolean).map((line) => line.split(" "));
|
|
2029
2192
|
const sha = (lines.find(([, name]) => name?.endsWith("^{}")) ?? lines[0])?.[0]?.trim();
|
|
2030
2193
|
return sha && SHA_PATTERN.test(sha) ? sha.toLowerCase() : void 0;
|
|
2031
|
-
} catch {
|
|
2194
|
+
} catch (error) {
|
|
2195
|
+
if (abortSignal?.aborted) throw normalizeAbortError(error, "resolve Docker repository template");
|
|
2032
2196
|
return;
|
|
2033
2197
|
}
|
|
2034
2198
|
}
|