@vercel/container 22.0.2 → 23.0.0
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/buildah-store-command.js +418 -0
- package/dist/index.js +1107 -520
- package/dist/schedules-command-dispatcher +0 -0
- package/package.json +6 -5
package/dist/index.js
CHANGED
|
@@ -38,7 +38,43 @@ __export(src_exports, {
|
|
|
38
38
|
version: () => version
|
|
39
39
|
});
|
|
40
40
|
module.exports = __toCommonJS(src_exports);
|
|
41
|
-
var
|
|
41
|
+
var import_build_utils8 = require("@vercel/build-utils");
|
|
42
|
+
|
|
43
|
+
// src/schedules-command.ts
|
|
44
|
+
var import_promises = require("fs/promises");
|
|
45
|
+
var import_node_os = require("os");
|
|
46
|
+
var import_node_path = require("path");
|
|
47
|
+
var SCHEDULES_COMMAND_ENV = "VERCEL_SCHEDULE_COMMAND";
|
|
48
|
+
var DISPATCHER = "schedules-command-dispatcher";
|
|
49
|
+
async function buildSchedulesCommandImage(engine, params, dispatcher = (0, import_node_path.join)(__dirname, DISPATCHER)) {
|
|
50
|
+
await engine.build(params);
|
|
51
|
+
if ((await engine.onBuildTriggers?.(params.imageRef))?.length) {
|
|
52
|
+
throw new Error(
|
|
53
|
+
`"${params.dockerfilePath}" declares ONBUILD instructions, which only run when another image is built from it. Remove them from the schedule's Dockerfile.`
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
const contextDir = await (0, import_promises.mkdtemp)((0, import_node_path.join)((0, import_node_os.tmpdir)(), "vercel-schedules-image-"));
|
|
57
|
+
try {
|
|
58
|
+
await (0, import_promises.copyFile)(dispatcher, (0, import_node_path.join)(contextDir, DISPATCHER));
|
|
59
|
+
const dockerfilePath = (0, import_node_path.join)(contextDir, "Dockerfile");
|
|
60
|
+
await (0, import_promises.writeFile)(
|
|
61
|
+
dockerfilePath,
|
|
62
|
+
[
|
|
63
|
+
`FROM ${params.imageRef}`,
|
|
64
|
+
`COPY --chmod=0555 ${DISPATCHER} /__vercel_runtime/${DISPATCHER}`,
|
|
65
|
+
`ENTRYPOINT ["/__vercel_runtime/${DISPATCHER}"]`
|
|
66
|
+
].join("\n")
|
|
67
|
+
);
|
|
68
|
+
await engine.build({
|
|
69
|
+
...params,
|
|
70
|
+
contextDir,
|
|
71
|
+
dockerfilePath,
|
|
72
|
+
buildArgs: void 0
|
|
73
|
+
});
|
|
74
|
+
} finally {
|
|
75
|
+
await (0, import_promises.rm)(contextDir, { recursive: true, force: true });
|
|
76
|
+
}
|
|
77
|
+
}
|
|
42
78
|
|
|
43
79
|
// src/diagnostics.ts
|
|
44
80
|
var import_build_utils = require("@vercel/build-utils");
|
|
@@ -63,14 +99,15 @@ var diagnostics = (0, import_build_utils.createDiagnostics)("container");
|
|
|
63
99
|
|
|
64
100
|
// src/index.ts
|
|
65
101
|
var import_node_fs16 = require("fs");
|
|
66
|
-
var
|
|
102
|
+
var import_node_path22 = __toESM(require("path"));
|
|
67
103
|
|
|
68
104
|
// src/util.ts
|
|
69
105
|
var import_build_utils2 = require("@vercel/build-utils");
|
|
70
106
|
var import_node_child_process = require("child_process");
|
|
71
107
|
var import_node_fs = require("fs");
|
|
72
|
-
var
|
|
73
|
-
var
|
|
108
|
+
var import_promises2 = require("fs/promises");
|
|
109
|
+
var import_node_os2 = require("os");
|
|
110
|
+
var import_node_path2 = require("path");
|
|
74
111
|
var DEBUG = Boolean((0, import_build_utils2.getPlatformEnv)("BUILDER_DEBUG"));
|
|
75
112
|
function normalizeCommand(command) {
|
|
76
113
|
if (typeof command === "string") {
|
|
@@ -105,11 +142,23 @@ function debug(message) {
|
|
|
105
142
|
function elapsed(since) {
|
|
106
143
|
return `${((Date.now() - since) / 1e3).toFixed(1)}s`;
|
|
107
144
|
}
|
|
145
|
+
function formatBytes(bytes) {
|
|
146
|
+
if (bytes < 1024)
|
|
147
|
+
return `${bytes} B`;
|
|
148
|
+
const units = ["KiB", "MiB", "GiB"];
|
|
149
|
+
let value = bytes / 1024;
|
|
150
|
+
let unit = 0;
|
|
151
|
+
while (value >= 1024 && unit < units.length - 1) {
|
|
152
|
+
value /= 1024;
|
|
153
|
+
unit += 1;
|
|
154
|
+
}
|
|
155
|
+
return `${value.toFixed(value >= 10 ? 0 : 1)} ${units[unit]}`;
|
|
156
|
+
}
|
|
108
157
|
function shortDigest(digest) {
|
|
109
158
|
return digest.startsWith("sha256:") ? `${digest.slice(0, 19)}\u2026` : digest;
|
|
110
159
|
}
|
|
111
160
|
function delay(ms) {
|
|
112
|
-
return new Promise((
|
|
161
|
+
return new Promise((resolve5) => setTimeout(resolve5, ms));
|
|
113
162
|
}
|
|
114
163
|
async function withSpan(parent, name, attrs, fn) {
|
|
115
164
|
if (!parent) {
|
|
@@ -124,21 +173,21 @@ function readString(value) {
|
|
|
124
173
|
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
125
174
|
}
|
|
126
175
|
function isDockerfileRef(ref) {
|
|
127
|
-
return /(docker|container)file/i.test((0,
|
|
176
|
+
return /(docker|container)file/i.test((0, import_node_path2.basename)(ref));
|
|
128
177
|
}
|
|
129
178
|
var DOCKERFILE_CANDIDATES = [
|
|
130
179
|
"Dockerfile.vercel",
|
|
131
180
|
"Containerfile.vercel"
|
|
132
181
|
];
|
|
133
182
|
function findDockerfile(workPath) {
|
|
134
|
-
return DOCKERFILE_CANDIDATES.find((name) => (0, import_node_fs.existsSync)((0,
|
|
183
|
+
return DOCKERFILE_CANDIDATES.find((name) => (0, import_node_fs.existsSync)((0, import_node_path2.join)(workPath, name)));
|
|
135
184
|
}
|
|
136
185
|
function devImageTag(serviceName) {
|
|
137
186
|
const safe = serviceName.toLowerCase().replace(/[^a-z0-9-_.]/g, "-");
|
|
138
187
|
return `vercel-dev/${safe || "service"}:dev`;
|
|
139
188
|
}
|
|
140
189
|
function run(cmd, args, opts = {}) {
|
|
141
|
-
return new Promise((
|
|
190
|
+
return new Promise((resolve5, reject) => {
|
|
142
191
|
const child = (0, import_node_child_process.spawn)(cmd, args, {
|
|
143
192
|
cwd: opts.cwd,
|
|
144
193
|
env: opts.env,
|
|
@@ -173,7 +222,7 @@ function run(cmd, args, opts = {}) {
|
|
|
173
222
|
});
|
|
174
223
|
child.on("close", (code) => {
|
|
175
224
|
if (code === 0) {
|
|
176
|
-
|
|
225
|
+
resolve5({ stdout, stderr });
|
|
177
226
|
} else {
|
|
178
227
|
const detail = stderr.trim().split("\n").slice(-5).join("\n");
|
|
179
228
|
const error = new Error(
|
|
@@ -189,6 +238,23 @@ ${detail}` : "")
|
|
|
189
238
|
}
|
|
190
239
|
});
|
|
191
240
|
}
|
|
241
|
+
async function hasCommand(name) {
|
|
242
|
+
try {
|
|
243
|
+
await run("which", [name], { quiet: true });
|
|
244
|
+
return true;
|
|
245
|
+
} catch {
|
|
246
|
+
return false;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
async function lstatIfPresent(path4) {
|
|
250
|
+
try {
|
|
251
|
+
return await (0, import_promises2.lstat)(path4);
|
|
252
|
+
} catch (error) {
|
|
253
|
+
if (error.code === "ENOENT")
|
|
254
|
+
return void 0;
|
|
255
|
+
throw error;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
192
258
|
function extractField(text, label) {
|
|
193
259
|
const match = text.match(new RegExp(`^\\s*${label}:\\s*(.+)$`, "m"));
|
|
194
260
|
return match?.[1]?.trim();
|
|
@@ -202,44 +268,130 @@ function existingRegistryAuthFile() {
|
|
|
202
268
|
return (0, import_node_fs.existsSync)(explicit) ? explicit : void 0;
|
|
203
269
|
}
|
|
204
270
|
const fromXdg = readString(process.env.XDG_CONFIG_HOME);
|
|
205
|
-
const configHome = fromXdg || (0,
|
|
206
|
-
const defaultPath = (0,
|
|
271
|
+
const configHome = fromXdg || (0, import_node_path2.join)((0, import_node_os2.homedir)(), ".config");
|
|
272
|
+
const defaultPath = (0, import_node_path2.join)(configHome, "containers", "auth.json");
|
|
207
273
|
return (0, import_node_fs.existsSync)(defaultPath) ? defaultPath : void 0;
|
|
208
274
|
}
|
|
209
275
|
|
|
210
276
|
// src/engines/buildah.ts
|
|
277
|
+
var import_build_utils3 = require("@vercel/build-utils");
|
|
278
|
+
var import_node_crypto3 = require("crypto");
|
|
211
279
|
var import_node_fs3 = require("fs");
|
|
280
|
+
var import_promises5 = require("fs/promises");
|
|
281
|
+
var import_node_os5 = require("os");
|
|
282
|
+
var import_node_path5 = require("path");
|
|
283
|
+
|
|
284
|
+
// src/buildah-store-cache/state.ts
|
|
285
|
+
var import_node_crypto = require("crypto");
|
|
286
|
+
var import_promises3 = require("fs/promises");
|
|
212
287
|
var import_node_os3 = require("os");
|
|
213
288
|
var import_node_path3 = require("path");
|
|
214
289
|
|
|
290
|
+
// src/buildah-store-cache/config.ts
|
|
291
|
+
var BUILDAH_STORE_CACHE_FLAG = "VERCEL_BUILDAH_STORE_CACHE";
|
|
292
|
+
function buildahStoreCacheRequested() {
|
|
293
|
+
return isBuildContainer() && process.env[BUILDAH_STORE_CACHE_FLAG] === "1";
|
|
294
|
+
}
|
|
295
|
+
function buildahStoreCacheEnabled() {
|
|
296
|
+
return buildahStoreCacheRequested() && deploymentId() !== void 0;
|
|
297
|
+
}
|
|
298
|
+
function deploymentId() {
|
|
299
|
+
return readString(process.env.VERCEL_DEPLOYMENT_ID);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// src/buildah-store-cache/state.ts
|
|
303
|
+
var CLEANUP_CONTAINER_RE = /^vercel-cnb-[a-zA-Z0-9_.-]+$/;
|
|
304
|
+
function buildahStoreStateDirectory() {
|
|
305
|
+
const deployment = deploymentId();
|
|
306
|
+
if (!deployment) {
|
|
307
|
+
throw new Error("VERCEL_DEPLOYMENT_ID is not set");
|
|
308
|
+
}
|
|
309
|
+
return (0, import_node_path3.join)(
|
|
310
|
+
(0, import_node_os3.tmpdir)(),
|
|
311
|
+
"vercel-buildah-store",
|
|
312
|
+
deployment.replace(/[^a-zA-Z0-9_-]/g, "_")
|
|
313
|
+
);
|
|
314
|
+
}
|
|
315
|
+
async function createBuildahStoreStateDirectory() {
|
|
316
|
+
const directory = buildahStoreStateDirectory();
|
|
317
|
+
await (0, import_promises3.mkdir)(directory, { recursive: true, mode: 448 });
|
|
318
|
+
return directory;
|
|
319
|
+
}
|
|
320
|
+
async function writeStateRecord(suffix, contents) {
|
|
321
|
+
const path4 = (0, import_node_path3.join)(
|
|
322
|
+
await createBuildahStoreStateDirectory(),
|
|
323
|
+
`${(0, import_node_crypto.randomUUID)()}${suffix}`
|
|
324
|
+
);
|
|
325
|
+
await (0, import_promises3.writeFile)(path4, contents, { mode: 384, flag: "wx" });
|
|
326
|
+
return path4;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// src/buildah-store-cache/usage.ts
|
|
330
|
+
var import_promises4 = require("fs/promises");
|
|
331
|
+
function imageIdsFromBuildOutput(output) {
|
|
332
|
+
const ids = /* @__PURE__ */ new Set();
|
|
333
|
+
for (const match of output.matchAll(
|
|
334
|
+
/--> (?:Using cache )?([a-f0-9]{12,64})[ \t\r]*$/gm
|
|
335
|
+
)) {
|
|
336
|
+
ids.add(match[1]);
|
|
337
|
+
}
|
|
338
|
+
return [...ids];
|
|
339
|
+
}
|
|
340
|
+
var BuildahImageUsage = class _BuildahImageUsage {
|
|
341
|
+
constructor(path4) {
|
|
342
|
+
this.path = path4;
|
|
343
|
+
}
|
|
344
|
+
static async begin() {
|
|
345
|
+
if (!buildahStoreCacheEnabled()) {
|
|
346
|
+
return void 0;
|
|
347
|
+
}
|
|
348
|
+
try {
|
|
349
|
+
return new _BuildahImageUsage(
|
|
350
|
+
await writeStateRecord(".images", "pending\n")
|
|
351
|
+
);
|
|
352
|
+
} catch (error) {
|
|
353
|
+
debug(
|
|
354
|
+
`could not record Buildah image usage: ${error.message}`
|
|
355
|
+
);
|
|
356
|
+
return void 0;
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
/** A failure leaves the record pending, so the save stops instead of pruning blindly. */
|
|
360
|
+
async record(images) {
|
|
361
|
+
try {
|
|
362
|
+
const ids = (await images()).map((id) => id.replace(/^sha256:/, ""));
|
|
363
|
+
if (ids.length === 0) {
|
|
364
|
+
throw new Error("no Buildah image IDs were recorded");
|
|
365
|
+
}
|
|
366
|
+
const temporary = `${this.path}.tmp`;
|
|
367
|
+
await (0, import_promises4.writeFile)(temporary, `${ids.join("\n")}
|
|
368
|
+
`, { mode: 384 });
|
|
369
|
+
await (0, import_promises4.rename)(temporary, this.path);
|
|
370
|
+
} catch (error) {
|
|
371
|
+
debug(
|
|
372
|
+
`could not record Buildah image usage: ${error.message}`
|
|
373
|
+
);
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
};
|
|
377
|
+
|
|
215
378
|
// src/storage-driver.ts
|
|
216
|
-
var import_node_child_process2 = require("child_process");
|
|
217
379
|
var import_node_fs2 = require("fs");
|
|
218
|
-
var
|
|
219
|
-
var
|
|
380
|
+
var import_node_os4 = require("os");
|
|
381
|
+
var import_node_path4 = require("path");
|
|
220
382
|
var BUILDAH_GRAPH_ROOT = "/vercel/.containers/storage";
|
|
221
383
|
var BUILDAH_RUN_ROOT = "/run/containers/storage";
|
|
222
384
|
var REQUIRED_BUILD_CONTAINER_DRIVER = "overlay";
|
|
223
|
-
async function hasBinary(name) {
|
|
224
|
-
try {
|
|
225
|
-
await run("which", [name], { quiet: true });
|
|
226
|
-
return true;
|
|
227
|
-
} catch {
|
|
228
|
-
return false;
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
385
|
var cachedStorageDriver;
|
|
232
386
|
function selectStorageDriver() {
|
|
233
387
|
if (!cachedStorageDriver) {
|
|
234
388
|
cachedStorageDriver = (async () => {
|
|
235
389
|
const override = readString(process.env.VERCEL_VCR_DOCKER_STORAGE_DRIVER);
|
|
236
|
-
if (override)
|
|
390
|
+
if (override)
|
|
237
391
|
return override;
|
|
238
|
-
|
|
239
|
-
if (isBuildContainer()) {
|
|
392
|
+
if (isBuildContainer())
|
|
240
393
|
return void 0;
|
|
241
|
-
|
|
242
|
-
if (await hasBinary("fuse-overlayfs") && (0, import_node_fs2.existsSync)("/dev/fuse")) {
|
|
394
|
+
if (await hasCommand("fuse-overlayfs") && (0, import_node_fs2.existsSync)("/dev/fuse")) {
|
|
243
395
|
return "fuse-overlayfs";
|
|
244
396
|
}
|
|
245
397
|
return "vfs";
|
|
@@ -253,8 +405,8 @@ short-name-mode = "permissive"
|
|
|
253
405
|
var cachedRegistriesConfPath;
|
|
254
406
|
function buildahRegistriesConfPath() {
|
|
255
407
|
if (!cachedRegistriesConfPath) {
|
|
256
|
-
const dir = (0, import_node_fs2.mkdtempSync)((0,
|
|
257
|
-
cachedRegistriesConfPath = (0,
|
|
408
|
+
const dir = (0, import_node_fs2.mkdtempSync)((0, import_node_path4.join)((0, import_node_os4.tmpdir)(), "vercel-container-registries-"));
|
|
409
|
+
cachedRegistriesConfPath = (0, import_node_path4.join)(dir, "registries.conf");
|
|
258
410
|
(0, import_node_fs2.writeFileSync)(cachedRegistriesConfPath, BUILDAH_REGISTRIES_CONF);
|
|
259
411
|
}
|
|
260
412
|
return cachedRegistriesConfPath;
|
|
@@ -266,9 +418,8 @@ async function buildahStorageArgs() {
|
|
|
266
418
|
"--registries-conf",
|
|
267
419
|
buildahRegistriesConfPath()
|
|
268
420
|
];
|
|
269
|
-
if (!driver)
|
|
421
|
+
if (!driver)
|
|
270
422
|
return [...rootArgs, ...registriesArgs];
|
|
271
|
-
}
|
|
272
423
|
if (driver === "fuse-overlayfs") {
|
|
273
424
|
return [
|
|
274
425
|
...rootArgs,
|
|
@@ -281,121 +432,9 @@ async function buildahStorageArgs() {
|
|
|
281
432
|
}
|
|
282
433
|
return [...rootArgs, ...registriesArgs, "--storage-driver", driver];
|
|
283
434
|
}
|
|
284
|
-
async function runBuildah(args, opts = {}) {
|
|
285
|
-
const fullArgs = [...await buildahStorageArgs(), ...args];
|
|
286
|
-
debug(`exec: buildah ${fullArgs.join(" ")}`);
|
|
287
|
-
const start = Date.now();
|
|
288
|
-
try {
|
|
289
|
-
const result = await run("buildah", fullArgs, opts);
|
|
290
|
-
debug(`buildah ${args[0]} finished in ${Date.now() - start}ms`);
|
|
291
|
-
return result;
|
|
292
|
-
} catch (error) {
|
|
293
|
-
debug(`buildah ${args[0]} failed after ${Date.now() - start}ms`);
|
|
294
|
-
throw error;
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
async function startBuildahInBackground(args) {
|
|
298
|
-
const fullArgs = [...await buildahStorageArgs(), ...args];
|
|
299
|
-
debug(`exec in the background: buildah ${fullArgs.join(" ")}`);
|
|
300
|
-
const start = Date.now();
|
|
301
|
-
const child = (0, import_node_child_process2.spawn)("buildah", fullArgs, {
|
|
302
|
-
detached: true,
|
|
303
|
-
stdio: "ignore"
|
|
304
|
-
});
|
|
305
|
-
child.on(
|
|
306
|
-
"error",
|
|
307
|
-
(error) => debug(`background buildah ${args[0]} failed: ${error.message}`)
|
|
308
|
-
);
|
|
309
|
-
child.on(
|
|
310
|
-
"exit",
|
|
311
|
-
(code) => debug(
|
|
312
|
-
`background buildah ${args[0]} exited with code ${code} after ${Date.now() - start}ms`
|
|
313
|
-
)
|
|
314
|
-
);
|
|
315
|
-
child.unref();
|
|
316
|
-
}
|
|
317
|
-
async function readBuildahStoreInfo() {
|
|
318
|
-
const { stdout } = await runBuildah(["info"], { quiet: true });
|
|
319
|
-
const store = JSON.parse(stdout).store;
|
|
320
|
-
if (!store) {
|
|
321
|
-
return void 0;
|
|
322
|
-
}
|
|
323
|
-
const graphStatus = store.GraphStatus;
|
|
324
|
-
return {
|
|
325
|
-
graphRoot: String(store.GraphRoot ?? ""),
|
|
326
|
-
runRoot: String(store.RunRoot ?? ""),
|
|
327
|
-
driver: String(store.GraphDriverName ?? ""),
|
|
328
|
-
backingFs: String(
|
|
329
|
-
graphStatus?.["Backing Filesystem"] ?? graphStatus?.["Backing filesystem"] ?? ""
|
|
330
|
-
)
|
|
331
|
-
};
|
|
332
|
-
}
|
|
333
|
-
async function assertBuildContainerStorage(log = () => {
|
|
334
|
-
}) {
|
|
335
|
-
if (!isBuildContainer()) {
|
|
336
|
-
return;
|
|
337
|
-
}
|
|
338
|
-
if (readString(process.env.VERCEL_VCR_DOCKER_STORAGE_DRIVER)) {
|
|
339
|
-
return;
|
|
340
|
-
}
|
|
341
|
-
const strict = Boolean(readString(process.env.VERCEL_VCR_STRICT_STORAGE));
|
|
342
|
-
let storeInfo;
|
|
343
|
-
try {
|
|
344
|
-
storeInfo = await readBuildahStoreInfo();
|
|
345
|
-
} catch (err) {
|
|
346
|
-
const message = `Could not verify buildah storage via \`buildah info\`: ${err.message}`;
|
|
347
|
-
if (strict) {
|
|
348
|
-
throw new Error(message);
|
|
349
|
-
}
|
|
350
|
-
log(message);
|
|
351
|
-
return;
|
|
352
|
-
}
|
|
353
|
-
if (!storeInfo) {
|
|
354
|
-
const message = "Could not verify buildah storage: `buildah info` returned no store data.";
|
|
355
|
-
if (strict) {
|
|
356
|
-
throw new Error(message);
|
|
357
|
-
}
|
|
358
|
-
log(message);
|
|
359
|
-
return;
|
|
360
|
-
}
|
|
361
|
-
const problems = [];
|
|
362
|
-
if (storeInfo.driver !== REQUIRED_BUILD_CONTAINER_DRIVER) {
|
|
363
|
-
problems.push(
|
|
364
|
-
`storage driver is "${storeInfo.driver}", expected "${REQUIRED_BUILD_CONTAINER_DRIVER}"`
|
|
365
|
-
);
|
|
366
|
-
}
|
|
367
|
-
if (storeInfo.graphRoot !== BUILDAH_GRAPH_ROOT) {
|
|
368
|
-
problems.push(
|
|
369
|
-
`graphRoot is "${storeInfo.graphRoot}", expected the mounted volume "${BUILDAH_GRAPH_ROOT}"`
|
|
370
|
-
);
|
|
371
|
-
}
|
|
372
|
-
if (storeInfo.backingFs && storeInfo.backingFs === "overlayfs") {
|
|
373
|
-
problems.push(
|
|
374
|
-
`backing filesystem is "${storeInfo.backingFs}" (the overlay rootfs), not the mounted volume`
|
|
375
|
-
);
|
|
376
|
-
}
|
|
377
|
-
const summary = `buildah storage: driver=${storeInfo.driver} graphRoot=${storeInfo.graphRoot} runRoot=${storeInfo.runRoot} backingFs=${storeInfo.backingFs || "?"}`;
|
|
378
|
-
if (problems.length === 0) {
|
|
379
|
-
log(`${summary} \u2014 verified`);
|
|
380
|
-
return;
|
|
381
|
-
}
|
|
382
|
-
const detail = `${summary}
|
|
383
|
-
Problems: ${problems.join("; ")}.
|
|
384
|
-
Expected the native overlay driver with the graphroot under the XFS \`/vercel\` cell volume (requires vercel/hive#2310 capabilities + the storage.conf from vercel/api#76567).`;
|
|
385
|
-
if (strict) {
|
|
386
|
-
throw new Error(
|
|
387
|
-
`Container build storage is not configured as intended.
|
|
388
|
-
${detail}`
|
|
389
|
-
);
|
|
390
|
-
}
|
|
391
|
-
log(
|
|
392
|
-
`${detail}
|
|
393
|
-
Continuing (set VERCEL_VCR_STRICT_STORAGE=1 to fail builds).`
|
|
394
|
-
);
|
|
395
|
-
}
|
|
396
435
|
|
|
397
436
|
// src/oidc.ts
|
|
398
|
-
var
|
|
437
|
+
var import_node_crypto2 = require("crypto");
|
|
399
438
|
function parseOidcToken(token) {
|
|
400
439
|
const parts = token.split(".");
|
|
401
440
|
if (parts.length !== 3) {
|
|
@@ -426,7 +465,7 @@ function decodeOidcClaims(token) {
|
|
|
426
465
|
function tokenFingerprint(token) {
|
|
427
466
|
if (!token)
|
|
428
467
|
return "absent";
|
|
429
|
-
const sha = (0,
|
|
468
|
+
const sha = (0, import_node_crypto2.createHash)("sha256").update(token).digest("hex").slice(0, 8);
|
|
430
469
|
return `present(len=${token.length}, sha256=${sha})`;
|
|
431
470
|
}
|
|
432
471
|
function debugTokenClaims(label, token) {
|
|
@@ -720,206 +759,259 @@ async function ensureRepository(repository, token, claims, span) {
|
|
|
720
759
|
}
|
|
721
760
|
|
|
722
761
|
// src/engines/buildah.ts
|
|
723
|
-
function
|
|
724
|
-
const
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
return;
|
|
762
|
+
function localImageRef(params) {
|
|
763
|
+
const { imageRef } = params;
|
|
764
|
+
if (!params.useBuildahStoreCache) {
|
|
765
|
+
return imageRef;
|
|
728
766
|
}
|
|
729
|
-
|
|
730
|
-
info(`layer cache: ${cached}/${steps} steps reused, ${built} rebuilt`);
|
|
767
|
+
return `${imageRef.slice(0, imageRef.lastIndexOf(":"))}:vercel-buildah-store`;
|
|
731
768
|
}
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
769
|
+
async function deploymentSourceDateEpoch() {
|
|
770
|
+
if ((0, import_build_utils3.resolveBuildConcurrency)() <= 1) {
|
|
771
|
+
return void 0;
|
|
772
|
+
}
|
|
773
|
+
if (readString(process.env.SOURCE_DATE_EPOCH?.trim()) !== void 0) {
|
|
774
|
+
return void 0;
|
|
775
|
+
}
|
|
776
|
+
try {
|
|
777
|
+
const path4 = (0, import_node_path5.join)(
|
|
778
|
+
await createBuildahStoreStateDirectory(),
|
|
779
|
+
"source-date-epoch"
|
|
780
|
+
);
|
|
781
|
+
const candidate = `${path4}.${(0, import_node_crypto3.randomUUID)()}`;
|
|
782
|
+
await (0, import_promises5.writeFile)(candidate, `${Math.floor(Date.now() / 1e3)}
|
|
783
|
+
`, {
|
|
784
|
+
flag: "wx",
|
|
785
|
+
mode: 384
|
|
786
|
+
});
|
|
735
787
|
try {
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
});
|
|
742
|
-
} catch (err) {
|
|
743
|
-
const message = err.message;
|
|
744
|
-
if (/Command not found/i.test(message)) {
|
|
745
|
-
throw new Error(
|
|
746
|
-
isBuildContainer() ? "The `buildah` CLI is not available in this build container. Install buildah (via SPAL) in the build image." : "Buildah was not found on your PATH. Install buildah or run the build on Vercel where the build container provides it."
|
|
747
|
-
);
|
|
748
|
-
}
|
|
749
|
-
throw err;
|
|
788
|
+
await (0, import_promises5.link)(candidate, path4);
|
|
789
|
+
} catch (error) {
|
|
790
|
+
if (error.code !== "EEXIST")
|
|
791
|
+
throw error;
|
|
792
|
+
} finally {
|
|
793
|
+
await (0, import_promises5.rm)(candidate, { force: true });
|
|
750
794
|
}
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
const storageDriver = await selectStorageDriver();
|
|
755
|
-
const version2 = (await runBuildah(["--version"], { quiet: true })).stdout.trim();
|
|
756
|
-
info(
|
|
757
|
-
`buildah: ${version2.split("\n")[0] ?? version2} (storage-driver=${storageDriver ?? "storage.conf"})`
|
|
758
|
-
);
|
|
759
|
-
span?.setAttributes({
|
|
760
|
-
"container.engine": "buildah",
|
|
761
|
-
"buildah.version": toTag(version2.split("\n")[0]),
|
|
762
|
-
"buildah.storage_driver": toTag(storageDriver ?? "storage.conf")
|
|
763
|
-
});
|
|
764
|
-
} catch (err) {
|
|
765
|
-
debug(`buildah diagnostics unavailable: ${err.message}`);
|
|
795
|
+
const epoch = (await (0, import_promises5.readFile)(path4, "utf8")).trim();
|
|
796
|
+
if (!/^[0-9]+$/.test(epoch)) {
|
|
797
|
+
throw new Error(`invalid value ${JSON.stringify(epoch)}`);
|
|
766
798
|
}
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
799
|
+
return epoch;
|
|
800
|
+
} catch (error) {
|
|
801
|
+
debug(
|
|
802
|
+
`could not share a Buildah source date epoch: ${error.message}`
|
|
803
|
+
);
|
|
804
|
+
return void 0;
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
function createBuildahEngine(client) {
|
|
808
|
+
return {
|
|
809
|
+
name: "buildah",
|
|
810
|
+
async ensureReady(span) {
|
|
811
|
+
try {
|
|
812
|
+
const storageDriver = await selectStorageDriver();
|
|
813
|
+
const version2 = await client.version();
|
|
814
|
+
span?.setAttributes({
|
|
815
|
+
"buildah.version": version2.split("\n")[0],
|
|
816
|
+
"buildah.storage_driver": storageDriver ?? "storage.conf"
|
|
817
|
+
});
|
|
818
|
+
} catch (err) {
|
|
819
|
+
const message = err.message;
|
|
820
|
+
if (/Command not found/i.test(message)) {
|
|
821
|
+
throw new Error(
|
|
822
|
+
isBuildContainer() ? "The `buildah` CLI is not available in this build container. Install buildah (via SPAL) in the build image." : "Buildah was not found on your PATH. Install buildah or run the build on Vercel where the build container provides it."
|
|
823
|
+
);
|
|
824
|
+
}
|
|
825
|
+
throw err;
|
|
826
|
+
}
|
|
827
|
+
},
|
|
828
|
+
async logDiagnostics(span) {
|
|
829
|
+
try {
|
|
830
|
+
const storageDriver = await selectStorageDriver();
|
|
831
|
+
const version2 = await client.version();
|
|
832
|
+
info(
|
|
833
|
+
`buildah: ${version2.split("\n")[0] ?? version2} (storage-driver=${storageDriver ?? "storage.conf"})`
|
|
834
|
+
);
|
|
835
|
+
span?.setAttributes({
|
|
836
|
+
"container.engine": "buildah",
|
|
837
|
+
"buildah.version": toTag(version2.split("\n")[0]),
|
|
838
|
+
"buildah.storage_driver": toTag(storageDriver ?? "storage.conf")
|
|
839
|
+
});
|
|
840
|
+
} catch (err) {
|
|
841
|
+
debug(`buildah diagnostics unavailable: ${err.message}`);
|
|
842
|
+
}
|
|
843
|
+
},
|
|
844
|
+
async withRuntime(_span, fn) {
|
|
845
|
+
return fn();
|
|
846
|
+
},
|
|
847
|
+
async build(params) {
|
|
848
|
+
try {
|
|
849
|
+
const { stdout: stdout2 } = await client.run(["images", "--quiet"], {
|
|
850
|
+
quiet: true
|
|
851
|
+
});
|
|
852
|
+
const imageCount = stdout2.trim() ? stdout2.trim().split("\n").length : 0;
|
|
853
|
+
info(
|
|
854
|
+
imageCount > 0 ? `Buildah store: warm (${imageCount} image(s) present before build)` : "Buildah store: cold (no images present before build)"
|
|
855
|
+
);
|
|
856
|
+
} catch (err) {
|
|
857
|
+
debug(`could not read store warmth: ${err.message}`);
|
|
858
|
+
}
|
|
859
|
+
const sourceDateEpoch = await deploymentSourceDateEpoch();
|
|
860
|
+
if (sourceDateEpoch) {
|
|
861
|
+
info(
|
|
862
|
+
`Buildah source date epoch: ${sourceDateEpoch} (shared by this deployment's concurrent service builds)`
|
|
863
|
+
);
|
|
864
|
+
}
|
|
865
|
+
const usage = await BuildahImageUsage.begin();
|
|
866
|
+
const { stdout, stderr } = await client.run([
|
|
867
|
+
"build",
|
|
868
|
+
"--platform",
|
|
869
|
+
TARGET_PLATFORM,
|
|
870
|
+
// Commit and cache a layer per Dockerfile instruction so unchanged steps
|
|
871
|
+
// (base image, dependency installs, etc.) can be reused on later builds
|
|
872
|
+
// when the image store is warm. Without this buildah squashes the build
|
|
873
|
+
// and no per-step caching happens.
|
|
874
|
+
"--layers",
|
|
875
|
+
// With --layers, Buildah leaves the working container of a FROM-only
|
|
876
|
+
// stage behind, which stops the store cache from being saved.
|
|
877
|
+
"--force-rm",
|
|
878
|
+
...sourceDateEpoch ? ["--source-date-epoch", sourceDateEpoch] : [],
|
|
879
|
+
...params.useBuildahStoreCache ? ["--pull=always"] : [],
|
|
880
|
+
// Use the host network namespace for RUN steps. The build runs inside a
|
|
881
|
+
// restricted Hive cell that cannot program iptables, so buildah's default
|
|
882
|
+
// rootless networking (netavark) fails with
|
|
883
|
+
// "netavark: iptables ... Could not fetch rule set generation id".
|
|
884
|
+
// Host networking skips per-container network setup and reuses the cell's
|
|
885
|
+
// existing egress.
|
|
886
|
+
"--network",
|
|
887
|
+
"host",
|
|
888
|
+
...buildArgFlags(params),
|
|
889
|
+
"-t",
|
|
890
|
+
localImageRef(params),
|
|
891
|
+
"-f",
|
|
892
|
+
params.dockerfilePath,
|
|
893
|
+
params.contextDir
|
|
894
|
+
]);
|
|
895
|
+
await usage?.record(
|
|
896
|
+
() => imageIdsFromBuildOutput(`${stdout}
|
|
897
|
+
${stderr}`)
|
|
821
898
|
);
|
|
822
|
-
}
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
899
|
+
},
|
|
900
|
+
async login(params) {
|
|
901
|
+
try {
|
|
902
|
+
await client.run(
|
|
903
|
+
[
|
|
904
|
+
"login",
|
|
827
905
|
params.registry,
|
|
906
|
+
"--username",
|
|
828
907
|
params.username,
|
|
829
|
-
|
|
830
|
-
|
|
908
|
+
"--password-stdin"
|
|
909
|
+
],
|
|
910
|
+
{ input: params.token, quiet: !DEBUG }
|
|
831
911
|
);
|
|
912
|
+
} catch (err) {
|
|
913
|
+
const message = err.message;
|
|
914
|
+
if (/denied|forbidden|unauthorized|401|403/i.test(message)) {
|
|
915
|
+
throw new Error(
|
|
916
|
+
formatVcrAuthError(
|
|
917
|
+
params.registry,
|
|
918
|
+
params.username,
|
|
919
|
+
`Underlying error: ${message}`
|
|
920
|
+
)
|
|
921
|
+
);
|
|
922
|
+
}
|
|
923
|
+
throw err;
|
|
832
924
|
}
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
async reportStorage(span) {
|
|
844
|
-
if (!DEBUG) {
|
|
845
|
-
return;
|
|
846
|
-
}
|
|
847
|
-
try {
|
|
848
|
-
const storeInfo = await readBuildahStoreInfo();
|
|
849
|
-
if (!storeInfo) {
|
|
850
|
-
debug("buildah info: no `store` field in output");
|
|
925
|
+
},
|
|
926
|
+
async verifyStorage(span) {
|
|
927
|
+
await client.verifyStorage((message) => {
|
|
928
|
+
info(message);
|
|
929
|
+
const info0 = message.split("\n")[0];
|
|
930
|
+
span?.setAttributes({ "buildah.storage.verify": info0 });
|
|
931
|
+
});
|
|
932
|
+
},
|
|
933
|
+
async reportStorage(span) {
|
|
934
|
+
if (!DEBUG) {
|
|
851
935
|
return;
|
|
852
936
|
}
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
});
|
|
862
|
-
} catch (err) {
|
|
863
|
-
debug(`buildah storage report unavailable: ${err.message}`);
|
|
864
|
-
}
|
|
865
|
-
},
|
|
866
|
-
async push(params) {
|
|
867
|
-
const digestDir = (0, import_node_fs3.mkdtempSync)((0, import_node_path3.join)((0, import_node_os3.tmpdir)(), "vercel-container-digest-"));
|
|
868
|
-
const digestFile = (0, import_node_path3.join)(digestDir, "digest");
|
|
869
|
-
const zstdEnabled = !readString(process.env.VERCEL_VCR_DISABLE_ZSTD);
|
|
870
|
-
const zstdArgs = zstdEnabled ? [
|
|
871
|
-
"--compression-format",
|
|
872
|
-
"zstd",
|
|
873
|
-
"--compression-level",
|
|
874
|
-
"3",
|
|
875
|
-
"--force-compression",
|
|
876
|
-
"--format",
|
|
877
|
-
"oci"
|
|
878
|
-
] : [];
|
|
879
|
-
info(
|
|
880
|
-
`pushing ${params.imageRef} ` + (zstdEnabled ? "with zstd compression (level=3, force, oci)" : "with default compression (zstd disabled)")
|
|
881
|
-
);
|
|
882
|
-
const pushStart = Date.now();
|
|
883
|
-
let digest;
|
|
884
|
-
try {
|
|
885
|
-
await runBuildah([
|
|
886
|
-
"push",
|
|
887
|
-
...zstdArgs,
|
|
888
|
-
"--digestfile",
|
|
889
|
-
digestFile,
|
|
890
|
-
params.imageRef
|
|
891
|
-
]);
|
|
892
|
-
digest = readDigestFile(digestFile);
|
|
893
|
-
} catch (err) {
|
|
894
|
-
const message = err.message;
|
|
895
|
-
if (/denied|forbidden|unauthorized|not found|401|403|404/i.test(message)) {
|
|
896
|
-
throw new Error(
|
|
897
|
-
[
|
|
898
|
-
`Pushing ${params.imageRef} was denied.`,
|
|
899
|
-
"",
|
|
900
|
-
`The build tried to ensure the "${params.repository}" repository exists, but`,
|
|
901
|
-
"the push was still rejected. Verify access (or create the repository under",
|
|
902
|
-
"your project's Sandboxes \u2192 Container Registry tab), then re-run the build.",
|
|
903
|
-
"",
|
|
904
|
-
`Underlying error: ${message}`
|
|
905
|
-
].join("\n")
|
|
937
|
+
try {
|
|
938
|
+
const storeInfo = await client.info();
|
|
939
|
+
if (!storeInfo) {
|
|
940
|
+
debug("buildah info: no `store` field in output");
|
|
941
|
+
return;
|
|
942
|
+
}
|
|
943
|
+
info(
|
|
944
|
+
`buildah storage: graphRoot=${storeInfo.graphRoot} runRoot=${storeInfo.runRoot} driver=${storeInfo.driver} backingFs=${storeInfo.backingFs || "?"}`
|
|
906
945
|
);
|
|
946
|
+
span?.setAttributes({
|
|
947
|
+
"buildah.storage.graph_root": storeInfo.graphRoot,
|
|
948
|
+
"buildah.storage.run_root": storeInfo.runRoot,
|
|
949
|
+
"buildah.storage.driver": storeInfo.driver,
|
|
950
|
+
"buildah.storage.backing_fs": storeInfo.backingFs
|
|
951
|
+
});
|
|
952
|
+
} catch (err) {
|
|
953
|
+
debug(`buildah storage report unavailable: ${err.message}`);
|
|
954
|
+
}
|
|
955
|
+
},
|
|
956
|
+
async push(params) {
|
|
957
|
+
const digestDir = (0, import_node_fs3.mkdtempSync)((0, import_node_path5.join)((0, import_node_os5.tmpdir)(), "vercel-container-digest-"));
|
|
958
|
+
const digestFile = (0, import_node_path5.join)(digestDir, "digest");
|
|
959
|
+
const zstdEnabled = !readString(process.env.VERCEL_VCR_DISABLE_ZSTD);
|
|
960
|
+
const zstdArgs = zstdEnabled ? [
|
|
961
|
+
"--compression-format",
|
|
962
|
+
"zstd",
|
|
963
|
+
"--compression-level",
|
|
964
|
+
"3",
|
|
965
|
+
"--force-compression",
|
|
966
|
+
"--format",
|
|
967
|
+
"oci"
|
|
968
|
+
] : [];
|
|
969
|
+
info(
|
|
970
|
+
`pushing ${params.imageRef} ` + (zstdEnabled ? "with zstd compression (level=3, force, oci)" : "with default compression (zstd disabled)")
|
|
971
|
+
);
|
|
972
|
+
const pushStart = Date.now();
|
|
973
|
+
let digest;
|
|
974
|
+
try {
|
|
975
|
+
await client.run([
|
|
976
|
+
"push",
|
|
977
|
+
...zstdArgs,
|
|
978
|
+
"--digestfile",
|
|
979
|
+
digestFile,
|
|
980
|
+
localImageRef(params),
|
|
981
|
+
...params.useBuildahStoreCache ? [`docker://${params.imageRef}`] : []
|
|
982
|
+
]);
|
|
983
|
+
digest = readDigestFile(digestFile);
|
|
984
|
+
} catch (err) {
|
|
985
|
+
const message = err.message;
|
|
986
|
+
if (/denied|forbidden|unauthorized|not found|401|403|404/i.test(message)) {
|
|
987
|
+
throw new Error(
|
|
988
|
+
[
|
|
989
|
+
`Pushing ${params.imageRef} was denied.`,
|
|
990
|
+
"",
|
|
991
|
+
`The build tried to ensure the "${params.repository}" repository exists, but`,
|
|
992
|
+
"the push was still rejected. Verify access (or create the repository under",
|
|
993
|
+
"your project's Sandboxes \u2192 Container Registry tab), then re-run the build.",
|
|
994
|
+
"",
|
|
995
|
+
`Underlying error: ${message}`
|
|
996
|
+
].join("\n")
|
|
997
|
+
);
|
|
998
|
+
}
|
|
999
|
+
throw err;
|
|
1000
|
+
} finally {
|
|
1001
|
+
(0, import_node_fs3.rmSync)(digestDir, { recursive: true, force: true });
|
|
907
1002
|
}
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
1003
|
+
debug(`push completed in ${Date.now() - pushStart}ms`);
|
|
1004
|
+
if (DIGEST_RE.test(digest)) {
|
|
1005
|
+
debug(`pushed digest ${digest}`);
|
|
1006
|
+
return digest;
|
|
1007
|
+
}
|
|
1008
|
+
debug(
|
|
1009
|
+
`--digestfile did not contain a digest (${JSON.stringify(digest)}) \u2014 resolving registry manifest`
|
|
1010
|
+
);
|
|
1011
|
+
return resolvePushedImageDigest(params);
|
|
916
1012
|
}
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
);
|
|
920
|
-
return resolvePushedImageDigest(params);
|
|
921
|
-
}
|
|
922
|
-
};
|
|
1013
|
+
};
|
|
1014
|
+
}
|
|
923
1015
|
function readDigestFile(path4) {
|
|
924
1016
|
try {
|
|
925
1017
|
return (0, import_node_fs3.readFileSync)(path4, "utf8").trim();
|
|
@@ -930,19 +1022,11 @@ function readDigestFile(path4) {
|
|
|
930
1022
|
}
|
|
931
1023
|
|
|
932
1024
|
// src/engines/docker.ts
|
|
933
|
-
var
|
|
1025
|
+
var import_node_child_process2 = require("child_process");
|
|
934
1026
|
function runDocker(args, opts = {}) {
|
|
935
1027
|
debug(`exec: docker ${args.join(" ")}`);
|
|
936
1028
|
return run("docker", args, opts);
|
|
937
1029
|
}
|
|
938
|
-
async function hasBinary2(name) {
|
|
939
|
-
try {
|
|
940
|
-
await run("which", [name], { quiet: true });
|
|
941
|
-
return true;
|
|
942
|
-
} catch {
|
|
943
|
-
return false;
|
|
944
|
-
}
|
|
945
|
-
}
|
|
946
1030
|
async function isDockerDaemonReachable() {
|
|
947
1031
|
try {
|
|
948
1032
|
await run("docker", ["version", "--format", "{{.Server.Version}}"], {
|
|
@@ -966,7 +1050,7 @@ async function startDockerDaemon(span) {
|
|
|
966
1050
|
span?.setAttributes({ "docker.storage_driver": driver });
|
|
967
1051
|
step(`Starting Docker daemon (storage-driver=${driver})`);
|
|
968
1052
|
debug(`exec: dockerd ${args.join(" ")}`);
|
|
969
|
-
const child = (0,
|
|
1053
|
+
const child = (0, import_node_child_process2.spawn)("dockerd", args, {
|
|
970
1054
|
stdio: ["ignore", "pipe", "pipe"]
|
|
971
1055
|
});
|
|
972
1056
|
let log = "";
|
|
@@ -1019,7 +1103,7 @@ async function startDockerDaemon(span) {
|
|
|
1019
1103
|
].join("\n")
|
|
1020
1104
|
);
|
|
1021
1105
|
}
|
|
1022
|
-
await new Promise((
|
|
1106
|
+
await new Promise((resolve5) => setTimeout(resolve5, 500));
|
|
1023
1107
|
}
|
|
1024
1108
|
}
|
|
1025
1109
|
async function stopDockerDaemon(daemon, span) {
|
|
@@ -1029,12 +1113,12 @@ async function stopDockerDaemon(daemon, span) {
|
|
|
1029
1113
|
}
|
|
1030
1114
|
step("Stopping Docker daemon");
|
|
1031
1115
|
const stopTimeoutMs = Number(process.env.VERCEL_VCR_DOCKERD_STOP_TIMEOUT_MS) || 1e4;
|
|
1032
|
-
await new Promise((
|
|
1116
|
+
await new Promise((resolve5) => {
|
|
1033
1117
|
let settled = false;
|
|
1034
1118
|
const finish = () => {
|
|
1035
1119
|
if (!settled) {
|
|
1036
1120
|
settled = true;
|
|
1037
|
-
|
|
1121
|
+
resolve5();
|
|
1038
1122
|
}
|
|
1039
1123
|
};
|
|
1040
1124
|
child.once("exit", finish);
|
|
@@ -1062,7 +1146,7 @@ async function withManagedDaemon(span, fn) {
|
|
|
1062
1146
|
if (await isDockerDaemonReachable()) {
|
|
1063
1147
|
return fn();
|
|
1064
1148
|
}
|
|
1065
|
-
if (!await
|
|
1149
|
+
if (!await hasCommand("dockerd")) {
|
|
1066
1150
|
return fn();
|
|
1067
1151
|
}
|
|
1068
1152
|
const daemon = await withSpan(
|
|
@@ -1162,6 +1246,16 @@ ${version2.trim()}`);
|
|
|
1162
1246
|
params.contextDir
|
|
1163
1247
|
]);
|
|
1164
1248
|
},
|
|
1249
|
+
async onBuildTriggers(imageRef) {
|
|
1250
|
+
const { stdout } = await runDocker([
|
|
1251
|
+
"image",
|
|
1252
|
+
"inspect",
|
|
1253
|
+
"--format",
|
|
1254
|
+
"{{json .Config.OnBuild}}",
|
|
1255
|
+
imageRef
|
|
1256
|
+
]);
|
|
1257
|
+
return JSON.parse(stdout) ?? [];
|
|
1258
|
+
},
|
|
1165
1259
|
async login(params) {
|
|
1166
1260
|
try {
|
|
1167
1261
|
await runDocker(
|
|
@@ -1223,41 +1317,163 @@ ${version2.trim()}`);
|
|
|
1223
1317
|
};
|
|
1224
1318
|
|
|
1225
1319
|
// src/engines/index.ts
|
|
1226
|
-
function selectContainerEngine() {
|
|
1320
|
+
function selectContainerEngine(client) {
|
|
1227
1321
|
const override = readString(
|
|
1228
1322
|
process.env.VERCEL_CONTAINER_ENGINE
|
|
1229
1323
|
)?.toLowerCase();
|
|
1230
1324
|
if (override === "buildah")
|
|
1231
|
-
return
|
|
1325
|
+
return createBuildahEngine(client);
|
|
1232
1326
|
if (override === "docker")
|
|
1233
1327
|
return dockerEngine;
|
|
1234
|
-
return isBuildContainer() ?
|
|
1328
|
+
return isBuildContainer() ? createBuildahEngine(client) : dockerEngine;
|
|
1235
1329
|
}
|
|
1236
1330
|
|
|
1331
|
+
// src/clients/buildah.ts
|
|
1332
|
+
var BuildahClient = class {
|
|
1333
|
+
constructor(lock) {
|
|
1334
|
+
this.lock = lock;
|
|
1335
|
+
}
|
|
1336
|
+
async run(args, options = {}) {
|
|
1337
|
+
const fullArgs = [...await buildahStorageArgs(), ...args];
|
|
1338
|
+
debug(`exec: buildah ${fullArgs.join(" ")}`);
|
|
1339
|
+
const start = Date.now();
|
|
1340
|
+
try {
|
|
1341
|
+
const [command, ...commandArgs] = await this.lock.wrap([
|
|
1342
|
+
"buildah",
|
|
1343
|
+
...fullArgs
|
|
1344
|
+
]);
|
|
1345
|
+
const result = await run(command, commandArgs, options);
|
|
1346
|
+
debug(`buildah ${args[0]} finished in ${Date.now() - start}ms`);
|
|
1347
|
+
return result;
|
|
1348
|
+
} catch (error) {
|
|
1349
|
+
debug(`buildah ${args[0]} failed after ${Date.now() - start}ms`);
|
|
1350
|
+
throw error;
|
|
1351
|
+
}
|
|
1352
|
+
}
|
|
1353
|
+
/** Version checks must not open the store or acquire its lock before restore. */
|
|
1354
|
+
version() {
|
|
1355
|
+
this.cachedVersion ??= run("buildah", ["--version"], { quiet: true }).then(({ stdout }) => stdout.trim()).catch((error) => {
|
|
1356
|
+
this.cachedVersion = void 0;
|
|
1357
|
+
throw error;
|
|
1358
|
+
});
|
|
1359
|
+
return this.cachedVersion;
|
|
1360
|
+
}
|
|
1361
|
+
/** Only stable store fields are cached; image and container counts are not. */
|
|
1362
|
+
info() {
|
|
1363
|
+
this.cachedStoreInfo ??= this.readStoreInfo().then(
|
|
1364
|
+
(store) => {
|
|
1365
|
+
if (!store)
|
|
1366
|
+
this.cachedStoreInfo = void 0;
|
|
1367
|
+
return store;
|
|
1368
|
+
},
|
|
1369
|
+
(error) => {
|
|
1370
|
+
this.cachedStoreInfo = void 0;
|
|
1371
|
+
throw error;
|
|
1372
|
+
}
|
|
1373
|
+
);
|
|
1374
|
+
return this.cachedStoreInfo;
|
|
1375
|
+
}
|
|
1376
|
+
/** Storage mismatches are diagnostic unless strict verification is requested. */
|
|
1377
|
+
async verifyStorage(log = () => {
|
|
1378
|
+
}) {
|
|
1379
|
+
if (!isBuildContainer())
|
|
1380
|
+
return;
|
|
1381
|
+
if (readString(process.env.VERCEL_VCR_DOCKER_STORAGE_DRIVER))
|
|
1382
|
+
return;
|
|
1383
|
+
const strict = Boolean(readString(process.env.VERCEL_VCR_STRICT_STORAGE));
|
|
1384
|
+
let storeInfo;
|
|
1385
|
+
try {
|
|
1386
|
+
storeInfo = await this.info();
|
|
1387
|
+
} catch (error) {
|
|
1388
|
+
const message = `Could not verify buildah storage via \`buildah info\`: ${error.message}`;
|
|
1389
|
+
if (strict)
|
|
1390
|
+
throw new Error(message);
|
|
1391
|
+
log(message);
|
|
1392
|
+
return;
|
|
1393
|
+
}
|
|
1394
|
+
if (!storeInfo) {
|
|
1395
|
+
const message = "Could not verify buildah storage: `buildah info` returned no store data.";
|
|
1396
|
+
if (strict)
|
|
1397
|
+
throw new Error(message);
|
|
1398
|
+
log(message);
|
|
1399
|
+
return;
|
|
1400
|
+
}
|
|
1401
|
+
const problems = [];
|
|
1402
|
+
if (storeInfo.driver !== REQUIRED_BUILD_CONTAINER_DRIVER) {
|
|
1403
|
+
problems.push(
|
|
1404
|
+
`storage driver is "${storeInfo.driver}", expected "${REQUIRED_BUILD_CONTAINER_DRIVER}"`
|
|
1405
|
+
);
|
|
1406
|
+
}
|
|
1407
|
+
if (storeInfo.graphRoot !== BUILDAH_GRAPH_ROOT) {
|
|
1408
|
+
problems.push(
|
|
1409
|
+
`graphRoot is "${storeInfo.graphRoot}", expected the mounted volume "${BUILDAH_GRAPH_ROOT}"`
|
|
1410
|
+
);
|
|
1411
|
+
}
|
|
1412
|
+
if (storeInfo.backingFs && storeInfo.backingFs === "overlayfs") {
|
|
1413
|
+
problems.push(
|
|
1414
|
+
`backing filesystem is "${storeInfo.backingFs}" (the overlay rootfs), not the mounted volume`
|
|
1415
|
+
);
|
|
1416
|
+
}
|
|
1417
|
+
const summary = `buildah storage: driver=${storeInfo.driver} graphRoot=${storeInfo.graphRoot} runRoot=${storeInfo.runRoot} backingFs=${storeInfo.backingFs || "?"}`;
|
|
1418
|
+
if (problems.length === 0) {
|
|
1419
|
+
log(`${summary} \u2014 verified`);
|
|
1420
|
+
return;
|
|
1421
|
+
}
|
|
1422
|
+
const detail = `${summary}
|
|
1423
|
+
Problems: ${problems.join("; ")}.
|
|
1424
|
+
Expected the native overlay driver with the graphroot under the XFS \`/vercel\` cell volume (requires vercel/hive#2310 capabilities + the storage.conf from vercel/api#76567).`;
|
|
1425
|
+
if (strict) {
|
|
1426
|
+
throw new Error(
|
|
1427
|
+
`Container build storage is not configured as intended.
|
|
1428
|
+
${detail}`
|
|
1429
|
+
);
|
|
1430
|
+
}
|
|
1431
|
+
log(
|
|
1432
|
+
`${detail}
|
|
1433
|
+
Continuing (set VERCEL_VCR_STRICT_STORAGE=1 to fail builds).`
|
|
1434
|
+
);
|
|
1435
|
+
}
|
|
1436
|
+
async readStoreInfo() {
|
|
1437
|
+
const { stdout } = await this.run(["info"], { quiet: true });
|
|
1438
|
+
const store = JSON.parse(stdout).store;
|
|
1439
|
+
if (!store)
|
|
1440
|
+
return void 0;
|
|
1441
|
+
const graphStatus = store.GraphStatus;
|
|
1442
|
+
return {
|
|
1443
|
+
graphRoot: String(store.GraphRoot ?? ""),
|
|
1444
|
+
runRoot: String(store.RunRoot ?? ""),
|
|
1445
|
+
driver: String(store.GraphDriverName ?? ""),
|
|
1446
|
+
backingFs: String(
|
|
1447
|
+
graphStatus?.["Backing Filesystem"] ?? graphStatus?.["Backing filesystem"] ?? ""
|
|
1448
|
+
)
|
|
1449
|
+
};
|
|
1450
|
+
}
|
|
1451
|
+
};
|
|
1452
|
+
|
|
1237
1453
|
// src/buildpacks/build.ts
|
|
1238
1454
|
var import_node_fs13 = require("fs");
|
|
1239
|
-
var
|
|
1240
|
-
var
|
|
1455
|
+
var import_node_os10 = require("os");
|
|
1456
|
+
var import_node_path17 = require("path");
|
|
1241
1457
|
|
|
1242
1458
|
// src/buildpacks/cache.ts
|
|
1243
|
-
var
|
|
1459
|
+
var import_node_crypto4 = require("crypto");
|
|
1244
1460
|
var import_node_fs4 = require("fs");
|
|
1245
|
-
var
|
|
1246
|
-
var
|
|
1461
|
+
var import_promises6 = require("fs/promises");
|
|
1462
|
+
var import_node_path6 = require("path");
|
|
1247
1463
|
var BUILDPACK_CACHE_DIR = ".vercel/cache/buildpacks";
|
|
1248
1464
|
var CNB_COMMITTED_DIR = "committed";
|
|
1249
1465
|
var CNB_CACHE_METADATA_FILE = "io.buildpacks.lifecycle.cache.metadata";
|
|
1250
1466
|
var IMAGE_ID_FILE = "image-id";
|
|
1251
1467
|
function buildpackCacheDir(options) {
|
|
1252
|
-
const root = (0,
|
|
1253
|
-
const app = (0,
|
|
1468
|
+
const root = (0, import_node_path6.resolve)(options.repoRootPath || options.workPath);
|
|
1469
|
+
const app = (0, import_node_path6.relative)(root, (0, import_node_path6.resolve)(options.workPath)).split(import_node_path6.sep).join("/") || ".";
|
|
1254
1470
|
const daemon = options.config?.daemon;
|
|
1255
1471
|
const owner = daemon ? { daemon: daemon.name } : options.service?.name ?? null;
|
|
1256
|
-
const key = (0,
|
|
1257
|
-
return (0,
|
|
1472
|
+
const key = (0, import_node_crypto4.createHash)("sha256").update(JSON.stringify([app, owner])).digest("hex").slice(0, 32);
|
|
1473
|
+
return (0, import_node_path6.join)(root, BUILDPACK_CACHE_DIR, key);
|
|
1258
1474
|
}
|
|
1259
1475
|
async function restoreBuildpackCache(dir, cacheKey, cacheDir) {
|
|
1260
|
-
const committed = (0,
|
|
1476
|
+
const committed = (0, import_node_path6.join)(cacheDir, CNB_COMMITTED_DIR);
|
|
1261
1477
|
const [copy, key] = await Promise.allSettled([
|
|
1262
1478
|
copyCommitted(dir, cacheDir),
|
|
1263
1479
|
cacheKey
|
|
@@ -1267,7 +1483,7 @@ async function restoreBuildpackCache(dir, cacheKey, cacheDir) {
|
|
|
1267
1483
|
throw copy.reason;
|
|
1268
1484
|
if (key.status === "rejected")
|
|
1269
1485
|
throw key.reason;
|
|
1270
|
-
if (readRegularFile((0,
|
|
1486
|
+
if (readRegularFile((0, import_node_path6.join)(dir, IMAGE_ID_FILE)) !== key.value) {
|
|
1271
1487
|
(0, import_node_fs4.rmSync)(committed, { recursive: true, force: true });
|
|
1272
1488
|
return { status: "miss", reason: "base_image_changed" };
|
|
1273
1489
|
}
|
|
@@ -1282,11 +1498,11 @@ async function restoreBuildpackCache(dir, cacheKey, cacheDir) {
|
|
|
1282
1498
|
}
|
|
1283
1499
|
}
|
|
1284
1500
|
async function saveBuildpackCache(dir, cacheKey, cacheDir) {
|
|
1285
|
-
const committed = (0,
|
|
1501
|
+
const committed = (0, import_node_path6.join)(cacheDir, CNB_COMMITTED_DIR);
|
|
1286
1502
|
let metadata;
|
|
1287
1503
|
try {
|
|
1288
1504
|
assertDirectory(committed);
|
|
1289
|
-
metadata = readRegularFile((0,
|
|
1505
|
+
metadata = readRegularFile((0, import_node_path6.join)(committed, CNB_CACHE_METADATA_FILE));
|
|
1290
1506
|
} catch (error) {
|
|
1291
1507
|
const missing = error.code === "ENOENT";
|
|
1292
1508
|
return {
|
|
@@ -1300,7 +1516,7 @@ async function saveBuildpackCache(dir, cacheKey, cacheDir) {
|
|
|
1300
1516
|
const bytes = sizeOf(committed);
|
|
1301
1517
|
(0, import_node_fs4.rmSync)(dir, { recursive: true, force: true });
|
|
1302
1518
|
await copyCommitted(cacheDir, dir);
|
|
1303
|
-
(0, import_node_fs4.writeFileSync)((0,
|
|
1519
|
+
(0, import_node_fs4.writeFileSync)((0, import_node_path6.join)(dir, IMAGE_ID_FILE), cacheKey);
|
|
1304
1520
|
return { status: "saved", bytes };
|
|
1305
1521
|
} catch (error) {
|
|
1306
1522
|
try {
|
|
@@ -1312,16 +1528,16 @@ async function saveBuildpackCache(dir, cacheKey, cacheDir) {
|
|
|
1312
1528
|
}
|
|
1313
1529
|
function isSaved(dir, cacheKey, metadata) {
|
|
1314
1530
|
try {
|
|
1315
|
-
const committed = (0,
|
|
1531
|
+
const committed = (0, import_node_path6.join)(dir, CNB_COMMITTED_DIR);
|
|
1316
1532
|
assertDirectory(committed);
|
|
1317
|
-
return readRegularFile((0,
|
|
1533
|
+
return readRegularFile((0, import_node_path6.join)(dir, IMAGE_ID_FILE)) === cacheKey && readRegularFile((0, import_node_path6.join)(committed, CNB_CACHE_METADATA_FILE)) === metadata;
|
|
1318
1534
|
} catch {
|
|
1319
1535
|
return false;
|
|
1320
1536
|
}
|
|
1321
1537
|
}
|
|
1322
1538
|
async function copyCommitted(from, to) {
|
|
1323
|
-
const source = (0,
|
|
1324
|
-
const target = (0,
|
|
1539
|
+
const source = (0, import_node_path6.join)(from, CNB_COMMITTED_DIR);
|
|
1540
|
+
const target = (0, import_node_path6.join)(to, CNB_COMMITTED_DIR);
|
|
1325
1541
|
assertDirectory(source);
|
|
1326
1542
|
(0, import_node_fs4.mkdirSync)(target, { recursive: true });
|
|
1327
1543
|
const entries = (0, import_node_fs4.readdirSync)(source, { withFileTypes: true });
|
|
@@ -1331,9 +1547,9 @@ async function copyCommitted(from, to) {
|
|
|
1331
1547
|
}
|
|
1332
1548
|
const copies = await Promise.allSettled(
|
|
1333
1549
|
entries.map(
|
|
1334
|
-
(entry) => (0,
|
|
1335
|
-
(0,
|
|
1336
|
-
(0,
|
|
1550
|
+
(entry) => (0, import_promises6.copyFile)(
|
|
1551
|
+
(0, import_node_path6.join)(source, entry.name),
|
|
1552
|
+
(0, import_node_path6.join)(target, entry.name),
|
|
1337
1553
|
import_node_fs4.constants.COPYFILE_FICLONE
|
|
1338
1554
|
)
|
|
1339
1555
|
)
|
|
@@ -1344,12 +1560,12 @@ async function copyCommitted(from, to) {
|
|
|
1344
1560
|
}
|
|
1345
1561
|
function assertDirectory(path4) {
|
|
1346
1562
|
if (!(0, import_node_fs4.lstatSync)(path4).isDirectory()) {
|
|
1347
|
-
throw new Error(`${(0,
|
|
1563
|
+
throw new Error(`${(0, import_node_path6.basename)(path4)} is not a directory`);
|
|
1348
1564
|
}
|
|
1349
1565
|
}
|
|
1350
1566
|
function readRegularFile(path4) {
|
|
1351
1567
|
if (!(0, import_node_fs4.lstatSync)(path4).isFile())
|
|
1352
|
-
throw notRegularFile((0,
|
|
1568
|
+
throw notRegularFile((0, import_node_path6.basename)(path4));
|
|
1353
1569
|
return (0, import_node_fs4.readFileSync)(path4, "utf8");
|
|
1354
1570
|
}
|
|
1355
1571
|
function notRegularFile(name) {
|
|
@@ -1357,25 +1573,13 @@ function notRegularFile(name) {
|
|
|
1357
1573
|
}
|
|
1358
1574
|
function sizeOf(dir) {
|
|
1359
1575
|
return (0, import_node_fs4.readdirSync)(dir).reduce(
|
|
1360
|
-
(bytes, name) => bytes + (0, import_node_fs4.lstatSync)((0,
|
|
1576
|
+
(bytes, name) => bytes + (0, import_node_fs4.lstatSync)((0, import_node_path6.join)(dir, name)).size,
|
|
1361
1577
|
0
|
|
1362
1578
|
);
|
|
1363
1579
|
}
|
|
1364
1580
|
function firstLine(error) {
|
|
1365
1581
|
return error.message.split("\n")[0];
|
|
1366
1582
|
}
|
|
1367
|
-
function formatBytes(bytes) {
|
|
1368
|
-
if (bytes < 1024)
|
|
1369
|
-
return `${bytes} B`;
|
|
1370
|
-
const units = ["KiB", "MiB", "GiB"];
|
|
1371
|
-
let value = bytes / 1024;
|
|
1372
|
-
let unit = 0;
|
|
1373
|
-
while (value >= 1024 && unit < units.length - 1) {
|
|
1374
|
-
value /= 1024;
|
|
1375
|
-
unit += 1;
|
|
1376
|
-
}
|
|
1377
|
-
return `${value.toFixed(value >= 10 ? 0 : 1)} ${units[unit]}`;
|
|
1378
|
-
}
|
|
1379
1583
|
function describeRestore(outcome) {
|
|
1380
1584
|
return outcome.status === "hit" ? `restored buildpack cache (${formatBytes(outcome.bytes)})` : `no buildpack cache to restore (${outcome.reason})`;
|
|
1381
1585
|
}
|
|
@@ -1406,11 +1610,11 @@ function recordSave(span, outcome, durationMs) {
|
|
|
1406
1610
|
}
|
|
1407
1611
|
|
|
1408
1612
|
// src/buildpacks/lifecycle/lifecycle.ts
|
|
1409
|
-
var
|
|
1410
|
-
var
|
|
1613
|
+
var import_build_utils4 = require("@vercel/build-utils");
|
|
1614
|
+
var import_node_crypto5 = require("crypto");
|
|
1411
1615
|
var import_node_fs5 = require("fs");
|
|
1412
|
-
var
|
|
1413
|
-
var
|
|
1616
|
+
var import_node_os6 = require("os");
|
|
1617
|
+
var import_node_path7 = require("path");
|
|
1414
1618
|
var import_smol_toml = require("smol-toml");
|
|
1415
1619
|
var import_tar = require("tar");
|
|
1416
1620
|
|
|
@@ -1561,39 +1765,39 @@ async function stageWorkspace({ files, workPath }, destinationDir) {
|
|
|
1561
1765
|
const symlinks = /* @__PURE__ */ new Set();
|
|
1562
1766
|
for (const [name, file] of Object.entries(files)) {
|
|
1563
1767
|
const portable = name.replace(/\\/g, "/");
|
|
1564
|
-
const normalized =
|
|
1565
|
-
if (!normalized || normalized === "." ||
|
|
1768
|
+
const normalized = import_node_path7.posix.normalize(portable).replace(/\/$/, "");
|
|
1769
|
+
if (!normalized || normalized === "." || import_node_path7.win32.isAbsolute(name) || import_node_path7.posix.isAbsolute(portable) || portable.split("/").includes("..") || normalized in staged) {
|
|
1566
1770
|
throw new Error(`Invalid buildpack source path ${JSON.stringify(name)}.`);
|
|
1567
1771
|
}
|
|
1568
1772
|
staged[normalized] = file;
|
|
1569
|
-
if (!(0,
|
|
1773
|
+
if (!(0, import_build_utils4.isSymbolicLink)(file.mode))
|
|
1570
1774
|
continue;
|
|
1571
|
-
let target = (0,
|
|
1775
|
+
let target = (0, import_build_utils4.getSymlinkTarget)(file);
|
|
1572
1776
|
if (target === null && file.type === "FileRef") {
|
|
1573
|
-
target = (await (0,
|
|
1777
|
+
target = (await (0, import_build_utils4.streamToBuffer)(await file.toStreamAsync())).toString();
|
|
1574
1778
|
}
|
|
1575
1779
|
if (!target) {
|
|
1576
1780
|
throw new Error(`Cannot read buildpack source symlink ${name}.`);
|
|
1577
1781
|
}
|
|
1578
|
-
if ((0,
|
|
1579
|
-
target = (0,
|
|
1580
|
-
} else if (
|
|
1782
|
+
if ((0, import_node_path7.isAbsolute)(target)) {
|
|
1783
|
+
target = (0, import_node_path7.relative)((0, import_node_path7.resolve)(workPath, import_node_path7.posix.dirname(normalized)), target) || ".";
|
|
1784
|
+
} else if (import_node_path7.win32.isAbsolute(target)) {
|
|
1581
1785
|
throw new Error(
|
|
1582
1786
|
`Buildpack source symlink ${name} has an absolute target.`
|
|
1583
1787
|
);
|
|
1584
1788
|
}
|
|
1585
1789
|
target = target.replace(/\\/g, "/");
|
|
1586
|
-
const resolved =
|
|
1790
|
+
const resolved = import_node_path7.posix.join(import_node_path7.posix.dirname(normalized), target);
|
|
1587
1791
|
if (resolved === ".." || resolved.startsWith("../")) {
|
|
1588
1792
|
throw new Error(
|
|
1589
1793
|
`Buildpack source symlink ${name} escapes the service root. Buildpack services must be self-contained.`
|
|
1590
1794
|
);
|
|
1591
1795
|
}
|
|
1592
|
-
staged[normalized] = new
|
|
1796
|
+
staged[normalized] = new import_build_utils4.FileBlob({ data: target, mode: file.mode });
|
|
1593
1797
|
symlinks.add(normalized);
|
|
1594
1798
|
}
|
|
1595
1799
|
for (const name of Object.keys(staged)) {
|
|
1596
|
-
for (let parent =
|
|
1800
|
+
for (let parent = import_node_path7.posix.dirname(name); parent !== "."; parent = import_node_path7.posix.dirname(parent)) {
|
|
1597
1801
|
if (symlinks.has(parent)) {
|
|
1598
1802
|
throw new Error(
|
|
1599
1803
|
`Buildpack source ${name} is inside symlink ${parent}.`
|
|
@@ -1601,17 +1805,17 @@ async function stageWorkspace({ files, workPath }, destinationDir) {
|
|
|
1601
1805
|
}
|
|
1602
1806
|
}
|
|
1603
1807
|
const file = staged[name];
|
|
1604
|
-
if (file.type === "FileFsRef" && !(0,
|
|
1605
|
-
const sourcePath = (0,
|
|
1606
|
-
const sourceRelative = (0,
|
|
1607
|
-
if (sourceRelative.startsWith(`..${
|
|
1808
|
+
if (file.type === "FileFsRef" && !(0, import_build_utils4.isSymbolicLink)(file.mode)) {
|
|
1809
|
+
const sourcePath = (0, import_node_path7.resolve)(file.fsPath);
|
|
1810
|
+
const sourceRelative = (0, import_node_path7.relative)((0, import_node_path7.resolve)(workPath), sourcePath);
|
|
1811
|
+
if (sourceRelative.startsWith(`..${import_node_path7.sep}`) || sourceRelative === ".." || (0, import_node_path7.isAbsolute)(sourceRelative)) {
|
|
1608
1812
|
continue;
|
|
1609
1813
|
}
|
|
1610
1814
|
const sourceRoot = import_node_fs5.realpathSync.native(workPath);
|
|
1611
1815
|
const sourceTarget = import_node_fs5.realpathSync.native(sourcePath);
|
|
1612
|
-
if (sourceTarget !== (0,
|
|
1613
|
-
const target = (0,
|
|
1614
|
-
if (target === ".." || target.startsWith("../") || (0,
|
|
1816
|
+
if (sourceTarget !== (0, import_node_path7.resolve)(sourceRoot, sourceRelative)) {
|
|
1817
|
+
const target = (0, import_node_path7.relative)(sourceRoot, sourceTarget).split(import_node_path7.sep).join("/");
|
|
1818
|
+
if (target === ".." || target.startsWith("../") || (0, import_node_path7.isAbsolute)(target) || !(target in staged)) {
|
|
1615
1819
|
throw new Error(
|
|
1616
1820
|
`Buildpack source ${name} follows a symlink to an excluded or outside-root file. Buildpack services must be self-contained.`
|
|
1617
1821
|
);
|
|
@@ -1619,25 +1823,25 @@ async function stageWorkspace({ files, workPath }, destinationDir) {
|
|
|
1619
1823
|
}
|
|
1620
1824
|
}
|
|
1621
1825
|
}
|
|
1622
|
-
await (0,
|
|
1826
|
+
await (0, import_build_utils4.download)(staged, destinationDir);
|
|
1623
1827
|
const root = import_node_fs5.realpathSync.native(destinationDir);
|
|
1624
1828
|
for (const name of symlinks) {
|
|
1625
1829
|
let target;
|
|
1626
1830
|
try {
|
|
1627
|
-
target = (0,
|
|
1831
|
+
target = (0, import_node_path7.relative)(root, import_node_fs5.realpathSync.native((0, import_node_path7.join)(destinationDir, name)));
|
|
1628
1832
|
} catch {
|
|
1629
1833
|
throw new Error(
|
|
1630
1834
|
`Buildpack source symlink ${name} has no target in the staged service.`
|
|
1631
1835
|
);
|
|
1632
1836
|
}
|
|
1633
|
-
if (target === ".." || target.startsWith(`..${
|
|
1837
|
+
if (target === ".." || target.startsWith(`..${import_node_path7.sep}`) || (0, import_node_path7.isAbsolute)(target)) {
|
|
1634
1838
|
throw new Error(
|
|
1635
1839
|
`Buildpack source symlink ${name} escapes the staged service.`
|
|
1636
1840
|
);
|
|
1637
1841
|
}
|
|
1638
1842
|
}
|
|
1639
1843
|
}
|
|
1640
|
-
function writePlatformEnvDir(buildEnv, config, parentDir = (0,
|
|
1844
|
+
function writePlatformEnvDir(buildEnv, config, parentDir = (0, import_node_os6.tmpdir)()) {
|
|
1641
1845
|
const environment = { ...buildEnv };
|
|
1642
1846
|
delete environment.VERCEL_NODE_HOME;
|
|
1643
1847
|
delete environment.VERCEL_ENTRYPOINT;
|
|
@@ -1651,14 +1855,14 @@ function writePlatformEnvDir(buildEnv, config, parentDir = (0, import_node_os4.t
|
|
|
1651
1855
|
if (config.command) {
|
|
1652
1856
|
environment.VERCEL_COMMAND = JSON.stringify(config.command);
|
|
1653
1857
|
}
|
|
1654
|
-
const dir = (0, import_node_fs5.mkdtempSync)((0,
|
|
1858
|
+
const dir = (0, import_node_fs5.mkdtempSync)((0, import_node_path7.join)(parentDir, "vercel-cnb-env-"));
|
|
1655
1859
|
(0, import_node_fs5.chmodSync)(dir, 493);
|
|
1656
1860
|
for (const [key, value] of Object.entries(environment)) {
|
|
1657
1861
|
if (!ENV_NAME_RE.test(key)) {
|
|
1658
1862
|
debug(`skipping build env var with an unsupported name: ${key}`);
|
|
1659
1863
|
continue;
|
|
1660
1864
|
}
|
|
1661
|
-
const file = (0,
|
|
1865
|
+
const file = (0, import_node_path7.join)(dir, key);
|
|
1662
1866
|
(0, import_node_fs5.writeFileSync)(file, value);
|
|
1663
1867
|
(0, import_node_fs5.chmodSync)(file, 420);
|
|
1664
1868
|
}
|
|
@@ -1723,7 +1927,7 @@ function lifecycleFailure(error, context) {
|
|
|
1723
1927
|
{ exitCode }
|
|
1724
1928
|
);
|
|
1725
1929
|
}
|
|
1726
|
-
function writeOrderDir(buildpack, parentDir = (0,
|
|
1930
|
+
function writeOrderDir(buildpack, parentDir = (0, import_node_os6.tmpdir)()) {
|
|
1727
1931
|
const order = {
|
|
1728
1932
|
order: [
|
|
1729
1933
|
{
|
|
@@ -1734,9 +1938,9 @@ function writeOrderDir(buildpack, parentDir = (0, import_node_os4.tmpdir)()) {
|
|
|
1734
1938
|
}
|
|
1735
1939
|
]
|
|
1736
1940
|
};
|
|
1737
|
-
const dir = (0, import_node_fs5.mkdtempSync)((0,
|
|
1941
|
+
const dir = (0, import_node_fs5.mkdtempSync)((0, import_node_path7.join)(parentDir, "vercel-cnb-order-"));
|
|
1738
1942
|
(0, import_node_fs5.chmodSync)(dir, 493);
|
|
1739
|
-
const file = (0,
|
|
1943
|
+
const file = (0, import_node_path7.join)(dir, "order.toml");
|
|
1740
1944
|
(0, import_node_fs5.writeFileSync)(file, (0, import_smol_toml.stringify)(order));
|
|
1741
1945
|
(0, import_node_fs5.chmodSync)(file, 420);
|
|
1742
1946
|
return dir;
|
|
@@ -1765,7 +1969,7 @@ async function downloadPinnedArchive(archive, destinationPath) {
|
|
|
1765
1969
|
);
|
|
1766
1970
|
}
|
|
1767
1971
|
const contents = new Uint8Array(await response.arrayBuffer());
|
|
1768
|
-
const checksum = (0,
|
|
1972
|
+
const checksum = (0, import_node_crypto5.createHash)("sha256").update(contents).digest("hex");
|
|
1769
1973
|
if (checksum !== archive.sha256) {
|
|
1770
1974
|
throw new Error(
|
|
1771
1975
|
`Checksum mismatch for ${archiveName(archive)}: expected ${archive.sha256}, received ${checksum}.`
|
|
@@ -1774,7 +1978,7 @@ async function downloadPinnedArchive(archive, destinationPath) {
|
|
|
1774
1978
|
(0, import_node_fs5.writeFileSync)(destinationPath, contents);
|
|
1775
1979
|
}
|
|
1776
1980
|
async function fetchPinnedArchive(archive, destinationDir) {
|
|
1777
|
-
const archivePath = (0,
|
|
1981
|
+
const archivePath = (0, import_node_path7.join)(destinationDir, `${archive.sha256}.tgz`);
|
|
1778
1982
|
try {
|
|
1779
1983
|
await downloadPinnedArchive(archive, archivePath);
|
|
1780
1984
|
(0, import_tar.extract)({ file: archivePath, cwd: destinationDir, sync: true });
|
|
@@ -1791,16 +1995,16 @@ function blobPath(layoutDir, digest) {
|
|
|
1791
1995
|
if (!match) {
|
|
1792
1996
|
throw new Error(`Unsupported OCI digest ${JSON.stringify(digest)}.`);
|
|
1793
1997
|
}
|
|
1794
|
-
return (0,
|
|
1998
|
+
return (0, import_node_path7.join)(layoutDir, "blobs", "sha256", match[1]);
|
|
1795
1999
|
}
|
|
1796
2000
|
async function fetchBuildpackage(archive, cnbDir) {
|
|
1797
|
-
const layoutDir = (0, import_node_fs5.mkdtempSync)((0,
|
|
1798
|
-
const archivePath = (0,
|
|
2001
|
+
const layoutDir = (0, import_node_fs5.mkdtempSync)((0, import_node_path7.join)((0, import_node_os6.tmpdir)(), "vercel-cnb-buildpackage-"));
|
|
2002
|
+
const archivePath = (0, import_node_path7.join)(layoutDir, "buildpackage.cnb");
|
|
1799
2003
|
try {
|
|
1800
2004
|
await downloadPinnedArchive(archive, archivePath);
|
|
1801
2005
|
await (0, import_tar.extract)({ file: archivePath, cwd: layoutDir });
|
|
1802
2006
|
const index = readOciJson(
|
|
1803
|
-
(0,
|
|
2007
|
+
(0, import_node_path7.join)(layoutDir, "index.json")
|
|
1804
2008
|
);
|
|
1805
2009
|
const name = archiveName(archive);
|
|
1806
2010
|
const manifestDigest = index.manifests?.[0]?.digest;
|
|
@@ -1822,7 +2026,7 @@ async function fetchBuildpackage(archive, cnbDir) {
|
|
|
1822
2026
|
);
|
|
1823
2027
|
}
|
|
1824
2028
|
const layerPath = blobPath(layoutDir, layer.digest);
|
|
1825
|
-
const actual = (0,
|
|
2029
|
+
const actual = (0, import_node_crypto5.createHash)("sha256").update(new Uint8Array((0, import_node_fs5.readFileSync)(layerPath))).digest("hex");
|
|
1826
2030
|
if (`sha256:${actual}` !== layer.digest) {
|
|
1827
2031
|
throw new Error(
|
|
1828
2032
|
`${name} layer ${layer.digest} hashes to sha256:${actual}.`
|
|
@@ -1859,14 +2063,14 @@ async function fetchDistribution(buildpack, cnbDir) {
|
|
|
1859
2063
|
)
|
|
1860
2064
|
]);
|
|
1861
2065
|
for (const entry of buildpack.buildpacks) {
|
|
1862
|
-
const directory = (0,
|
|
2066
|
+
const directory = (0, import_node_path7.join)(
|
|
1863
2067
|
cnbDir,
|
|
1864
2068
|
"buildpacks",
|
|
1865
2069
|
entry.id.replace("/", "_"),
|
|
1866
2070
|
entry.version
|
|
1867
2071
|
);
|
|
1868
2072
|
for (const file of ["buildpack.toml", "bin/detect", "bin/build"]) {
|
|
1869
|
-
if (!(0, import_node_fs5.statSync)((0,
|
|
2073
|
+
if (!(0, import_node_fs5.statSync)((0, import_node_path7.join)(directory, file), { throwIfNoEntry: false })?.isFile()) {
|
|
1870
2074
|
throw new Error(
|
|
1871
2075
|
`Buildpack ${entry.id}@${entry.version} is missing ${file}.`
|
|
1872
2076
|
);
|
|
@@ -1891,20 +2095,113 @@ function creatorArgs(image, imageRef, options = {}) {
|
|
|
1891
2095
|
|
|
1892
2096
|
// src/buildpacks/lifecycle/buildah.ts
|
|
1893
2097
|
var import_node_fs6 = require("fs");
|
|
1894
|
-
var
|
|
1895
|
-
var
|
|
1896
|
-
var
|
|
1897
|
-
|
|
2098
|
+
var import_node_crypto6 = require("crypto");
|
|
2099
|
+
var import_node_os8 = require("os");
|
|
2100
|
+
var import_node_path10 = require("path");
|
|
2101
|
+
|
|
2102
|
+
// src/buildah-store-cache/cleanup.ts
|
|
2103
|
+
var import_node_child_process3 = require("child_process");
|
|
2104
|
+
|
|
2105
|
+
// src/buildah-store-cache/command/index.ts
|
|
2106
|
+
var import_node_path8 = require("path");
|
|
2107
|
+
var STORE_COMMAND_SCRIPT = (0, import_node_path8.join)(__dirname, "buildah-store-command.js");
|
|
2108
|
+
function storeCommand(request, script = STORE_COMMAND_SCRIPT) {
|
|
2109
|
+
return [process.execPath, script, JSON.stringify(request)];
|
|
2110
|
+
}
|
|
2111
|
+
async function runStoreCommand(request, lock) {
|
|
2112
|
+
const [command, ...args] = lock.exclusive(storeCommand(request));
|
|
2113
|
+
const { stdout } = await run(command, args, { quiet: true });
|
|
2114
|
+
return JSON.parse(stdout);
|
|
2115
|
+
}
|
|
2116
|
+
|
|
2117
|
+
// src/buildah-store-cache/lock.ts
|
|
2118
|
+
var import_node_os7 = require("os");
|
|
2119
|
+
var import_node_path9 = require("path");
|
|
2120
|
+
var FileLock = class {
|
|
2121
|
+
constructor(path4) {
|
|
2122
|
+
this.path = path4;
|
|
2123
|
+
}
|
|
2124
|
+
shared(command) {
|
|
2125
|
+
return this.wrap("--shared", command);
|
|
2126
|
+
}
|
|
2127
|
+
exclusive(command) {
|
|
2128
|
+
return this.wrap("--exclusive", command);
|
|
2129
|
+
}
|
|
2130
|
+
wrap(mode, command) {
|
|
2131
|
+
return ["flock", mode, "--close", this.path, ...command];
|
|
2132
|
+
}
|
|
2133
|
+
};
|
|
2134
|
+
var flockAvailable;
|
|
2135
|
+
async function buildahStoreLock() {
|
|
2136
|
+
if (!buildahStoreCacheEnabled()) {
|
|
2137
|
+
return void 0;
|
|
2138
|
+
}
|
|
2139
|
+
flockAvailable ??= hasCommand("flock");
|
|
2140
|
+
return await flockAvailable ? new FileLock((0, import_node_path9.join)((0, import_node_os7.tmpdir)(), "vercel-buildah-store.lock")) : void 0;
|
|
2141
|
+
}
|
|
2142
|
+
var sharedBuildahStoreLock = {
|
|
2143
|
+
async wrap(command) {
|
|
2144
|
+
const lock = await buildahStoreLock();
|
|
2145
|
+
return lock ? lock.shared(command) : command;
|
|
2146
|
+
}
|
|
2147
|
+
};
|
|
2148
|
+
|
|
2149
|
+
// src/buildah-store-cache/cleanup.ts
|
|
2150
|
+
async function startContainerCleanup(name) {
|
|
2151
|
+
const rmArgs = [...await buildahStorageArgs(), "rm", name];
|
|
2152
|
+
debug(`exec in the background: buildah ${rmArgs.join(" ")}`);
|
|
2153
|
+
const start = Date.now();
|
|
2154
|
+
const [command, ...commandArgs] = await cleanupCommand(name, rmArgs);
|
|
2155
|
+
const child = (0, import_node_child_process3.spawn)(command, commandArgs, {
|
|
2156
|
+
detached: true,
|
|
2157
|
+
stdio: "ignore"
|
|
2158
|
+
});
|
|
2159
|
+
child.on(
|
|
2160
|
+
"error",
|
|
2161
|
+
(error) => debug(`background buildah rm failed: ${error.message}`)
|
|
2162
|
+
);
|
|
2163
|
+
child.on(
|
|
2164
|
+
"exit",
|
|
2165
|
+
(code) => debug(
|
|
2166
|
+
`background buildah rm exited with code ${code} after ${Date.now() - start}ms`
|
|
2167
|
+
)
|
|
2168
|
+
);
|
|
2169
|
+
child.unref();
|
|
2170
|
+
}
|
|
2171
|
+
async function cleanupCommand(container, buildahArgs) {
|
|
2172
|
+
const record = await recordPendingCleanup(container);
|
|
2173
|
+
return sharedBuildahStoreLock.wrap(
|
|
2174
|
+
record ? storeCommand({ action: "cleanup", options: { record, buildahArgs } }) : ["buildah", ...buildahArgs]
|
|
2175
|
+
);
|
|
2176
|
+
}
|
|
2177
|
+
async function recordPendingCleanup(container) {
|
|
2178
|
+
if (!buildahStoreCacheEnabled()) {
|
|
2179
|
+
return void 0;
|
|
2180
|
+
}
|
|
2181
|
+
try {
|
|
2182
|
+
if (!CLEANUP_CONTAINER_RE.test(container)) {
|
|
2183
|
+
throw new Error("invalid Buildah cleanup container");
|
|
2184
|
+
}
|
|
2185
|
+
return await writeStateRecord(".cleanup", `${container}
|
|
2186
|
+
`);
|
|
2187
|
+
} catch (error) {
|
|
2188
|
+
debug(`could not record Buildah cleanup: ${error.message}`);
|
|
2189
|
+
return void 0;
|
|
2190
|
+
}
|
|
2191
|
+
}
|
|
2192
|
+
|
|
2193
|
+
// src/buildpacks/lifecycle/buildah.ts
|
|
2194
|
+
async function cleanUpBuildContainer(name) {
|
|
1898
2195
|
try {
|
|
1899
|
-
await
|
|
2196
|
+
await startContainerCleanup(name);
|
|
1900
2197
|
} catch (error) {
|
|
1901
2198
|
debug(
|
|
1902
|
-
`could not
|
|
2199
|
+
`could not clean up Buildah CNB container ${name}: ${error.message}`
|
|
1903
2200
|
);
|
|
1904
2201
|
}
|
|
1905
2202
|
}
|
|
1906
|
-
async function inspectBaseImageId(container) {
|
|
1907
|
-
const { stdout } = await
|
|
2203
|
+
async function inspectBaseImageId(container, client) {
|
|
2204
|
+
const { stdout } = await client.run(
|
|
1908
2205
|
["inspect", "--type", "container", container],
|
|
1909
2206
|
{ quiet: true }
|
|
1910
2207
|
);
|
|
@@ -1929,14 +2226,14 @@ function cacheScratchParent() {
|
|
|
1929
2226
|
);
|
|
1930
2227
|
}
|
|
1931
2228
|
}
|
|
1932
|
-
return (0,
|
|
2229
|
+
return (0, import_node_os8.tmpdir)();
|
|
1933
2230
|
}
|
|
1934
2231
|
function chownTree(root, uid, gid) {
|
|
1935
2232
|
const stat = (0, import_node_fs6.lstatSync)(root);
|
|
1936
2233
|
if (stat.isDirectory()) {
|
|
1937
2234
|
(0, import_node_fs6.chmodSync)(root, 493);
|
|
1938
2235
|
for (const entry of (0, import_node_fs6.readdirSync)(root)) {
|
|
1939
|
-
chownTree((0,
|
|
2236
|
+
chownTree((0, import_node_path10.join)(root, entry), uid, gid);
|
|
1940
2237
|
}
|
|
1941
2238
|
} else if (stat.isFile()) {
|
|
1942
2239
|
(0, import_node_fs6.chmodSync)(root, 420);
|
|
@@ -1949,12 +2246,13 @@ var RegistrySetupFailure = class extends Error {
|
|
|
1949
2246
|
this.reason = reason;
|
|
1950
2247
|
}
|
|
1951
2248
|
};
|
|
1952
|
-
async function prepareBuildContainer(buildContainer, buildpack, params) {
|
|
2249
|
+
async function prepareBuildContainer(buildContainer, buildpack, params, client) {
|
|
1953
2250
|
const start = Date.now();
|
|
1954
2251
|
step(
|
|
1955
2252
|
`Preparing ${buildpack.runtime} ${params.image.version} stock image and Vercel buildpacks`
|
|
1956
2253
|
);
|
|
1957
|
-
await
|
|
2254
|
+
const usage = await BuildahImageUsage.begin();
|
|
2255
|
+
await client.run([
|
|
1958
2256
|
"from",
|
|
1959
2257
|
"--pull=always",
|
|
1960
2258
|
"--platform",
|
|
@@ -1963,7 +2261,10 @@ async function prepareBuildContainer(buildContainer, buildpack, params) {
|
|
|
1963
2261
|
buildContainer,
|
|
1964
2262
|
params.image.buildImage
|
|
1965
2263
|
]);
|
|
1966
|
-
await
|
|
2264
|
+
await usage?.record(async () => [
|
|
2265
|
+
await inspectBaseImageId(buildContainer, client)
|
|
2266
|
+
]);
|
|
2267
|
+
await client.run([
|
|
1967
2268
|
"run",
|
|
1968
2269
|
"--network",
|
|
1969
2270
|
"host",
|
|
@@ -1973,7 +2274,7 @@ async function prepareBuildContainer(buildContainer, buildpack, params) {
|
|
|
1973
2274
|
"-c",
|
|
1974
2275
|
`groupadd -g ${BUILD_GROUP_ID} cnb && useradd -m -u ${BUILD_USER_ID} -g ${BUILD_GROUP_ID} cnb && mkdir -p /layers /platform /workspace && chown -R ${BUILD_USER} /layers /platform /workspace /home/cnb`
|
|
1975
2276
|
]);
|
|
1976
|
-
await
|
|
2277
|
+
await client.run([
|
|
1977
2278
|
"copy",
|
|
1978
2279
|
"--chown",
|
|
1979
2280
|
BUILD_USER,
|
|
@@ -1989,17 +2290,17 @@ async function fetchDistributionTimed(buildpack, cnbDir) {
|
|
|
1989
2290
|
await fetchDistribution(buildpack, cnbDir);
|
|
1990
2291
|
done(`fetched the CNB lifecycle and buildpacks in ${elapsed(start)}`);
|
|
1991
2292
|
}
|
|
1992
|
-
async function copySavedCache(dir, buildContainer, containerReady) {
|
|
2293
|
+
async function copySavedCache(dir, buildContainer, containerReady, client) {
|
|
1993
2294
|
const start = Date.now();
|
|
1994
2295
|
step("Restoring the buildpack cache");
|
|
1995
2296
|
let scratch;
|
|
1996
2297
|
try {
|
|
1997
|
-
scratch = (0, import_node_fs6.mkdtempSync)((0,
|
|
2298
|
+
scratch = (0, import_node_fs6.mkdtempSync)((0, import_node_path10.join)(cacheScratchParent(), "vercel-cnb-cache-"));
|
|
1998
2299
|
(0, import_node_fs6.chmodSync)(scratch, 493);
|
|
1999
|
-
const cacheDir = (0,
|
|
2300
|
+
const cacheDir = (0, import_node_path10.join)(scratch, "cache");
|
|
2000
2301
|
(0, import_node_fs6.mkdirSync)(cacheDir, { mode: 493 });
|
|
2001
2302
|
const imageId = containerReady.then(
|
|
2002
|
-
() => inspectBaseImageId(buildContainer)
|
|
2303
|
+
() => inspectBaseImageId(buildContainer, client)
|
|
2003
2304
|
);
|
|
2004
2305
|
const restored = await restoreBuildpackCache(dir, imageId, cacheDir);
|
|
2005
2306
|
const baseImageId = await imageId;
|
|
@@ -2025,7 +2326,7 @@ async function copySavedCache(dir, buildContainer, containerReady) {
|
|
|
2025
2326
|
};
|
|
2026
2327
|
}
|
|
2027
2328
|
}
|
|
2028
|
-
async function buildAndPushWithLifecycle(buildpack, params, span) {
|
|
2329
|
+
async function buildAndPushWithLifecycle(buildpack, params, client, span) {
|
|
2029
2330
|
const urls = distributionUrls(buildpack);
|
|
2030
2331
|
return withSpan(
|
|
2031
2332
|
span,
|
|
@@ -2041,20 +2342,20 @@ async function buildAndPushWithLifecycle(buildpack, params, span) {
|
|
|
2041
2342
|
"image.ref": params.imageRef
|
|
2042
2343
|
},
|
|
2043
2344
|
async (lifecycleSpan) => {
|
|
2044
|
-
const suffix = `${process.pid}-${(0,
|
|
2345
|
+
const suffix = `${process.pid}-${(0, import_node_crypto6.randomBytes)(4).toString("hex")}`;
|
|
2045
2346
|
const buildContainer = `vercel-cnb-${buildpack.runtime}-${suffix}`;
|
|
2046
2347
|
let cnbDir;
|
|
2047
2348
|
let reportDir;
|
|
2048
2349
|
let orderDir;
|
|
2049
2350
|
let platformEnvDir;
|
|
2050
2351
|
let cacheScratch;
|
|
2051
|
-
let
|
|
2352
|
+
let containerCleanup;
|
|
2052
2353
|
try {
|
|
2053
|
-
reportDir = (0, import_node_fs6.mkdtempSync)((0,
|
|
2054
|
-
const reportPath = (0,
|
|
2354
|
+
reportDir = (0, import_node_fs6.mkdtempSync)((0, import_node_path10.join)((0, import_node_os8.tmpdir)(), "vercel-cnb-report-"));
|
|
2355
|
+
const reportPath = (0, import_node_path10.join)(reportDir, "report.toml");
|
|
2055
2356
|
(0, import_node_fs6.chownSync)(reportDir, BUILD_USER_ID, BUILD_GROUP_ID);
|
|
2056
2357
|
(0, import_node_fs6.chmodSync)(reportDir, 493);
|
|
2057
|
-
cnbDir = (0, import_node_fs6.mkdtempSync)((0,
|
|
2358
|
+
cnbDir = (0, import_node_fs6.mkdtempSync)((0, import_node_path10.join)((0, import_node_os8.tmpdir)(), "vercel-cnb-"));
|
|
2058
2359
|
(0, import_node_fs6.chmodSync)(cnbDir, 493);
|
|
2059
2360
|
orderDir = writeOrderDir(buildpack);
|
|
2060
2361
|
platformEnvDir = writePlatformEnvDir(params.buildEnv, {
|
|
@@ -2066,13 +2367,19 @@ async function buildAndPushWithLifecycle(buildpack, params, span) {
|
|
|
2066
2367
|
const containerReady = prepareBuildContainer(
|
|
2067
2368
|
buildContainer,
|
|
2068
2369
|
buildpack,
|
|
2069
|
-
params
|
|
2370
|
+
params,
|
|
2371
|
+
client
|
|
2070
2372
|
);
|
|
2071
2373
|
const [registry, container, distribution2, cacheCopy] = await Promise.allSettled([
|
|
2072
2374
|
registrySetup,
|
|
2073
2375
|
containerReady,
|
|
2074
2376
|
fetchDistributionTimed(buildpack, cnbDir),
|
|
2075
|
-
params.cache ? copySavedCache(
|
|
2377
|
+
params.cache ? copySavedCache(
|
|
2378
|
+
params.cache.dir,
|
|
2379
|
+
buildContainer,
|
|
2380
|
+
containerReady,
|
|
2381
|
+
client
|
|
2382
|
+
) : void 0
|
|
2076
2383
|
]);
|
|
2077
2384
|
const copy = cacheCopy.status === "fulfilled" ? cacheCopy.value : void 0;
|
|
2078
2385
|
cacheScratch = copy?.scratch;
|
|
@@ -2105,7 +2412,7 @@ async function buildAndPushWithLifecycle(buildpack, params, span) {
|
|
|
2105
2412
|
step(
|
|
2106
2413
|
`Building and publishing ${params.imageRef} via Vercel ${buildpack.runtime} buildpacks`
|
|
2107
2414
|
);
|
|
2108
|
-
await
|
|
2415
|
+
await client.run(
|
|
2109
2416
|
[
|
|
2110
2417
|
"run",
|
|
2111
2418
|
"--network",
|
|
@@ -2142,7 +2449,7 @@ async function buildAndPushWithLifecycle(buildpack, params, span) {
|
|
|
2142
2449
|
],
|
|
2143
2450
|
{ env: lifecycleEnv }
|
|
2144
2451
|
);
|
|
2145
|
-
|
|
2452
|
+
containerCleanup = cleanUpBuildContainer(buildContainer);
|
|
2146
2453
|
const digest = readReportDigest((0, import_node_fs6.readFileSync)(reportPath, "utf8"));
|
|
2147
2454
|
if (!digest) {
|
|
2148
2455
|
throw new Error(
|
|
@@ -2179,7 +2486,7 @@ async function buildAndPushWithLifecycle(buildpack, params, span) {
|
|
|
2179
2486
|
span: lifecycleSpan
|
|
2180
2487
|
});
|
|
2181
2488
|
} finally {
|
|
2182
|
-
|
|
2489
|
+
containerCleanup ??= cleanUpBuildContainer(buildContainer);
|
|
2183
2490
|
for (const dir of [
|
|
2184
2491
|
cnbDir,
|
|
2185
2492
|
reportDir,
|
|
@@ -2195,17 +2502,17 @@ async function buildAndPushWithLifecycle(buildpack, params, span) {
|
|
|
2195
2502
|
}
|
|
2196
2503
|
}
|
|
2197
2504
|
}
|
|
2198
|
-
await
|
|
2505
|
+
await containerCleanup;
|
|
2199
2506
|
}
|
|
2200
2507
|
}
|
|
2201
2508
|
);
|
|
2202
2509
|
}
|
|
2203
2510
|
|
|
2204
2511
|
// src/buildpacks/lifecycle/docker.ts
|
|
2205
|
-
var
|
|
2512
|
+
var import_node_crypto7 = require("crypto");
|
|
2206
2513
|
var import_node_fs7 = require("fs");
|
|
2207
|
-
var
|
|
2208
|
-
var
|
|
2514
|
+
var import_node_os9 = require("os");
|
|
2515
|
+
var import_node_path11 = require("path");
|
|
2209
2516
|
function localBuildImageDockerfile(buildImage, nodeMajor) {
|
|
2210
2517
|
const lines = [];
|
|
2211
2518
|
if (nodeMajor !== void 0) {
|
|
@@ -2224,7 +2531,7 @@ function localBuildImageDockerfile(buildImage, nodeMajor) {
|
|
|
2224
2531
|
`;
|
|
2225
2532
|
}
|
|
2226
2533
|
function localBuildImageTag(dockerfile) {
|
|
2227
|
-
const hash = (0,
|
|
2534
|
+
const hash = (0, import_node_crypto7.createHash)("sha256").update(dockerfile).digest("hex");
|
|
2228
2535
|
return `vercel-cnb-build:${hash.slice(0, 16)}`;
|
|
2229
2536
|
}
|
|
2230
2537
|
async function runDocker2(args, opts = {}) {
|
|
@@ -2250,8 +2557,8 @@ var buildAndPushWithLifecycleDocker = async (buildpack, params, span) => {
|
|
|
2250
2557
|
let stageDir;
|
|
2251
2558
|
const containers = [];
|
|
2252
2559
|
try {
|
|
2253
|
-
stageDir = (0, import_node_fs7.mkdtempSync)((0,
|
|
2254
|
-
const cnbDir = (0,
|
|
2560
|
+
stageDir = (0, import_node_fs7.mkdtempSync)((0, import_node_path11.join)((0, import_node_os9.tmpdir)(), "vercel-cnb-"));
|
|
2561
|
+
const cnbDir = (0, import_node_path11.join)(stageDir, "cnb");
|
|
2255
2562
|
(0, import_node_fs7.mkdirSync)(cnbDir, { mode: 493 });
|
|
2256
2563
|
const orderDir = writeOrderDir(buildpack, stageDir);
|
|
2257
2564
|
const platformEnvDir = writePlatformEnvDir(
|
|
@@ -2271,7 +2578,7 @@ var buildAndPushWithLifecycleDocker = async (buildpack, params, span) => {
|
|
|
2271
2578
|
params.nodeToolchain?.major
|
|
2272
2579
|
);
|
|
2273
2580
|
const buildImageTag = localBuildImageTag(dockerfile);
|
|
2274
|
-
const buildImageIdPath = (0,
|
|
2581
|
+
const buildImageIdPath = (0, import_node_path11.join)(stageDir, "build-image.id");
|
|
2275
2582
|
step(
|
|
2276
2583
|
`Preparing ${buildpack.runtime} ${params.image.version} build image` + (params.nodeToolchain ? ` with Node.js ${params.nodeToolchain.major}` : "")
|
|
2277
2584
|
);
|
|
@@ -2293,7 +2600,7 @@ var buildAndPushWithLifecycleDocker = async (buildpack, params, span) => {
|
|
|
2293
2600
|
if (!/^sha256:[a-f0-9]{64}$/.test(buildImageId)) {
|
|
2294
2601
|
throw new Error("Docker build did not report a valid image ID.");
|
|
2295
2602
|
}
|
|
2296
|
-
const containerName = `vercel-cnb-${buildpack.runtime}-${process.pid}-${(0,
|
|
2603
|
+
const containerName = `vercel-cnb-${buildpack.runtime}-${process.pid}-${(0, import_node_crypto7.randomBytes)(8).toString("hex")}`;
|
|
2297
2604
|
const initContainerName = `${containerName}-init`;
|
|
2298
2605
|
containers.push(initContainerName);
|
|
2299
2606
|
await runDocker2([
|
|
@@ -2377,7 +2684,7 @@ var buildAndPushWithLifecycleDocker = async (buildpack, params, span) => {
|
|
|
2377
2684
|
`Building and publishing ${params.imageRef} via Vercel ${buildpack.runtime} buildpacks`
|
|
2378
2685
|
);
|
|
2379
2686
|
await runDocker2(["start", "--attach", containerName]);
|
|
2380
|
-
const reportPath = (0,
|
|
2687
|
+
const reportPath = (0, import_node_path11.join)(stageDir, "report.toml");
|
|
2381
2688
|
await runDocker2([
|
|
2382
2689
|
"cp",
|
|
2383
2690
|
`${containerName}:/platform-output/report.toml`,
|
|
@@ -2431,15 +2738,15 @@ var buildAndPushWithLifecycleDocker = async (buildpack, params, span) => {
|
|
|
2431
2738
|
};
|
|
2432
2739
|
|
|
2433
2740
|
// src/buildpacks/node-toolchain.ts
|
|
2434
|
-
var
|
|
2741
|
+
var import_build_utils5 = require("@vercel/build-utils");
|
|
2435
2742
|
var import_node_fs8 = require("fs");
|
|
2436
|
-
var
|
|
2743
|
+
var import_node_path12 = require("path");
|
|
2437
2744
|
async function selectNodeMajor(workPath, config, meta) {
|
|
2438
|
-
if (!(0, import_node_fs8.existsSync)((0,
|
|
2745
|
+
if (!(0, import_node_fs8.existsSync)((0, import_node_path12.join)(workPath, "package.json"))) {
|
|
2439
2746
|
return void 0;
|
|
2440
2747
|
}
|
|
2441
|
-
const version2 = await (0,
|
|
2442
|
-
if ((0,
|
|
2748
|
+
const version2 = await (0, import_build_utils5.getNodeVersion)(workPath, void 0, config, meta);
|
|
2749
|
+
if ((0, import_build_utils5.isBunVersion)(version2)) {
|
|
2443
2750
|
throw new Error(
|
|
2444
2751
|
"Buildpack asset builds require Node.js. Bun is not supported yet; configure engines.node or use Dockerfile.vercel."
|
|
2445
2752
|
);
|
|
@@ -2456,7 +2763,7 @@ async function resolveNodeToolchain(workPath, config, meta) {
|
|
|
2456
2763
|
return void 0;
|
|
2457
2764
|
}
|
|
2458
2765
|
for (const executable of ["node", "npm"]) {
|
|
2459
|
-
if (!(0, import_node_fs8.existsSync)((0,
|
|
2766
|
+
if (!(0, import_node_fs8.existsSync)((0, import_node_path12.join)(toolchain.home, "bin", executable))) {
|
|
2460
2767
|
throw new Error(
|
|
2461
2768
|
`The selected Node.js ${toolchain.version} installation is missing ${toolchain.home}/bin/${executable} in the build environment.`
|
|
2462
2769
|
);
|
|
@@ -2470,11 +2777,11 @@ async function resolveLocalNodeToolchain(workPath, config, meta) {
|
|
|
2470
2777
|
|
|
2471
2778
|
// src/buildpacks/registry.ts
|
|
2472
2779
|
var import_node_fs12 = require("fs");
|
|
2473
|
-
var
|
|
2780
|
+
var import_node_path16 = require("path");
|
|
2474
2781
|
|
|
2475
2782
|
// src/buildpacks/go.ts
|
|
2476
2783
|
var import_node_fs9 = require("fs");
|
|
2477
|
-
var
|
|
2784
|
+
var import_node_path13 = require("path");
|
|
2478
2785
|
var DEFAULT_GO_VERSION = "1";
|
|
2479
2786
|
var GO_IMAGE_REPOSITORY = "docker.io/library/golang";
|
|
2480
2787
|
var GO_BUILD_IMAGE_DISTRO = "bookworm";
|
|
@@ -2503,7 +2810,7 @@ function compareImageVersions(a, b) {
|
|
|
2503
2810
|
return a.parts[0] - b.parts[0] || a.parts[1] - b.parts[1] || a.parts[2] - b.parts[2];
|
|
2504
2811
|
}
|
|
2505
2812
|
function selectGoVersion(workPath) {
|
|
2506
|
-
const goMod = (0,
|
|
2813
|
+
const goMod = (0, import_node_path13.join)(workPath, "go.mod");
|
|
2507
2814
|
if (!(0, import_node_fs9.existsSync)(goMod))
|
|
2508
2815
|
return DEFAULT_GO_VERSION;
|
|
2509
2816
|
const contents = (0, import_node_fs9.readFileSync)(goMod, "utf8");
|
|
@@ -2528,10 +2835,10 @@ var go = {
|
|
|
2528
2835
|
},
|
|
2529
2836
|
defaultEntrypoint({ workPath, zeroConfig }) {
|
|
2530
2837
|
const candidate = GO_CANDIDATE_ENTRYPOINTS.find(
|
|
2531
|
-
(name) => (0, import_node_fs9.existsSync)((0,
|
|
2838
|
+
(name) => (0, import_node_fs9.existsSync)((0, import_node_path13.join)(workPath, name))
|
|
2532
2839
|
);
|
|
2533
2840
|
if (candidate)
|
|
2534
|
-
return
|
|
2841
|
+
return import_node_path13.posix.dirname(candidate);
|
|
2535
2842
|
if (zeroConfig) {
|
|
2536
2843
|
const last = GO_CANDIDATE_ENTRYPOINTS.length - 1;
|
|
2537
2844
|
throw new Error(
|
|
@@ -2544,7 +2851,7 @@ var go = {
|
|
|
2544
2851
|
|
|
2545
2852
|
// src/buildpacks/ruby.ts
|
|
2546
2853
|
var import_node_fs10 = require("fs");
|
|
2547
|
-
var
|
|
2854
|
+
var import_node_path14 = require("path");
|
|
2548
2855
|
var import_smol_toml2 = require("smol-toml");
|
|
2549
2856
|
|
|
2550
2857
|
// src/buildpacks/version.ts
|
|
@@ -2576,7 +2883,7 @@ var ruby = {
|
|
|
2576
2883
|
return { version: version2, buildImage: image, runImage: image };
|
|
2577
2884
|
},
|
|
2578
2885
|
validate({ workPath, buildEnv }) {
|
|
2579
|
-
const gemfile = (0,
|
|
2886
|
+
const gemfile = (0, import_node_path14.join)(workPath, "Gemfile");
|
|
2580
2887
|
if (!(0, import_node_fs10.existsSync)(gemfile))
|
|
2581
2888
|
return;
|
|
2582
2889
|
if (!GEMFILE_RAILS_GEM_RE.test((0, import_node_fs10.readFileSync)(gemfile, "utf8")))
|
|
@@ -2590,12 +2897,12 @@ var ruby = {
|
|
|
2590
2897
|
};
|
|
2591
2898
|
function selectRubyVersion(workPath) {
|
|
2592
2899
|
for (const name of RUBY_VERSION_FILES) {
|
|
2593
|
-
const file = (0,
|
|
2900
|
+
const file = (0, import_node_path14.join)(workPath, name);
|
|
2594
2901
|
if ((0, import_node_fs10.existsSync)(file)) {
|
|
2595
2902
|
return parseVersionFile(name, (0, import_node_fs10.readFileSync)(file, "utf8"));
|
|
2596
2903
|
}
|
|
2597
2904
|
}
|
|
2598
|
-
const gemfile = (0,
|
|
2905
|
+
const gemfile = (0, import_node_path14.join)(workPath, "Gemfile");
|
|
2599
2906
|
return (0, import_node_fs10.existsSync)(gemfile) ? parseGemfileRubyVersion((0, import_node_fs10.readFileSync)(gemfile, "utf8")) : void 0;
|
|
2600
2907
|
}
|
|
2601
2908
|
function parseVersionFile(name, contents) {
|
|
@@ -2634,7 +2941,7 @@ function parseGemfileRubyVersion(contents) {
|
|
|
2634
2941
|
|
|
2635
2942
|
// src/buildpacks/rust.ts
|
|
2636
2943
|
var import_node_fs11 = require("fs");
|
|
2637
|
-
var
|
|
2944
|
+
var import_node_path15 = require("path");
|
|
2638
2945
|
var import_smol_toml3 = require("smol-toml");
|
|
2639
2946
|
var DEFAULT_RUST_VERSION = "1";
|
|
2640
2947
|
var RUST_IMAGE_REPOSITORY = "docker.io/library/rust";
|
|
@@ -2654,12 +2961,12 @@ var rust = {
|
|
|
2654
2961
|
};
|
|
2655
2962
|
},
|
|
2656
2963
|
defaultEntrypoint({ workPath, zeroConfig }) {
|
|
2657
|
-
return zeroConfig && (0, import_node_fs11.existsSync)((0,
|
|
2964
|
+
return zeroConfig && (0, import_node_fs11.existsSync)((0, import_node_path15.join)(workPath, "src/main.rs")) ? "src/main.rs" : void 0;
|
|
2658
2965
|
}
|
|
2659
2966
|
};
|
|
2660
2967
|
function selectRustVersion(workPath) {
|
|
2661
2968
|
for (const name of RUST_TOOLCHAIN_FILES) {
|
|
2662
|
-
const file = (0,
|
|
2969
|
+
const file = (0, import_node_path15.join)(workPath, name);
|
|
2663
2970
|
if ((0, import_node_fs11.existsSync)(file)) {
|
|
2664
2971
|
const channel = parseToolchainFile(name, (0, import_node_fs11.readFileSync)(file, "utf8"));
|
|
2665
2972
|
return channel !== void 0 && isValidVersion(channel) ? channel : void 0;
|
|
@@ -2705,7 +3012,7 @@ function requestedBuildpack(config) {
|
|
|
2705
3012
|
}
|
|
2706
3013
|
function hasProjectMarkers(buildpack, workPath) {
|
|
2707
3014
|
return buildpack.projectMarkers.some(
|
|
2708
|
-
(name) => (0, import_node_fs12.existsSync)((0,
|
|
3015
|
+
(name) => (0, import_node_fs12.existsSync)((0, import_node_path16.join)(workPath, name))
|
|
2709
3016
|
);
|
|
2710
3017
|
}
|
|
2711
3018
|
|
|
@@ -2719,11 +3026,12 @@ function selectEntrypoint(params, sourceDir) {
|
|
|
2719
3026
|
return fromEnv ?? params.buildpack.defaultEntrypoint?.({ workPath: sourceDir, zeroConfig });
|
|
2720
3027
|
}
|
|
2721
3028
|
async function buildAndPushBuildpack(params) {
|
|
2722
|
-
const sourceDir = (0, import_node_fs13.mkdtempSync)((0,
|
|
3029
|
+
const sourceDir = (0, import_node_fs13.mkdtempSync)((0, import_node_path17.join)((0, import_node_os10.tmpdir)(), "vercel-cnb-source-"));
|
|
2723
3030
|
try {
|
|
2724
3031
|
step("Staging buildpack source files");
|
|
2725
3032
|
await stageWorkspace(params, sourceDir);
|
|
2726
|
-
const
|
|
3033
|
+
const { buildahClient } = params;
|
|
3034
|
+
const engine = selectContainerEngine(buildahClient);
|
|
2727
3035
|
if (!hasProjectMarkers(params.buildpack, sourceDir)) {
|
|
2728
3036
|
throw new Error(
|
|
2729
3037
|
`The ${params.buildpack.runtime} buildpack was selected, but no supported project marker was found in the service sources. Add ${params.buildpack.projectMarkers.join(" or ")}, or add a Dockerfile.vercel to control the image build.`
|
|
@@ -2840,6 +3148,7 @@ async function buildAndPushBuildpack(params) {
|
|
|
2840
3148
|
result = await buildAndPushWithLifecycle(
|
|
2841
3149
|
buildpack,
|
|
2842
3150
|
{ ...lifecycleParams, prepareRegistry },
|
|
3151
|
+
buildahClient,
|
|
2843
3152
|
buildSpan
|
|
2844
3153
|
);
|
|
2845
3154
|
} else {
|
|
@@ -2871,7 +3180,7 @@ async function buildAndPushBuildpack(params) {
|
|
|
2871
3180
|
|
|
2872
3181
|
// src/image-source.ts
|
|
2873
3182
|
var import_node_fs14 = require("fs");
|
|
2874
|
-
var
|
|
3183
|
+
var import_node_path18 = __toESM(require("path"));
|
|
2875
3184
|
var DETECT_SENTINEL = "<detect>";
|
|
2876
3185
|
function resolveImageSource(options, context) {
|
|
2877
3186
|
const { config, workPath, entrypoint } = options;
|
|
@@ -2888,7 +3197,7 @@ function resolveImageSource(options, context) {
|
|
|
2888
3197
|
}
|
|
2889
3198
|
const dockerfileConfigured = entrypointRef && isDockerfileRef(entrypointRef) ? entrypointRef : findDockerfile(workPath);
|
|
2890
3199
|
const dockerfileRel = dockerfileConfigured ?? "Dockerfile";
|
|
2891
|
-
const dockerfilePath =
|
|
3200
|
+
const dockerfilePath = import_node_path18.default.join(workPath, dockerfileRel);
|
|
2892
3201
|
const hasDockerfile = dockerfileConfigured !== void 0 || !buildpack && (0, import_node_fs14.existsSync)(dockerfilePath);
|
|
2893
3202
|
if (hasDockerfile) {
|
|
2894
3203
|
return { kind: "dockerfile", dockerfileRel, dockerfilePath };
|
|
@@ -2905,11 +3214,252 @@ function resolveImageSource(options, context) {
|
|
|
2905
3214
|
);
|
|
2906
3215
|
}
|
|
2907
3216
|
|
|
3217
|
+
// src/buildah-store-cache/cache.ts
|
|
3218
|
+
var import_build_utils6 = require("@vercel/build-utils");
|
|
3219
|
+
var import_node_crypto8 = require("crypto");
|
|
3220
|
+
var import_promises7 = require("fs/promises");
|
|
3221
|
+
var import_node_os11 = require("os");
|
|
3222
|
+
var import_node_path19 = require("path");
|
|
3223
|
+
var BUILDAH_STORE_CACHE_DIR = ".vercel/cache/buildah-store";
|
|
3224
|
+
var STORE_PATHS = {
|
|
3225
|
+
graphRoot: BUILDAH_GRAPH_ROOT,
|
|
3226
|
+
// Buildah's default when running as root, as it does in the build container.
|
|
3227
|
+
blobInfoCache: "/var/lib/containers/cache/blob-info-cache-v1.sqlite"
|
|
3228
|
+
};
|
|
3229
|
+
var BuildahStoreCache = class _BuildahStoreCache {
|
|
3230
|
+
constructor(repositoryRoot, client) {
|
|
3231
|
+
this.repositoryRoot = repositoryRoot;
|
|
3232
|
+
this.client = client;
|
|
3233
|
+
}
|
|
3234
|
+
static forProject(options, client) {
|
|
3235
|
+
return new _BuildahStoreCache(
|
|
3236
|
+
(0, import_node_path19.resolve)(options.repoRootPath || options.workPath),
|
|
3237
|
+
client
|
|
3238
|
+
);
|
|
3239
|
+
}
|
|
3240
|
+
/**
|
|
3241
|
+
* From `build()`, before a Buildah service opens the store or reads its build
|
|
3242
|
+
* context. A service that builds nothing leaves the snapshot for a later
|
|
3243
|
+
* deployment.
|
|
3244
|
+
*/
|
|
3245
|
+
async beforeBuild(options) {
|
|
3246
|
+
if (!isBuildContainer()) {
|
|
3247
|
+
return;
|
|
3248
|
+
}
|
|
3249
|
+
if (buildahStoreCacheEnabled()) {
|
|
3250
|
+
if (options.buildsWithBuildah) {
|
|
3251
|
+
await this.restore(options.span);
|
|
3252
|
+
}
|
|
3253
|
+
return;
|
|
3254
|
+
}
|
|
3255
|
+
if (buildahStoreCacheRequested()) {
|
|
3256
|
+
info("Buildah store cache disabled: VERCEL_DEPLOYMENT_ID is not set");
|
|
3257
|
+
}
|
|
3258
|
+
await this.removeSnapshots();
|
|
3259
|
+
}
|
|
3260
|
+
/** From `prepareCache`, which the platform runs once every service has built. */
|
|
3261
|
+
async afterBuilds() {
|
|
3262
|
+
if (!isBuildContainer()) {
|
|
3263
|
+
return {};
|
|
3264
|
+
}
|
|
3265
|
+
if (buildahStoreCacheEnabled()) {
|
|
3266
|
+
return this.save();
|
|
3267
|
+
}
|
|
3268
|
+
await this.removeSnapshots();
|
|
3269
|
+
return {};
|
|
3270
|
+
}
|
|
3271
|
+
async restore(span) {
|
|
3272
|
+
const start = Date.now();
|
|
3273
|
+
step("Restoring the Buildah store cache");
|
|
3274
|
+
let result = "miss";
|
|
3275
|
+
let reason;
|
|
3276
|
+
let bytes = 0;
|
|
3277
|
+
try {
|
|
3278
|
+
const archive = await this.archivePath({ create: false });
|
|
3279
|
+
const restored = await restoreStore(archive);
|
|
3280
|
+
result = restored.status;
|
|
3281
|
+
if (restored.status === "hit")
|
|
3282
|
+
bytes = restored.bytes;
|
|
3283
|
+
if (restored.status === "miss")
|
|
3284
|
+
reason = restored.reason;
|
|
3285
|
+
} catch (error) {
|
|
3286
|
+
result = "miss";
|
|
3287
|
+
reason = failureReason(error);
|
|
3288
|
+
}
|
|
3289
|
+
await this.removeSnapshots();
|
|
3290
|
+
const restoredBytes = result === "hit" ? bytes : 0;
|
|
3291
|
+
const detail = reason ?? (result === "hit" ? formatBytes(restoredBytes) : void 0);
|
|
3292
|
+
done(
|
|
3293
|
+
`Buildah store cache: ${result}${detail ? ` (${detail})` : ""} in ${elapsed(start)}`
|
|
3294
|
+
);
|
|
3295
|
+
span?.setAttributes({
|
|
3296
|
+
"container.buildah_store.restore": result,
|
|
3297
|
+
"container.buildah_store.restore_ms": String(Date.now() - start),
|
|
3298
|
+
"container.buildah_store.restore_bytes": String(restoredBytes),
|
|
3299
|
+
"container.buildah_store.miss_reason": reason
|
|
3300
|
+
});
|
|
3301
|
+
}
|
|
3302
|
+
async save() {
|
|
3303
|
+
const start = Date.now();
|
|
3304
|
+
step("Saving the Buildah store cache");
|
|
3305
|
+
try {
|
|
3306
|
+
const archive = await this.archivePath({ create: true });
|
|
3307
|
+
const saved = await saveStore(archive);
|
|
3308
|
+
done(await describeSave2(saved, archive, start));
|
|
3309
|
+
if (!await isRegularFile(archive)) {
|
|
3310
|
+
return {};
|
|
3311
|
+
}
|
|
3312
|
+
return await (0, import_build_utils6.glob)(
|
|
3313
|
+
`${BUILDAH_STORE_CACHE_DIR}/${(0, import_node_path19.basename)(archive)}`,
|
|
3314
|
+
this.repositoryRoot
|
|
3315
|
+
);
|
|
3316
|
+
} catch (error) {
|
|
3317
|
+
done(`Buildah store cache not saved (${failureReason(error)})`);
|
|
3318
|
+
return {};
|
|
3319
|
+
}
|
|
3320
|
+
}
|
|
3321
|
+
async removeSnapshots() {
|
|
3322
|
+
try {
|
|
3323
|
+
await (0, import_promises7.rm)(await this.snapshotDirectory({ create: false }), {
|
|
3324
|
+
recursive: true,
|
|
3325
|
+
force: true
|
|
3326
|
+
});
|
|
3327
|
+
} catch (error) {
|
|
3328
|
+
debug(
|
|
3329
|
+
`could not remove Buildah store snapshots: ${failureReason(error)}`
|
|
3330
|
+
);
|
|
3331
|
+
}
|
|
3332
|
+
}
|
|
3333
|
+
async archivePath(options) {
|
|
3334
|
+
const key = await snapshotKey(this.client);
|
|
3335
|
+
return (0, import_node_path19.join)(await this.snapshotDirectory(options), `store-${key}.tar.zst`);
|
|
3336
|
+
}
|
|
3337
|
+
async snapshotDirectory({
|
|
3338
|
+
create
|
|
3339
|
+
}) {
|
|
3340
|
+
const root = await (0, import_promises7.realpath)(this.repositoryRoot);
|
|
3341
|
+
let directory = root;
|
|
3342
|
+
for (const part of BUILDAH_STORE_CACHE_DIR.split("/")) {
|
|
3343
|
+
directory = (0, import_node_path19.join)(directory, part);
|
|
3344
|
+
if (create) {
|
|
3345
|
+
await (0, import_promises7.mkdir)(directory, { mode: 448 }).catch((error) => {
|
|
3346
|
+
if (error.code !== "EEXIST")
|
|
3347
|
+
throw error;
|
|
3348
|
+
});
|
|
3349
|
+
}
|
|
3350
|
+
const stats = await lstatIfPresent(directory);
|
|
3351
|
+
if (!stats) {
|
|
3352
|
+
return (0, import_node_path19.join)(root, BUILDAH_STORE_CACHE_DIR);
|
|
3353
|
+
}
|
|
3354
|
+
if (!stats.isDirectory()) {
|
|
3355
|
+
throw new Error(`${directory} is not a directory`);
|
|
3356
|
+
}
|
|
3357
|
+
}
|
|
3358
|
+
return directory;
|
|
3359
|
+
}
|
|
3360
|
+
};
|
|
3361
|
+
async function restoreStore(archive) {
|
|
3362
|
+
const storeLock = await requireStoreLock();
|
|
3363
|
+
const state = await createBuildahStoreStateDirectory();
|
|
3364
|
+
const gate = new FileLock((0, import_node_path19.join)(state, "restore.lock"));
|
|
3365
|
+
return runStoreCommand(
|
|
3366
|
+
{
|
|
3367
|
+
action: "restore",
|
|
3368
|
+
options: {
|
|
3369
|
+
...STORE_PATHS,
|
|
3370
|
+
archive,
|
|
3371
|
+
storeLock: storeLock.path,
|
|
3372
|
+
marker: (0, import_node_path19.join)(state, "restored")
|
|
3373
|
+
}
|
|
3374
|
+
},
|
|
3375
|
+
gate
|
|
3376
|
+
);
|
|
3377
|
+
}
|
|
3378
|
+
async function saveStore(archive) {
|
|
3379
|
+
const buildahArgs = await buildahStorageArgs();
|
|
3380
|
+
const storeLock = await requireStoreLock();
|
|
3381
|
+
return runStoreCommand(
|
|
3382
|
+
{
|
|
3383
|
+
action: "save",
|
|
3384
|
+
options: {
|
|
3385
|
+
...STORE_PATHS,
|
|
3386
|
+
archive,
|
|
3387
|
+
stateDirectory: await createBuildahStoreStateDirectory(),
|
|
3388
|
+
threads: (0, import_node_os11.availableParallelism)(),
|
|
3389
|
+
buildahArgs
|
|
3390
|
+
}
|
|
3391
|
+
},
|
|
3392
|
+
storeLock
|
|
3393
|
+
);
|
|
3394
|
+
}
|
|
3395
|
+
async function requireStoreLock() {
|
|
3396
|
+
const lock = await buildahStoreLock();
|
|
3397
|
+
if (!lock)
|
|
3398
|
+
throw new Error("flock unavailable");
|
|
3399
|
+
return lock;
|
|
3400
|
+
}
|
|
3401
|
+
async function describeSave2(result, archive, start) {
|
|
3402
|
+
if (result.status === "saved") {
|
|
3403
|
+
return `saved the Buildah store cache (${formatBytes((await (0, import_promises7.lstat)(archive)).size)}) in ${elapsed(start)}`;
|
|
3404
|
+
}
|
|
3405
|
+
if (result.status === "unused") {
|
|
3406
|
+
return await isRegularFile(archive) ? "kept the previous Buildah store cache: no service built with Buildah" : "no Buildah store cache to keep: no service recorded Buildah image usage";
|
|
3407
|
+
}
|
|
3408
|
+
if (result.status === "skipped" && result.previous === "failed") {
|
|
3409
|
+
throw new Error("an earlier save for this deployment failed");
|
|
3410
|
+
}
|
|
3411
|
+
return "Buildah store cache already prepared for this deployment";
|
|
3412
|
+
}
|
|
3413
|
+
async function snapshotKey(client) {
|
|
3414
|
+
const configuration = await Promise.all(
|
|
3415
|
+
[
|
|
3416
|
+
"/usr/share/containers/storage.conf",
|
|
3417
|
+
"/etc/containers/storage.conf",
|
|
3418
|
+
(0, import_node_path19.join)(
|
|
3419
|
+
process.env.XDG_CONFIG_HOME || (0, import_node_path19.join)((0, import_node_os11.homedir)(), ".config"),
|
|
3420
|
+
"containers/storage.conf"
|
|
3421
|
+
),
|
|
3422
|
+
process.env.CONTAINERS_STORAGE_CONF,
|
|
3423
|
+
"/etc/subuid",
|
|
3424
|
+
"/etc/subgid"
|
|
3425
|
+
].map(readOptionalFile)
|
|
3426
|
+
);
|
|
3427
|
+
return (0, import_node_crypto8.createHash)("sha256").update(
|
|
3428
|
+
JSON.stringify({
|
|
3429
|
+
version: await client.version(),
|
|
3430
|
+
arch: process.arch,
|
|
3431
|
+
uid: process.getuid?.(),
|
|
3432
|
+
gid: process.getgid?.(),
|
|
3433
|
+
driver: process.env.VERCEL_VCR_DOCKER_STORAGE_DRIVER,
|
|
3434
|
+
image: process.env.VERCEL_BUILD_IMAGE,
|
|
3435
|
+
configuration
|
|
3436
|
+
})
|
|
3437
|
+
).digest("hex").slice(0, 32);
|
|
3438
|
+
}
|
|
3439
|
+
async function readOptionalFile(path4) {
|
|
3440
|
+
if (!path4) {
|
|
3441
|
+
return "";
|
|
3442
|
+
}
|
|
3443
|
+
try {
|
|
3444
|
+
return await (0, import_promises7.readFile)(path4, "utf8");
|
|
3445
|
+
} catch (error) {
|
|
3446
|
+
if (error.code === "ENOENT")
|
|
3447
|
+
return "";
|
|
3448
|
+
throw error;
|
|
3449
|
+
}
|
|
3450
|
+
}
|
|
3451
|
+
async function isRegularFile(path4) {
|
|
3452
|
+
return (await lstatIfPresent(path4))?.isFile() ?? false;
|
|
3453
|
+
}
|
|
3454
|
+
function failureReason(error) {
|
|
3455
|
+
return String(error?.message ?? error).trim().split("\n").at(-1) ?? "";
|
|
3456
|
+
}
|
|
3457
|
+
|
|
2908
3458
|
// src/dev.ts
|
|
2909
3459
|
var import_node_child_process4 = require("child_process");
|
|
2910
3460
|
var import_node_fs15 = require("fs");
|
|
2911
|
-
var
|
|
2912
|
-
var
|
|
3461
|
+
var import_node_os12 = require("os");
|
|
3462
|
+
var import_node_path20 = __toESM(require("path"));
|
|
2913
3463
|
var HOST_ONLY_ENV = /* @__PURE__ */ new Set([
|
|
2914
3464
|
"TMPDIR",
|
|
2915
3465
|
"TMP",
|
|
@@ -2939,8 +3489,8 @@ function isHostOnlyEnvVar(key) {
|
|
|
2939
3489
|
return HOST_ONLY_ENV.has(key) || key.startsWith("__") || key.startsWith("XPC_") || key.startsWith("SSH_") || key.startsWith("Apple");
|
|
2940
3490
|
}
|
|
2941
3491
|
function writeEnvFile(env) {
|
|
2942
|
-
const dir = (0, import_node_fs15.mkdtempSync)(
|
|
2943
|
-
const file =
|
|
3492
|
+
const dir = (0, import_node_fs15.mkdtempSync)(import_node_path20.default.join((0, import_node_os12.tmpdir)(), "vercel-container-dev-env-"));
|
|
3493
|
+
const file = import_node_path20.default.join(dir, "env");
|
|
2944
3494
|
const lines = [];
|
|
2945
3495
|
for (const [key, value] of Object.entries(env)) {
|
|
2946
3496
|
if (value.includes("\n")) {
|
|
@@ -2973,7 +3523,7 @@ function emit(out, line) {
|
|
|
2973
3523
|
}
|
|
2974
3524
|
}
|
|
2975
3525
|
function runForwarded(cmd, args, out, opts = {}) {
|
|
2976
|
-
return new Promise((
|
|
3526
|
+
return new Promise((resolve5, reject) => {
|
|
2977
3527
|
const child = (0, import_node_child_process4.spawn)(cmd, args, { stdio: ["ignore", "pipe", "pipe"] });
|
|
2978
3528
|
let stdout = "";
|
|
2979
3529
|
let stderr = "";
|
|
@@ -3010,7 +3560,7 @@ function runForwarded(cmd, args, out, opts = {}) {
|
|
|
3010
3560
|
});
|
|
3011
3561
|
child.on("close", (code) => {
|
|
3012
3562
|
if (code === 0) {
|
|
3013
|
-
|
|
3563
|
+
resolve5({ stdout });
|
|
3014
3564
|
} else {
|
|
3015
3565
|
const detail = stderr.trim().split("\n").slice(-5).join("\n");
|
|
3016
3566
|
reject(
|
|
@@ -3046,7 +3596,7 @@ async function resolveDevImage(options, out, span) {
|
|
|
3046
3596
|
}
|
|
3047
3597
|
const serviceName = options.service?.name ?? "service";
|
|
3048
3598
|
const tag = devImageTag(serviceName);
|
|
3049
|
-
const contextDir =
|
|
3599
|
+
const contextDir = import_node_path20.default.dirname(dockerfilePath);
|
|
3050
3600
|
const buildArgFlags2 = [];
|
|
3051
3601
|
const buildEnv = options.meta?.buildEnv ?? {};
|
|
3052
3602
|
for (const [key, value] of Object.entries(buildEnv)) {
|
|
@@ -3251,7 +3801,7 @@ async function startContainer(options, reuseKey) {
|
|
|
3251
3801
|
}
|
|
3252
3802
|
});
|
|
3253
3803
|
const cleanupEnvFile = () => {
|
|
3254
|
-
(0, import_node_fs15.rmSync)(
|
|
3804
|
+
(0, import_node_fs15.rmSync)(import_node_path20.default.dirname(envFilePath), { recursive: true, force: true });
|
|
3255
3805
|
};
|
|
3256
3806
|
const shutdown = async () => {
|
|
3257
3807
|
runningContainers.delete(reuseKey);
|
|
@@ -3284,7 +3834,7 @@ async function startContainer(options, reuseKey) {
|
|
|
3284
3834
|
break;
|
|
3285
3835
|
} catch (err) {
|
|
3286
3836
|
lastErr = err;
|
|
3287
|
-
await new Promise((
|
|
3837
|
+
await new Promise((resolve5) => setTimeout(resolve5, 250));
|
|
3288
3838
|
}
|
|
3289
3839
|
}
|
|
3290
3840
|
if (hostPort === void 0) {
|
|
@@ -3327,19 +3877,27 @@ async function startContainer(options, reuseKey) {
|
|
|
3327
3877
|
}
|
|
3328
3878
|
|
|
3329
3879
|
// src/prepare-cache.ts
|
|
3330
|
-
var
|
|
3331
|
-
var
|
|
3880
|
+
var import_build_utils7 = require("@vercel/build-utils");
|
|
3881
|
+
var import_node_path21 = require("path");
|
|
3332
3882
|
async function prepareCache(options) {
|
|
3333
|
-
const root = (0,
|
|
3883
|
+
const root = (0, import_node_path21.resolve)(options.repoRootPath || options.workPath);
|
|
3884
|
+
const buildpackFiles = await collectBuildpackCache(root);
|
|
3885
|
+
const buildahStoreFiles = await BuildahStoreCache.forProject(
|
|
3886
|
+
options,
|
|
3887
|
+
new BuildahClient(sharedBuildahStoreLock)
|
|
3888
|
+
).afterBuilds();
|
|
3889
|
+
return { ...buildpackFiles, ...buildahStoreFiles };
|
|
3890
|
+
}
|
|
3891
|
+
async function collectBuildpackCache(root) {
|
|
3334
3892
|
const start = Date.now();
|
|
3335
|
-
const files = await (0,
|
|
3893
|
+
const files = await (0, import_build_utils7.glob)(`${BUILDPACK_CACHE_DIR}/**`, root);
|
|
3336
3894
|
const count = Object.keys(files).length;
|
|
3337
3895
|
if (count > 0) {
|
|
3338
3896
|
info(
|
|
3339
|
-
`cached
|
|
3897
|
+
`cached ${count} file${count === 1 ? "" : "s"} under ${BUILDPACK_CACHE_DIR} in ${Date.now() - start}ms`
|
|
3340
3898
|
);
|
|
3341
3899
|
} else {
|
|
3342
|
-
debug(
|
|
3900
|
+
debug(`no cache to persist under ${BUILDPACK_CACHE_DIR}`);
|
|
3343
3901
|
}
|
|
3344
3902
|
return files;
|
|
3345
3903
|
}
|
|
@@ -3363,9 +3921,9 @@ function resolveImageTag() {
|
|
|
3363
3921
|
if (sha) {
|
|
3364
3922
|
return sha.slice(0, 12);
|
|
3365
3923
|
}
|
|
3366
|
-
const
|
|
3367
|
-
if (
|
|
3368
|
-
return
|
|
3924
|
+
const deploymentId2 = readString(process.env.VERCEL_DEPLOYMENT_ID);
|
|
3925
|
+
if (deploymentId2) {
|
|
3926
|
+
return deploymentId2.replace(/[^a-z0-9-_.]/gi, "-");
|
|
3369
3927
|
}
|
|
3370
3928
|
return `build-${Date.now().toString(36)}`;
|
|
3371
3929
|
}
|
|
@@ -3397,8 +3955,16 @@ async function authenticateRegistry(engine, buildParams, span) {
|
|
|
3397
3955
|
done("authenticated");
|
|
3398
3956
|
}
|
|
3399
3957
|
async function buildAndPushImage(params) {
|
|
3400
|
-
const {
|
|
3401
|
-
|
|
3958
|
+
const {
|
|
3959
|
+
contextDir,
|
|
3960
|
+
dockerfilePath,
|
|
3961
|
+
repository,
|
|
3962
|
+
tag,
|
|
3963
|
+
buildArgs,
|
|
3964
|
+
buildahClient,
|
|
3965
|
+
parentSpan
|
|
3966
|
+
} = params;
|
|
3967
|
+
const engine = selectContainerEngine(buildahClient);
|
|
3402
3968
|
return withSpan(
|
|
3403
3969
|
parentSpan,
|
|
3404
3970
|
"container.build_and_push",
|
|
@@ -3448,6 +4014,7 @@ async function buildAndPushImage(params) {
|
|
|
3448
4014
|
token,
|
|
3449
4015
|
repository,
|
|
3450
4016
|
buildArgs,
|
|
4017
|
+
useBuildahStoreCache: buildahStoreCacheEnabled(),
|
|
3451
4018
|
span: buildSpan
|
|
3452
4019
|
};
|
|
3453
4020
|
await authenticateRegistry(engine, buildParams, buildSpan);
|
|
@@ -3470,7 +4037,7 @@ async function buildAndPushImage(params) {
|
|
|
3470
4037
|
buildSpan,
|
|
3471
4038
|
"container.image_build",
|
|
3472
4039
|
{ "image.ref": imageRef, "image.platform": TARGET_PLATFORM },
|
|
3473
|
-
() => engine.build(buildParams)
|
|
4040
|
+
() => params.schedulesCommand ? buildSchedulesCommandImage(engine, buildParams) : engine.build(buildParams)
|
|
3474
4041
|
);
|
|
3475
4042
|
done(`built in ${elapsed(buildStart)}`);
|
|
3476
4043
|
const pushStart = Date.now();
|
|
@@ -3502,7 +4069,7 @@ async function buildAndPushImage(params) {
|
|
|
3502
4069
|
}
|
|
3503
4070
|
);
|
|
3504
4071
|
}
|
|
3505
|
-
async function resolveImageHandler(options, source, span) {
|
|
4072
|
+
async function resolveImageHandler(options, source, buildahClient, span, schedulesCommand) {
|
|
3506
4073
|
const { config, meta } = options;
|
|
3507
4074
|
const { daemon } = config;
|
|
3508
4075
|
span?.setAttributes({
|
|
@@ -3525,7 +4092,7 @@ async function resolveImageHandler(options, source, span) {
|
|
|
3525
4092
|
});
|
|
3526
4093
|
return tag3;
|
|
3527
4094
|
}
|
|
3528
|
-
const repository2 = daemon ? (0,
|
|
4095
|
+
const repository2 = daemon ? (0, import_build_utils8.getDaemonImageRepository)(daemon.name) : sanitizeRepository(options.service?.name ?? buildpack.runtime);
|
|
3529
4096
|
const tag2 = resolveImageTag();
|
|
3530
4097
|
span?.setAttributes({
|
|
3531
4098
|
"container.mode": "buildpack-build-and-push",
|
|
@@ -3545,6 +4112,7 @@ async function resolveImageHandler(options, source, span) {
|
|
|
3545
4112
|
config,
|
|
3546
4113
|
meta,
|
|
3547
4114
|
buildEnv: buildArgsFromEnv(meta?.buildEnv),
|
|
4115
|
+
buildahClient,
|
|
3548
4116
|
parentSpan: span
|
|
3549
4117
|
});
|
|
3550
4118
|
} catch (error) {
|
|
@@ -3560,7 +4128,7 @@ async function resolveImageHandler(options, source, span) {
|
|
|
3560
4128
|
if (meta?.isDev) {
|
|
3561
4129
|
const serviceName = options.service?.name;
|
|
3562
4130
|
const tag2 = devImageTag(
|
|
3563
|
-
serviceName ??
|
|
4131
|
+
serviceName ?? import_node_path22.default.basename(dockerfileRel).split(".")[0]
|
|
3564
4132
|
);
|
|
3565
4133
|
span?.setAttributes({ "container.mode": "dev", "image.tag": tag2 });
|
|
3566
4134
|
return tag2;
|
|
@@ -3570,11 +4138,11 @@ async function resolveImageHandler(options, source, span) {
|
|
|
3570
4138
|
`Dockerfile not found at "${dockerfilePath}" for container service.`
|
|
3571
4139
|
);
|
|
3572
4140
|
}
|
|
3573
|
-
const repository = daemon ? (0,
|
|
3574
|
-
options.service?.name ??
|
|
4141
|
+
const repository = daemon ? (0, import_build_utils8.getDaemonImageRepository)(daemon.name) : sanitizeRepository(
|
|
4142
|
+
options.service?.name ?? import_node_path22.default.basename(dockerfileRel).split(".")[0]
|
|
3575
4143
|
);
|
|
3576
4144
|
const tag = resolveImageTag();
|
|
3577
|
-
const contextDir =
|
|
4145
|
+
const contextDir = schedulesCommand ? import_node_path22.default.join(options.workPath, schedulesCommand.root) : import_node_path22.default.dirname(dockerfilePath);
|
|
3578
4146
|
const buildArgs = buildArgsFromEnv(meta?.buildEnv);
|
|
3579
4147
|
span?.setAttributes({
|
|
3580
4148
|
"container.mode": "build_and_push",
|
|
@@ -3587,7 +4155,9 @@ async function resolveImageHandler(options, source, span) {
|
|
|
3587
4155
|
repository,
|
|
3588
4156
|
tag,
|
|
3589
4157
|
buildArgs,
|
|
3590
|
-
|
|
4158
|
+
buildahClient,
|
|
4159
|
+
parentSpan: span,
|
|
4160
|
+
schedulesCommand
|
|
3591
4161
|
});
|
|
3592
4162
|
}
|
|
3593
4163
|
function buildArgsFromEnv(env) {
|
|
@@ -3604,12 +4174,26 @@ function buildArgsFromEnv(env) {
|
|
|
3604
4174
|
}
|
|
3605
4175
|
async function build(options) {
|
|
3606
4176
|
const source = resolveImageSource(options, "build");
|
|
4177
|
+
const schedulesCommand = options.config.schedulesCommand;
|
|
3607
4178
|
const { daemon } = options.config;
|
|
4179
|
+
const buildahClient = new BuildahClient(sharedBuildahStoreLock);
|
|
4180
|
+
if (!options.meta?.isDev && selectContainerEngine(buildahClient).name === "buildah") {
|
|
4181
|
+
await BuildahStoreCache.forProject(options, buildahClient).beforeBuild({
|
|
4182
|
+
buildsWithBuildah: source.kind !== "prebuilt",
|
|
4183
|
+
span: options.span
|
|
4184
|
+
});
|
|
4185
|
+
}
|
|
3608
4186
|
const image = await withSpan(
|
|
3609
4187
|
options.span,
|
|
3610
4188
|
"container.resolve_image",
|
|
3611
4189
|
{ "service.name": options.service?.name },
|
|
3612
|
-
(span) => resolveImageHandler(
|
|
4190
|
+
(span) => resolveImageHandler(
|
|
4191
|
+
options,
|
|
4192
|
+
source,
|
|
4193
|
+
buildahClient,
|
|
4194
|
+
span,
|
|
4195
|
+
schedulesCommand
|
|
4196
|
+
)
|
|
3613
4197
|
);
|
|
3614
4198
|
if (daemon) {
|
|
3615
4199
|
return {
|
|
@@ -3640,15 +4224,16 @@ async function build(options) {
|
|
|
3640
4224
|
}
|
|
3641
4225
|
};
|
|
3642
4226
|
}
|
|
3643
|
-
const lambdaOptions = await (0,
|
|
4227
|
+
const lambdaOptions = await (0, import_build_utils8.getLambdaOptionsFromFunction)({
|
|
3644
4228
|
sourceFile: resolveFunctionSourceFile(options),
|
|
3645
|
-
config: options.config
|
|
4229
|
+
config: options.config,
|
|
4230
|
+
isScheduleEntrypoint: schedulesCommand !== void 0
|
|
3646
4231
|
});
|
|
3647
|
-
const command = source.kind === "buildpack" ? void 0 : normalizeCommand(options.config.command);
|
|
4232
|
+
const command = source.kind === "buildpack" || schedulesCommand ? void 0 : normalizeCommand(options.config.command);
|
|
3648
4233
|
await generateProjectManifest({
|
|
3649
4234
|
workPath: options.workPath,
|
|
3650
4235
|
framework: options.diagnosticsValues?.framework,
|
|
3651
|
-
serviceType: options.service ? (0,
|
|
4236
|
+
serviceType: options.service ? (0, import_build_utils8.getReportedServiceType)(options.service) : void 0
|
|
3652
4237
|
});
|
|
3653
4238
|
const routes = [
|
|
3654
4239
|
{ handle: "filesystem" },
|
|
@@ -3668,7 +4253,7 @@ async function build(options) {
|
|
|
3668
4253
|
}
|
|
3669
4254
|
];
|
|
3670
4255
|
return {
|
|
3671
|
-
routes,
|
|
4256
|
+
routes: schedulesCommand ? [] : routes,
|
|
3672
4257
|
output: {
|
|
3673
4258
|
[CONTAINER_OUTPUT_PATH]: {
|
|
3674
4259
|
type: "Lambda",
|
|
@@ -3678,7 +4263,9 @@ async function build(options) {
|
|
|
3678
4263
|
// (vercel/api#76729).
|
|
3679
4264
|
handler: image,
|
|
3680
4265
|
runtime: "container",
|
|
3681
|
-
environment: {
|
|
4266
|
+
environment: schedulesCommand ? {
|
|
4267
|
+
[SCHEDULES_COMMAND_ENV]: JSON.stringify(schedulesCommand.command)
|
|
4268
|
+
} : {},
|
|
3682
4269
|
...command ? { command } : {},
|
|
3683
4270
|
...lambdaOptions
|
|
3684
4271
|
}
|