@vercel/container 22.0.2 → 23.0.1-snapshot-13615777d933
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 +1124 -520
- package/dist/schedules-command-dispatcher +0 -0
- package/package.json +6 -5
package/dist/index.js
CHANGED
|
@@ -38,7 +38,44 @@ __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
|
+
const baseImage = await engine.build(params);
|
|
51
|
+
if ((await engine.onBuildTriggers?.(baseImage))?.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 ${baseImage}`,
|
|
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
|
+
baseImageIsLocal: true
|
|
74
|
+
});
|
|
75
|
+
} finally {
|
|
76
|
+
await (0, import_promises.rm)(contextDir, { recursive: true, force: true });
|
|
77
|
+
}
|
|
78
|
+
}
|
|
42
79
|
|
|
43
80
|
// src/diagnostics.ts
|
|
44
81
|
var import_build_utils = require("@vercel/build-utils");
|
|
@@ -63,14 +100,15 @@ var diagnostics = (0, import_build_utils.createDiagnostics)("container");
|
|
|
63
100
|
|
|
64
101
|
// src/index.ts
|
|
65
102
|
var import_node_fs16 = require("fs");
|
|
66
|
-
var
|
|
103
|
+
var import_node_path22 = __toESM(require("path"));
|
|
67
104
|
|
|
68
105
|
// src/util.ts
|
|
69
106
|
var import_build_utils2 = require("@vercel/build-utils");
|
|
70
107
|
var import_node_child_process = require("child_process");
|
|
71
108
|
var import_node_fs = require("fs");
|
|
72
|
-
var
|
|
73
|
-
var
|
|
109
|
+
var import_promises2 = require("fs/promises");
|
|
110
|
+
var import_node_os2 = require("os");
|
|
111
|
+
var import_node_path2 = require("path");
|
|
74
112
|
var DEBUG = Boolean((0, import_build_utils2.getPlatformEnv)("BUILDER_DEBUG"));
|
|
75
113
|
function normalizeCommand(command) {
|
|
76
114
|
if (typeof command === "string") {
|
|
@@ -105,11 +143,23 @@ function debug(message) {
|
|
|
105
143
|
function elapsed(since) {
|
|
106
144
|
return `${((Date.now() - since) / 1e3).toFixed(1)}s`;
|
|
107
145
|
}
|
|
146
|
+
function formatBytes(bytes) {
|
|
147
|
+
if (bytes < 1024)
|
|
148
|
+
return `${bytes} B`;
|
|
149
|
+
const units = ["KiB", "MiB", "GiB"];
|
|
150
|
+
let value = bytes / 1024;
|
|
151
|
+
let unit = 0;
|
|
152
|
+
while (value >= 1024 && unit < units.length - 1) {
|
|
153
|
+
value /= 1024;
|
|
154
|
+
unit += 1;
|
|
155
|
+
}
|
|
156
|
+
return `${value.toFixed(value >= 10 ? 0 : 1)} ${units[unit]}`;
|
|
157
|
+
}
|
|
108
158
|
function shortDigest(digest) {
|
|
109
159
|
return digest.startsWith("sha256:") ? `${digest.slice(0, 19)}\u2026` : digest;
|
|
110
160
|
}
|
|
111
161
|
function delay(ms) {
|
|
112
|
-
return new Promise((
|
|
162
|
+
return new Promise((resolve5) => setTimeout(resolve5, ms));
|
|
113
163
|
}
|
|
114
164
|
async function withSpan(parent, name, attrs, fn) {
|
|
115
165
|
if (!parent) {
|
|
@@ -124,21 +174,21 @@ function readString(value) {
|
|
|
124
174
|
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
125
175
|
}
|
|
126
176
|
function isDockerfileRef(ref) {
|
|
127
|
-
return /(docker|container)file/i.test((0,
|
|
177
|
+
return /(docker|container)file/i.test((0, import_node_path2.basename)(ref));
|
|
128
178
|
}
|
|
129
179
|
var DOCKERFILE_CANDIDATES = [
|
|
130
180
|
"Dockerfile.vercel",
|
|
131
181
|
"Containerfile.vercel"
|
|
132
182
|
];
|
|
133
183
|
function findDockerfile(workPath) {
|
|
134
|
-
return DOCKERFILE_CANDIDATES.find((name) => (0, import_node_fs.existsSync)((0,
|
|
184
|
+
return DOCKERFILE_CANDIDATES.find((name) => (0, import_node_fs.existsSync)((0, import_node_path2.join)(workPath, name)));
|
|
135
185
|
}
|
|
136
186
|
function devImageTag(serviceName) {
|
|
137
187
|
const safe = serviceName.toLowerCase().replace(/[^a-z0-9-_.]/g, "-");
|
|
138
188
|
return `vercel-dev/${safe || "service"}:dev`;
|
|
139
189
|
}
|
|
140
190
|
function run(cmd, args, opts = {}) {
|
|
141
|
-
return new Promise((
|
|
191
|
+
return new Promise((resolve5, reject) => {
|
|
142
192
|
const child = (0, import_node_child_process.spawn)(cmd, args, {
|
|
143
193
|
cwd: opts.cwd,
|
|
144
194
|
env: opts.env,
|
|
@@ -173,7 +223,7 @@ function run(cmd, args, opts = {}) {
|
|
|
173
223
|
});
|
|
174
224
|
child.on("close", (code) => {
|
|
175
225
|
if (code === 0) {
|
|
176
|
-
|
|
226
|
+
resolve5({ stdout, stderr });
|
|
177
227
|
} else {
|
|
178
228
|
const detail = stderr.trim().split("\n").slice(-5).join("\n");
|
|
179
229
|
const error = new Error(
|
|
@@ -189,6 +239,23 @@ ${detail}` : "")
|
|
|
189
239
|
}
|
|
190
240
|
});
|
|
191
241
|
}
|
|
242
|
+
async function hasCommand(name) {
|
|
243
|
+
try {
|
|
244
|
+
await run("which", [name], { quiet: true });
|
|
245
|
+
return true;
|
|
246
|
+
} catch {
|
|
247
|
+
return false;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
async function lstatIfPresent(path4) {
|
|
251
|
+
try {
|
|
252
|
+
return await (0, import_promises2.lstat)(path4);
|
|
253
|
+
} catch (error) {
|
|
254
|
+
if (error.code === "ENOENT")
|
|
255
|
+
return void 0;
|
|
256
|
+
throw error;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
192
259
|
function extractField(text, label) {
|
|
193
260
|
const match = text.match(new RegExp(`^\\s*${label}:\\s*(.+)$`, "m"));
|
|
194
261
|
return match?.[1]?.trim();
|
|
@@ -202,44 +269,130 @@ function existingRegistryAuthFile() {
|
|
|
202
269
|
return (0, import_node_fs.existsSync)(explicit) ? explicit : void 0;
|
|
203
270
|
}
|
|
204
271
|
const fromXdg = readString(process.env.XDG_CONFIG_HOME);
|
|
205
|
-
const configHome = fromXdg || (0,
|
|
206
|
-
const defaultPath = (0,
|
|
272
|
+
const configHome = fromXdg || (0, import_node_path2.join)((0, import_node_os2.homedir)(), ".config");
|
|
273
|
+
const defaultPath = (0, import_node_path2.join)(configHome, "containers", "auth.json");
|
|
207
274
|
return (0, import_node_fs.existsSync)(defaultPath) ? defaultPath : void 0;
|
|
208
275
|
}
|
|
209
276
|
|
|
210
277
|
// src/engines/buildah.ts
|
|
278
|
+
var import_build_utils3 = require("@vercel/build-utils");
|
|
279
|
+
var import_node_crypto3 = require("crypto");
|
|
211
280
|
var import_node_fs3 = require("fs");
|
|
281
|
+
var import_promises5 = require("fs/promises");
|
|
282
|
+
var import_node_os5 = require("os");
|
|
283
|
+
var import_node_path5 = require("path");
|
|
284
|
+
|
|
285
|
+
// src/buildah-store-cache/state.ts
|
|
286
|
+
var import_node_crypto = require("crypto");
|
|
287
|
+
var import_promises3 = require("fs/promises");
|
|
212
288
|
var import_node_os3 = require("os");
|
|
213
289
|
var import_node_path3 = require("path");
|
|
214
290
|
|
|
291
|
+
// src/buildah-store-cache/config.ts
|
|
292
|
+
var BUILDAH_STORE_CACHE_FLAG = "VERCEL_BUILDAH_STORE_CACHE";
|
|
293
|
+
function buildahStoreCacheRequested() {
|
|
294
|
+
return isBuildContainer() && process.env[BUILDAH_STORE_CACHE_FLAG] === "1";
|
|
295
|
+
}
|
|
296
|
+
function buildahStoreCacheEnabled() {
|
|
297
|
+
return buildahStoreCacheRequested() && deploymentId() !== void 0;
|
|
298
|
+
}
|
|
299
|
+
function deploymentId() {
|
|
300
|
+
return readString(process.env.VERCEL_DEPLOYMENT_ID);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// src/buildah-store-cache/state.ts
|
|
304
|
+
var CLEANUP_CONTAINER_RE = /^vercel-cnb-[a-zA-Z0-9_.-]+$/;
|
|
305
|
+
function buildahStoreStateDirectory() {
|
|
306
|
+
const deployment = deploymentId();
|
|
307
|
+
if (!deployment) {
|
|
308
|
+
throw new Error("VERCEL_DEPLOYMENT_ID is not set");
|
|
309
|
+
}
|
|
310
|
+
return (0, import_node_path3.join)(
|
|
311
|
+
(0, import_node_os3.tmpdir)(),
|
|
312
|
+
"vercel-buildah-store",
|
|
313
|
+
deployment.replace(/[^a-zA-Z0-9_-]/g, "_")
|
|
314
|
+
);
|
|
315
|
+
}
|
|
316
|
+
async function createBuildahStoreStateDirectory() {
|
|
317
|
+
const directory = buildahStoreStateDirectory();
|
|
318
|
+
await (0, import_promises3.mkdir)(directory, { recursive: true, mode: 448 });
|
|
319
|
+
return directory;
|
|
320
|
+
}
|
|
321
|
+
async function writeStateRecord(suffix, contents) {
|
|
322
|
+
const path4 = (0, import_node_path3.join)(
|
|
323
|
+
await createBuildahStoreStateDirectory(),
|
|
324
|
+
`${(0, import_node_crypto.randomUUID)()}${suffix}`
|
|
325
|
+
);
|
|
326
|
+
await (0, import_promises3.writeFile)(path4, contents, { mode: 384, flag: "wx" });
|
|
327
|
+
return path4;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
// src/buildah-store-cache/usage.ts
|
|
331
|
+
var import_promises4 = require("fs/promises");
|
|
332
|
+
function imageIdsFromBuildOutput(output) {
|
|
333
|
+
const ids = /* @__PURE__ */ new Set();
|
|
334
|
+
for (const match of output.matchAll(
|
|
335
|
+
/--> (?:Using cache )?([a-f0-9]{12,64})[ \t\r]*$/gm
|
|
336
|
+
)) {
|
|
337
|
+
ids.add(match[1]);
|
|
338
|
+
}
|
|
339
|
+
return [...ids];
|
|
340
|
+
}
|
|
341
|
+
var BuildahImageUsage = class _BuildahImageUsage {
|
|
342
|
+
constructor(path4) {
|
|
343
|
+
this.path = path4;
|
|
344
|
+
}
|
|
345
|
+
static async begin() {
|
|
346
|
+
if (!buildahStoreCacheEnabled()) {
|
|
347
|
+
return void 0;
|
|
348
|
+
}
|
|
349
|
+
try {
|
|
350
|
+
return new _BuildahImageUsage(
|
|
351
|
+
await writeStateRecord(".images", "pending\n")
|
|
352
|
+
);
|
|
353
|
+
} catch (error) {
|
|
354
|
+
debug(
|
|
355
|
+
`could not record Buildah image usage: ${error.message}`
|
|
356
|
+
);
|
|
357
|
+
return void 0;
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
/** A failure leaves the record pending, so the save stops instead of pruning blindly. */
|
|
361
|
+
async record(images) {
|
|
362
|
+
try {
|
|
363
|
+
const ids = (await images()).map((id) => id.replace(/^sha256:/, ""));
|
|
364
|
+
if (ids.length === 0) {
|
|
365
|
+
throw new Error("no Buildah image IDs were recorded");
|
|
366
|
+
}
|
|
367
|
+
const temporary = `${this.path}.tmp`;
|
|
368
|
+
await (0, import_promises4.writeFile)(temporary, `${ids.join("\n")}
|
|
369
|
+
`, { mode: 384 });
|
|
370
|
+
await (0, import_promises4.rename)(temporary, this.path);
|
|
371
|
+
} catch (error) {
|
|
372
|
+
debug(
|
|
373
|
+
`could not record Buildah image usage: ${error.message}`
|
|
374
|
+
);
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
};
|
|
378
|
+
|
|
215
379
|
// src/storage-driver.ts
|
|
216
|
-
var import_node_child_process2 = require("child_process");
|
|
217
380
|
var import_node_fs2 = require("fs");
|
|
218
|
-
var
|
|
219
|
-
var
|
|
381
|
+
var import_node_os4 = require("os");
|
|
382
|
+
var import_node_path4 = require("path");
|
|
220
383
|
var BUILDAH_GRAPH_ROOT = "/vercel/.containers/storage";
|
|
221
384
|
var BUILDAH_RUN_ROOT = "/run/containers/storage";
|
|
222
385
|
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
386
|
var cachedStorageDriver;
|
|
232
387
|
function selectStorageDriver() {
|
|
233
388
|
if (!cachedStorageDriver) {
|
|
234
389
|
cachedStorageDriver = (async () => {
|
|
235
390
|
const override = readString(process.env.VERCEL_VCR_DOCKER_STORAGE_DRIVER);
|
|
236
|
-
if (override)
|
|
391
|
+
if (override)
|
|
237
392
|
return override;
|
|
238
|
-
|
|
239
|
-
if (isBuildContainer()) {
|
|
393
|
+
if (isBuildContainer())
|
|
240
394
|
return void 0;
|
|
241
|
-
|
|
242
|
-
if (await hasBinary("fuse-overlayfs") && (0, import_node_fs2.existsSync)("/dev/fuse")) {
|
|
395
|
+
if (await hasCommand("fuse-overlayfs") && (0, import_node_fs2.existsSync)("/dev/fuse")) {
|
|
243
396
|
return "fuse-overlayfs";
|
|
244
397
|
}
|
|
245
398
|
return "vfs";
|
|
@@ -253,8 +406,8 @@ short-name-mode = "permissive"
|
|
|
253
406
|
var cachedRegistriesConfPath;
|
|
254
407
|
function buildahRegistriesConfPath() {
|
|
255
408
|
if (!cachedRegistriesConfPath) {
|
|
256
|
-
const dir = (0, import_node_fs2.mkdtempSync)((0,
|
|
257
|
-
cachedRegistriesConfPath = (0,
|
|
409
|
+
const dir = (0, import_node_fs2.mkdtempSync)((0, import_node_path4.join)((0, import_node_os4.tmpdir)(), "vercel-container-registries-"));
|
|
410
|
+
cachedRegistriesConfPath = (0, import_node_path4.join)(dir, "registries.conf");
|
|
258
411
|
(0, import_node_fs2.writeFileSync)(cachedRegistriesConfPath, BUILDAH_REGISTRIES_CONF);
|
|
259
412
|
}
|
|
260
413
|
return cachedRegistriesConfPath;
|
|
@@ -266,9 +419,8 @@ async function buildahStorageArgs() {
|
|
|
266
419
|
"--registries-conf",
|
|
267
420
|
buildahRegistriesConfPath()
|
|
268
421
|
];
|
|
269
|
-
if (!driver)
|
|
422
|
+
if (!driver)
|
|
270
423
|
return [...rootArgs, ...registriesArgs];
|
|
271
|
-
}
|
|
272
424
|
if (driver === "fuse-overlayfs") {
|
|
273
425
|
return [
|
|
274
426
|
...rootArgs,
|
|
@@ -281,121 +433,9 @@ async function buildahStorageArgs() {
|
|
|
281
433
|
}
|
|
282
434
|
return [...rootArgs, ...registriesArgs, "--storage-driver", driver];
|
|
283
435
|
}
|
|
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
436
|
|
|
397
437
|
// src/oidc.ts
|
|
398
|
-
var
|
|
438
|
+
var import_node_crypto2 = require("crypto");
|
|
399
439
|
function parseOidcToken(token) {
|
|
400
440
|
const parts = token.split(".");
|
|
401
441
|
if (parts.length !== 3) {
|
|
@@ -426,7 +466,7 @@ function decodeOidcClaims(token) {
|
|
|
426
466
|
function tokenFingerprint(token) {
|
|
427
467
|
if (!token)
|
|
428
468
|
return "absent";
|
|
429
|
-
const sha = (0,
|
|
469
|
+
const sha = (0, import_node_crypto2.createHash)("sha256").update(token).digest("hex").slice(0, 8);
|
|
430
470
|
return `present(len=${token.length}, sha256=${sha})`;
|
|
431
471
|
}
|
|
432
472
|
function debugTokenClaims(label, token) {
|
|
@@ -720,206 +760,266 @@ async function ensureRepository(repository, token, claims, span) {
|
|
|
720
760
|
}
|
|
721
761
|
|
|
722
762
|
// src/engines/buildah.ts
|
|
723
|
-
function
|
|
724
|
-
const
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
return;
|
|
763
|
+
function localImageRef(params) {
|
|
764
|
+
const { imageRef } = params;
|
|
765
|
+
if (!params.useBuildahStoreCache) {
|
|
766
|
+
return imageRef;
|
|
728
767
|
}
|
|
729
|
-
|
|
730
|
-
info(`layer cache: ${cached}/${steps} steps reused, ${built} rebuilt`);
|
|
768
|
+
return `${imageRef.slice(0, imageRef.lastIndexOf(":"))}:vercel-buildah-store`;
|
|
731
769
|
}
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
}
|
|
751
|
-
|
|
752
|
-
|
|
770
|
+
function pullFlags(params) {
|
|
771
|
+
if (params.baseImageIsLocal) {
|
|
772
|
+
return ["--pull=never"];
|
|
773
|
+
}
|
|
774
|
+
return params.useBuildahStoreCache ? ["--pull=always"] : [];
|
|
775
|
+
}
|
|
776
|
+
async function deploymentSourceDateEpoch() {
|
|
777
|
+
if ((0, import_build_utils3.resolveBuildConcurrency)() <= 1) {
|
|
778
|
+
return void 0;
|
|
779
|
+
}
|
|
780
|
+
if (readString(process.env.SOURCE_DATE_EPOCH?.trim()) !== void 0) {
|
|
781
|
+
return void 0;
|
|
782
|
+
}
|
|
783
|
+
try {
|
|
784
|
+
const path4 = (0, import_node_path5.join)(
|
|
785
|
+
await createBuildahStoreStateDirectory(),
|
|
786
|
+
"source-date-epoch"
|
|
787
|
+
);
|
|
788
|
+
const candidate = `${path4}.${(0, import_node_crypto3.randomUUID)()}`;
|
|
789
|
+
await (0, import_promises5.writeFile)(candidate, `${Math.floor(Date.now() / 1e3)}
|
|
790
|
+
`, {
|
|
791
|
+
flag: "wx",
|
|
792
|
+
mode: 384
|
|
793
|
+
});
|
|
753
794
|
try {
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
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
|
+
await (0, import_promises5.link)(candidate, path4);
|
|
796
|
+
} catch (error) {
|
|
797
|
+
if (error.code !== "EEXIST")
|
|
798
|
+
throw error;
|
|
799
|
+
} finally {
|
|
800
|
+
await (0, import_promises5.rm)(candidate, { force: true });
|
|
766
801
|
}
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
},
|
|
771
|
-
async build(params) {
|
|
772
|
-
try {
|
|
773
|
-
const { stdout: stdout2 } = await runBuildah(["images", "--quiet"], {
|
|
774
|
-
quiet: true
|
|
775
|
-
});
|
|
776
|
-
const imageCount = stdout2.trim() ? stdout2.trim().split("\n").length : 0;
|
|
777
|
-
info(
|
|
778
|
-
imageCount > 0 ? `layer store: warm (${imageCount} image(s) present before build)` : "layer store: cold (no images present before build)"
|
|
779
|
-
);
|
|
780
|
-
} catch (err) {
|
|
781
|
-
debug(`could not read store warmth: ${err.message}`);
|
|
802
|
+
const epoch = (await (0, import_promises5.readFile)(path4, "utf8")).trim();
|
|
803
|
+
if (!/^[0-9]+$/.test(epoch)) {
|
|
804
|
+
throw new Error(`invalid value ${JSON.stringify(epoch)}`);
|
|
782
805
|
}
|
|
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
|
-
|
|
806
|
+
return epoch;
|
|
807
|
+
} catch (error) {
|
|
808
|
+
debug(
|
|
809
|
+
`could not share a Buildah source date epoch: ${error.message}`
|
|
810
|
+
);
|
|
811
|
+
return void 0;
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
function createBuildahEngine(client) {
|
|
815
|
+
return {
|
|
816
|
+
name: "buildah",
|
|
817
|
+
async ensureReady(span) {
|
|
818
|
+
try {
|
|
819
|
+
const storageDriver = await selectStorageDriver();
|
|
820
|
+
const version2 = await client.version();
|
|
821
|
+
span?.setAttributes({
|
|
822
|
+
"buildah.version": version2.split("\n")[0],
|
|
823
|
+
"buildah.storage_driver": storageDriver ?? "storage.conf"
|
|
824
|
+
});
|
|
825
|
+
} catch (err) {
|
|
826
|
+
const message = err.message;
|
|
827
|
+
if (/Command not found/i.test(message)) {
|
|
828
|
+
throw new Error(
|
|
829
|
+
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."
|
|
830
|
+
);
|
|
831
|
+
}
|
|
832
|
+
throw err;
|
|
833
|
+
}
|
|
834
|
+
},
|
|
835
|
+
async logDiagnostics(span) {
|
|
836
|
+
try {
|
|
837
|
+
const storageDriver = await selectStorageDriver();
|
|
838
|
+
const version2 = await client.version();
|
|
839
|
+
info(
|
|
840
|
+
`buildah: ${version2.split("\n")[0] ?? version2} (storage-driver=${storageDriver ?? "storage.conf"})`
|
|
841
|
+
);
|
|
842
|
+
span?.setAttributes({
|
|
843
|
+
"container.engine": "buildah",
|
|
844
|
+
"buildah.version": toTag(version2.split("\n")[0]),
|
|
845
|
+
"buildah.storage_driver": toTag(storageDriver ?? "storage.conf")
|
|
846
|
+
});
|
|
847
|
+
} catch (err) {
|
|
848
|
+
debug(`buildah diagnostics unavailable: ${err.message}`);
|
|
849
|
+
}
|
|
850
|
+
},
|
|
851
|
+
async withRuntime(_span, fn) {
|
|
852
|
+
return fn();
|
|
853
|
+
},
|
|
854
|
+
async build(params) {
|
|
855
|
+
try {
|
|
856
|
+
const { stdout: stdout2 } = await client.run(["images", "--quiet"], {
|
|
857
|
+
quiet: true
|
|
858
|
+
});
|
|
859
|
+
const imageCount = stdout2.trim() ? stdout2.trim().split("\n").length : 0;
|
|
860
|
+
info(
|
|
861
|
+
imageCount > 0 ? `Buildah store: warm (${imageCount} image(s) present before build)` : "Buildah store: cold (no images present before build)"
|
|
862
|
+
);
|
|
863
|
+
} catch (err) {
|
|
864
|
+
debug(`could not read store warmth: ${err.message}`);
|
|
865
|
+
}
|
|
866
|
+
const sourceDateEpoch = await deploymentSourceDateEpoch();
|
|
867
|
+
if (sourceDateEpoch) {
|
|
868
|
+
info(
|
|
869
|
+
`Buildah source date epoch: ${sourceDateEpoch} (shared by this deployment's concurrent service builds)`
|
|
870
|
+
);
|
|
871
|
+
}
|
|
872
|
+
const usage = await BuildahImageUsage.begin();
|
|
873
|
+
const { stdout, stderr } = await client.run([
|
|
874
|
+
"build",
|
|
875
|
+
"--platform",
|
|
876
|
+
TARGET_PLATFORM,
|
|
877
|
+
// Commit and cache a layer per Dockerfile instruction so unchanged steps
|
|
878
|
+
// (base image, dependency installs, etc.) can be reused on later builds
|
|
879
|
+
// when the image store is warm. Without this buildah squashes the build
|
|
880
|
+
// and no per-step caching happens.
|
|
881
|
+
"--layers",
|
|
882
|
+
// With --layers, Buildah leaves the working container of a FROM-only
|
|
883
|
+
// stage behind, which stops the store cache from being saved.
|
|
884
|
+
"--force-rm",
|
|
885
|
+
...sourceDateEpoch ? ["--source-date-epoch", sourceDateEpoch] : [],
|
|
886
|
+
...pullFlags(params),
|
|
887
|
+
// Use the host network namespace for RUN steps. The build runs inside a
|
|
888
|
+
// restricted Hive cell that cannot program iptables, so buildah's default
|
|
889
|
+
// rootless networking (netavark) fails with
|
|
890
|
+
// "netavark: iptables ... Could not fetch rule set generation id".
|
|
891
|
+
// Host networking skips per-container network setup and reuses the cell's
|
|
892
|
+
// existing egress.
|
|
893
|
+
"--network",
|
|
894
|
+
"host",
|
|
895
|
+
...buildArgFlags(params),
|
|
896
|
+
"-t",
|
|
897
|
+
localImageRef(params),
|
|
898
|
+
"-f",
|
|
899
|
+
params.dockerfilePath,
|
|
900
|
+
params.contextDir
|
|
901
|
+
]);
|
|
902
|
+
await usage?.record(
|
|
903
|
+
() => imageIdsFromBuildOutput(`${stdout}
|
|
904
|
+
${stderr}`)
|
|
821
905
|
);
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
906
|
+
return localImageRef(params);
|
|
907
|
+
},
|
|
908
|
+
async login(params) {
|
|
909
|
+
try {
|
|
910
|
+
await client.run(
|
|
911
|
+
[
|
|
912
|
+
"login",
|
|
827
913
|
params.registry,
|
|
914
|
+
"--username",
|
|
828
915
|
params.username,
|
|
829
|
-
|
|
830
|
-
|
|
916
|
+
"--password-stdin"
|
|
917
|
+
],
|
|
918
|
+
{ input: params.token, quiet: !DEBUG }
|
|
831
919
|
);
|
|
920
|
+
} catch (err) {
|
|
921
|
+
const message = err.message;
|
|
922
|
+
if (/denied|forbidden|unauthorized|401|403/i.test(message)) {
|
|
923
|
+
throw new Error(
|
|
924
|
+
formatVcrAuthError(
|
|
925
|
+
params.registry,
|
|
926
|
+
params.username,
|
|
927
|
+
`Underlying error: ${message}`
|
|
928
|
+
)
|
|
929
|
+
);
|
|
930
|
+
}
|
|
931
|
+
throw err;
|
|
832
932
|
}
|
|
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");
|
|
933
|
+
},
|
|
934
|
+
async verifyStorage(span) {
|
|
935
|
+
await client.verifyStorage((message) => {
|
|
936
|
+
info(message);
|
|
937
|
+
const info0 = message.split("\n")[0];
|
|
938
|
+
span?.setAttributes({ "buildah.storage.verify": info0 });
|
|
939
|
+
});
|
|
940
|
+
},
|
|
941
|
+
async reportStorage(span) {
|
|
942
|
+
if (!DEBUG) {
|
|
851
943
|
return;
|
|
852
944
|
}
|
|
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")
|
|
945
|
+
try {
|
|
946
|
+
const storeInfo = await client.info();
|
|
947
|
+
if (!storeInfo) {
|
|
948
|
+
debug("buildah info: no `store` field in output");
|
|
949
|
+
return;
|
|
950
|
+
}
|
|
951
|
+
info(
|
|
952
|
+
`buildah storage: graphRoot=${storeInfo.graphRoot} runRoot=${storeInfo.runRoot} driver=${storeInfo.driver} backingFs=${storeInfo.backingFs || "?"}`
|
|
906
953
|
);
|
|
954
|
+
span?.setAttributes({
|
|
955
|
+
"buildah.storage.graph_root": storeInfo.graphRoot,
|
|
956
|
+
"buildah.storage.run_root": storeInfo.runRoot,
|
|
957
|
+
"buildah.storage.driver": storeInfo.driver,
|
|
958
|
+
"buildah.storage.backing_fs": storeInfo.backingFs
|
|
959
|
+
});
|
|
960
|
+
} catch (err) {
|
|
961
|
+
debug(`buildah storage report unavailable: ${err.message}`);
|
|
962
|
+
}
|
|
963
|
+
},
|
|
964
|
+
async push(params) {
|
|
965
|
+
const digestDir = (0, import_node_fs3.mkdtempSync)((0, import_node_path5.join)((0, import_node_os5.tmpdir)(), "vercel-container-digest-"));
|
|
966
|
+
const digestFile = (0, import_node_path5.join)(digestDir, "digest");
|
|
967
|
+
const zstdEnabled = !readString(process.env.VERCEL_VCR_DISABLE_ZSTD);
|
|
968
|
+
const zstdArgs = zstdEnabled ? [
|
|
969
|
+
"--compression-format",
|
|
970
|
+
"zstd",
|
|
971
|
+
"--compression-level",
|
|
972
|
+
"3",
|
|
973
|
+
"--force-compression",
|
|
974
|
+
"--format",
|
|
975
|
+
"oci"
|
|
976
|
+
] : [];
|
|
977
|
+
info(
|
|
978
|
+
`pushing ${params.imageRef} ` + (zstdEnabled ? "with zstd compression (level=3, force, oci)" : "with default compression (zstd disabled)")
|
|
979
|
+
);
|
|
980
|
+
const pushStart = Date.now();
|
|
981
|
+
let digest;
|
|
982
|
+
try {
|
|
983
|
+
await client.run([
|
|
984
|
+
"push",
|
|
985
|
+
...zstdArgs,
|
|
986
|
+
"--digestfile",
|
|
987
|
+
digestFile,
|
|
988
|
+
localImageRef(params),
|
|
989
|
+
...params.useBuildahStoreCache ? [`docker://${params.imageRef}`] : []
|
|
990
|
+
]);
|
|
991
|
+
digest = readDigestFile(digestFile);
|
|
992
|
+
} catch (err) {
|
|
993
|
+
const message = err.message;
|
|
994
|
+
if (/denied|forbidden|unauthorized|not found|401|403|404/i.test(message)) {
|
|
995
|
+
throw new Error(
|
|
996
|
+
[
|
|
997
|
+
`Pushing ${params.imageRef} was denied.`,
|
|
998
|
+
"",
|
|
999
|
+
`The build tried to ensure the "${params.repository}" repository exists, but`,
|
|
1000
|
+
"the push was still rejected. Verify access (or create the repository under",
|
|
1001
|
+
"your project's Sandboxes \u2192 Container Registry tab), then re-run the build.",
|
|
1002
|
+
"",
|
|
1003
|
+
`Underlying error: ${message}`
|
|
1004
|
+
].join("\n")
|
|
1005
|
+
);
|
|
1006
|
+
}
|
|
1007
|
+
throw err;
|
|
1008
|
+
} finally {
|
|
1009
|
+
(0, import_node_fs3.rmSync)(digestDir, { recursive: true, force: true });
|
|
907
1010
|
}
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
1011
|
+
debug(`push completed in ${Date.now() - pushStart}ms`);
|
|
1012
|
+
if (DIGEST_RE.test(digest)) {
|
|
1013
|
+
debug(`pushed digest ${digest}`);
|
|
1014
|
+
return digest;
|
|
1015
|
+
}
|
|
1016
|
+
debug(
|
|
1017
|
+
`--digestfile did not contain a digest (${JSON.stringify(digest)}) \u2014 resolving registry manifest`
|
|
1018
|
+
);
|
|
1019
|
+
return resolvePushedImageDigest(params);
|
|
916
1020
|
}
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
);
|
|
920
|
-
return resolvePushedImageDigest(params);
|
|
921
|
-
}
|
|
922
|
-
};
|
|
1021
|
+
};
|
|
1022
|
+
}
|
|
923
1023
|
function readDigestFile(path4) {
|
|
924
1024
|
try {
|
|
925
1025
|
return (0, import_node_fs3.readFileSync)(path4, "utf8").trim();
|
|
@@ -930,19 +1030,11 @@ function readDigestFile(path4) {
|
|
|
930
1030
|
}
|
|
931
1031
|
|
|
932
1032
|
// src/engines/docker.ts
|
|
933
|
-
var
|
|
1033
|
+
var import_node_child_process2 = require("child_process");
|
|
934
1034
|
function runDocker(args, opts = {}) {
|
|
935
1035
|
debug(`exec: docker ${args.join(" ")}`);
|
|
936
1036
|
return run("docker", args, opts);
|
|
937
1037
|
}
|
|
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
1038
|
async function isDockerDaemonReachable() {
|
|
947
1039
|
try {
|
|
948
1040
|
await run("docker", ["version", "--format", "{{.Server.Version}}"], {
|
|
@@ -966,7 +1058,7 @@ async function startDockerDaemon(span) {
|
|
|
966
1058
|
span?.setAttributes({ "docker.storage_driver": driver });
|
|
967
1059
|
step(`Starting Docker daemon (storage-driver=${driver})`);
|
|
968
1060
|
debug(`exec: dockerd ${args.join(" ")}`);
|
|
969
|
-
const child = (0,
|
|
1061
|
+
const child = (0, import_node_child_process2.spawn)("dockerd", args, {
|
|
970
1062
|
stdio: ["ignore", "pipe", "pipe"]
|
|
971
1063
|
});
|
|
972
1064
|
let log = "";
|
|
@@ -1019,7 +1111,7 @@ async function startDockerDaemon(span) {
|
|
|
1019
1111
|
].join("\n")
|
|
1020
1112
|
);
|
|
1021
1113
|
}
|
|
1022
|
-
await new Promise((
|
|
1114
|
+
await new Promise((resolve5) => setTimeout(resolve5, 500));
|
|
1023
1115
|
}
|
|
1024
1116
|
}
|
|
1025
1117
|
async function stopDockerDaemon(daemon, span) {
|
|
@@ -1029,12 +1121,12 @@ async function stopDockerDaemon(daemon, span) {
|
|
|
1029
1121
|
}
|
|
1030
1122
|
step("Stopping Docker daemon");
|
|
1031
1123
|
const stopTimeoutMs = Number(process.env.VERCEL_VCR_DOCKERD_STOP_TIMEOUT_MS) || 1e4;
|
|
1032
|
-
await new Promise((
|
|
1124
|
+
await new Promise((resolve5) => {
|
|
1033
1125
|
let settled = false;
|
|
1034
1126
|
const finish = () => {
|
|
1035
1127
|
if (!settled) {
|
|
1036
1128
|
settled = true;
|
|
1037
|
-
|
|
1129
|
+
resolve5();
|
|
1038
1130
|
}
|
|
1039
1131
|
};
|
|
1040
1132
|
child.once("exit", finish);
|
|
@@ -1062,7 +1154,7 @@ async function withManagedDaemon(span, fn) {
|
|
|
1062
1154
|
if (await isDockerDaemonReachable()) {
|
|
1063
1155
|
return fn();
|
|
1064
1156
|
}
|
|
1065
|
-
if (!await
|
|
1157
|
+
if (!await hasCommand("dockerd")) {
|
|
1066
1158
|
return fn();
|
|
1067
1159
|
}
|
|
1068
1160
|
const daemon = await withSpan(
|
|
@@ -1161,6 +1253,17 @@ ${version2.trim()}`);
|
|
|
1161
1253
|
params.dockerfilePath,
|
|
1162
1254
|
params.contextDir
|
|
1163
1255
|
]);
|
|
1256
|
+
return params.imageRef;
|
|
1257
|
+
},
|
|
1258
|
+
async onBuildTriggers(imageRef) {
|
|
1259
|
+
const { stdout } = await runDocker([
|
|
1260
|
+
"image",
|
|
1261
|
+
"inspect",
|
|
1262
|
+
"--format",
|
|
1263
|
+
"{{json .Config.OnBuild}}",
|
|
1264
|
+
imageRef
|
|
1265
|
+
]);
|
|
1266
|
+
return JSON.parse(stdout) ?? [];
|
|
1164
1267
|
},
|
|
1165
1268
|
async login(params) {
|
|
1166
1269
|
try {
|
|
@@ -1223,41 +1326,163 @@ ${version2.trim()}`);
|
|
|
1223
1326
|
};
|
|
1224
1327
|
|
|
1225
1328
|
// src/engines/index.ts
|
|
1226
|
-
function selectContainerEngine() {
|
|
1329
|
+
function selectContainerEngine(client) {
|
|
1227
1330
|
const override = readString(
|
|
1228
1331
|
process.env.VERCEL_CONTAINER_ENGINE
|
|
1229
1332
|
)?.toLowerCase();
|
|
1230
1333
|
if (override === "buildah")
|
|
1231
|
-
return
|
|
1334
|
+
return createBuildahEngine(client);
|
|
1232
1335
|
if (override === "docker")
|
|
1233
1336
|
return dockerEngine;
|
|
1234
|
-
return isBuildContainer() ?
|
|
1337
|
+
return isBuildContainer() ? createBuildahEngine(client) : dockerEngine;
|
|
1235
1338
|
}
|
|
1236
1339
|
|
|
1340
|
+
// src/clients/buildah.ts
|
|
1341
|
+
var BuildahClient = class {
|
|
1342
|
+
constructor(lock) {
|
|
1343
|
+
this.lock = lock;
|
|
1344
|
+
}
|
|
1345
|
+
async run(args, options = {}) {
|
|
1346
|
+
const fullArgs = [...await buildahStorageArgs(), ...args];
|
|
1347
|
+
debug(`exec: buildah ${fullArgs.join(" ")}`);
|
|
1348
|
+
const start = Date.now();
|
|
1349
|
+
try {
|
|
1350
|
+
const [command, ...commandArgs] = await this.lock.wrap([
|
|
1351
|
+
"buildah",
|
|
1352
|
+
...fullArgs
|
|
1353
|
+
]);
|
|
1354
|
+
const result = await run(command, commandArgs, options);
|
|
1355
|
+
debug(`buildah ${args[0]} finished in ${Date.now() - start}ms`);
|
|
1356
|
+
return result;
|
|
1357
|
+
} catch (error) {
|
|
1358
|
+
debug(`buildah ${args[0]} failed after ${Date.now() - start}ms`);
|
|
1359
|
+
throw error;
|
|
1360
|
+
}
|
|
1361
|
+
}
|
|
1362
|
+
/** Version checks must not open the store or acquire its lock before restore. */
|
|
1363
|
+
version() {
|
|
1364
|
+
this.cachedVersion ??= run("buildah", ["--version"], { quiet: true }).then(({ stdout }) => stdout.trim()).catch((error) => {
|
|
1365
|
+
this.cachedVersion = void 0;
|
|
1366
|
+
throw error;
|
|
1367
|
+
});
|
|
1368
|
+
return this.cachedVersion;
|
|
1369
|
+
}
|
|
1370
|
+
/** Only stable store fields are cached; image and container counts are not. */
|
|
1371
|
+
info() {
|
|
1372
|
+
this.cachedStoreInfo ??= this.readStoreInfo().then(
|
|
1373
|
+
(store) => {
|
|
1374
|
+
if (!store)
|
|
1375
|
+
this.cachedStoreInfo = void 0;
|
|
1376
|
+
return store;
|
|
1377
|
+
},
|
|
1378
|
+
(error) => {
|
|
1379
|
+
this.cachedStoreInfo = void 0;
|
|
1380
|
+
throw error;
|
|
1381
|
+
}
|
|
1382
|
+
);
|
|
1383
|
+
return this.cachedStoreInfo;
|
|
1384
|
+
}
|
|
1385
|
+
/** Storage mismatches are diagnostic unless strict verification is requested. */
|
|
1386
|
+
async verifyStorage(log = () => {
|
|
1387
|
+
}) {
|
|
1388
|
+
if (!isBuildContainer())
|
|
1389
|
+
return;
|
|
1390
|
+
if (readString(process.env.VERCEL_VCR_DOCKER_STORAGE_DRIVER))
|
|
1391
|
+
return;
|
|
1392
|
+
const strict = Boolean(readString(process.env.VERCEL_VCR_STRICT_STORAGE));
|
|
1393
|
+
let storeInfo;
|
|
1394
|
+
try {
|
|
1395
|
+
storeInfo = await this.info();
|
|
1396
|
+
} catch (error) {
|
|
1397
|
+
const message = `Could not verify buildah storage via \`buildah info\`: ${error.message}`;
|
|
1398
|
+
if (strict)
|
|
1399
|
+
throw new Error(message);
|
|
1400
|
+
log(message);
|
|
1401
|
+
return;
|
|
1402
|
+
}
|
|
1403
|
+
if (!storeInfo) {
|
|
1404
|
+
const message = "Could not verify buildah storage: `buildah info` returned no store data.";
|
|
1405
|
+
if (strict)
|
|
1406
|
+
throw new Error(message);
|
|
1407
|
+
log(message);
|
|
1408
|
+
return;
|
|
1409
|
+
}
|
|
1410
|
+
const problems = [];
|
|
1411
|
+
if (storeInfo.driver !== REQUIRED_BUILD_CONTAINER_DRIVER) {
|
|
1412
|
+
problems.push(
|
|
1413
|
+
`storage driver is "${storeInfo.driver}", expected "${REQUIRED_BUILD_CONTAINER_DRIVER}"`
|
|
1414
|
+
);
|
|
1415
|
+
}
|
|
1416
|
+
if (storeInfo.graphRoot !== BUILDAH_GRAPH_ROOT) {
|
|
1417
|
+
problems.push(
|
|
1418
|
+
`graphRoot is "${storeInfo.graphRoot}", expected the mounted volume "${BUILDAH_GRAPH_ROOT}"`
|
|
1419
|
+
);
|
|
1420
|
+
}
|
|
1421
|
+
if (storeInfo.backingFs && storeInfo.backingFs === "overlayfs") {
|
|
1422
|
+
problems.push(
|
|
1423
|
+
`backing filesystem is "${storeInfo.backingFs}" (the overlay rootfs), not the mounted volume`
|
|
1424
|
+
);
|
|
1425
|
+
}
|
|
1426
|
+
const summary = `buildah storage: driver=${storeInfo.driver} graphRoot=${storeInfo.graphRoot} runRoot=${storeInfo.runRoot} backingFs=${storeInfo.backingFs || "?"}`;
|
|
1427
|
+
if (problems.length === 0) {
|
|
1428
|
+
log(`${summary} \u2014 verified`);
|
|
1429
|
+
return;
|
|
1430
|
+
}
|
|
1431
|
+
const detail = `${summary}
|
|
1432
|
+
Problems: ${problems.join("; ")}.
|
|
1433
|
+
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).`;
|
|
1434
|
+
if (strict) {
|
|
1435
|
+
throw new Error(
|
|
1436
|
+
`Container build storage is not configured as intended.
|
|
1437
|
+
${detail}`
|
|
1438
|
+
);
|
|
1439
|
+
}
|
|
1440
|
+
log(
|
|
1441
|
+
`${detail}
|
|
1442
|
+
Continuing (set VERCEL_VCR_STRICT_STORAGE=1 to fail builds).`
|
|
1443
|
+
);
|
|
1444
|
+
}
|
|
1445
|
+
async readStoreInfo() {
|
|
1446
|
+
const { stdout } = await this.run(["info"], { quiet: true });
|
|
1447
|
+
const store = JSON.parse(stdout).store;
|
|
1448
|
+
if (!store)
|
|
1449
|
+
return void 0;
|
|
1450
|
+
const graphStatus = store.GraphStatus;
|
|
1451
|
+
return {
|
|
1452
|
+
graphRoot: String(store.GraphRoot ?? ""),
|
|
1453
|
+
runRoot: String(store.RunRoot ?? ""),
|
|
1454
|
+
driver: String(store.GraphDriverName ?? ""),
|
|
1455
|
+
backingFs: String(
|
|
1456
|
+
graphStatus?.["Backing Filesystem"] ?? graphStatus?.["Backing filesystem"] ?? ""
|
|
1457
|
+
)
|
|
1458
|
+
};
|
|
1459
|
+
}
|
|
1460
|
+
};
|
|
1461
|
+
|
|
1237
1462
|
// src/buildpacks/build.ts
|
|
1238
1463
|
var import_node_fs13 = require("fs");
|
|
1239
|
-
var
|
|
1240
|
-
var
|
|
1464
|
+
var import_node_os10 = require("os");
|
|
1465
|
+
var import_node_path17 = require("path");
|
|
1241
1466
|
|
|
1242
1467
|
// src/buildpacks/cache.ts
|
|
1243
|
-
var
|
|
1468
|
+
var import_node_crypto4 = require("crypto");
|
|
1244
1469
|
var import_node_fs4 = require("fs");
|
|
1245
|
-
var
|
|
1246
|
-
var
|
|
1470
|
+
var import_promises6 = require("fs/promises");
|
|
1471
|
+
var import_node_path6 = require("path");
|
|
1247
1472
|
var BUILDPACK_CACHE_DIR = ".vercel/cache/buildpacks";
|
|
1248
1473
|
var CNB_COMMITTED_DIR = "committed";
|
|
1249
1474
|
var CNB_CACHE_METADATA_FILE = "io.buildpacks.lifecycle.cache.metadata";
|
|
1250
1475
|
var IMAGE_ID_FILE = "image-id";
|
|
1251
1476
|
function buildpackCacheDir(options) {
|
|
1252
|
-
const root = (0,
|
|
1253
|
-
const app = (0,
|
|
1477
|
+
const root = (0, import_node_path6.resolve)(options.repoRootPath || options.workPath);
|
|
1478
|
+
const app = (0, import_node_path6.relative)(root, (0, import_node_path6.resolve)(options.workPath)).split(import_node_path6.sep).join("/") || ".";
|
|
1254
1479
|
const daemon = options.config?.daemon;
|
|
1255
1480
|
const owner = daemon ? { daemon: daemon.name } : options.service?.name ?? null;
|
|
1256
|
-
const key = (0,
|
|
1257
|
-
return (0,
|
|
1481
|
+
const key = (0, import_node_crypto4.createHash)("sha256").update(JSON.stringify([app, owner])).digest("hex").slice(0, 32);
|
|
1482
|
+
return (0, import_node_path6.join)(root, BUILDPACK_CACHE_DIR, key);
|
|
1258
1483
|
}
|
|
1259
1484
|
async function restoreBuildpackCache(dir, cacheKey, cacheDir) {
|
|
1260
|
-
const committed = (0,
|
|
1485
|
+
const committed = (0, import_node_path6.join)(cacheDir, CNB_COMMITTED_DIR);
|
|
1261
1486
|
const [copy, key] = await Promise.allSettled([
|
|
1262
1487
|
copyCommitted(dir, cacheDir),
|
|
1263
1488
|
cacheKey
|
|
@@ -1267,7 +1492,7 @@ async function restoreBuildpackCache(dir, cacheKey, cacheDir) {
|
|
|
1267
1492
|
throw copy.reason;
|
|
1268
1493
|
if (key.status === "rejected")
|
|
1269
1494
|
throw key.reason;
|
|
1270
|
-
if (readRegularFile((0,
|
|
1495
|
+
if (readRegularFile((0, import_node_path6.join)(dir, IMAGE_ID_FILE)) !== key.value) {
|
|
1271
1496
|
(0, import_node_fs4.rmSync)(committed, { recursive: true, force: true });
|
|
1272
1497
|
return { status: "miss", reason: "base_image_changed" };
|
|
1273
1498
|
}
|
|
@@ -1282,11 +1507,11 @@ async function restoreBuildpackCache(dir, cacheKey, cacheDir) {
|
|
|
1282
1507
|
}
|
|
1283
1508
|
}
|
|
1284
1509
|
async function saveBuildpackCache(dir, cacheKey, cacheDir) {
|
|
1285
|
-
const committed = (0,
|
|
1510
|
+
const committed = (0, import_node_path6.join)(cacheDir, CNB_COMMITTED_DIR);
|
|
1286
1511
|
let metadata;
|
|
1287
1512
|
try {
|
|
1288
1513
|
assertDirectory(committed);
|
|
1289
|
-
metadata = readRegularFile((0,
|
|
1514
|
+
metadata = readRegularFile((0, import_node_path6.join)(committed, CNB_CACHE_METADATA_FILE));
|
|
1290
1515
|
} catch (error) {
|
|
1291
1516
|
const missing = error.code === "ENOENT";
|
|
1292
1517
|
return {
|
|
@@ -1300,7 +1525,7 @@ async function saveBuildpackCache(dir, cacheKey, cacheDir) {
|
|
|
1300
1525
|
const bytes = sizeOf(committed);
|
|
1301
1526
|
(0, import_node_fs4.rmSync)(dir, { recursive: true, force: true });
|
|
1302
1527
|
await copyCommitted(cacheDir, dir);
|
|
1303
|
-
(0, import_node_fs4.writeFileSync)((0,
|
|
1528
|
+
(0, import_node_fs4.writeFileSync)((0, import_node_path6.join)(dir, IMAGE_ID_FILE), cacheKey);
|
|
1304
1529
|
return { status: "saved", bytes };
|
|
1305
1530
|
} catch (error) {
|
|
1306
1531
|
try {
|
|
@@ -1312,16 +1537,16 @@ async function saveBuildpackCache(dir, cacheKey, cacheDir) {
|
|
|
1312
1537
|
}
|
|
1313
1538
|
function isSaved(dir, cacheKey, metadata) {
|
|
1314
1539
|
try {
|
|
1315
|
-
const committed = (0,
|
|
1540
|
+
const committed = (0, import_node_path6.join)(dir, CNB_COMMITTED_DIR);
|
|
1316
1541
|
assertDirectory(committed);
|
|
1317
|
-
return readRegularFile((0,
|
|
1542
|
+
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
1543
|
} catch {
|
|
1319
1544
|
return false;
|
|
1320
1545
|
}
|
|
1321
1546
|
}
|
|
1322
1547
|
async function copyCommitted(from, to) {
|
|
1323
|
-
const source = (0,
|
|
1324
|
-
const target = (0,
|
|
1548
|
+
const source = (0, import_node_path6.join)(from, CNB_COMMITTED_DIR);
|
|
1549
|
+
const target = (0, import_node_path6.join)(to, CNB_COMMITTED_DIR);
|
|
1325
1550
|
assertDirectory(source);
|
|
1326
1551
|
(0, import_node_fs4.mkdirSync)(target, { recursive: true });
|
|
1327
1552
|
const entries = (0, import_node_fs4.readdirSync)(source, { withFileTypes: true });
|
|
@@ -1331,9 +1556,9 @@ async function copyCommitted(from, to) {
|
|
|
1331
1556
|
}
|
|
1332
1557
|
const copies = await Promise.allSettled(
|
|
1333
1558
|
entries.map(
|
|
1334
|
-
(entry) => (0,
|
|
1335
|
-
(0,
|
|
1336
|
-
(0,
|
|
1559
|
+
(entry) => (0, import_promises6.copyFile)(
|
|
1560
|
+
(0, import_node_path6.join)(source, entry.name),
|
|
1561
|
+
(0, import_node_path6.join)(target, entry.name),
|
|
1337
1562
|
import_node_fs4.constants.COPYFILE_FICLONE
|
|
1338
1563
|
)
|
|
1339
1564
|
)
|
|
@@ -1344,12 +1569,12 @@ async function copyCommitted(from, to) {
|
|
|
1344
1569
|
}
|
|
1345
1570
|
function assertDirectory(path4) {
|
|
1346
1571
|
if (!(0, import_node_fs4.lstatSync)(path4).isDirectory()) {
|
|
1347
|
-
throw new Error(`${(0,
|
|
1572
|
+
throw new Error(`${(0, import_node_path6.basename)(path4)} is not a directory`);
|
|
1348
1573
|
}
|
|
1349
1574
|
}
|
|
1350
1575
|
function readRegularFile(path4) {
|
|
1351
1576
|
if (!(0, import_node_fs4.lstatSync)(path4).isFile())
|
|
1352
|
-
throw notRegularFile((0,
|
|
1577
|
+
throw notRegularFile((0, import_node_path6.basename)(path4));
|
|
1353
1578
|
return (0, import_node_fs4.readFileSync)(path4, "utf8");
|
|
1354
1579
|
}
|
|
1355
1580
|
function notRegularFile(name) {
|
|
@@ -1357,25 +1582,13 @@ function notRegularFile(name) {
|
|
|
1357
1582
|
}
|
|
1358
1583
|
function sizeOf(dir) {
|
|
1359
1584
|
return (0, import_node_fs4.readdirSync)(dir).reduce(
|
|
1360
|
-
(bytes, name) => bytes + (0, import_node_fs4.lstatSync)((0,
|
|
1585
|
+
(bytes, name) => bytes + (0, import_node_fs4.lstatSync)((0, import_node_path6.join)(dir, name)).size,
|
|
1361
1586
|
0
|
|
1362
1587
|
);
|
|
1363
1588
|
}
|
|
1364
1589
|
function firstLine(error) {
|
|
1365
1590
|
return error.message.split("\n")[0];
|
|
1366
1591
|
}
|
|
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
1592
|
function describeRestore(outcome) {
|
|
1380
1593
|
return outcome.status === "hit" ? `restored buildpack cache (${formatBytes(outcome.bytes)})` : `no buildpack cache to restore (${outcome.reason})`;
|
|
1381
1594
|
}
|
|
@@ -1406,11 +1619,11 @@ function recordSave(span, outcome, durationMs) {
|
|
|
1406
1619
|
}
|
|
1407
1620
|
|
|
1408
1621
|
// src/buildpacks/lifecycle/lifecycle.ts
|
|
1409
|
-
var
|
|
1410
|
-
var
|
|
1622
|
+
var import_build_utils4 = require("@vercel/build-utils");
|
|
1623
|
+
var import_node_crypto5 = require("crypto");
|
|
1411
1624
|
var import_node_fs5 = require("fs");
|
|
1412
|
-
var
|
|
1413
|
-
var
|
|
1625
|
+
var import_node_os6 = require("os");
|
|
1626
|
+
var import_node_path7 = require("path");
|
|
1414
1627
|
var import_smol_toml = require("smol-toml");
|
|
1415
1628
|
var import_tar = require("tar");
|
|
1416
1629
|
|
|
@@ -1561,39 +1774,39 @@ async function stageWorkspace({ files, workPath }, destinationDir) {
|
|
|
1561
1774
|
const symlinks = /* @__PURE__ */ new Set();
|
|
1562
1775
|
for (const [name, file] of Object.entries(files)) {
|
|
1563
1776
|
const portable = name.replace(/\\/g, "/");
|
|
1564
|
-
const normalized =
|
|
1565
|
-
if (!normalized || normalized === "." ||
|
|
1777
|
+
const normalized = import_node_path7.posix.normalize(portable).replace(/\/$/, "");
|
|
1778
|
+
if (!normalized || normalized === "." || import_node_path7.win32.isAbsolute(name) || import_node_path7.posix.isAbsolute(portable) || portable.split("/").includes("..") || normalized in staged) {
|
|
1566
1779
|
throw new Error(`Invalid buildpack source path ${JSON.stringify(name)}.`);
|
|
1567
1780
|
}
|
|
1568
1781
|
staged[normalized] = file;
|
|
1569
|
-
if (!(0,
|
|
1782
|
+
if (!(0, import_build_utils4.isSymbolicLink)(file.mode))
|
|
1570
1783
|
continue;
|
|
1571
|
-
let target = (0,
|
|
1784
|
+
let target = (0, import_build_utils4.getSymlinkTarget)(file);
|
|
1572
1785
|
if (target === null && file.type === "FileRef") {
|
|
1573
|
-
target = (await (0,
|
|
1786
|
+
target = (await (0, import_build_utils4.streamToBuffer)(await file.toStreamAsync())).toString();
|
|
1574
1787
|
}
|
|
1575
1788
|
if (!target) {
|
|
1576
1789
|
throw new Error(`Cannot read buildpack source symlink ${name}.`);
|
|
1577
1790
|
}
|
|
1578
|
-
if ((0,
|
|
1579
|
-
target = (0,
|
|
1580
|
-
} else if (
|
|
1791
|
+
if ((0, import_node_path7.isAbsolute)(target)) {
|
|
1792
|
+
target = (0, import_node_path7.relative)((0, import_node_path7.resolve)(workPath, import_node_path7.posix.dirname(normalized)), target) || ".";
|
|
1793
|
+
} else if (import_node_path7.win32.isAbsolute(target)) {
|
|
1581
1794
|
throw new Error(
|
|
1582
1795
|
`Buildpack source symlink ${name} has an absolute target.`
|
|
1583
1796
|
);
|
|
1584
1797
|
}
|
|
1585
1798
|
target = target.replace(/\\/g, "/");
|
|
1586
|
-
const resolved =
|
|
1799
|
+
const resolved = import_node_path7.posix.join(import_node_path7.posix.dirname(normalized), target);
|
|
1587
1800
|
if (resolved === ".." || resolved.startsWith("../")) {
|
|
1588
1801
|
throw new Error(
|
|
1589
1802
|
`Buildpack source symlink ${name} escapes the service root. Buildpack services must be self-contained.`
|
|
1590
1803
|
);
|
|
1591
1804
|
}
|
|
1592
|
-
staged[normalized] = new
|
|
1805
|
+
staged[normalized] = new import_build_utils4.FileBlob({ data: target, mode: file.mode });
|
|
1593
1806
|
symlinks.add(normalized);
|
|
1594
1807
|
}
|
|
1595
1808
|
for (const name of Object.keys(staged)) {
|
|
1596
|
-
for (let parent =
|
|
1809
|
+
for (let parent = import_node_path7.posix.dirname(name); parent !== "."; parent = import_node_path7.posix.dirname(parent)) {
|
|
1597
1810
|
if (symlinks.has(parent)) {
|
|
1598
1811
|
throw new Error(
|
|
1599
1812
|
`Buildpack source ${name} is inside symlink ${parent}.`
|
|
@@ -1601,17 +1814,17 @@ async function stageWorkspace({ files, workPath }, destinationDir) {
|
|
|
1601
1814
|
}
|
|
1602
1815
|
}
|
|
1603
1816
|
const file = staged[name];
|
|
1604
|
-
if (file.type === "FileFsRef" && !(0,
|
|
1605
|
-
const sourcePath = (0,
|
|
1606
|
-
const sourceRelative = (0,
|
|
1607
|
-
if (sourceRelative.startsWith(`..${
|
|
1817
|
+
if (file.type === "FileFsRef" && !(0, import_build_utils4.isSymbolicLink)(file.mode)) {
|
|
1818
|
+
const sourcePath = (0, import_node_path7.resolve)(file.fsPath);
|
|
1819
|
+
const sourceRelative = (0, import_node_path7.relative)((0, import_node_path7.resolve)(workPath), sourcePath);
|
|
1820
|
+
if (sourceRelative.startsWith(`..${import_node_path7.sep}`) || sourceRelative === ".." || (0, import_node_path7.isAbsolute)(sourceRelative)) {
|
|
1608
1821
|
continue;
|
|
1609
1822
|
}
|
|
1610
1823
|
const sourceRoot = import_node_fs5.realpathSync.native(workPath);
|
|
1611
1824
|
const sourceTarget = import_node_fs5.realpathSync.native(sourcePath);
|
|
1612
|
-
if (sourceTarget !== (0,
|
|
1613
|
-
const target = (0,
|
|
1614
|
-
if (target === ".." || target.startsWith("../") || (0,
|
|
1825
|
+
if (sourceTarget !== (0, import_node_path7.resolve)(sourceRoot, sourceRelative)) {
|
|
1826
|
+
const target = (0, import_node_path7.relative)(sourceRoot, sourceTarget).split(import_node_path7.sep).join("/");
|
|
1827
|
+
if (target === ".." || target.startsWith("../") || (0, import_node_path7.isAbsolute)(target) || !(target in staged)) {
|
|
1615
1828
|
throw new Error(
|
|
1616
1829
|
`Buildpack source ${name} follows a symlink to an excluded or outside-root file. Buildpack services must be self-contained.`
|
|
1617
1830
|
);
|
|
@@ -1619,25 +1832,25 @@ async function stageWorkspace({ files, workPath }, destinationDir) {
|
|
|
1619
1832
|
}
|
|
1620
1833
|
}
|
|
1621
1834
|
}
|
|
1622
|
-
await (0,
|
|
1835
|
+
await (0, import_build_utils4.download)(staged, destinationDir);
|
|
1623
1836
|
const root = import_node_fs5.realpathSync.native(destinationDir);
|
|
1624
1837
|
for (const name of symlinks) {
|
|
1625
1838
|
let target;
|
|
1626
1839
|
try {
|
|
1627
|
-
target = (0,
|
|
1840
|
+
target = (0, import_node_path7.relative)(root, import_node_fs5.realpathSync.native((0, import_node_path7.join)(destinationDir, name)));
|
|
1628
1841
|
} catch {
|
|
1629
1842
|
throw new Error(
|
|
1630
1843
|
`Buildpack source symlink ${name} has no target in the staged service.`
|
|
1631
1844
|
);
|
|
1632
1845
|
}
|
|
1633
|
-
if (target === ".." || target.startsWith(`..${
|
|
1846
|
+
if (target === ".." || target.startsWith(`..${import_node_path7.sep}`) || (0, import_node_path7.isAbsolute)(target)) {
|
|
1634
1847
|
throw new Error(
|
|
1635
1848
|
`Buildpack source symlink ${name} escapes the staged service.`
|
|
1636
1849
|
);
|
|
1637
1850
|
}
|
|
1638
1851
|
}
|
|
1639
1852
|
}
|
|
1640
|
-
function writePlatformEnvDir(buildEnv, config, parentDir = (0,
|
|
1853
|
+
function writePlatformEnvDir(buildEnv, config, parentDir = (0, import_node_os6.tmpdir)()) {
|
|
1641
1854
|
const environment = { ...buildEnv };
|
|
1642
1855
|
delete environment.VERCEL_NODE_HOME;
|
|
1643
1856
|
delete environment.VERCEL_ENTRYPOINT;
|
|
@@ -1651,14 +1864,14 @@ function writePlatformEnvDir(buildEnv, config, parentDir = (0, import_node_os4.t
|
|
|
1651
1864
|
if (config.command) {
|
|
1652
1865
|
environment.VERCEL_COMMAND = JSON.stringify(config.command);
|
|
1653
1866
|
}
|
|
1654
|
-
const dir = (0, import_node_fs5.mkdtempSync)((0,
|
|
1867
|
+
const dir = (0, import_node_fs5.mkdtempSync)((0, import_node_path7.join)(parentDir, "vercel-cnb-env-"));
|
|
1655
1868
|
(0, import_node_fs5.chmodSync)(dir, 493);
|
|
1656
1869
|
for (const [key, value] of Object.entries(environment)) {
|
|
1657
1870
|
if (!ENV_NAME_RE.test(key)) {
|
|
1658
1871
|
debug(`skipping build env var with an unsupported name: ${key}`);
|
|
1659
1872
|
continue;
|
|
1660
1873
|
}
|
|
1661
|
-
const file = (0,
|
|
1874
|
+
const file = (0, import_node_path7.join)(dir, key);
|
|
1662
1875
|
(0, import_node_fs5.writeFileSync)(file, value);
|
|
1663
1876
|
(0, import_node_fs5.chmodSync)(file, 420);
|
|
1664
1877
|
}
|
|
@@ -1723,7 +1936,7 @@ function lifecycleFailure(error, context) {
|
|
|
1723
1936
|
{ exitCode }
|
|
1724
1937
|
);
|
|
1725
1938
|
}
|
|
1726
|
-
function writeOrderDir(buildpack, parentDir = (0,
|
|
1939
|
+
function writeOrderDir(buildpack, parentDir = (0, import_node_os6.tmpdir)()) {
|
|
1727
1940
|
const order = {
|
|
1728
1941
|
order: [
|
|
1729
1942
|
{
|
|
@@ -1734,9 +1947,9 @@ function writeOrderDir(buildpack, parentDir = (0, import_node_os4.tmpdir)()) {
|
|
|
1734
1947
|
}
|
|
1735
1948
|
]
|
|
1736
1949
|
};
|
|
1737
|
-
const dir = (0, import_node_fs5.mkdtempSync)((0,
|
|
1950
|
+
const dir = (0, import_node_fs5.mkdtempSync)((0, import_node_path7.join)(parentDir, "vercel-cnb-order-"));
|
|
1738
1951
|
(0, import_node_fs5.chmodSync)(dir, 493);
|
|
1739
|
-
const file = (0,
|
|
1952
|
+
const file = (0, import_node_path7.join)(dir, "order.toml");
|
|
1740
1953
|
(0, import_node_fs5.writeFileSync)(file, (0, import_smol_toml.stringify)(order));
|
|
1741
1954
|
(0, import_node_fs5.chmodSync)(file, 420);
|
|
1742
1955
|
return dir;
|
|
@@ -1765,7 +1978,7 @@ async function downloadPinnedArchive(archive, destinationPath) {
|
|
|
1765
1978
|
);
|
|
1766
1979
|
}
|
|
1767
1980
|
const contents = new Uint8Array(await response.arrayBuffer());
|
|
1768
|
-
const checksum = (0,
|
|
1981
|
+
const checksum = (0, import_node_crypto5.createHash)("sha256").update(contents).digest("hex");
|
|
1769
1982
|
if (checksum !== archive.sha256) {
|
|
1770
1983
|
throw new Error(
|
|
1771
1984
|
`Checksum mismatch for ${archiveName(archive)}: expected ${archive.sha256}, received ${checksum}.`
|
|
@@ -1774,7 +1987,7 @@ async function downloadPinnedArchive(archive, destinationPath) {
|
|
|
1774
1987
|
(0, import_node_fs5.writeFileSync)(destinationPath, contents);
|
|
1775
1988
|
}
|
|
1776
1989
|
async function fetchPinnedArchive(archive, destinationDir) {
|
|
1777
|
-
const archivePath = (0,
|
|
1990
|
+
const archivePath = (0, import_node_path7.join)(destinationDir, `${archive.sha256}.tgz`);
|
|
1778
1991
|
try {
|
|
1779
1992
|
await downloadPinnedArchive(archive, archivePath);
|
|
1780
1993
|
(0, import_tar.extract)({ file: archivePath, cwd: destinationDir, sync: true });
|
|
@@ -1791,16 +2004,16 @@ function blobPath(layoutDir, digest) {
|
|
|
1791
2004
|
if (!match) {
|
|
1792
2005
|
throw new Error(`Unsupported OCI digest ${JSON.stringify(digest)}.`);
|
|
1793
2006
|
}
|
|
1794
|
-
return (0,
|
|
2007
|
+
return (0, import_node_path7.join)(layoutDir, "blobs", "sha256", match[1]);
|
|
1795
2008
|
}
|
|
1796
2009
|
async function fetchBuildpackage(archive, cnbDir) {
|
|
1797
|
-
const layoutDir = (0, import_node_fs5.mkdtempSync)((0,
|
|
1798
|
-
const archivePath = (0,
|
|
2010
|
+
const layoutDir = (0, import_node_fs5.mkdtempSync)((0, import_node_path7.join)((0, import_node_os6.tmpdir)(), "vercel-cnb-buildpackage-"));
|
|
2011
|
+
const archivePath = (0, import_node_path7.join)(layoutDir, "buildpackage.cnb");
|
|
1799
2012
|
try {
|
|
1800
2013
|
await downloadPinnedArchive(archive, archivePath);
|
|
1801
2014
|
await (0, import_tar.extract)({ file: archivePath, cwd: layoutDir });
|
|
1802
2015
|
const index = readOciJson(
|
|
1803
|
-
(0,
|
|
2016
|
+
(0, import_node_path7.join)(layoutDir, "index.json")
|
|
1804
2017
|
);
|
|
1805
2018
|
const name = archiveName(archive);
|
|
1806
2019
|
const manifestDigest = index.manifests?.[0]?.digest;
|
|
@@ -1822,7 +2035,7 @@ async function fetchBuildpackage(archive, cnbDir) {
|
|
|
1822
2035
|
);
|
|
1823
2036
|
}
|
|
1824
2037
|
const layerPath = blobPath(layoutDir, layer.digest);
|
|
1825
|
-
const actual = (0,
|
|
2038
|
+
const actual = (0, import_node_crypto5.createHash)("sha256").update(new Uint8Array((0, import_node_fs5.readFileSync)(layerPath))).digest("hex");
|
|
1826
2039
|
if (`sha256:${actual}` !== layer.digest) {
|
|
1827
2040
|
throw new Error(
|
|
1828
2041
|
`${name} layer ${layer.digest} hashes to sha256:${actual}.`
|
|
@@ -1859,14 +2072,14 @@ async function fetchDistribution(buildpack, cnbDir) {
|
|
|
1859
2072
|
)
|
|
1860
2073
|
]);
|
|
1861
2074
|
for (const entry of buildpack.buildpacks) {
|
|
1862
|
-
const directory = (0,
|
|
2075
|
+
const directory = (0, import_node_path7.join)(
|
|
1863
2076
|
cnbDir,
|
|
1864
2077
|
"buildpacks",
|
|
1865
2078
|
entry.id.replace("/", "_"),
|
|
1866
2079
|
entry.version
|
|
1867
2080
|
);
|
|
1868
2081
|
for (const file of ["buildpack.toml", "bin/detect", "bin/build"]) {
|
|
1869
|
-
if (!(0, import_node_fs5.statSync)((0,
|
|
2082
|
+
if (!(0, import_node_fs5.statSync)((0, import_node_path7.join)(directory, file), { throwIfNoEntry: false })?.isFile()) {
|
|
1870
2083
|
throw new Error(
|
|
1871
2084
|
`Buildpack ${entry.id}@${entry.version} is missing ${file}.`
|
|
1872
2085
|
);
|
|
@@ -1891,20 +2104,113 @@ function creatorArgs(image, imageRef, options = {}) {
|
|
|
1891
2104
|
|
|
1892
2105
|
// src/buildpacks/lifecycle/buildah.ts
|
|
1893
2106
|
var import_node_fs6 = require("fs");
|
|
1894
|
-
var
|
|
1895
|
-
var
|
|
1896
|
-
var
|
|
1897
|
-
|
|
2107
|
+
var import_node_crypto6 = require("crypto");
|
|
2108
|
+
var import_node_os8 = require("os");
|
|
2109
|
+
var import_node_path10 = require("path");
|
|
2110
|
+
|
|
2111
|
+
// src/buildah-store-cache/cleanup.ts
|
|
2112
|
+
var import_node_child_process3 = require("child_process");
|
|
2113
|
+
|
|
2114
|
+
// src/buildah-store-cache/command/index.ts
|
|
2115
|
+
var import_node_path8 = require("path");
|
|
2116
|
+
var STORE_COMMAND_SCRIPT = (0, import_node_path8.join)(__dirname, "buildah-store-command.js");
|
|
2117
|
+
function storeCommand(request, script = STORE_COMMAND_SCRIPT) {
|
|
2118
|
+
return [process.execPath, script, JSON.stringify(request)];
|
|
2119
|
+
}
|
|
2120
|
+
async function runStoreCommand(request, lock) {
|
|
2121
|
+
const [command, ...args] = lock.exclusive(storeCommand(request));
|
|
2122
|
+
const { stdout } = await run(command, args, { quiet: true });
|
|
2123
|
+
return JSON.parse(stdout);
|
|
2124
|
+
}
|
|
2125
|
+
|
|
2126
|
+
// src/buildah-store-cache/lock.ts
|
|
2127
|
+
var import_node_os7 = require("os");
|
|
2128
|
+
var import_node_path9 = require("path");
|
|
2129
|
+
var FileLock = class {
|
|
2130
|
+
constructor(path4) {
|
|
2131
|
+
this.path = path4;
|
|
2132
|
+
}
|
|
2133
|
+
shared(command) {
|
|
2134
|
+
return this.wrap("--shared", command);
|
|
2135
|
+
}
|
|
2136
|
+
exclusive(command) {
|
|
2137
|
+
return this.wrap("--exclusive", command);
|
|
2138
|
+
}
|
|
2139
|
+
wrap(mode, command) {
|
|
2140
|
+
return ["flock", mode, "--close", this.path, ...command];
|
|
2141
|
+
}
|
|
2142
|
+
};
|
|
2143
|
+
var flockAvailable;
|
|
2144
|
+
async function buildahStoreLock() {
|
|
2145
|
+
if (!buildahStoreCacheEnabled()) {
|
|
2146
|
+
return void 0;
|
|
2147
|
+
}
|
|
2148
|
+
flockAvailable ??= hasCommand("flock");
|
|
2149
|
+
return await flockAvailable ? new FileLock((0, import_node_path9.join)((0, import_node_os7.tmpdir)(), "vercel-buildah-store.lock")) : void 0;
|
|
2150
|
+
}
|
|
2151
|
+
var sharedBuildahStoreLock = {
|
|
2152
|
+
async wrap(command) {
|
|
2153
|
+
const lock = await buildahStoreLock();
|
|
2154
|
+
return lock ? lock.shared(command) : command;
|
|
2155
|
+
}
|
|
2156
|
+
};
|
|
2157
|
+
|
|
2158
|
+
// src/buildah-store-cache/cleanup.ts
|
|
2159
|
+
async function startContainerCleanup(name) {
|
|
2160
|
+
const rmArgs = [...await buildahStorageArgs(), "rm", name];
|
|
2161
|
+
debug(`exec in the background: buildah ${rmArgs.join(" ")}`);
|
|
2162
|
+
const start = Date.now();
|
|
2163
|
+
const [command, ...commandArgs] = await cleanupCommand(name, rmArgs);
|
|
2164
|
+
const child = (0, import_node_child_process3.spawn)(command, commandArgs, {
|
|
2165
|
+
detached: true,
|
|
2166
|
+
stdio: "ignore"
|
|
2167
|
+
});
|
|
2168
|
+
child.on(
|
|
2169
|
+
"error",
|
|
2170
|
+
(error) => debug(`background buildah rm failed: ${error.message}`)
|
|
2171
|
+
);
|
|
2172
|
+
child.on(
|
|
2173
|
+
"exit",
|
|
2174
|
+
(code) => debug(
|
|
2175
|
+
`background buildah rm exited with code ${code} after ${Date.now() - start}ms`
|
|
2176
|
+
)
|
|
2177
|
+
);
|
|
2178
|
+
child.unref();
|
|
2179
|
+
}
|
|
2180
|
+
async function cleanupCommand(container, buildahArgs) {
|
|
2181
|
+
const record = await recordPendingCleanup(container);
|
|
2182
|
+
return sharedBuildahStoreLock.wrap(
|
|
2183
|
+
record ? storeCommand({ action: "cleanup", options: { record, buildahArgs } }) : ["buildah", ...buildahArgs]
|
|
2184
|
+
);
|
|
2185
|
+
}
|
|
2186
|
+
async function recordPendingCleanup(container) {
|
|
2187
|
+
if (!buildahStoreCacheEnabled()) {
|
|
2188
|
+
return void 0;
|
|
2189
|
+
}
|
|
2190
|
+
try {
|
|
2191
|
+
if (!CLEANUP_CONTAINER_RE.test(container)) {
|
|
2192
|
+
throw new Error("invalid Buildah cleanup container");
|
|
2193
|
+
}
|
|
2194
|
+
return await writeStateRecord(".cleanup", `${container}
|
|
2195
|
+
`);
|
|
2196
|
+
} catch (error) {
|
|
2197
|
+
debug(`could not record Buildah cleanup: ${error.message}`);
|
|
2198
|
+
return void 0;
|
|
2199
|
+
}
|
|
2200
|
+
}
|
|
2201
|
+
|
|
2202
|
+
// src/buildpacks/lifecycle/buildah.ts
|
|
2203
|
+
async function cleanUpBuildContainer(name) {
|
|
1898
2204
|
try {
|
|
1899
|
-
await
|
|
2205
|
+
await startContainerCleanup(name);
|
|
1900
2206
|
} catch (error) {
|
|
1901
2207
|
debug(
|
|
1902
|
-
`could not
|
|
2208
|
+
`could not clean up Buildah CNB container ${name}: ${error.message}`
|
|
1903
2209
|
);
|
|
1904
2210
|
}
|
|
1905
2211
|
}
|
|
1906
|
-
async function inspectBaseImageId(container) {
|
|
1907
|
-
const { stdout } = await
|
|
2212
|
+
async function inspectBaseImageId(container, client) {
|
|
2213
|
+
const { stdout } = await client.run(
|
|
1908
2214
|
["inspect", "--type", "container", container],
|
|
1909
2215
|
{ quiet: true }
|
|
1910
2216
|
);
|
|
@@ -1929,14 +2235,14 @@ function cacheScratchParent() {
|
|
|
1929
2235
|
);
|
|
1930
2236
|
}
|
|
1931
2237
|
}
|
|
1932
|
-
return (0,
|
|
2238
|
+
return (0, import_node_os8.tmpdir)();
|
|
1933
2239
|
}
|
|
1934
2240
|
function chownTree(root, uid, gid) {
|
|
1935
2241
|
const stat = (0, import_node_fs6.lstatSync)(root);
|
|
1936
2242
|
if (stat.isDirectory()) {
|
|
1937
2243
|
(0, import_node_fs6.chmodSync)(root, 493);
|
|
1938
2244
|
for (const entry of (0, import_node_fs6.readdirSync)(root)) {
|
|
1939
|
-
chownTree((0,
|
|
2245
|
+
chownTree((0, import_node_path10.join)(root, entry), uid, gid);
|
|
1940
2246
|
}
|
|
1941
2247
|
} else if (stat.isFile()) {
|
|
1942
2248
|
(0, import_node_fs6.chmodSync)(root, 420);
|
|
@@ -1949,12 +2255,13 @@ var RegistrySetupFailure = class extends Error {
|
|
|
1949
2255
|
this.reason = reason;
|
|
1950
2256
|
}
|
|
1951
2257
|
};
|
|
1952
|
-
async function prepareBuildContainer(buildContainer, buildpack, params) {
|
|
2258
|
+
async function prepareBuildContainer(buildContainer, buildpack, params, client) {
|
|
1953
2259
|
const start = Date.now();
|
|
1954
2260
|
step(
|
|
1955
2261
|
`Preparing ${buildpack.runtime} ${params.image.version} stock image and Vercel buildpacks`
|
|
1956
2262
|
);
|
|
1957
|
-
await
|
|
2263
|
+
const usage = await BuildahImageUsage.begin();
|
|
2264
|
+
await client.run([
|
|
1958
2265
|
"from",
|
|
1959
2266
|
"--pull=always",
|
|
1960
2267
|
"--platform",
|
|
@@ -1963,7 +2270,10 @@ async function prepareBuildContainer(buildContainer, buildpack, params) {
|
|
|
1963
2270
|
buildContainer,
|
|
1964
2271
|
params.image.buildImage
|
|
1965
2272
|
]);
|
|
1966
|
-
await
|
|
2273
|
+
await usage?.record(async () => [
|
|
2274
|
+
await inspectBaseImageId(buildContainer, client)
|
|
2275
|
+
]);
|
|
2276
|
+
await client.run([
|
|
1967
2277
|
"run",
|
|
1968
2278
|
"--network",
|
|
1969
2279
|
"host",
|
|
@@ -1973,7 +2283,7 @@ async function prepareBuildContainer(buildContainer, buildpack, params) {
|
|
|
1973
2283
|
"-c",
|
|
1974
2284
|
`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
2285
|
]);
|
|
1976
|
-
await
|
|
2286
|
+
await client.run([
|
|
1977
2287
|
"copy",
|
|
1978
2288
|
"--chown",
|
|
1979
2289
|
BUILD_USER,
|
|
@@ -1989,17 +2299,17 @@ async function fetchDistributionTimed(buildpack, cnbDir) {
|
|
|
1989
2299
|
await fetchDistribution(buildpack, cnbDir);
|
|
1990
2300
|
done(`fetched the CNB lifecycle and buildpacks in ${elapsed(start)}`);
|
|
1991
2301
|
}
|
|
1992
|
-
async function copySavedCache(dir, buildContainer, containerReady) {
|
|
2302
|
+
async function copySavedCache(dir, buildContainer, containerReady, client) {
|
|
1993
2303
|
const start = Date.now();
|
|
1994
2304
|
step("Restoring the buildpack cache");
|
|
1995
2305
|
let scratch;
|
|
1996
2306
|
try {
|
|
1997
|
-
scratch = (0, import_node_fs6.mkdtempSync)((0,
|
|
2307
|
+
scratch = (0, import_node_fs6.mkdtempSync)((0, import_node_path10.join)(cacheScratchParent(), "vercel-cnb-cache-"));
|
|
1998
2308
|
(0, import_node_fs6.chmodSync)(scratch, 493);
|
|
1999
|
-
const cacheDir = (0,
|
|
2309
|
+
const cacheDir = (0, import_node_path10.join)(scratch, "cache");
|
|
2000
2310
|
(0, import_node_fs6.mkdirSync)(cacheDir, { mode: 493 });
|
|
2001
2311
|
const imageId = containerReady.then(
|
|
2002
|
-
() => inspectBaseImageId(buildContainer)
|
|
2312
|
+
() => inspectBaseImageId(buildContainer, client)
|
|
2003
2313
|
);
|
|
2004
2314
|
const restored = await restoreBuildpackCache(dir, imageId, cacheDir);
|
|
2005
2315
|
const baseImageId = await imageId;
|
|
@@ -2025,7 +2335,7 @@ async function copySavedCache(dir, buildContainer, containerReady) {
|
|
|
2025
2335
|
};
|
|
2026
2336
|
}
|
|
2027
2337
|
}
|
|
2028
|
-
async function buildAndPushWithLifecycle(buildpack, params, span) {
|
|
2338
|
+
async function buildAndPushWithLifecycle(buildpack, params, client, span) {
|
|
2029
2339
|
const urls = distributionUrls(buildpack);
|
|
2030
2340
|
return withSpan(
|
|
2031
2341
|
span,
|
|
@@ -2041,20 +2351,20 @@ async function buildAndPushWithLifecycle(buildpack, params, span) {
|
|
|
2041
2351
|
"image.ref": params.imageRef
|
|
2042
2352
|
},
|
|
2043
2353
|
async (lifecycleSpan) => {
|
|
2044
|
-
const suffix = `${process.pid}-${(0,
|
|
2354
|
+
const suffix = `${process.pid}-${(0, import_node_crypto6.randomBytes)(4).toString("hex")}`;
|
|
2045
2355
|
const buildContainer = `vercel-cnb-${buildpack.runtime}-${suffix}`;
|
|
2046
2356
|
let cnbDir;
|
|
2047
2357
|
let reportDir;
|
|
2048
2358
|
let orderDir;
|
|
2049
2359
|
let platformEnvDir;
|
|
2050
2360
|
let cacheScratch;
|
|
2051
|
-
let
|
|
2361
|
+
let containerCleanup;
|
|
2052
2362
|
try {
|
|
2053
|
-
reportDir = (0, import_node_fs6.mkdtempSync)((0,
|
|
2054
|
-
const reportPath = (0,
|
|
2363
|
+
reportDir = (0, import_node_fs6.mkdtempSync)((0, import_node_path10.join)((0, import_node_os8.tmpdir)(), "vercel-cnb-report-"));
|
|
2364
|
+
const reportPath = (0, import_node_path10.join)(reportDir, "report.toml");
|
|
2055
2365
|
(0, import_node_fs6.chownSync)(reportDir, BUILD_USER_ID, BUILD_GROUP_ID);
|
|
2056
2366
|
(0, import_node_fs6.chmodSync)(reportDir, 493);
|
|
2057
|
-
cnbDir = (0, import_node_fs6.mkdtempSync)((0,
|
|
2367
|
+
cnbDir = (0, import_node_fs6.mkdtempSync)((0, import_node_path10.join)((0, import_node_os8.tmpdir)(), "vercel-cnb-"));
|
|
2058
2368
|
(0, import_node_fs6.chmodSync)(cnbDir, 493);
|
|
2059
2369
|
orderDir = writeOrderDir(buildpack);
|
|
2060
2370
|
platformEnvDir = writePlatformEnvDir(params.buildEnv, {
|
|
@@ -2066,13 +2376,19 @@ async function buildAndPushWithLifecycle(buildpack, params, span) {
|
|
|
2066
2376
|
const containerReady = prepareBuildContainer(
|
|
2067
2377
|
buildContainer,
|
|
2068
2378
|
buildpack,
|
|
2069
|
-
params
|
|
2379
|
+
params,
|
|
2380
|
+
client
|
|
2070
2381
|
);
|
|
2071
2382
|
const [registry, container, distribution2, cacheCopy] = await Promise.allSettled([
|
|
2072
2383
|
registrySetup,
|
|
2073
2384
|
containerReady,
|
|
2074
2385
|
fetchDistributionTimed(buildpack, cnbDir),
|
|
2075
|
-
params.cache ? copySavedCache(
|
|
2386
|
+
params.cache ? copySavedCache(
|
|
2387
|
+
params.cache.dir,
|
|
2388
|
+
buildContainer,
|
|
2389
|
+
containerReady,
|
|
2390
|
+
client
|
|
2391
|
+
) : void 0
|
|
2076
2392
|
]);
|
|
2077
2393
|
const copy = cacheCopy.status === "fulfilled" ? cacheCopy.value : void 0;
|
|
2078
2394
|
cacheScratch = copy?.scratch;
|
|
@@ -2105,7 +2421,7 @@ async function buildAndPushWithLifecycle(buildpack, params, span) {
|
|
|
2105
2421
|
step(
|
|
2106
2422
|
`Building and publishing ${params.imageRef} via Vercel ${buildpack.runtime} buildpacks`
|
|
2107
2423
|
);
|
|
2108
|
-
await
|
|
2424
|
+
await client.run(
|
|
2109
2425
|
[
|
|
2110
2426
|
"run",
|
|
2111
2427
|
"--network",
|
|
@@ -2142,7 +2458,7 @@ async function buildAndPushWithLifecycle(buildpack, params, span) {
|
|
|
2142
2458
|
],
|
|
2143
2459
|
{ env: lifecycleEnv }
|
|
2144
2460
|
);
|
|
2145
|
-
|
|
2461
|
+
containerCleanup = cleanUpBuildContainer(buildContainer);
|
|
2146
2462
|
const digest = readReportDigest((0, import_node_fs6.readFileSync)(reportPath, "utf8"));
|
|
2147
2463
|
if (!digest) {
|
|
2148
2464
|
throw new Error(
|
|
@@ -2179,7 +2495,7 @@ async function buildAndPushWithLifecycle(buildpack, params, span) {
|
|
|
2179
2495
|
span: lifecycleSpan
|
|
2180
2496
|
});
|
|
2181
2497
|
} finally {
|
|
2182
|
-
|
|
2498
|
+
containerCleanup ??= cleanUpBuildContainer(buildContainer);
|
|
2183
2499
|
for (const dir of [
|
|
2184
2500
|
cnbDir,
|
|
2185
2501
|
reportDir,
|
|
@@ -2195,17 +2511,17 @@ async function buildAndPushWithLifecycle(buildpack, params, span) {
|
|
|
2195
2511
|
}
|
|
2196
2512
|
}
|
|
2197
2513
|
}
|
|
2198
|
-
await
|
|
2514
|
+
await containerCleanup;
|
|
2199
2515
|
}
|
|
2200
2516
|
}
|
|
2201
2517
|
);
|
|
2202
2518
|
}
|
|
2203
2519
|
|
|
2204
2520
|
// src/buildpacks/lifecycle/docker.ts
|
|
2205
|
-
var
|
|
2521
|
+
var import_node_crypto7 = require("crypto");
|
|
2206
2522
|
var import_node_fs7 = require("fs");
|
|
2207
|
-
var
|
|
2208
|
-
var
|
|
2523
|
+
var import_node_os9 = require("os");
|
|
2524
|
+
var import_node_path11 = require("path");
|
|
2209
2525
|
function localBuildImageDockerfile(buildImage, nodeMajor) {
|
|
2210
2526
|
const lines = [];
|
|
2211
2527
|
if (nodeMajor !== void 0) {
|
|
@@ -2224,7 +2540,7 @@ function localBuildImageDockerfile(buildImage, nodeMajor) {
|
|
|
2224
2540
|
`;
|
|
2225
2541
|
}
|
|
2226
2542
|
function localBuildImageTag(dockerfile) {
|
|
2227
|
-
const hash = (0,
|
|
2543
|
+
const hash = (0, import_node_crypto7.createHash)("sha256").update(dockerfile).digest("hex");
|
|
2228
2544
|
return `vercel-cnb-build:${hash.slice(0, 16)}`;
|
|
2229
2545
|
}
|
|
2230
2546
|
async function runDocker2(args, opts = {}) {
|
|
@@ -2250,8 +2566,8 @@ var buildAndPushWithLifecycleDocker = async (buildpack, params, span) => {
|
|
|
2250
2566
|
let stageDir;
|
|
2251
2567
|
const containers = [];
|
|
2252
2568
|
try {
|
|
2253
|
-
stageDir = (0, import_node_fs7.mkdtempSync)((0,
|
|
2254
|
-
const cnbDir = (0,
|
|
2569
|
+
stageDir = (0, import_node_fs7.mkdtempSync)((0, import_node_path11.join)((0, import_node_os9.tmpdir)(), "vercel-cnb-"));
|
|
2570
|
+
const cnbDir = (0, import_node_path11.join)(stageDir, "cnb");
|
|
2255
2571
|
(0, import_node_fs7.mkdirSync)(cnbDir, { mode: 493 });
|
|
2256
2572
|
const orderDir = writeOrderDir(buildpack, stageDir);
|
|
2257
2573
|
const platformEnvDir = writePlatformEnvDir(
|
|
@@ -2271,7 +2587,7 @@ var buildAndPushWithLifecycleDocker = async (buildpack, params, span) => {
|
|
|
2271
2587
|
params.nodeToolchain?.major
|
|
2272
2588
|
);
|
|
2273
2589
|
const buildImageTag = localBuildImageTag(dockerfile);
|
|
2274
|
-
const buildImageIdPath = (0,
|
|
2590
|
+
const buildImageIdPath = (0, import_node_path11.join)(stageDir, "build-image.id");
|
|
2275
2591
|
step(
|
|
2276
2592
|
`Preparing ${buildpack.runtime} ${params.image.version} build image` + (params.nodeToolchain ? ` with Node.js ${params.nodeToolchain.major}` : "")
|
|
2277
2593
|
);
|
|
@@ -2293,7 +2609,7 @@ var buildAndPushWithLifecycleDocker = async (buildpack, params, span) => {
|
|
|
2293
2609
|
if (!/^sha256:[a-f0-9]{64}$/.test(buildImageId)) {
|
|
2294
2610
|
throw new Error("Docker build did not report a valid image ID.");
|
|
2295
2611
|
}
|
|
2296
|
-
const containerName = `vercel-cnb-${buildpack.runtime}-${process.pid}-${(0,
|
|
2612
|
+
const containerName = `vercel-cnb-${buildpack.runtime}-${process.pid}-${(0, import_node_crypto7.randomBytes)(8).toString("hex")}`;
|
|
2297
2613
|
const initContainerName = `${containerName}-init`;
|
|
2298
2614
|
containers.push(initContainerName);
|
|
2299
2615
|
await runDocker2([
|
|
@@ -2377,7 +2693,7 @@ var buildAndPushWithLifecycleDocker = async (buildpack, params, span) => {
|
|
|
2377
2693
|
`Building and publishing ${params.imageRef} via Vercel ${buildpack.runtime} buildpacks`
|
|
2378
2694
|
);
|
|
2379
2695
|
await runDocker2(["start", "--attach", containerName]);
|
|
2380
|
-
const reportPath = (0,
|
|
2696
|
+
const reportPath = (0, import_node_path11.join)(stageDir, "report.toml");
|
|
2381
2697
|
await runDocker2([
|
|
2382
2698
|
"cp",
|
|
2383
2699
|
`${containerName}:/platform-output/report.toml`,
|
|
@@ -2431,15 +2747,15 @@ var buildAndPushWithLifecycleDocker = async (buildpack, params, span) => {
|
|
|
2431
2747
|
};
|
|
2432
2748
|
|
|
2433
2749
|
// src/buildpacks/node-toolchain.ts
|
|
2434
|
-
var
|
|
2750
|
+
var import_build_utils5 = require("@vercel/build-utils");
|
|
2435
2751
|
var import_node_fs8 = require("fs");
|
|
2436
|
-
var
|
|
2752
|
+
var import_node_path12 = require("path");
|
|
2437
2753
|
async function selectNodeMajor(workPath, config, meta) {
|
|
2438
|
-
if (!(0, import_node_fs8.existsSync)((0,
|
|
2754
|
+
if (!(0, import_node_fs8.existsSync)((0, import_node_path12.join)(workPath, "package.json"))) {
|
|
2439
2755
|
return void 0;
|
|
2440
2756
|
}
|
|
2441
|
-
const version2 = await (0,
|
|
2442
|
-
if ((0,
|
|
2757
|
+
const version2 = await (0, import_build_utils5.getNodeVersion)(workPath, void 0, config, meta);
|
|
2758
|
+
if ((0, import_build_utils5.isBunVersion)(version2)) {
|
|
2443
2759
|
throw new Error(
|
|
2444
2760
|
"Buildpack asset builds require Node.js. Bun is not supported yet; configure engines.node or use Dockerfile.vercel."
|
|
2445
2761
|
);
|
|
@@ -2456,7 +2772,7 @@ async function resolveNodeToolchain(workPath, config, meta) {
|
|
|
2456
2772
|
return void 0;
|
|
2457
2773
|
}
|
|
2458
2774
|
for (const executable of ["node", "npm"]) {
|
|
2459
|
-
if (!(0, import_node_fs8.existsSync)((0,
|
|
2775
|
+
if (!(0, import_node_fs8.existsSync)((0, import_node_path12.join)(toolchain.home, "bin", executable))) {
|
|
2460
2776
|
throw new Error(
|
|
2461
2777
|
`The selected Node.js ${toolchain.version} installation is missing ${toolchain.home}/bin/${executable} in the build environment.`
|
|
2462
2778
|
);
|
|
@@ -2470,11 +2786,11 @@ async function resolveLocalNodeToolchain(workPath, config, meta) {
|
|
|
2470
2786
|
|
|
2471
2787
|
// src/buildpacks/registry.ts
|
|
2472
2788
|
var import_node_fs12 = require("fs");
|
|
2473
|
-
var
|
|
2789
|
+
var import_node_path16 = require("path");
|
|
2474
2790
|
|
|
2475
2791
|
// src/buildpacks/go.ts
|
|
2476
2792
|
var import_node_fs9 = require("fs");
|
|
2477
|
-
var
|
|
2793
|
+
var import_node_path13 = require("path");
|
|
2478
2794
|
var DEFAULT_GO_VERSION = "1";
|
|
2479
2795
|
var GO_IMAGE_REPOSITORY = "docker.io/library/golang";
|
|
2480
2796
|
var GO_BUILD_IMAGE_DISTRO = "bookworm";
|
|
@@ -2503,7 +2819,7 @@ function compareImageVersions(a, b) {
|
|
|
2503
2819
|
return a.parts[0] - b.parts[0] || a.parts[1] - b.parts[1] || a.parts[2] - b.parts[2];
|
|
2504
2820
|
}
|
|
2505
2821
|
function selectGoVersion(workPath) {
|
|
2506
|
-
const goMod = (0,
|
|
2822
|
+
const goMod = (0, import_node_path13.join)(workPath, "go.mod");
|
|
2507
2823
|
if (!(0, import_node_fs9.existsSync)(goMod))
|
|
2508
2824
|
return DEFAULT_GO_VERSION;
|
|
2509
2825
|
const contents = (0, import_node_fs9.readFileSync)(goMod, "utf8");
|
|
@@ -2528,10 +2844,10 @@ var go = {
|
|
|
2528
2844
|
},
|
|
2529
2845
|
defaultEntrypoint({ workPath, zeroConfig }) {
|
|
2530
2846
|
const candidate = GO_CANDIDATE_ENTRYPOINTS.find(
|
|
2531
|
-
(name) => (0, import_node_fs9.existsSync)((0,
|
|
2847
|
+
(name) => (0, import_node_fs9.existsSync)((0, import_node_path13.join)(workPath, name))
|
|
2532
2848
|
);
|
|
2533
2849
|
if (candidate)
|
|
2534
|
-
return
|
|
2850
|
+
return import_node_path13.posix.dirname(candidate);
|
|
2535
2851
|
if (zeroConfig) {
|
|
2536
2852
|
const last = GO_CANDIDATE_ENTRYPOINTS.length - 1;
|
|
2537
2853
|
throw new Error(
|
|
@@ -2544,7 +2860,7 @@ var go = {
|
|
|
2544
2860
|
|
|
2545
2861
|
// src/buildpacks/ruby.ts
|
|
2546
2862
|
var import_node_fs10 = require("fs");
|
|
2547
|
-
var
|
|
2863
|
+
var import_node_path14 = require("path");
|
|
2548
2864
|
var import_smol_toml2 = require("smol-toml");
|
|
2549
2865
|
|
|
2550
2866
|
// src/buildpacks/version.ts
|
|
@@ -2576,7 +2892,7 @@ var ruby = {
|
|
|
2576
2892
|
return { version: version2, buildImage: image, runImage: image };
|
|
2577
2893
|
},
|
|
2578
2894
|
validate({ workPath, buildEnv }) {
|
|
2579
|
-
const gemfile = (0,
|
|
2895
|
+
const gemfile = (0, import_node_path14.join)(workPath, "Gemfile");
|
|
2580
2896
|
if (!(0, import_node_fs10.existsSync)(gemfile))
|
|
2581
2897
|
return;
|
|
2582
2898
|
if (!GEMFILE_RAILS_GEM_RE.test((0, import_node_fs10.readFileSync)(gemfile, "utf8")))
|
|
@@ -2590,12 +2906,12 @@ var ruby = {
|
|
|
2590
2906
|
};
|
|
2591
2907
|
function selectRubyVersion(workPath) {
|
|
2592
2908
|
for (const name of RUBY_VERSION_FILES) {
|
|
2593
|
-
const file = (0,
|
|
2909
|
+
const file = (0, import_node_path14.join)(workPath, name);
|
|
2594
2910
|
if ((0, import_node_fs10.existsSync)(file)) {
|
|
2595
2911
|
return parseVersionFile(name, (0, import_node_fs10.readFileSync)(file, "utf8"));
|
|
2596
2912
|
}
|
|
2597
2913
|
}
|
|
2598
|
-
const gemfile = (0,
|
|
2914
|
+
const gemfile = (0, import_node_path14.join)(workPath, "Gemfile");
|
|
2599
2915
|
return (0, import_node_fs10.existsSync)(gemfile) ? parseGemfileRubyVersion((0, import_node_fs10.readFileSync)(gemfile, "utf8")) : void 0;
|
|
2600
2916
|
}
|
|
2601
2917
|
function parseVersionFile(name, contents) {
|
|
@@ -2634,7 +2950,7 @@ function parseGemfileRubyVersion(contents) {
|
|
|
2634
2950
|
|
|
2635
2951
|
// src/buildpacks/rust.ts
|
|
2636
2952
|
var import_node_fs11 = require("fs");
|
|
2637
|
-
var
|
|
2953
|
+
var import_node_path15 = require("path");
|
|
2638
2954
|
var import_smol_toml3 = require("smol-toml");
|
|
2639
2955
|
var DEFAULT_RUST_VERSION = "1";
|
|
2640
2956
|
var RUST_IMAGE_REPOSITORY = "docker.io/library/rust";
|
|
@@ -2654,12 +2970,12 @@ var rust = {
|
|
|
2654
2970
|
};
|
|
2655
2971
|
},
|
|
2656
2972
|
defaultEntrypoint({ workPath, zeroConfig }) {
|
|
2657
|
-
return zeroConfig && (0, import_node_fs11.existsSync)((0,
|
|
2973
|
+
return zeroConfig && (0, import_node_fs11.existsSync)((0, import_node_path15.join)(workPath, "src/main.rs")) ? "src/main.rs" : void 0;
|
|
2658
2974
|
}
|
|
2659
2975
|
};
|
|
2660
2976
|
function selectRustVersion(workPath) {
|
|
2661
2977
|
for (const name of RUST_TOOLCHAIN_FILES) {
|
|
2662
|
-
const file = (0,
|
|
2978
|
+
const file = (0, import_node_path15.join)(workPath, name);
|
|
2663
2979
|
if ((0, import_node_fs11.existsSync)(file)) {
|
|
2664
2980
|
const channel = parseToolchainFile(name, (0, import_node_fs11.readFileSync)(file, "utf8"));
|
|
2665
2981
|
return channel !== void 0 && isValidVersion(channel) ? channel : void 0;
|
|
@@ -2705,7 +3021,7 @@ function requestedBuildpack(config) {
|
|
|
2705
3021
|
}
|
|
2706
3022
|
function hasProjectMarkers(buildpack, workPath) {
|
|
2707
3023
|
return buildpack.projectMarkers.some(
|
|
2708
|
-
(name) => (0, import_node_fs12.existsSync)((0,
|
|
3024
|
+
(name) => (0, import_node_fs12.existsSync)((0, import_node_path16.join)(workPath, name))
|
|
2709
3025
|
);
|
|
2710
3026
|
}
|
|
2711
3027
|
|
|
@@ -2719,11 +3035,12 @@ function selectEntrypoint(params, sourceDir) {
|
|
|
2719
3035
|
return fromEnv ?? params.buildpack.defaultEntrypoint?.({ workPath: sourceDir, zeroConfig });
|
|
2720
3036
|
}
|
|
2721
3037
|
async function buildAndPushBuildpack(params) {
|
|
2722
|
-
const sourceDir = (0, import_node_fs13.mkdtempSync)((0,
|
|
3038
|
+
const sourceDir = (0, import_node_fs13.mkdtempSync)((0, import_node_path17.join)((0, import_node_os10.tmpdir)(), "vercel-cnb-source-"));
|
|
2723
3039
|
try {
|
|
2724
3040
|
step("Staging buildpack source files");
|
|
2725
3041
|
await stageWorkspace(params, sourceDir);
|
|
2726
|
-
const
|
|
3042
|
+
const { buildahClient } = params;
|
|
3043
|
+
const engine = selectContainerEngine(buildahClient);
|
|
2727
3044
|
if (!hasProjectMarkers(params.buildpack, sourceDir)) {
|
|
2728
3045
|
throw new Error(
|
|
2729
3046
|
`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 +3157,7 @@ async function buildAndPushBuildpack(params) {
|
|
|
2840
3157
|
result = await buildAndPushWithLifecycle(
|
|
2841
3158
|
buildpack,
|
|
2842
3159
|
{ ...lifecycleParams, prepareRegistry },
|
|
3160
|
+
buildahClient,
|
|
2843
3161
|
buildSpan
|
|
2844
3162
|
);
|
|
2845
3163
|
} else {
|
|
@@ -2871,7 +3189,7 @@ async function buildAndPushBuildpack(params) {
|
|
|
2871
3189
|
|
|
2872
3190
|
// src/image-source.ts
|
|
2873
3191
|
var import_node_fs14 = require("fs");
|
|
2874
|
-
var
|
|
3192
|
+
var import_node_path18 = __toESM(require("path"));
|
|
2875
3193
|
var DETECT_SENTINEL = "<detect>";
|
|
2876
3194
|
function resolveImageSource(options, context) {
|
|
2877
3195
|
const { config, workPath, entrypoint } = options;
|
|
@@ -2888,7 +3206,7 @@ function resolveImageSource(options, context) {
|
|
|
2888
3206
|
}
|
|
2889
3207
|
const dockerfileConfigured = entrypointRef && isDockerfileRef(entrypointRef) ? entrypointRef : findDockerfile(workPath);
|
|
2890
3208
|
const dockerfileRel = dockerfileConfigured ?? "Dockerfile";
|
|
2891
|
-
const dockerfilePath =
|
|
3209
|
+
const dockerfilePath = import_node_path18.default.join(workPath, dockerfileRel);
|
|
2892
3210
|
const hasDockerfile = dockerfileConfigured !== void 0 || !buildpack && (0, import_node_fs14.existsSync)(dockerfilePath);
|
|
2893
3211
|
if (hasDockerfile) {
|
|
2894
3212
|
return { kind: "dockerfile", dockerfileRel, dockerfilePath };
|
|
@@ -2905,11 +3223,252 @@ function resolveImageSource(options, context) {
|
|
|
2905
3223
|
);
|
|
2906
3224
|
}
|
|
2907
3225
|
|
|
3226
|
+
// src/buildah-store-cache/cache.ts
|
|
3227
|
+
var import_build_utils6 = require("@vercel/build-utils");
|
|
3228
|
+
var import_node_crypto8 = require("crypto");
|
|
3229
|
+
var import_promises7 = require("fs/promises");
|
|
3230
|
+
var import_node_os11 = require("os");
|
|
3231
|
+
var import_node_path19 = require("path");
|
|
3232
|
+
var BUILDAH_STORE_CACHE_DIR = ".vercel/cache/buildah-store";
|
|
3233
|
+
var STORE_PATHS = {
|
|
3234
|
+
graphRoot: BUILDAH_GRAPH_ROOT,
|
|
3235
|
+
// Buildah's default when running as root, as it does in the build container.
|
|
3236
|
+
blobInfoCache: "/var/lib/containers/cache/blob-info-cache-v1.sqlite"
|
|
3237
|
+
};
|
|
3238
|
+
var BuildahStoreCache = class _BuildahStoreCache {
|
|
3239
|
+
constructor(repositoryRoot, client) {
|
|
3240
|
+
this.repositoryRoot = repositoryRoot;
|
|
3241
|
+
this.client = client;
|
|
3242
|
+
}
|
|
3243
|
+
static forProject(options, client) {
|
|
3244
|
+
return new _BuildahStoreCache(
|
|
3245
|
+
(0, import_node_path19.resolve)(options.repoRootPath || options.workPath),
|
|
3246
|
+
client
|
|
3247
|
+
);
|
|
3248
|
+
}
|
|
3249
|
+
/**
|
|
3250
|
+
* From `build()`, before a Buildah service opens the store or reads its build
|
|
3251
|
+
* context. A service that builds nothing leaves the snapshot for a later
|
|
3252
|
+
* deployment.
|
|
3253
|
+
*/
|
|
3254
|
+
async beforeBuild(options) {
|
|
3255
|
+
if (!isBuildContainer()) {
|
|
3256
|
+
return;
|
|
3257
|
+
}
|
|
3258
|
+
if (buildahStoreCacheEnabled()) {
|
|
3259
|
+
if (options.buildsWithBuildah) {
|
|
3260
|
+
await this.restore(options.span);
|
|
3261
|
+
}
|
|
3262
|
+
return;
|
|
3263
|
+
}
|
|
3264
|
+
if (buildahStoreCacheRequested()) {
|
|
3265
|
+
info("Buildah store cache disabled: VERCEL_DEPLOYMENT_ID is not set");
|
|
3266
|
+
}
|
|
3267
|
+
await this.removeSnapshots();
|
|
3268
|
+
}
|
|
3269
|
+
/** From `prepareCache`, which the platform runs once every service has built. */
|
|
3270
|
+
async afterBuilds() {
|
|
3271
|
+
if (!isBuildContainer()) {
|
|
3272
|
+
return {};
|
|
3273
|
+
}
|
|
3274
|
+
if (buildahStoreCacheEnabled()) {
|
|
3275
|
+
return this.save();
|
|
3276
|
+
}
|
|
3277
|
+
await this.removeSnapshots();
|
|
3278
|
+
return {};
|
|
3279
|
+
}
|
|
3280
|
+
async restore(span) {
|
|
3281
|
+
const start = Date.now();
|
|
3282
|
+
step("Restoring the Buildah store cache");
|
|
3283
|
+
let result = "miss";
|
|
3284
|
+
let reason;
|
|
3285
|
+
let bytes = 0;
|
|
3286
|
+
try {
|
|
3287
|
+
const archive = await this.archivePath({ create: false });
|
|
3288
|
+
const restored = await restoreStore(archive);
|
|
3289
|
+
result = restored.status;
|
|
3290
|
+
if (restored.status === "hit")
|
|
3291
|
+
bytes = restored.bytes;
|
|
3292
|
+
if (restored.status === "miss")
|
|
3293
|
+
reason = restored.reason;
|
|
3294
|
+
} catch (error) {
|
|
3295
|
+
result = "miss";
|
|
3296
|
+
reason = failureReason(error);
|
|
3297
|
+
}
|
|
3298
|
+
await this.removeSnapshots();
|
|
3299
|
+
const restoredBytes = result === "hit" ? bytes : 0;
|
|
3300
|
+
const detail = reason ?? (result === "hit" ? formatBytes(restoredBytes) : void 0);
|
|
3301
|
+
done(
|
|
3302
|
+
`Buildah store cache: ${result}${detail ? ` (${detail})` : ""} in ${elapsed(start)}`
|
|
3303
|
+
);
|
|
3304
|
+
span?.setAttributes({
|
|
3305
|
+
"container.buildah_store.restore": result,
|
|
3306
|
+
"container.buildah_store.restore_ms": String(Date.now() - start),
|
|
3307
|
+
"container.buildah_store.restore_bytes": String(restoredBytes),
|
|
3308
|
+
"container.buildah_store.miss_reason": reason
|
|
3309
|
+
});
|
|
3310
|
+
}
|
|
3311
|
+
async save() {
|
|
3312
|
+
const start = Date.now();
|
|
3313
|
+
step("Saving the Buildah store cache");
|
|
3314
|
+
try {
|
|
3315
|
+
const archive = await this.archivePath({ create: true });
|
|
3316
|
+
const saved = await saveStore(archive);
|
|
3317
|
+
done(await describeSave2(saved, archive, start));
|
|
3318
|
+
if (!await isRegularFile(archive)) {
|
|
3319
|
+
return {};
|
|
3320
|
+
}
|
|
3321
|
+
return await (0, import_build_utils6.glob)(
|
|
3322
|
+
`${BUILDAH_STORE_CACHE_DIR}/${(0, import_node_path19.basename)(archive)}`,
|
|
3323
|
+
this.repositoryRoot
|
|
3324
|
+
);
|
|
3325
|
+
} catch (error) {
|
|
3326
|
+
done(`Buildah store cache not saved (${failureReason(error)})`);
|
|
3327
|
+
return {};
|
|
3328
|
+
}
|
|
3329
|
+
}
|
|
3330
|
+
async removeSnapshots() {
|
|
3331
|
+
try {
|
|
3332
|
+
await (0, import_promises7.rm)(await this.snapshotDirectory({ create: false }), {
|
|
3333
|
+
recursive: true,
|
|
3334
|
+
force: true
|
|
3335
|
+
});
|
|
3336
|
+
} catch (error) {
|
|
3337
|
+
debug(
|
|
3338
|
+
`could not remove Buildah store snapshots: ${failureReason(error)}`
|
|
3339
|
+
);
|
|
3340
|
+
}
|
|
3341
|
+
}
|
|
3342
|
+
async archivePath(options) {
|
|
3343
|
+
const key = await snapshotKey(this.client);
|
|
3344
|
+
return (0, import_node_path19.join)(await this.snapshotDirectory(options), `store-${key}.tar.zst`);
|
|
3345
|
+
}
|
|
3346
|
+
async snapshotDirectory({
|
|
3347
|
+
create
|
|
3348
|
+
}) {
|
|
3349
|
+
const root = await (0, import_promises7.realpath)(this.repositoryRoot);
|
|
3350
|
+
let directory = root;
|
|
3351
|
+
for (const part of BUILDAH_STORE_CACHE_DIR.split("/")) {
|
|
3352
|
+
directory = (0, import_node_path19.join)(directory, part);
|
|
3353
|
+
if (create) {
|
|
3354
|
+
await (0, import_promises7.mkdir)(directory, { mode: 448 }).catch((error) => {
|
|
3355
|
+
if (error.code !== "EEXIST")
|
|
3356
|
+
throw error;
|
|
3357
|
+
});
|
|
3358
|
+
}
|
|
3359
|
+
const stats = await lstatIfPresent(directory);
|
|
3360
|
+
if (!stats) {
|
|
3361
|
+
return (0, import_node_path19.join)(root, BUILDAH_STORE_CACHE_DIR);
|
|
3362
|
+
}
|
|
3363
|
+
if (!stats.isDirectory()) {
|
|
3364
|
+
throw new Error(`${directory} is not a directory`);
|
|
3365
|
+
}
|
|
3366
|
+
}
|
|
3367
|
+
return directory;
|
|
3368
|
+
}
|
|
3369
|
+
};
|
|
3370
|
+
async function restoreStore(archive) {
|
|
3371
|
+
const storeLock = await requireStoreLock();
|
|
3372
|
+
const state = await createBuildahStoreStateDirectory();
|
|
3373
|
+
const gate = new FileLock((0, import_node_path19.join)(state, "restore.lock"));
|
|
3374
|
+
return runStoreCommand(
|
|
3375
|
+
{
|
|
3376
|
+
action: "restore",
|
|
3377
|
+
options: {
|
|
3378
|
+
...STORE_PATHS,
|
|
3379
|
+
archive,
|
|
3380
|
+
storeLock: storeLock.path,
|
|
3381
|
+
marker: (0, import_node_path19.join)(state, "restored")
|
|
3382
|
+
}
|
|
3383
|
+
},
|
|
3384
|
+
gate
|
|
3385
|
+
);
|
|
3386
|
+
}
|
|
3387
|
+
async function saveStore(archive) {
|
|
3388
|
+
const buildahArgs = await buildahStorageArgs();
|
|
3389
|
+
const storeLock = await requireStoreLock();
|
|
3390
|
+
return runStoreCommand(
|
|
3391
|
+
{
|
|
3392
|
+
action: "save",
|
|
3393
|
+
options: {
|
|
3394
|
+
...STORE_PATHS,
|
|
3395
|
+
archive,
|
|
3396
|
+
stateDirectory: await createBuildahStoreStateDirectory(),
|
|
3397
|
+
threads: (0, import_node_os11.availableParallelism)(),
|
|
3398
|
+
buildahArgs
|
|
3399
|
+
}
|
|
3400
|
+
},
|
|
3401
|
+
storeLock
|
|
3402
|
+
);
|
|
3403
|
+
}
|
|
3404
|
+
async function requireStoreLock() {
|
|
3405
|
+
const lock = await buildahStoreLock();
|
|
3406
|
+
if (!lock)
|
|
3407
|
+
throw new Error("flock unavailable");
|
|
3408
|
+
return lock;
|
|
3409
|
+
}
|
|
3410
|
+
async function describeSave2(result, archive, start) {
|
|
3411
|
+
if (result.status === "saved") {
|
|
3412
|
+
return `saved the Buildah store cache (${formatBytes((await (0, import_promises7.lstat)(archive)).size)}) in ${elapsed(start)}`;
|
|
3413
|
+
}
|
|
3414
|
+
if (result.status === "unused") {
|
|
3415
|
+
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";
|
|
3416
|
+
}
|
|
3417
|
+
if (result.status === "skipped" && result.previous === "failed") {
|
|
3418
|
+
throw new Error("an earlier save for this deployment failed");
|
|
3419
|
+
}
|
|
3420
|
+
return "Buildah store cache already prepared for this deployment";
|
|
3421
|
+
}
|
|
3422
|
+
async function snapshotKey(client) {
|
|
3423
|
+
const configuration = await Promise.all(
|
|
3424
|
+
[
|
|
3425
|
+
"/usr/share/containers/storage.conf",
|
|
3426
|
+
"/etc/containers/storage.conf",
|
|
3427
|
+
(0, import_node_path19.join)(
|
|
3428
|
+
process.env.XDG_CONFIG_HOME || (0, import_node_path19.join)((0, import_node_os11.homedir)(), ".config"),
|
|
3429
|
+
"containers/storage.conf"
|
|
3430
|
+
),
|
|
3431
|
+
process.env.CONTAINERS_STORAGE_CONF,
|
|
3432
|
+
"/etc/subuid",
|
|
3433
|
+
"/etc/subgid"
|
|
3434
|
+
].map(readOptionalFile)
|
|
3435
|
+
);
|
|
3436
|
+
return (0, import_node_crypto8.createHash)("sha256").update(
|
|
3437
|
+
JSON.stringify({
|
|
3438
|
+
version: await client.version(),
|
|
3439
|
+
arch: process.arch,
|
|
3440
|
+
uid: process.getuid?.(),
|
|
3441
|
+
gid: process.getgid?.(),
|
|
3442
|
+
driver: process.env.VERCEL_VCR_DOCKER_STORAGE_DRIVER,
|
|
3443
|
+
image: process.env.VERCEL_BUILD_IMAGE,
|
|
3444
|
+
configuration
|
|
3445
|
+
})
|
|
3446
|
+
).digest("hex").slice(0, 32);
|
|
3447
|
+
}
|
|
3448
|
+
async function readOptionalFile(path4) {
|
|
3449
|
+
if (!path4) {
|
|
3450
|
+
return "";
|
|
3451
|
+
}
|
|
3452
|
+
try {
|
|
3453
|
+
return await (0, import_promises7.readFile)(path4, "utf8");
|
|
3454
|
+
} catch (error) {
|
|
3455
|
+
if (error.code === "ENOENT")
|
|
3456
|
+
return "";
|
|
3457
|
+
throw error;
|
|
3458
|
+
}
|
|
3459
|
+
}
|
|
3460
|
+
async function isRegularFile(path4) {
|
|
3461
|
+
return (await lstatIfPresent(path4))?.isFile() ?? false;
|
|
3462
|
+
}
|
|
3463
|
+
function failureReason(error) {
|
|
3464
|
+
return String(error?.message ?? error).trim().split("\n").at(-1) ?? "";
|
|
3465
|
+
}
|
|
3466
|
+
|
|
2908
3467
|
// src/dev.ts
|
|
2909
3468
|
var import_node_child_process4 = require("child_process");
|
|
2910
3469
|
var import_node_fs15 = require("fs");
|
|
2911
|
-
var
|
|
2912
|
-
var
|
|
3470
|
+
var import_node_os12 = require("os");
|
|
3471
|
+
var import_node_path20 = __toESM(require("path"));
|
|
2913
3472
|
var HOST_ONLY_ENV = /* @__PURE__ */ new Set([
|
|
2914
3473
|
"TMPDIR",
|
|
2915
3474
|
"TMP",
|
|
@@ -2939,8 +3498,8 @@ function isHostOnlyEnvVar(key) {
|
|
|
2939
3498
|
return HOST_ONLY_ENV.has(key) || key.startsWith("__") || key.startsWith("XPC_") || key.startsWith("SSH_") || key.startsWith("Apple");
|
|
2940
3499
|
}
|
|
2941
3500
|
function writeEnvFile(env) {
|
|
2942
|
-
const dir = (0, import_node_fs15.mkdtempSync)(
|
|
2943
|
-
const file =
|
|
3501
|
+
const dir = (0, import_node_fs15.mkdtempSync)(import_node_path20.default.join((0, import_node_os12.tmpdir)(), "vercel-container-dev-env-"));
|
|
3502
|
+
const file = import_node_path20.default.join(dir, "env");
|
|
2944
3503
|
const lines = [];
|
|
2945
3504
|
for (const [key, value] of Object.entries(env)) {
|
|
2946
3505
|
if (value.includes("\n")) {
|
|
@@ -2973,7 +3532,7 @@ function emit(out, line) {
|
|
|
2973
3532
|
}
|
|
2974
3533
|
}
|
|
2975
3534
|
function runForwarded(cmd, args, out, opts = {}) {
|
|
2976
|
-
return new Promise((
|
|
3535
|
+
return new Promise((resolve5, reject) => {
|
|
2977
3536
|
const child = (0, import_node_child_process4.spawn)(cmd, args, { stdio: ["ignore", "pipe", "pipe"] });
|
|
2978
3537
|
let stdout = "";
|
|
2979
3538
|
let stderr = "";
|
|
@@ -3010,7 +3569,7 @@ function runForwarded(cmd, args, out, opts = {}) {
|
|
|
3010
3569
|
});
|
|
3011
3570
|
child.on("close", (code) => {
|
|
3012
3571
|
if (code === 0) {
|
|
3013
|
-
|
|
3572
|
+
resolve5({ stdout });
|
|
3014
3573
|
} else {
|
|
3015
3574
|
const detail = stderr.trim().split("\n").slice(-5).join("\n");
|
|
3016
3575
|
reject(
|
|
@@ -3046,7 +3605,7 @@ async function resolveDevImage(options, out, span) {
|
|
|
3046
3605
|
}
|
|
3047
3606
|
const serviceName = options.service?.name ?? "service";
|
|
3048
3607
|
const tag = devImageTag(serviceName);
|
|
3049
|
-
const contextDir =
|
|
3608
|
+
const contextDir = import_node_path20.default.dirname(dockerfilePath);
|
|
3050
3609
|
const buildArgFlags2 = [];
|
|
3051
3610
|
const buildEnv = options.meta?.buildEnv ?? {};
|
|
3052
3611
|
for (const [key, value] of Object.entries(buildEnv)) {
|
|
@@ -3251,7 +3810,7 @@ async function startContainer(options, reuseKey) {
|
|
|
3251
3810
|
}
|
|
3252
3811
|
});
|
|
3253
3812
|
const cleanupEnvFile = () => {
|
|
3254
|
-
(0, import_node_fs15.rmSync)(
|
|
3813
|
+
(0, import_node_fs15.rmSync)(import_node_path20.default.dirname(envFilePath), { recursive: true, force: true });
|
|
3255
3814
|
};
|
|
3256
3815
|
const shutdown = async () => {
|
|
3257
3816
|
runningContainers.delete(reuseKey);
|
|
@@ -3284,7 +3843,7 @@ async function startContainer(options, reuseKey) {
|
|
|
3284
3843
|
break;
|
|
3285
3844
|
} catch (err) {
|
|
3286
3845
|
lastErr = err;
|
|
3287
|
-
await new Promise((
|
|
3846
|
+
await new Promise((resolve5) => setTimeout(resolve5, 250));
|
|
3288
3847
|
}
|
|
3289
3848
|
}
|
|
3290
3849
|
if (hostPort === void 0) {
|
|
@@ -3327,19 +3886,27 @@ async function startContainer(options, reuseKey) {
|
|
|
3327
3886
|
}
|
|
3328
3887
|
|
|
3329
3888
|
// src/prepare-cache.ts
|
|
3330
|
-
var
|
|
3331
|
-
var
|
|
3889
|
+
var import_build_utils7 = require("@vercel/build-utils");
|
|
3890
|
+
var import_node_path21 = require("path");
|
|
3332
3891
|
async function prepareCache(options) {
|
|
3333
|
-
const root = (0,
|
|
3892
|
+
const root = (0, import_node_path21.resolve)(options.repoRootPath || options.workPath);
|
|
3893
|
+
const buildpackFiles = await collectBuildpackCache(root);
|
|
3894
|
+
const buildahStoreFiles = await BuildahStoreCache.forProject(
|
|
3895
|
+
options,
|
|
3896
|
+
new BuildahClient(sharedBuildahStoreLock)
|
|
3897
|
+
).afterBuilds();
|
|
3898
|
+
return { ...buildpackFiles, ...buildahStoreFiles };
|
|
3899
|
+
}
|
|
3900
|
+
async function collectBuildpackCache(root) {
|
|
3334
3901
|
const start = Date.now();
|
|
3335
|
-
const files = await (0,
|
|
3902
|
+
const files = await (0, import_build_utils7.glob)(`${BUILDPACK_CACHE_DIR}/**`, root);
|
|
3336
3903
|
const count = Object.keys(files).length;
|
|
3337
3904
|
if (count > 0) {
|
|
3338
3905
|
info(
|
|
3339
|
-
`cached
|
|
3906
|
+
`cached ${count} file${count === 1 ? "" : "s"} under ${BUILDPACK_CACHE_DIR} in ${Date.now() - start}ms`
|
|
3340
3907
|
);
|
|
3341
3908
|
} else {
|
|
3342
|
-
debug(
|
|
3909
|
+
debug(`no cache to persist under ${BUILDPACK_CACHE_DIR}`);
|
|
3343
3910
|
}
|
|
3344
3911
|
return files;
|
|
3345
3912
|
}
|
|
@@ -3363,9 +3930,9 @@ function resolveImageTag() {
|
|
|
3363
3930
|
if (sha) {
|
|
3364
3931
|
return sha.slice(0, 12);
|
|
3365
3932
|
}
|
|
3366
|
-
const
|
|
3367
|
-
if (
|
|
3368
|
-
return
|
|
3933
|
+
const deploymentId2 = readString(process.env.VERCEL_DEPLOYMENT_ID);
|
|
3934
|
+
if (deploymentId2) {
|
|
3935
|
+
return deploymentId2.replace(/[^a-z0-9-_.]/gi, "-");
|
|
3369
3936
|
}
|
|
3370
3937
|
return `build-${Date.now().toString(36)}`;
|
|
3371
3938
|
}
|
|
@@ -3397,8 +3964,16 @@ async function authenticateRegistry(engine, buildParams, span) {
|
|
|
3397
3964
|
done("authenticated");
|
|
3398
3965
|
}
|
|
3399
3966
|
async function buildAndPushImage(params) {
|
|
3400
|
-
const {
|
|
3401
|
-
|
|
3967
|
+
const {
|
|
3968
|
+
contextDir,
|
|
3969
|
+
dockerfilePath,
|
|
3970
|
+
repository,
|
|
3971
|
+
tag,
|
|
3972
|
+
buildArgs,
|
|
3973
|
+
buildahClient,
|
|
3974
|
+
parentSpan
|
|
3975
|
+
} = params;
|
|
3976
|
+
const engine = selectContainerEngine(buildahClient);
|
|
3402
3977
|
return withSpan(
|
|
3403
3978
|
parentSpan,
|
|
3404
3979
|
"container.build_and_push",
|
|
@@ -3448,6 +4023,7 @@ async function buildAndPushImage(params) {
|
|
|
3448
4023
|
token,
|
|
3449
4024
|
repository,
|
|
3450
4025
|
buildArgs,
|
|
4026
|
+
useBuildahStoreCache: buildahStoreCacheEnabled(),
|
|
3451
4027
|
span: buildSpan
|
|
3452
4028
|
};
|
|
3453
4029
|
await authenticateRegistry(engine, buildParams, buildSpan);
|
|
@@ -3470,7 +4046,13 @@ async function buildAndPushImage(params) {
|
|
|
3470
4046
|
buildSpan,
|
|
3471
4047
|
"container.image_build",
|
|
3472
4048
|
{ "image.ref": imageRef, "image.platform": TARGET_PLATFORM },
|
|
3473
|
-
() =>
|
|
4049
|
+
async () => {
|
|
4050
|
+
if (params.schedulesCommand) {
|
|
4051
|
+
await buildSchedulesCommandImage(engine, buildParams);
|
|
4052
|
+
} else {
|
|
4053
|
+
await engine.build(buildParams);
|
|
4054
|
+
}
|
|
4055
|
+
}
|
|
3474
4056
|
);
|
|
3475
4057
|
done(`built in ${elapsed(buildStart)}`);
|
|
3476
4058
|
const pushStart = Date.now();
|
|
@@ -3502,7 +4084,7 @@ async function buildAndPushImage(params) {
|
|
|
3502
4084
|
}
|
|
3503
4085
|
);
|
|
3504
4086
|
}
|
|
3505
|
-
async function resolveImageHandler(options, source, span) {
|
|
4087
|
+
async function resolveImageHandler(options, source, buildahClient, span, schedulesCommand) {
|
|
3506
4088
|
const { config, meta } = options;
|
|
3507
4089
|
const { daemon } = config;
|
|
3508
4090
|
span?.setAttributes({
|
|
@@ -3525,7 +4107,7 @@ async function resolveImageHandler(options, source, span) {
|
|
|
3525
4107
|
});
|
|
3526
4108
|
return tag3;
|
|
3527
4109
|
}
|
|
3528
|
-
const repository2 = daemon ? (0,
|
|
4110
|
+
const repository2 = daemon ? (0, import_build_utils8.getDaemonImageRepository)(daemon.name) : sanitizeRepository(options.service?.name ?? buildpack.runtime);
|
|
3529
4111
|
const tag2 = resolveImageTag();
|
|
3530
4112
|
span?.setAttributes({
|
|
3531
4113
|
"container.mode": "buildpack-build-and-push",
|
|
@@ -3545,6 +4127,7 @@ async function resolveImageHandler(options, source, span) {
|
|
|
3545
4127
|
config,
|
|
3546
4128
|
meta,
|
|
3547
4129
|
buildEnv: buildArgsFromEnv(meta?.buildEnv),
|
|
4130
|
+
buildahClient,
|
|
3548
4131
|
parentSpan: span
|
|
3549
4132
|
});
|
|
3550
4133
|
} catch (error) {
|
|
@@ -3560,7 +4143,7 @@ async function resolveImageHandler(options, source, span) {
|
|
|
3560
4143
|
if (meta?.isDev) {
|
|
3561
4144
|
const serviceName = options.service?.name;
|
|
3562
4145
|
const tag2 = devImageTag(
|
|
3563
|
-
serviceName ??
|
|
4146
|
+
serviceName ?? import_node_path22.default.basename(dockerfileRel).split(".")[0]
|
|
3564
4147
|
);
|
|
3565
4148
|
span?.setAttributes({ "container.mode": "dev", "image.tag": tag2 });
|
|
3566
4149
|
return tag2;
|
|
@@ -3570,15 +4153,17 @@ async function resolveImageHandler(options, source, span) {
|
|
|
3570
4153
|
`Dockerfile not found at "${dockerfilePath}" for container service.`
|
|
3571
4154
|
);
|
|
3572
4155
|
}
|
|
3573
|
-
const repository = daemon ? (0,
|
|
3574
|
-
options.service?.name ??
|
|
4156
|
+
const repository = daemon ? (0, import_build_utils8.getDaemonImageRepository)(daemon.name) : sanitizeRepository(
|
|
4157
|
+
options.service?.name ?? import_node_path22.default.basename(dockerfileRel).split(".")[0]
|
|
3575
4158
|
);
|
|
3576
4159
|
const tag = resolveImageTag();
|
|
3577
|
-
const contextDir =
|
|
4160
|
+
const contextDir = schedulesCommand ? import_node_path22.default.join(options.workPath, schedulesCommand.root) : import_node_path22.default.dirname(dockerfilePath);
|
|
4161
|
+
const contextIsRoot = Boolean(schedulesCommand) || import_node_path22.default.dirname(import_node_path22.default.normalize(daemon ? daemon.entrypoint : dockerfileRel)) === ".";
|
|
3578
4162
|
const buildArgs = buildArgsFromEnv(meta?.buildEnv);
|
|
3579
4163
|
span?.setAttributes({
|
|
3580
4164
|
"container.mode": "build_and_push",
|
|
3581
4165
|
"container.repository": repository,
|
|
4166
|
+
"container.context_is_root": toTag(contextIsRoot),
|
|
3582
4167
|
"image.tag": tag
|
|
3583
4168
|
});
|
|
3584
4169
|
return buildAndPushImage({
|
|
@@ -3587,7 +4172,9 @@ async function resolveImageHandler(options, source, span) {
|
|
|
3587
4172
|
repository,
|
|
3588
4173
|
tag,
|
|
3589
4174
|
buildArgs,
|
|
3590
|
-
|
|
4175
|
+
buildahClient,
|
|
4176
|
+
parentSpan: span,
|
|
4177
|
+
schedulesCommand
|
|
3591
4178
|
});
|
|
3592
4179
|
}
|
|
3593
4180
|
function buildArgsFromEnv(env) {
|
|
@@ -3604,12 +4191,26 @@ function buildArgsFromEnv(env) {
|
|
|
3604
4191
|
}
|
|
3605
4192
|
async function build(options) {
|
|
3606
4193
|
const source = resolveImageSource(options, "build");
|
|
4194
|
+
const schedulesCommand = options.config.schedulesCommand;
|
|
3607
4195
|
const { daemon } = options.config;
|
|
4196
|
+
const buildahClient = new BuildahClient(sharedBuildahStoreLock);
|
|
4197
|
+
if (!options.meta?.isDev && selectContainerEngine(buildahClient).name === "buildah") {
|
|
4198
|
+
await BuildahStoreCache.forProject(options, buildahClient).beforeBuild({
|
|
4199
|
+
buildsWithBuildah: source.kind !== "prebuilt",
|
|
4200
|
+
span: options.span
|
|
4201
|
+
});
|
|
4202
|
+
}
|
|
3608
4203
|
const image = await withSpan(
|
|
3609
4204
|
options.span,
|
|
3610
4205
|
"container.resolve_image",
|
|
3611
4206
|
{ "service.name": options.service?.name },
|
|
3612
|
-
(span) => resolveImageHandler(
|
|
4207
|
+
(span) => resolveImageHandler(
|
|
4208
|
+
options,
|
|
4209
|
+
source,
|
|
4210
|
+
buildahClient,
|
|
4211
|
+
span,
|
|
4212
|
+
schedulesCommand
|
|
4213
|
+
)
|
|
3613
4214
|
);
|
|
3614
4215
|
if (daemon) {
|
|
3615
4216
|
return {
|
|
@@ -3640,15 +4241,16 @@ async function build(options) {
|
|
|
3640
4241
|
}
|
|
3641
4242
|
};
|
|
3642
4243
|
}
|
|
3643
|
-
const lambdaOptions = await (0,
|
|
4244
|
+
const lambdaOptions = await (0, import_build_utils8.getLambdaOptionsFromFunction)({
|
|
3644
4245
|
sourceFile: resolveFunctionSourceFile(options),
|
|
3645
|
-
config: options.config
|
|
4246
|
+
config: options.config,
|
|
4247
|
+
isScheduleEntrypoint: schedulesCommand !== void 0
|
|
3646
4248
|
});
|
|
3647
|
-
const command = source.kind === "buildpack" ? void 0 : normalizeCommand(options.config.command);
|
|
4249
|
+
const command = source.kind === "buildpack" || schedulesCommand ? void 0 : normalizeCommand(options.config.command);
|
|
3648
4250
|
await generateProjectManifest({
|
|
3649
4251
|
workPath: options.workPath,
|
|
3650
4252
|
framework: options.diagnosticsValues?.framework,
|
|
3651
|
-
serviceType: options.service ? (0,
|
|
4253
|
+
serviceType: options.service ? (0, import_build_utils8.getReportedServiceType)(options.service) : void 0
|
|
3652
4254
|
});
|
|
3653
4255
|
const routes = [
|
|
3654
4256
|
{ handle: "filesystem" },
|
|
@@ -3668,7 +4270,7 @@ async function build(options) {
|
|
|
3668
4270
|
}
|
|
3669
4271
|
];
|
|
3670
4272
|
return {
|
|
3671
|
-
routes,
|
|
4273
|
+
routes: schedulesCommand ? [] : routes,
|
|
3672
4274
|
output: {
|
|
3673
4275
|
[CONTAINER_OUTPUT_PATH]: {
|
|
3674
4276
|
type: "Lambda",
|
|
@@ -3678,7 +4280,9 @@ async function build(options) {
|
|
|
3678
4280
|
// (vercel/api#76729).
|
|
3679
4281
|
handler: image,
|
|
3680
4282
|
runtime: "container",
|
|
3681
|
-
environment: {
|
|
4283
|
+
environment: schedulesCommand ? {
|
|
4284
|
+
[SCHEDULES_COMMAND_ENV]: JSON.stringify(schedulesCommand.command)
|
|
4285
|
+
} : {},
|
|
3682
4286
|
...command ? { command } : {},
|
|
3683
4287
|
...lambdaOptions
|
|
3684
4288
|
}
|