diff --git a/run-ai-image.sh b/run-ai-image.sh index 2b59137..e349575 100755 --- a/run-ai-image.sh +++ b/run-ai-image.sh @@ -10,6 +10,7 @@ cd "$(dirname "$0")" [ -f .ai-config ] || { echo "No .ai-config yet - run ./setup.sh first." >&2; exit 1; } # shellcheck disable=SC1091 source .ai-config +PORT=${AI_PORT:-$PORT} # override for one run: AI_PORT=11500 ./run-ai-image.sh running() { [ "$(docker inspect -f '{{.State.Running}}' "$CONTAINER_NAME" 2>/dev/null)" = "true" ]; } exists() { docker inspect "$CONTAINER_NAME" >/dev/null 2>&1; } @@ -28,6 +29,12 @@ if running; then echo "Already running on http://127.0.0.1:$PORT" else exists && docker rm -f "$CONTAINER_NAME" >/dev/null + if (exec 3<>"/dev/tcp/127.0.0.1/$PORT") 2>/dev/null; then + echo "Port $PORT is already in use by something else (a host Ollama, another container?):" >&2 + docker ps --filter "publish=$PORT" --format ' container {{.Names}}' >&2 || true + echo "Re-run ./setup.sh to pick a free port, or use: AI_PORT= ./run-ai-image.sh" >&2 + exit 1 + fi args=(-d --name "$CONTAINER_NAME" -p "127.0.0.1:$PORT:11434" --restart unless-stopped) [ "$USE_GPU" = "1" ] && args+=(--gpus all) docker run "${args[@]}" "$IMAGE_NAME" >/dev/null diff --git a/setup.sh b/setup.sh index ac2c394..06af0c2 100755 --- a/setup.sh +++ b/setup.sh @@ -4,24 +4,26 @@ # .ai-config (settings used by build-ai-image.sh / run-ai-image.sh) # RUN_AIDER.md (how to run aider against the container on this machine) # -# Usage: ./setup.sh [--model ] [--gpu|--no-gpu] [--yes] +# Usage: ./setup.sh [--model ] [--gpu|--no-gpu] [--port ] [--yes] set -euo pipefail cd "$(dirname "$0")" -FORCE_MODEL=""; FORCE_GPU=""; ASSUME_YES=0 +FORCE_MODEL=""; FORCE_GPU=""; ASSUME_YES=0; FORCE_PORT="" while [ $# -gt 0 ]; do case "$1" in --model) FORCE_MODEL="${2:?--model needs a value}"; shift ;; --gpu) FORCE_GPU=1 ;; --no-gpu) FORCE_GPU=0 ;; + --port) FORCE_PORT="${2:?--port needs a value}"; shift ;; --yes|-y) ASSUME_YES=1 ;; - -h|--help) sed -n '2,8p' "$0" | sed 's/^# \?//'; exit 0 ;; + -h|--help) sed -n '2,9p' "$0" | sed 's/^# \?//'; exit 0 ;; *) echo "Unknown option: $1" >&2; exit 1 ;; esac shift done -IMAGE_NAME="local-ai"; CONTAINER_NAME="local-ai"; PORT=11434 +IMAGE_NAME="local-ai"; CONTAINER_NAME="local-ai"; PORT="" +port_busy() { (exec 3<>"/dev/tcp/127.0.0.1/$1") 2>/dev/null; } NOTES=() # machine-specific remarks, echoed to the terminal and RUN_AIDER.md # Distro-specific commands to make an NVIDIA GPU usable from Docker. @@ -219,6 +221,19 @@ if awk -v d="$sel_disk" -v f="$disk_gb" 'BEGIN{exit !(d*2>f)}'; then NOTES+=("Low disk space: ${disk_gb} GB free, the build needs roughly 2x the model size (${sel_disk} GB).") fi +# ------------------------------------------------------------ host port +# Default 11434 (Ollama's standard); if something else (e.g. a host Ollama) +# already uses it, take the next free port. +if [ -n "$FORCE_PORT" ]; then PORT=$FORCE_PORT +else + for PORT in $(seq 11434 11534); do + # our own container from an earlier run doesn't count as busy + [ "$(docker port "$CONTAINER_NAME" 11434/tcp 2>/dev/null | head -1 | sed 's/.*://')" = "$PORT" ] && break + port_busy "$PORT" || break + done + [ "$PORT" != 11434 ] && NOTES+=("Port 11434 is already in use on this machine, so the AI container will use port $PORT.") +fi + # ------------------------------------------------------------------- output USE_GPU=$gpu_usable sed -e "s|@@MODEL@@|$MODEL|g" -e "s|@@CONTEXT@@|$CONTEXT|g" Dockerfile.template > Dockerfile