@superblocksteam/sdk 2.0.130-next.3 → 2.0.130-next.5
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/.turbo/turbo-build.log +1 -1
- package/dist/cli-replacement/dependency-install-classifier.d.mts.map +1 -1
- package/dist/cli-replacement/dependency-install-classifier.mjs +11 -0
- package/dist/cli-replacement/dependency-install-classifier.mjs.map +1 -1
- package/dist/cli-replacement/dependency-install-classifier.test.mjs +26 -0
- package/dist/cli-replacement/dependency-install-classifier.test.mjs.map +1 -1
- package/dist/cli-replacement/dev-s3-restore.test.mjs +1 -0
- package/dist/cli-replacement/dev-s3-restore.test.mjs.map +1 -1
- package/dist/cli-replacement/dev-startup-git-before-dbfs-order.test.mjs +1 -0
- package/dist/cli-replacement/dev-startup-git-before-dbfs-order.test.mjs.map +1 -1
- package/dist/cli-replacement/dev.d.mts.map +1 -1
- package/dist/cli-replacement/dev.mjs +70 -4
- package/dist/cli-replacement/dev.mjs.map +1 -1
- package/dist/cli-replacement/disk-space.d.mts +86 -0
- package/dist/cli-replacement/disk-space.d.mts.map +1 -0
- package/dist/cli-replacement/disk-space.mjs +133 -0
- package/dist/cli-replacement/disk-space.mjs.map +1 -0
- package/dist/cli-replacement/disk-space.test.d.mts +2 -0
- package/dist/cli-replacement/disk-space.test.d.mts.map +1 -0
- package/dist/cli-replacement/disk-space.test.mjs +186 -0
- package/dist/cli-replacement/disk-space.test.mjs.map +1 -0
- package/dist/dev-utils/dev-server-metrics.d.mts +11 -0
- package/dist/dev-utils/dev-server-metrics.d.mts.map +1 -1
- package/dist/dev-utils/dev-server-metrics.mjs +18 -0
- package/dist/dev-utils/dev-server-metrics.mjs.map +1 -1
- package/dist/telemetry/memory-metrics.d.ts +7 -0
- package/dist/telemetry/memory-metrics.d.ts.map +1 -1
- package/dist/telemetry/memory-metrics.js +221 -44
- package/dist/telemetry/memory-metrics.js.map +1 -1
- package/dist/telemetry/memory-metrics.test.d.ts +2 -0
- package/dist/telemetry/memory-metrics.test.d.ts.map +1 -0
- package/dist/telemetry/memory-metrics.test.js +256 -0
- package/dist/telemetry/memory-metrics.test.js.map +1 -0
- package/package.json +6 -6
- package/src/cli-replacement/dependency-install-classifier.mts +14 -0
- package/src/cli-replacement/dependency-install-classifier.test.mts +39 -0
- package/src/cli-replacement/dev-s3-restore.test.mts +1 -0
- package/src/cli-replacement/dev-startup-git-before-dbfs-order.test.mts +1 -0
- package/src/cli-replacement/dev.mts +90 -10
- package/src/cli-replacement/disk-space.mts +216 -0
- package/src/cli-replacement/disk-space.test.mts +246 -0
- package/src/dev-utils/dev-server-metrics.mts +20 -0
- package/src/telemetry/memory-metrics.test.ts +363 -0
- package/src/telemetry/memory-metrics.ts +329 -60
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
import { mkdirSync, mkdtempSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { tmpdir } from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
5
|
+
const h = vi.hoisted(() => {
|
|
6
|
+
const gauges = [];
|
|
7
|
+
const observableCounters = [];
|
|
8
|
+
const counters = [];
|
|
9
|
+
const histograms = [];
|
|
10
|
+
return {
|
|
11
|
+
gauges,
|
|
12
|
+
observableCounters,
|
|
13
|
+
counters,
|
|
14
|
+
histograms,
|
|
15
|
+
throwOnGaugeName: undefined,
|
|
16
|
+
delayMonitor: {
|
|
17
|
+
enable: vi.fn(),
|
|
18
|
+
disable: vi.fn(),
|
|
19
|
+
reset: vi.fn(),
|
|
20
|
+
mean: 2_000_000,
|
|
21
|
+
max: 80_000_000,
|
|
22
|
+
percentile: vi.fn((p) => p * 1_000_000),
|
|
23
|
+
},
|
|
24
|
+
gcCallback: undefined,
|
|
25
|
+
elu: vi.fn(),
|
|
26
|
+
addBatchObservableCallback: vi.fn((callback, callbackInstruments) => {
|
|
27
|
+
for (const instrument of callbackInstruments) {
|
|
28
|
+
const captured = [...gauges, ...observableCounters].find((g) => g.name === instrument.name);
|
|
29
|
+
if (captured)
|
|
30
|
+
captured.callback = callback;
|
|
31
|
+
}
|
|
32
|
+
}),
|
|
33
|
+
removeBatchObservableCallback: vi.fn(),
|
|
34
|
+
};
|
|
35
|
+
});
|
|
36
|
+
vi.mock("node:perf_hooks", async () => {
|
|
37
|
+
const actual = await vi.importActual("node:perf_hooks");
|
|
38
|
+
return {
|
|
39
|
+
...actual,
|
|
40
|
+
PerformanceObserver: class {
|
|
41
|
+
constructor(callback) {
|
|
42
|
+
h.gcCallback = callback;
|
|
43
|
+
}
|
|
44
|
+
observe = vi.fn();
|
|
45
|
+
disconnect = vi.fn();
|
|
46
|
+
},
|
|
47
|
+
monitorEventLoopDelay: vi.fn(() => h.delayMonitor),
|
|
48
|
+
performance: {
|
|
49
|
+
...actual.performance,
|
|
50
|
+
eventLoopUtilization: h.elu,
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
});
|
|
54
|
+
vi.mock("./index.js", () => ({
|
|
55
|
+
getMeter: () => ({
|
|
56
|
+
createObservableGauge: (name) => {
|
|
57
|
+
if (h.throwOnGaugeName === name) {
|
|
58
|
+
throw new Error(`createObservableGauge failed for ${name}`);
|
|
59
|
+
}
|
|
60
|
+
const gauge = { name };
|
|
61
|
+
h.gauges.push(gauge);
|
|
62
|
+
return gauge;
|
|
63
|
+
},
|
|
64
|
+
createObservableCounter: (name) => {
|
|
65
|
+
const counter = { name };
|
|
66
|
+
h.observableCounters.push(counter);
|
|
67
|
+
return counter;
|
|
68
|
+
},
|
|
69
|
+
createCounter: (name) => {
|
|
70
|
+
const counter = { name, adds: [] };
|
|
71
|
+
h.counters.push(counter);
|
|
72
|
+
return {
|
|
73
|
+
add: (value, attrs) => counter.adds.push({ value, attrs }),
|
|
74
|
+
};
|
|
75
|
+
},
|
|
76
|
+
createHistogram: (name, options) => {
|
|
77
|
+
const histogram = { name, options, records: [] };
|
|
78
|
+
h.histograms.push(histogram);
|
|
79
|
+
return {
|
|
80
|
+
record: (value, attrs) => histogram.records.push({ value, attrs }),
|
|
81
|
+
};
|
|
82
|
+
},
|
|
83
|
+
addBatchObservableCallback: h.addBatchObservableCallback,
|
|
84
|
+
removeBatchObservableCallback: h.removeBatchObservableCallback,
|
|
85
|
+
}),
|
|
86
|
+
}));
|
|
87
|
+
function observeAll() {
|
|
88
|
+
const observations = [];
|
|
89
|
+
const callback = h.gauges.find((g) => g.callback)?.callback;
|
|
90
|
+
if (!callback)
|
|
91
|
+
throw new Error("missing observable callback");
|
|
92
|
+
callback({
|
|
93
|
+
observe: (gauge, value, attrs) => observations.push({ name: gauge.name, value, attrs }),
|
|
94
|
+
});
|
|
95
|
+
return observations;
|
|
96
|
+
}
|
|
97
|
+
describe("memory metrics", () => {
|
|
98
|
+
beforeEach(async () => {
|
|
99
|
+
vi.resetModules();
|
|
100
|
+
h.gauges.length = 0;
|
|
101
|
+
h.observableCounters.length = 0;
|
|
102
|
+
h.counters.length = 0;
|
|
103
|
+
h.histograms.length = 0;
|
|
104
|
+
h.throwOnGaugeName = undefined;
|
|
105
|
+
h.delayMonitor.enable.mockClear();
|
|
106
|
+
h.delayMonitor.disable.mockClear();
|
|
107
|
+
h.delayMonitor.reset.mockClear();
|
|
108
|
+
h.delayMonitor.percentile.mockClear();
|
|
109
|
+
h.gcCallback = undefined;
|
|
110
|
+
h.elu.mockReset();
|
|
111
|
+
h.elu
|
|
112
|
+
.mockReturnValueOnce({ idle: 0, active: 0, utilization: 0 })
|
|
113
|
+
.mockReturnValueOnce({ idle: 25, active: 75, utilization: 0.75 })
|
|
114
|
+
.mockReturnValue({ idle: 100, active: 200, utilization: 0.67 });
|
|
115
|
+
h.addBatchObservableCallback.mockClear();
|
|
116
|
+
h.removeBatchObservableCallback.mockClear();
|
|
117
|
+
});
|
|
118
|
+
it("observes event-loop, V8 heap, and memory-pressure gauges", async () => {
|
|
119
|
+
const { startMemoryMetrics, stopMemoryMetrics } = await import("./memory-metrics.js");
|
|
120
|
+
startMemoryMetrics();
|
|
121
|
+
const observations = observeAll();
|
|
122
|
+
stopMemoryMetrics();
|
|
123
|
+
expect(h.delayMonitor.enable).toHaveBeenCalledOnce();
|
|
124
|
+
expect(observations.map((o) => o.name)).toEqual(expect.arrayContaining([
|
|
125
|
+
"nodejs.event_loop.delay.max_ms",
|
|
126
|
+
"nodejs.event_loop.delay.p99_ms",
|
|
127
|
+
"nodejs.event_loop.utilization",
|
|
128
|
+
"nodejs.memory.heap_limit_bytes",
|
|
129
|
+
"nodejs.memory.heap_used_ratio",
|
|
130
|
+
]));
|
|
131
|
+
expect(h.observableCounters.map((counter) => counter.name)).toEqual([
|
|
132
|
+
"nodejs.process.cpu.user_seconds",
|
|
133
|
+
"nodejs.process.cpu.system_seconds",
|
|
134
|
+
]);
|
|
135
|
+
expect(observations.find((o) => o.name === "nodejs.event_loop.delay.max_ms")
|
|
136
|
+
?.value).toBe(80);
|
|
137
|
+
expect(observations.find((o) => o.name === "nodejs.event_loop.utilization")
|
|
138
|
+
?.value).toBe(0.75);
|
|
139
|
+
expect(h.delayMonitor.reset).toHaveBeenCalledOnce();
|
|
140
|
+
expect(h.delayMonitor.disable).toHaveBeenCalledOnce();
|
|
141
|
+
});
|
|
142
|
+
it("cleans up the event-loop delay monitor when setup fails", async () => {
|
|
143
|
+
const { startMemoryMetrics, stopMemoryMetrics } = await import("./memory-metrics.js");
|
|
144
|
+
h.throwOnGaugeName = "nodejs.memory.rss_bytes";
|
|
145
|
+
expect(() => startMemoryMetrics()).toThrow("createObservableGauge failed for nodejs.memory.rss_bytes");
|
|
146
|
+
expect(h.delayMonitor.disable).toHaveBeenCalledOnce();
|
|
147
|
+
h.throwOnGaugeName = undefined;
|
|
148
|
+
startMemoryMetrics();
|
|
149
|
+
stopMemoryMetrics();
|
|
150
|
+
expect(h.delayMonitor.enable).toHaveBeenCalledOnce();
|
|
151
|
+
expect(h.delayMonitor.disable).toHaveBeenCalledTimes(2);
|
|
152
|
+
});
|
|
153
|
+
it("resets event-loop delay even when observation fails", async () => {
|
|
154
|
+
const { startMemoryMetrics, stopMemoryMetrics } = await import("./memory-metrics.js");
|
|
155
|
+
startMemoryMetrics();
|
|
156
|
+
const callback = h.gauges.find((g) => g.callback)?.callback;
|
|
157
|
+
if (!callback)
|
|
158
|
+
throw new Error("missing observable callback");
|
|
159
|
+
expect(() => callback({
|
|
160
|
+
observe: (instrument) => {
|
|
161
|
+
if (instrument.name === "nodejs.memory.rss_bytes") {
|
|
162
|
+
throw new Error("observe failed");
|
|
163
|
+
}
|
|
164
|
+
},
|
|
165
|
+
})).toThrow("observe failed");
|
|
166
|
+
stopMemoryMetrics();
|
|
167
|
+
expect(h.delayMonitor.reset).toHaveBeenCalledOnce();
|
|
168
|
+
});
|
|
169
|
+
it("does not register metrics twice until stopped", async () => {
|
|
170
|
+
const { startMemoryMetrics, stopMemoryMetrics } = await import("./memory-metrics.js");
|
|
171
|
+
startMemoryMetrics();
|
|
172
|
+
startMemoryMetrics();
|
|
173
|
+
stopMemoryMetrics();
|
|
174
|
+
startMemoryMetrics();
|
|
175
|
+
stopMemoryMetrics();
|
|
176
|
+
expect(h.delayMonitor.enable).toHaveBeenCalledTimes(2);
|
|
177
|
+
expect(h.addBatchObservableCallback).toHaveBeenCalledTimes(2);
|
|
178
|
+
expect(h.removeBatchObservableCallback).toHaveBeenCalledTimes(2);
|
|
179
|
+
});
|
|
180
|
+
it("records GC count and duration with bounded kind labels", async () => {
|
|
181
|
+
const { startMemoryMetrics, stopMemoryMetrics } = await import("./memory-metrics.js");
|
|
182
|
+
startMemoryMetrics();
|
|
183
|
+
h.gcCallback?.({
|
|
184
|
+
getEntries: () => [
|
|
185
|
+
{ detail: { kind: 1 }, duration: 12 },
|
|
186
|
+
{ detail: { kind: 4 }, duration: 34 },
|
|
187
|
+
{ kind: 999, duration: 56 },
|
|
188
|
+
],
|
|
189
|
+
});
|
|
190
|
+
stopMemoryMetrics();
|
|
191
|
+
const gcCounter = h.counters.find((c) => c.name === "nodejs.gc.count");
|
|
192
|
+
const gcDuration = h.histograms.find((histogram) => histogram.name === "nodejs.gc.duration_ms");
|
|
193
|
+
expect(gcCounter?.adds.map((a) => a.attrs)).toEqual([
|
|
194
|
+
{ kind: "minor" },
|
|
195
|
+
{ kind: "major" },
|
|
196
|
+
{ kind: "unknown" },
|
|
197
|
+
]);
|
|
198
|
+
expect(gcDuration?.records).toEqual([
|
|
199
|
+
{ value: 12, attrs: { kind: "minor" } },
|
|
200
|
+
{ value: 34, attrs: { kind: "major" } },
|
|
201
|
+
{ value: 56, attrs: { kind: "unknown" } },
|
|
202
|
+
]);
|
|
203
|
+
expect(gcDuration?.options).toEqual({
|
|
204
|
+
description: "Garbage collection duration by collection kind",
|
|
205
|
+
unit: "ms",
|
|
206
|
+
advice: {
|
|
207
|
+
explicitBucketBoundaries: [0.5, 1, 2, 5, 10, 25, 50, 100, 250, 500],
|
|
208
|
+
},
|
|
209
|
+
});
|
|
210
|
+
});
|
|
211
|
+
});
|
|
212
|
+
describe("readCgroupMemory", () => {
|
|
213
|
+
beforeEach(() => {
|
|
214
|
+
vi.resetModules();
|
|
215
|
+
});
|
|
216
|
+
it("reads cgroup v2 memory usage and ratio", async () => {
|
|
217
|
+
const root = mkdtempSync(path.join(tmpdir(), "sb-cgroup-"));
|
|
218
|
+
writeFileSync(path.join(root, "memory.current"), "50\n");
|
|
219
|
+
writeFileSync(path.join(root, "memory.max"), "200\n");
|
|
220
|
+
const { readCgroupMemory } = await import("./memory-metrics.js");
|
|
221
|
+
expect(readCgroupMemory(root)).toEqual({
|
|
222
|
+
currentBytes: 50,
|
|
223
|
+
limitBytes: 200,
|
|
224
|
+
usageRatio: 0.25,
|
|
225
|
+
});
|
|
226
|
+
});
|
|
227
|
+
it("returns undefined when cgroup files are absent or unlimited", async () => {
|
|
228
|
+
const root = mkdtempSync(path.join(tmpdir(), "sb-cgroup-"));
|
|
229
|
+
const { readCgroupMemory } = await import("./memory-metrics.js");
|
|
230
|
+
expect(readCgroupMemory(root)).toBeUndefined();
|
|
231
|
+
writeFileSync(path.join(root, "memory.current"), "50\n");
|
|
232
|
+
writeFileSync(path.join(root, "memory.max"), "max\n");
|
|
233
|
+
expect(readCgroupMemory(root)).toBeUndefined();
|
|
234
|
+
});
|
|
235
|
+
it("does not combine v2 usage with v1 limits", async () => {
|
|
236
|
+
const root = mkdtempSync(path.join(tmpdir(), "sb-cgroup-"));
|
|
237
|
+
const memoryRoot = path.join(root, "memory");
|
|
238
|
+
mkdirSync(memoryRoot);
|
|
239
|
+
writeFileSync(path.join(root, "memory.current"), "50\n");
|
|
240
|
+
writeFileSync(path.join(root, "memory.max"), "max\n");
|
|
241
|
+
writeFileSync(path.join(memoryRoot, "memory.usage_in_bytes"), "60\n");
|
|
242
|
+
writeFileSync(path.join(memoryRoot, "memory.limit_in_bytes"), "200\n");
|
|
243
|
+
const { readCgroupMemory } = await import("./memory-metrics.js");
|
|
244
|
+
expect(readCgroupMemory(root)).toBeUndefined();
|
|
245
|
+
});
|
|
246
|
+
it("treats cgroup v1 sentinel limits as unlimited", async () => {
|
|
247
|
+
const root = mkdtempSync(path.join(tmpdir(), "sb-cgroup-"));
|
|
248
|
+
const memoryRoot = path.join(root, "memory");
|
|
249
|
+
mkdirSync(memoryRoot);
|
|
250
|
+
writeFileSync(path.join(memoryRoot, "memory.usage_in_bytes"), "50\n");
|
|
251
|
+
writeFileSync(path.join(memoryRoot, "memory.limit_in_bytes"), "9223372036854771712\n");
|
|
252
|
+
const { readCgroupMemory } = await import("./memory-metrics.js");
|
|
253
|
+
expect(readCgroupMemory(root)).toBeUndefined();
|
|
254
|
+
});
|
|
255
|
+
});
|
|
256
|
+
//# sourceMappingURL=memory-metrics.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memory-metrics.test.js","sourceRoot":"","sources":["../../src/telemetry/memory-metrics.test.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@superblocksteam/sdk",
|
|
3
|
-
"version": "2.0.130-next.
|
|
3
|
+
"version": "2.0.130-next.5",
|
|
4
4
|
"description": "Superblocks JS SDK",
|
|
5
5
|
"homepage": "https://www.superblocks.com",
|
|
6
6
|
"license": "Superblocks Community Software License",
|
|
@@ -48,11 +48,11 @@
|
|
|
48
48
|
"vite-tsconfig-paths": "^6.0.4",
|
|
49
49
|
"winston": "^3.17.0",
|
|
50
50
|
"yaml": "^2.7.1",
|
|
51
|
-
"@superblocksteam/
|
|
52
|
-
"@superblocksteam/shared": "0.
|
|
53
|
-
"@superblocksteam/telemetry": "2.0.130-next.
|
|
54
|
-
"@superblocksteam/util": "2.0.130-next.
|
|
55
|
-
"@superblocksteam/vite-plugin-file-sync": "2.0.130-next.
|
|
51
|
+
"@superblocksteam/shared": "0.9592.1",
|
|
52
|
+
"@superblocksteam/library-shared": "2.0.130-next.5",
|
|
53
|
+
"@superblocksteam/telemetry": "2.0.130-next.5",
|
|
54
|
+
"@superblocksteam/util": "2.0.130-next.5",
|
|
55
|
+
"@superblocksteam/vite-plugin-file-sync": "2.0.130-next.5"
|
|
56
56
|
},
|
|
57
57
|
"devDependencies": {
|
|
58
58
|
"@eslint/js": "^9.39.2",
|
|
@@ -9,6 +9,8 @@ import {
|
|
|
9
9
|
type ParseContext,
|
|
10
10
|
} from "@superblocksteam/vite-plugin-file-sync/npm-registry";
|
|
11
11
|
|
|
12
|
+
import { isDiskFullError } from "./disk-space.mjs";
|
|
13
|
+
|
|
12
14
|
/** Marker thrown by the INITIAL verification install so the dev.mts catch can
|
|
13
15
|
* degrade by ORIGIN (never on classifiability). Carries the classified error. */
|
|
14
16
|
export class InitialInstallFailed extends Error {
|
|
@@ -90,6 +92,18 @@ export function classifyInitialInstallError(
|
|
|
90
92
|
if (blockedFromJson) return fromBlocked(blockedFromJson, rawError);
|
|
91
93
|
|
|
92
94
|
const combined = `${stdout}\n${stderr}`;
|
|
95
|
+
|
|
96
|
+
if (isDiskFullError({ stdout, stderr, message: failure.message })) {
|
|
97
|
+
return {
|
|
98
|
+
type: "dev-server/dependency-install",
|
|
99
|
+
timestamp: new Date().toISOString(),
|
|
100
|
+
category: "disk_full",
|
|
101
|
+
npmErrorCode: "ENOSPC",
|
|
102
|
+
hasAnyRegistryConfigured: ctx.hasAnyRegistryConfigured,
|
|
103
|
+
rawError,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
|
|
93
107
|
const codeMatch = combined.match(DEP_CONFLICT_CODES);
|
|
94
108
|
if (codeMatch) {
|
|
95
109
|
return {
|
|
@@ -47,6 +47,45 @@ describe("classifyInitialInstallError", () => {
|
|
|
47
47
|
expect(e.registryHost).toBe("reg.corp");
|
|
48
48
|
});
|
|
49
49
|
|
|
50
|
+
it("classifies an ENOSPC --json envelope as disk_full", () => {
|
|
51
|
+
const ENOSPC_JSON = JSON.stringify({
|
|
52
|
+
error: {
|
|
53
|
+
code: "ENOSPC",
|
|
54
|
+
summary: "ENOSPC: no space left on device, write",
|
|
55
|
+
detail: "nospc ENOSPC: no space left on device, write",
|
|
56
|
+
},
|
|
57
|
+
});
|
|
58
|
+
const e = classifyInitialInstallError(
|
|
59
|
+
{ stdout: ENOSPC_JSON, stderr: "npm error code ENOSPC", message: "x" },
|
|
60
|
+
{ hasAnyRegistryConfigured: true, requestedPackages: [] },
|
|
61
|
+
);
|
|
62
|
+
expect(e.category).toBe("disk_full");
|
|
63
|
+
expect(e.npmErrorCode).toBe("ENOSPC");
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it("classifies a bare 'no space left on device' stderr as disk_full", () => {
|
|
67
|
+
const e = classifyInitialInstallError(
|
|
68
|
+
{
|
|
69
|
+
stdout: "not json",
|
|
70
|
+
stderr:
|
|
71
|
+
"npm error code ENOSPC\nnpm error syscall write\nnpm error errno -28\nnpm error nospc ENOSPC: no space left on device, write",
|
|
72
|
+
message: "Command failed",
|
|
73
|
+
},
|
|
74
|
+
{},
|
|
75
|
+
);
|
|
76
|
+
expect(e.category).toBe("disk_full");
|
|
77
|
+
expect(e.npmErrorCode).toBe("ENOSPC");
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it("classifies a bare 'errno -28' (no ENOSPC code name) as disk_full", () => {
|
|
81
|
+
const e = classifyInitialInstallError(
|
|
82
|
+
{ stdout: "", stderr: "npm error errno -28", message: "Command failed" },
|
|
83
|
+
{},
|
|
84
|
+
);
|
|
85
|
+
expect(e.category).toBe("disk_full");
|
|
86
|
+
expect(e.npmErrorCode).toBe("ENOSPC");
|
|
87
|
+
});
|
|
88
|
+
|
|
50
89
|
it("falls back to unknown for an unmapped code (still a DependencyInstallError)", () => {
|
|
51
90
|
const e = classifyInitialInstallError(
|
|
52
91
|
{
|
|
@@ -115,6 +115,7 @@ vi.mock("@superblocksteam/util", () => ({
|
|
|
115
115
|
vi.mock("@superblocksteam/vite-plugin-file-sync/ai-service", () => ({
|
|
116
116
|
AiService: class {
|
|
117
117
|
initialize = vi.fn(async () => undefined);
|
|
118
|
+
notifyMigrationAppArtifactsReady = vi.fn();
|
|
118
119
|
removeIntegrationCache = vi.fn(async () => undefined);
|
|
119
120
|
chatSessionStore = { invalidateCache: vi.fn() };
|
|
120
121
|
// `dev.mts` reads this at startup for two paths: `syncHomeNpmrc`
|
|
@@ -115,6 +115,7 @@ vi.mock("@superblocksteam/util", () => ({
|
|
|
115
115
|
vi.mock("@superblocksteam/vite-plugin-file-sync/ai-service", () => ({
|
|
116
116
|
AiService: class {
|
|
117
117
|
initialize = vi.fn(async () => undefined);
|
|
118
|
+
notifyMigrationAppArtifactsReady = vi.fn();
|
|
118
119
|
removeIntegrationCache = vi.fn(async () => undefined);
|
|
119
120
|
chatSessionStore = { invalidateCache: vi.fn() };
|
|
120
121
|
getNpmRegistryClient = vi.fn(() => undefined);
|
|
@@ -70,6 +70,7 @@ import {
|
|
|
70
70
|
classifyInitialInstallError,
|
|
71
71
|
InitialInstallFailed,
|
|
72
72
|
} from "./dependency-install-classifier.mjs";
|
|
73
|
+
import { getDiskSpace, recoverFromDiskFull } from "./disk-space.mjs";
|
|
73
74
|
import {
|
|
74
75
|
ensureRemoteHasDefaultBranch,
|
|
75
76
|
getGitErrorFields,
|
|
@@ -342,6 +343,12 @@ export async function installPackages(
|
|
|
342
343
|
logger: Logger,
|
|
343
344
|
npmRegistryClient?: NpmRegistryClient,
|
|
344
345
|
) {
|
|
346
|
+
// Hoisted so the catch's ENOSPC recovery can re-run the EXACT same install
|
|
347
|
+
// (same agent, args, env, --ignore-scripts policy) after reclaiming disk
|
|
348
|
+
// space, rather than reconstructing — and risking diverging from — it.
|
|
349
|
+
let runInstall:
|
|
350
|
+
| (() => Promise<{ stdout: string; stderr: string }>)
|
|
351
|
+
| undefined;
|
|
345
352
|
try {
|
|
346
353
|
const pm = await detect({
|
|
347
354
|
strategies: [
|
|
@@ -445,25 +452,89 @@ export async function installPackages(
|
|
|
445
452
|
// (`category: "unknown"`, `npmErrorCode: undefined` from real npm against a
|
|
446
453
|
// missing `/tmp/app`).
|
|
447
454
|
const exec = promisify(child_process.exec);
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
455
|
+
// Re-runnable so the ENOSPC recovery path below can retry the EXACT same
|
|
456
|
+
// install after reclaiming disk space.
|
|
457
|
+
runInstall = () =>
|
|
458
|
+
exec(`${command} ${args.join(" ")}`, {
|
|
459
|
+
cwd,
|
|
460
|
+
env: buildInstallEnv(superblocksNpmrcPath(), logsDir),
|
|
461
|
+
});
|
|
462
|
+
|
|
463
|
+
// Cheap `df` snapshot before the install (statfs only — no directory walk
|
|
464
|
+
// on the happy path). On a 3Gi live-edit PVC this is the baseline that
|
|
465
|
+
// makes an ENOSPC mid-install interpretable after the fact.
|
|
466
|
+
const diskBefore = await getDiskSpace(cwd);
|
|
467
|
+
if (diskBefore) {
|
|
468
|
+
logger.info(
|
|
469
|
+
`Disk usage before package install [freeBytes=${diskBefore.freeBytes} totalBytes=${diskBefore.totalBytes} percentUsed=${diskBefore.percentUsed}] cwd=${cwd}`,
|
|
470
|
+
{ disk: diskBefore },
|
|
471
|
+
);
|
|
472
|
+
} else {
|
|
473
|
+
logger.warn(
|
|
474
|
+
`Could not read disk usage before package install (statfs failed) cwd=${cwd}`,
|
|
475
|
+
);
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
const { stdout } = await runInstall();
|
|
452
479
|
logger.info("Package installation completed successfully");
|
|
453
480
|
logger.info(stdout);
|
|
454
481
|
} catch (error) {
|
|
455
|
-
logger.error("Error during package installation", getErrorMeta(error));
|
|
456
482
|
// `util.promisify(child_process.exec)` preserves `.stdout`/`.stderr` on
|
|
457
483
|
// the rejected error separately; with `--json`, the structured npm error
|
|
458
484
|
// envelope lands on `.stdout`. Classify it into a DependencyInstallError
|
|
459
485
|
// and throw the `InitialInstallFailed` marker so the `dev()` catch can
|
|
460
486
|
// degrade by origin rather than crash-looping the dev-server pod.
|
|
461
487
|
const ctx = await buildInstallParseContext(cwd, npmRegistryClient);
|
|
462
|
-
const
|
|
463
|
-
|
|
464
|
-
{ stdout: f.stdout, stderr: f.stderr, message: f.message }
|
|
465
|
-
|
|
466
|
-
);
|
|
488
|
+
const toFailure = (e: unknown) => {
|
|
489
|
+
const f = e as { stdout?: string; stderr?: string; message?: string };
|
|
490
|
+
return { stdout: f.stdout, stderr: f.stderr, message: f.message };
|
|
491
|
+
};
|
|
492
|
+
let serverError = classifyInitialInstallError(toFailure(error), ctx);
|
|
493
|
+
// The error whose meta we log on the way out — kept in lockstep with the
|
|
494
|
+
// (possibly reclassified) `serverError` we throw, so operational logs and
|
|
495
|
+
// the user-visible error never disagree.
|
|
496
|
+
let errorToLog: unknown = error;
|
|
497
|
+
|
|
498
|
+
// ENOSPC: the live-edit volume filled up mid-install. Try to recover by
|
|
499
|
+
// reclaiming regenerable build artifacts (and, if that's not enough, the
|
|
500
|
+
// half-written node_modules) and retrying the install before giving up.
|
|
501
|
+
// The recovery logs each tier's df + freed bytes, which doubles as the
|
|
502
|
+
// only autopsy we can get of a PVC that `reclaimPolicy: Delete` destroys
|
|
503
|
+
// at teardown. `runInstall` is only set once the command was resolved; if
|
|
504
|
+
// the failure happened before that, there's nothing to retry.
|
|
505
|
+
if (serverError.category === "disk_full" && runInstall) {
|
|
506
|
+
let recovery: Awaited<ReturnType<typeof recoverFromDiskFull>> | undefined;
|
|
507
|
+
try {
|
|
508
|
+
recovery = await recoverFromDiskFull(cwd, runInstall, logger);
|
|
509
|
+
} catch (recoveryErr) {
|
|
510
|
+
// Recovery is best-effort; an unexpected throw must never escape as a
|
|
511
|
+
// raw error (that would bypass the InitialInstallFailed contract the
|
|
512
|
+
// dev() catch relies on). Swallow it and surface the original failure.
|
|
513
|
+
logger.warn("Disk recovery threw unexpectedly", {
|
|
514
|
+
error: String(recoveryErr),
|
|
515
|
+
});
|
|
516
|
+
}
|
|
517
|
+
if (recovery) {
|
|
518
|
+
devServerMetrics.recordDiskRecovery({ recovered: recovery.recovered });
|
|
519
|
+
if (recovery.recovered) {
|
|
520
|
+
logger.info(
|
|
521
|
+
"Package installation completed successfully after disk recovery",
|
|
522
|
+
);
|
|
523
|
+
return;
|
|
524
|
+
}
|
|
525
|
+
// Reclassify the final attempt so the surfaced error reflects the last
|
|
526
|
+
// npm output (still disk_full if genuinely out of space).
|
|
527
|
+
if (recovery.lastError !== undefined) {
|
|
528
|
+
errorToLog = recovery.lastError;
|
|
529
|
+
serverError = classifyInitialInstallError(
|
|
530
|
+
toFailure(recovery.lastError),
|
|
531
|
+
ctx,
|
|
532
|
+
);
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
logger.error("Error during package installation", getErrorMeta(errorToLog));
|
|
467
538
|
throw new InitialInstallFailed(serverError);
|
|
468
539
|
}
|
|
469
540
|
}
|
|
@@ -968,6 +1039,7 @@ export async function dev(options: {
|
|
|
968
1039
|
logger,
|
|
969
1040
|
tokenManager,
|
|
970
1041
|
features: AiServiceFeatureFlags.create(flagBootstrap),
|
|
1042
|
+
flagBootstrap,
|
|
971
1043
|
pluginExecutionVersions:
|
|
972
1044
|
currentUser.organizations[0].pluginExecutionVersions,
|
|
973
1045
|
});
|
|
@@ -983,6 +1055,10 @@ export async function dev(options: {
|
|
|
983
1055
|
},
|
|
984
1056
|
);
|
|
985
1057
|
|
|
1058
|
+
if (uploadFirst) {
|
|
1059
|
+
aiService?.notifyMigrationAppArtifactsReady();
|
|
1060
|
+
}
|
|
1061
|
+
|
|
986
1062
|
logger.info(
|
|
987
1063
|
`[dev-startup] Starting git sync (${downloadFirst ? "download" : "upload"}-first) on branch '${activeDbfsBranchName}'`,
|
|
988
1064
|
);
|
|
@@ -1174,6 +1250,10 @@ export async function dev(options: {
|
|
|
1174
1250
|
logger.info("[dev-startup] Skipping download, already in sync");
|
|
1175
1251
|
}
|
|
1176
1252
|
|
|
1253
|
+
if (downloadFirst) {
|
|
1254
|
+
aiService?.notifyMigrationAppArtifactsReady();
|
|
1255
|
+
}
|
|
1256
|
+
|
|
1177
1257
|
// Unconditional lockfile sanitation (APPS-4300): strip
|
|
1178
1258
|
// cross-registry `resolved` URLs from any lockfile on disk
|
|
1179
1259
|
// regardless of whether install runs next. The lockfile here is
|