#!/usr/bin/env bash set -euo pipefail main() { [[ "$(uname -s)" == Linux ]] || { printf 'Run this installer on Ubuntu.\n' >&2; return 1; } ((EUID != 0)) || { printf 'Run as your normal user; the installer uses sudo when needed.\n' >&2; return 1; } umask 077 local packages=() command -v curl >/dev/null 2>&1 || packages+=(curl) command -v git >/dev/null 2>&1 || packages+=(git) if ! command -v ssh >/dev/null 2>&1 || ! command -v ssh-keygen >/dev/null 2>&1; then packages+=(openssh-client) fi if ((${#packages[@]})); then sudo apt-get update sudo apt-get install -y "${packages[@]}" fi local temporary temporary_root temporary_root="$(cd -- "${TMPDIR:-/tmp}" && pwd -P)" temporary="$(mktemp -d "$temporary_root/homelab-install.XXXXXX")" [[ "$temporary" == "$temporary_root"/homelab-install.* && -d "$temporary" ]] || return 1 # Expand the directory now: the local variable is gone when EXIT runs. trap "$(printf 'rm -rf -- %q' "$temporary")" EXIT if ! command -v croc >/dev/null 2>&1; then printf 'Installing croc...\n' curl -fsSL https://getcroc.com -o "$temporary/install-croc.sh" bash "$temporary/install-croc.sh" hash -r command -v croc >/dev/null 2>&1 || { printf 'croc installation did not make the command available.\n' >&2; return 1; } fi local key_path=~/.ssh/id_ed25519 if [[ ! -e "$key_path" && ! -L "$key_path" ]]; then printf '\nOn the other computer, send your GitHub SSH private key:\n' printf ' croc send PATH_TO_id_ed25519\n' printf 'On Windows, for example:\n' printf ' croc send C:\\Users\\Ben\\.ssh\\id_ed25519\n' printf '\nEnter the transfer code below and receive the file named id_ed25519.\n' mkdir "$temporary/receive" (cd "$temporary/receive" && croc) [[ -f "$temporary/receive/id_ed25519" && ! -L "$temporary/receive/id_ed25519" ]] \ || { printf 'The transfer did not include a file named id_ed25519.\n' >&2; return 1; } chmod 600 "$temporary/receive/id_ed25519" ssh-keygen -y -f "$temporary/receive/id_ed25519" >/dev/null mkdir -p ~/.ssh chmod 700 ~/.ssh [[ ! -e "$key_path" && ! -L "$key_path" ]] \ || { printf 'An SSH key appeared during the transfer; leaving it unchanged.\n' >&2; return 1; } install -m 600 "$temporary/receive/id_ed25519" "$key_path" else [[ -f "$key_path" ]] || { printf '%s is not a regular key file.\n' "$key_path" >&2; return 1; } printf 'Using existing SSH key: %s\n' "$key_path" chmod 600 "$key_path" fi printf '\nTesting GitHub SSH access. Confirm GitHub host identity if prompted.\n' local response ssh_status=0 response="$(ssh -i "$key_path" -o IdentitiesOnly=yes -T git@github.com 2>&1)" || ssh_status=$? printf '%s\n' "$response" # GitHub returns 1 even when authentication succeeds (no shell access). if [[ "$ssh_status" -ne 1 && "$ssh_status" -ne 0 ]] \ || [[ "$response" != *'successfully authenticated'* ]]; then printf 'GitHub authentication failed. Add the matching public key to your GitHub account.\n' >&2 return 1 fi local repo_dir=~/homelab if [[ -e "$repo_dir" || -L "$repo_dir" ]]; then local origin [[ -d "$repo_dir/.git" ]] || { printf '%s already exists and is not this repository.\n' "$repo_dir" >&2; return 1; } origin="$(git -C "$repo_dir" remote get-url origin)" case "$origin" in git@github.com:Bennycopter/homelab.git|https://github.com/Bennycopter/homelab.git) printf 'Using existing checkout: %s\n' "$repo_dir" ;; *) printf 'The existing checkout has a different origin; leaving it unchanged.\n' >&2; return 1 ;; esac else GIT_SSH_COMMAND="ssh -i $(printf '%q' "$key_path") -o IdentitiesOnly=yes" \ git clone git@github.com:Bennycopter/homelab.git "$repo_dir" fi [[ -f "$repo_dir/bootstrap.sh" ]] || { printf 'bootstrap.sh is missing from the downloaded checkout.\n' >&2; return 1; } bash "$repo_dir/bootstrap.sh" --no-menu --bin-dir /usr/local/bin printf '\nReady. Run homelab to open the menu, or homelab bootstrap.\n' } # Bash is reading the script from a pipe. Interactive commands need the terminal. if ! { true < /dev/tty; } 2>/dev/null; then printf 'Run this installer in an interactive terminal or SSH session.\n' >&2 exit 1 fi main "$@" < /dev/tty