openship 0.7.0 → 0.7.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/dist/bare-AB4SMTNJ.js +29 -0
- package/dist/{chunk-J2NDOB7D.js → chunk-2BAKEGGF.js} +1 -1
- package/dist/{chunk-JAKVPLHT.js → chunk-2YAY5XHK.js} +1 -1
- package/dist/{chunk-YGA52DHX.js → chunk-37XZTLBJ.js} +3 -3
- package/dist/{chunk-GYDROM4W.js → chunk-3K65MMYV.js} +1 -1
- package/dist/{chunk-XQC6KYZD.js → chunk-3WBKLDGC.js} +3 -3
- package/dist/{chunk-5CFUYMPE.js → chunk-3YSSXSC2.js} +12 -12
- package/dist/{chunk-CMGRMPRQ.js → chunk-7TQP6723.js} +2 -2
- package/dist/{chunk-MZYD3LFP.js → chunk-AMADTKC6.js} +1 -1
- package/dist/{chunk-JWW5YAFL.js → chunk-B2BQX6JX.js} +3 -3
- package/dist/{chunk-DZBXOF5Q.js → chunk-BBPKAQ54.js} +6 -6
- package/dist/{chunk-IVRTIKNZ.js → chunk-COI4VFRW.js} +34 -2
- package/dist/{chunk-ZFA3MZ5N.js → chunk-CQ2JOXEQ.js} +3 -3
- package/dist/{chunk-BOD2IQ3P.js → chunk-DQR5TPFB.js} +5 -5
- package/dist/{chunk-AUBB5DLY.js → chunk-ETK6IBCH.js} +6 -6
- package/dist/{chunk-TPGBT5UR.js → chunk-EXXKRORU.js} +637 -97
- package/dist/{chunk-RHHCAWW3.js → chunk-FNWNUYS3.js} +3 -3
- package/dist/{chunk-3KUW2UC6.js → chunk-GPTBEPUE.js} +21 -14
- package/dist/{chunk-SYQ3FOLE.js → chunk-IVMPA6RC.js} +2 -2
- package/dist/{chunk-SPR7N2EK.js → chunk-JJD4YD2O.js} +9 -9
- package/dist/{chunk-3LWGDMJU.js → chunk-KATXZ5AB.js} +1 -1
- package/dist/{chunk-J4SYG2LC.js → chunk-KCMEWNP7.js} +1 -1
- package/dist/{chunk-LL7M7GL4.js → chunk-KQPFPKL4.js} +4 -4
- package/dist/{chunk-N5YZMO33.js → chunk-L4DRH6ZA.js} +12 -1
- package/dist/{chunk-GEHV7JBS.js → chunk-NF7NQOAP.js} +2 -2
- package/dist/{chunk-QNQFS37A.js → chunk-S5GU345P.js} +3 -3
- package/dist/{chunk-HBQWOJRP.js → chunk-TKK5NPDQ.js} +2 -2
- package/dist/{chunk-KYTTPWRP.js → chunk-V7JLX2QD.js} +9 -9
- package/dist/{chunk-7MKZP4TW.js → chunk-XJSLJLNY.js} +14 -14
- package/dist/{chunk-TT7P5ZZ3.js → chunk-XKLY2GH5.js} +8 -8
- package/dist/{chunk-5D27WZBW.js → chunk-XWZVDLGF.js} +5 -5
- package/dist/{cloud-RFZSO6P5.js → cloud-T2JOFEQL.js} +20 -20
- package/dist/{detect-NXD3PYBM.js → detect-SWLZURYX.js} +5 -5
- package/dist/{docker-U5APUZBI.js → docker-N3OVJUSM.js} +11 -11
- package/dist/{docker-edge-executor-HUQOWFQU.js → docker-edge-executor-BSSQQS76.js} +8 -8
- package/dist/{edge-import-UXD52WQR.js → edge-import-YU5YKFXT.js} +27 -27
- package/dist/{edge-preflight-3PMJ5BWM.js → edge-preflight-RCXSCKM3.js} +14 -14
- package/dist/{ensure-container-edge-GDPY7UJD.js → ensure-container-edge-U7FTLFVJ.js} +12 -12
- package/dist/{executor-XOVSO6QZ.js → executor-GYRZ7RIU.js} +7 -7
- package/dist/host-channel-preflight-U4NF425M.js +54 -0
- package/dist/index.js +45 -45
- package/dist/{local-executor-KR7BFCJP.js → local-executor-JCR46VAP.js} +3 -3
- package/dist/{nginx-JMLAS7YS.js → nginx-YA6IRPJT.js} +10 -10
- package/dist/{openresty-lua-FJH3KEC4.js → openresty-lua-DUOB64TP.js} +8 -8
- package/dist/{privilege-H2QTJ5TE.js → privilege-4B2TMVW2.js} +6 -6
- package/dist/server/index.js +3734 -1005
- package/dist/server/migrations/0122_data_transfer_staging.sql +45 -0
- package/dist/server/migrations/meta/_journal.json +7 -0
- package/dist/setup-T3WN3HRS.js +25 -0
- package/dist/{ssh-client-WVLLOAYK.js → ssh-client-FZDWP5PQ.js} +3 -3
- package/dist/up-Z2CMXZED.js +61 -0
- package/package.json +1 -1
- package/dist/bare-3POKLHTW.js +0 -29
- package/dist/host-channel-preflight-T2QUDQXU.js +0 -54
- package/dist/setup-ERGE5XNW.js +0 -25
- package/dist/up-EFCE6KA6.js +0 -61
|
@@ -9,11 +9,11 @@ import {
|
|
|
9
9
|
prepareSourceTree,
|
|
10
10
|
resolveDockerBuildArgs,
|
|
11
11
|
resolveServiceDockerfile
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-S5GU345P.js";
|
|
13
13
|
import {
|
|
14
14
|
isArtifactPathRef,
|
|
15
15
|
removeManagedArtifact
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-AMADTKC6.js";
|
|
17
17
|
import {
|
|
18
18
|
BuildCancelledError,
|
|
19
19
|
BuildLogger,
|
|
@@ -35,7 +35,7 @@ import {
|
|
|
35
35
|
sq,
|
|
36
36
|
staticBuilderOutputPath,
|
|
37
37
|
transferLocalDirectory
|
|
38
|
-
} from "./chunk-
|
|
38
|
+
} from "./chunk-IVMPA6RC.js";
|
|
39
39
|
import {
|
|
40
40
|
DOCKER_DIAL_STDIO_COMMAND,
|
|
41
41
|
attachDialStdioDiagnostics,
|
|
@@ -44,17 +44,17 @@ import {
|
|
|
44
44
|
execSshCommand,
|
|
45
45
|
openDockerDialStdioChannel,
|
|
46
46
|
openSshUnixSocket
|
|
47
|
-
} from "./chunk-
|
|
47
|
+
} from "./chunk-TKK5NPDQ.js";
|
|
48
48
|
import {
|
|
49
49
|
dirOf,
|
|
50
50
|
ensureOwnedDir
|
|
51
|
-
} from "./chunk-
|
|
51
|
+
} from "./chunk-B2BQX6JX.js";
|
|
52
52
|
import {
|
|
53
53
|
OPENSHIP_LABEL
|
|
54
|
-
} from "./chunk-
|
|
54
|
+
} from "./chunk-7TQP6723.js";
|
|
55
55
|
import {
|
|
56
56
|
isRuntimeNotFoundError
|
|
57
|
-
} from "./chunk-
|
|
57
|
+
} from "./chunk-L4DRH6ZA.js";
|
|
58
58
|
import {
|
|
59
59
|
formatCpuCores,
|
|
60
60
|
formatMemoryMb,
|
|
@@ -65,7 +65,7 @@ import {
|
|
|
65
65
|
registryForImage,
|
|
66
66
|
safeErrorMessage,
|
|
67
67
|
withTimeout
|
|
68
|
-
} from "./chunk-
|
|
68
|
+
} from "./chunk-COI4VFRW.js";
|
|
69
69
|
import {
|
|
70
70
|
__commonJS,
|
|
71
71
|
__require,
|
|
@@ -4627,6 +4627,7 @@ var require_tar_fs = __commonJS({
|
|
|
4627
4627
|
// ../../packages/adapters/src/runtime/docker.ts
|
|
4628
4628
|
var tarFs = __toESM(require_tar_fs(), 1);
|
|
4629
4629
|
import Dockerode from "dockerode";
|
|
4630
|
+
import { randomUUID } from "crypto";
|
|
4630
4631
|
import { createGzip } from "zlib";
|
|
4631
4632
|
import { PassThrough, Writable } from "stream";
|
|
4632
4633
|
import { relative, sep } from "path";
|
|
@@ -5630,6 +5631,247 @@ function resolveDockerTransport(opts) {
|
|
|
5630
5631
|
};
|
|
5631
5632
|
}
|
|
5632
5633
|
|
|
5634
|
+
// ../../packages/adapters/src/runtime/docker-build-diagnostics.ts
|
|
5635
|
+
var DEFAULT_DOCKER_BUILD_IDLE_TIMEOUT_MS = 10 * 6e4;
|
|
5636
|
+
var MIN_DOCKER_BUILD_IDLE_TIMEOUT_MS = 6e4;
|
|
5637
|
+
var MAX_DOCKER_BUILD_IDLE_TIMEOUT_MS = 24 * 60 * 6e4;
|
|
5638
|
+
function getDockerBuildIdleTimeoutMs(raw = process.env.OPENSHIP_BUILD_IDLE_TIMEOUT_MS) {
|
|
5639
|
+
if (raw == null || raw.trim() === "") return DEFAULT_DOCKER_BUILD_IDLE_TIMEOUT_MS;
|
|
5640
|
+
const value = Number(raw);
|
|
5641
|
+
return Number.isSafeInteger(value) && value >= MIN_DOCKER_BUILD_IDLE_TIMEOUT_MS && value <= MAX_DOCKER_BUILD_IDLE_TIMEOUT_MS ? value : DEFAULT_DOCKER_BUILD_IDLE_TIMEOUT_MS;
|
|
5642
|
+
}
|
|
5643
|
+
function memoryGuidance(context) {
|
|
5644
|
+
const memoryMb = context.configuredMemoryMb;
|
|
5645
|
+
if (context.memoryLimitApplied && memoryMb && memoryMb > 0) {
|
|
5646
|
+
return `The classic Docker builder was capped at ${memoryMb} MB RAM; raise Build Memory in Project Settings \u2192 Resources if this build needs more.`;
|
|
5647
|
+
}
|
|
5648
|
+
return "This build path was not under an OpenShip-enforced memory cap; check available RAM/swap and the host or Docker daemon OOM logs.";
|
|
5649
|
+
}
|
|
5650
|
+
function findNonZeroExitCode(output) {
|
|
5651
|
+
for (const match of output.matchAll(
|
|
5652
|
+
/(?:returned a non-zero code|exit(?:ed)? with(?: exit)? code|exit code)\s*:?\s*(\d+)\b/gi
|
|
5653
|
+
)) {
|
|
5654
|
+
const code = Number(match[1]);
|
|
5655
|
+
if (code !== 0) return code;
|
|
5656
|
+
}
|
|
5657
|
+
return null;
|
|
5658
|
+
}
|
|
5659
|
+
function extractDockerBuildFailureHint(line, context = {}) {
|
|
5660
|
+
const code = findNonZeroExitCode(line);
|
|
5661
|
+
if (code === 137) {
|
|
5662
|
+
return `${line} \u2014 The build process was killed by SIGKILL (exit code 137). Memory exhaustion is the most common cause, but exit 137 alone does not prove OOM. ${memoryGuidance(context)}`;
|
|
5663
|
+
}
|
|
5664
|
+
if (code === 143) {
|
|
5665
|
+
return `${line} \u2014 The build process received SIGTERM (exit code 143), usually because it was cancelled or stopped by another supervisor.`;
|
|
5666
|
+
}
|
|
5667
|
+
if (code !== null) return line;
|
|
5668
|
+
if (/JavaScript heap out of memory|Fatal process out of memory|Ineffective mark-compacts near heap limit|Reached heap limit Allocation failed|fatal error:\s*(?:runtime:\s*)?out of memory/i.test(
|
|
5669
|
+
line
|
|
5670
|
+
)) {
|
|
5671
|
+
return `${line} \u2014 The build process explicitly reported that it ran out of memory. ${memoryGuidance(context)}`;
|
|
5672
|
+
}
|
|
5673
|
+
if (/\bENOMEM\b|cannot allocate memory/i.test(line)) {
|
|
5674
|
+
return `${line} \u2014 The build process could not allocate memory (ENOMEM). ${memoryGuidance(context)}`;
|
|
5675
|
+
}
|
|
5676
|
+
return null;
|
|
5677
|
+
}
|
|
5678
|
+
function chooseDockerBuildFailureHint(current, candidate) {
|
|
5679
|
+
if (!candidate) return current;
|
|
5680
|
+
if (!current) return candidate;
|
|
5681
|
+
const score = (message) => {
|
|
5682
|
+
if (/explicitly reported that it ran out of memory|could not allocate memory/i.test(message)) {
|
|
5683
|
+
return 4;
|
|
5684
|
+
}
|
|
5685
|
+
if (/SIGKILL \(exit code 137\)/i.test(message)) return 3;
|
|
5686
|
+
if (/BuildKit|package\.json was not found/i.test(message)) return 2;
|
|
5687
|
+
if (/returned a non-zero code|failed to solve|executor failed running|error: build/i.test(message)) {
|
|
5688
|
+
return 1;
|
|
5689
|
+
}
|
|
5690
|
+
return 0;
|
|
5691
|
+
};
|
|
5692
|
+
return score(candidate) > score(current) ? candidate : current;
|
|
5693
|
+
}
|
|
5694
|
+
function dockerBuildExitMessage(code, streamedHint, context = {}) {
|
|
5695
|
+
if (streamedHint) return streamedHint;
|
|
5696
|
+
const plain = `docker build exited with code ${code}`;
|
|
5697
|
+
return extractDockerBuildFailureHint(plain, context) ?? plain;
|
|
5698
|
+
}
|
|
5699
|
+
function formatDuration(ms) {
|
|
5700
|
+
if (ms % 6e4 === 0) {
|
|
5701
|
+
const minutes = ms / 6e4;
|
|
5702
|
+
return `${minutes} minute${minutes === 1 ? "" : "s"}`;
|
|
5703
|
+
}
|
|
5704
|
+
const seconds = Math.ceil(ms / 1e3);
|
|
5705
|
+
return `${seconds} second${seconds === 1 ? "" : "s"}`;
|
|
5706
|
+
}
|
|
5707
|
+
function dockerBuildIdleTimeoutError(timeoutMs, context = {}) {
|
|
5708
|
+
const limit = context.memoryLimitApplied && context.configuredMemoryMb && context.configuredMemoryMb > 0 ? ` The classic Docker builder was capped at ${context.configuredMemoryMb} MB RAM, so sustained memory pressure is one possible cause.` : "";
|
|
5709
|
+
return new Error(
|
|
5710
|
+
`Docker build produced no output for ${formatDuration(timeoutMs)} and was cancelled.${limit} Other common causes are package-registry or DNS failures, a process waiting for input, and a stalled Docker daemon.`
|
|
5711
|
+
);
|
|
5712
|
+
}
|
|
5713
|
+
function startDockerBuildIdleMonitor(options) {
|
|
5714
|
+
let stopped = false;
|
|
5715
|
+
let lastProgressAt = Date.now();
|
|
5716
|
+
let lastReportedMinute = 0;
|
|
5717
|
+
let deadline;
|
|
5718
|
+
const armDeadline = () => {
|
|
5719
|
+
clearTimeout(deadline);
|
|
5720
|
+
deadline = setTimeout(() => {
|
|
5721
|
+
if (stopped) return;
|
|
5722
|
+
stopped = true;
|
|
5723
|
+
clearInterval(heartbeat);
|
|
5724
|
+
options.onTimeout();
|
|
5725
|
+
}, options.timeoutMs);
|
|
5726
|
+
deadline.unref?.();
|
|
5727
|
+
};
|
|
5728
|
+
const heartbeat = setInterval(() => {
|
|
5729
|
+
if (stopped) return;
|
|
5730
|
+
const idleMs = Date.now() - lastProgressAt;
|
|
5731
|
+
const idleMinutes = Math.floor(idleMs / 6e4);
|
|
5732
|
+
if (idleMinutes > lastReportedMinute) {
|
|
5733
|
+
lastReportedMinute = idleMinutes;
|
|
5734
|
+
options.onIdle(idleMs);
|
|
5735
|
+
}
|
|
5736
|
+
}, 6e4);
|
|
5737
|
+
heartbeat.unref?.();
|
|
5738
|
+
armDeadline();
|
|
5739
|
+
return {
|
|
5740
|
+
progress() {
|
|
5741
|
+
if (stopped) return;
|
|
5742
|
+
lastProgressAt = Date.now();
|
|
5743
|
+
lastReportedMinute = 0;
|
|
5744
|
+
armDeadline();
|
|
5745
|
+
},
|
|
5746
|
+
stop() {
|
|
5747
|
+
if (stopped) return;
|
|
5748
|
+
stopped = true;
|
|
5749
|
+
clearTimeout(deadline);
|
|
5750
|
+
clearInterval(heartbeat);
|
|
5751
|
+
}
|
|
5752
|
+
};
|
|
5753
|
+
}
|
|
5754
|
+
|
|
5755
|
+
// ../../packages/adapters/src/runtime/docker-build-container-tracker.ts
|
|
5756
|
+
function normalizedImageId(value) {
|
|
5757
|
+
return (value ?? "").replace(/^sha256:/, "").toLowerCase();
|
|
5758
|
+
}
|
|
5759
|
+
function commandMatches(instruction, actual) {
|
|
5760
|
+
const run = instruction.match(/^RUN\s+([\s\S]+)$/i)?.[1]?.trim();
|
|
5761
|
+
if (!run || !actual?.length) return false;
|
|
5762
|
+
if (run.startsWith("[")) {
|
|
5763
|
+
try {
|
|
5764
|
+
const parsed = JSON.parse(run);
|
|
5765
|
+
return Array.isArray(parsed) && parsed.every((part) => typeof part === "string") && parsed.length === actual.length && parsed.every((part, index) => part === actual[index]);
|
|
5766
|
+
} catch {
|
|
5767
|
+
return false;
|
|
5768
|
+
}
|
|
5769
|
+
}
|
|
5770
|
+
return actual[actual.length - 1] === run;
|
|
5771
|
+
}
|
|
5772
|
+
var LegacyBuildContainerTracker = class {
|
|
5773
|
+
constructor(client, expectedMemoryBytes, ownershipHost) {
|
|
5774
|
+
this.client = client;
|
|
5775
|
+
this.expectedMemoryBytes = expectedMemoryBytes;
|
|
5776
|
+
this.ownershipHost = ownershipHost;
|
|
5777
|
+
}
|
|
5778
|
+
parentImageId = null;
|
|
5779
|
+
pendingStep = null;
|
|
5780
|
+
active = null;
|
|
5781
|
+
expectedStepTotal = null;
|
|
5782
|
+
lastStepNumber = 0;
|
|
5783
|
+
chunkBuffer = "";
|
|
5784
|
+
queue = Promise.resolve();
|
|
5785
|
+
termination = null;
|
|
5786
|
+
/** Consume one complete dockerode `event.stream` field. */
|
|
5787
|
+
observe(stream) {
|
|
5788
|
+
if (!stream) return;
|
|
5789
|
+
for (const rawLine of stream.split(/\r?\n/)) {
|
|
5790
|
+
this.enqueueLine(rawLine);
|
|
5791
|
+
}
|
|
5792
|
+
}
|
|
5793
|
+
/** Consume an arbitrary SSH stdout/stderr chunk, retaining a partial line. */
|
|
5794
|
+
observeChunk(chunk) {
|
|
5795
|
+
if (!chunk) return;
|
|
5796
|
+
const parts = `${this.chunkBuffer}${chunk}`.split(/\r\n|\n|\r/);
|
|
5797
|
+
this.chunkBuffer = parts.pop() ?? "";
|
|
5798
|
+
if (this.chunkBuffer.length > 8192) this.chunkBuffer = this.chunkBuffer.slice(-8192);
|
|
5799
|
+
for (const line of parts) this.enqueueLine(line);
|
|
5800
|
+
}
|
|
5801
|
+
flush() {
|
|
5802
|
+
this.enqueueLine(this.chunkBuffer);
|
|
5803
|
+
this.chunkBuffer = "";
|
|
5804
|
+
}
|
|
5805
|
+
enqueueLine(rawLine) {
|
|
5806
|
+
const line = rawLine.trim();
|
|
5807
|
+
if (!line) return;
|
|
5808
|
+
this.queue = this.queue.then(
|
|
5809
|
+
() => this.observeLine(line),
|
|
5810
|
+
() => this.observeLine(line)
|
|
5811
|
+
);
|
|
5812
|
+
}
|
|
5813
|
+
async observeLine(line) {
|
|
5814
|
+
if (this.active) {
|
|
5815
|
+
const removed = line.match(/^---> Removed intermediate container\s+([a-f0-9]{12,64})$/i);
|
|
5816
|
+
if (removed?.[1]?.toLowerCase() === this.active.id) {
|
|
5817
|
+
try {
|
|
5818
|
+
const info = await this.client.inspect(this.active.id);
|
|
5819
|
+
if (!info?.State?.Running) this.active = null;
|
|
5820
|
+
} catch {
|
|
5821
|
+
}
|
|
5822
|
+
}
|
|
5823
|
+
return;
|
|
5824
|
+
}
|
|
5825
|
+
const layer = line.match(/^--->\s+([a-f0-9]{12,64})$/i);
|
|
5826
|
+
if (layer?.[1]) {
|
|
5827
|
+
this.parentImageId = layer[1].toLowerCase();
|
|
5828
|
+
return;
|
|
5829
|
+
}
|
|
5830
|
+
const step = line.match(/^Step\s+(\d+)\/(\d+)\s*:\s*([\s\S]+)$/i);
|
|
5831
|
+
if (step?.[1] && step[2] && step[3]) {
|
|
5832
|
+
const number = Number(step[1]);
|
|
5833
|
+
const total = Number(step[2]);
|
|
5834
|
+
const firstStep = this.lastStepNumber === 0 && number === 1;
|
|
5835
|
+
const nextStep = number === this.lastStepNumber + 1;
|
|
5836
|
+
const sameTotal = this.expectedStepTotal === null || total === this.expectedStepTotal;
|
|
5837
|
+
if (number >= 1 && total >= number && sameTotal && (firstStep || nextStep)) {
|
|
5838
|
+
this.expectedStepTotal ??= total;
|
|
5839
|
+
this.lastStepNumber = number;
|
|
5840
|
+
this.pendingStep = {
|
|
5841
|
+
number,
|
|
5842
|
+
total,
|
|
5843
|
+
instruction: step[3].trim(),
|
|
5844
|
+
parentImageId: this.parentImageId
|
|
5845
|
+
};
|
|
5846
|
+
}
|
|
5847
|
+
return;
|
|
5848
|
+
}
|
|
5849
|
+
const running = line.match(/^---> Running in\s+([a-f0-9]{12,64})$/i);
|
|
5850
|
+
if (running?.[1] && this.pendingStep && /^RUN\s+/i.test(this.pendingStep.instruction)) {
|
|
5851
|
+
this.active = { ...this.pendingStep, id: running[1].toLowerCase() };
|
|
5852
|
+
this.pendingStep = null;
|
|
5853
|
+
}
|
|
5854
|
+
}
|
|
5855
|
+
async terminate() {
|
|
5856
|
+
await this.queue;
|
|
5857
|
+
const candidate = this.active;
|
|
5858
|
+
if (!candidate) return { status: "none" };
|
|
5859
|
+
const info = await this.client.inspect(candidate.id);
|
|
5860
|
+
if (!info?.State?.Running) return { status: "not-running", id: candidate.id };
|
|
5861
|
+
const actualId = info.Id?.toLowerCase() ?? "";
|
|
5862
|
+
const actualImage = normalizedImageId(info.Config?.Image);
|
|
5863
|
+
const expectedMemory = this.expectedMemoryBytes ?? 0;
|
|
5864
|
+
const ownershipEntry = `${this.ownershipHost}:127.0.0.1`;
|
|
5865
|
+
const verified = actualId.startsWith(candidate.id) && candidate.parentImageId !== null && actualImage.startsWith(candidate.parentImageId) && (info.HostConfig?.Memory ?? 0) === expectedMemory && info.HostConfig?.ExtraHosts?.includes(ownershipEntry) === true && commandMatches(candidate.instruction, info.Config?.Cmd);
|
|
5866
|
+
if (!verified) return { status: "unverified", id: candidate.id };
|
|
5867
|
+
await this.client.kill(candidate.id);
|
|
5868
|
+
return { status: "killed", id: candidate.id };
|
|
5869
|
+
}
|
|
5870
|
+
terminateActive() {
|
|
5871
|
+
return this.termination ??= this.terminate();
|
|
5872
|
+
}
|
|
5873
|
+
};
|
|
5874
|
+
|
|
5633
5875
|
// ../../packages/adapters/src/runtime/docker.ts
|
|
5634
5876
|
var isDockerNotFoundError = isRuntimeNotFoundError;
|
|
5635
5877
|
function ownsBuiltImage(imageRef) {
|
|
@@ -5647,7 +5889,42 @@ var RESTART_POLICIES = {
|
|
|
5647
5889
|
"unless-stopped": { Name: "unless-stopped", MaximumRetryCount: 0 },
|
|
5648
5890
|
no: { Name: "no", MaximumRetryCount: 0 }
|
|
5649
5891
|
};
|
|
5650
|
-
var
|
|
5892
|
+
var BUILD_CONTAINER_CLEANUP_TIMEOUT_MS = 5e3;
|
|
5893
|
+
var BUILD_CONTAINER_IDENTIFICATION_GRACE_MS = 500;
|
|
5894
|
+
function newBuildOwnershipHost() {
|
|
5895
|
+
return `openship-build-${randomUUID()}.invalid`;
|
|
5896
|
+
}
|
|
5897
|
+
async function boundedBuildContainerCleanup(cleanup) {
|
|
5898
|
+
let timer = null;
|
|
5899
|
+
const guarded = cleanup.catch(() => null);
|
|
5900
|
+
const deadline = new Promise((resolve) => {
|
|
5901
|
+
timer = setTimeout(() => resolve(null), BUILD_CONTAINER_CLEANUP_TIMEOUT_MS);
|
|
5902
|
+
timer.unref?.();
|
|
5903
|
+
});
|
|
5904
|
+
try {
|
|
5905
|
+
return await Promise.race([guarded, deadline]);
|
|
5906
|
+
} finally {
|
|
5907
|
+
if (timer) clearTimeout(timer);
|
|
5908
|
+
}
|
|
5909
|
+
}
|
|
5910
|
+
async function stopTrackedBuildContainer(tracker, log, reason) {
|
|
5911
|
+
if (!tracker) return;
|
|
5912
|
+
tracker.flush();
|
|
5913
|
+
const cleanup = await boundedBuildContainerCleanup(tracker.terminateActive());
|
|
5914
|
+
if (cleanup?.status === "killed") {
|
|
5915
|
+
log.log(`Stopped ${reason} Docker build container ${cleanup.id.slice(0, 12)}.`);
|
|
5916
|
+
} else if (cleanup?.status === "unverified") {
|
|
5917
|
+
log.log(
|
|
5918
|
+
`${reason === "timed-out" ? "Timed-out" : "Interrupted"} build container ${cleanup.id.slice(0, 12)} did not match this build's verified step; it was left untouched.`,
|
|
5919
|
+
"warn"
|
|
5920
|
+
);
|
|
5921
|
+
} else if (cleanup === null) {
|
|
5922
|
+
log.log(
|
|
5923
|
+
`${reason === "timed-out" ? "Timed-out" : "Interrupted"} Docker build cleanup could not be confirmed within 5 seconds; Docker may still be releasing the build container.`,
|
|
5924
|
+
"warn"
|
|
5925
|
+
);
|
|
5926
|
+
}
|
|
5927
|
+
}
|
|
5651
5928
|
var activeDockerBuilds = /* @__PURE__ */ new Map();
|
|
5652
5929
|
var pendingDockerBuildCancellations = /* @__PURE__ */ new Map();
|
|
5653
5930
|
var PENDING_CANCEL_TTL_MS = 10 * 60 * 1e3;
|
|
@@ -5817,21 +6094,34 @@ function parsePortBindings(portSpecs) {
|
|
|
5817
6094
|
}
|
|
5818
6095
|
function packBuildContext(contextDir, entries, cancelSignal, ignoreContextPath) {
|
|
5819
6096
|
const controller = new AbortController();
|
|
6097
|
+
const abort = (reason) => {
|
|
6098
|
+
if (!controller.signal.aborted) controller.abort(reason);
|
|
6099
|
+
};
|
|
5820
6100
|
const ignore = ignoreContextPath ? (absolutePath) => ignoreContextPath(relative(contextDir, absolutePath).split(sep).join("/")) : void 0;
|
|
5821
6101
|
const pack2 = tarFs.pack(contextDir, { entries, ignore });
|
|
5822
6102
|
const body = pack2.pipe(createGzip());
|
|
5823
6103
|
let contextError = null;
|
|
5824
6104
|
const capture = (err) => {
|
|
5825
6105
|
contextError ??= err instanceof Error ? err : new Error(String(err));
|
|
5826
|
-
|
|
6106
|
+
abort(contextError);
|
|
5827
6107
|
};
|
|
6108
|
+
const onCancel = () => abort(cancelSignal?.reason);
|
|
5828
6109
|
if (cancelSignal) {
|
|
5829
|
-
if (cancelSignal.aborted)
|
|
5830
|
-
else cancelSignal.addEventListener("abort",
|
|
6110
|
+
if (cancelSignal.aborted) abort(cancelSignal.reason);
|
|
6111
|
+
else cancelSignal.addEventListener("abort", onCancel, { once: true });
|
|
5831
6112
|
}
|
|
5832
6113
|
pack2.once("error", capture);
|
|
5833
6114
|
body.once("error", capture);
|
|
5834
|
-
return {
|
|
6115
|
+
return {
|
|
6116
|
+
body,
|
|
6117
|
+
abortSignal: controller.signal,
|
|
6118
|
+
abort,
|
|
6119
|
+
// Once buildImage has returned the response stream, streamDockerodeBuild
|
|
6120
|
+
// owns cancellation. It briefly keeps that stream alive to receive Docker's
|
|
6121
|
+
// exact intermediate-container id before aborting the request.
|
|
6122
|
+
detachCancellation: () => cancelSignal?.removeEventListener("abort", onCancel),
|
|
6123
|
+
takeError: () => contextError
|
|
6124
|
+
};
|
|
5835
6125
|
}
|
|
5836
6126
|
var DURATION_UNITS_NS = {
|
|
5837
6127
|
ns: 1,
|
|
@@ -6179,19 +6469,25 @@ var DockerRuntime = class _DockerRuntime {
|
|
|
6179
6469
|
logger.log(`[${step}] ${label} ${message}`);
|
|
6180
6470
|
logger.step(step, status, message);
|
|
6181
6471
|
}
|
|
6182
|
-
handleBuildEvent(event, logger, trace) {
|
|
6183
|
-
const errorMessage = event.errorDetail?.message
|
|
6472
|
+
handleBuildEvent(event, logger, trace, diagnosticContext = {}) {
|
|
6473
|
+
const errorMessage = event.errorDetail?.message || event.error;
|
|
6184
6474
|
if (errorMessage) {
|
|
6185
6475
|
logger.log(errorMessage, "error");
|
|
6186
|
-
return
|
|
6476
|
+
return {
|
|
6477
|
+
errorMessage,
|
|
6478
|
+
failureHint: this.extractBuildFailureHint(errorMessage, diagnosticContext) ?? errorMessage
|
|
6479
|
+
};
|
|
6187
6480
|
}
|
|
6188
6481
|
if (event.id === "moby.buildkit.trace" && typeof event.aux === "string") {
|
|
6189
6482
|
let hint = null;
|
|
6190
6483
|
for (const line of trace?.push(event.aux) ?? []) {
|
|
6191
6484
|
logger.log(line.message, line.level === "error" ? "error" : parseLogLevel(line.message));
|
|
6192
|
-
hint
|
|
6485
|
+
hint = chooseDockerBuildFailureHint(
|
|
6486
|
+
hint,
|
|
6487
|
+
this.extractBuildFailureHint(line.message, diagnosticContext) ?? (line.level === "error" ? line.message : null)
|
|
6488
|
+
);
|
|
6193
6489
|
}
|
|
6194
|
-
return hint;
|
|
6490
|
+
return { failureHint: hint };
|
|
6195
6491
|
}
|
|
6196
6492
|
if (event.stream) {
|
|
6197
6493
|
const line = event.stream.trim();
|
|
@@ -6216,7 +6512,7 @@ var DockerRuntime = class _DockerRuntime {
|
|
|
6216
6512
|
return null;
|
|
6217
6513
|
}
|
|
6218
6514
|
logger.log(line, parseLogLevel(line));
|
|
6219
|
-
return this.extractBuildFailureHint(line);
|
|
6515
|
+
return { failureHint: this.extractBuildFailureHint(line, diagnosticContext) };
|
|
6220
6516
|
}
|
|
6221
6517
|
if (event.status) {
|
|
6222
6518
|
const parts = [event.id, event.status, event.progress].filter((p) => Boolean(p?.trim())).map((p) => p.trim());
|
|
@@ -6245,10 +6541,9 @@ var DockerRuntime = class _DockerRuntime {
|
|
|
6245
6541
|
isLowSignalDockerLine(line) {
|
|
6246
6542
|
return _DockerRuntime.DOCKER_BUILDER_NOISE.some((p) => p.test(line));
|
|
6247
6543
|
}
|
|
6248
|
-
extractBuildFailureHint(line) {
|
|
6249
|
-
|
|
6250
|
-
|
|
6251
|
-
}
|
|
6544
|
+
extractBuildFailureHint(line, diagnosticContext = {}) {
|
|
6545
|
+
const processFailure = extractDockerBuildFailureHint(line, diagnosticContext);
|
|
6546
|
+
if (processFailure) return processFailure;
|
|
6252
6547
|
if (/requires? BuildKit|BuildKit is enabled but the buildx component/i.test(line)) {
|
|
6253
6548
|
return `${line} \u2014 this Dockerfile needs BuildKit. Install the docker buildx plugin on the build host (\`docker-buildx-plugin\`), or remove the BuildKit-only instruction.`;
|
|
6254
6549
|
}
|
|
@@ -6345,7 +6640,8 @@ var DockerRuntime = class _DockerRuntime {
|
|
|
6345
6640
|
const dockerfileFlag = dockerfileName && dockerfileName !== "Dockerfile" ? ` -f ${sq(dockerfileName)}` : "";
|
|
6346
6641
|
const buildKit = await this.remoteBuildKitAvailable();
|
|
6347
6642
|
const builderEnv = buildKit ? "DOCKER_BUILDKIT=1 " : "";
|
|
6348
|
-
const
|
|
6643
|
+
const ownershipHost = buildKit ? null : newBuildOwnershipHost();
|
|
6644
|
+
const builderFlags = buildKit ? " --progress=plain" : ` --force-rm --add-host ${sq(`${ownershipHost}:127.0.0.1`)}`;
|
|
6349
6645
|
if (buildKit) {
|
|
6350
6646
|
log.log("Builder: BuildKit (docker buildx detected on the host)");
|
|
6351
6647
|
} else if (opts?.requiresBuildKit) {
|
|
@@ -6369,16 +6665,79 @@ var DockerRuntime = class _DockerRuntime {
|
|
|
6369
6665
|
"running",
|
|
6370
6666
|
"Running install inside container (docker build)"
|
|
6371
6667
|
);
|
|
6372
|
-
const
|
|
6373
|
-
|
|
6374
|
-
|
|
6375
|
-
|
|
6668
|
+
const diagnosticContext = {
|
|
6669
|
+
configuredMemoryMb: config.resources.memoryMb,
|
|
6670
|
+
memoryLimitApplied: false
|
|
6671
|
+
};
|
|
6672
|
+
const buildContainerTracker = buildKit ? null : this.remoteLegacyBuildContainerTracker(executor, ownershipHost);
|
|
6673
|
+
const commandAbort = new AbortController();
|
|
6674
|
+
const forwardCancellation = () => {
|
|
6675
|
+
buildContainerTracker?.flush();
|
|
6676
|
+
if (buildContainerTracker) void buildContainerTracker.terminateActive();
|
|
6677
|
+
commandAbort.abort(signal?.reason);
|
|
6678
|
+
};
|
|
6679
|
+
if (signal?.aborted) forwardCancellation();
|
|
6680
|
+
else signal?.addEventListener("abort", forwardCancellation, { once: true });
|
|
6681
|
+
let timeoutError = null;
|
|
6682
|
+
let streamedFailureHint = null;
|
|
6683
|
+
const timeoutMs = getDockerBuildIdleTimeoutMs();
|
|
6684
|
+
const idleMonitor = startDockerBuildIdleMonitor({
|
|
6685
|
+
timeoutMs,
|
|
6686
|
+
onIdle: (idleMs) => {
|
|
6687
|
+
log.log(`Still building... (no output for ${Math.floor(idleMs / 6e4)}m)`);
|
|
6376
6688
|
},
|
|
6377
|
-
|
|
6378
|
-
|
|
6689
|
+
onTimeout: () => {
|
|
6690
|
+
timeoutError = dockerBuildIdleTimeoutError(timeoutMs, diagnosticContext);
|
|
6691
|
+
buildContainerTracker?.flush();
|
|
6692
|
+
if (buildContainerTracker) void buildContainerTracker.terminateActive();
|
|
6693
|
+
commandAbort.abort(timeoutError);
|
|
6694
|
+
}
|
|
6695
|
+
});
|
|
6696
|
+
let code = null;
|
|
6697
|
+
let execError = null;
|
|
6698
|
+
try {
|
|
6699
|
+
const result = await executor.streamExec(
|
|
6700
|
+
buildCmd,
|
|
6701
|
+
(entry) => {
|
|
6702
|
+
idleMonitor.progress();
|
|
6703
|
+
buildContainerTracker?.observeChunk(entry.message);
|
|
6704
|
+
streamedFailureHint = chooseDockerBuildFailureHint(
|
|
6705
|
+
streamedFailureHint,
|
|
6706
|
+
this.extractBuildFailureHint(entry.message, diagnosticContext)
|
|
6707
|
+
);
|
|
6708
|
+
log.log(entry.message, parseLogLevel(entry.message));
|
|
6709
|
+
},
|
|
6710
|
+
{ signal: commandAbort.signal }
|
|
6711
|
+
);
|
|
6712
|
+
code = result.code;
|
|
6713
|
+
} catch (error) {
|
|
6714
|
+
execError = error;
|
|
6715
|
+
} finally {
|
|
6716
|
+
idleMonitor.stop();
|
|
6717
|
+
buildContainerTracker?.flush();
|
|
6718
|
+
signal?.removeEventListener("abort", forwardCancellation);
|
|
6719
|
+
}
|
|
6379
6720
|
log.log("\u2500\u2500\u2500 end docker build output \u2500\u2500\u2500");
|
|
6380
|
-
if (
|
|
6381
|
-
|
|
6721
|
+
if (timeoutError) {
|
|
6722
|
+
await stopTrackedBuildContainer(buildContainerTracker, log, "timed-out");
|
|
6723
|
+
throw timeoutError;
|
|
6724
|
+
}
|
|
6725
|
+
if (signal?.aborted) {
|
|
6726
|
+
await stopTrackedBuildContainer(buildContainerTracker, log, "interrupted");
|
|
6727
|
+
throw new BuildCancelledError();
|
|
6728
|
+
}
|
|
6729
|
+
if (execError) {
|
|
6730
|
+
await stopTrackedBuildContainer(buildContainerTracker, log, "interrupted");
|
|
6731
|
+
throw execError;
|
|
6732
|
+
}
|
|
6733
|
+
if (code === null) {
|
|
6734
|
+
await stopTrackedBuildContainer(buildContainerTracker, log, "interrupted");
|
|
6735
|
+
throw new Error("docker build ended without an exit code");
|
|
6736
|
+
}
|
|
6737
|
+
if (code !== 0) {
|
|
6738
|
+
await stopTrackedBuildContainer(buildContainerTracker, log, "interrupted");
|
|
6739
|
+
throw new Error(dockerBuildExitMessage(code, streamedFailureHint, diagnosticContext));
|
|
6740
|
+
}
|
|
6382
6741
|
this.emitDockerStep(log, "install", "completed", "Image build finished");
|
|
6383
6742
|
}
|
|
6384
6743
|
/**
|
|
@@ -6607,6 +6966,55 @@ var DockerRuntime = class _DockerRuntime {
|
|
|
6607
6966
|
}
|
|
6608
6967
|
return { options: { version: "2" }, trace: new BuildKitTraceDecoder() };
|
|
6609
6968
|
}
|
|
6969
|
+
/**
|
|
6970
|
+
* Bind legacy-builder cleanup to the exact container id Docker announces in
|
|
6971
|
+
* this build's own progress stream. A 404 is an ordinary "already cleaned"
|
|
6972
|
+
* result; every other inspection failure remains inconclusive and therefore
|
|
6973
|
+
* can never authorize a kill.
|
|
6974
|
+
*/
|
|
6975
|
+
legacyBuildContainerTracker(expectedMemoryBytes, ownershipHost) {
|
|
6976
|
+
return new LegacyBuildContainerTracker(
|
|
6977
|
+
{
|
|
6978
|
+
inspect: async (id) => {
|
|
6979
|
+
try {
|
|
6980
|
+
return await this.docker.getContainer(id).inspect();
|
|
6981
|
+
} catch (error) {
|
|
6982
|
+
if (isDockerNotFoundError(error)) return null;
|
|
6983
|
+
throw error;
|
|
6984
|
+
}
|
|
6985
|
+
},
|
|
6986
|
+
kill: async (id) => {
|
|
6987
|
+
await this.docker.getContainer(id).kill({ signal: "SIGKILL" });
|
|
6988
|
+
}
|
|
6989
|
+
},
|
|
6990
|
+
expectedMemoryBytes,
|
|
6991
|
+
ownershipHost
|
|
6992
|
+
);
|
|
6993
|
+
}
|
|
6994
|
+
/** Same proof model as the Engine-API tracker, using the already-live SSH executor. */
|
|
6995
|
+
remoteLegacyBuildContainerTracker(executor, ownershipHost) {
|
|
6996
|
+
return new LegacyBuildContainerTracker(
|
|
6997
|
+
{
|
|
6998
|
+
inspect: async (id) => {
|
|
6999
|
+
try {
|
|
7000
|
+
const output = await executor.exec(`docker container inspect ${sq(id)}`);
|
|
7001
|
+
const parsed = JSON.parse(output);
|
|
7002
|
+
return Array.isArray(parsed) && parsed[0] && typeof parsed[0] === "object" ? parsed[0] : null;
|
|
7003
|
+
} catch (error) {
|
|
7004
|
+
if (/No such (?:object|container)|not found/i.test(safeErrorMessage(error)))
|
|
7005
|
+
return null;
|
|
7006
|
+
throw error;
|
|
7007
|
+
}
|
|
7008
|
+
},
|
|
7009
|
+
kill: async (id) => {
|
|
7010
|
+
await executor.exec(`docker container kill --signal KILL ${sq(id)} >/dev/null`);
|
|
7011
|
+
}
|
|
7012
|
+
},
|
|
7013
|
+
// The native CLI path intentionally applies no OpenShip memory flag.
|
|
7014
|
+
void 0,
|
|
7015
|
+
ownershipHost
|
|
7016
|
+
);
|
|
7017
|
+
}
|
|
6610
7018
|
/**
|
|
6611
7019
|
* Dockerode build path. Used for local socket and TCP transports, plus
|
|
6612
7020
|
* SSH transports that didn't get an executor wired in (shouldn't happen
|
|
@@ -6618,13 +7026,21 @@ var DockerRuntime = class _DockerRuntime {
|
|
|
6618
7026
|
*/
|
|
6619
7027
|
async buildViaDockerode(config, buildContext, tag, log, cancelSignal) {
|
|
6620
7028
|
log.log(`Streaming build context to Docker daemon - image tag: ${tag}`);
|
|
6621
|
-
const { body, abortSignal, takeError } = packBuildContext(
|
|
7029
|
+
const { body, abortSignal, abort, detachCancellation, takeError } = packBuildContext(
|
|
6622
7030
|
buildContext.buildContextDir,
|
|
6623
7031
|
buildContext.contextEntries,
|
|
6624
7032
|
cancelSignal,
|
|
6625
7033
|
buildContext.ignoreContextPath
|
|
6626
7034
|
);
|
|
6627
7035
|
const builder = this.selectDockerodeBuilder(config, buildContext.requiresBuildKit, log);
|
|
7036
|
+
const buildLimits = dockerBuildResourceLimits(config.resources);
|
|
7037
|
+
const ownershipHost = builder.options.version !== "2" ? newBuildOwnershipHost() : null;
|
|
7038
|
+
const diagnosticContext = {
|
|
7039
|
+
configuredMemoryMb: config.resources.memoryMb,
|
|
7040
|
+
// Engine API build limits work only with the classic builder. The
|
|
7041
|
+
// BuildKit selection above already logs that its build runs uncapped.
|
|
7042
|
+
memoryLimitApplied: builder.options.version !== "2" && buildLimits.memory !== void 0
|
|
7043
|
+
};
|
|
6628
7044
|
try {
|
|
6629
7045
|
const stream = await this.docker.buildImage(body, {
|
|
6630
7046
|
t: tag,
|
|
@@ -6634,13 +7050,23 @@ var DockerRuntime = class _DockerRuntime {
|
|
|
6634
7050
|
// Omitted entirely unless the project set a build cap — a self-hosted
|
|
6635
7051
|
// build should be free to use the machine (a production build often
|
|
6636
7052
|
// needs several GB). Opt-in only; see dockerBuildResourceLimits.
|
|
6637
|
-
...
|
|
7053
|
+
...buildLimits,
|
|
7054
|
+
...ownershipHost && { extrahosts: `${ownershipHost}:127.0.0.1` },
|
|
6638
7055
|
forcerm: true,
|
|
6639
7056
|
...builder.options,
|
|
6640
7057
|
abortSignal
|
|
6641
7058
|
});
|
|
7059
|
+
detachCancellation();
|
|
6642
7060
|
log.log("Connected to Docker daemon. Build output follows:");
|
|
6643
|
-
await this.streamDockerodeBuild(stream, log,
|
|
7061
|
+
await this.streamDockerodeBuild(stream, log, {
|
|
7062
|
+
trace: builder.trace,
|
|
7063
|
+
diagnosticContext,
|
|
7064
|
+
abortBuild: abort,
|
|
7065
|
+
cancelSignal,
|
|
7066
|
+
...ownershipHost && {
|
|
7067
|
+
legacyBuilder: { expectedMemoryBytes: buildLimits.memory, ownershipHost }
|
|
7068
|
+
}
|
|
7069
|
+
});
|
|
6644
7070
|
log.log("Docker daemon finished streaming build output. Finalizing image...\n");
|
|
6645
7071
|
} catch (err) {
|
|
6646
7072
|
const contextErr = takeError();
|
|
@@ -6648,6 +7074,7 @@ var DockerRuntime = class _DockerRuntime {
|
|
|
6648
7074
|
cause: contextErr
|
|
6649
7075
|
}) : err;
|
|
6650
7076
|
} finally {
|
|
7077
|
+
detachCancellation();
|
|
6651
7078
|
await buildContext.cleanup();
|
|
6652
7079
|
}
|
|
6653
7080
|
}
|
|
@@ -6656,71 +7083,116 @@ var DockerRuntime = class _DockerRuntime {
|
|
|
6656
7083
|
* idle timeout + keepalive, and throw on a fatal build event. Shared by the
|
|
6657
7084
|
* single-image dockerode path and the local batch build (buildImages).
|
|
6658
7085
|
*/
|
|
6659
|
-
async streamDockerodeBuild(stream, log,
|
|
7086
|
+
async streamDockerodeBuild(stream, log, options = {}) {
|
|
6660
7087
|
let fatalBuildError = null;
|
|
6661
|
-
|
|
6662
|
-
|
|
6663
|
-
|
|
6664
|
-
|
|
6665
|
-
|
|
6666
|
-
|
|
6667
|
-
|
|
6668
|
-
|
|
6669
|
-
|
|
6670
|
-
|
|
6671
|
-
|
|
6672
|
-
|
|
6673
|
-
|
|
6674
|
-
|
|
6675
|
-
|
|
6676
|
-
|
|
6677
|
-
|
|
6678
|
-
|
|
6679
|
-
|
|
6680
|
-
|
|
6681
|
-
|
|
6682
|
-
|
|
6683
|
-
|
|
6684
|
-
|
|
6685
|
-
|
|
6686
|
-
|
|
6687
|
-
|
|
6688
|
-
|
|
6689
|
-
|
|
6690
|
-
|
|
6691
|
-
|
|
6692
|
-
|
|
6693
|
-
|
|
6694
|
-
|
|
6695
|
-
|
|
6696
|
-
|
|
6697
|
-
|
|
6698
|
-
|
|
6699
|
-
|
|
6700
|
-
"Docker build produced no output for 30 minutes. This usually means the remote server cannot reach the package registry, has broken DNS, or the Docker daemon stalled during the build."
|
|
6701
|
-
)
|
|
7088
|
+
let streamedFailureHint = null;
|
|
7089
|
+
const timeoutMs = getDockerBuildIdleTimeoutMs();
|
|
7090
|
+
const diagnosticContext = options.diagnosticContext ?? {};
|
|
7091
|
+
const buildContainerTracker = options.legacyBuilder ? this.legacyBuildContainerTracker(
|
|
7092
|
+
options.legacyBuilder.expectedMemoryBytes,
|
|
7093
|
+
options.legacyBuilder.ownershipHost
|
|
7094
|
+
) : null;
|
|
7095
|
+
let inactivityTimedOut = false;
|
|
7096
|
+
try {
|
|
7097
|
+
await new Promise((resolve, reject) => {
|
|
7098
|
+
let settled = false;
|
|
7099
|
+
let idleMonitor = null;
|
|
7100
|
+
let cancellationTimer = null;
|
|
7101
|
+
const clearTimers = () => {
|
|
7102
|
+
idleMonitor?.stop();
|
|
7103
|
+
if (cancellationTimer) clearTimeout(cancellationTimer);
|
|
7104
|
+
options.cancelSignal?.removeEventListener("abort", requestCancellation);
|
|
7105
|
+
};
|
|
7106
|
+
const fail = (error) => {
|
|
7107
|
+
if (settled) return;
|
|
7108
|
+
settled = true;
|
|
7109
|
+
clearTimers();
|
|
7110
|
+
stream.destroy?.(error);
|
|
7111
|
+
reject(error);
|
|
7112
|
+
};
|
|
7113
|
+
const abortCancelledBuild = () => {
|
|
7114
|
+
if (settled) return;
|
|
7115
|
+
buildContainerTracker?.flush();
|
|
7116
|
+
if (buildContainerTracker) void buildContainerTracker.terminateActive();
|
|
7117
|
+
const error = new BuildCancelledError();
|
|
7118
|
+
fail(error);
|
|
7119
|
+
options.abortBuild?.(error);
|
|
7120
|
+
};
|
|
7121
|
+
const requestCancellation = () => {
|
|
7122
|
+
if (settled || cancellationTimer) return;
|
|
7123
|
+
idleMonitor?.stop();
|
|
7124
|
+
cancellationTimer = setTimeout(
|
|
7125
|
+
abortCancelledBuild,
|
|
7126
|
+
buildContainerTracker ? BUILD_CONTAINER_IDENTIFICATION_GRACE_MS : 0
|
|
6702
7127
|
);
|
|
6703
|
-
|
|
6704
|
-
|
|
6705
|
-
|
|
6706
|
-
|
|
6707
|
-
|
|
6708
|
-
|
|
6709
|
-
(err) => {
|
|
6710
|
-
if (err) {
|
|
6711
|
-
fail(err);
|
|
7128
|
+
cancellationTimer.unref?.();
|
|
7129
|
+
};
|
|
7130
|
+
const succeed = () => {
|
|
7131
|
+
if (settled) return;
|
|
7132
|
+
if (options.cancelSignal?.aborted) {
|
|
7133
|
+
abortCancelledBuild();
|
|
6712
7134
|
return;
|
|
6713
7135
|
}
|
|
6714
|
-
|
|
6715
|
-
|
|
6716
|
-
|
|
6717
|
-
|
|
6718
|
-
|
|
6719
|
-
|
|
7136
|
+
settled = true;
|
|
7137
|
+
clearTimers();
|
|
7138
|
+
resolve();
|
|
7139
|
+
};
|
|
7140
|
+
idleMonitor = startDockerBuildIdleMonitor({
|
|
7141
|
+
timeoutMs,
|
|
7142
|
+
onIdle: (idleMs) => {
|
|
7143
|
+
log.log(`Still building... (no output for ${Math.floor(idleMs / 6e4)}m)`);
|
|
7144
|
+
},
|
|
7145
|
+
onTimeout: () => {
|
|
7146
|
+
const error = dockerBuildIdleTimeoutError(timeoutMs, diagnosticContext);
|
|
7147
|
+
inactivityTimedOut = true;
|
|
7148
|
+
if (buildContainerTracker) {
|
|
7149
|
+
void buildContainerTracker.terminateActive();
|
|
7150
|
+
}
|
|
7151
|
+
fail(error);
|
|
7152
|
+
options.abortBuild?.(error);
|
|
7153
|
+
}
|
|
7154
|
+
});
|
|
7155
|
+
if (options.cancelSignal?.aborted) requestCancellation();
|
|
7156
|
+
else options.cancelSignal?.addEventListener("abort", requestCancellation, { once: true });
|
|
7157
|
+
this.docker.modem.followProgress(
|
|
7158
|
+
stream,
|
|
7159
|
+
(err) => {
|
|
7160
|
+
if (err) {
|
|
7161
|
+
fail(err);
|
|
7162
|
+
return;
|
|
7163
|
+
}
|
|
7164
|
+
succeed();
|
|
7165
|
+
},
|
|
7166
|
+
(event) => {
|
|
7167
|
+
idleMonitor?.progress();
|
|
7168
|
+
buildContainerTracker?.observe(event.stream);
|
|
7169
|
+
const diagnostics = this.handleBuildEvent(event, log, options.trace, diagnosticContext);
|
|
7170
|
+
fatalBuildError ??= diagnostics?.errorMessage ?? null;
|
|
7171
|
+
streamedFailureHint = chooseDockerBuildFailureHint(
|
|
7172
|
+
streamedFailureHint,
|
|
7173
|
+
diagnostics?.failureHint ?? null
|
|
7174
|
+
);
|
|
7175
|
+
}
|
|
7176
|
+
);
|
|
7177
|
+
});
|
|
7178
|
+
} catch (error) {
|
|
7179
|
+
await stopTrackedBuildContainer(
|
|
7180
|
+
buildContainerTracker,
|
|
7181
|
+
log,
|
|
7182
|
+
inactivityTimedOut ? "timed-out" : "interrupted"
|
|
6720
7183
|
);
|
|
6721
|
-
|
|
7184
|
+
throw error;
|
|
7185
|
+
}
|
|
7186
|
+
if (options.cancelSignal?.aborted) {
|
|
7187
|
+
await stopTrackedBuildContainer(buildContainerTracker, log, "interrupted");
|
|
7188
|
+
throw new BuildCancelledError();
|
|
7189
|
+
}
|
|
6722
7190
|
if (fatalBuildError) {
|
|
6723
|
-
|
|
7191
|
+
const hint = chooseDockerBuildFailureHint(fatalBuildError, streamedFailureHint);
|
|
7192
|
+
throw new Error(
|
|
7193
|
+
hint && !hint.includes(fatalBuildError) ? `${fatalBuildError}
|
|
7194
|
+
${hint}` : hint ?? fatalBuildError
|
|
7195
|
+
);
|
|
6724
7196
|
}
|
|
6725
7197
|
}
|
|
6726
7198
|
/**
|
|
@@ -7445,13 +7917,19 @@ var DockerRuntime = class _DockerRuntime {
|
|
|
7445
7917
|
{ signal, requiresBuildKit }
|
|
7446
7918
|
);
|
|
7447
7919
|
} else {
|
|
7448
|
-
const { body, abortSignal, takeError } = packBuildContext(
|
|
7920
|
+
const { body, abortSignal, abort, detachCancellation, takeError } = packBuildContext(
|
|
7449
7921
|
buildContextDir ?? tree.contextDir,
|
|
7450
7922
|
contextEntries ?? [],
|
|
7451
7923
|
signal,
|
|
7452
7924
|
ignoreContextPath
|
|
7453
7925
|
);
|
|
7454
7926
|
const builder = this.selectDockerodeBuilder(spec.config, requiresBuildKit, spec.logger);
|
|
7927
|
+
const buildLimits = dockerBuildResourceLimits(spec.config.resources);
|
|
7928
|
+
const ownershipHost = builder.options.version !== "2" ? newBuildOwnershipHost() : null;
|
|
7929
|
+
const diagnosticContext = {
|
|
7930
|
+
configuredMemoryMb: spec.config.resources.memoryMb,
|
|
7931
|
+
memoryLimitApplied: builder.options.version !== "2" && buildLimits.memory !== void 0
|
|
7932
|
+
};
|
|
7455
7933
|
try {
|
|
7456
7934
|
const stream = await this.docker.buildImage(body, {
|
|
7457
7935
|
t: tag,
|
|
@@ -7461,18 +7939,30 @@ var DockerRuntime = class _DockerRuntime {
|
|
|
7461
7939
|
sessionId: spec.config.sessionId
|
|
7462
7940
|
}),
|
|
7463
7941
|
buildargs: resolveDockerBuildArgs(spec.config),
|
|
7464
|
-
...
|
|
7942
|
+
...buildLimits,
|
|
7943
|
+
...ownershipHost && { extrahosts: `${ownershipHost}:127.0.0.1` },
|
|
7465
7944
|
forcerm: true,
|
|
7466
7945
|
...builder.options,
|
|
7467
7946
|
abortSignal
|
|
7468
7947
|
});
|
|
7469
|
-
|
|
7948
|
+
detachCancellation();
|
|
7949
|
+
await this.streamDockerodeBuild(stream, spec.logger, {
|
|
7950
|
+
trace: builder.trace,
|
|
7951
|
+
diagnosticContext,
|
|
7952
|
+
abortBuild: abort,
|
|
7953
|
+
cancelSignal: signal,
|
|
7954
|
+
...ownershipHost && {
|
|
7955
|
+
legacyBuilder: { expectedMemoryBytes: buildLimits.memory, ownershipHost }
|
|
7956
|
+
}
|
|
7957
|
+
});
|
|
7470
7958
|
} catch (err) {
|
|
7471
7959
|
const contextErr = takeError();
|
|
7472
7960
|
throw contextErr ? new Error(
|
|
7473
7961
|
`Failed to read Docker build context: ${safeErrorMessage(contextErr)}`,
|
|
7474
7962
|
{ cause: contextErr }
|
|
7475
7963
|
) : err;
|
|
7964
|
+
} finally {
|
|
7965
|
+
detachCancellation();
|
|
7476
7966
|
}
|
|
7477
7967
|
}
|
|
7478
7968
|
if (isCancelled(spec.config.sessionId)) {
|
|
@@ -7792,6 +8282,56 @@ var DockerRuntime = class _DockerRuntime {
|
|
|
7792
8282
|
} catch {
|
|
7793
8283
|
}
|
|
7794
8284
|
}
|
|
8285
|
+
/**
|
|
8286
|
+
* Reclaim BuildKit/classic-builder cache through Docker Engine's native API.
|
|
8287
|
+
*
|
|
8288
|
+
* Build cache belongs to the DAEMON, not to a project: intermediate records
|
|
8289
|
+
* do not reliably retain OpenShip's final-image labels. Callers therefore own
|
|
8290
|
+
* the host-wide policy (bounded automatic retention vs an explicit full
|
|
8291
|
+
* clear). Docker itself excludes records leased by an active build.
|
|
8292
|
+
*
|
|
8293
|
+
* dockerode 4.0.9's `pruneBuilder(opts)` silently discards `opts`, so use its
|
|
8294
|
+
* modem directly until upstream forwards the query parameters.
|
|
8295
|
+
*/
|
|
8296
|
+
async pruneBuildCache(options = {}) {
|
|
8297
|
+
const keepStorageBytes = options.keepStorageBytes;
|
|
8298
|
+
if (keepStorageBytes !== void 0 && (!Number.isSafeInteger(keepStorageBytes) || keepStorageBytes < 0)) {
|
|
8299
|
+
throw new RangeError("keepStorageBytes must be a non-negative safe integer");
|
|
8300
|
+
}
|
|
8301
|
+
const query = {
|
|
8302
|
+
all: options.all ?? false
|
|
8303
|
+
};
|
|
8304
|
+
if (keepStorageBytes !== void 0) query["keep-storage"] = keepStorageBytes;
|
|
8305
|
+
if (options.filters && Object.keys(options.filters).length > 0) {
|
|
8306
|
+
query.filters = options.filters;
|
|
8307
|
+
}
|
|
8308
|
+
const raw = await new Promise((resolve, reject) => {
|
|
8309
|
+
this.docker.modem.dial(
|
|
8310
|
+
{
|
|
8311
|
+
path: "/build/prune?",
|
|
8312
|
+
method: "POST",
|
|
8313
|
+
// `_body: {}` prevents docker-modem from serializing the query object
|
|
8314
|
+
// into an irrelevant POST body while preserving its normal query
|
|
8315
|
+
// encoding (including JSON-encoded filter maps).
|
|
8316
|
+
options: { _query: query, _body: {} },
|
|
8317
|
+
abortSignal: options.abortSignal,
|
|
8318
|
+
statusCodes: {
|
|
8319
|
+
200: true,
|
|
8320
|
+
500: "server error"
|
|
8321
|
+
}
|
|
8322
|
+
},
|
|
8323
|
+
(err, data) => {
|
|
8324
|
+
if (err) reject(err);
|
|
8325
|
+
else resolve(data);
|
|
8326
|
+
}
|
|
8327
|
+
);
|
|
8328
|
+
});
|
|
8329
|
+
const response = raw ?? {};
|
|
8330
|
+
return {
|
|
8331
|
+
cachesDeleted: Array.isArray(response.CachesDeleted) ? response.CachesDeleted.filter((id) => typeof id === "string") : [],
|
|
8332
|
+
spaceReclaimed: typeof response.SpaceReclaimed === "number" && Number.isFinite(response.SpaceReclaimed) && response.SpaceReclaimed >= 0 ? response.SpaceReclaimed : 0
|
|
8333
|
+
};
|
|
8334
|
+
}
|
|
7795
8335
|
/**
|
|
7796
8336
|
* Containers labeled for this deployment, with live state — the reconcile
|
|
7797
8337
|
* read-back. `State` is dockerode's `running | exited | paused | ...`; map it
|