all repos — dot @ master

Shell, editor and terminal configuration, and the tools that go with them

link/ssh.sh (view raw)

 1
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
#!/bin/bash

# ssh client config. Dry run by default; pass `yes` to apply.

set -euo pipefail

# shellcheck source-path=SCRIPTDIR
# shellcheck source=lib.sh
source "$(dirname "$0")/lib.sh" "$@"

# ~/.ssh/config itself is the entry point: it reads ~/.config/ssh/config-fleet
# first, if another layer has linked one in, then this host's
# ~/.config/ssh/config-local -- see config/ssh/entry. Only the file is linked;
# ~/.ssh stays a real directory, because what else lives in it (keys,
# known_hosts, control and agent sockets) is this host's own.
#
# Each step below is skipped, loudly, rather than forced, because link() starts
# with `rm -rf`:
#   - a symlinked ~/.ssh. On the Macs it once pointed into iCloud Drive, a
#     single directory shared by two machines, so replacing a file in it on one
#     deleted it on the other.
#   - a real ~/.ssh/config holding anything but `Include ~/.config/ssh/config`,
#     the one line each host was given by hand on 2026-09-07, before this file
#     was linked. Anything else is host-specific and belongs in config-local;
#     move it there first.
ssh_dir="$HOME/.ssh"
if [[ -L "$ssh_dir" ]]
then
  red "$ssh_dir is a symlink to $(readlink "$ssh_dir"); skipping ssh links -- make it a real local directory first"
  exit 0
fi

if [[ -z "${is_dry_run}" && ! -d "$ssh_dir" ]]
then
  install -d -m 700 "$ssh_dir"
fi

ssh_config_extra=""
if [[ -f "$ssh_dir/config" && ! -L "$ssh_dir/config" ]]
then
  ssh_config_extra="$(grep -vE '^[[:space:]]*(#|$)' "$ssh_dir/config" \
    | grep -vxF 'Include ~/.config/ssh/config' || true)"
fi
if [[ -n "$ssh_config_extra" ]]
then
  red "$ssh_dir/config has host-specific lines; move them to ~/.config/ssh/config-local, then re-run"
else
  link "$config_dir/ssh/entry" "$ssh_dir/config"
fi

# Links this repo made while host blocks and their key pins were tracked here,
# now that another layer owns them: only a dangling link in ~/.config/ssh that
# points into this repo's config/ssh is removed -- never a real file, and never
# a link someone else made. Compared as real paths: the link holds whatever
# spelling this repo had when it was made, which on a machine whose checkout is
# reached through a symlink is not this run's.
ssh_repo_dir="$(realpath "$config_dir/ssh")"
for stale in "$xdg_config_dir"/ssh/*
do
  if [[ -L "$stale" && ! -e "$stale" ]] \
    && [[ "$(realpath "$(dirname "$(readlink "$stale")")" 2>/dev/null)" == "$ssh_repo_dir" ]]
  then
    if [[ -n "${is_dry_run}" ]]
    then
      yellow "$stale is a dangling link into this repo and will be removed"
    else
      rm "$stale"
    fi
  fi
done