diff --git a/scripts/sandbox.sh b/scripts/sandbox.sh new file mode 100755 index 00000000..44efe9b9 --- /dev/null +++ b/scripts/sandbox.sh @@ -0,0 +1,108 @@ +#!/bin/bash +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set -euo pipefail + +# parse flags +interactive=false +while getopts "i" opt; do + case "$opt" in + \?) + echo "usage: sandbox.sh [-i] [sandbox-name-or-index = AUTO] [command... = bash -l]" + echo " -i: enable interactive mode for custom command (enabled by default for login shell)" + echo " (WARNING: interactive mode causes stderr to be redirected to stdout)" + exit 1 + ;; + i) + interactive=true + if [ ! -t 0 ]; then + echo "ERROR: interactive mode (-i) requested without a terminal attached" + exit 1 + fi + ;; + esac +done +shift $((OPTIND - 1)) + + +IMAGE=gemini-code-sandbox + +# use docker if installed, otherwise try to use podman instead +if command -v docker &> /dev/null; then + CMD=docker +elif command -v podman &> /dev/null; then + CMD=podman +else + echo "ERROR: missing docker or podman for sandboxing" + exit 1 +fi + +# list all containers running on sandbox image +sandboxes=() +while IFS= read -r line; do + sandboxes+=("$line") +done < <($CMD ps --filter "ancestor=$IMAGE" --format "{{.Names}}") + +# take first argument as sandbox name if it starts with image name or is an integer +# otherwise require a unique sandbox to be running and take its name +if [[ "${1:-}" =~ ^$IMAGE(-[0-9]+)?$ ]]; then + SANDBOX=$1 + shift +elif [[ "${1:-}" =~ ^[0-9]+$ ]]; then + SANDBOX=$IMAGE-$1 + shift +else + # exit if no sandbox is running + if [ ${#sandboxes[@]} -eq 0 ]; then + echo "No sandboxes found. Are you running gemini-code with sandboxing enabled?" + exit 1 + fi + # exit if multiple sandboxes are running + if [ ${#sandboxes[@]} -gt 1 ]; then + echo "Multiple sandboxes found:" + for sandbox in "${sandboxes[@]}"; do + echo " $sandbox" + done + echo "Sandbox name or index (0,1,...) must be specified as first argument" + exit 1 + fi + SANDBOX=${sandboxes[0]} +fi + +# check that sandbox exists +if ! [[ " ${sandboxes[*]} " == *" $SANDBOX "* ]]; then + echo "unknown sandbox $SANDBOX" + echo "known sandboxes:" + for sandbox in "${sandboxes[@]}"; do + echo " $sandbox" + done + exit 1 +fi + +# determine command and args for exec +if [ $# -gt 0 ]; then + cmd=(bash -l -c "$(printf '%q ' "$@")") # fixes quoting, e.g. bash -c 'echo $SANDBOX' + exec_args=() + if [ "$interactive" = true ]; then + exec_args=(-it) + fi +else + cmd=(bash -l) + exec_args=(-it) +fi + +# run command in sandbox +exec_args+=("$SANDBOX" "${cmd[@]}") +$CMD exec "${exec_args[@]}" \ No newline at end of file diff --git a/scripts/start_sandbox.sh b/scripts/start_sandbox.sh index efa8ecc2..04f3a55f 100755 --- a/scripts/start_sandbox.sh +++ b/scripts/start_sandbox.sh @@ -41,7 +41,10 @@ INDEX=0 while $CMD ps -a --format "{{.Names}}" | grep -q "$IMAGE-$INDEX"; do INDEX=$((INDEX + 1)) done -run_args+=(--name "$IMAGE-$INDEX") +run_args+=(--name "$IMAGE-$INDEX" --hostname "$IMAGE-$INDEX") + +# also set SANDBOX environment variable as container name +run_args+=(--env "SANDBOX=$IMAGE-$INDEX") # enable debugging via node --inspect-brk (and $DEBUG_PORT) if DEBUG is set node_args=()