@vercel/python 6.40.0 → 6.42.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +298 -238
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -3213,6 +3213,7 @@ var src_exports = {};
|
|
|
3213
3213
|
__export(src_exports, {
|
|
3214
3214
|
build: () => build,
|
|
3215
3215
|
defaultShouldServe: () => defaultShouldServe,
|
|
3216
|
+
detectEntrypoint: () => detectEntrypoint,
|
|
3216
3217
|
diagnostics: () => diagnostics,
|
|
3217
3218
|
downloadFilesInWorkPath: () => downloadFilesInWorkPath,
|
|
3218
3219
|
installRequirement: () => installRequirement,
|
|
@@ -3231,7 +3232,7 @@ var import_path13 = require("path");
|
|
|
3231
3232
|
|
|
3232
3233
|
// src/package-versions.ts
|
|
3233
3234
|
var VERCEL_RUNTIME_VERSION = "0.13.2";
|
|
3234
|
-
var VERCEL_WORKERS_VERSION = "0.0.
|
|
3235
|
+
var VERCEL_WORKERS_VERSION = "0.0.23";
|
|
3235
3236
|
|
|
3236
3237
|
// src/index.ts
|
|
3237
3238
|
var import_build_utils16 = require("@vercel/build-utils");
|
|
@@ -5215,166 +5216,17 @@ async function generateProjectManifest({
|
|
|
5215
5216
|
var diagnostics = (0, import_build_utils6.createDiagnostics)("python");
|
|
5216
5217
|
|
|
5217
5218
|
// src/crons.ts
|
|
5218
|
-
var
|
|
5219
|
-
var
|
|
5219
|
+
var import_fs8 = __toESM(require("fs"));
|
|
5220
|
+
var import_path8 = require("path");
|
|
5220
5221
|
var import_execa4 = __toESM(require_execa());
|
|
5221
|
-
var
|
|
5222
|
-
var DYNAMIC_SCHEDULE = "<dynamic>";
|
|
5223
|
-
function entrypointToModule(entrypoint) {
|
|
5224
|
-
return entrypoint.replace(/\\/g, "/").replace(/\.py$/i, "").replace(/\//g, ".");
|
|
5225
|
-
}
|
|
5226
|
-
var scriptPath = (0, import_path7.join)(__dirname, "..", "templates", "vc_cron_detect.py");
|
|
5227
|
-
var script = import_fs7.default.readFileSync(scriptPath, "utf-8");
|
|
5228
|
-
function buildCronRouteTable(crons) {
|
|
5229
|
-
const table = {};
|
|
5230
|
-
for (const cron of crons) {
|
|
5231
|
-
table[cron.path] = cron.resolvedHandler;
|
|
5232
|
-
}
|
|
5233
|
-
return table;
|
|
5234
|
-
}
|
|
5235
|
-
async function getServiceCrons(opts) {
|
|
5236
|
-
const { service, entrypoint, rawEntrypoint, handlerFunction } = opts;
|
|
5237
|
-
const isScheduledService = !!service && (0, import_build_utils7.isScheduleTriggeredService)(service);
|
|
5238
|
-
if (!isScheduledService || !service.name || typeof service.schedule !== "string" && !Array.isArray(service.schedule)) {
|
|
5239
|
-
return void 0;
|
|
5240
|
-
}
|
|
5241
|
-
const cronEntrypoint = entrypoint || rawEntrypoint;
|
|
5242
|
-
if (!cronEntrypoint) {
|
|
5243
|
-
throw new import_build_utils7.NowBuildError({
|
|
5244
|
-
code: "PYTHON_CRON_NO_ENTRYPOINT",
|
|
5245
|
-
message: "Cron service is missing an entrypoint."
|
|
5246
|
-
});
|
|
5247
|
-
}
|
|
5248
|
-
if (service.schedule === DYNAMIC_SCHEDULE) {
|
|
5249
|
-
return getServiceCronsDynamic({
|
|
5250
|
-
serviceName: service.name,
|
|
5251
|
-
cronEntrypoint,
|
|
5252
|
-
handlerFunction,
|
|
5253
|
-
pythonBin: opts.pythonBin,
|
|
5254
|
-
env: opts.env,
|
|
5255
|
-
workPath: opts.workPath
|
|
5256
|
-
});
|
|
5257
|
-
}
|
|
5258
|
-
const cronPath = (0, import_build_utils7.getInternalServiceCronPath)(
|
|
5259
|
-
service.name,
|
|
5260
|
-
cronEntrypoint,
|
|
5261
|
-
handlerFunction || "cron"
|
|
5262
|
-
);
|
|
5263
|
-
const moduleName = entrypointToModule(cronEntrypoint);
|
|
5264
|
-
const resolvedHandler = handlerFunction ? `${moduleName}:${handlerFunction}` : moduleName;
|
|
5265
|
-
const schedules = Array.isArray(service.schedule) ? service.schedule : [service.schedule];
|
|
5266
|
-
return schedules.map((schedule) => ({
|
|
5267
|
-
path: cronPath,
|
|
5268
|
-
schedule,
|
|
5269
|
-
resolvedHandler
|
|
5270
|
-
}));
|
|
5271
|
-
}
|
|
5272
|
-
async function getServiceCronsDynamic(opts) {
|
|
5273
|
-
const {
|
|
5274
|
-
serviceName,
|
|
5275
|
-
cronEntrypoint,
|
|
5276
|
-
handlerFunction,
|
|
5277
|
-
pythonBin,
|
|
5278
|
-
env,
|
|
5279
|
-
workPath
|
|
5280
|
-
} = opts;
|
|
5281
|
-
if (!handlerFunction) {
|
|
5282
|
-
throw new import_build_utils7.NowBuildError({
|
|
5283
|
-
code: "PYTHON_DYNAMIC_CRON_NO_HANDLER",
|
|
5284
|
-
message: 'Dynamic cron detection requires a "module:object" entrypoint where the object has a get_crons() method.'
|
|
5285
|
-
});
|
|
5286
|
-
}
|
|
5287
|
-
const moduleName = entrypointToModule(cronEntrypoint);
|
|
5288
|
-
const entries = await detectDynamicCrons({
|
|
5289
|
-
pythonBin,
|
|
5290
|
-
env,
|
|
5291
|
-
workPath,
|
|
5292
|
-
moduleName,
|
|
5293
|
-
attrName: handlerFunction
|
|
5294
|
-
});
|
|
5295
|
-
console.log(
|
|
5296
|
-
`Detected ${entries.length} cron entry(s): ${entries.map((e) => `${e.module_function} (${e.schedule})`).join(", ")}`
|
|
5297
|
-
);
|
|
5298
|
-
if (entries.length === 0) {
|
|
5299
|
-
throw new import_build_utils7.NowBuildError({
|
|
5300
|
-
code: "PYTHON_DYNAMIC_CRON_EMPTY",
|
|
5301
|
-
message: `Dynamic cron detection returned no entries. "${moduleName}.${handlerFunction}.get_crons()" returned an empty iterable.`
|
|
5302
|
-
});
|
|
5303
|
-
}
|
|
5304
|
-
return entries.map((entry) => {
|
|
5305
|
-
const cronPath = moduleColonFuncToCronPath(
|
|
5306
|
-
serviceName,
|
|
5307
|
-
entry.module_function
|
|
5308
|
-
);
|
|
5309
|
-
return {
|
|
5310
|
-
path: cronPath,
|
|
5311
|
-
schedule: entry.schedule,
|
|
5312
|
-
resolvedHandler: entry.module_function
|
|
5313
|
-
};
|
|
5314
|
-
});
|
|
5315
|
-
}
|
|
5316
|
-
async function detectDynamicCrons(opts) {
|
|
5317
|
-
const { pythonBin, env, workPath, moduleName, attrName } = opts;
|
|
5318
|
-
let stdout;
|
|
5319
|
-
try {
|
|
5320
|
-
const result = await (0, import_execa4.default)(
|
|
5321
|
-
pythonBin,
|
|
5322
|
-
["-c", script, moduleName, attrName],
|
|
5323
|
-
{ env, cwd: workPath }
|
|
5324
|
-
);
|
|
5325
|
-
stdout = result.stdout;
|
|
5326
|
-
} catch (err) {
|
|
5327
|
-
let detail = err?.stderr || err?.message || String(err);
|
|
5328
|
-
try {
|
|
5329
|
-
const parsed2 = JSON.parse(err?.stdout);
|
|
5330
|
-
if (parsed2.error)
|
|
5331
|
-
detail = parsed2.error;
|
|
5332
|
-
} catch {
|
|
5333
|
-
}
|
|
5334
|
-
throw new import_build_utils7.NowBuildError({
|
|
5335
|
-
code: "PYTHON_DYNAMIC_CRON_DETECTION_FAILED",
|
|
5336
|
-
message: `Failed to detect dynamic cron entries: ${detail}`
|
|
5337
|
-
});
|
|
5338
|
-
}
|
|
5339
|
-
let parsed;
|
|
5340
|
-
try {
|
|
5341
|
-
parsed = JSON.parse(stdout);
|
|
5342
|
-
} catch {
|
|
5343
|
-
throw new import_build_utils7.NowBuildError({
|
|
5344
|
-
code: "PYTHON_DYNAMIC_CRON_DETECTION_FAILED",
|
|
5345
|
-
message: `Dynamic cron detection returned invalid JSON: ${stdout}`
|
|
5346
|
-
});
|
|
5347
|
-
}
|
|
5348
|
-
return parsed.entries || [];
|
|
5349
|
-
}
|
|
5350
|
-
function moduleColonFuncToCronPath(serviceName, moduleFunction) {
|
|
5351
|
-
const colonIdx = moduleFunction.indexOf(":");
|
|
5352
|
-
if (colonIdx === -1) {
|
|
5353
|
-
throw new import_build_utils7.NowBuildError({
|
|
5354
|
-
code: "PYTHON_DYNAMIC_CRON_INVALID_FORMAT",
|
|
5355
|
-
message: `Dynamic cron entry must use "module:function" format, got: "${moduleFunction}"`
|
|
5356
|
-
});
|
|
5357
|
-
}
|
|
5358
|
-
const modulePart = moduleFunction.slice(0, colonIdx);
|
|
5359
|
-
const funcPart = moduleFunction.slice(colonIdx + 1);
|
|
5360
|
-
const entrypointPath = modulePart.replace(/\./g, "/");
|
|
5361
|
-
return (0, import_build_utils7.getInternalServiceCronPath)(serviceName, entrypointPath, funcPart);
|
|
5362
|
-
}
|
|
5363
|
-
|
|
5364
|
-
// src/start-dev-server.ts
|
|
5365
|
-
var import_child_process2 = require("child_process");
|
|
5366
|
-
var import_fs9 = require("fs");
|
|
5367
|
-
var import_path9 = require("path");
|
|
5368
|
-
var import_build_utils11 = require("@vercel/build-utils");
|
|
5369
|
-
var import_get_port = __toESM(require_get_port());
|
|
5370
|
-
var import_is_port_reachable = __toESM(require_is_port_reachable());
|
|
5222
|
+
var import_build_utils10 = require("@vercel/build-utils");
|
|
5371
5223
|
|
|
5372
5224
|
// src/entrypoint.ts
|
|
5373
|
-
var
|
|
5374
|
-
var
|
|
5225
|
+
var import_fs7 = __toESM(require("fs"));
|
|
5226
|
+
var import_path7 = require("path");
|
|
5227
|
+
var import_build_utils7 = require("@vercel/build-utils");
|
|
5375
5228
|
var import_build_utils8 = require("@vercel/build-utils");
|
|
5376
5229
|
var import_build_utils9 = require("@vercel/build-utils");
|
|
5377
|
-
var import_build_utils10 = require("@vercel/build-utils");
|
|
5378
5230
|
var import_python_analysis5 = require("@vercel/python-analysis");
|
|
5379
5231
|
var PYTHON_ENTRYPOINT_FILENAMES = [
|
|
5380
5232
|
"app",
|
|
@@ -5389,16 +5241,17 @@ var PYTHON_CANDIDATE_ENTRYPOINTS = getCandidateEntrypointsInDirs(
|
|
|
5389
5241
|
PYTHON_ENTRYPOINT_DIRS
|
|
5390
5242
|
);
|
|
5391
5243
|
var PYTHON_ENTRYPOINT_DOCS_URL = "https://vercel.com/docs/functions/runtimes/python#python-entrypoints";
|
|
5244
|
+
var SKIP_DIRS = /* @__PURE__ */ new Set(["__pycache__", "node_modules"]);
|
|
5392
5245
|
function getCandidateEntrypointsInDirs(dirs) {
|
|
5393
5246
|
return dirs.flatMap(
|
|
5394
5247
|
(dir) => PYTHON_ENTRYPOINT_FILENAMES.map(
|
|
5395
|
-
(filename) =>
|
|
5248
|
+
(filename) => import_path7.posix.join(dir, `${filename}.py`)
|
|
5396
5249
|
)
|
|
5397
5250
|
);
|
|
5398
5251
|
}
|
|
5399
5252
|
async function fileExists(filePath) {
|
|
5400
5253
|
try {
|
|
5401
|
-
const stat = await
|
|
5254
|
+
const stat = await import_fs7.default.promises.stat(filePath);
|
|
5402
5255
|
return stat.isFile();
|
|
5403
5256
|
} catch {
|
|
5404
5257
|
return false;
|
|
@@ -5449,34 +5302,67 @@ function getMissingExportMessage(framework, entrypoints) {
|
|
|
5449
5302
|
return entrypoints.length === 1 ? `Found ${fileList} but it does not export a top-level "app", "application", or "handler" variable.` : `Found ${fileList} but none export a top-level "app", "application", or "handler" variable.`;
|
|
5450
5303
|
}
|
|
5451
5304
|
}
|
|
5305
|
+
function entrypointToModule(entrypoint) {
|
|
5306
|
+
return entrypoint.replace(/\\/g, "/").replace(/\.py$/i, "").replace(/\//g, ".");
|
|
5307
|
+
}
|
|
5452
5308
|
async function checkEntrypoint(workPath, relPath) {
|
|
5453
|
-
const absPath = (0,
|
|
5309
|
+
const absPath = (0, import_path7.join)(workPath, relPath);
|
|
5454
5310
|
if (!await fileExists(absPath))
|
|
5455
5311
|
return null;
|
|
5456
|
-
const content = await
|
|
5312
|
+
const content = await import_fs7.default.promises.readFile(absPath, "utf-8");
|
|
5457
5313
|
return (0, import_python_analysis5.findAppOrHandler)(content);
|
|
5458
5314
|
}
|
|
5459
|
-
async function
|
|
5315
|
+
async function findPythonFilesRecursive(dir, rootDir) {
|
|
5460
5316
|
const results = [];
|
|
5461
|
-
|
|
5462
|
-
|
|
5317
|
+
let entries;
|
|
5318
|
+
try {
|
|
5319
|
+
entries = await import_fs7.default.promises.readdir(dir, { withFileTypes: true });
|
|
5320
|
+
} catch {
|
|
5321
|
+
return results;
|
|
5322
|
+
}
|
|
5323
|
+
for (const entry of entries) {
|
|
5324
|
+
if (entry.name.startsWith(".") || SKIP_DIRS.has(entry.name))
|
|
5325
|
+
continue;
|
|
5326
|
+
const fullPath = (0, import_path7.join)(dir, entry.name);
|
|
5327
|
+
if (entry.isDirectory()) {
|
|
5328
|
+
results.push(...await findPythonFilesRecursive(fullPath, rootDir));
|
|
5329
|
+
} else if (entry.isFile() && entry.name.endsWith(".py")) {
|
|
5330
|
+
results.push((0, import_path7.relative)(rootDir, fullPath).replace(/\\/g, "/"));
|
|
5331
|
+
}
|
|
5332
|
+
}
|
|
5333
|
+
return results;
|
|
5334
|
+
}
|
|
5335
|
+
async function findEntrypointCandidates(workPath) {
|
|
5336
|
+
const pyFiles = await findPythonFilesRecursive(workPath, workPath);
|
|
5337
|
+
const candidates = [];
|
|
5338
|
+
for (const relPath of pyFiles) {
|
|
5463
5339
|
try {
|
|
5464
|
-
const
|
|
5465
|
-
|
|
5466
|
-
|
|
5467
|
-
|
|
5468
|
-
|
|
5469
|
-
|
|
5470
|
-
|
|
5471
|
-
const stat = await import_fs8.default.promises.stat((0, import_path8.join)(dirPath, entry));
|
|
5472
|
-
if (stat.isFile()) {
|
|
5473
|
-
results.push(relPath);
|
|
5474
|
-
}
|
|
5340
|
+
const content = await import_fs7.default.promises.readFile(
|
|
5341
|
+
(0, import_path7.join)(workPath, relPath),
|
|
5342
|
+
"utf-8"
|
|
5343
|
+
);
|
|
5344
|
+
const varName = await (0, import_python_analysis5.findAppOrHandler)(content);
|
|
5345
|
+
if (varName) {
|
|
5346
|
+
candidates.push({ entrypoint: relPath, variableName: varName });
|
|
5475
5347
|
}
|
|
5476
5348
|
} catch {
|
|
5477
5349
|
}
|
|
5478
5350
|
}
|
|
5479
|
-
return
|
|
5351
|
+
return candidates;
|
|
5352
|
+
}
|
|
5353
|
+
function renderCandidateSuggestion(candidates) {
|
|
5354
|
+
const lines = candidates.map(
|
|
5355
|
+
(c) => ` ${c.entrypoint} (variable: ${c.variableName})`
|
|
5356
|
+
);
|
|
5357
|
+
const suggested = candidates[0];
|
|
5358
|
+
const moduleAttr = `${entrypointToModule(suggested.entrypoint)}:${suggested.variableName}`;
|
|
5359
|
+
return `but found potential entrypoints:
|
|
5360
|
+
${lines.join("\n")}
|
|
5361
|
+
|
|
5362
|
+
Add this to your pyproject.toml:
|
|
5363
|
+
|
|
5364
|
+
[tool.vercel]
|
|
5365
|
+
entrypoint = "${moduleAttr}"`;
|
|
5480
5366
|
}
|
|
5481
5367
|
async function resolveModuleAttrEntrypoint(workPath, value) {
|
|
5482
5368
|
const match = value.match(/([A-Za-z_][\w.]*)\s*:\s*([A-Za-z_][\w]*)/);
|
|
@@ -5487,14 +5373,14 @@ async function resolveModuleAttrEntrypoint(workPath, value) {
|
|
|
5487
5373
|
const relPath = modulePath.replace(/\./g, "/");
|
|
5488
5374
|
const candidates = [`${relPath}.py`, `${relPath}/__init__.py`];
|
|
5489
5375
|
for (const candidate of candidates) {
|
|
5490
|
-
if (await fileExists((0,
|
|
5376
|
+
if (await fileExists((0, import_path7.join)(workPath, candidate))) {
|
|
5491
5377
|
return { entrypoint: candidate, variableName };
|
|
5492
5378
|
}
|
|
5493
5379
|
}
|
|
5494
5380
|
return null;
|
|
5495
5381
|
}
|
|
5496
5382
|
async function getVercelToolsEntrypoint(workPath) {
|
|
5497
|
-
const pyprojectData = await (0,
|
|
5383
|
+
const pyprojectData = await (0, import_build_utils9.readConfigFile)((0, import_path7.join)(workPath, "pyproject.toml"));
|
|
5498
5384
|
if (!pyprojectData)
|
|
5499
5385
|
return null;
|
|
5500
5386
|
const vercelEntrypoint = pyprojectData.tool?.vercel?.entrypoint;
|
|
@@ -5503,7 +5389,7 @@ async function getVercelToolsEntrypoint(workPath) {
|
|
|
5503
5389
|
return resolveModuleAttrEntrypoint(workPath, vercelEntrypoint);
|
|
5504
5390
|
}
|
|
5505
5391
|
async function getPyprojectEntrypointWithDiagnostics(workPath) {
|
|
5506
|
-
const pyprojectData = await (0,
|
|
5392
|
+
const pyprojectData = await (0, import_build_utils9.readConfigFile)((0, import_path7.join)(workPath, "pyproject.toml"));
|
|
5507
5393
|
if (!pyprojectData)
|
|
5508
5394
|
return { entrypoint: null };
|
|
5509
5395
|
const scripts = pyprojectData.project?.scripts;
|
|
@@ -5518,7 +5404,7 @@ async function getPyprojectEntrypointWithDiagnostics(workPath) {
|
|
|
5518
5404
|
const relPath = modulePath.replace(/\./g, "/");
|
|
5519
5405
|
const candidates = [`${relPath}.py`, `${relPath}/__init__.py`];
|
|
5520
5406
|
for (const candidate of candidates) {
|
|
5521
|
-
if (await fileExists((0,
|
|
5407
|
+
if (await fileExists((0, import_path7.join)(workPath, candidate))) {
|
|
5522
5408
|
return { entrypoint: { entrypoint: candidate, variableName } };
|
|
5523
5409
|
}
|
|
5524
5410
|
}
|
|
@@ -5527,13 +5413,13 @@ async function getPyprojectEntrypointWithDiagnostics(workPath) {
|
|
|
5527
5413
|
async function findValidEntrypoint(workPath, candidates) {
|
|
5528
5414
|
const existingWithoutEntrypoint = [];
|
|
5529
5415
|
for (const candidate of candidates) {
|
|
5530
|
-
const absPath = (0,
|
|
5416
|
+
const absPath = (0, import_path7.join)(workPath, candidate);
|
|
5531
5417
|
if (!await fileExists(absPath))
|
|
5532
5418
|
continue;
|
|
5533
|
-
const content = await
|
|
5419
|
+
const content = await import_fs7.default.promises.readFile(absPath, "utf-8");
|
|
5534
5420
|
const varName = await (0, import_python_analysis5.findAppOrHandler)(content);
|
|
5535
5421
|
if (varName) {
|
|
5536
|
-
(0,
|
|
5422
|
+
(0, import_build_utils8.debug)(`Detected Python entrypoint: ${candidate} (variable: ${varName})`);
|
|
5537
5423
|
return {
|
|
5538
5424
|
entrypoint: { entrypoint: candidate, variableName: varName },
|
|
5539
5425
|
existingWithoutEntrypoint
|
|
@@ -5544,12 +5430,12 @@ async function findValidEntrypoint(workPath, candidates) {
|
|
|
5544
5430
|
return { entrypoint: null, existingWithoutEntrypoint };
|
|
5545
5431
|
}
|
|
5546
5432
|
async function checkDjangoManage(workPath) {
|
|
5547
|
-
const managePath = (0,
|
|
5433
|
+
const managePath = (0, import_path7.join)(workPath, "manage.py");
|
|
5548
5434
|
try {
|
|
5549
|
-
const content = await
|
|
5435
|
+
const content = await import_fs7.default.promises.readFile(managePath, "utf-8");
|
|
5550
5436
|
if (!content.includes("DJANGO_SETTINGS_MODULE"))
|
|
5551
5437
|
return false;
|
|
5552
|
-
(0,
|
|
5438
|
+
(0, import_build_utils8.debug)(`Found Django manage.py with DJANGO_SETTINGS_MODULE at ${workPath}`);
|
|
5553
5439
|
return true;
|
|
5554
5440
|
} catch {
|
|
5555
5441
|
return false;
|
|
@@ -5557,7 +5443,7 @@ async function checkDjangoManage(workPath) {
|
|
|
5557
5443
|
}
|
|
5558
5444
|
async function getSubdirectories(workPath) {
|
|
5559
5445
|
try {
|
|
5560
|
-
const entries = await
|
|
5446
|
+
const entries = await import_fs7.default.promises.readdir(workPath, {
|
|
5561
5447
|
withFileTypes: true
|
|
5562
5448
|
});
|
|
5563
5449
|
return entries.filter((e) => e.isDirectory() && !e.name.startsWith(".")).map((e) => e.name).sort();
|
|
@@ -5570,23 +5456,39 @@ function makeDiagnosticError(framework, diagnostics2) {
|
|
|
5570
5456
|
const link = getFrameworkEntrypointDocsUrl(framework);
|
|
5571
5457
|
const action = "Learn More";
|
|
5572
5458
|
const displayName = getFrameworkDisplayName(framework);
|
|
5459
|
+
const elsewhere = diagnostics2.validCandidatesElsewhere ?? [];
|
|
5460
|
+
const suggestion = elsewhere.length > 0 ? renderCandidateSuggestion(elsewhere) : null;
|
|
5573
5461
|
let message;
|
|
5574
5462
|
if (diagnostics2.existingWithoutEntrypoint.length > 0) {
|
|
5575
5463
|
message = getMissingExportMessage(
|
|
5576
5464
|
framework,
|
|
5577
5465
|
diagnostics2.existingWithoutEntrypoint
|
|
5578
5466
|
);
|
|
5467
|
+
if (suggestion)
|
|
5468
|
+
message += `
|
|
5469
|
+
|
|
5470
|
+
${suggestion}`;
|
|
5471
|
+
} else if (diagnostics2.djangoManageBaseDir !== void 0) {
|
|
5472
|
+
const where = diagnostics2.djangoManageBaseDir || ".";
|
|
5473
|
+
message = `Found Django manage.py in "${where}" but could not resolve the application entrypoint. Ensure WSGI_APPLICATION or ASGI_APPLICATION is set in your Django settings.`;
|
|
5474
|
+
if (suggestion)
|
|
5475
|
+
message += `
|
|
5476
|
+
|
|
5477
|
+
${suggestion}`;
|
|
5579
5478
|
} else if (diagnostics2.pyprojectAppScript && diagnostics2.pyprojectCheckedPaths?.length) {
|
|
5580
5479
|
const checked = diagnostics2.pyprojectCheckedPaths.join(" or ");
|
|
5581
5480
|
message = `pyproject.toml [project.scripts] defines app = "${diagnostics2.pyprojectAppScript}" but ${checked} was not found.`;
|
|
5582
|
-
|
|
5583
|
-
|
|
5584
|
-
|
|
5481
|
+
if (suggestion)
|
|
5482
|
+
message += `
|
|
5483
|
+
|
|
5484
|
+
${suggestion}`;
|
|
5485
|
+
} else if (suggestion) {
|
|
5486
|
+
message = `No ${displayName} entrypoint found in default locations, ${suggestion}`;
|
|
5585
5487
|
} else {
|
|
5586
5488
|
const searchedList = PYTHON_CANDIDATE_ENTRYPOINTS.join(", ");
|
|
5587
5489
|
message = `No ${displayName} entrypoint found. Set "tool.vercel.entrypoint" in pyproject.toml or define an entrypoint in one of: ${searchedList}.`;
|
|
5588
5490
|
}
|
|
5589
|
-
return new
|
|
5491
|
+
return new import_build_utils7.NowBuildError({ code, message, link, action });
|
|
5590
5492
|
}
|
|
5591
5493
|
async function detectGenericPythonEntrypoint(workPath) {
|
|
5592
5494
|
try {
|
|
@@ -5599,7 +5501,7 @@ async function detectGenericPythonEntrypoint(workPath) {
|
|
|
5599
5501
|
findDiagnostics: result
|
|
5600
5502
|
};
|
|
5601
5503
|
} catch {
|
|
5602
|
-
(0,
|
|
5504
|
+
(0, import_build_utils8.debug)("Failed to discover Python entrypoint");
|
|
5603
5505
|
return {
|
|
5604
5506
|
detected: null,
|
|
5605
5507
|
findDiagnostics: { entrypoint: null, existingWithoutEntrypoint: [] }
|
|
@@ -5612,20 +5514,18 @@ async function detectDjangoPythonEntrypoint(workPath) {
|
|
|
5612
5514
|
findDiagnostics: {
|
|
5613
5515
|
entrypoint: null,
|
|
5614
5516
|
existingWithoutEntrypoint: []
|
|
5615
|
-
}
|
|
5616
|
-
dirs: PYTHON_ENTRYPOINT_DIRS
|
|
5517
|
+
}
|
|
5617
5518
|
};
|
|
5618
5519
|
try {
|
|
5619
5520
|
const subdirs = await getSubdirectories(workPath);
|
|
5620
5521
|
const rootDirs = ["", ...subdirs];
|
|
5621
5522
|
for (const rootDir of rootDirs) {
|
|
5622
|
-
const currPath = (0,
|
|
5523
|
+
const currPath = (0, import_path7.join)(workPath, rootDir);
|
|
5623
5524
|
const isDjango = await checkDjangoManage(currPath);
|
|
5624
5525
|
if (isDjango) {
|
|
5625
5526
|
return {
|
|
5626
5527
|
detected: { baseDir: rootDir },
|
|
5627
|
-
findDiagnostics: { entrypoint: null, existingWithoutEntrypoint: [] }
|
|
5628
|
-
dirs: rootDirs
|
|
5528
|
+
findDiagnostics: { entrypoint: null, existingWithoutEntrypoint: [] }
|
|
5629
5529
|
};
|
|
5630
5530
|
}
|
|
5631
5531
|
}
|
|
@@ -5633,11 +5533,10 @@ async function detectDjangoPythonEntrypoint(workPath) {
|
|
|
5633
5533
|
const result = await findValidEntrypoint(workPath, candidates);
|
|
5634
5534
|
return {
|
|
5635
5535
|
detected: result.entrypoint ? { entrypoint: result.entrypoint } : null,
|
|
5636
|
-
findDiagnostics: result
|
|
5637
|
-
dirs: rootDirs
|
|
5536
|
+
findDiagnostics: result
|
|
5638
5537
|
};
|
|
5639
5538
|
} catch {
|
|
5640
|
-
(0,
|
|
5539
|
+
(0, import_build_utils8.debug)("Failed to discover Django Python entrypoint");
|
|
5641
5540
|
return emptyResult;
|
|
5642
5541
|
}
|
|
5643
5542
|
}
|
|
@@ -5645,10 +5544,10 @@ async function detectPythonEntrypoint(framework, workPath, configuredEntrypoint,
|
|
|
5645
5544
|
if (configuredEntrypoint) {
|
|
5646
5545
|
const { filePath: configEntryFile, varName: configEntryVar } = configuredEntrypoint;
|
|
5647
5546
|
const entrypoint = configEntryFile.endsWith(".py") ? configEntryFile : `${configEntryFile}.py`;
|
|
5648
|
-
const entrypointExists = await fileExists((0,
|
|
5547
|
+
const entrypointExists = await fileExists((0, import_path7.join)(workPath, entrypoint));
|
|
5649
5548
|
if (!entrypointExists) {
|
|
5650
5549
|
return {
|
|
5651
|
-
error: new
|
|
5550
|
+
error: new import_build_utils7.NowBuildError({
|
|
5652
5551
|
code: "PYTHON_ENTRYPOINT_NOT_FOUND",
|
|
5653
5552
|
message: `Configured Python entrypoint "${entrypoint}" was not found.`,
|
|
5654
5553
|
link: PYTHON_ENTRYPOINT_DOCS_URL,
|
|
@@ -5658,17 +5557,17 @@ async function detectPythonEntrypoint(framework, workPath, configuredEntrypoint,
|
|
|
5658
5557
|
}
|
|
5659
5558
|
let varName = configEntryVar ?? await checkEntrypoint(workPath, entrypoint);
|
|
5660
5559
|
if (!varName) {
|
|
5661
|
-
const needsDynamicApp = !!service && ((0,
|
|
5560
|
+
const needsDynamicApp = !!service && ((0, import_build_utils7.isScheduleTriggeredService)(service) || (0, import_build_utils7.isQueueTriggeredService)(service));
|
|
5662
5561
|
if (needsDynamicApp) {
|
|
5663
5562
|
varName = "app";
|
|
5664
5563
|
}
|
|
5665
5564
|
}
|
|
5666
5565
|
if (varName) {
|
|
5667
|
-
(0,
|
|
5566
|
+
(0, import_build_utils8.debug)(`Using configured Python entrypoint: ${entrypoint}`);
|
|
5668
5567
|
return { entrypoint: { entrypoint, variableName: varName } };
|
|
5669
5568
|
} else {
|
|
5670
5569
|
return {
|
|
5671
|
-
error: new
|
|
5570
|
+
error: new import_build_utils7.NowBuildError({
|
|
5672
5571
|
code: "PYTHON_ENTRYPOINT_NOT_FOUND",
|
|
5673
5572
|
message: `Could not find a top-level "app", "application", or "handler" in "${entrypoint}".`,
|
|
5674
5573
|
link: PYTHON_ENTRYPOINT_DOCS_URL,
|
|
@@ -5684,36 +5583,18 @@ async function detectPythonEntrypoint(framework, workPath, configuredEntrypoint,
|
|
|
5684
5583
|
if (vercelEntry)
|
|
5685
5584
|
return { entrypoint: vercelEntry };
|
|
5686
5585
|
let findDiagnostics;
|
|
5687
|
-
let
|
|
5586
|
+
let djangoManageBaseDir;
|
|
5688
5587
|
if (framework === "django") {
|
|
5689
5588
|
const djangoResult = await detectDjangoPythonEntrypoint(workPath);
|
|
5690
5589
|
findDiagnostics = djangoResult.findDiagnostics;
|
|
5691
|
-
searchDirs = djangoResult.dirs;
|
|
5692
5590
|
if (djangoResult.detected) {
|
|
5693
5591
|
if (djangoResult.detected.entrypoint)
|
|
5694
5592
|
return djangoResult.detected;
|
|
5695
|
-
|
|
5696
|
-
const otherPyFiles2 = await findOtherPyFiles(
|
|
5697
|
-
workPath,
|
|
5698
|
-
searchDirs,
|
|
5699
|
-
candidateSet2
|
|
5700
|
-
);
|
|
5701
|
-
const pyprojectResult2 = await getPyprojectEntrypointWithDiagnostics(workPath);
|
|
5702
|
-
const diagnostics3 = {
|
|
5703
|
-
existingWithoutEntrypoint: findDiagnostics.existingWithoutEntrypoint,
|
|
5704
|
-
otherPyFiles: otherPyFiles2,
|
|
5705
|
-
pyprojectAppScript: pyprojectResult2.appScript,
|
|
5706
|
-
pyprojectCheckedPaths: pyprojectResult2.checkedPaths
|
|
5707
|
-
};
|
|
5708
|
-
return {
|
|
5709
|
-
baseDir: djangoResult.detected.baseDir,
|
|
5710
|
-
error: makeDiagnosticError("django", diagnostics3)
|
|
5711
|
-
};
|
|
5593
|
+
djangoManageBaseDir = djangoResult.detected.baseDir;
|
|
5712
5594
|
}
|
|
5713
5595
|
} else {
|
|
5714
5596
|
const genericResult = await detectGenericPythonEntrypoint(workPath);
|
|
5715
5597
|
findDiagnostics = genericResult.findDiagnostics;
|
|
5716
|
-
searchDirs = PYTHON_ENTRYPOINT_DIRS;
|
|
5717
5598
|
if (genericResult.detected)
|
|
5718
5599
|
return genericResult.detected;
|
|
5719
5600
|
}
|
|
@@ -5721,22 +5602,183 @@ async function detectPythonEntrypoint(framework, workPath, configuredEntrypoint,
|
|
|
5721
5602
|
if (pyprojectResult.entrypoint) {
|
|
5722
5603
|
return { entrypoint: pyprojectResult.entrypoint };
|
|
5723
5604
|
}
|
|
5724
|
-
const
|
|
5725
|
-
const otherPyFiles = await findOtherPyFiles(
|
|
5726
|
-
workPath,
|
|
5727
|
-
searchDirs,
|
|
5728
|
-
candidateSet
|
|
5729
|
-
);
|
|
5605
|
+
const validCandidatesElsewhere = await findEntrypointCandidates(workPath);
|
|
5730
5606
|
const diagnostics2 = {
|
|
5731
5607
|
existingWithoutEntrypoint: findDiagnostics.existingWithoutEntrypoint,
|
|
5732
|
-
otherPyFiles,
|
|
5733
5608
|
pyprojectAppScript: pyprojectResult.appScript,
|
|
5734
|
-
pyprojectCheckedPaths: pyprojectResult.checkedPaths
|
|
5609
|
+
pyprojectCheckedPaths: pyprojectResult.checkedPaths,
|
|
5610
|
+
validCandidatesElsewhere,
|
|
5611
|
+
djangoManageBaseDir
|
|
5612
|
+
};
|
|
5613
|
+
const error = makeDiagnosticError(framework, diagnostics2);
|
|
5614
|
+
return djangoManageBaseDir !== void 0 ? { baseDir: djangoManageBaseDir, error } : { error };
|
|
5615
|
+
}
|
|
5616
|
+
var detectEntrypoint = async ({
|
|
5617
|
+
workPath,
|
|
5618
|
+
framework
|
|
5619
|
+
}) => {
|
|
5620
|
+
if (!(0, import_build_utils7.isPythonFramework)(framework))
|
|
5621
|
+
return null;
|
|
5622
|
+
const detected = await detectPythonEntrypoint(
|
|
5623
|
+
framework,
|
|
5624
|
+
workPath
|
|
5625
|
+
);
|
|
5626
|
+
if (!detected?.entrypoint)
|
|
5627
|
+
return null;
|
|
5628
|
+
const { entrypoint, variableName } = detected.entrypoint;
|
|
5629
|
+
return {
|
|
5630
|
+
kind: "py-module:attr",
|
|
5631
|
+
entrypoint: `${entrypointToModule(entrypoint)}:${variableName}`
|
|
5735
5632
|
};
|
|
5736
|
-
|
|
5633
|
+
};
|
|
5634
|
+
|
|
5635
|
+
// src/crons.ts
|
|
5636
|
+
var DYNAMIC_SCHEDULE = "<dynamic>";
|
|
5637
|
+
var scriptPath = (0, import_path8.join)(__dirname, "..", "templates", "vc_cron_detect.py");
|
|
5638
|
+
var script = import_fs8.default.readFileSync(scriptPath, "utf-8");
|
|
5639
|
+
function buildCronRouteTable(crons) {
|
|
5640
|
+
const table = {};
|
|
5641
|
+
for (const cron of crons) {
|
|
5642
|
+
table[cron.path] = cron.resolvedHandler;
|
|
5643
|
+
}
|
|
5644
|
+
return table;
|
|
5645
|
+
}
|
|
5646
|
+
async function getServiceCrons(opts) {
|
|
5647
|
+
const { service, entrypoint, rawEntrypoint, handlerFunction } = opts;
|
|
5648
|
+
const isScheduledService = !!service && (0, import_build_utils10.isScheduleTriggeredService)(service);
|
|
5649
|
+
if (!isScheduledService || !service.name || typeof service.schedule !== "string" && !Array.isArray(service.schedule)) {
|
|
5650
|
+
return void 0;
|
|
5651
|
+
}
|
|
5652
|
+
const cronEntrypoint = entrypoint || rawEntrypoint;
|
|
5653
|
+
if (!cronEntrypoint) {
|
|
5654
|
+
throw new import_build_utils10.NowBuildError({
|
|
5655
|
+
code: "PYTHON_CRON_NO_ENTRYPOINT",
|
|
5656
|
+
message: "Cron service is missing an entrypoint."
|
|
5657
|
+
});
|
|
5658
|
+
}
|
|
5659
|
+
if (service.schedule === DYNAMIC_SCHEDULE) {
|
|
5660
|
+
return getServiceCronsDynamic({
|
|
5661
|
+
serviceName: service.name,
|
|
5662
|
+
cronEntrypoint,
|
|
5663
|
+
handlerFunction,
|
|
5664
|
+
pythonBin: opts.pythonBin,
|
|
5665
|
+
env: opts.env,
|
|
5666
|
+
workPath: opts.workPath
|
|
5667
|
+
});
|
|
5668
|
+
}
|
|
5669
|
+
const cronPath = (0, import_build_utils10.getInternalServiceCronPath)(
|
|
5670
|
+
service.name,
|
|
5671
|
+
cronEntrypoint,
|
|
5672
|
+
handlerFunction || "cron"
|
|
5673
|
+
);
|
|
5674
|
+
const moduleName = entrypointToModule(cronEntrypoint);
|
|
5675
|
+
const resolvedHandler = handlerFunction ? `${moduleName}:${handlerFunction}` : moduleName;
|
|
5676
|
+
const schedules = Array.isArray(service.schedule) ? service.schedule : [service.schedule];
|
|
5677
|
+
return schedules.map((schedule) => ({
|
|
5678
|
+
path: cronPath,
|
|
5679
|
+
schedule,
|
|
5680
|
+
resolvedHandler
|
|
5681
|
+
}));
|
|
5682
|
+
}
|
|
5683
|
+
async function getServiceCronsDynamic(opts) {
|
|
5684
|
+
const {
|
|
5685
|
+
serviceName,
|
|
5686
|
+
cronEntrypoint,
|
|
5687
|
+
handlerFunction,
|
|
5688
|
+
pythonBin,
|
|
5689
|
+
env,
|
|
5690
|
+
workPath
|
|
5691
|
+
} = opts;
|
|
5692
|
+
if (!handlerFunction) {
|
|
5693
|
+
throw new import_build_utils10.NowBuildError({
|
|
5694
|
+
code: "PYTHON_DYNAMIC_CRON_NO_HANDLER",
|
|
5695
|
+
message: 'Dynamic cron detection requires a "module:object" entrypoint where the object has a get_crons() method.'
|
|
5696
|
+
});
|
|
5697
|
+
}
|
|
5698
|
+
const moduleName = entrypointToModule(cronEntrypoint);
|
|
5699
|
+
const entries = await detectDynamicCrons({
|
|
5700
|
+
pythonBin,
|
|
5701
|
+
env,
|
|
5702
|
+
workPath,
|
|
5703
|
+
moduleName,
|
|
5704
|
+
attrName: handlerFunction
|
|
5705
|
+
});
|
|
5706
|
+
console.log(
|
|
5707
|
+
`Detected ${entries.length} cron entry(s): ${entries.map((e) => `${e.module_function} (${e.schedule})`).join(", ")}`
|
|
5708
|
+
);
|
|
5709
|
+
if (entries.length === 0) {
|
|
5710
|
+
throw new import_build_utils10.NowBuildError({
|
|
5711
|
+
code: "PYTHON_DYNAMIC_CRON_EMPTY",
|
|
5712
|
+
message: `Dynamic cron detection returned no entries. "${moduleName}.${handlerFunction}.get_crons()" returned an empty iterable.`
|
|
5713
|
+
});
|
|
5714
|
+
}
|
|
5715
|
+
return entries.map((entry) => {
|
|
5716
|
+
const cronPath = moduleColonFuncToCronPath(
|
|
5717
|
+
serviceName,
|
|
5718
|
+
entry.module_function
|
|
5719
|
+
);
|
|
5720
|
+
return {
|
|
5721
|
+
path: cronPath,
|
|
5722
|
+
schedule: entry.schedule,
|
|
5723
|
+
resolvedHandler: entry.module_function
|
|
5724
|
+
};
|
|
5725
|
+
});
|
|
5726
|
+
}
|
|
5727
|
+
async function detectDynamicCrons(opts) {
|
|
5728
|
+
const { pythonBin, env, workPath, moduleName, attrName } = opts;
|
|
5729
|
+
let stdout;
|
|
5730
|
+
try {
|
|
5731
|
+
const result = await (0, import_execa4.default)(
|
|
5732
|
+
pythonBin,
|
|
5733
|
+
["-c", script, moduleName, attrName],
|
|
5734
|
+
{ env, cwd: workPath }
|
|
5735
|
+
);
|
|
5736
|
+
stdout = result.stdout;
|
|
5737
|
+
} catch (err) {
|
|
5738
|
+
let detail = err?.stderr || err?.message || String(err);
|
|
5739
|
+
try {
|
|
5740
|
+
const parsed2 = JSON.parse(err?.stdout);
|
|
5741
|
+
if (parsed2.error)
|
|
5742
|
+
detail = parsed2.error;
|
|
5743
|
+
} catch {
|
|
5744
|
+
}
|
|
5745
|
+
throw new import_build_utils10.NowBuildError({
|
|
5746
|
+
code: "PYTHON_DYNAMIC_CRON_DETECTION_FAILED",
|
|
5747
|
+
message: `Failed to detect dynamic cron entries: ${detail}`
|
|
5748
|
+
});
|
|
5749
|
+
}
|
|
5750
|
+
let parsed;
|
|
5751
|
+
try {
|
|
5752
|
+
parsed = JSON.parse(stdout);
|
|
5753
|
+
} catch {
|
|
5754
|
+
throw new import_build_utils10.NowBuildError({
|
|
5755
|
+
code: "PYTHON_DYNAMIC_CRON_DETECTION_FAILED",
|
|
5756
|
+
message: `Dynamic cron detection returned invalid JSON: ${stdout}`
|
|
5757
|
+
});
|
|
5758
|
+
}
|
|
5759
|
+
return parsed.entries || [];
|
|
5760
|
+
}
|
|
5761
|
+
function moduleColonFuncToCronPath(serviceName, moduleFunction) {
|
|
5762
|
+
const colonIdx = moduleFunction.indexOf(":");
|
|
5763
|
+
if (colonIdx === -1) {
|
|
5764
|
+
throw new import_build_utils10.NowBuildError({
|
|
5765
|
+
code: "PYTHON_DYNAMIC_CRON_INVALID_FORMAT",
|
|
5766
|
+
message: `Dynamic cron entry must use "module:function" format, got: "${moduleFunction}"`
|
|
5767
|
+
});
|
|
5768
|
+
}
|
|
5769
|
+
const modulePart = moduleFunction.slice(0, colonIdx);
|
|
5770
|
+
const funcPart = moduleFunction.slice(colonIdx + 1);
|
|
5771
|
+
const entrypointPath = modulePart.replace(/\./g, "/");
|
|
5772
|
+
return (0, import_build_utils10.getInternalServiceCronPath)(serviceName, entrypointPath, funcPart);
|
|
5737
5773
|
}
|
|
5738
5774
|
|
|
5739
5775
|
// src/start-dev-server.ts
|
|
5776
|
+
var import_child_process2 = require("child_process");
|
|
5777
|
+
var import_fs9 = require("fs");
|
|
5778
|
+
var import_path9 = require("path");
|
|
5779
|
+
var import_build_utils11 = require("@vercel/build-utils");
|
|
5780
|
+
var import_get_port = __toESM(require_get_port());
|
|
5781
|
+
var import_is_port_reachable = __toESM(require_is_port_reachable());
|
|
5740
5782
|
var import_python_analysis6 = require("@vercel/python-analysis");
|
|
5741
5783
|
var DEV_SERVER_STARTUP_TIMEOUT = 5 * 6e4;
|
|
5742
5784
|
function silenceNodeWarnings() {
|
|
@@ -6313,7 +6355,7 @@ var startDevServer = async (opts) => {
|
|
|
6313
6355
|
});
|
|
6314
6356
|
}
|
|
6315
6357
|
const { entrypoint: entry, variableName } = resolved;
|
|
6316
|
-
const modulePath = entry
|
|
6358
|
+
const modulePath = entrypointToModule(entry);
|
|
6317
6359
|
let childProcess = null;
|
|
6318
6360
|
let stdoutLogListener = null;
|
|
6319
6361
|
let stderrLogListener = null;
|
|
@@ -7198,7 +7240,8 @@ var build = async ({
|
|
|
7198
7240
|
meta = {},
|
|
7199
7241
|
config,
|
|
7200
7242
|
span: parentSpan,
|
|
7201
|
-
service
|
|
7243
|
+
service,
|
|
7244
|
+
registerPreDeploy
|
|
7202
7245
|
}) => {
|
|
7203
7246
|
let entrypoint = rawEntrypoint === "<detect>" ? void 0 : rawEntrypoint;
|
|
7204
7247
|
const builderSpan = parentSpan ?? new import_build_utils16.Span({ name: "vc.builder" });
|
|
@@ -7442,7 +7485,7 @@ var build = async ({
|
|
|
7442
7485
|
await uv.pip({
|
|
7443
7486
|
venvPath,
|
|
7444
7487
|
projectDir: (0, import_path13.join)(workPath, entryDirectory),
|
|
7445
|
-
args: ["install", runtimeDep]
|
|
7488
|
+
args: ["install", "--link-mode", "copy", runtimeDep]
|
|
7446
7489
|
});
|
|
7447
7490
|
if (shouldInstallVercelWorkers) {
|
|
7448
7491
|
const workersDep = baseEnv.VERCEL_WORKERS_PYTHON || `vercel-workers==${VERCEL_WORKERS_VERSION}`;
|
|
@@ -7450,15 +7493,31 @@ var build = async ({
|
|
|
7450
7493
|
await uv.pip({
|
|
7451
7494
|
venvPath,
|
|
7452
7495
|
projectDir: (0, import_path13.join)(workPath, entryDirectory),
|
|
7453
|
-
args: ["install", workersDep]
|
|
7496
|
+
args: ["install", "--link-mode", "copy", workersDep]
|
|
7454
7497
|
});
|
|
7455
7498
|
}
|
|
7456
7499
|
const quirksResult = await runQuirks({ venvPath, pythonEnv, workPath });
|
|
7457
7500
|
if (quirksResult.buildEnv) {
|
|
7458
7501
|
Object.assign(pythonEnv, quirksResult.buildEnv);
|
|
7459
7502
|
}
|
|
7503
|
+
const preDeployCommand = config?.preDeployCommand;
|
|
7504
|
+
if (registerPreDeploy && typeof preDeployCommand === "string") {
|
|
7505
|
+
const capturedEnv = { ...pythonEnv };
|
|
7506
|
+
const capturedCwd = workPath;
|
|
7507
|
+
registerPreDeploy(async () => {
|
|
7508
|
+
await builderSpan.child(import_build_utils16.BUILDER_PRE_DEPLOY_STEP, {
|
|
7509
|
+
preDeployCommand
|
|
7510
|
+
}).trace(async () => {
|
|
7511
|
+
console.log(`Running pre-deploy command: \`${preDeployCommand}\``);
|
|
7512
|
+
await (0, import_build_utils16.execCommand)(preDeployCommand, {
|
|
7513
|
+
env: capturedEnv,
|
|
7514
|
+
cwd: capturedCwd
|
|
7515
|
+
});
|
|
7516
|
+
});
|
|
7517
|
+
});
|
|
7518
|
+
}
|
|
7460
7519
|
(0, import_build_utils16.debug)("Entrypoint is", entrypoint);
|
|
7461
|
-
const moduleName = entrypoint
|
|
7520
|
+
const moduleName = entrypointToModule(entrypoint);
|
|
7462
7521
|
if (handlerFunction) {
|
|
7463
7522
|
const entrypointPath = (0, import_path13.join)(workPath, entrypoint);
|
|
7464
7523
|
const source = await import_fs13.default.promises.readFile(entrypointPath, "utf-8");
|
|
@@ -7697,6 +7756,7 @@ function hasProp(obj, key) {
|
|
|
7697
7756
|
0 && (module.exports = {
|
|
7698
7757
|
build,
|
|
7699
7758
|
defaultShouldServe,
|
|
7759
|
+
detectEntrypoint,
|
|
7700
7760
|
diagnostics,
|
|
7701
7761
|
downloadFilesInWorkPath,
|
|
7702
7762
|
installRequirement,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vercel/python",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.42.0",
|
|
4
4
|
"main": "./dist/index.js",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/python",
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"smol-toml": "1.5.2",
|
|
35
35
|
"vitest": "2.1.4",
|
|
36
36
|
"which": "3.0.0",
|
|
37
|
-
"@vercel/build-utils": "13.
|
|
37
|
+
"@vercel/build-utils": "13.25.0",
|
|
38
38
|
"@vercel/error-utils": "2.1.0",
|
|
39
39
|
"@vercel/python-runtime": "0.13.2"
|
|
40
40
|
},
|