Aligne le deploiement Proxmox sur la stack Docker complete

This commit is contained in:
2026-04-14 20:32:39 +02:00
parent 5cf46dce31
commit d36da7c993
5 changed files with 543 additions and 252 deletions

View File

@@ -1,3 +1,5 @@
WEB_PORT=8080
PUBLIC_BASE_URL=http://localhost:8080
KEYCLOAK_DB_NAME=keycloak KEYCLOAK_DB_NAME=keycloak
KEYCLOAK_DB_USER=keycloak KEYCLOAK_DB_USER=keycloak
KEYCLOAK_DB_PASSWORD=change-me KEYCLOAK_DB_PASSWORD=change-me

View File

@@ -76,6 +76,7 @@ Identifiants d'administration par defaut pour le premier demarrage local :
Ces valeurs peuvent etre surchargees via les variables d'environnement de `.env.example`. Ces valeurs peuvent etre surchargees via les variables d'environnement de `.env.example`.
La base MySQL du site utilise les variables `SITE_DB_*` du meme fichier. La base MySQL du site utilise les variables `SITE_DB_*` du meme fichier.
Pour un deploiement hors localhost, `PUBLIC_BASE_URL` doit pointer vers l'URL publique du site et `WEB_PORT` vers le port HTTP expose.
Au demarrage, le service `keycloak-init` resynchronise automatiquement le realm courant pour garder l'inscription active et autoriser le flux de connexion integre, meme si la base Keycloak existe deja. Au demarrage, le service `keycloak-init` resynchronise automatiquement le realm courant pour garder l'inscription active et autoriser le flux de connexion integre, meme si la base Keycloak existe deja.
@@ -99,9 +100,11 @@ Prerrequis sur la machine qui lance les scripts :
- en mode distant : `ssh` et `sshpass` - en mode distant : `ssh` et `sshpass`
- en mode local sur l'hote Proxmox : aucun paquet supplementaire n'est installe sur Proxmox - en mode local sur l'hote Proxmox : aucun paquet supplementaire n'est installe sur Proxmox
Le deploiement dans le LXC n'utilise pas Docker. Le script clone le depot, publie l'application Blazor dans le conteneur, puis sert le resultat via `nginx`. Le deploiement dans le LXC Proxmox utilise maintenant Docker dans le conteneur pour lancer la meme stack qu'en local : `web`, `auth`, `keycloak`, `postgres` et `mysql`.
Attention : la pile Keycloak fournie ici est actuellement prete a l'emploi dans la stack Docker du projet. Les scripts LXC existants ne provisionnent pas encore automatiquement Keycloak ni sa base Postgres. Le script prepare une URL publique pour Keycloak via `PUBLIC_BASE_URL`, installe Docker dans le LXC, puis lance `docker compose up -d --build`.
Pour un usage confortable, il est recommande de prevoir un LXC avec au moins 2 vCPU, 4 Go de RAM et 10 Go de disque.
### Installer un nouveau LXC ### Installer un nouveau LXC

View File

@@ -10,7 +10,7 @@ services:
keycloak: keycloak:
condition: service_started condition: service_started
ports: ports:
- "8080:80" - "${WEB_PORT:-8080}:80"
restart: unless-stopped restart: unless-stopped
auth: auth:
@@ -53,7 +53,7 @@ services:
KC_BOOTSTRAP_ADMIN_USERNAME: ${KEYCLOAK_ADMIN_USER:-admin} KC_BOOTSTRAP_ADMIN_USERNAME: ${KEYCLOAK_ADMIN_USER:-admin}
KC_BOOTSTRAP_ADMIN_PASSWORD: ${KEYCLOAK_ADMIN_PASSWORD:-admin} KC_BOOTSTRAP_ADMIN_PASSWORD: ${KEYCLOAK_ADMIN_PASSWORD:-admin}
KC_PROXY_HEADERS: xforwarded KC_PROXY_HEADERS: xforwarded
KC_HOSTNAME: http://localhost:8080/auth KC_HOSTNAME: "${PUBLIC_BASE_URL:-http://localhost:8080}/auth"
KC_HTTP_RELATIVE_PATH: /auth KC_HTTP_RELATIVE_PATH: /auth
KC_HOSTNAME_STRICT: "false" KC_HOSTNAME_STRICT: "false"
volumes: volumes:

View File

