# tools/_build_python.sh — the Python that release builds use. Sourced, not executed. # # Launch audit batch A (PORT-001, PORT-011), 23 Sep 2026. Until 0.9.16 the DMG was built # with venv.nosync, i.e. Homebrew's Python 3.11: its framework, its 57 lib-dynload modules # and the openssl/sqlite/lzma/mpdecimal dylibs it drags in all declared minimum macOS 26, # while the app claimed 13.0. Release builds now use python-build-standalone # (https://github.com/astral-sh/python-build-standalone, installed by uv): CPython built # for macOS 11.0 with OpenSSL, SQLite, xz etc. statically inside libpython, and OpenSSL # configured to read the system CA store (/private/etc/ssl/cert.pem) instead of a # Homebrew path that users' Macs don't have. python.org no longer publishes 3.11 # installers (security-only branch; the last one, 3.11.9, predates a year of fixes). # # Two build venvs, both recreated from their lock on every build (uv pip sync): # venv-build.nosync the closed app (requirements.lock) — no GPL code in it # venv-piper.nosync piper-worker GPL (tools/piper_worker/requirements.lock) # venv.nosync stays the everyday dev/test venv (Homebrew Python) and is not used to build. BUILD_PY_VERSION="3.11.15" # The exact python-build-standalone release, not just the CPython version: which release # `uv python install 3.11.15` resolves to depends on the uv version, and a newer release # would silently change the OpenSSL/SQLite inside the app (review of batch A). The build # stamp and OpenSSL of release 20260510 are pinned; a different interpreter stops the # build until someone reviews it and updates both lines. BUILD_PY_STAMP="May 10 2026 19:23:07" # platform.python_build()[1] BUILD_PY_OPENSSL="OpenSSL 3.5.6 7 Apr 2026" # ssl.OPENSSL_VERSION build_python() { command -v uv >/dev/null || { echo "❌ falta uv → brew install uv" >&2; return 1; } uv python install -q "$BUILD_PY_VERSION" >/dev/null 2>&1 || true uv python find --managed-python "$BUILD_PY_VERSION" } # ensure_build_venv DIR LOCKFILE — create DIR from the standalone Python if needed, then # make its packages exactly the lock (adds, upgrades AND removes). ensure_build_venv() { local dir="$1" lock="$2" py py="$(build_python)" || return 1 [ -n "$py" ] || { echo "❌ no encuentro CPython $BUILD_PY_VERSION de uv" >&2; return 1; } local have want want="$(realpath "$py")" have="$("$dir/bin/python" -c 'import os, sys; print(os.path.realpath(sys._base_executable))' 2>/dev/null || true)" local ident ident="$("$py" -c 'import platform, ssl; print(platform.python_build()[1] + "|" + ssl.OPENSSL_VERSION)')" || return 1 if [ "$ident" != "${BUILD_PY_STAMP}|${BUILD_PY_OPENSSL}" ]; then echo "❌ $py no es el python-build-standalone fijado:" >&2 echo " es $ident" >&2 echo " fijado ${BUILD_PY_STAMP}|${BUILD_PY_OPENSSL}" >&2 echo " Revisa el cambio (OpenSSL, SQLite de la app) y actualiza BUILD_PY_STAMP/BUILD_PY_OPENSSL." >&2 return 1 fi if [ "$have" != "$want" ]; then rm -rf "$dir" uv venv -q -p "$py" "$dir" || return 1 fi uv pip sync -q -p "$dir/bin/python" --require-hashes "$lock" }