199 lines
5.0 KiB
Bash
199 lines
5.0 KiB
Bash
#!/usr/bin/env bash
|
||
# Генерирует SSH-ключи и ~/.ssh/config для Git-хостов из local/git-ssh-hosts.conf.
|
||
# Совместим с macOS bash 3.2 (без declare -A).
|
||
set -euo pipefail
|
||
|
||
CONFIG_DIR="${CONFIG_DIR:-$HOME/CursorRules/local}"
|
||
HOSTS_CONFIG="${HOSTS_CONFIG:-$CONFIG_DIR/git-ssh-hosts.conf}"
|
||
WHAT_IF="${WHAT_IF:-0}"
|
||
|
||
log_step() { printf '[*] %s\n' "$1"; }
|
||
log_ok() { printf '[+] %s\n' "$1"; }
|
||
log_warn() { printf '[!] %s\n' "$1"; }
|
||
|
||
cfg_key() { printf '%s' "$1" | tr '.-' '__'; }
|
||
|
||
set_profile_field() {
|
||
local profile="$1" field="$2" value="$3"
|
||
eval "__pf_$(cfg_key "$profile")_${field}=\"\$value\""
|
||
}
|
||
|
||
get_profile_field() {
|
||
local profile="$1" field="$2"
|
||
eval "printf '%s' \"\${__pf_$(cfg_key "$profile")_${field}:-}\""
|
||
}
|
||
|
||
set_host_field() {
|
||
local host="$1" field="$2" value="$3"
|
||
eval "__hf_$(cfg_key "$host")_${field}=\"\$value\""
|
||
}
|
||
|
||
get_host_field() {
|
||
local host="$1" field="$2"
|
||
eval "printf '%s' \"\${__hf_$(cfg_key "$host")_${field}:-}\""
|
||
}
|
||
|
||
if [[ ! -f "$HOSTS_CONFIG" ]]; then
|
||
echo "Config not found: $HOSTS_CONFIG" >&2
|
||
exit 1
|
||
fi
|
||
|
||
SSH_DIR="$HOME/.ssh"
|
||
SSH_CONFIG="$SSH_DIR/config"
|
||
mkdir -p "$SSH_DIR"
|
||
chmod 700 "$SSH_DIR"
|
||
|
||
PROFILE_LIST=()
|
||
HOST_LIST=()
|
||
current_section=''
|
||
current_name=''
|
||
|
||
while IFS= read -r line || [[ -n "$line" ]]; do
|
||
line="${line%%#*}"
|
||
line="${line%"${line##*[![:space:]]}"}"
|
||
[[ -z "$line" ]] && continue
|
||
|
||
if [[ "$line" =~ ^\[profile:(.+)\]$ ]]; then
|
||
current_section='profile'
|
||
current_name="${BASH_REMATCH[1]}"
|
||
PROFILE_LIST+=("$current_name")
|
||
continue
|
||
fi
|
||
if [[ "$line" =~ ^\[host:(.+)\]$ ]]; then
|
||
current_section='host'
|
||
current_name="${BASH_REMATCH[1]}"
|
||
HOST_LIST+=("$current_name")
|
||
set_host_field "$current_name" HostName ''
|
||
continue
|
||
fi
|
||
if [[ "$line" != *=* ]]; then
|
||
continue
|
||
fi
|
||
|
||
key="${line%%=*}"
|
||
value="${line#*=}"
|
||
key="${key%"${key##*[![:space:]]}"}"
|
||
value="${value#"${value%%[![:space:]]*}"}"
|
||
|
||
if [[ "$current_section" == 'profile' ]]; then
|
||
case "$key" in
|
||
KeyName|KeyComment|GiteaConfig) set_profile_field "$current_name" "$key" "$value" ;;
|
||
esac
|
||
elif [[ "$current_section" == 'host' ]]; then
|
||
case "$key" in
|
||
HostName|Port|User|Profile) set_host_field "$current_name" "$key" "$value" ;;
|
||
esac
|
||
fi
|
||
done < "$HOSTS_CONFIG"
|
||
|
||
KEYS_SEEN=()
|
||
|
||
for profile in "${PROFILE_LIST[@]}"; do
|
||
key_name="$(get_profile_field "$profile" KeyName)"
|
||
[[ -z "$key_name" ]] && continue
|
||
|
||
seen=0
|
||
for existing in "${KEYS_SEEN[@]:-}"; do
|
||
[[ "$existing" == "$key_name" ]] && seen=1 && break
|
||
done
|
||
[[ "$seen" -eq 1 ]] && continue
|
||
KEYS_SEEN+=("$key_name")
|
||
|
||
key_path="$SSH_DIR/$key_name"
|
||
comment="$(get_profile_field "$profile" KeyComment)"
|
||
comment="${comment:-$key_name}"
|
||
|
||
if [[ ! -f "$key_path" ]]; then
|
||
log_step "Generating $key_name"
|
||
if [[ "$WHAT_IF" == '1' ]]; then
|
||
log_warn "WhatIf: ssh-keygen -t ed25519 -f $key_path"
|
||
else
|
||
ssh-keygen -t ed25519 -f "$key_path" -N '' -C "$comment" -q
|
||
chmod 600 "$key_path"
|
||
log_ok "key $key_name created"
|
||
fi
|
||
else
|
||
log_ok "key $key_name exists"
|
||
fi
|
||
done
|
||
|
||
update_ssh_block() {
|
||
local host_alias="$1"
|
||
local block="$2"
|
||
local begin="# BEGIN Setup-SshAccess:$host_alias"
|
||
local end="# END Setup-SshAccess:$host_alias"
|
||
local tmp block_file
|
||
tmp="$(mktemp)"
|
||
block_file="$(mktemp)"
|
||
|
||
printf '%s\n' "$block" > "$block_file"
|
||
|
||
if [[ -f "$SSH_CONFIG" ]]; then
|
||
awk -v begin="$begin" -v end="$end" -v blockfile="$block_file" '
|
||
$0 == begin { skip=1; next }
|
||
$0 == end { skip=0; next }
|
||
skip { next }
|
||
{ print }
|
||
END {
|
||
if (blockfile != "") {
|
||
while ((getline line < blockfile) > 0) {
|
||
print line
|
||
}
|
||
close(blockfile)
|
||
}
|
||
}
|
||
' "$SSH_CONFIG" > "$tmp"
|
||
else
|
||
cp "$block_file" "$tmp"
|
||
fi
|
||
|
||
rm -f "$block_file"
|
||
|
||
if [[ "$WHAT_IF" != '1' ]]; then
|
||
mv "$tmp" "$SSH_CONFIG"
|
||
chmod 600 "$SSH_CONFIG"
|
||
else
|
||
rm -f "$tmp"
|
||
fi
|
||
}
|
||
|
||
IFS=$'\n'
|
||
sorted_hosts=($(printf '%s\n' "${HOST_LIST[@]}" | sort))
|
||
unset IFS
|
||
|
||
for host_alias in "${sorted_hosts[@]}"; do
|
||
profile="$(get_host_field "$host_alias" Profile)"
|
||
key_name="$(get_profile_field "$profile" KeyName)"
|
||
if [[ -z "$profile" || -z "$key_name" ]]; then
|
||
echo "Host $host_alias: unknown profile" >&2
|
||
exit 1
|
||
fi
|
||
|
||
block="# BEGIN Setup-SshAccess:$host_alias
|
||
Host $host_alias
|
||
HostName $(get_host_field "$host_alias" HostName)
|
||
User $(get_host_field "$host_alias" User)
|
||
Port $(get_host_field "$host_alias" Port)
|
||
IdentityFile ~/.ssh/$key_name
|
||
IdentitiesOnly yes
|
||
AddKeysToAgent yes
|
||
StrictHostKeyChecking accept-new
|
||
# END Setup-SshAccess:$host_alias"
|
||
|
||
if [[ "$WHAT_IF" == '1' ]]; then
|
||
log_step "WhatIf: ssh config block for $host_alias"
|
||
else
|
||
update_ssh_block "$host_alias" "$block"
|
||
log_ok "ssh config: $host_alias"
|
||
fi
|
||
done
|
||
|
||
if [[ "$WHAT_IF" != '1' ]]; then
|
||
eval "$(ssh-agent -s)" >/dev/null
|
||
for key_name in "${KEYS_SEEN[@]:-}"; do
|
||
[[ -f "$SSH_DIR/$key_name" ]] && ssh-add "$SSH_DIR/$key_name" >/dev/null 2>&1 || true
|
||
done
|
||
fi
|
||
|
||
log_ok 'SSH setup complete'
|