From 950a53017f2880e36c6f7062b8b00427df1f294b Mon Sep 17 00:00:00 2001 From: Roland Date: Mon, 20 Jul 2026 08:55:41 -0600 Subject: [PATCH] upgrade: rebuild venvs after a Python major-version change A Debian major upgrade (e.g. 12 -> 13) replaces /usr/bin/python3 (3.11 -> 3.13). Existing venvs still resolve `python3` to the system interpreter, but their packages live in lib/python3.11/site-packages, which the new interpreter never searches -- every import fails and `pip install --upgrade` cannot repair them, taking down the API, Controller, yt-dlp, and Help. upgrade.sh now compares each venv's recorded Python version against the current one and does a full rebuild via reset_virtual_environments.sh when they differ. Detecting a mismatch (rather than literally "3.11") also self-heals future bumps and is a no-op once versions match or on a fresh install with no pyvenv.cfg yet. reset_virtual_environments.sh gains a -y/--yes flag to skip its confirmation prompt; the interactive yes_or_no read loops forever on EOF under the tty-less wrolpi-upgrade.service. Co-Authored-By: Claude Opus 4.8 (1M context) --- scripts/reset_virtual_environments.sh | 16 +++++++++++++- scripts/upgrade.sh | 31 +++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 1 deletion(-) diff --git a/scripts/reset_virtual_environments.sh b/scripts/reset_virtual_environments.sh index 9e361fe4..20b95756 100755 --- a/scripts/reset_virtual_environments.sh +++ b/scripts/reset_virtual_environments.sh @@ -1,6 +1,18 @@ #!/usr/bin/env bash # Deletes and recreates all WROLPi Python virtual environments. # This script requires internet access for pip install. +# +# Usage: reset_virtual_environments.sh [-y|--yes] +# -y, --yes Skip the confirmation prompt. Used by automated upgrades +# (wrolpi-upgrade.service), which run without a tty -- the +# interactive prompt would otherwise loop forever on EOF. + +ASSUME_YES=false +for arg in "$@"; do + case "${arg}" in + -y | --yes) ASSUME_YES=true ;; + esac +done # Re-execute this script if it wasn't called with sudo. if [ $EUID != 0 ]; then @@ -10,7 +22,9 @@ fi source /opt/wrolpi/wrolpi/scripts/lib.sh -yes_or_no "This script requires internet access. Do you want to continue?" || exit 0 +if [ "${ASSUME_YES}" != true ]; then + yes_or_no "This script requires internet access. Do you want to continue?" || exit 0 +fi set -e set -x diff --git a/scripts/upgrade.sh b/scripts/upgrade.sh index 67c124fe..382e4a80 100755 --- a/scripts/upgrade.sh +++ b/scripts/upgrade.sh @@ -119,6 +119,37 @@ EOF debian12_guard "$@" # --------------------------------------------------------------------------- +# --- Rebuild virtual environments after a Python major-version change ------- +# A Debian major upgrade (e.g. 12 -> 13) replaces /usr/bin/python3 (3.11 -> 3.13). +# The existing venvs still resolve their `python3` to the system interpreter, +# but their packages live in lib/python3.11/site-packages, which the new +# interpreter never searches -- so every import fails and `pip install --upgrade` +# cannot repair them. Detect this by comparing each venv's recorded Python +# version against the current one, and do a full rebuild (delete + recreate + +# reinstall) via reset_virtual_environments.sh when they differ. +rebuild_stale_venvs() { + local current + current=$(python3 -c 'import sys; print("%d.%d" % sys.version_info[:2])' 2>/dev/null) || return 0 + local cfg venv_version + for cfg in /opt/wrolpi/venv/pyvenv.cfg \ + /opt/wrolpi/controller/venv/pyvenv.cfg \ + /opt/wrolpi-help/venv/pyvenv.cfg; do + [ -r "${cfg}" ] || continue + # pyvenv.cfg records e.g. "version = 3.11.2"; match that key exactly so + # the newer "version_info = ..." line is ignored. Reduce to major.minor. + venv_version=$(awk -F'=' '$1 ~ /^[[:space:]]*version[[:space:]]*$/ {gsub(/[[:space:]]/,"",$2); print $2; exit}' "${cfg}") + venv_version=${venv_version%.*} + if [ -n "${venv_version}" ] && [ "${venv_version}" != "${current}" ]; then + echo "Python changed (venv at ${cfg%/pyvenv.cfg} built for ${venv_version}, system is now ${current}); rebuilding all virtual environments..." + /opt/wrolpi/scripts/reset_virtual_environments.sh -y + return 0 + fi + done +} + +rebuild_stale_venvs +# --------------------------------------------------------------------------- + # Upgrade Controller first so users can monitor the rest of the upgrade. upgrade_controller() { echo "Upgrading WROLPi Controller..."