Skip to content

Commit 29d73bc

Browse files
committed
fix(vercel): anchor vercelignore patterns so app/evals is not stripped from the bundle
1 parent 32c597f commit 29d73bc

2 files changed

Lines changed: 48 additions & 26 deletions

File tree

.vercelignore

Lines changed: 26 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,33 @@
11
# Keep the serverless bundle under the 250 MB unzipped limit.
22
#
3-
# Only api/ and app/ are needed at runtime. Everything below is development,
4-
# CI, or deployment tooling that would otherwise be uploaded and counted
5-
# against the limit.
3+
# Every pattern here is ANCHORED with a leading slash, and that is not stylistic.
4+
# .vercelignore uses gitignore semantics: an unanchored `evals/` matches a
5+
# directory of that name at ANY depth, so it silently excluded `app/evals/` —
6+
# a package the request path imports — and the function died at cold start with
7+
# FUNCTION_INVOCATION_FAILED while the build itself reported success.
8+
#
9+
# Only api/ and app/ are needed at runtime.
610

7-
tests/
8-
k8s/
9-
.github/
10-
scripts/
11+
/tests/
12+
/k8s/
13+
/.github/
14+
/scripts/
1115

12-
Dockerfile
13-
docker-compose.yml
14-
.dockerignore
16+
/Dockerfile
17+
/docker-compose.yml
18+
/.dockerignore
1519

16-
# Migrations run from a developer machine or CI, never from the function —
17-
# two concurrent instances racing the same migration is worse than a manual
18-
# step. The golden set and corpus are eval fixtures, not runtime data.
19-
alembic.ini
20-
evals/
20+
# Migrations run from a developer machine or CI, never from the function — two
21+
# concurrent instances racing the same migration is worse than a manual step.
22+
# /evals/ holds the golden set and corpus: eval fixtures, not runtime data.
23+
# (app/evals/ is source code and must ship — hence the leading slash.)
24+
/alembic.ini
25+
/evals/
2126

22-
requirements-local.txt
23-
requirements-dev.txt
27+
/requirements-local.txt
28+
/requirements-dev.txt
2429

25-
README.md
26-
.venv/
27-
.ruff_cache/
28-
.pytest_cache/
30+
/README.md
31+
/.venv/
32+
/.ruff_cache/
33+
/.pytest_cache/

api/index.py

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,32 @@
11
"""Vercel serverless entrypoint.
22
3-
Vercel's @vercel/python runtime looks for a module-level ASGI callable named
4-
`app`. Everything else in this file exists to make the repo root importable,
5-
since Vercel executes this module from inside api/ rather than the project root.
3+
Vercel's Python runtime looks for a module-level ASGI callable named `app`.
4+
Everything else here exists because this module is executed from inside api/
5+
rather than the project root, so the repo root has to be put on sys.path before
6+
`app` is importable.
67
"""
78

89
import os
910
import sys
1011

11-
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
12+
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
13+
sys.path.insert(0, ROOT)
1214

13-
from app.main import create_app # noqa: E402
15+
try:
16+
from app.main import create_app
17+
except ModuleNotFoundError as exc: # pragma: no cover - deployment diagnostics
18+
# A missing package here is almost always a packaging problem, not a code
19+
# one: something excluded `app/` or a subpackage from the bundle. The raw
20+
# traceback surfaces only as FUNCTION_INVOCATION_FAILED with the build
21+
# reporting success, which sends you looking in entirely the wrong place —
22+
# so say what is actually on disk.
23+
present = sorted(p for p in os.listdir(ROOT) if not p.startswith("."))
24+
raise RuntimeError(
25+
f"Could not import {exc.name!r} from the deployment bundle. "
26+
f"Project root {ROOT} contains: {present}. "
27+
"If 'app' is absent or incomplete, check .vercelignore — its patterns "
28+
"use gitignore semantics, so an unanchored directory name matches at "
29+
"every depth."
30+
) from exc
1431

1532
app = create_app()

0 commit comments

Comments
 (0)