SSH to 172.16.3.20:2222 is unreachable when not on local network. Updated remotes, docs, and migration script to use https://git.azcomputerguru.com instead. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
297 lines
11 KiB
Bash
297 lines
11 KiB
Bash
#!/bin/bash
|
|
###############################################################################
|
|
# migration-restore.sh
|
|
#
|
|
# Restores a ClaudeTools environment from an encrypted migration archive.
|
|
# Works in Git Bash on Windows AND native Linux bash.
|
|
#
|
|
# Usage: ./migration-restore.sh <archive.tar.gpg> [target_dir]
|
|
# archive.tar.gpg Path to the encrypted migration archive
|
|
# target_dir Where to clone/restore (default: $HOME/ClaudeTools)
|
|
###############################################################################
|
|
|
|
set -euo pipefail
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Globals
|
|
# ---------------------------------------------------------------------------
|
|
ARCHIVE_PATH="${1:-}"
|
|
TARGET_DIR="${2:-"${HOME}/ClaudeTools"}"
|
|
GITEA_REPO="https://git.azcomputerguru.com/azcomputerguru/claudetools.git"
|
|
TEMP_EXTRACT=""
|
|
WARN_COUNT=0
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Helpers
|
|
# ---------------------------------------------------------------------------
|
|
log_info() { echo "[INFO] $*"; }
|
|
log_ok() { echo "[OK] $*"; }
|
|
log_warn() { echo "[WARNING] $*"; WARN_COUNT=$((WARN_COUNT + 1)); }
|
|
log_error() { echo "[ERROR] $*"; }
|
|
|
|
cleanup() {
|
|
if [[ -n "$TEMP_EXTRACT" && -d "$TEMP_EXTRACT" ]]; then
|
|
log_info "Cleaning up temporary extraction directory..."
|
|
rm -rf "$TEMP_EXTRACT"
|
|
fi
|
|
}
|
|
trap cleanup EXIT
|
|
|
|
check_tool() {
|
|
local tool="$1"
|
|
if ! command -v "$tool" &>/dev/null; then
|
|
log_error "Required tool not found: ${tool}"
|
|
log_error "Please install ${tool} before running this script."
|
|
exit 1
|
|
fi
|
|
log_ok "Found: ${tool}"
|
|
}
|
|
|
|
# Derive the Claude projects directory name from the target path.
|
|
# On Windows (Git Bash): /d/ClaudeTools -> D--ClaudeTools
|
|
# On Linux: /home/user/ClaudeTools -> -home-user-ClaudeTools
|
|
derive_claude_project_name() {
|
|
local abs_target
|
|
abs_target="$(cd "$TARGET_DIR" && pwd)"
|
|
|
|
# Check if we are on Windows (Git Bash) by looking at path format
|
|
if [[ "$abs_target" =~ ^/([a-zA-Z])/(.*) ]]; then
|
|
# Git Bash path: /d/ClaudeTools -> D--ClaudeTools
|
|
local drive="${BASH_REMATCH[1]^^}"
|
|
local rest="${BASH_REMATCH[2]}"
|
|
echo "${drive}--${rest//\//-}"
|
|
elif [[ "$abs_target" =~ ^([a-zA-Z]):(.*) ]]; then
|
|
# Raw Windows path: D:\ClaudeTools -> D--ClaudeTools
|
|
local drive="${BASH_REMATCH[1]^^}"
|
|
local rest="${BASH_REMATCH[2]}"
|
|
rest="${rest//\\/-}"
|
|
rest="${rest//\//-}"
|
|
rest="${rest#-}"
|
|
echo "${drive}--${rest}"
|
|
else
|
|
# Linux/macOS: /home/user/ClaudeTools -> -home-user-ClaudeTools
|
|
local name="${abs_target//\//-}"
|
|
name="${name#-}"
|
|
echo "${name}"
|
|
fi
|
|
}
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Main
|
|
# ---------------------------------------------------------------------------
|
|
main() {
|
|
echo "============================================================"
|
|
echo " ClaudeTools Migration Restore"
|
|
echo " $(date '+%Y-%m-%d %H:%M:%S')"
|
|
echo "============================================================"
|
|
echo ""
|
|
|
|
# ------------------------------------------------------------------
|
|
# Validate arguments
|
|
# ------------------------------------------------------------------
|
|
if [[ -z "$ARCHIVE_PATH" ]]; then
|
|
log_error "Usage: $0 <archive.tar.gpg> [target_dir]"
|
|
log_error " archive.tar.gpg Encrypted migration archive"
|
|
log_error " target_dir Restore location (default: \$HOME/ClaudeTools)"
|
|
exit 1
|
|
fi
|
|
|
|
if [[ ! -f "$ARCHIVE_PATH" ]]; then
|
|
log_error "Archive not found: ${ARCHIVE_PATH}"
|
|
exit 1
|
|
fi
|
|
|
|
log_info "Archive: ${ARCHIVE_PATH}"
|
|
log_info "Target dir: ${TARGET_DIR}"
|
|
echo ""
|
|
|
|
# ------------------------------------------------------------------
|
|
# Check required tools
|
|
# ------------------------------------------------------------------
|
|
log_info "--- Checking required tools ---"
|
|
check_tool git
|
|
check_tool gpg
|
|
check_tool tar
|
|
echo ""
|
|
|
|
# ------------------------------------------------------------------
|
|
# Decrypt archive
|
|
# ------------------------------------------------------------------
|
|
log_info "--- Decrypting archive ---"
|
|
log_info "You will be prompted for the passphrase."
|
|
echo ""
|
|
|
|
TEMP_EXTRACT="$(mktemp -d 2>/dev/null || mktemp -d -t 'migration-restore')"
|
|
local tar_tmp="${TEMP_EXTRACT}/archive.tar"
|
|
|
|
if [[ -n "${GPG_PASSPHRASE:-}" ]]; then
|
|
echo "$GPG_PASSPHRASE" | gpg --batch --yes --passphrase-fd 0 \
|
|
--decrypt --output "$tar_tmp" "$ARCHIVE_PATH"
|
|
else
|
|
gpg --decrypt --output "$tar_tmp" "$ARCHIVE_PATH"
|
|
fi
|
|
|
|
if [[ ! -f "$tar_tmp" ]]; then
|
|
log_error "Decryption failed. Check your passphrase and try again."
|
|
exit 1
|
|
fi
|
|
log_ok "Archive decrypted."
|
|
echo ""
|
|
|
|
# ------------------------------------------------------------------
|
|
# Extract archive to temp location
|
|
# ------------------------------------------------------------------
|
|
log_info "--- Extracting archive ---"
|
|
local extract_dir="${TEMP_EXTRACT}/contents"
|
|
mkdir -p "$extract_dir"
|
|
tar -xf "$tar_tmp" -C "$extract_dir"
|
|
rm -f "$tar_tmp"
|
|
log_ok "Archive extracted."
|
|
|
|
# Show manifest if present
|
|
if [[ -f "${extract_dir}/MIGRATION_MANIFEST.txt" ]]; then
|
|
echo ""
|
|
log_info "--- Migration Manifest ---"
|
|
cat "${extract_dir}/MIGRATION_MANIFEST.txt"
|
|
echo ""
|
|
fi
|
|
|
|
# ------------------------------------------------------------------
|
|
# Clone repository
|
|
# ------------------------------------------------------------------
|
|
log_info "--- Cloning repository ---"
|
|
|
|
if [[ -d "$TARGET_DIR/.git" ]]; then
|
|
log_warn "Target directory already contains a git repo: ${TARGET_DIR}"
|
|
log_info "Skipping clone; will overlay migration files into existing repo."
|
|
elif [[ -d "$TARGET_DIR" ]]; then
|
|
# Directory exists but is not a git repo
|
|
log_warn "Target directory exists but is not a git repo: ${TARGET_DIR}"
|
|
log_info "Attempting clone into existing directory..."
|
|
git clone "$GITEA_REPO" "${TARGET_DIR}.tmp"
|
|
# Move .git and tracked files into existing directory
|
|
mv "${TARGET_DIR}.tmp/.git" "${TARGET_DIR}/"
|
|
# Checkout working tree into existing directory
|
|
(cd "$TARGET_DIR" && git checkout -- .)
|
|
rm -rf "${TARGET_DIR}.tmp"
|
|
log_ok "Cloned repository into existing directory."
|
|
else
|
|
git clone "$GITEA_REPO" "$TARGET_DIR"
|
|
log_ok "Cloned repository to: ${TARGET_DIR}"
|
|
fi
|
|
echo ""
|
|
|
|
# ------------------------------------------------------------------
|
|
# Overlay non-git files from migration archive
|
|
# ------------------------------------------------------------------
|
|
log_info "--- Restoring non-git files ---"
|
|
|
|
# Copy everything except claude-context (handled separately) and manifest
|
|
local item
|
|
for item in "$extract_dir"/*; do
|
|
local basename
|
|
basename="$(basename "$item")"
|
|
|
|
# Skip claude-context dir and manifest
|
|
if [[ "$basename" == "claude-context" || "$basename" == "MIGRATION_MANIFEST.txt" ]]; then
|
|
continue
|
|
fi
|
|
|
|
if [[ -d "$item" ]]; then
|
|
cp -a "$item" "$TARGET_DIR/"
|
|
log_ok "Restored directory: ${basename}"
|
|
elif [[ -f "$item" ]]; then
|
|
cp -a "$item" "$TARGET_DIR/"
|
|
log_ok "Restored file: ${basename}"
|
|
fi
|
|
done
|
|
|
|
# Handle dotfiles (hidden files/dirs from archive root)
|
|
for item in "$extract_dir"/.*; do
|
|
local basename
|
|
basename="$(basename "$item")"
|
|
[[ "$basename" == "." || "$basename" == ".." ]] && continue
|
|
|
|
if [[ -d "$item" ]]; then
|
|
# Merge directory contents (e.g., .claude/)
|
|
cp -a "$item"/. "$TARGET_DIR/${basename}/" 2>/dev/null || cp -a "$item" "$TARGET_DIR/"
|
|
log_ok "Restored directory: ${basename}"
|
|
elif [[ -f "$item" ]]; then
|
|
cp -a "$item" "$TARGET_DIR/"
|
|
log_ok "Restored file: ${basename}"
|
|
fi
|
|
done
|
|
echo ""
|
|
|
|
# ------------------------------------------------------------------
|
|
# Restore Claude AI context
|
|
# ------------------------------------------------------------------
|
|
log_info "--- Restoring Claude AI context ---"
|
|
|
|
local claude_ctx_src="${extract_dir}/claude-context"
|
|
if [[ -d "$claude_ctx_src" ]]; then
|
|
local project_name
|
|
project_name="$(derive_claude_project_name)"
|
|
local claude_ctx_dst="${HOME}/.claude/projects/${project_name}"
|
|
|
|
mkdir -p "$claude_ctx_dst"
|
|
cp -a "$claude_ctx_src"/. "$claude_ctx_dst/"
|
|
log_ok "Restored Claude context to: ${claude_ctx_dst}"
|
|
else
|
|
log_warn "No claude-context directory found in archive. Skipping."
|
|
fi
|
|
echo ""
|
|
|
|
# ------------------------------------------------------------------
|
|
# Initialize submodules
|
|
# ------------------------------------------------------------------
|
|
log_info "--- Initializing git submodules ---"
|
|
(cd "$TARGET_DIR" && git submodule update --init --recursive) && \
|
|
log_ok "Submodules initialized." || \
|
|
log_warn "Submodule initialization had issues. You may need to run it manually."
|
|
echo ""
|
|
|
|
# ------------------------------------------------------------------
|
|
# Summary and post-restore checklist
|
|
# ------------------------------------------------------------------
|
|
echo "============================================================"
|
|
echo " Restore Complete"
|
|
echo "============================================================"
|
|
echo ""
|
|
echo " Target: ${TARGET_DIR}"
|
|
echo " Warnings: ${WARN_COUNT}"
|
|
echo ""
|
|
echo "============================================================"
|
|
echo " Post-Restore Checklist"
|
|
echo "============================================================"
|
|
echo ""
|
|
echo " [ ] Verify credentials.md contains correct, unredacted values"
|
|
echo " File: ${TARGET_DIR}/credentials.md"
|
|
echo ""
|
|
echo " [ ] Set up Python virtual environment for MCP servers"
|
|
echo " cd ${TARGET_DIR} && python -m venv .venv"
|
|
echo " source .venv/bin/activate (or .venv\\Scripts\\activate on Windows)"
|
|
echo " pip install -r requirements.txt"
|
|
echo ""
|
|
echo " [ ] Configure Claude Code CLI"
|
|
echo " Run: claude (first launch will prompt for authentication)"
|
|
echo " Verify .claude/ context was restored correctly"
|
|
echo ""
|
|
echo " [ ] Test Gitea SSH access"
|
|
echo " Run: ssh -p 2222 git@172.16.3.20"
|
|
echo " If it fails, copy your SSH keys and update ~/.ssh/config"
|
|
echo ""
|
|
echo " [ ] Rebuild grepai index"
|
|
echo " The semantic search index is machine-specific."
|
|
echo " Run grepai indexing from within Claude Code."
|
|
echo ""
|
|
echo " [ ] Verify .env and .mcp.json values are correct for new machine"
|
|
echo ""
|
|
echo " [ ] Test database connectivity"
|
|
echo " Ensure 172.16.3.30:3306 is reachable from this machine"
|
|
echo ""
|
|
log_ok "Done."
|
|
}
|
|
|
|
main "$@"
|