Files
rpa_vision_v3/deploy/build_package_full.sh
Dom 94fd93ad19 chore(build): verifie anyio+typing_extensions dans l'embed (deps transitives httpx)
Le check de completude embed ne verifiait que httpx/httpcore/h11 ; anyio et
typing_extensions (requis par httpx 0.28.1 sous py<3.13) manquaient => import
httpx aurait pu casser a l'install malgre un build vert.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 23:48:40 +02:00

254 lines
11 KiB
Bash
Executable File

#!/bin/bash
# ============================================================
# build_package_full.sh — Construit le ZIP Lea COMPLET autoportant
# ------------------------------------------------------------
#
# Produit : deploy/build/Lea_full_v<version>.zip
#
# Ce ZIP est destine a etre servi par le dashboard Fleet
# (web_dashboard/app.py -> /api/fleet/download/<machine_id>).
# Contrairement a deploy/Lea_v1.0.0.zip (sources seules, suppose
# Python systeme), ce ZIP est 100% autonome :
#
# - Code source Lea A JOUR (working tree courant du repo,
# via build_package.sh : agent_v0/agent_v1, lea_ui, run_agent_v1)
# - Runtime Python 3.12 embedded complet (python-embed/)
# avec toutes les dependances pre-installees (mss, pynput,
# pystray, plyer, requests, PIL, pywin32, socketio...)
# - Lea.bat pointant directement sur python-embed\pythonw.exe
# (version embedded de configure_embed.ps1 : ni venv, ni pip,
# ni reseau, ni Python systeme)
# - python312._pth patche (import site active)
# - Lea/config.txt placeholder (CONFIGURE_ME) que le dashboard
# remplace a la volee par la config de l'agent
# - PAS de install.bat (plus aucune etape d'installation Python)
#
# Experience utilisateur cible (non-IT) :
# dezipper -> double-clic Lea.bat -> Lea demarre dans le systray.
# Aucune installation de Python, aucun UAC.
#
# Usage :
# ./deploy/build_package_full.sh # Build complet
# ./deploy/build_package_full.sh --clean # Nettoyer avant
#
# Pre-requis :
# - bash, rsync, zip
# - deploy/installer/python-3.12-embed/ (runtime embedded, ~80 Mo,
# non versionne — restaure depuis lea_python_embed_working.tgz si absent)
# ============================================================
set -euo pipefail
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
RED='\033[0;31m'
NC='\033[0m'
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" # deploy/
PROJECT_ROOT="$(dirname "$SCRIPT_DIR")" # racine repo
INSTALLER_DIR="$SCRIPT_DIR/installer"
STAGING_DIR="$SCRIPT_DIR/build/installer_staging"
BUILD_DIR="$SCRIPT_DIR/build"
ASSEMBLY_DIR="$BUILD_DIR/Lea_full_assembly" # arborescence Lea/ temporaire
# Version lue depuis la source courante.
# NB : la ligne peut etre soit AGENT_VERSION = "1.0.1" soit
# AGENT_VERSION = os.environ.get("RPA_AGENT_VERSION", "1.0.1").
# La regex de build_package.sh/build_installer.sh ne gere QUE la 1ere forme
# (et retombe sur 1.0.0 pour la 2e). Ici on prend le DERNIER litteral entre
# guillemets de la ligne AGENT_VERSION (= la valeur par defaut effective),
# pour nommer le ZIP de maniere stable quelle que soit la forme.
VERSION=$(grep -m1 'AGENT_VERSION' "$PROJECT_ROOT/agent_v0/agent_v1/config.py" \
| grep -oP '"[^"]+"' | tr -d '"' | tail -1)
VERSION="${VERSION:-1.0.0}"
OUTPUT_ZIP="$BUILD_DIR/Lea_full_v${VERSION}.zip"
echo -e "${GREEN}============================================================${NC}"
echo -e "${GREEN} Build ZIP Lea COMPLET autoportant v${VERSION}${NC}"
echo -e "${GREEN}============================================================${NC}"
echo ""
CLEAN=0
for arg in "$@"; do
case "$arg" in
--clean) CLEAN=1 ;;
*) echo "Argument inconnu : $arg" ;;
esac
done
# ---------------------------------------------------------------
# 1. Regenerer le staging depuis la SOURCE COURANTE du repo
# build_installer.sh --stage-only appelle build_package.sh
# (qui copie agent_v0/agent_v1, lea_ui, run_agent_v1.py courants)
# puis ajoute python-3.12-embed/ + helpers, et exclut install.bat.
#
# --clean est TOUJOURS force : sans lui, build_installer.sh reutilise
# un deploy/build/Lea/ deja present (cache du build precedent) et ne
# re-execute PAS build_package.sh -> la source embarquee serait perimee.
# On veut au contraire garantir le working tree COURANT du repo.
# ---------------------------------------------------------------
echo "[1/6] Regeneration du staging depuis la source courante (--clean force)..."
bash "$INSTALLER_DIR/build_installer.sh" --stage-only --clean
if [[ ! -d "$STAGING_DIR" ]]; then
echo -e "${RED} ERREUR : staging $STAGING_DIR absent apres build_installer.sh${NC}"
exit 1
fi
echo " Staging pret : $STAGING_DIR"
echo ""
# ---------------------------------------------------------------
# 2. Assembler l'arborescence Lea/ (prefixe attendu par le dashboard
# qui remplace exactement 'Lea/config.txt').
# ---------------------------------------------------------------
echo "[2/6] Assemblage de l'arborescence Lea/..."
rm -rf "$ASSEMBLY_DIR"
mkdir -p "$ASSEMBLY_DIR/Lea"
# Copier le staging, en renommant python-3.12-embed -> python-embed
# (chemin attendu par le Lea.bat embedded : %~dp0python-embed\pythonw.exe)
rsync -a \
--exclude='python-3.12-embed' \
--exclude='install.bat' \
--exclude='config.txt' \
"$STAGING_DIR/" \
"$ASSEMBLY_DIR/Lea/"
rsync -a "$STAGING_DIR/python-3.12-embed/" "$ASSEMBLY_DIR/Lea/python-embed/"
echo " Source + python-embed/ assembles"
echo ""
# ---------------------------------------------------------------
# 3. Lea.bat embedded : extraire le bloc canonique de configure_embed.ps1
# (le here-string $NewLeaBat). C'est la SEULE source de verite du
# Lea.bat embedded ; on ne le duplique pas dans ce script.
# ---------------------------------------------------------------
echo "[3/6] Generation de Lea.bat (runtime embedded)..."
LEA_BAT_OUT="$ASSEMBLY_DIR/Lea/Lea.bat"
python3 - "$INSTALLER_DIR/configure_embed.ps1" "$LEA_BAT_OUT" <<'PYEOF'
import sys, re
ps1_path, out_path = sys.argv[1], sys.argv[2]
text = open(ps1_path, encoding="utf-8").read()
# Extrait le here-string PowerShell : $NewLeaBat = @" ... "@
m = re.search(r'\$NewLeaBat\s*=\s*@"\r?\n(.*?)\r?\n"@', text, re.DOTALL)
if not m:
sys.exit("ERREUR : bloc $NewLeaBat introuvable dans configure_embed.ps1")
content = m.group(1)
# CRLF pour un .bat Windows
content = content.replace("\r\n", "\n").replace("\n", "\r\n")
if not content.endswith("\r\n"):
content += "\r\n"
open(out_path, "wb").write(content.encode("ascii"))
print(f" Lea.bat genere depuis configure_embed.ps1 ({len(content)} octets)")
PYEOF
# Installateur 1-clic non-IT (raccourci Bureau + Demarrage automatique,
# per-user, sans admin). Asset statique CRLF/ASCII copie tel quel dans Lea/.
INSTALLER_BAT_SRC="$INSTALLER_DIR/Installer-Lea.bat"
if [[ ! -f "$INSTALLER_BAT_SRC" ]]; then
echo -e "${RED} ERREUR : $INSTALLER_BAT_SRC introuvable${NC}"
exit 1
fi
cp "$INSTALLER_BAT_SRC" "$ASSEMBLY_DIR/Lea/Installer-Lea.bat"
echo " Installer-Lea.bat (installation 1-clic) ajoute"
# Notice utilisateur dediee a l'install autonome (remplace la LISEZMOI legacy
# du staging, qui decrit l'ancien flux install.bat + Python systeme).
LISEZMOI_SRC="$INSTALLER_DIR/LISEZMOI-autonome.txt"
if [[ -f "$LISEZMOI_SRC" ]]; then
cp "$LISEZMOI_SRC" "$ASSEMBLY_DIR/Lea/LISEZMOI.txt"
echo " LISEZMOI.txt (version install autonome) pose"
fi
echo ""
# ---------------------------------------------------------------
# 4. Patcher python312._pth (import site active) — idempotent.
# Necessaire pour que l'embed charge site-packages.
# ---------------------------------------------------------------
echo "[4/6] Patch python312._pth (import site)..."
PTH_FILE=$(find "$ASSEMBLY_DIR/Lea/python-embed" -name "python*._pth" | head -1)
if [[ -z "$PTH_FILE" ]]; then
echo -e "${RED} ERREUR : python*._pth introuvable dans python-embed/${NC}"
exit 1
fi
# Decommente '#import site' s'il est commente ; sinon laisse tel quel.
sed -i 's/^#import site/import site/' "$PTH_FILE"
if ! grep -q '^import site' "$PTH_FILE"; then
printf 'import site\r\n' >> "$PTH_FILE"
fi
echo " $(basename "$PTH_FILE") : import site actif"
echo ""
# ---------------------------------------------------------------
# 5. config.txt placeholder (CONFIGURE_ME) — cible de l'injection
# dashboard (app.py remplace 'Lea/config.txt').
# ---------------------------------------------------------------
echo "[5/6] Pose du config.txt placeholder..."
cp "$INSTALLER_DIR/../lea_package/config.txt" "$ASSEMBLY_DIR/Lea/config.txt"
if ! grep -q 'CONFIGURE_ME' "$ASSEMBLY_DIR/Lea/config.txt"; then
echo -e "${YELLOW} AVERTISSEMENT : config.txt ne contient pas CONFIGURE_ME (placeholder inattendu)${NC}"
fi
echo " Lea/config.txt (placeholder) pose"
echo ""
# ---------------------------------------------------------------
# 6. Validation de completude AVANT zip (un ZIP incomplet = install
# cassee chez le client non-IT).
# ---------------------------------------------------------------
echo "[6/6] Validation + creation du ZIP..."
REQUIRED=(
"Lea/run_agent_v1.py"
"Lea/agent_v1/config.py"
"Lea/agent_v1/main.py"
"Lea/lea_ui/server_client.py"
"Lea/Lea.bat"
"Lea/Installer-Lea.bat"
"Lea/config.txt"
"Lea/python-embed/python.exe"
"Lea/python-embed/pythonw.exe"
"Lea/python-embed/Lib/site-packages/mss"
"Lea/python-embed/Lib/site-packages/win32"
"Lea/python-embed/Lib/site-packages/socketio"
"Lea/python-embed/Lib/site-packages/httpx"
"Lea/python-embed/Lib/site-packages/httpcore"
"Lea/python-embed/Lib/site-packages/h11"
"Lea/python-embed/Lib/site-packages/anyio"
"Lea/python-embed/Lib/site-packages/typing_extensions.py"
MISSING=()
for f in "${REQUIRED[@]}"; do
[[ -e "$ASSEMBLY_DIR/$f" ]] || MISSING+=("$f")
done
# install.bat NE DOIT PAS etre present
if [[ -e "$ASSEMBLY_DIR/Lea/install.bat" ]]; then
echo -e "${RED} ERREUR : install.bat present dans l'assemblage (doit etre absent).${NC}"
exit 1
fi
if [[ ${#MISSING[@]} -gt 0 ]]; then
echo -e "${RED} ERREUR : assemblage incomplet. Manquants :${NC}"
printf ' - %s\n' "${MISSING[@]}"
exit 1
fi
echo " Completude verifiee (${#REQUIRED[@]} elements, install.bat absent)"
# Verif source A JOUR : le config.py embarque doit etre identique au repo
if ! diff -q "$PROJECT_ROOT/agent_v0/agent_v1/config.py" "$ASSEMBLY_DIR/Lea/agent_v1/config.py" >/dev/null; then
echo -e "${RED} ERREUR : agent_v1/config.py embarque DIFFERE de la source repo !${NC}"
echo " Le ZIP n'embarque pas la source a jour — build interrompu."
exit 1
fi
echo " Source a jour confirmee (agent_v1/config.py == repo)"
rm -f "$OUTPUT_ZIP"
( cd "$ASSEMBLY_DIR" && zip -q -r -X "$OUTPUT_ZIP" Lea )
ZIP_SIZE=$(du -h "$OUTPUT_ZIP" | cut -f1)
echo ""
echo -e "${GREEN}============================================================${NC}"
echo -e "${GREEN} ZIP complet produit !${NC}"
echo -e "${GREEN}============================================================${NC}"
echo ""
echo " Fichier : $OUTPUT_ZIP"
echo " Taille : $ZIP_SIZE"
echo ""
echo " Servi par le dashboard via web_dashboard/app.py (_LEA_ZIP_TEMPLATE)."
echo " L'utilisateur : dezippe -> double-clic Lea.bat (aucun Python systeme requis)."
echo ""