erplibre/script/install/install_venv.sh
Mathieu Benoit 42e10bcced [UPD] install : le venv d'outillage passe en Python 3.14.7
install_erplibre.sh bâtissait .venv.erplibre avec le python3 du système, quelle
que soit sa version : un 3.10 y donnait un venv que l'outillage ne sait pas
charger. Il délègue à install_venv.sh, qui obtient la version par mise, pyenv
ou la distribution, et rebâtit un venv hors service. Le PATCH ne borne que le
venv d'Odoo, dont le pyproject exige « >=3.12.10,<3.13 » : l'exiger ailleurs
écartait un Python de distribution d'un cran en retard — NixOS 25.11 livre
3.14.2 — et faisait compiler CPython pour rien. Le module NixOS déclare
désormais les DEUX Python, substitués depuis les fichiers de version.
Vérifié : 15 tests sur des venvs factices, conservés, rebâtis ou refusés selon
leur état, et un chemin sans pyvenv.cfg garde son contenu.

--- EN ---

install_erplibre.sh built .venv.erplibre with the system python3, whatever its
version: a 3.10 gave a venv the tooling cannot load. It delegates to
install_venv.sh, which obtains the version through mise, pyenv or the
distribution, and rebuilds a venv out of service. The PATCH bounds only Odoo's
venv, whose pyproject requires ">=3.12.10,<3.13": requiring it elsewhere turned
away a distribution Python one step behind — NixOS 25.11 ships 3.14.2 — and
compiled CPython for nothing. The NixOS module now declares BOTH Pythons,
substituted from the version files.
Checked: 15 tests on stub venvs, kept, rebuilt or refused by their state, and a
path without pyvenv.cfg keeps its content.

Assisted-by: Claude Opus 5
2026-09-24 13:56:57 -04:00

176 lines
6.9 KiB
Bash
Executable file

