#!/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" }