83 lines
2.9 KiB
Bash
Executable File
83 lines
2.9 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# claudio — run Claude Code isolated in a podman container.
|
|
#
|
|
# Install: keep this file next to the Containerfile, then
|
|
# alias claudio=/path/to/claudio.sh (e.g. in ~/.bashrc)
|
|
#
|
|
# Usage: claudio [claude args...] run Claude Code in the current directory
|
|
# claudio shell [bash args] open a bash shell in the container instead
|
|
# claudio build build the image
|
|
# claudio update rebuild from scratch (latest Debian + Claude Code)
|
|
#
|
|
# The current directory is mounted at its own path. If it contains a .claudio
|
|
# file, that file is sourced (bash) for extra mounts and options, and can set
|
|
# GUI=1 / AUDIO=1 to share the host's Wayland / PulseAudio sockets.
|
|
# The container's home (/home/claude: login, settings, history) lives in the
|
|
# global podman volume "claudio-home".
|
|
set -euo pipefail
|
|
|
|
IMAGE=localhost/claudio:latest
|
|
VOLUME=claudio-home
|
|
DIR="$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")"
|
|
|
|
build() { podman build -t "$IMAGE" "$@" "$DIR"; }
|
|
|
|
case "${1:-}" in
|
|
build) build; exit ;;
|
|
update) build --pull=always --no-cache && podman image prune -f; exit ;;
|
|
esac
|
|
|
|
if [[ "$PWD" == "$HOME" || "$PWD" == / ]]; then
|
|
echo "claudio: refusing to mount $PWD — run it from a project directory." >&2
|
|
exit 1
|
|
fi
|
|
|
|
podman image exists "$IMAGE" || build
|
|
|
|
# Project settings (see the .claudio template).
|
|
MOUNTS=() PODMAN_ARGS=() CLAUDE_ARGS=() GUI=0 AUDIO=0
|
|
if [[ -f .claudio ]]; then source ./.claudio; fi
|
|
|
|
args=(-v "$PWD:$PWD" -w "$PWD" -v "$VOLUME:/home/claude")
|
|
for m in "${MOUNTS[@]}"; do
|
|
[[ "$m" == *:* ]] || m="$m:$m" # bare path -> same path inside the container
|
|
args+=(-v "$m")
|
|
done
|
|
|
|
# GUI/AUDIO: share a socket from the host's runtime dir into the container's.
|
|
host_run="${XDG_RUNTIME_DIR:-/run/user/$(id -u)}"
|
|
share() {
|
|
[[ -S "$host_run/$1" ]] || { echo "claudio: no socket at $host_run/$1" >&2; exit 1; }
|
|
args+=(-v "$host_run/$1:/run/user/1000/$1")
|
|
}
|
|
|
|
if [[ "$GUI" == 1 ]]; then
|
|
wl="${WAYLAND_DISPLAY:-wayland-0}"
|
|
share "$wl"
|
|
args+=(-e WAYLAND_DISPLAY="$wl" -e GDK_BACKEND=wayland -e QT_QPA_PLATFORM=wayland
|
|
-e SDL_VIDEODRIVER=wayland -e ELECTRON_OZONE_PLATFORM_HINT=wayland)
|
|
if [[ -e /dev/dri ]]; then args+=(--device /dev/dri); fi
|
|
fi
|
|
|
|
if [[ "$AUDIO" == 1 ]]; then
|
|
share pulse/native
|
|
args+=(-e PULSE_SERVER=unix:/run/user/1000/pulse/native)
|
|
fi
|
|
|
|
if [[ "${1:-}" == shell ]]; then
|
|
shift
|
|
args+=(--entrypoint bash)
|
|
CLAUDE_ARGS=()
|
|
fi
|
|
|
|
if [[ -t 0 && -t 1 ]]; then args+=(-it); else args+=(-i); fi
|
|
|
|
# keep-id maps your host user to the container's uid 1000, so files written
|
|
# in mounts stay owned by you. label=disable avoids SELinux relabeling of mounts.
|
|
exec podman run --rm "${args[@]}" \
|
|
--userns=keep-id:uid=1000,gid=1000 \
|
|
--security-opt label=disable \
|
|
--hostname claudio \
|
|
"${PODMAN_ARGS[@]}" \
|
|
"$IMAGE" "${CLAUDE_ARGS[@]}" "$@"
|