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>
254 lines
11 KiB
Bash
Executable File
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 ""
|