#!/bin/bash # Default values WORKDIR="$(pwd)" CONFIG_DIR="/root" ARCH="x64" DOCKER_ARGS="" USE_NFS=false NFS_SERVER="10.23.23.3" # Function to show usage usage() { echo "Usage: $0 [OPTIONS] [DOCKER_ARGS...]" echo "Options:" echo " -w DIR Set working directory (default: current directory)" echo " -f DIR Set config directory on host (default: /root)" echo " -n [SERVER] Use NFS mount for config (default: 10.23.23.3:/bignas/claude-settings)" echo " -a ARCH Set architecture (x64 or aarch64, default: x64)" echo " -h Show this help message" echo "" echo "Note: -f and -n are mutually exclusive" echo "" echo "Examples:" echo " $0 # Use defaults" echo " $0 -w /home/user/project # Custom working directory" echo " $0 -f /home/user # Custom config directory" echo " $0 -n # Use NFS config from default server" echo " $0 -n 192.168.1.100 # Use NFS config from custom server" echo " $0 -a aarch64 # Use ARM64 image" echo " $0 -w /tmp -n 192.168.1.100 # Custom workdir + NFS config" echo " $0 -- --help # Pass --help to claude-code" exit 1 } # Function to check and install NFS client tools check_nfs_tools() { # Check if NFS tools are installed if command -v mount.nfs >/dev/null 2>&1; then return 0 # NFS tools are installed fi echo "NFS client tools are not installed on this system." echo "Docker requires NFS client tools to mount NFS volumes." echo "" echo "Would you like to install nfs-common now? (y/N)" read -r response case "$response" in [yY][eE][sS]|[yY]) echo "Installing nfs-common..." if apt update && apt install -y nfs-common; then echo "NFS client tools installed successfully!" return 0 else echo "Failed to install nfs-common. Please install manually:" >&2 echo "sudo apt update && sudo apt install -y nfs-common" >&2 exit 1 fi ;; *) echo "NFS client tools are required for NFS mounting. Please install manually:" >&2 echo "sudo apt update && sudo apt install -y nfs-common" >&2 exit 1 ;; esac } # Parse command line arguments while getopts "w:f:na:h" opt; do case $opt in w) WORKDIR="$OPTARG" ;; f) CONFIG_DIR="$OPTARG" ;; n) USE_NFS=true # Check if next argument looks like an IP/hostname (not starting with -) if [ $OPTIND -le $# ]; then eval "next_arg=\${$OPTIND}" if [[ "$next_arg" =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]] || [[ "$next_arg" =~ ^[a-zA-Z] ]]; then NFS_SERVER="$next_arg" OPTIND=$((OPTIND + 1)) fi fi ;; a) ARCH="$OPTARG" ;; h) usage ;; \?) echo "Invalid option: -$OPTARG" >&2 usage ;; :) echo "Option -$OPTARG requires an argument." >&2 usage ;; esac done # Shift processed options shift $((OPTIND-1)) # Remaining arguments are passed to docker DOCKER_ARGS="$@" # Check for mutually exclusive options if [ "$USE_NFS" = true ] && [ "$CONFIG_DIR" != "/root" ]; then echo "Error: Options -f and -n are mutually exclusive" >&2 echo "Use -f for local config directory OR -n for NFS config, not both" >&2 exit 1 fi # Validate directories if [ ! -d "$WORKDIR" ]; then echo "Error: Working directory '$WORKDIR' does not exist" >&2 exit 1 fi if [ "$USE_NFS" = false ] && [ ! -d "$CONFIG_DIR" ]; then echo "Error: Config directory '$CONFIG_DIR' does not exist" >&2 exit 1 fi # Check NFS tools if using NFS if [ "$USE_NFS" = true ]; then check_nfs_tools fi # Validate NFS server if using NFS (should always be set now) if [ "$USE_NFS" = true ] && [ -z "$NFS_SERVER" ]; then echo "Error: NFS server not specified" >&2 exit 1 fi # Validate architecture if [ "$ARCH" != "x64" ] && [ "$ARCH" != "aarch64" ]; then echo "Error: Architecture must be 'x64' or 'aarch64', got '$ARCH'" >&2 exit 1 fi # Set up Docker volume arguments based on config type if [ "$USE_NFS" = true ]; then echo "Using NFS config from $NFS_SERVER:/bignas/claude-settings" # Docker NFS volume arguments - mount NFS directly to /root CONFIG_VOLUME_ARGS="--mount type=volume,dst=/root,volume-driver=local,volume-opt=type=nfs,volume-opt=device=$NFS_SERVER:/bignas/claude-settings,volume-opt=o=addr=$NFS_SERVER" CONFIG_JSON_ARG="" CONFIG_DIR_ARG="" else # Local directory mounts CONFIG_VOLUME_ARGS="" CONFIG_JSON_ARG="-v $CONFIG_DIR/.claude.json:/root/.claude.json" CONFIG_DIR_ARG="-v $CONFIG_DIR/.claude:/root/.claude" fi # Create .claude.json if it doesn't exist if [ "$USE_NFS" = false ]; then CLAUDE_JSON="$CONFIG_DIR/.claude.json" if [ ! -f "$CLAUDE_JSON" ]; then echo "Creating default .claude.json at $CLAUDE_JSON" cat > "$CLAUDE_JSON" << 'EOF' { "numStartups": 0, "theme": "dark", "preferredNotifChannel": "auto", "verbose": false, "editorMode": "normal", "autoCompactEnabled": true, "hasSeenTasksHint": false, "queuedCommandUpHintCount": 0, "diffTool": "auto", "customApiKeyResponses": { "approved": [], "rejected": [] }, "env": {}, "tipsHistory": {}, "memoryUsageCount": 0, "parallelTasksCount": 1, "promptQueueUseCount": 0, "todoFeatureEnabled": true, "messageIdleNotifThresholdMs": 60000, "autoConnectIde": false } EOF fi # Create .claude directory if it doesn't exist CLAUDE_DIR="$CONFIG_DIR/.claude" if [ ! -d "$CLAUDE_DIR" ]; then echo "Creating .claude directory at $CLAUDE_DIR" mkdir -p "$CLAUDE_DIR" fi else echo "Note: When using NFS, config files will be created automatically on first run" fi # Run docker command exec docker run -it --rm \ -v "$WORKDIR:/app" \ $CONFIG_VOLUME_ARGS \ $CONFIG_JSON_ARG \ $CONFIG_DIR_ARG \ harbor.wgdns.net/claude-code/claude-code:$ARCH \ $DOCKER_ARGS