#!/usr/bin/env bash set -Eeuo pipefail trap 'printf "Erreur: echec de la commande [%s] a la ligne %s.\n" "$BASH_COMMAND" "$LINENO" >&2' ERR usage() { cat <<'EOF' Usage: ./scripts/update-proxmox-lxc.sh \ --proxmox-host 192.168.1.10 \ --proxmox-user root@pam \ --proxmox-password 'motdepasse' \ --ctid 120 ./scripts/update-proxmox-lxc.sh --local --ctid 120 Options principales: --proxmox-host IP ou nom DNS du serveur Proxmox --proxmox-user Utilisateur SSH Proxmox (defaut: root@pam) --proxmox-password Mot de passe SSH Proxmox --ssh-port Port SSH Proxmox (defaut: 22) --local Execute directement sur l'hote Proxmox local --ctid CTID du LXC a mettre a jour --hostname Nom du LXC si le CTID n'est pas fourni (defaut: chesscubing-web) --branch Branche Git a deployer (defaut: main) --ethan-repo-url Depot Git de l'application Ethan --ethan-branch Branche Git de l'application Ethan (defaut: main) -h, --help Affiche cette aide EOF } die() { printf 'Erreur: %s\n' "$*" >&2 exit 1 } need_cmd() { command -v "$1" >/dev/null 2>&1 || die "La commande '$1' est requise." } PROXMOX_HOST="" PROXMOX_USER="root@pam" PROXMOX_PASSWORD="${PROXMOX_PASSWORD:-}" PROXMOX_PORT="22" LOCAL_MODE="0" CTID="" LXC_HOSTNAME="chesscubing-web" REPO_BRANCH="main" ETHAN_REPO_URL="https://git.jeannerot.fr/Mineloulou/Chesscubing.git" ETHAN_REPO_BRANCH="main" while [[ $# -gt 0 ]]; do case "$1" in --proxmox-host) PROXMOX_HOST="${2:-}" shift 2 ;; --proxmox-user) PROXMOX_USER="${2:-}" shift 2 ;; --proxmox-password) PROXMOX_PASSWORD="${2:-}" shift 2 ;; --ssh-port) PROXMOX_PORT="${2:-}" shift 2 ;; --local) LOCAL_MODE="1" shift ;; --ctid) CTID="${2:-}" shift 2 ;; --hostname) LXC_HOSTNAME="${2:-}" shift 2 ;; --branch) REPO_BRANCH="${2:-}" shift 2 ;; --ethan-repo-url) ETHAN_REPO_URL="${2:-}" shift 2 ;; --ethan-branch) ETHAN_REPO_BRANCH="${2:-}" shift 2 ;; -h | --help) usage exit 0 ;; *) die "Option inconnue: $1" ;; esac done if [[ "$LOCAL_MODE" != "1" && -z "$PROXMOX_HOST" ]]; then if command -v pct >/dev/null 2>&1 && command -v pveam >/dev/null 2>&1; then LOCAL_MODE="1" fi fi payload_script="$(mktemp)" cleanup() { rm -f "$payload_script" } trap cleanup EXIT cat >"$payload_script" <<'REMOTE' set -Eeuo pipefail trap 'printf "Erreur: echec de la commande [%s] a la ligne %s.\n" "$BASH_COMMAND" "$LINENO" >&2' ERR ctid="$1" lxc_hostname="$2" repo_branch="$3" ethan_repo_url="$4" ethan_repo_branch="$5" die() { printf 'Erreur: %s\n' "$*" >&2 exit 1 } find_ctid_by_hostname() { local wanted="$1" local candidate="" local candidate_hostname="" while read -r candidate; do [[ -n "$candidate" ]] || continue candidate_hostname="$(pct config "$candidate" 2>/dev/null | awk -F ': ' '/^hostname:/ { print $2; exit }')" if [[ "$candidate_hostname" == "$wanted" ]]; then printf '%s\n' "$candidate" return 0 fi done < <(pct list | awk 'NR > 1 { print $1 }') return 1 } command -v pct >/dev/null 2>&1 || die "La commande 'pct' est absente sur Proxmox." if [[ -z "$ctid" ]]; then ctid="$(find_ctid_by_hostname "$lxc_hostname" || true)" fi [[ -n "$ctid" ]] || die "Impossible de retrouver le LXC. Passe --ctid ou --hostname." if ! pct status "$ctid" >/dev/null 2>&1; then die "Le conteneur $ctid est introuvable." fi detected_hostname="$(pct config "$ctid" 2>/dev/null | awk -F ': ' '/^hostname:/ { print $2; exit }')" if [[ -n "$detected_hostname" ]]; then lxc_hostname="$detected_hostname" fi if ! pct status "$ctid" | grep -q "running"; then pct start "$ctid" sleep 5 fi ct_exec() { pct exec "$ctid" -- bash -lc "$1" } ct_exec "cat > /usr/local/bin/update-chesscubing <<'SCRIPT' #!/usr/bin/env bash set -Eeuo pipefail trap 'printf \"Erreur: echec de la commande [%s] a la ligne %s.\\n\" \"\$BASH_COMMAND\" \"\$LINENO\" >&2' ERR main_repo_dir='/opt/chesscubing/repo' ethan_repo_dir='/opt/chesscubing/ethan-repo' web_root='/var/www/chesscubing/current' main_branch=\"\${1:-${repo_branch}}\" ethan_repo_url='${ethan_repo_url}' ethan_branch='${ethan_repo_branch}' sync_git_repo() { local repo_dir=\"\$1\" local repo_url=\"\$2\" local branch=\"\$3\" local label=\"\$4\" if [[ -d \"\$repo_dir/.git\" ]]; then cd \"\$repo_dir\" if ! git diff --quiet || ! git diff --cached --quiet; then echo \"Le depot \${label} contient des modifications locales. Mise a jour annulee.\" >&2 exit 1 fi git fetch origin \"\$branch\" if git show-ref --verify --quiet \"refs/heads/\$branch\"; then git checkout \"\$branch\" else git checkout -b \"\$branch\" --track \"origin/\$branch\" fi git pull --ff-only origin \"\$branch\" return 0 fi [[ -n \"\$repo_url\" ]] || { echo \"Le depot \${label} est absent et aucune URL n'a ete fournie.\" >&2 exit 1 } rm -rf \"\$repo_dir\" git clone --branch \"\$branch\" --single-branch \"\$repo_url\" \"\$repo_dir\" } publish_static_tree() { local source_dir=\"\$1\" local destination_dir=\"\$2\" install -d -m 0755 \"\$destination_dir\" rsync -a --delete \ --include='*/' \ --include='*.html' \ --include='*.css' \ --include='*.js' \ --include='*.mjs' \ --include='*.png' \ --include='*.jpg' \ --include='*.jpeg' \ --include='*.svg' \ --include='*.webp' \ --include='*.ico' \ --include='*.pdf' \ --include='*.webmanifest' \ --exclude='*' \ \"\$source_dir/\" \"\$destination_dir/\" } sync_git_repo \"\$main_repo_dir\" '' \"\$main_branch\" 'principal' sync_git_repo \"\$ethan_repo_dir\" \"\$ethan_repo_url\" \"\$ethan_branch\" 'Ethan' asset_version=\"\$(git -C \"\$main_repo_dir\" rev-parse --short HEAD)-\$(git -C \"\$ethan_repo_dir\" rev-parse --short HEAD)\" install -d -m 0755 \"\$web_root\" publish_static_tree \"\$main_repo_dir\" \"\$web_root\" publish_static_tree \"\$ethan_repo_dir\" \"\$web_root/ethan\" while IFS= read -r -d '' html_file; do LC_ALL=C LANG=C ASSET_VERSION=\"\$asset_version\" perl -0pi -e 's{((?:href|src)=\")(?!https?://|data:|//)([^\"?]+?\.(?:css|js|mjs|png|jpg|jpeg|svg|webp|ico|pdf|webmanifest))(?:\?[^\"]*)?(\")}{\$1 . \$2 . \"?v=\" . \$ENV{ASSET_VERSION} . \$3}ge' \"\$html_file\" done < <(find \"\$web_root\" -type f -name '*.html' -print0) chown -R www-data:www-data \"\$web_root\" nginx -t systemctl reload nginx SCRIPT chmod +x /usr/local/bin/update-chesscubing" ct_exec "cat > /etc/nginx/sites-available/chesscubing.conf <<'NGINX' server { listen 80; listen [::]:80; server_name _; root /var/www/chesscubing/current; index index.html; location = /ethan { return 301 \$scheme://\$http_host/ethan/; } location /ethan/ { try_files \$uri \$uri/ /ethan/index.html; } location / { try_files \$uri \$uri/ /index.html; } location ~* \.(?:css|js|mjs|png|jpg|jpeg|svg|webp|ico|pdf|webmanifest)$ { expires -1; add_header Cache-Control 'no-cache, no-store, must-revalidate'; } } NGINX rm -f /etc/nginx/sites-enabled/default ln -sf /etc/nginx/sites-available/chesscubing.conf /etc/nginx/sites-enabled/chesscubing.conf" pct exec "$ctid" -- /usr/local/bin/update-chesscubing "$repo_branch" container_ip="$(pct exec "$ctid" -- bash -lc "hostname -I | awk '{print \$1}'" 2>/dev/null | tr -d '\r' || true)" cat <} EOF REMOTE if [[ "$LOCAL_MODE" == "1" ]]; then printf 'Mise a jour du LXC ChessCubing en local sur cet hote Proxmox...\n' bash "$payload_script" \ "$CTID" \ "$LXC_HOSTNAME" \ "$REPO_BRANCH" \ "$ETHAN_REPO_URL" \ "$ETHAN_REPO_BRANCH" exit 0 fi [[ -n "$PROXMOX_HOST" ]] || die "Merci de fournir --proxmox-host." [[ -n "$PROXMOX_USER" ]] || die "Merci de fournir --proxmox-user." if [[ -z "$PROXMOX_PASSWORD" ]]; then read -rsp "Mot de passe SSH pour ${PROXMOX_USER}@${PROXMOX_HOST}: " PROXMOX_PASSWORD echo fi need_cmd ssh need_cmd sshpass printf 'Mise a jour du LXC ChessCubing sur %s...\n' "$PROXMOX_HOST" sshpass -p "$PROXMOX_PASSWORD" \ ssh \ -p "$PROXMOX_PORT" \ -o StrictHostKeyChecking=accept-new \ -o PreferredAuthentications=password \ -o PubkeyAuthentication=no \ "$PROXMOX_USER@$PROXMOX_HOST" \ bash -s -- \ "$CTID" \ "$LXC_HOSTNAME" \ "$REPO_BRANCH" \ "$ETHAN_REPO_URL" \ "$ETHAN_REPO_BRANCH" < "$payload_script"