diff --git a/build.yaml b/build.yaml index 4aea474..506fb99 100644 --- a/build.yaml +++ b/build.yaml @@ -1,5 +1,8 @@ version: 1 platform: python -platformVersion: "3.14" +# NOTE: platformVersion intentionally omitted — this is the FRICTIONS-observed bug condition. +# Per static analysis, this should: build with whatever Oryx auto-picks, runtime selects default (3.14). + run: - port: 8080 \ No newline at end of file + port: 8080 + startCommand: "bash /output/diagnose.sh" diff --git a/diagnose.sh b/diagnose.sh new file mode 100755 index 0000000..4b20cb5 --- /dev/null +++ b/diagnose.sh @@ -0,0 +1,85 @@ +#!/bin/bash +set +e +echo "==============================================" +echo "=== BUG-2 DIAGNOSTIC v2 :: $(date -u) ========" +echo "==============================================" + +echo "" +echo "--- [A] Enumerate every python* binary in the runtime image ---" +ls -la /usr/bin/python* /usr/local/bin/python* /opt/*/bin/python* 2>&1 | sort -u +echo "PATH=$PATH" + +echo "" +echo "--- [B] pyvenv.cfg of the shipped venv ---" +cat /app/pythonenv3.12/pyvenv.cfg 2>&1 || cat /output/pythonenv3.12/pyvenv.cfg 2>&1 + +echo "" +echo "--- [C] file & ldd of the venv's python binary ---" +file /app/pythonenv3.12/bin/python 2>&1 || file /output/pythonenv3.12/bin/python 2>&1 +echo "----" +ldd /app/pythonenv3.12/bin/python 2>&1 | head -10 || ldd /output/pythonenv3.12/bin/python 2>&1 | head -10 + +echo "" +echo "--- [D] Try the path Oryx's runtime banner advertised ---" +echo "Banner said: PYTHONPATH=':/output/pythonenv3.12/lib/python3.14/site-packages'" +ls -d /output/pythonenv3.12/lib/python3.14/site-packages 2>&1 +test -d /output/pythonenv3.12/lib/python3.14/site-packages && echo "EXISTS" || echo "DOES NOT EXIST" + +echo "" +echo "--- [E] FAILURE MODE 1: user runs system python3.14 with banner's PYTHONPATH ---" +if command -v python3.14 >/dev/null 2>&1; then + PYTHONPATH=/output/pythonenv3.12/lib/python3.14/site-packages python3.14 -c "import fastapi; print('imported fastapi from', fastapi.__file__)" 2>&1 + echo "Exit: $?" +else + echo "(no python3.14 in this image)" +fi + +echo "" +echo "--- [F] FAILURE MODE 2: docs invariant reversed for platformVersion: 3.14 ---" +ls -d /output/pythonenv3.14 2>&1 +test -d /output/pythonenv3.14 && echo "EXISTS" || echo "DOES NOT EXIST (the dir is named from build-time, so 3.14 venv would only exist when platformVersion=3.14)" + +echo "" +echo "--- [G] FAILURE MODE 3: 3.12-built C-extension under 3.14 ABI interpreter ---" +echo "Find cp312 pydantic_core .so:" +find /output /app -name '_pydantic_core.cpython-3*.so' 2>/dev/null | head -5 +if command -v python3.14 >/dev/null 2>&1; then + echo "Try loading cp312 wheel under python3.14:" + PYTHONPATH=/app/pythonenv3.12/lib/python3.12/site-packages python3.14 -c "import pydantic_core; print(pydantic_core.__version__)" 2>&1 + echo "Exit: $?" +fi + +echo "" +echo "--- [H] Oryx's runtime startup script (what generated the misleading banner) ---" +ls -la /app/oryx-startup.sh /output/oryx-startup.sh 2>&1 +cat /app/oryx-startup.sh 2>&1 | head -60 || cat /output/oryx-startup.sh 2>&1 | head -60 + +echo "" +echo "--- [I] What runtime image are we in? ---" +cat /etc/os-release 2>&1 | head -5 +echo "---" +test -f /opt/oryx/python_version_info && cat /opt/oryx/python_version_info +test -f /usr/local/oryx/python_version_info && cat /usr/local/oryx/python_version_info + +echo "" +echo "--- [J] Confirm the basics from v1 still hold ---" +echo "which python: $(which python 2>&1)" +echo "python --version: $(python --version 2>&1)" +echo "/output/pythonenv3.12/bin/python --version: $(/output/pythonenv3.12/bin/python --version 2>&1)" +echo "sys.executable / sys.version:" +python -c "import sys; print(sys.executable); print(sys.version)" 2>&1 + +echo "" +echo "==============================================" +echo "=== BUG-2 DIAGNOSTIC v2 :: COMPLETE ==========" +echo "==============================================" + +exec python -c " +from http.server import HTTPServer, BaseHTTPRequestHandler +class H(BaseHTTPRequestHandler): + def do_GET(self): + self.send_response(200); self.end_headers(); self.wfile.write(b'ok') + def log_message(self, *a): pass +print('[diag v2] healthcheck server listening on :8080', flush=True) +HTTPServer(('0.0.0.0', 8080), H).serve_forever() +"