ia_dev/deploy/lib/ssh.sh
Nicolas Cantu ecb2811209 ia_dev: analyse agent, SSH scp helpers, deploy log, kogus docs, push-by-script rules
**Motivations:**
- Version the new analyse Cursor agent and keep push-by-script closure rules accurate.
- Improve deploy SSH/SCP reliability for publishing remote lib pairs and transient connection failures.
- Align kogus documentation with current deployment and code standards.

**Root causes:**
- None (incremental tooling and documentation maintenance).

**Correctifs:**
- Minor adjustment in deploy log helper output (staged change).

**Evolutions:**
- Add `.smartIde/agents/analyse.md` for the analyse agent workflow.
- Extend `deploy/lib/ssh.sh` with remote lib pair publish helpers and `scp_copy_retry` / retry wrapper for ProxyJump/transient SCP failures.
- Update `.smartIde/agents/push-by-script.md` (lint closure and workflow notes).
- Update `projects/kogus/docs/Code-Standards.md` and `projects/kogus/docs/Deployment.md`.

**Pages affectées:**
- N/A (ia_dev agents, deploy libs, and project docs only).
2026-04-29 13:20:16 +02:00

131 lines
3.6 KiB
Bash

#!/usr/bin/env bash
# Shared SSH/SCP helpers for deploy scripts (ProxyJump, BatchMode, keepalive).
# Sourced by project deploy/_lib/ssh.sh when ia_dev is present under the repo root (or sibling layout).
set -euo pipefail
require_ssh_key() {
local key_path="$1"
if [[ -z "$key_path" ]]; then
echo "SSH key path is required" >&2
return 1
fi
if [[ ! -f "$key_path" ]]; then
echo "SSH key not found: $key_path" >&2
return 1
fi
}
ssh_common_opts() {
local ssh_user="$1"
local ssh_host="$2"
echo \
-o BatchMode=yes \
-o StrictHostKeyChecking=accept-new \
-o ConnectTimeout=30 \
-o ServerAliveInterval=10 \
-o ServerAliveCountMax=6 \
-o TCPKeepAlive=yes \
-o Compression=no
}
ssh_run() {
local ssh_key="$1"
local ssh_user="$2"
local ssh_host="$3"
shift 3
require_ssh_key "$ssh_key"
local proxy_host="${DEPLOY_SSH_PROXY_HOST:-}"
local proxy_user="${DEPLOY_SSH_PROXY_USER:-$ssh_user}"
local proxy_args=()
if [[ -n "$proxy_host" ]]; then
proxy_args=(-J "$proxy_user@$proxy_host")
fi
# shellcheck disable=SC2207
local common_opts=($(ssh_common_opts "$ssh_user" "$ssh_host"))
ssh -i "$ssh_key" \
"${common_opts[@]}" \
"${proxy_args[@]}" \
"$ssh_user@$ssh_host" "$@"
}
scp_copy() {
local ssh_key="$1"
local src="$2"
local ssh_user="$3"
local ssh_host="$4"
local dst="$5"
local recursive="${6:-false}"
require_ssh_key "$ssh_key"
local proxy_host="${DEPLOY_SSH_PROXY_HOST:-}"
local proxy_user="${DEPLOY_SSH_PROXY_USER:-$ssh_user}"
local proxy_args=()
if [[ -n "$proxy_host" ]]; then
proxy_args=(-o "ProxyJump=$proxy_user@$proxy_host")
fi
# shellcheck disable=SC2207
local common_opts=($(ssh_common_opts "$ssh_user" "$ssh_host"))
local scp_opts=()
if [[ "$recursive" == "true" ]] || [[ -d "$src" ]]; then
scp_opts=(-r)
fi
scp -i "$ssh_key" \
"${scp_opts[@]}" \
"${common_opts[@]}" \
"${proxy_args[@]}" \
"$src" "$ssh_user@$ssh_host:$dst"
}
# Publish remote/_lib.sh dependency pair (remote/_lib.sh sources multisite-deployment-site-codes.sh from the same directory on the target).
# Args: ssh_key ssh_user ssh_host local_remote_dir remote_remote_dir (both paths to .../deploy/scripts_v2/remote, no trailing slash).
lecoffre_scp_publish_remote_lib_pair() {
local ssh_key="${1:?}"
local ssh_user="${2:?}"
local ssh_host="${3:?}"
local local_remote_dir="${4:?}"
local remote_remote_dir="${5:?}"
scp_copy "$ssh_key" "${local_remote_dir}/_lib.sh" "$ssh_user" "$ssh_host" "${remote_remote_dir}/_lib.sh"
scp_copy "$ssh_key" "${local_remote_dir}/multisite-deployment-site-codes.sh" "$ssh_user" "$ssh_host" "${remote_remote_dir}/multisite-deployment-site-codes.sh"
}
# Retries for transient SCP failures (e.g. Connection reset by peer on ProxyJump).
scp_copy_retry() {
local max="${SSH_TRANSIENT_RETRY_MAX:-3}"
local delay="${SSH_TRANSIENT_RETRY_DELAY_SEC:-5}"
local attempt=1
while [[ $attempt -le $max ]]; do
if scp_copy "$@"; then
return 0
fi
if [[ $attempt -lt $max ]]; then
echo "[$(date -u '+%Y-%m-%dT%H:%M:%SZ')] scp_copy_retry: attempt $attempt/$max failed, sleep ${delay}s" >&2
sleep "$delay"
fi
attempt=$((attempt + 1))
done
return 1
}
# Same as lecoffre_scp_publish_remote_lib_pair but uses scp_copy_retry (scripts/ e2e, user-profil, etc.).
lecoffre_scp_publish_remote_lib_pair_retry() {
local ssh_key="${1:?}"
local ssh_user="${2:?}"
local ssh_host="${3:?}"
local local_remote_dir="${4:?}"
local remote_remote_dir="${5:?}"
scp_copy_retry "$ssh_key" "${local_remote_dir}/_lib.sh" "$ssh_user" "$ssh_host" "${remote_remote_dir}/_lib.sh"
scp_copy_retry "$ssh_key" "${local_remote_dir}/multisite-deployment-site-codes.sh" "$ssh_user" "$ssh_host" "${remote_remote_dir}/multisite-deployment-site-codes.sh"
}