#!/usr/bin/env bash
# Check if all 3 parameters are present
if [ -z "$1" ] || [ -z "$2" ] || [ -z "$3" ]; then
echo "Error: One or more parameters are missing."
echo "Usage: $0 <Context> <Venv_Path> <Python_Version>"
exit 1
fi
# Assign arguments to variables
CONTEXT="$1"
# Sans « / » final : « rm -rf lien/ » viderait la cible d'un lien.
VENV_PATH="${2%"${2##*[!/]}"}"
PYTHON_VERSION="$3"
if [ -z "${VENV_PATH}" ]; then
echo "Error: Venv_Path '$2' designates the root."
exit 1
fi
# Display variables (for verification)
echo "Context: $CONTEXT"
echo "Venv Path: $VENV_PATH"
echo "Python Version: $PYTHON_VERSION"
# Le CHOIX du fournisseur (mise ou pyenv) vit dans la bibliothèque : ce script
# ne connaît qu'un chemin d'interpréteur.
# shellcheck source=script/install/lib_python_provider.sh
. ./script/install/lib_python_provider.sh
# Ce qui borne le PATCH, et pour qui.
#
# Le venv d'Odoo est contraint par son pyproject (« >=3.12.10,<3.13 ») : un
# patch inférieur y serait refusé par Poetry. Rien n'épingle celui de
# l'outillage — exiger le patch y écarte le Python des distributions dès
# qu'il est d'un cran en retard, et force pyenv à compiler CPython pour rien.
EXIGENCE="patch"
[ "${CONTEXT}" = "ERPLibre" ] && EXIGENCE="mineure"
# Version RÉELLE d'un venv : celle que rend son interpréteur, jamais celle
# qu'annonce pyvenv.cfg, figée à la création et que rien ne revalide. Rien
# n'est rendu quand bin/python ne s'exécute plus — un venv bâti sur un CPython
# depuis retiré garde un lien mort, et c'est justement un venv à rebâtir.
el_venv_python_version() {
local py="$1/bin/python"
[ -x "${py}" ] || return 1
"${py}" -c 'import platform;print(platform.python_version())' 2> /dev/null
}
# Le venv est-il HORS SERVICE — non pas d'une autre version, mais inutilisable
# tel quel ? Ce qui suit ne se répare pas : on rebâtit, quel que soit l'appelant.
el_venv_hors_service() {
local py="$1/bin/python"
if [[ ! -f "$1/pyvenv.cfg" ]]; then
echo "aucun pyvenv.cfg"
elif [[ -z "$(el_venv_python_version "$1")" ]]; then
echo "son interpreteur ne s'execute plus"
elif [[ ! -f "$1/bin/activate" ]] || ! "${py}" -m pip --version > /dev/null 2>&1; then
# Ce que laisse un « python -m venv » sans ensurepip.
echo "bin/activate ou pip manquant"
fi
}
# Le venv tourne, mais pas sur la version demandée. La version est celle que
# rend bin/python, pas celle de pyvenv.cfg, figée à la création. Compatible et
# non identique : un python de distribution d'un patch plus récent convient, et
# l'égalité stricte rebâtirait à chaque appel.
el_venv_version_autre() {
if ! el_python_is_compatible \
"$1/bin/python" "${PYTHON_VERSION}" "${EXIGENCE}"; then
echo "bin/python en $(el_venv_python_version "$1"), ${PYTHON_VERSION} exige"
fi
}
# Ce qui oblige à rebâtir, ou rien.
#
# Une version qui diffère ne vaut destruction que pour le venv d'OUTILLAGE :
# le rebâtir coûte une installation pip, quand celui d'Odoo représente une
# installation Poetry entière — des centaines de paquets, dont certains se
# compilent. Un écart de patch ne l'empêche pas de servir ; on le dit, et on le
# laisse. Hors service, en revanche, il ne sert plus personne.
el_venv_a_rebatir() {
local cause
cause="$(el_venv_hors_service "$1")"
if [[ -n "${cause}" ]]; then
echo "${cause}"
return
fi
cause="$(el_venv_version_autre "$1")"
if [[ -n "${cause}" ]] && [[ "${CONTEXT}" == "ERPLibre" ]]; then
echo "${cause}"
fi
}
# Un checkout MONTÉ depuis une autre machine porte le venv de cette machine.
# Son interpréteur ne démarre pas ici — un CPython précompilé cherche sa
# bibliothèque standard sous le préfixe de sa construction —, ce qui le fait
# passer pour défectueux. Le détruire effacerait, à travers le réseau,
# l'installation de la machine à qui il appartient.
# findmnt vient d'util-linux : absent ailleurs, le contrôle s'abstient plutôt
# que de refuser à tort.
el_venv_est_distant() {
local fs
command -v findmnt > /dev/null 2>&1 || return 1
fs="$(findmnt -n -o FSTYPE --target "$1" 2> /dev/null)"
case "${fs}" in
fuse* | sshfs | nfs* | cifs | smb*) return 0 ;;
*) return 1 ;;
esac
}
PYTHON_EXEC="$(el_python_exec "${PYTHON_VERSION}" "${EXIGENCE}")"
if [[ -z "${PYTHON_EXEC}" ]] || [[ ! -x "${PYTHON_EXEC}" ]]; then
echo "Aucun interpreteur Python ${PYTHON_VERSION} n'a pu etre obtenu."
echo " Fournisseur demande : ${EL_PYTHON_PROVIDER:-auto}"
if command -v mise > /dev/null 2>&1; then
echo " Voir : mise install python@${PYTHON_VERSION} (reseau requis)"
else
echo " Voir 'make install_mise', ou installez pyenv."
fi
exit 1
fi
echo "Interpreteur retenu : ${PYTHON_EXEC}"
# L'interpréteur est obtenu AVANT toute destruction : un provisionnement en
# échec laisse le venv en place.
if [[ -d ${VENV_PATH} ]]; then
REASON="$(el_venv_a_rebatir "${VENV_PATH}")"
CONSERVE="$(el_venv_version_autre "${VENV_PATH}")"
if [[ -z "${REASON}" ]] && [[ -n "${CONSERVE}" ]]; then
echo "Virtual environment ${VENV_PATH} conserve : ${CONSERVE}."
echo " Le rebatir refait toute son installation. Si vous le voulez :"
echo " rm -rf ${VENV_PATH} puis relancez."
fi
# rmdir n'écarte qu'un répertoire vide, ce que laisse une création
# interrompue ; seul un pyvenv.cfg autorise le « rm -rf ».
if [[ -n "${REASON}" ]] && ! rmdir "${VENV_PATH}" 2> /dev/null; then
if [[ ! -f "${VENV_PATH}/pyvenv.cfg" ]]; then
echo "Refus de detruire ${VENV_PATH} : ce n'est pas un venv (${REASON})."
echo " Ecartez-le vous-meme, puis relancez."
exit 1
fi
if el_venv_est_distant "${VENV_PATH}"; then
echo "Refus de detruire ${VENV_PATH} : il est sur un systeme de"
echo " fichiers monte a distance, donc il appartient a une autre"
echo " machine (${REASON} vu d'ici)."
echo " Installez SUR cette machine-la, pas au travers du montage."
exit 1
fi
echo "DESTRUCTION de ${VENV_PATH} : ${REASON}."
echo " Ce qui y a ete pose a la main part avec lui ; il est rebati."
if ! rm -rf "${VENV_PATH}"; then
echo "Destruction de ${VENV_PATH} en echec, probablement faute de droits :"
echo " le venv est peut-etre partiellement detruit. Rien n'est rebati."
echo " Corrigez les droits, puis : rm -rf ${VENV_PATH} et relancez."
exit 1
fi
fi
fi
if [[ ! -d ${VENV_PATH} ]]; then
echo -e "\n---- Create Virtual environment Python ----"
if ! "${PYTHON_EXEC}" -m venv "${VENV_PATH}"; then
echo "Virtual environment, error when creating ${VENV_PATH}"
exit 1
fi
fi
# Contrôle final : l'appelant enchaîne sur ce venv, un 0 doit le garantir.
# Hors service seulement : un venv d'une autre version qu'on vient de conserver
# sciemment reste utilisable.
REASON="$(el_venv_hors_service "${VENV_PATH}")"
if [[ ! -d ${VENV_PATH} ]] || [[ -n "${REASON}" ]]; then
echo "Virtual environment ${VENV_PATH} inutilisable : ${REASON:-absent}."
exit 1
fi
echo "Virtual environment ${VENV_PATH} pret."