devshell: Cache SSH connection settings
Cache all deploy node connection settings and invalidate them when HEAD or tracked working-tree contents change. Preserve SSH argument boundaries and support explicit refreshes.
This commit is contained in:
+6
-14
@@ -2,6 +2,11 @@
|
||||
let
|
||||
homeFlake = "$HOME/.config/home-manager/flake.nix";
|
||||
devKey = ".keys/dev.key";
|
||||
sshMachine = pkgs.writeShellApplication {
|
||||
name = "ssh-machine";
|
||||
runtimeInputs = with pkgs; [ coreutils git jq openssh ];
|
||||
text = builtins.readFile ./ssh-machine.sh;
|
||||
};
|
||||
in
|
||||
{
|
||||
commands = [
|
||||
@@ -52,20 +57,7 @@ in
|
||||
name = "ssh-machine";
|
||||
category = "utilities";
|
||||
help = "SSH to a machine by NixOS system or home-manager config name";
|
||||
command = ''
|
||||
name="$1"
|
||||
shift
|
||||
# Run from the project root so deploy's relative identity path (.keys/deploy.key) resolves
|
||||
cd "$PRJ_ROOT"
|
||||
# deploy-rs node names are the system/home name, with `@` mangled to `-at-`
|
||||
node="''${name//@/-at-}"
|
||||
# Single eval: resolve `user@host` plus merged (global + node) deploy-rs ssh options
|
||||
info="$(nix eval --raw .#deploy --apply 'd: let n = d.nodes."'"$node"'"; in "''${n.sshUser}@''${n.hostname} ''${builtins.concatStringsSep " " (d.sshOpts ++ n.sshOpts)}"')"
|
||||
target="''${info%% *}"
|
||||
opts="''${info#* }"
|
||||
# shellcheck disable=SC2086
|
||||
exec ${pkgs.openssh}/bin/ssh $opts "$target" "$@"
|
||||
'';
|
||||
command = ''exec ${sshMachine}/bin/ssh-machine "$@"'';
|
||||
}
|
||||
{
|
||||
name = "json2nix";
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
set -euo pipefail
|
||||
|
||||
refresh=false
|
||||
|
||||
if [[ ${1:-} == --refresh ]]; then
|
||||
refresh=true
|
||||
shift
|
||||
fi
|
||||
|
||||
if [[ $# == 0 && $refresh == false ]] || [[ ${1:-} == --help ]]; then
|
||||
echo 'Usage: ssh-machine [--refresh] <name> [cmd ...]'
|
||||
echo ' ssh-machine --refresh'
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Resolve deploy's relative identity path from the checkout root.
|
||||
cd "$PRJ_ROOT"
|
||||
|
||||
cache_dir=.cache/ssh-machine
|
||||
cache=$cache_dir/connections.json
|
||||
|
||||
fingerprint() {
|
||||
{
|
||||
git rev-parse HEAD &&
|
||||
git diff --no-ext-diff --no-textconv --binary HEAD -- &&
|
||||
git diff --no-ext-diff --no-textconv --binary --cached --
|
||||
} | sha256sum | cut -d ' ' -f 1
|
||||
}
|
||||
|
||||
key=$(fingerprint)
|
||||
|
||||
if [[ $refresh == true ]] || ! jq -e --arg key "$key" \
|
||||
'.fingerprint == $key and (.nodes | type == "object")' "$cache" >/dev/null 2>&1; then
|
||||
mkdir -p "$cache_dir"
|
||||
tmp=$(mktemp "$cache_dir/.connections.XXXXXXXX")
|
||||
trap 'rm -f "$tmp"' EXIT
|
||||
|
||||
echo 'Refreshing ssh-machine connection cache…' >&2
|
||||
|
||||
# Interpolation here belongs to Nix, not the shell.
|
||||
# shellcheck disable=SC2016
|
||||
nix eval --json .#deploy --apply '
|
||||
d: builtins.mapAttrs (_: n:
|
||||
d.sshOpts ++ n.sshOpts ++ [ "${n.sshUser}@${n.hostname}" ]
|
||||
) d.nodes
|
||||
' > "$tmp"
|
||||
|
||||
if [[ $(fingerprint) != "$key" ]]; then
|
||||
echo 'Checkout changed during evaluation; retry ssh-machine.' >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
payload=$(jq -c --arg key "$key" '{fingerprint: $key, nodes: .}' "$tmp")
|
||||
printf '%s\n' "$payload" > "$tmp"
|
||||
mv -f "$tmp" "$cache"
|
||||
trap - EXIT
|
||||
fi
|
||||
|
||||
[[ $# -gt 0 ]] || exit 0
|
||||
|
||||
name=$1
|
||||
shift
|
||||
|
||||
# deploy-rs mangles home configuration names containing @.
|
||||
node=${name//@/-at-}
|
||||
|
||||
args=$(jq -ce --arg node "$node" '
|
||||
.nodes | if has($node) then .[$node]
|
||||
else error("Unknown deploy node: " + $node) end
|
||||
' "$cache")
|
||||
|
||||
mapfile -d '' -t ssh_args < <(jq -j '.[] | ., "\u0000"' <<< "$args")
|
||||
|
||||
exec ssh "${ssh_args[@]}" "$@"
|
||||
Reference in New Issue
Block a user