Files
matej.nix/scripts/ephvm-run.sh
2026-03-15 10:05:06 +01:00

90 lines
2.0 KiB
Bash
Executable File

#!/usr/bin/env bash
set -euo pipefail
SSH_PORT=2222
MEMORY=8G
CPUS=4
MOUNTS=()
CLAUDE=false
IMAGE=""
usage() {
cat <<EOF
Usage: ephvm-run.sh <image.qcow2> [options]
Options:
--mount <path> Mount host directory into VM (repeatable)
--claude Mount claude config dir (auto-detects from CLAUDE_CONFIG_DIR or ~/.claude)
--memory <size> VM memory (default: 8G)
--cpus <n> VM CPUs (default: 4)
--ssh-port <port> SSH port forward (default: 2222)
EOF
exit 1
}
[ "${1:-}" ] || usage
IMAGE="$1"
shift
while [ $# -gt 0 ]; do
case "$1" in
--mount) MOUNTS+=("$2"); shift 2 ;;
--claude) CLAUDE=true; shift ;;
--memory) MEMORY="$2"; shift 2 ;;
--cpus) CPUS="$2"; shift 2 ;;
--ssh-port) SSH_PORT="$2"; shift 2 ;;
*) echo "unknown option: $1"; usage ;;
esac
done
if [ ! -f "$IMAGE" ]; then
echo "error: image not found: $IMAGE"
exit 1
fi
ACCEL="tcg"
[ -r /dev/kvm ] && ACCEL="kvm"
QEMU_ARGS=(
qemu-system-x86_64
-accel "$ACCEL"
-m "$MEMORY"
-smp "$CPUS"
-drive "file=$IMAGE,format=qcow2,snapshot=on"
-nic "user,hostfwd=tcp::${SSH_PORT}-:22"
-nographic
)
if [ "$ACCEL" != "tcg" ]; then
QEMU_ARGS+=(-cpu host)
fi
FS_ID=0
for mount_path in "${MOUNTS[@]}"; do
mount_path=$(realpath "$mount_path")
name=$(basename "$mount_path")
QEMU_ARGS+=(
-virtfs "local,path=$mount_path,mount_tag=mount_$name,security_model=none,id=fs${FS_ID}"
)
FS_ID=$((FS_ID + 1))
done
if [ "$CLAUDE" = true ]; then
if [ -n "${CLAUDE_CONFIG_DIR:-}" ]; then
claude_dir=$(realpath "$CLAUDE_CONFIG_DIR")
else
# symlink .claude.json into ~/.claude so the VM sees it via one 9p mount
claude_dir=$(realpath "$HOME/.claude")
if [ -f "$HOME/.claude.json" ] && [ ! -e "$claude_dir/.claude.json" ]; then
ln -s "$HOME/.claude.json" "$claude_dir/.claude.json"
fi
fi
QEMU_ARGS+=(
-virtfs "local,path=$claude_dir,mount_tag=claude,security_model=none,id=fs${FS_ID}"
)
fi
exec "${QEMU_ARGS[@]}"