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_USER=keycloak
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`.
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.
@@ -99,9 +100,11 @@ Prerrequis sur la machine qui lance les scripts :
- en mode distant : `ssh` et `sshpass`
- 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

View File

@@ -10,7 +10,7 @@ services:
keycloak:
condition: service_started
ports:
- "8080:80"
- "${WEB_PORT:-8080}:80"
restart: unless-stopped
auth:
@@ -53,7 +53,7 @@ services:
KC_BOOTSTRAP_ADMIN_USERNAME: ${KEYCLOAK_ADMIN_USER:-admin}
KC_BOOTSTRAP_ADMIN_PASSWORD: ${KEYCLOAK_ADMIN_PASSWORD:-admin}
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_HOSTNAME_STRICT: "false"
volumes:

View File

@@ -25,9 +25,9 @@ Options principales:
--gateway Passerelle si IP statique
--bridge Bridge reseau Proxmox (defaut: vmbr0)
--cores Nombre de vCPU du LXC (defaut: 2)
--memory Memoire RAM en Mo (defaut: 1024)
--swap Swap en Mo (defaut: 512)
--disk-gb Taille disque du LXC en Go (defaut: 6)
--memory Memoire RAM en Mo (defaut: 4096)
--swap Swap en Mo (defaut: 1024)
--disk-gb Taille disque du LXC en Go (defaut: 12)
--template-storage Stockage Proxmox pour les templates
--rootfs-storage Stockage Proxmox pour le disque LXC
--repo-url Depot Git a deployer
@@ -36,6 +36,11 @@ Options principales:
--ethan-branch Branche Git de l'application Ethan (defaut: main)
--brice-repo-url Depot Git de l'application Brice
--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
-h, --help Affiche cette aide
@@ -68,9 +73,9 @@ LXC_IP="dhcp"
LXC_GATEWAY=""
LXC_BRIDGE="vmbr0"
LXC_CORES="2"
LXC_MEMORY="1024"
LXC_SWAP="512"
LXC_DISK_GB="6"
LXC_MEMORY="4096"
LXC_SWAP="1024"
LXC_DISK_GB="12"
TEMPLATE_STORAGE=""
ROOTFS_STORAGE=""
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"
BRICE_REPO_URL="https://git.jeannerot.fr/Lescratcheur/ChessCubing.git"
BRICE_REPO_BRANCH="main"
PUBLIC_BASE_URL=""
WEB_PORT="80"
KEYCLOAK_ADMIN_USER="admin"
KEYCLOAK_ADMIN_PASSWORD=""
LXC_PASSWORD=""
while [[ $# -gt 0 ]]; do
@@ -171,6 +180,22 @@ while [[ $# -gt 0 ]]; do
BRICE_REPO_BRANCH="${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="${2:-}"
shift 2
@@ -221,6 +246,10 @@ ethan_repo_branch="$6"
brice_repo_url="$7"
brice_repo_branch="$8"
lxc_password="$9"
public_base_url="${10}"
web_port="${11}"
keycloak_admin_user="${12}"
keycloak_admin_password="${13}"
die() {
printf 'Erreur: %s\n' "$*" >&2
@@ -339,7 +368,8 @@ pct create "$ctid" "$template_ref" \
--onboot 1 \
--ostype debian \
--password "$lxc_password" \
--unprivileged 1
--unprivileged 1 \
--features nesting=1,keyctl=1
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."
printf 'Installation de nginx, git, rsync et des prerequis de build dans le conteneur...\n'
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 "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'
#!/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'
ethan_repo_dir='/opt/chesscubing/ethan-repo'
brice_repo_dir='/opt/chesscubing/brice-repo'
publish_root='/opt/chesscubing/publish'
web_root='/var/www/chesscubing/current'
deploy_dir='/opt/chesscubing/deploy'
config_dir='/opt/chesscubing/config'
env_file=\"\$config_dir/chesscubing.env\"
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_branch='${ethan_repo_branch}'
brice_repo_url='${brice_repo_url}'
brice_branch='${brice_repo_branch}'
ensure_dotnet_sdk() {
if command -v dotnet >/dev/null 2>&1; then
return 0
random_secret() {
od -An -N24 -tx1 /dev/urandom | tr -d ' \n'
}
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
apt-get update
apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
fi
apt-get update
apt-get install -y ca-certificates curl gpg
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
apt-get update
apt-get install -y dotnet-sdk-10.0
systemctl enable docker >/dev/null 2>&1 || true
systemctl restart docker
}
sync_git_repo() {
@@ -437,111 +475,189 @@ sync_git_repo() {
git clone --branch \"\$branch\" --single-branch \"\$repo_url\" \"\$repo_dir\"
}
publish_blazor_app() {
local repo_dir=\"\$1\"
local output_dir=\"\$2\"
get_env_var() {
local key=\"\$1\"
ensure_dotnet_sdk
rm -rf \"\$output_dir\"
dotnet publish \"\$repo_dir/ChessCubing.App/ChessCubing.App.csproj\" -c Release -o \"\$output_dir\"
[[ -f \"\$env_file\" ]] || return 0
awk -F= -v wanted=\"\$key\" '\$1 == wanted { print substr(\$0, length(wanted) + 2); exit }' \"\$env_file\"
}
publish_static_tree() {
local source_dir=\"\$1\"
local destination_dir=\"\$2\"
set_env_var() {
local key=\"\$1\"
local value=\"\$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='*.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\"
touch \"\$env_file\"
if grep -q \"^\${key}=\" \"\$env_file\" 2>/dev/null; then
sed -i \"s|^\${key}=.*|\${key}=\${value}|\" \"\$env_file\"
else
printf '%s=%s\n' \"\$key\" \"\$value\" >> \"\$env_file\"
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 \"\$brice_repo_dir\" \"\$brice_repo_url\" \"\$brice_branch\" 'Brice'
install -d -m 0755 \"\$web_root\" \"\$publish_root\"
publish_blazor_app \"\$main_repo_dir\" \"\$publish_root/main\"
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
sync_deploy_tree
configure_env_file
disable_legacy_nginx
deploy_stack
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 = /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"
printf 'Deploiement de la stack Docker complete dans le LXC...\n'
ct_exec "/usr/local/bin/update-chesscubing '$repo_branch' '$public_base_url' '$web_port' '$keycloak_admin_user' '$keycloak_admin_password'"
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
@@ -549,7 +665,10 @@ Installation terminee.
- CTID: $ctid
- Nom du LXC: $lxc_hostname
- 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:
./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" \
"$BRICE_REPO_URL" \
"$BRICE_REPO_BRANCH" \
"$LXC_PASSWORD"
"$LXC_PASSWORD" \
"$PUBLIC_BASE_URL" \
"$WEB_PORT" \
"$KEYCLOAK_ADMIN_USER" \
"$KEYCLOAK_ADMIN_PASSWORD"
exit 0
fi
@@ -618,4 +741,8 @@ sshpass -p "$PROXMOX_PASSWORD" \
"$ETHAN_REPO_BRANCH" \
"$BRICE_REPO_URL" \
"$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
--ctid CTID du LXC a mettre a jour
--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)
--ethan-repo-url Depot Git de l'application Ethan
--ethan-branch Branche Git de l'application Ethan (defaut: main)
--brice-repo-url Depot Git de l'application Brice
--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
EOF
}
@@ -48,11 +54,16 @@ LOCAL_MODE="0"
CTID=""
LXC_HOSTNAME="chesscubing-web"
REPO_URL="https://git.jeannerot.fr/christophe/chesscubing.git"
REPO_BRANCH="main"
ETHAN_REPO_URL="https://git.jeannerot.fr/Mineloulou/Chesscubing.git"
ETHAN_REPO_BRANCH="main"
BRICE_REPO_URL="https://git.jeannerot.fr/Lescratcheur/ChessCubing.git"
BRICE_REPO_BRANCH="main"
PUBLIC_BASE_URL=""
WEB_PORT=""
KEYCLOAK_ADMIN_USER=""
KEYCLOAK_ADMIN_PASSWORD=""
while [[ $# -gt 0 ]]; do
case "$1" in
@@ -84,6 +95,10 @@ while [[ $# -gt 0 ]]; do
LXC_HOSTNAME="${2:-}"
shift 2
;;
--repo-url)
REPO_URL="${2:-}"
shift 2
;;
--branch)
REPO_BRANCH="${2:-}"
shift 2
@@ -104,6 +119,22 @@ while [[ $# -gt 0 ]]; do
BRICE_REPO_BRANCH="${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)
usage
exit 0
@@ -133,11 +164,16 @@ trap 'printf "Erreur: echec de la commande [%s] a la ligne %s.\n" "$BASH_COMMAND
ctid="$1"
lxc_hostname="$2"
repo_branch="$3"
ethan_repo_url="$4"
ethan_repo_branch="$5"
brice_repo_url="$6"
brice_repo_branch="$7"
repo_url="$3"
repo_branch="$4"
ethan_repo_url="$5"
ethan_repo_branch="$6"
brice_repo_url="$7"
brice_repo_branch="$8"
public_base_url="$9"
web_port="${10}"
keycloak_admin_user="${11}"
keycloak_admin_password="${12}"
die() {
printf 'Erreur: %s\n' "$*" >&2
@@ -178,15 +214,28 @@ if [[ -n "$detected_hostname" ]]; then
lxc_hostname="$detected_hostname"
fi
if ! pct status "$ctid" | grep -q "running"; then
pct start "$ctid"
sleep 5
if pct status "$ctid" | grep -q "running"; then
pct stop "$ctid"
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() {
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'
#!/usr/bin/env bash
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'
ethan_repo_dir='/opt/chesscubing/ethan-repo'
brice_repo_dir='/opt/chesscubing/brice-repo'
publish_root='/opt/chesscubing/publish'
web_root='/var/www/chesscubing/current'
deploy_dir='/opt/chesscubing/deploy'
config_dir='/opt/chesscubing/config'
env_file=\"\$config_dir/chesscubing.env\"
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_branch='${ethan_repo_branch}'
brice_repo_url='${brice_repo_url}'
brice_branch='${brice_repo_branch}'
ensure_dotnet_sdk() {
if command -v dotnet >/dev/null 2>&1; then
return 0
random_secret() {
od -An -N24 -tx1 /dev/urandom | tr -d ' \n'
}
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
apt-get update
apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
fi
apt-get update
apt-get install -y ca-certificates curl gpg
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
apt-get update
apt-get install -y dotnet-sdk-10.0
systemctl enable docker >/dev/null 2>&1 || true
systemctl restart docker
}
sync_git_repo() {
@@ -255,116 +326,194 @@ sync_git_repo() {
git clone --branch \"\$branch\" --single-branch \"\$repo_url\" \"\$repo_dir\"
}
publish_blazor_app() {
local repo_dir=\"\$1\"
local output_dir=\"\$2\"
get_env_var() {
local key=\"\$1\"
ensure_dotnet_sdk
rm -rf \"\$output_dir\"
dotnet publish \"\$repo_dir/ChessCubing.App/ChessCubing.App.csproj\" -c Release -o \"\$output_dir\"
[[ -f \"\$env_file\" ]] || return 0
awk -F= -v wanted=\"\$key\" '\$1 == wanted { print substr(\$0, length(wanted) + 2); exit }' \"\$env_file\"
}
publish_static_tree() {
local source_dir=\"\$1\"
local destination_dir=\"\$2\"
set_env_var() {
local key=\"\$1\"
local value=\"\$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='*.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\"
touch \"\$env_file\"
if grep -q \"^\${key}=\" \"\$env_file\" 2>/dev/null; then
sed -i \"s|^\${key}=.*|\${key}=\${value}|\" \"\$env_file\"
else
printf '%s=%s\n' \"\$key\" \"\$value\" >> \"\$env_file\"
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 \"\$brice_repo_dir\" \"\$brice_repo_url\" \"\$brice_branch\" 'Brice'
install -d -m 0755 \"\$web_root\" \"\$publish_root\"
publish_blazor_app \"\$main_repo_dir\" \"\$publish_root/main\"
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
sync_deploy_tree
configure_env_file
disable_legacy_nginx
deploy_stack
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 = /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"
ct_exec "/usr/local/bin/update-chesscubing '$repo_branch' '$public_base_url' '$web_port' '$keycloak_admin_user' '$keycloak_admin_password'"
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
Mise a jour terminee.
- CTID: $ctid
- 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
REMOTE
@@ -373,11 +522,16 @@ if [[ "$LOCAL_MODE" == "1" ]]; then
bash "$payload_script" \
"$CTID" \
"$LXC_HOSTNAME" \
"$REPO_URL" \
"$REPO_BRANCH" \
"$ETHAN_REPO_URL" \
"$ETHAN_REPO_BRANCH" \
"$BRICE_REPO_URL" \
"$BRICE_REPO_BRANCH"
"$BRICE_REPO_BRANCH" \
"$PUBLIC_BASE_URL" \
"$WEB_PORT" \
"$KEYCLOAK_ADMIN_USER" \
"$KEYCLOAK_ADMIN_PASSWORD"
exit 0
fi
@@ -404,8 +558,13 @@ sshpass -p "$PROXMOX_PASSWORD" \
bash -s -- \
"$CTID" \
"$LXC_HOSTNAME" \
"$REPO_URL" \
"$REPO_BRANCH" \
"$ETHAN_REPO_URL" \
"$ETHAN_REPO_BRANCH" \
"$BRICE_REPO_URL" \
"$BRICE_REPO_BRANCH" < "$payload_script"
"$BRICE_REPO_BRANCH" \
"$PUBLIC_BASE_URL" \
"$WEB_PORT" \
"$KEYCLOAK_ADMIN_USER" \
"$KEYCLOAK_ADMIN_PASSWORD" < "$payload_script"