@@ -25,9 +25,9 @@ Options principales:
--gateway Passerelle si IP statique --gateway Passerelle si IP statique
--bridge Bridge reseau Proxmox (defaut: vmbr0) --bridge Bridge reseau Proxmox (defaut: vmbr0)
--cores Nombre de vCPU du LXC (defaut: 2) --cores Nombre de vCPU du LXC (defaut: 2)
--memory Memoire RAM en Mo (defaut: 1024) --memory Memoire RAM en Mo (defaut: 4096)
--swap Swap en Mo (defaut: 512) --swap Swap en Mo (defaut: 1024)
--disk-gb Taille disque du LXC en Go (defaut: 6) --disk-gb Taille disque du LXC en Go (defaut: 12)
--template-storage Stockage Proxmox pour les templates --template-storage Stockage Proxmox pour les templates
--rootfs-storage Stockage Proxmox pour le disque LXC --rootfs-storage Stockage Proxmox pour le disque LXC
--repo-url Depot Git a deployer --repo-url Depot Git a deployer
@@ -36,6 +36,11 @@ Options principales:
--ethan-branch Branche Git de l'application Ethan (defaut: main) --ethan-branch Branche Git de l'application Ethan (defaut: main)
--brice-repo-url Depot Git de l'application Brice --brice-repo-url Depot Git de l'application Brice
--brice-branch Branche Git de l'application Brice (defaut: main) --brice-branch Branche Git de l'application Brice (defaut: main)
--public-base-url URL publique du site (ex: http://jeu.example.com)
--web-port Port HTTP expose dans le LXC (defaut: 80)
--keycloak-admin-user Utilisateur admin Keycloak (defaut: admin)
--keycloak-admin-password
Mot de passe admin Keycloak. Genere si absent
--lxc-password Mot de passe root du LXC. Genere si absent --lxc-password Mot de passe root du LXC. Genere si absent
-h, --help Affiche cette aide -h, --help Affiche cette aide
@@ -68,9 +73,9 @@ LXC_IP="dhcp"
LXC_GATEWAY="" LXC_GATEWAY=""
LXC_BRIDGE="vmbr0" LXC_BRIDGE="vmbr0"
LXC_CORES="2" LXC_CORES="2"
LXC_MEMORY="1024" LXC_MEMORY="4096"
LXC_SWAP="512" LXC_SWAP="1024"
LXC_DISK_GB="6" LXC_DISK_GB="12"
TEMPLATE_STORAGE="" TEMPLATE_STORAGE=""
ROOTFS_STORAGE="" ROOTFS_STORAGE=""
REPO_URL="https://git.jeannerot.fr/christophe/chesscubing.git" REPO_URL="https://git.jeannerot.fr/christophe/chesscubing.git"
@@ -79,6 +84,10 @@ ETHAN_REPO_URL="https://git.jeannerot.fr/Mineloulou/Chesscubing.git"
ETHAN_REPO_BRANCH="main" ETHAN_REPO_BRANCH="main"
BRICE_REPO_URL="https://git.jeannerot.fr/Lescratcheur/ChessCubing.git" BRICE_REPO_URL="https://git.jeannerot.fr/Lescratcheur/ChessCubing.git"
BRICE_REPO_BRANCH="main" BRICE_REPO_BRANCH="main"
PUBLIC_BASE_URL=""
WEB_PORT="80"
KEYCLOAK_ADMIN_USER="admin"
KEYCLOAK_ADMIN_PASSWORD=""
LXC_PASSWORD="" LXC_PASSWORD=""
while [[ $# -gt 0 ]]; do while [[ $# -gt 0 ]]; do
@@ -171,6 +180,22 @@ while [[ $# -gt 0 ]]; do
BRICE_REPO_BRANCH="${2:-}" BRICE_REPO_BRANCH="${2:-}"
shift 2 shift 2
;; ;;
--public-base-url)
PUBLIC_BASE_URL="${2:-}"
shift 2
;;
--web-port)
WEB_PORT="${2:-}"
shift 2
;;
--keycloak-admin-user)
KEYCLOAK_ADMIN_USER="${2:-}"
shift 2
;;
--keycloak-admin-password)
KEYCLOAK_ADMIN_PASSWORD="${2:-}"
shift 2
;;
--lxc-password) --lxc-password)
LXC_PASSWORD="${2:-}" LXC_PASSWORD="${2:-}"
shift 2 shift 2
@@ -221,6 +246,10 @@ ethan_repo_branch="$6"
brice_repo_url="$7" brice_repo_url="$7"
brice_repo_branch="$8" brice_repo_branch="$8"
lxc_password="$9" lxc_password="$9"
public_base_url="${10}"
web_port="${11}"
keycloak_admin_user="${12}"
keycloak_admin_password="${13}"
die() { die() {
printf 'Erreur: %s\n' "$*" >&2 printf 'Erreur: %s\n' "$*" >&2
@@ -339,7 +368,8 @@ pct create "$ctid" "$template_ref" \
--onboot 1 \ --onboot 1 \
--ostype debian \ --ostype debian \
--password "$lxc_password" \ --password "$lxc_password" \
--unprivileged 1 --unprivileged 1 \
--features nesting=1,keyctl=1
pct start "$ctid" pct start "$ctid"
@@ -353,21 +383,7 @@ done
pct exec "$ctid" -- true >/dev/null 2>&1 || die "Le LXC n'est pas joignable apres le demarrage." pct exec "$ctid" -- true >/dev/null 2>&1 || die "Le LXC n'est pas joignable apres le demarrage."
printf 'Installation de nginx, git, rsync et des prerequis de build dans le conteneur...\n' ct_exec "install -d -m 0755 /opt/chesscubing/repo /opt/chesscubing/ethan-repo /opt/chesscubing/brice-repo /opt/chesscubing/deploy /opt/chesscubing/config"
ct_exec "apt-get update && apt-get install -y ca-certificates curl gpg git nginx rsync"
ct_exec "install -d -m 0755 /opt/chesscubing/repo /opt/chesscubing/ethan-repo /opt/chesscubing/brice-repo /opt/chesscubing/publish /var/www/chesscubing/current"
printf 'Clonage du depot %s...\n' "$repo_url"
ct_exec "if [ ! -d /opt/chesscubing/repo/.git ]; then \
rm -rf /opt/chesscubing/repo/* /opt/chesscubing/repo/.[!.]* /opt/chesscubing/repo/..?* 2>/dev/null || true; \
git clone --branch '$repo_branch' --single-branch '$repo_url' /opt/chesscubing/repo; \
else \
cd /opt/chesscubing/repo && \
git fetch origin '$repo_branch' && \
if git show-ref --verify --quiet 'refs/heads/$repo_branch'; then git checkout '$repo_branch'; else git checkout -b '$repo_branch' --track 'origin/$repo_branch'; fi && \
git pull --ff-only origin '$repo_branch'; \
fi"
ct_exec "cat > /usr/local/bin/update-chesscubing <<'SCRIPT' ct_exec "cat > /usr/local/bin/update-chesscubing <<'SCRIPT'
#!/usr/bin/env bash #!/usr/bin/env bash
@@ -378,30 +394,52 @@ trap 'printf \"Erreur: echec de la commande [%s] a la ligne %s.\\n\" \"\$BASH_CO
main_repo_dir='/opt/chesscubing/repo' main_repo_dir='/opt/chesscubing/repo'
ethan_repo_dir='/opt/chesscubing/ethan-repo' ethan_repo_dir='/opt/chesscubing/ethan-repo'
brice_repo_dir='/opt/chesscubing/brice-repo' brice_repo_dir='/opt/chesscubing/brice-repo'
publish_root='/opt/chesscubing/publish' deploy_dir='/opt/chesscubing/deploy'
web_root='/var/www/chesscubing/current' config_dir='/opt/chesscubing/config'
env_file=\"\$config_dir/chesscubing.env\"
main_branch=\"\${1:-${repo_branch}}\" main_branch=\"\${1:-${repo_branch}}\"
public_base_url_override=\"\${2:-}\"
web_port_override=\"\${3:-}\"
keycloak_admin_user_override=\"\${4:-}\"
keycloak_admin_password_override=\"\${5:-}\"
main_repo_url='${repo_url}'
ethan_repo_url='${ethan_repo_url}' ethan_repo_url='${ethan_repo_url}'
ethan_branch='${ethan_repo_branch}' ethan_branch='${ethan_repo_branch}'
brice_repo_url='${brice_repo_url}' brice_repo_url='${brice_repo_url}'
brice_branch='${brice_repo_branch}' brice_branch='${brice_repo_branch}'
ensure_dotnet_sdk() { random_secret() {
if command -v dotnet >/dev/null 2>&1; then od -An -N24 -tx1 /dev/urandom | tr -d ' \n'
return 0 }
ensure_base_packages() {
apt-get update
apt-get install -y ca-certificates curl gpg git rsync
}
ensure_docker_stack() {
ensure_base_packages
if ! command -v docker >/dev/null 2>&1 || ! docker compose version >/dev/null 2>&1; then
install -m 0755 -d /etc/apt/keyrings
if [[ ! -f /etc/apt/keyrings/docker.asc ]]; then
curl -fsSL https://download.docker.com/linux/debian/gpg -o /etc/apt/keyrings/docker.asc
chmod a+r /etc/apt/keyrings/docker.asc
fi
if [[ ! -f /etc/apt/sources.list.d/docker.list ]]; then
printf 'deb [arch=%s signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/debian %s stable\n' \
\"\$(dpkg --print-architecture)\" \
\"\$(. /etc/os-release && printf '%s' \"\$VERSION_CODENAME\")\" > /etc/apt/sources.list.d/docker.list
fi fi
apt-get update apt-get update
apt-get install -y ca-certificates curl gpg apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
if [[ ! -f /etc/apt/sources.list.d/microsoft-prod.list ]]; then
curl -fsSL https://packages.microsoft.com/config/debian/12/packages-microsoft-prod.deb -o /tmp/packages-microsoft-prod.deb
dpkg -i /tmp/packages-microsoft-prod.deb
rm -f /tmp/packages-microsoft-prod.deb
fi fi
apt-get update systemctl enable docker >/dev/null 2>&1 || true
apt-get install -y dotnet-sdk-10.0 systemctl restart docker
} }
sync_git_repo() { sync_git_repo() {
@@ -437,111 +475,189 @@ sync_git_repo() {
git clone --branch \"\$branch\" --single-branch \"\$repo_url\" \"\$repo_dir\" git clone --branch \"\$branch\" --single-branch \"\$repo_url\" \"\$repo_dir\"
} }
publish_blazor_app() { get_env_var() {
local repo_dir=\"\$1\" local key=\"\$1\"
local output_dir=\"\$2\"
ensure_dotnet_sdk [[ -f \"\$env_file\" ]] || return 0
rm -rf \"\$output_dir\" awk -F= -v wanted=\"\$key\" '\$1 == wanted { print substr(\$0, length(wanted) + 2); exit }' \"\$env_file\"
dotnet publish \"\$repo_dir/ChessCubing.App/ChessCubing.App.csproj\" -c Release -o \"\$output_dir\"
} }
publish_static_tree() { set_env_var() {
local source_dir=\"\$1\" local key=\"\$1\"
local destination_dir=\"\$2\" local value=\"\$2\"
install -d -m 0755 \"\$destination_dir\" touch \"\$env_file\"
if grep -q \"^\${key}=\" \"\$env_file\" 2>/dev/null; then
rsync -a --delete \ sed -i \"s|^\${key}=.*|\${key}=\${value}|\" \"\$env_file\"
--include='*/' \ else
--include='*.html' \ printf '%s=%s\n' \"\$key\" \"\$value\" >> \"\$env_file\"
--include='*.css' \
--include='*.js' \
--include='*.mjs' \
--include='*.png' \
--include='*.jpg' \
--include='*.jpeg' \
--include='*.svg' \
--include='*.webp' \
--include='*.ico' \
--include='*.pdf' \
--include='*.json' \
--include='*.webmanifest' \
--exclude='*' \
\"\$source_dir/\" \"\$destination_dir/\"
}
ensure_browser_stub() {
local destination_dir=\"\$1\"
local stub_path=\"\$destination_dir/cordova.js\"
if [[ ! -f \"\$stub_path\" ]]; then
printf '%s\n' '// Browser stub for Cordova builds.' > \"\$stub_path\"
fi fi
} }
sync_git_repo \"\$main_repo_dir\" '${repo_url}' \"\$main_branch\" 'principal' ensure_env_default() {
local key=\"\$1\"
local fallback=\"\$2\"
local value
value=\"\$(get_env_var \"\$key\")\"
if [[ -z \"\$value\" ]]; then
value=\"\$fallback\"
fi
set_env_var \"\$key\" \"\$value\"
}
normalize_public_base_url() {
local value=\"\$1\"
printf '%s\n' \"\${value%/}\"
}
build_default_public_base_url() {
local port=\"\$1\"
local detected_ip
detected_ip=\"\$(hostname -I | awk '{print \$1}')\"
[[ -n \"\$detected_ip\" ]] || return 1
if [[ \"\$port\" == \"80\" ]]; then
printf 'http://%s\n' \"\$detected_ip\"
else
printf 'http://%s:%s\n' \"\$detected_ip\" \"\$port\"
fi
}
configure_env_file() {
local current_value
local effective_web_port
local effective_public_base_url
local effective_keycloak_admin_user
local effective_keycloak_admin_password
effective_web_port=\"\$web_port_override\"
if [[ -z \"\$effective_web_port\" ]]; then
effective_web_port=\"\$(get_env_var WEB_PORT)\"
fi
if [[ -z \"\$effective_web_port\" ]]; then
effective_web_port='80'
fi
set_env_var WEB_PORT \"\$effective_web_port\"
effective_public_base_url=\"\$public_base_url_override\"
if [[ -z \"\$effective_public_base_url\" ]]; then
effective_public_base_url=\"\$(get_env_var PUBLIC_BASE_URL)\"
fi
if [[ -z \"\$effective_public_base_url\" ]]; then
effective_public_base_url=\"\$(build_default_public_base_url \"\$effective_web_port\" || true)\"
fi
if [[ -z \"\$effective_public_base_url\" ]]; then
if [[ \"\$effective_web_port\" == \"80\" ]]; then
effective_public_base_url='http://localhost'
else
effective_public_base_url=\"http://localhost:\$effective_web_port\"
fi
fi
set_env_var PUBLIC_BASE_URL \"\$(normalize_public_base_url \"\$effective_public_base_url\")\"
effective_keycloak_admin_user=\"\$keycloak_admin_user_override\"
if [[ -z \"\$effective_keycloak_admin_user\" ]]; then
effective_keycloak_admin_user=\"\$(get_env_var KEYCLOAK_ADMIN_USER)\"
fi
if [[ -z \"\$effective_keycloak_admin_user\" ]]; then
effective_keycloak_admin_user='admin'
fi
set_env_var KEYCLOAK_ADMIN_USER \"\$effective_keycloak_admin_user\"
effective_keycloak_admin_password=\"\$keycloak_admin_password_override\"
if [[ -z \"\$effective_keycloak_admin_password\" ]]; then
effective_keycloak_admin_password=\"\$(get_env_var KEYCLOAK_ADMIN_PASSWORD)\"
fi
if [[ -z \"\$effective_keycloak_admin_password\" ]]; then
effective_keycloak_admin_password=\"\$(random_secret)\"
fi
set_env_var KEYCLOAK_ADMIN_PASSWORD \"\$effective_keycloak_admin_password\"
ensure_env_default KEYCLOAK_DB_NAME keycloak
ensure_env_default KEYCLOAK_DB_USER keycloak
current_value=\"\$(get_env_var KEYCLOAK_DB_PASSWORD)\"
if [[ -z \"\$current_value\" ]]; then
current_value=\"\$(random_secret)\"
fi
set_env_var KEYCLOAK_DB_PASSWORD \"\$current_value\"
ensure_env_default SITE_DB_NAME chesscubing_site
ensure_env_default SITE_DB_USER chesscubing
current_value=\"\$(get_env_var SITE_DB_PASSWORD)\"
if [[ -z \"\$current_value\" ]]; then
current_value=\"\$(random_secret)\"
fi
set_env_var SITE_DB_PASSWORD \"\$current_value\"
current_value=\"\$(get_env_var SITE_DB_ROOT_PASSWORD)\"
if [[ -z \"\$current_value\" ]]; then
current_value=\"\$(random_secret)\"
fi
set_env_var SITE_DB_ROOT_PASSWORD \"\$current_value\"
}
sync_deploy_tree() {
install -d -m 0755 \"\$deploy_dir\" \"\$config_dir\"
rsync -a --delete \
--exclude='.git/' \
--exclude='bin/' \
--exclude='obj/' \
--exclude='.env' \
--exclude='node_modules/' \
\"\$main_repo_dir/\" \"\$deploy_dir/\"
if [[ -d \"\$ethan_repo_dir/.git\" ]]; then
rm -rf \"\$deploy_dir/ethan\"
rsync -a --delete \
--exclude='.git/' \
--exclude='node_modules/' \
\"\$ethan_repo_dir/\" \"\$deploy_dir/ethan/\"
fi
if [[ -d \"\$brice_repo_dir/.git\" ]]; then
rm -rf \"\$deploy_dir/brice\"
rsync -a --delete \
--exclude='.git/' \
--exclude='node_modules/' \
\"\$brice_repo_dir/\" \"\$deploy_dir/brice/\"
fi
}
disable_legacy_nginx() {
systemctl disable --now nginx >/dev/null 2>&1 || true
}
deploy_stack() {
cp \"\$env_file\" \"\$deploy_dir/.env\"
cd \"\$deploy_dir\"
docker compose down || true
docker compose up -d --build
docker compose ps
}
ensure_docker_stack
sync_git_repo \"\$main_repo_dir\" \"\$main_repo_url\" \"\$main_branch\" 'principal'
sync_git_repo \"\$ethan_repo_dir\" \"\$ethan_repo_url\" \"\$ethan_branch\" 'Ethan' sync_git_repo \"\$ethan_repo_dir\" \"\$ethan_repo_url\" \"\$ethan_branch\" 'Ethan'
sync_git_repo \"\$brice_repo_dir\" \"\$brice_repo_url\" \"\$brice_branch\" 'Brice' sync_git_repo \"\$brice_repo_dir\" \"\$brice_repo_url\" \"\$brice_branch\" 'Brice'
sync_deploy_tree
install -d -m 0755 \"\$web_root\" \"\$publish_root\" configure_env_file
disable_legacy_nginx
publish_blazor_app \"\$main_repo_dir\" \"\$publish_root/main\" deploy_stack
rsync -a --delete \"\$publish_root/main/wwwroot/\" \"\$web_root/\"
publish_static_tree \"\$ethan_repo_dir\" \"\$web_root/ethan\"
publish_static_tree \"\$brice_repo_dir/www\" \"\$web_root/brice\"
ensure_browser_stub \"\$web_root/brice\"
chown -R www-data:www-data \"\$web_root\"
nginx -t
systemctl reload nginx
SCRIPT SCRIPT
chmod +x /usr/local/bin/update-chesscubing" chmod +x /usr/local/bin/update-chesscubing"
ct_exec "cat > /etc/nginx/sites-available/chesscubing.conf <<'NGINX' printf 'Deploiement de la stack Docker complete dans le LXC...\n'
server { ct_exec "/usr/local/bin/update-chesscubing '$repo_branch' '$public_base_url' '$web_port' '$keycloak_admin_user' '$keycloak_admin_password'"
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 = /brice {
return 301 \$scheme://\$http_host/brice/;
}
location /brice/ {
try_files \$uri \$uri/ /brice/index.html;
}
location / {
try_files \$uri \$uri/ /index.html;
}
location ~* \.(?:css|js|json|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"
printf 'Publication du site dans le LXC...\n'
ct_exec "/usr/local/bin/update-chesscubing '$repo_branch'"
ct_exec "systemctl enable nginx >/dev/null && systemctl restart nginx"
container_ip="$(pct exec "$ctid" -- bash -lc "hostname -I | awk '{print \$1}'" 2>/dev/null | tr -d '\r' || true)" container_ip="$(pct exec "$ctid" -- bash -lc "hostname -I | awk '{print \$1}'" 2>/dev/null | tr -d '\r' || true)"
public_url="$(pct exec "$ctid" -- bash -lc "awk -F= '/^PUBLIC_BASE_URL=/{print substr(\$0, 17); exit}' /opt/chesscubing/config/chesscubing.env" 2>/dev/null | tr -d '\r' || true)"
effective_keycloak_admin_user="$(pct exec "$ctid" -- bash -lc "awk -F= '/^KEYCLOAK_ADMIN_USER=/{print substr(\$0, 21); exit}' /opt/chesscubing/config/chesscubing.env" 2>/dev/null | tr -d '\r' || true)"
effective_keycloak_admin_password="$(pct exec "$ctid" -- bash -lc "awk -F= '/^KEYCLOAK_ADMIN_PASSWORD=/{print substr(\$0, 25); exit}' /opt/chesscubing/config/chesscubing.env" 2>/dev/null | tr -d '\r' || true)"
cat <<EOF cat <<EOF
@@ -549,7 +665,10 @@ Installation terminee.
- CTID: $ctid - CTID: $ctid
- Nom du LXC: $lxc_hostname - Nom du LXC: $lxc_hostname
- Mot de passe root du LXC: $lxc_password - Mot de passe root du LXC: $lxc_password
- URL probable: http://${container_ip:-<ip_du_lxc>} - IP detectee du LXC: ${container_ip:-<ip_du_lxc>}
- URL publique configuree: ${public_url:-http://${container_ip:-<ip_du_lxc>}}
- Admin Keycloak: ${effective_keycloak_admin_user:-admin}
- Mot de passe admin Keycloak: ${effective_keycloak_admin_password:-<voir /opt/chesscubing/config/chesscubing.env>}
Pour mettre a jour l'application plus tard: Pour mettre a jour l'application plus tard:
./scripts/update-proxmox-lxc.sh --proxmox-host <ip-proxmox> --proxmox-user <user> --proxmox-password '<motdepasse>' --ctid $ctid ./scripts/update-proxmox-lxc.sh --proxmox-host <ip-proxmox> --proxmox-user <user> --proxmox-password '<motdepasse>' --ctid $ctid
@@ -576,7 +695,11 @@ if [[ "$LOCAL_MODE" == "1" ]]; then
"$ETHAN_REPO_BRANCH" \ "$ETHAN_REPO_BRANCH" \
"$BRICE_REPO_URL" \ "$BRICE_REPO_URL" \
"$BRICE_REPO_BRANCH" \ "$BRICE_REPO_BRANCH" \
"$LXC_PASSWORD" "$LXC_PASSWORD" \
"$PUBLIC_BASE_URL" \
"$WEB_PORT" \
"$KEYCLOAK_ADMIN_USER" \
"$KEYCLOAK_ADMIN_PASSWORD"
exit 0 exit 0
fi fi
@@ -618,4 +741,8 @@ sshpass -p "$PROXMOX_PASSWORD" \
"$ETHAN_REPO_BRANCH" \ "$ETHAN_REPO_BRANCH" \
"$BRICE_REPO_URL" \ "$BRICE_REPO_URL" \
"$BRICE_REPO_BRANCH" \ "$BRICE_REPO_BRANCH" \
"$LXC_PASSWORD" < "$payload_script" "$LXC_PASSWORD" \
"$PUBLIC_BASE_URL" \
"$WEB_PORT" \
"$KEYCLOAK_ADMIN_USER" \
"$KEYCLOAK_ADMIN_PASSWORD" < "$payload_script"

View File

@@ -22,11 +22,17 @@ Options principales:
--local Execute directement sur l'hote Proxmox local --local Execute directement sur l'hote Proxmox local
--ctid CTID du LXC a mettre a jour --ctid CTID du LXC a mettre a jour
--hostname Nom du LXC si le CTID n'est pas fourni (defaut: chesscubing-web) --hostname Nom du LXC si le CTID n'est pas fourni (defaut: chesscubing-web)
--repo-url Depot Git principal a deployer
--branch Branche Git a deployer (defaut: main) --branch Branche Git a deployer (defaut: main)
--ethan-repo-url Depot Git de l'application Ethan --ethan-repo-url Depot Git de l'application Ethan
--ethan-branch Branche Git de l'application Ethan (defaut: main) --ethan-branch Branche Git de l'application Ethan (defaut: main)
--brice-repo-url Depot Git de l'application Brice --brice-repo-url Depot Git de l'application Brice
--brice-branch Branche Git de l'application Brice (defaut: main) --brice-branch Branche Git de l'application Brice (defaut: main)
--public-base-url URL publique du site (ex: http://jeu.example.com)
--web-port Port HTTP expose dans le LXC (defaut: conserve ou 80)
--keycloak-admin-user Utilisateur admin Keycloak a forcer
--keycloak-admin-password
Mot de passe admin Keycloak a forcer
-h, --help Affiche cette aide -h, --help Affiche cette aide
EOF EOF
} }
@@ -48,11 +54,16 @@ LOCAL_MODE="0"
CTID="" CTID=""
LXC_HOSTNAME="chesscubing-web" LXC_HOSTNAME="chesscubing-web"
REPO_URL="https://git.jeannerot.fr/christophe/chesscubing.git"
REPO_BRANCH="main" REPO_BRANCH="main"
ETHAN_REPO_URL="https://git.jeannerot.fr/Mineloulou/Chesscubing.git" ETHAN_REPO_URL="https://git.jeannerot.fr/Mineloulou/Chesscubing.git"
ETHAN_REPO_BRANCH="main" ETHAN_REPO_BRANCH="main"
BRICE_REPO_URL="https://git.jeannerot.fr/Lescratcheur/ChessCubing.git" BRICE_REPO_URL="https://git.jeannerot.fr/Lescratcheur/ChessCubing.git"
BRICE_REPO_BRANCH="main" BRICE_REPO_BRANCH="main"
PUBLIC_BASE_URL=""
WEB_PORT=""
KEYCLOAK_ADMIN_USER=""
KEYCLOAK_ADMIN_PASSWORD=""
while [[ $# -gt 0 ]]; do while [[ $# -gt 0 ]]; do
case "$1" in case "$1" in
@@ -84,6 +95,10 @@ while [[ $# -gt 0 ]]; do
LXC_HOSTNAME="${2:-}" LXC_HOSTNAME="${2:-}"
shift 2 shift 2
;; ;;
--repo-url)
REPO_URL="${2:-}"
shift 2
;;
--branch) --branch)
REPO_BRANCH="${2:-}" REPO_BRANCH="${2:-}"
shift 2 shift 2
@@ -104,6 +119,22 @@ while [[ $# -gt 0 ]]; do
BRICE_REPO_BRANCH="${2:-}" BRICE_REPO_BRANCH="${2:-}"
shift 2 shift 2
;; ;;
--public-base-url)
PUBLIC_BASE_URL="${2:-}"
shift 2
;;
--web-port)
WEB_PORT="${2:-}"
shift 2
;;
--keycloak-admin-user)
KEYCLOAK_ADMIN_USER="${2:-}"
shift 2
;;
--keycloak-admin-password)
KEYCLOAK_ADMIN_PASSWORD="${2:-}"
shift 2
;;
-h | --help) -h | --help)
usage usage
exit 0 exit 0
@@ -133,11 +164,16 @@ trap 'printf "Erreur: echec de la commande [%s] a la ligne %s.\n" "$BASH_COMMAND
ctid="$1" ctid="$1"
lxc_hostname="$2" lxc_hostname="$2"
repo_branch="$3" repo_url="$3"
ethan_repo_url="$4" repo_branch="$4"
ethan_repo_branch="$5" ethan_repo_url="$5"
brice_repo_url="$6" ethan_repo_branch="$6"
brice_repo_branch="$7" brice_repo_url="$7"
brice_repo_branch="$8"
public_base_url="$9"
web_port="${10}"
keycloak_admin_user="${11}"
keycloak_admin_password="${12}"
die() { die() {
printf 'Erreur: %s\n' "$*" >&2 printf 'Erreur: %s\n' "$*" >&2
@@ -178,15 +214,28 @@ if [[ -n "$detected_hostname" ]]; then
lxc_hostname="$detected_hostname" lxc_hostname="$detected_hostname"
fi fi
if ! pct status "$ctid" | grep -q "running"; then if pct status "$ctid" | grep -q "running"; then
pct start "$ctid" pct stop "$ctid"
sleep 5
fi fi
pct set "$ctid" --features nesting=1,keyctl=1 >/dev/null
pct start "$ctid"
for _ in $(seq 1 20); do
if pct exec "$ctid" -- true >/dev/null 2>&1; then
break
fi
sleep 2
done
pct exec "$ctid" -- true >/dev/null 2>&1 || die "Le LXC n'est pas joignable apres le redemarrage."
ct_exec() { ct_exec() {
pct exec "$ctid" -- bash -lc "$1" pct exec "$ctid" -- bash -lc "$1"
} }
ct_exec "install -d -m 0755 /opt/chesscubing/repo /opt/chesscubing/ethan-repo /opt/chesscubing/brice-repo /opt/chesscubing/deploy /opt/chesscubing/config"
ct_exec "cat > /usr/local/bin/update-chesscubing <<'SCRIPT' ct_exec "cat > /usr/local/bin/update-chesscubing <<'SCRIPT'
#!/usr/bin/env bash #!/usr/bin/env bash
set -Eeuo pipefail set -Eeuo pipefail
@@ -196,30 +245,52 @@ trap 'printf \"Erreur: echec de la commande [%s] a la ligne %s.\\n\" \"\$BASH_CO
main_repo_dir='/opt/chesscubing/repo' main_repo_dir='/opt/chesscubing/repo'
ethan_repo_dir='/opt/chesscubing/ethan-repo' ethan_repo_dir='/opt/chesscubing/ethan-repo'
brice_repo_dir='/opt/chesscubing/brice-repo' brice_repo_dir='/opt/chesscubing/brice-repo'
publish_root='/opt/chesscubing/publish' deploy_dir='/opt/chesscubing/deploy'
web_root='/var/www/chesscubing/current' config_dir='/opt/chesscubing/config'
env_file=\"\$config_dir/chesscubing.env\"
main_branch=\"\${1:-${repo_branch}}\" main_branch=\"\${1:-${repo_branch}}\"
public_base_url_override=\"\${2:-}\"
web_port_override=\"\${3:-}\"
keycloak_admin_user_override=\"\${4:-}\"
keycloak_admin_password_override=\"\${5:-}\"
main_repo_url='${repo_url}'
ethan_repo_url='${ethan_repo_url}' ethan_repo_url='${ethan_repo_url}'
ethan_branch='${ethan_repo_branch}' ethan_branch='${ethan_repo_branch}'
brice_repo_url='${brice_repo_url}' brice_repo_url='${brice_repo_url}'
brice_branch='${brice_repo_branch}' brice_branch='${brice_repo_branch}'
ensure_dotnet_sdk() { random_secret() {
if command -v dotnet >/dev/null 2>&1; then od -An -N24 -tx1 /dev/urandom | tr -d ' \n'
return 0 }
ensure_base_packages() {
apt-get update
apt-get install -y ca-certificates curl gpg git rsync
}
ensure_docker_stack() {
ensure_base_packages
if ! command -v docker >/dev/null 2>&1 || ! docker compose version >/dev/null 2>&1; then
install -m 0755 -d /etc/apt/keyrings
if [[ ! -f /etc/apt/keyrings/docker.asc ]]; then
curl -fsSL https://download.docker.com/linux/debian/gpg -o /etc/apt/keyrings/docker.asc
chmod a+r /etc/apt/keyrings/docker.asc
fi
if [[ ! -f /etc/apt/sources.list.d/docker.list ]]; then
printf 'deb [arch=%s signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/debian %s stable\n' \
\"\$(dpkg --print-architecture)\" \
\"\$(. /etc/os-release && printf '%s' \"\$VERSION_CODENAME\")\" > /etc/apt/sources.list.d/docker.list
fi fi
apt-get update apt-get update
apt-get install -y ca-certificates curl gpg apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
if [[ ! -f /etc/apt/sources.list.d/microsoft-prod.list ]]; then
curl -fsSL https://packages.microsoft.com/config/debian/12/packages-microsoft-prod.deb -o /tmp/packages-microsoft-prod.deb
dpkg -i /tmp/packages-microsoft-prod.deb
rm -f /tmp/packages-microsoft-prod.deb
fi fi
apt-get update systemctl enable docker >/dev/null 2>&1 || true
apt-get install -y dotnet-sdk-10.0 systemctl restart docker
} }
sync_git_repo() { sync_git_repo() {
@@ -255,116 +326,194 @@ sync_git_repo() {
git clone --branch \"\$branch\" --single-branch \"\$repo_url\" \"\$repo_dir\" git clone --branch \"\$branch\" --single-branch \"\$repo_url\" \"\$repo_dir\"
} }
publish_blazor_app() { get_env_var() {
local repo_dir=\"\$1\" local key=\"\$1\"
local output_dir=\"\$2\"
ensure_dotnet_sdk [[ -f \"\$env_file\" ]] || return 0
rm -rf \"\$output_dir\" awk -F= -v wanted=\"\$key\" '\$1 == wanted { print substr(\$0, length(wanted) + 2); exit }' \"\$env_file\"
dotnet publish \"\$repo_dir/ChessCubing.App/ChessCubing.App.csproj\" -c Release -o \"\$output_dir\"
} }
publish_static_tree() { set_env_var() {
local source_dir=\"\$1\" local key=\"\$1\"
local destination_dir=\"\$2\" local value=\"\$2\"
install -d -m 0755 \"\$destination_dir\" touch \"\$env_file\"
if grep -q \"^\${key}=\" \"\$env_file\" 2>/dev/null; then
rsync -a --delete \ sed -i \"s|^\${key}=.*|\${key}=\${value}|\" \"\$env_file\"
--include='*/' \ else
--include='*.html' \ printf '%s=%s\n' \"\$key\" \"\$value\" >> \"\$env_file\"
--include='*.css' \
--include='*.js' \
--include='*.mjs' \
--include='*.png' \
--include='*.jpg' \
--include='*.jpeg' \
--include='*.svg' \
--include='*.webp' \
--include='*.ico' \
--include='*.pdf' \
--include='*.json' \
--include='*.webmanifest' \
--exclude='*' \
\"\$source_dir/\" \"\$destination_dir/\"
}
ensure_browser_stub() {
local destination_dir=\"\$1\"
local stub_path=\"\$destination_dir/cordova.js\"
if [[ ! -f \"\$stub_path\" ]]; then
printf '%s\n' '// Browser stub for Cordova builds.' > \"\$stub_path\"
fi fi
} }
sync_git_repo \"\$main_repo_dir\" '' \"\$main_branch\" 'principal' ensure_env_default() {
local key=\"\$1\"
local fallback=\"\$2\"
local value
value=\"\$(get_env_var \"\$key\")\"
if [[ -z \"\$value\" ]]; then
value=\"\$fallback\"
fi
set_env_var \"\$key\" \"\$value\"
}
normalize_public_base_url() {
local value=\"\$1\"
printf '%s\n' \"\${value%/}\"
}
build_default_public_base_url() {
local port=\"\$1\"
local detected_ip
detected_ip=\"\$(hostname -I | awk '{print \$1}')\"
[[ -n \"\$detected_ip\" ]] || return 1
if [[ \"\$port\" == \"80\" ]]; then
printf 'http://%s\n' \"\$detected_ip\"
else
printf 'http://%s:%s\n' \"\$detected_ip\" \"\$port\"
fi
}
configure_env_file() {
local current_value
local effective_web_port
local effective_public_base_url
local effective_keycloak_admin_user
local effective_keycloak_admin_password
effective_web_port=\"\$web_port_override\"
if [[ -z \"\$effective_web_port\" ]]; then
effective_web_port=\"\$(get_env_var WEB_PORT)\"
fi
if [[ -z \"\$effective_web_port\" ]]; then
effective_web_port='80'
fi
set_env_var WEB_PORT \"\$effective_web_port\"
effective_public_base_url=\"\$public_base_url_override\"
if [[ -z \"\$effective_public_base_url\" ]]; then
effective_public_base_url=\"\$(get_env_var PUBLIC_BASE_URL)\"
fi
if [[ -z \"\$effective_public_base_url\" ]]; then
effective_public_base_url=\"\$(build_default_public_base_url \"\$effective_web_port\" || true)\"
fi
if [[ -z \"\$effective_public_base_url\" ]]; then
if [[ \"\$effective_web_port\" == \"80\" ]]; then
effective_public_base_url='http://localhost'
else
effective_public_base_url=\"http://localhost:\$effective_web_port\"
fi
fi
set_env_var PUBLIC_BASE_URL \"\$(normalize_public_base_url \"\$effective_public_base_url\")\"
effective_keycloak_admin_user=\"\$keycloak_admin_user_override\"
if [[ -z \"\$effective_keycloak_admin_user\" ]]; then
effective_keycloak_admin_user=\"\$(get_env_var KEYCLOAK_ADMIN_USER)\"
fi
if [[ -z \"\$effective_keycloak_admin_user\" ]]; then
effective_keycloak_admin_user='admin'
fi
set_env_var KEYCLOAK_ADMIN_USER \"\$effective_keycloak_admin_user\"
effective_keycloak_admin_password=\"\$keycloak_admin_password_override\"
if [[ -z \"\$effective_keycloak_admin_password\" ]]; then
effective_keycloak_admin_password=\"\$(get_env_var KEYCLOAK_ADMIN_PASSWORD)\"
fi
if [[ -z \"\$effective_keycloak_admin_password\" ]]; then
effective_keycloak_admin_password=\"\$(random_secret)\"
fi
set_env_var KEYCLOAK_ADMIN_PASSWORD \"\$effective_keycloak_admin_password\"
ensure_env_default KEYCLOAK_DB_NAME keycloak
ensure_env_default KEYCLOAK_DB_USER keycloak
current_value=\"\$(get_env_var KEYCLOAK_DB_PASSWORD)\"
if [[ -z \"\$current_value\" ]]; then
current_value=\"\$(random_secret)\"
fi
set_env_var KEYCLOAK_DB_PASSWORD \"\$current_value\"
ensure_env_default SITE_DB_NAME chesscubing_site
ensure_env_default SITE_DB_USER chesscubing
current_value=\"\$(get_env_var SITE_DB_PASSWORD)\"
if [[ -z \"\$current_value\" ]]; then
current_value=\"\$(random_secret)\"
fi
set_env_var SITE_DB_PASSWORD \"\$current_value\"
current_value=\"\$(get_env_var SITE_DB_ROOT_PASSWORD)\"
if [[ -z \"\$current_value\" ]]; then
current_value=\"\$(random_secret)\"
fi
set_env_var SITE_DB_ROOT_PASSWORD \"\$current_value\"
}
sync_deploy_tree() {
install -d -m 0755 \"\$deploy_dir\" \"\$config_dir\"
rsync -a --delete \
--exclude='.git/' \
--exclude='bin/' \
--exclude='obj/' \
--exclude='.env' \
--exclude='node_modules/' \
\"\$main_repo_dir/\" \"\$deploy_dir/\"
if [[ -d \"\$ethan_repo_dir/.git\" ]]; then
rm -rf \"\$deploy_dir/ethan\"
rsync -a --delete \
--exclude='.git/' \
--exclude='node_modules/' \
\"\$ethan_repo_dir/\" \"\$deploy_dir/ethan/\"
fi
if [[ -d \"\$brice_repo_dir/.git\" ]]; then
rm -rf \"\$deploy_dir/brice\"
rsync -a --delete \
--exclude='.git/' \
--exclude='node_modules/' \
\"\$brice_repo_dir/\" \"\$deploy_dir/brice/\"
fi
}
disable_legacy_nginx() {
systemctl disable --now nginx >/dev/null 2>&1 || true
}
deploy_stack() {
cp \"\$env_file\" \"\$deploy_dir/.env\"
cd \"\$deploy_dir\"
docker compose down || true
docker compose up -d --build
docker compose ps
}
ensure_docker_stack
sync_git_repo \"\$main_repo_dir\" \"\$main_repo_url\" \"\$main_branch\" 'principal'
sync_git_repo \"\$ethan_repo_dir\" \"\$ethan_repo_url\" \"\$ethan_branch\" 'Ethan' sync_git_repo \"\$ethan_repo_dir\" \"\$ethan_repo_url\" \"\$ethan_branch\" 'Ethan'
sync_git_repo \"\$brice_repo_dir\" \"\$brice_repo_url\" \"\$brice_branch\" 'Brice' sync_git_repo \"\$brice_repo_dir\" \"\$brice_repo_url\" \"\$brice_branch\" 'Brice'
sync_deploy_tree
install -d -m 0755 \"\$web_root\" \"\$publish_root\" configure_env_file
disable_legacy_nginx
publish_blazor_app \"\$main_repo_dir\" \"\$publish_root/main\" deploy_stack
rsync -a --delete \"\$publish_root/main/wwwroot/\" \"\$web_root/\"
publish_static_tree \"\$ethan_repo_dir\" \"\$web_root/ethan\"
publish_static_tree \"\$brice_repo_dir/www\" \"\$web_root/brice\"
ensure_browser_stub \"\$web_root/brice\"
chown -R www-data:www-data \"\$web_root\"
nginx -t
systemctl reload nginx
SCRIPT SCRIPT
chmod +x /usr/local/bin/update-chesscubing" chmod +x /usr/local/bin/update-chesscubing"
ct_exec "cat > /etc/nginx/sites-available/chesscubing.conf <<'NGINX' ct_exec "/usr/local/bin/update-chesscubing '$repo_branch' '$public_base_url' '$web_port' '$keycloak_admin_user' '$keycloak_admin_password'"
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 = /brice {
return 301 \$scheme://\$http_host/brice/;
}
location /brice/ {
try_files \$uri \$uri/ /brice/index.html;
}
location / {
try_files \$uri \$uri/ /index.html;
}
location ~* \.(?:css|js|json|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)" container_ip="$(pct exec "$ctid" -- bash -lc "hostname -I | awk '{print \$1}'" 2>/dev/null | tr -d '\r' || true)"
public_url="$(pct exec "$ctid" -- bash -lc "awk -F= '/^PUBLIC_BASE_URL=/{print substr(\$0, 17); exit}' /opt/chesscubing/config/chesscubing.env" 2>/dev/null | tr -d '\r' || true)"
cat <<EOF cat <<EOF
Mise a jour terminee. Mise a jour terminee.
- CTID: $ctid - CTID: $ctid
- Nom du LXC: $lxc_hostname - Nom du LXC: $lxc_hostname
- URL probable: http://${container_ip:-<ip_du_lxc>} - IP detectee du LXC: ${container_ip:-<ip_du_lxc>}
- URL publique configuree: ${public_url:-http://${container_ip:-<ip_du_lxc>}}
EOF EOF
REMOTE REMOTE
@@ -373,11 +522,16 @@ if [[ "$LOCAL_MODE" == "1" ]]; then
bash "$payload_script" \ bash "$payload_script" \
"$CTID" \ "$CTID" \
"$LXC_HOSTNAME" \ "$LXC_HOSTNAME" \
"$REPO_URL" \
"$REPO_BRANCH" \ "$REPO_BRANCH" \
"$ETHAN_REPO_URL" \ "$ETHAN_REPO_URL" \
"$ETHAN_REPO_BRANCH" \ "$ETHAN_REPO_BRANCH" \
"$BRICE_REPO_URL" \ "$BRICE_REPO_URL" \
"$BRICE_REPO_BRANCH" "$BRICE_REPO_BRANCH" \
"$PUBLIC_BASE_URL" \
"$WEB_PORT" \
"$KEYCLOAK_ADMIN_USER" \
"$KEYCLOAK_ADMIN_PASSWORD"
exit 0 exit 0
fi fi
@@ -404,8 +558,13 @@ sshpass -p "$PROXMOX_PASSWORD" \
bash -s -- \ bash -s -- \
"$CTID" \ "$CTID" \
"$LXC_HOSTNAME" \ "$LXC_HOSTNAME" \
"$REPO_URL" \
"$REPO_BRANCH" \ "$REPO_BRANCH" \
"$ETHAN_REPO_URL" \ "$ETHAN_REPO_URL" \
"$ETHAN_REPO_BRANCH" \ "$ETHAN_REPO_BRANCH" \
"$BRICE_REPO_URL" \ "$BRICE_REPO_URL" \
"$BRICE_REPO_BRANCH" < "$payload_script" "$BRICE_REPO_BRANCH" \
"$PUBLIC_BASE_URL" \
"$WEB_PORT" \
"$KEYCLOAK_ADMIN_USER" \
"$KEYCLOAK_ADMIN_PASSWORD" < "$payload_script"