#!/usr/bin/env bash
# ───────────────────────────────────────────────────────────────────────────
# hooks.sh - Pre/post deploy hooks for smart-deploy
# Supports: http, wait_http, shell, ssh actions
# ───────────────────────────────────────────────────────────────────────────
# Run all hooks for a given phase (pre_deploy or post_deploy)
# Usage: run_hooks "pre_deploy" | run_hooks "post_deploy"
run_hooks() {
local phase="$1"
local count_var="HOOKS_${phase}_COUNT"
local count="${!count_var:-0}"
if [[ $count -eq 0 ]]; then
return 0
fi
log_step "Execution des hooks ${phase} (${count} action(s))..."
for ((i=0; i<count; i++)); do
local action_var="HOOKS_${phase}_${i}_action"
local desc_var="HOOKS_${phase}_${i}_description"
local action="${!action_var:-}"
local description="${!desc_var:-hook ${i}}"
if [[ -z "$action" ]]; then
log_warning "Hook ${phase}[${i}]: action manquante, ignoré"
continue
fi
log_info " [${phase}] ${description}"
case "$action" in
http)
_run_hook_http "$phase" "$i"
;;
wait_http)
_run_hook_wait_http "$phase" "$i"
;;
shell)
_run_hook_shell "$phase" "$i"
;;
ssh)
_run_hook_ssh "$phase" "$i"
;;
*)
log_warning "Hook ${phase}[${i}]: action inconnue '${action}', ignoré"
continue
;;
esac
local rc=$?
if [[ $rc -ne 0 ]]; then
local optional_var="HOOKS_${phase}_${i}_optional"
local is_optional="${!optional_var:-false}"
if [[ "$is_optional" == "true" ]]; then
log_warning "Hook ${phase}[${i}] echoue (${description}) - optionnel, deploiement continue"
elif [[ "$phase" == "pre_deploy" ]]; then
log_error "Hook ${phase}[${i}] echoue (${description}) - deploiement annule"
return 1
else
log_warning "Hook ${phase}[${i}] echoue (${description}) - deploiement continue"
fi
fi
done
log_success "Hooks ${phase} termines"
return 0
}
# ─── HTTP request (fire & forget) ───
_run_hook_http() {
local phase="$1"
local idx="$2"
local method_var="HOOKS_${phase}_${idx}_method"
local url_var="HOOKS_${phase}_${idx}_url"
local body_var="HOOKS_${phase}_${idx}_body"
local timeout_var="HOOKS_${phase}_${idx}_timeout"
local method="${!method_var:-GET}"
local url="${!url_var:-}"
local body="${!body_var:-}"
local timeout="${!timeout_var:-30}"
if [[ -z "$url" ]]; then
log_error "Hook http: url manquante"
return 1
fi
# Resolve relative URLs
url=$(_resolve_hook_url "$url")
local curl_args=(-s -S -X "$method" --connect-timeout 10 --max-time "$timeout")
if [[ -n "$body" ]]; then
curl_args+=(-H "Content-Type: application/json" -d "$body")
fi
local response
response=$(curl "${curl_args[@]}" -w "\n%{http_code}" "$url" 2>&1)
local http_code
http_code=$(echo "$response" | tail -1)
local body_response
body_response=$(echo "$response" | head -n -1)
if [[ "$http_code" -ge 200 ]] && [[ "$http_code" -lt 300 ]]; then
log_info " -> HTTP ${http_code} OK"
return 0
else
log_error " -> HTTP ${http_code}: ${body_response}"
return 1
fi
}
# ─── HTTP poll with jq condition ───
_run_hook_wait_http() {
local phase="$1"
local idx="$2"
local method_var="HOOKS_${phase}_${idx}_method"
local url_var="HOOKS_${phase}_${idx}_url"
local jq_var="HOOKS_${phase}_${idx}_jq_condition"
local timeout_var="HOOKS_${phase}_${idx}_timeout"
local interval_var="HOOKS_${phase}_${idx}_interval"
local method="${!method_var:-GET}"
local url="${!url_var:-}"
local jq_condition="${!jq_var:-}"
local timeout="${!timeout_var:-60}"
local interval="${!interval_var:-5}"
if [[ -z "$url" ]]; then
log_error "Hook wait_http: url manquante"
return 1
fi
if [[ -z "$jq_condition" ]]; then
log_error "Hook wait_http: jq_condition manquante"
return 1
fi
# Check jq is available
if ! command -v jq &>/dev/null; then
log_error "Hook wait_http: 'jq' requis mais non installe"
return 1
fi
# Cap timeout at 300s
if [[ $timeout -gt 300 ]]; then
timeout=300
fi
url=$(_resolve_hook_url "$url")
local start_time
start_time=$(date +%s)
while true; do
local elapsed=$(( $(date +%s) - start_time ))
if [[ $elapsed -ge $timeout ]]; then
log_error " -> Timeout apres ${timeout}s"
return 1
fi
local response
response=$(curl -s -X "$method" --connect-timeout 5 --max-time 10 "$url" 2>/dev/null)
if [[ -n "$response" ]]; then
local check
check=$(echo "$response" | jq -r "$jq_condition" 2>/dev/null)
if [[ "$check" == "true" ]]; then
log_info " -> Condition remplie apres ${elapsed}s"
return 0
fi
fi
log_info " -> En attente... (${elapsed}/${timeout}s)"
sleep "$interval"
done
}
# ─── Shell command (local) ───
_run_hook_shell() {
local phase="$1"
local idx="$2"
local cmd_var="HOOKS_${phase}_${idx}_command"
local command="${!cmd_var:-}"
if [[ -z "$command" ]]; then
log_error "Hook shell: command manquante"
return 1
fi
eval "$command"
return $?
}
# ─── SSH command (on target) ───
_run_hook_ssh() {
local phase="$1"
local idx="$2"
local cmd_var="HOOKS_${phase}_${idx}_command"
local command="${!cmd_var:-}"
if [[ -z "$command" ]]; then
log_error "Hook ssh: command manquante"
return 1
fi
if [[ -z "${TARGET_HOST:-}" ]] || [[ -z "${TARGET_USER:-}" ]]; then
log_error "Hook ssh: TARGET_HOST/TARGET_USER non definis"
return 1
fi
ssh_exec "$TARGET_HOST" "$TARGET_USER" "$command"
return $?
}
# ─── Resolve relative URLs ───
_resolve_hook_url() {
local url="$1"
if [[ "$url" =~ ^/ ]]; then
# Relative URL: prefix with target host and port
local host="${TARGET_HOST:-localhost}"
local port="${CFG_port:-80}"
echo "http://${host}:${port}${url}"
else
echo "$url"
fi
}