From f237082c37a10db1bf9d7daddf039bf4e002ec61 Mon Sep 17 00:00:00 2001 From: Olcan Date: Fri, 2 May 2025 12:04:22 -0700 Subject: [PATCH] pass PATH and PYTHONPATH into sandbox, let sandbox scripts recognize user settings for sandbox (#247) --- packages/cli/src/utils/sandbox.ts | 39 +++++++++++++++++++++++++++++-- scripts/sandbox_command.sh | 10 ++++++++ 2 files changed, 47 insertions(+), 2 deletions(-) diff --git a/packages/cli/src/utils/sandbox.ts b/packages/cli/src/utils/sandbox.ts index 40fca09c..85cf6c1a 100644 --- a/packages/cli/src/utils/sandbox.ts +++ b/packages/cli/src/utils/sandbox.ts @@ -103,7 +103,7 @@ export async function start_sandbox(sandbox: string) { // run init binary inside container to forward signals & reap zombies const args = ['run', '-it', '--rm', '--init', '--workdir', workdir]; - // mount current directory as ${workdir} inside container + // mount current directory as working directory in sandbox (set via --workdir) args.push('--volume', `${process.cwd()}:${workdir}`); // mount user settings directory inside container, after creating if missing @@ -195,6 +195,32 @@ export async function start_sandbox(sandbox: string) { args.push('--env', `COLORTERM=${process.env.COLORTERM}`); } + // copy any paths in PATH that are under working directory in sandbox + // note we can't just pass these as --env since that would override base PATH + // instead we construct a suffix and append as part of bashCmd below + let pathSuffix = ''; + if (process.env.PATH) { + const paths = process.env.PATH.split(':'); + for (const path of paths) { + if (path.startsWith(workdir)) { + pathSuffix += `:${path}`; + } + } + } + + // copy any paths in PYTHONPATH that are under working directory in sandbox + // note we can't just pass these as --env since that would override base PYTHONPATH + // instead we construct a suffix and append as part of bashCmd below + let pythonPathSuffix = ''; + if (process.env.PYTHONPATH) { + const paths = process.env.PYTHONPATH.split(':'); + for (const path of paths) { + if (path.startsWith(workdir)) { + pythonPathSuffix += `:${path}`; + } + } + } + // copy additional environment variables from SANDBOX_ENV if (process.env.SANDBOX_ENV) { for (let env of process.env.SANDBOX_ENV.split(',')) { @@ -230,9 +256,18 @@ export async function start_sandbox(sandbox: string) { nodeArgs.push(`--inspect-brk=0.0.0.0:${debugPort}`); } + // set up bash command to be run inside container + // start with setting up PATH and PYTHONPATH with optional suffixes from host + let bashCmd = ''; + if (pathSuffix) { + bashCmd += `export PATH="$PATH${pathSuffix}"; `; // suffix includes leading ':' + } + if (pythonPathSuffix) { + bashCmd += `export PYTHONPATH="$PYTHONPATH${pythonPathSuffix}"; `; // suffix includes leading ':' + } + // open additional ports if SANDBOX_PORTS is set // also set up redirects (via socat) so servers can listen on localhost instead of 0.0.0.0 - let bashCmd = ''; if (process.env.SANDBOX_PORTS) { for (let port of process.env.SANDBOX_PORTS.split(',')) { if ((port = port.trim())) { diff --git a/scripts/sandbox_command.sh b/scripts/sandbox_command.sh index fab94b52..f527292c 100755 --- a/scripts/sandbox_command.sh +++ b/scripts/sandbox_command.sh @@ -31,6 +31,16 @@ while getopts ":q" opt; do done shift $((OPTIND - 1)) +# if GEMINI_CODE_SANDBOX is not set, see if it is set in user settings +# note it can be string or boolean, and if missing jq will return null +USER_SETTINGS_FILE=~/.gemini/settings.json +if [ -z "${GEMINI_CODE_SANDBOX:-}" ] && [ -f "$USER_SETTINGS_FILE" ]; then + USER_SANDBOX_SETTING=$(jq -r '.sandbox' "$USER_SETTINGS_FILE") + if [ "$USER_SANDBOX_SETTING" != null ]; then + GEMINI_CODE_SANDBOX=$USER_SANDBOX_SETTING + fi +fi + # if GEMINI_CODE_SANDBOX is not set, try to source .env in case set there # allow .env to be in any ancestor directory (same as findEnvFile in config.ts) if [ -z "${GEMINI_CODE_SANDBOX:-}" ]; then