@mastra/docker 0.9.0-alpha.1 → 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 +194 -35
- 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 +194 -35
- 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/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 +3 -3
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
|
|
@@ -544,7 +576,9 @@ var DockerSandbox = class DockerSandbox extends MastraSandbox {
|
|
|
544
576
|
if (!this._container) throw new SandboxNotReadyError(this.id);
|
|
545
577
|
return this._container;
|
|
546
578
|
}
|
|
547
|
-
async start() {
|
|
579
|
+
async start(options = {}) {
|
|
580
|
+
const { abortSignal } = options;
|
|
581
|
+
throwIfAborted(abortSignal, "start Docker sandbox");
|
|
548
582
|
this.logger.debug(`${LOG_PREFIX} Starting sandbox ${this.id}...`);
|
|
549
583
|
const existing = await this._findExistingContainer();
|
|
550
584
|
if (existing) {
|
|
@@ -565,7 +599,7 @@ var DockerSandbox = class DockerSandbox extends MastraSandbox {
|
|
|
565
599
|
return;
|
|
566
600
|
}
|
|
567
601
|
this._warnOnPrivilegedHardeningConflict(this._privileged);
|
|
568
|
-
await this._resolveTemplate();
|
|
602
|
+
await this._resolveTemplate(abortSignal);
|
|
569
603
|
await this._ensureImage();
|
|
570
604
|
const envArray = Object.entries(this._env).map(([k, v]) => `${k}=${v}`);
|
|
571
605
|
const binds = Object.entries(this._volumes).map(([host, container]) => `${host}:${container}`);
|
|
@@ -825,10 +859,20 @@ var DockerSandbox = class DockerSandbox extends MastraSandbox {
|
|
|
825
859
|
* creates a container, so a resolver-form template re-resolves each time.
|
|
826
860
|
* Adopts the template's workdir unless the sandbox was given one explicitly.
|
|
827
861
|
*/
|
|
828
|
-
async _resolveTemplate() {
|
|
862
|
+
async _resolveTemplate(abortSignal) {
|
|
829
863
|
if (!this._templateSpec) return;
|
|
830
|
-
|
|
831
|
-
|
|
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
|
+
});
|
|
832
876
|
if (result.status !== "ready") throw new SandboxError(`Docker template build failed: ${result.error ?? "unknown error"}`, "START_FAILED", {
|
|
833
877
|
templateId: result.templateId,
|
|
834
878
|
reason: "template_build_failed"
|
|
@@ -1201,9 +1245,17 @@ const AUTH_CREDENTIALS_METHOD = "/moby.filesync.v1.Auth/Credentials";
|
|
|
1201
1245
|
* Open a session that answers `GetSecret` from `secrets`. The returned id must
|
|
1202
1246
|
* be sent as the `session` query parameter of the build request.
|
|
1203
1247
|
*/
|
|
1204
|
-
function openBuildSession(docker, secrets) {
|
|
1248
|
+
function openBuildSession(docker, secrets, abortSignal) {
|
|
1249
|
+
throwIfAborted(abortSignal, "build Docker template");
|
|
1205
1250
|
const id = randomUUID();
|
|
1206
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 });
|
|
1207
1259
|
docker.modem.dial({
|
|
1208
1260
|
method: "POST",
|
|
1209
1261
|
path: "/session",
|
|
@@ -1219,8 +1271,14 @@ function openBuildSession(docker, secrets) {
|
|
|
1219
1271
|
500: "server error"
|
|
1220
1272
|
}
|
|
1221
1273
|
}, (err, socket) => {
|
|
1274
|
+
if (settled) {
|
|
1275
|
+
socket?.end?.();
|
|
1276
|
+
return;
|
|
1277
|
+
}
|
|
1278
|
+
settled = true;
|
|
1279
|
+
abortSignal?.removeEventListener("abort", abort);
|
|
1222
1280
|
if (err) {
|
|
1223
|
-
reject(err);
|
|
1281
|
+
reject(abortSignal?.aborted ? createAbortError(abortSignal, "build Docker template") : err);
|
|
1224
1282
|
return;
|
|
1225
1283
|
}
|
|
1226
1284
|
const server = new Server();
|
|
@@ -1541,7 +1599,8 @@ var DockerTemplate = class DockerTemplate {
|
|
|
1541
1599
|
#secrets;
|
|
1542
1600
|
#docker;
|
|
1543
1601
|
#built = false;
|
|
1544
|
-
#
|
|
1602
|
+
#queueTail;
|
|
1603
|
+
#plainBuild;
|
|
1545
1604
|
constructor(options = {}, state) {
|
|
1546
1605
|
this.#baseImage = state ? state.baseImage : validateString(options.baseImage ?? "node:22-slim", "baseImage");
|
|
1547
1606
|
this.#operations = state?.operations ?? [];
|
|
@@ -1688,19 +1747,49 @@ var DockerTemplate = class DockerTemplate {
|
|
|
1688
1747
|
* `options.secrets`, the template's `secrets` option, or `process.env`.
|
|
1689
1748
|
*/
|
|
1690
1749
|
async build(options = {}) {
|
|
1691
|
-
|
|
1692
|
-
const
|
|
1693
|
-
this.#
|
|
1694
|
-
|
|
1695
|
-
|
|
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;
|
|
1696
1771
|
}).catch(() => void 0);
|
|
1697
|
-
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));
|
|
1698
1784
|
}
|
|
1699
1785
|
async #build(options) {
|
|
1700
1786
|
const docker = options.docker ?? this.#getDocker();
|
|
1701
1787
|
const tag = this.templateId;
|
|
1788
|
+
const { abortSignal } = options;
|
|
1789
|
+
throwIfAborted(abortSignal, "build Docker template");
|
|
1702
1790
|
if (!options.force) try {
|
|
1703
1791
|
await docker.getImage(tag).inspect();
|
|
1792
|
+
throwIfAborted(abortSignal, "build Docker template");
|
|
1704
1793
|
this.#built = true;
|
|
1705
1794
|
return {
|
|
1706
1795
|
status: "ready",
|
|
@@ -1710,24 +1799,37 @@ var DockerTemplate = class DockerTemplate {
|
|
|
1710
1799
|
if (!isImageNotFoundError(error)) throw error;
|
|
1711
1800
|
}
|
|
1712
1801
|
const secrets = await this.#resolveSecrets(options.secrets);
|
|
1802
|
+
throwIfAborted(abortSignal, "build Docker template");
|
|
1713
1803
|
const context = pack();
|
|
1714
1804
|
context.entry({ name: "Dockerfile" }, this.dockerfile);
|
|
1715
1805
|
context.finalize();
|
|
1716
1806
|
try {
|
|
1717
1807
|
const nocache = options.force === true;
|
|
1718
1808
|
if (secrets) {
|
|
1719
|
-
const { stream, session } = await this.#buildWithSecrets(docker, context, tag, secrets, nocache);
|
|
1809
|
+
const { stream, session } = await this.#buildWithSecrets(docker, context, tag, secrets, nocache, abortSignal);
|
|
1720
1810
|
try {
|
|
1721
|
-
await this.#followBuild(docker, stream);
|
|
1811
|
+
await this.#followBuild(docker, stream, abortSignal);
|
|
1722
1812
|
} finally {
|
|
1723
1813
|
session.close();
|
|
1724
1814
|
}
|
|
1725
|
-
} else
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
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
|
+
}
|
|
1729
1830
|
} catch (error) {
|
|
1730
1831
|
this.#built = false;
|
|
1832
|
+
if (abortSignal?.aborted) throw normalizeAbortError(error, "build Docker template");
|
|
1731
1833
|
return {
|
|
1732
1834
|
status: "failed",
|
|
1733
1835
|
templateId: tag,
|
|
@@ -1758,12 +1860,35 @@ var DockerTemplate = class DockerTemplate {
|
|
|
1758
1860
|
* directly because `docker.buildImage` replaces any session id with its own
|
|
1759
1861
|
* auth-only session when `version` is `'2'`.
|
|
1760
1862
|
*/
|
|
1761
|
-
async #buildWithSecrets(docker, context, tag, secrets, nocache) {
|
|
1762
|
-
|
|
1863
|
+
async #buildWithSecrets(docker, context, tag, secrets, nocache, abortSignal) {
|
|
1864
|
+
throwIfAborted(abortSignal, "build Docker template");
|
|
1865
|
+
const session = await openBuildSession(docker, secrets, abortSignal);
|
|
1763
1866
|
let stream;
|
|
1764
1867
|
try {
|
|
1765
1868
|
stream = await new Promise((resolve, reject) => {
|
|
1766
|
-
|
|
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({
|
|
1767
1892
|
path: "/build?",
|
|
1768
1893
|
method: "POST",
|
|
1769
1894
|
file: context,
|
|
@@ -1778,7 +1903,7 @@ var DockerTemplate = class DockerTemplate {
|
|
|
1778
1903
|
200: true,
|
|
1779
1904
|
500: "server error"
|
|
1780
1905
|
}
|
|
1781
|
-
}, (err, data) => err
|
|
1906
|
+
}, (err, data) => finish(err ?? void 0, data));
|
|
1782
1907
|
});
|
|
1783
1908
|
} catch (error) {
|
|
1784
1909
|
session.close();
|
|
@@ -1789,20 +1914,36 @@ var DockerTemplate = class DockerTemplate {
|
|
|
1789
1914
|
session
|
|
1790
1915
|
};
|
|
1791
1916
|
}
|
|
1792
|
-
#followBuild(docker, stream) {
|
|
1917
|
+
#followBuild(docker, stream, abortSignal) {
|
|
1793
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
|
+
}
|
|
1794
1935
|
docker.modem.followProgress(stream, (err, output) => {
|
|
1795
1936
|
if (err) {
|
|
1796
|
-
|
|
1937
|
+
finish(err);
|
|
1797
1938
|
return;
|
|
1798
1939
|
}
|
|
1799
1940
|
const failure = output?.find((entry) => entry && (entry.error !== void 0 || entry.errorDetail !== void 0));
|
|
1800
1941
|
if (failure) {
|
|
1801
1942
|
const detail = failure.errorDetail;
|
|
1802
|
-
|
|
1943
|
+
finish(new Error(String(detail?.message ?? failure.error ?? "docker build failed")));
|
|
1803
1944
|
return;
|
|
1804
1945
|
}
|
|
1805
|
-
|
|
1946
|
+
finish();
|
|
1806
1947
|
});
|
|
1807
1948
|
});
|
|
1808
1949
|
}
|
|
@@ -1951,16 +2092,31 @@ function createDockerRepoTemplate(options) {
|
|
|
1951
2092
|
if (options.ref !== void 0 && !REF_PATTERN.test(options.ref)) throw new Error(`Invalid ref '${options.ref}': expected a git ref name`);
|
|
1952
2093
|
const workingDirectory = trimTrailingSlashes(options.workingDirectory ?? DEFAULT_WORKING_DIRECTORY);
|
|
1953
2094
|
if (!workingDirectory.startsWith("/")) throw new Error(`workingDirectory must be an absolute path, got '${options.workingDirectory}'`);
|
|
1954
|
-
return () => resolveRepoTemplate(options, workingDirectory);
|
|
2095
|
+
return (resolveOptions) => resolveRepoTemplate(options, workingDirectory, resolveOptions);
|
|
1955
2096
|
}
|
|
1956
|
-
async function resolveRepoTemplate(options, workingDirectory) {
|
|
1957
|
-
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");
|
|
1958
2107
|
const cloneUrl = access?.cloneUrl;
|
|
1959
2108
|
if (!cloneUrl) throw new Error("Repo template has no clone URL: repository access returned none.");
|
|
1960
2109
|
assertCloneUrl(cloneUrl);
|
|
1961
2110
|
const token = access?.authorization?.token;
|
|
1962
|
-
|
|
1963
|
-
|
|
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");
|
|
1964
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`);
|
|
1965
2121
|
return buildRepoTemplate({
|
|
1966
2122
|
cloneUrl,
|
|
@@ -2009,7 +2165,8 @@ function buildRepoTemplate(inputs) {
|
|
|
2009
2165
|
* yields undefined; the caller decides how to surface it.
|
|
2010
2166
|
* @internal exported for tests.
|
|
2011
2167
|
*/
|
|
2012
|
-
async function resolveHead(cloneUrl, ref, token) {
|
|
2168
|
+
async function resolveHead(cloneUrl, ref, token, abortSignal) {
|
|
2169
|
+
throwIfAborted(abortSignal, "resolve Docker repository template");
|
|
2013
2170
|
if (ref && FULL_SHA_PATTERN.test(ref)) return ref.toLowerCase();
|
|
2014
2171
|
try {
|
|
2015
2172
|
const authEnv = token ? {
|
|
@@ -2024,6 +2181,7 @@ async function resolveHead(cloneUrl, ref, token) {
|
|
|
2024
2181
|
ref ?? "HEAD"
|
|
2025
2182
|
], {
|
|
2026
2183
|
timeout: 1e4,
|
|
2184
|
+
signal: abortSignal,
|
|
2027
2185
|
env: {
|
|
2028
2186
|
...process.env,
|
|
2029
2187
|
...authEnv,
|
|
@@ -2033,7 +2191,8 @@ async function resolveHead(cloneUrl, ref, token) {
|
|
|
2033
2191
|
const lines = stdout.split("\n").filter(Boolean).map((line) => line.split(" "));
|
|
2034
2192
|
const sha = (lines.find(([, name]) => name?.endsWith("^{}")) ?? lines[0])?.[0]?.trim();
|
|
2035
2193
|
return sha && SHA_PATTERN.test(sha) ? sha.toLowerCase() : void 0;
|
|
2036
|
-
} catch {
|
|
2194
|
+
} catch (error) {
|
|
2195
|
+
if (abortSignal?.aborted) throw normalizeAbortError(error, "resolve Docker repository template");
|
|
2037
2196
|
return;
|
|
2038
2197
|
}
|
|
2039
2198
|
}
|