From 796a073c0a57b765ea78560a46c5d2b8ad2b1295 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 20 Mar 2026 22:22:02 +0000 Subject: [PATCH] Add Docker/Compose always-check and SSH key import to AI stack setup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Docker + Docker Compose check now runs on every invocation (not skipped on updates), installing if missing — ensures the stack can always run - Added Docker Compose plugin install as fallback if compose is missing - NVIDIA Container Toolkit check also always runs (not update-gated) - Added SSH key import wizard question: import public keys from GitHub (gh:username) and/or Launchpad (lp:username) using ssh-import-id, so the machine is accessible over SSH immediately after setup https://claude.ai/code/session_012gDnantBmFTWZGCiKyjazx --- laptop_full_setup.sh | 96 +++++++++++++++++++++++++++++++------------- 1 file changed, 67 insertions(+), 29 deletions(-) diff --git a/laptop_full_setup.sh b/laptop_full_setup.sh index 4d16288..f93fada 100755 --- a/laptop_full_setup.sh +++ b/laptop_full_setup.sh @@ -137,13 +137,31 @@ fi $INSTALL_AI || $INSTALL_KIWIX || $INSTALL_POSTINSTALL \ || die "Nothing selected — run again and select at least one option." +# ── Q1b: SSH keys from GitHub / Launchpad ───────────────────────────────────── +SSH_IMPORT_IDS=() # list of "gh:username" or "lp:username" entries + +if ! $IS_UPDATE || [[ ! -f "$HOME/.ssh/authorized_keys" ]]; then + echo "" + echo -e " ${BOLD}[SSH] Import SSH public keys? (for passwordless SSH into this machine)${NC}" + echo " Pulls your public keys from GitHub or Launchpad and adds them to" + echo " ~/.ssh/authorized_keys using ssh-import-id." + echo "" + echo " Examples: gh:yourusername lp:yourlaunchpadid" + echo " Multiple: gh:alice lp:alice" + echo "" + read -rp " Usernames (or Enter to skip): " SSH_INPUT + if [[ -n "$SSH_INPUT" ]]; then + read -ra SSH_IMPORT_IDS <<< "$SSH_INPUT" + fi +fi + # ── Q2: Storage for Ollama models ───────────────────────────────────────────── OLLAMA_STORAGE="volume" # "volume" = Docker named volume, else a host path OLLAMA_HOST_PATH="" if $INSTALL_AI; then echo "" - echo -e " ${BOLD}[2/5] Where should Ollama models be stored?${NC}" + echo -e " ${BOLD}[2/6] Where should Ollama models be stored?${NC}" echo " (Models are large — 5-50GB each. A fast SSD or large HDD is ideal.)" echo "" echo " Available mount points with >20GB free:" @@ -172,7 +190,7 @@ KIWIX_DIR="$BASE/kiwix" # default if $INSTALL_KIWIX; then echo "" - echo -e " ${BOLD}[3/5] Where should Kiwix ZIM files be stored?${NC}" + echo -e " ${BOLD}[3/6] Where should Kiwix ZIM files be stored?${NC}" echo " (ZIMs are large — Wikipedia alone is ~46GB. Total collection ~130GB.)" echo "" echo " Available mount points with >50GB free:" @@ -191,7 +209,7 @@ if $INSTALL_KIWIX; then # ── Q4: Download ZIMs now? ───────────────────────────────────────────────── echo "" - echo -e " ${BOLD}[4/5] Download ZIM files? (offline Wikipedia, Stack Overflow, etc.)${NC}" + echo -e " ${BOLD}[4/6] Download ZIM files? (offline Wikipedia, Stack Overflow, etc.)${NC}" echo " Downloads run in the background — safe to start now and leave overnight." echo " Total: ~130GB (Wikipedia 46GB, Project Gutenberg 60GB, others smaller)" echo "" @@ -211,7 +229,7 @@ if command -v ufw &>/dev/null && { [[ ! -f "$BASE/.ufw-done" ]] || $FORCE; }; th echo "" # Auto-detect likely subnet from current IP AUTO_SUBNET=$(echo "$LOCAL_IP" | awk -F. '{print $1"."$2"."$3".0/24"}') - echo -e " ${BOLD}[4b] Firewall — allow LAN access to services${NC}" + echo -e " ${BOLD}[4b/6] Firewall — allow LAN access to services${NC}" read -rp " LAN subnet [${AUTO_SUBNET}]: " LAN_INPUT LAN_SUBNET="${LAN_INPUT:-$AUTO_SUBNET}" [[ "$LAN_SUBNET" =~ /[0-9]+$ ]] || LAN_SUBNET="${LAN_SUBNET}/24" @@ -223,7 +241,7 @@ PULL_DEEPSEEK=false if $INSTALL_AI; then echo "" - echo -e " ${BOLD}[5/5] Download AI models now?${NC}" + echo -e " ${BOLD}[5/6] Download AI models now?${NC}" echo " GPU detected: ${GPU_NAME} (${VRAM_GB}GB VRAM) → recommended tier: ${GPU_TIER}" echo "" echo " Models that will be downloaded:" @@ -248,6 +266,7 @@ echo -e "${BOLD}━━━━━━━━━━━━━━━━━━━━━ echo "" $INSTALL_POSTINSTALL && echo " ✓ Full system setup (ubuntu-post-install.sh — interactive)" $INSTALL_AI && echo " ✓ AI stack (Ollama · WebUI · RAG · MCP · Gitea · InvokeAI · Portainer)" +[[ "${#SSH_IMPORT_IDS[@]}" -gt 0 ]] && echo " ✓ SSH keys → ${SSH_IMPORT_IDS[*]}" $INSTALL_KIWIX && echo " ✓ Kiwix offline library" if $INSTALL_AI; then if [[ "$OLLAMA_STORAGE" == "bind" ]]; then @@ -297,21 +316,28 @@ fi # ============================================================================= section "Prerequisites" # ============================================================================= -if ! $IS_UPDATE; then - # Docker - if ! command -v docker &>/dev/null; then - info "Installing Docker..." - curl -fsSL https://get.docker.com | sh - sudo usermod -aG docker "$USER" - ok "Docker installed — log out/in or run: newgrp docker" - else - ok "Docker: $(docker --version)" - fi +# ── Docker (always check — required whether new install or update) ───────────── +if ! command -v docker &>/dev/null; then + info "Installing Docker..." + curl -fsSL https://get.docker.com | sh + sudo usermod -aG docker "$USER" + warn "Added $USER to docker group — changes take effect on next login." + warn "If docker commands fail, run: newgrp docker" +else + ok "Docker: $(docker --version | sed 's/Docker version //')" +fi - # NVIDIA Container Toolkit - if command -v nvidia-smi &>/dev/null && \ - ! dpkg -l 2>/dev/null | grep -q nvidia-container-toolkit; then +# Ensure Docker Compose plugin is present (included with modern Docker) +if ! docker compose version &>/dev/null; then + info "Installing docker-compose-plugin..." + sudo apt-get install -y docker-compose-plugin +fi +ok "Docker Compose: $(docker compose version --short 2>/dev/null || echo 'ok')" + +# ── NVIDIA Container Toolkit ─────────────────────────────────────────────────── +if command -v nvidia-smi &>/dev/null; then + if ! dpkg -l 2>/dev/null | grep -q nvidia-container-toolkit; then info "Installing NVIDIA Container Toolkit..." curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey \ | sudo gpg --dearmor --yes \ @@ -324,20 +350,32 @@ if ! $IS_UPDATE; then sudo nvidia-ctk runtime configure --runtime=docker sudo systemctl restart docker ok "NVIDIA Container Toolkit installed" - elif command -v nvidia-smi &>/dev/null; then - ok "NVIDIA Container Toolkit already present" else - warn "No NVIDIA GPU detected — CPU-only (Ollama will use CPU)" + ok "NVIDIA Container Toolkit: already present" fi - - # ripgrep (used by MCP search_code tool) - if ! command -v rg &>/dev/null; then - sudo apt-get install -y ripgrep - ok "ripgrep installed" - fi - else - ok "Skipping prereq checks (update mode)" + info "No NVIDIA GPU detected — Ollama will run on CPU" +fi + +# ── ripgrep (used by MCP search_code tool) ───────────────────────────────────── +if ! command -v rg &>/dev/null; then + sudo apt-get install -y ripgrep + ok "ripgrep installed" +fi + +# ── SSH key import from GitHub / Launchpad ───────────────────────────────────── +if [[ "${#SSH_IMPORT_IDS[@]}" -gt 0 ]]; then + section "SSH Keys" + if ! command -v ssh-import-id &>/dev/null; then + info "Installing ssh-import-id..." + sudo apt-get install -y ssh-import-id + fi + mkdir -p "$HOME/.ssh" + chmod 700 "$HOME/.ssh" + for id in "${SSH_IMPORT_IDS[@]}"; do + info "Importing SSH keys: $id" + ssh-import-id "$id" && ok "Keys imported: $id" || warn "Failed to import: $id" + done fi # =============================================================================