diff --git a/script/install/install_debian_dependency.sh b/script/install/install_debian_dependency.sh index cc56946..e0022ea 100755 --- a/script/install/install_debian_dependency.sh +++ b/script/install/install_debian_dependency.sh @@ -58,7 +58,161 @@ fi #-------------------------------------------------- if [ "$(uname -m)" = "s390x" ]; then echo "Arch s390x detected" - sudo apt install rust-all libqpdf-dev libgeos-dev libproj-dev proj-bin proj-data libgeographiclib-dev freetds-dev freetds-bin libkrb5-dev libssl-dev pkg-config build-essential npm -y + # Sur une VM fraîche, l'index apt peut être vide : « apt install » échouerait + # alors sur TOUT le lot, et l'absence d'un seul paquet ne se voit que bien + # plus loin, sous la forme d'une commande introuvable. + ${APT_GET} update + # « ${APT_GET} » et non « sudo apt » : au 1er boot d'une image cloud, + # cloud-init tient le verrou apt, et un « apt install » nu échoue AUSSITÔT. + # Ces trois lots étaient les seuls du script à contourner le wrapper, et + # aucun ne vérifiait son résultat : libqpdf-dev manquait sans un mot, et + # l'échec ne se voyait qu'une heure plus tard, à la compilation de pikepdf. + # Un nom de paquet CHANGE d'une version d'Ubuntu à l'autre : GeographicLib + # s'appelle « libgeographic-dev » sur 20.04 et 22.04, « libgeographiclib-dev » + # depuis 24.04. Le nom récent existe pourtant dans la base apt des anciennes, + # SANS version installable — apt refuse alors le lot ENTIER, emportant les + # quatorze autres paquets. On demande donc à apt lui-même quel nom est + # réellement installable, seule autorité sur la question. + apt_pick() { + for candidate in "$@"; do + if apt-get install -s -qq "${candidate}" > /dev/null 2>&1; then + echo "${candidate}" + return 0 + fi + done + echo "$1" + } + # Vrai si dpkg considère le paquet installé et configuré. + apt_is_installed() { + [ "$(dpkg-query -W -f='${db:Status-Status}' "$1" 2>/dev/null)" = "installed" ] + } + # Le lot d'abord, pour la vitesse. S'il échoue, on reprend paquet par paquet + # afin de NOMMER le fautif : sinon un seul nom obsolète masque les autres et + # le message ne dit pas lequel corriger. + apt_install_batch() { + if ${APT_GET} install "$@" -y; then + return 0 + fi + # Un dépaquetage raté laisse dpkg à moitié configuré, et TOUT apt suivant + # rend « Unmet dependencies » — y compris pour des paquets déjà installés. + # Sans cette réparation, la boucle ci-dessous accusait les quinze paquets + # à cause d'un seul, et l'installation s'arrêtait sur un paquet présent. + echo "Lot apt en echec : tentative de reparation dpkg..." + sudo dpkg --configure -a || true + ${APT_GET} --fix-broken install -y || true + if ${APT_GET} install "$@" -y; then + echo "Reparation reussie." + return 0 + fi + echo "Reprise paquet par paquet pour identifier le fautif..." + df -h / | sed 's/^/ disque: /' + APT_FAILED="" + for one in "$@"; do + ${APT_GET} install "${one}" -y && continue + # L'échec peut n'être qu'un contrecoup de l'état global : on ne l'impute + # au paquet que si dpkg confirme qu'il n'est PAS installé. + apt_is_installed "${one}" || APT_FAILED="${APT_FAILED} ${one}" + done + [ -z "${APT_FAILED}" ] + } + APT_FAILED="" + GEO_DEV="$(apt_pick libgeographiclib-dev libgeographic-dev)" + # manifold3d (via to-3mf) et pymupdf n'ont pas de roue s390x : ils se + # compilent, d'où tbb, cmake et ninja. Un seul lot, une seule reprise. + apt_install_batch rust-all libqpdf-dev libgeos-dev libproj-dev proj-bin \ + proj-data "${GEO_DEV}" freetds-dev freetds-bin libkrb5-dev libssl-dev \ + pkg-config build-essential zlib1g-dev libjpeg-dev libtbb-dev cmake \ + ninja-build + if [[ -n "${APT_FAILED}" ]]; then + # Seul un paquet dont dépend la SUITE immédiate est bloquant. Les autres + # servent des modules Odoo optionnels : les rendre fatals immobiliserait + # toute l'installation pour un module que personne n'utilise ici. + echo "Paquets s390x non installables :${APT_FAILED}" + for essential in build-essential pkg-config libssl-dev zlib1g-dev \ + libjpeg-dev cmake; do + case " ${APT_FAILED} " in + *" ${essential} "*) + echo "apt-get s390x : '${essential}' est indispensable, arret." + exit 1 + ;; + esac + done + echo "Aucun n'est indispensable a la compilation : on continue." + fi + # pymupdf non plus n'a pas de roue s390x : il compile MuPDF, dont le + # générateur de liaisons charge « libclang.so » par son nom nu, via ctypes. + # La roue PyPI libclang, qui embarque cette bibliothèque, n'existe pas ici. + # Le paquet de la distribution la fournit bien dans le chemin de l'éditeur de + # liens, mais sous un nom versionné : il ne manque que le lien non versionné. + ${APT_GET} install libclang-dev -y + CLANG_LIB_DIR="/usr/lib/s390x-linux-gnu" + if [ ! -e "${CLANG_LIB_DIR}/libclang.so" ]; then + CLANG_SO="$(ls -1 ${CLANG_LIB_DIR}/libclang-[0-9]*.so 2>/dev/null | sort -V | tail -1)" + if [ -n "${CLANG_SO}" ]; then + sudo ln -s "${CLANG_SO}" "${CLANG_LIB_DIR}/libclang.so" + sudo ldconfig + echo "libclang.so -> ${CLANG_SO}" + else + echo "Attention : libclang introuvable, la compilation de pymupdf va echouer." + fi + fi + # pikepdf n'a pas de roue s390x et se lie à qpdf, dont il exige une version + # PRÉCISE : 12.2.0 au minimum pour la 10.x. Ubuntu en livre 9.1.1 (20.04), + # 10.6.3 (22.04) et 11.9.0 (24.04) — trop anciennes, et aucune version de + # pikepdf ne descend sous qpdf 11.5. 25.10 et 26.04 passent, d'où le partage + # observé. On complète donc par les sources sous ce seuil ; /usr/local est + # déjà dans les chemins par défaut de g++ et de l'éditeur de liens. + QPDF_MIN=12.2.0 + QPDF_VER=12.3.2 + qpdf_have="$(pkg-config --modversion libqpdf 2>/dev/null || echo 0)" + if [ "$(printf '%s\n%s\n' "${QPDF_MIN}" "${qpdf_have}" | sort -V | head -1)" != "${QPDF_MIN}" ]; then + echo "qpdf ${qpdf_have} < ${QPDF_MIN} requis par pikepdf : compilation de qpdf ${QPDF_VER} (long en emulation)." + QPDF_TMP="$(mktemp -d)" + if curl -fsSL --max-time 600 -o "${QPDF_TMP}/qpdf.tar.gz" \ + "https://github.com/qpdf/qpdf/releases/download/v${QPDF_VER}/qpdf-${QPDF_VER}.tar.gz" && + tar -xzf "${QPDF_TMP}/qpdf.tar.gz" -C "${QPDF_TMP}" && + cmake -S "${QPDF_TMP}/qpdf-${QPDF_VER}" -B "${QPDF_TMP}/build" \ + -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr/local \ + -DBUILD_DOC=OFF -DBUILD_STATIC_LIBS=OFF && + cmake --build "${QPDF_TMP}/build" -j"$(nproc)" && + sudo cmake --install "${QPDF_TMP}/build"; then + sudo ldconfig + echo "qpdf $(PKG_CONFIG_PATH=/usr/local/lib/pkgconfig pkg-config --modversion libqpdf 2>/dev/null) installe dans /usr/local." + else + echo "Attention : compilation de qpdf echouee, pikepdf ne pourra pas se construire." + fi + rm -rf "${QPDF_TMP}" + fi + # cryptography ne publie aucune roue s390x : elle se compile, et son + # Cargo.lock est en version 4, que seul cargo >= 1.78 sait lire. Ubuntu 24.04 + # livre 1.75 et s'arrête sur « lock file version 4 requires + # -Znext-lockfile-bump » ; 25.10 et au-delà passent. On complète alors par + # rustup, en exposant la chaîne hors du shell de connexion : les étapes + # suivantes sont des processus distincts, qui ne liront ni ~/.bashrc ni + # ~/.cargo/env. + CARGO_MIN_MINOR=78 + cargo_ver="$(cargo --version 2>/dev/null | awk '{print $2}')" + cargo_major="${cargo_ver%%.*}" + cargo_rest="${cargo_ver#*.}" + cargo_minor="${cargo_rest%%.*}" + cargo_ok=0 + if [[ "${cargo_major}" =~ ^[0-9]+$ && "${cargo_minor}" =~ ^[0-9]+$ ]]; then + if [[ ${cargo_major} -gt 1 ]] || + [[ ${cargo_major} -eq 1 && ${cargo_minor} -ge ${CARGO_MIN_MINOR} ]]; then + cargo_ok=1 + fi + fi + if [ "${cargo_ok}" -ne 1 ]; then + echo "cargo trop ancien ($(cargo --version 2>/dev/null || echo absent)) pour cryptography : installation via rustup." + curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs \ + | sh -s -- -y --profile minimal --default-toolchain stable + for bin in cargo rustc; do + if [ -x "${HOME}/.cargo/bin/${bin}" ]; then + sudo ln -sf "${HOME}/.cargo/bin/${bin}" "/usr/local/bin/${bin}" + fi + done + echo "cargo retenu : $(PATH=/usr/local/bin:$PATH cargo --version 2>/dev/null || echo absent)" + fi fi #-------------------------------------------------- @@ -156,16 +310,97 @@ else NODE_MAJOR=22 fi -echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_$NODE_MAJOR.x nodistro main" | sudo tee /etc/apt/sources.list.d/nodesource.list -${APT_GET} update -${APT_GET} install nodejs -y +# NodeSource ne publie que amd64, arm64 et armhf : vérifié, binary-s390x +# répond 404. Ajouter le dépôt sur une autre architecture n'apporte rien et +# fait échouer « apt update » sur un index introuvable. La distribution +# fournit alors nodejs elle-même — Ubuntu 26.04 s390x livre la 22.22.1, ce qui +# convient. +NODE_ARCH="$(dpkg --print-architecture)" +case "${NODE_ARCH}" in + amd64 | arm64 | armhf) + echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_$NODE_MAJOR.x nodistro main" | sudo tee /etc/apt/sources.list.d/nodesource.list + ${APT_GET} update + ;; + *) + echo "NodeSource ne publie pas pour ${NODE_ARCH} : nodejs vient de la distribution." + sudo rm -f /etc/apt/sources.list.d/nodesource.list + ${APT_GET} update + # Le paquet « nodejs » de NodeSource embarque npm ; celui d'Ubuntu NON — + # vérifié : /usr/bin/npm est absent du paquet sur jammy, noble et questing. + # Sans cette ligne, tout ce qui suit tombe sur « npm: command not found ». + NODE_PKG="nodejs npm" + ;; +esac +${APT_GET} install ${NODE_PKG:-nodejs} -y -sudo npm install npm@latest -g -retVal=$? -if [[ $retVal -ne 0 ]]; then - echo "npm install npm lastest installation error." +node_major() { + node --version 2>/dev/null | sed -n 's/^v\([0-9]*\).*/\1/p' +} + +# Le node de la distribution suffit sur les versions récentes, pas sur les +# anciennes : focal livre la 10, jammy la 12, alors que « less » exige node 18 +# et « rtlcss » node 12. Là où NodeSource n'a rien à offrir, l'archive +# officielle de nodejs.org prend le relais — elle publie bien linux-s390x, +# vérifié dans son index. Rien n'est téléchargé si la distribution suffit. +NODE_MIN=18 +node_have="$(node_major)" +if [[ -n "${NODE_PKG}" && ! (${node_have:-0} -ge ${NODE_MIN}) ]]; then + echo "node $(node --version 2>/dev/null || echo absent) trop ancien (< ${NODE_MIN}) : archive officielle Node ${NODE_MAJOR}." + case "${NODE_ARCH}" in + # Node 22 publie linux-{x64,arm64,armv7l,ppc64le,s390x} — vérifié dans son + # index. Pas de riscv64 : cette architecture garde le node de sa + # distribution, et le message plus bas le dit. + s390x) NODE_DIST_ARCH=s390x ;; + ppc64el) NODE_DIST_ARCH=ppc64le ;; + *) NODE_DIST_ARCH="" ;; + esac + NODE_VER="$(curl -fsSL --max-time 60 https://nodejs.org/dist/index.json | + grep -o "\"version\":\"v${NODE_MAJOR}\.[0-9.]*\"" | head -1 | cut -d'"' -f4)" + if [[ -z "${NODE_DIST_ARCH}" || -z "${NODE_VER}" ]]; then + echo "Pas d'archive Node officielle pour ${NODE_ARCH} : on garde celle de la distribution." + else + NODE_TGZ="node-${NODE_VER}-linux-${NODE_DIST_ARCH}.tar.xz" + NODE_TMP="$(mktemp -d)" + if curl -fsSL --max-time 600 -o "${NODE_TMP}/${NODE_TGZ}" \ + "https://nodejs.org/dist/${NODE_VER}/${NODE_TGZ}" && + curl -fsSL --max-time 120 -o "${NODE_TMP}/SHASUMS256.txt" \ + "https://nodejs.org/dist/${NODE_VER}/SHASUMS256.txt" && + (cd "${NODE_TMP}" && grep " ${NODE_TGZ}\$" SHASUMS256.txt | sha256sum -c -); then + # Un « npm install npm@latest -g » antérieur a pu déposer un npm + # incompatible ici : l'archive écrase les fichiers mais ne supprime pas + # ceux qu'elle n'a pas, ce qui laisserait un mélange des deux versions. + sudo rm -rf /usr/local/lib/node_modules/npm + sudo tar -xJf "${NODE_TMP}/${NODE_TGZ}" -C /usr/local --strip-components=1 + hash -r + echo "node $(node --version) / npm $(npm --version) installés dans /usr/local." + else + echo "Telechargement de Node ${NODE_VER} impossible : on garde celle de la distribution." + fi + rm -rf "${NODE_TMP}" + fi +fi + +if ! command -v npm >/dev/null 2>&1; then + echo "npm est introuvable apres l'installation de nodejs (${NODE_ARCH})." exit 1 fi + +# « npm@latest » dépasse régulièrement le node en place : sur Ubuntu 26.04 +# s390x, npm 12 exige node ^22.22.2 alors que l'archive livre la 22.22.1 — un +# correctif d'écart, et EBADENGINE. Pire sur un node ancien : npm 6 n'applique +# PAS « engines », il se contente d'avertir, installe npm 12 par-dessus et le +# rend inutilisable (« Cannot find module 'node:path' », absent avant node +# 14.18). Le npm livré AVEC node est compatible par construction ; cette mise à +# niveau n'est qu'un confort, on ne la tente que si node est assez récent. +if [[ "$(node_major)" =~ ^[0-9]+$ && $(node_major) -ge ${NODE_MAJOR} ]]; then + sudo npm install npm@latest -g + retVal=$? + if [[ $retVal -ne 0 ]]; then + echo "Avertissement : npm n'a pas pu être mis à niveau, on garde $(npm --version)." + fi +else + echo "npm $(npm --version) conservé : node $(node --version) est en deçà de ${NODE_MAJOR}." +fi sudo npm install -g rtlcss retVal=$? if [[ $retVal -ne 0 ]]; then