e376d0aec9
Captures the full workspace state built up to this point:
- CLAUDE.md + README.md describing conventions and the four-host fleet
(ana-ml2, ana-docker, nh3-docker, esh-docker-vm).
- Per-host notes under servers/<host>/ with ssh-target fallback files
and latest system-details snapshots (two in-compose credential leaks
scrubbed; the upstream compose files still need to move those to .env).
- scripts/: server_inspect.sh (read-only remote diagnostic),
refresh-server-info.sh (dir-driven discovery + snapshot capture with
validation warnings), add-host.sh, sync-stacks.sh (pull
compose/conf trees), deploy-stack.sh (push with per-file diff + prompt).
- stacks/: canonical compose for backrest, beszel, dozzle, llama-swap,
rest-server-ana, rest-server-nh3, vllm-qwen3, plus the retired
infinity reference. All use the .env-driven + traefik-net + homepage
label pattern.
- configs/restic/ana-docker/: first resticprofile config + pre-backup
hook (Synapse pg_dump, Seafile mysqldump, Vaultwarden SQLite); templates
for the other three hosts to come.
- docs/pfi/: general infrastructure reference carried over.
- .gitignore excludes .env, stacks-mirror/, and assorted secret/state
filenames to prevent re-leaks on later commits.
70 lines
2.0 KiB
Bash
Executable File
70 lines
2.0 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# add-host.sh — register a new server so refresh-server-info.sh picks it up.
|
|
#
|
|
# Creates servers/<name>/ and writes servers/<name>/ssh-target with the
|
|
# given IP (or user@ip). The next `scripts/refresh-server-info.sh <name>`
|
|
# run will discover the host and pull its first system-details.txt.
|
|
#
|
|
# Usage:
|
|
# scripts/add-host.sh <name> <ip-or-user@ip>
|
|
# scripts/add-host.sh <name> <ip-or-user@ip> --force # overwrite existing
|
|
#
|
|
# Example:
|
|
# scripts/add-host.sh la-docker 10.60.50.12
|
|
# scripts/add-host.sh edge-01 admin@203.0.113.9
|
|
|
|
set -euo pipefail
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
SERVERS_DIR="$REPO_ROOT/servers"
|
|
|
|
FORCE=0
|
|
POSITIONAL=()
|
|
for arg in "$@"; do
|
|
case "$arg" in
|
|
-f|--force) FORCE=1 ;;
|
|
-h|--help) sed -n '2,14p' "$0"; exit 0 ;;
|
|
-*) echo "error: unknown flag $arg" >&2; exit 2 ;;
|
|
*) POSITIONAL+=("$arg") ;;
|
|
esac
|
|
done
|
|
|
|
if [ "${#POSITIONAL[@]}" -ne 2 ]; then
|
|
echo "usage: $(basename "$0") <name> <ip-or-user@ip> [--force]" >&2
|
|
exit 2
|
|
fi
|
|
|
|
name="${POSITIONAL[0]}"
|
|
target="${POSITIONAL[1]}"
|
|
|
|
if [[ ! "$name" =~ ^[a-zA-Z0-9][a-zA-Z0-9._-]*$ ]]; then
|
|
echo "error: invalid host name '$name' (expected [a-zA-Z0-9._-])" >&2
|
|
exit 2
|
|
fi
|
|
|
|
# Trivial sanity check on the target — not exhaustive, just catches typos.
|
|
if [[ -z "$target" ]] || [[ "$target" =~ [[:space:]] ]]; then
|
|
echo "error: invalid ssh target '$target'" >&2
|
|
exit 2
|
|
fi
|
|
|
|
host_dir="$SERVERS_DIR/$name"
|
|
ssh_target_file="$host_dir/ssh-target"
|
|
|
|
if [ -f "$ssh_target_file" ] && [ "$FORCE" -ne 1 ]; then
|
|
existing=$(awk 'NF{print $1; exit}' "$ssh_target_file")
|
|
if [ "$existing" = "$target" ]; then
|
|
echo "host '$name' already registered with target '$target' — nothing to do"
|
|
exit 0
|
|
fi
|
|
echo "error: $ssh_target_file already exists (currently '$existing'); pass --force to overwrite" >&2
|
|
exit 1
|
|
fi
|
|
|
|
mkdir -p "$host_dir"
|
|
printf '%s\n' "$target" > "$ssh_target_file"
|
|
|
|
printf 'registered: %s → %s\n' "$name" "$target"
|
|
printf 'next: scripts/refresh-server-info.sh %s\n' "$name"
|