feat(sandbox): use package config to dictate sandbox image name (#624)

This commit is contained in:
Brandon Keiji 2025-05-30 19:28:46 +00:00 committed by GitHub
parent 9f85f8ed29
commit 31a7affb74
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 32 additions and 22 deletions

View File

@ -30,7 +30,7 @@
"build:server": "npm run build --workspace packages/server", "build:server": "npm run build --workspace packages/server",
"build:packages": "npm run build:server && npm run build:cli", "build:packages": "npm run build:server && npm run build:cli",
"build:docker": "scripts/build_sandbox.sh -s", "build:docker": "scripts/build_sandbox.sh -s",
"tag:docker": "docker tag gemini-code-sandbox:latest ${SANDBOX_IMAGE_REGISTRY:?SANDBOX_IMAGE_REGISTRY not set}/${SANDBOX_IMAGE_NAME:?SANDBOX_IMAGE_NAME not set}:$npm_package_version", "tag:docker": "docker tag gemini-cli-sandbox ${SANDBOX_IMAGE_REGISTRY:?SANDBOX_IMAGE_REGISTRY not set}/${SANDBOX_IMAGE_NAME:?SANDBOX_IMAGE_NAME not set}:$npm_package_version",
"prepare:cli-packagejson": "node scripts/prepare-cli-packagejson.js", "prepare:cli-packagejson": "node scripts/prepare-cli-packagejson.js",
"publish:sandbox": "scripts/publish-sandbox.sh", "publish:sandbox": "scripts/publish-sandbox.sh",
"publish:npm": "npm publish --workspaces ${NPM_PUBLISH_TAG:+--tag=$NPM_PUBLISH_TAG} ${NPM_DRY_RUN:+--dry-run}", "publish:npm": "npm publish --workspaces ${NPM_PUBLISH_TAG:+--tag=$NPM_PUBLISH_TAG} ${NPM_DRY_RUN:+--dry-run}",

View File

@ -25,6 +25,9 @@
"files": [ "files": [
"dist" "dist"
], ],
"config": {
"sandboxImageUri": "gemini-cli-sandbox"
},
"dependencies": { "dependencies": {
"@gemini-code/server": "0.1.0", "@gemini-code/server": "0.1.0",
"diff": "^7.0.0", "diff": "^7.0.0",

View File

@ -10,6 +10,7 @@ import path from 'node:path';
import fs from 'node:fs'; import fs from 'node:fs';
import { readFile } from 'node:fs/promises'; import { readFile } from 'node:fs/promises';
import { quote } from 'shell-quote'; import { quote } from 'shell-quote';
import { readPackageUp } from 'read-package-up';
import { import {
USER_SETTINGS_DIR, USER_SETTINGS_DIR,
SETTINGS_DIRECTORY_NAME, SETTINGS_DIRECTORY_NAME,
@ -70,6 +71,18 @@ async function shouldUseCurrentUserInSandbox(): Promise<boolean> {
return false; // Default to false if no other condition is met return false; // Default to false if no other condition is met
} }
async function getSandboxImageName(): Promise<string> {
const packageJsonResult = await readPackageUp();
const packageJsonConfig = packageJsonResult?.packageJson.config as
| { sandboxImageUri?: string }
| undefined;
return (
process.env.GEMINI_SANDBOX_IMAGE ??
packageJsonConfig?.sandboxImageUri ??
'gemini-cli-sandbox'
);
}
// node.js equivalent of scripts/sandbox_command.sh // node.js equivalent of scripts/sandbox_command.sh
export function sandbox_command(sandbox?: string | boolean): string { export function sandbox_command(sandbox?: string | boolean): string {
// note environment variable takes precedence over argument (from command line or settings) // note environment variable takes precedence over argument (from command line or settings)
@ -257,7 +270,7 @@ export async function start_sandbox(sandbox: string) {
// determine full path for gemini-code to distinguish linked vs installed setting // determine full path for gemini-code to distinguish linked vs installed setting
const gcPath = execSync(`realpath $(which gemini)`).toString().trim(); const gcPath = execSync(`realpath $(which gemini)`).toString().trim();
const image = process.env.GEMINI_SANDBOX_IMAGE ?? 'gemini-code-sandbox'; const image = await getSandboxImageName();
const workdir = process.cwd(); const workdir = process.cwd();
// if BUILD_SANDBOX is set, then call scripts/build_sandbox.sh under gemini-code repo // if BUILD_SANDBOX is set, then call scripts/build_sandbox.sh under gemini-code repo
@ -362,23 +375,19 @@ export async function start_sandbox(sandbox: string) {
} }
// name container after image, plus numeric suffix to avoid conflicts // name container after image, plus numeric suffix to avoid conflicts
const containerName = parseImageName(image); const imageName = parseImageName(image);
let index = 0; let index = 0;
while ( while (
execSync( execSync(
`${sandbox} ps -a --format "{{.Names}}" | grep "${containerName}-${index}" || true`, `${sandbox} ps -a --format "{{.Names}}" | grep "${imageName}-${index}" || true`,
) )
.toString() .toString()
.trim() .trim()
) { ) {
index++; index++;
} }
args.push( const containerName = `${imageName}-${index}`;
'--name', args.push('--name', containerName, '--hostname', containerName);
`${containerName}-${index}`,
'--hostname',
`${containerName}-${index}`,
);
// copy GEMINI_API_KEY // copy GEMINI_API_KEY
if (process.env.GEMINI_API_KEY) { if (process.env.GEMINI_API_KEY) {
@ -437,7 +446,7 @@ export async function start_sandbox(sandbox: string) {
} }
// set SANDBOX as container name // set SANDBOX as container name
args.push('--env', `SANDBOX=${containerName}-${index}`); args.push('--env', `SANDBOX=${containerName}`);
// for podman only, use empty --authfile to skip unnecessary auth refresh overhead // for podman only, use empty --authfile to skip unnecessary auth refresh overhead
if (sandbox === 'podman') { if (sandbox === 'podman') {

View File

@ -26,7 +26,7 @@ fi
CMD=$(scripts/sandbox_command.sh) CMD=$(scripts/sandbox_command.sh)
echo "using $CMD for sandboxing" echo "using $CMD for sandboxing"
IMAGE=gemini-cli-sandbox:latest IMAGE=gemini-cli-sandbox
DOCKERFILE=Dockerfile DOCKERFILE=Dockerfile
SKIP_NPM_INSTALL_BUILD=false SKIP_NPM_INSTALL_BUILD=false

View File

@ -23,10 +23,10 @@ const version = process.env.npm_package_version;
// Get Docker registry and image name directly from PUBLISH_ environment variables. // Get Docker registry and image name directly from PUBLISH_ environment variables.
// These are expected to be set by the CI/build environment. // These are expected to be set by the CI/build environment.
const dockerRegistry = process.env.SANDBOX_IMAGE_REGISTRY; const containerImageRegistry = process.env.SANDBOX_IMAGE_REGISTRY;
const dockerImageName = process.env.SANDBOX_IMAGE_NAME; const containerImageName = process.env.SANDBOX_IMAGE_NAME;
if (!version || !dockerRegistry || !dockerImageName) { if (!version || !containerImageRegistry || !containerImageName) {
console.error( console.error(
'Error: Missing required environment variables. Need: ' + 'Error: Missing required environment variables. Need: ' +
'npm_package_version, SANDBOX_IMAGE_REGISTRY, and SANDBOX_IMAGE_NAME.', 'npm_package_version, SANDBOX_IMAGE_REGISTRY, and SANDBOX_IMAGE_NAME.',
@ -38,15 +38,13 @@ if (!version || !dockerRegistry || !dockerImageName) {
process.exit(1); process.exit(1);
} }
const dockerImageUri = `${dockerRegistry}/${dockerImageName}:${version}`; const containerImageUri = `${containerImageRegistry}/${containerImageName}:${version}`;
// Add or update fields in cliPackageJson.config to store this information // Add or update fields in cliPackageJson.config to store this information
if (!cliPackageJson.config) { if (!cliPackageJson.config) {
cliPackageJson.config = {}; cliPackageJson.config = {};
} }
cliPackageJson.config.dockerImageUri = dockerImageUri; cliPackageJson.config.sandboxImageUri = containerImageUri;
cliPackageJson.config.dockerRegistry = dockerRegistry;
cliPackageJson.config.dockerImageName = dockerImageName;
// Remove 'prepublishOnly' from scripts if it exists // Remove 'prepublishOnly' from scripts if it exists
if (cliPackageJson.scripts && cliPackageJson.scripts.prepublishOnly) { if (cliPackageJson.scripts && cliPackageJson.scripts.prepublishOnly) {
@ -61,6 +59,6 @@ fs.writeFileSync(
console.log( console.log(
`Updated ${path.relative(process.cwd(), cliPackageJsonPath)} with Docker image details:`, `Updated ${path.relative(process.cwd(), cliPackageJsonPath)} with Docker image details:`,
); );
console.log(` URI: ${dockerImageUri}`); console.log(` URI: ${containerImageUri}`);
console.log(` Registry: ${dockerRegistry}`); console.log(` Registry: ${containerImageRegistry}`);
console.log(` Image Name: ${dockerImageName}`); console.log(` Image Name: ${containerImageName}`);