mirror of
https://github.com/textcortex/claude-code-sandbox.git
synced 2025-08-04 19:08:21 +00:00
Add setupcommands
This commit is contained in:
parent
da0755dd19
commit
627ff0df6f
4 changed files with 225 additions and 1 deletions
169
docs/setup-commands.md
Normal file
169
docs/setup-commands.md
Normal file
|
@ -0,0 +1,169 @@
|
|||
# Setup Commands
|
||||
|
||||
This document explains how to run custom setup commands in your Claude Sandbox container.
|
||||
|
||||
## Overview
|
||||
|
||||
Setup commands allow you to automatically run initialization scripts when your container starts. This is useful for:
|
||||
- Installing project dependencies
|
||||
- Setting up databases
|
||||
- Configuring environment-specific settings
|
||||
- Installing additional tools
|
||||
|
||||
## Configuration
|
||||
|
||||
Add a `setupCommands` array to your `claude-sandbox.config.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"setupCommands": [
|
||||
"npm install",
|
||||
"pip install -r requirements.txt",
|
||||
"sudo apt-get update && sudo apt-get install -y postgresql-client",
|
||||
"createdb myapp_dev || true"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Execution Order
|
||||
|
||||
Setup commands run:
|
||||
1. **After** workspace files are copied
|
||||
2. **After** git branch is created
|
||||
3. **Before** Claude Code starts (if auto-start is enabled)
|
||||
4. **As the `claude` user** (with sudo access)
|
||||
|
||||
## Examples
|
||||
|
||||
### Node.js Project
|
||||
```json
|
||||
{
|
||||
"setupCommands": [
|
||||
"npm install",
|
||||
"npm run build",
|
||||
"npm run db:migrate"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Python Project
|
||||
```json
|
||||
{
|
||||
"setupCommands": [
|
||||
"pip install -r requirements.txt",
|
||||
"python manage.py migrate",
|
||||
"python manage.py collectstatic --noinput"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Installing System Packages
|
||||
```json
|
||||
{
|
||||
"setupCommands": [
|
||||
"sudo apt-get update",
|
||||
"sudo apt-get install -y redis-server postgresql-client",
|
||||
"sudo service redis-server start"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Complex Setup
|
||||
```json
|
||||
{
|
||||
"setupCommands": [
|
||||
"# Install dependencies",
|
||||
"npm install && pip install -r requirements.txt",
|
||||
|
||||
"# Set up database",
|
||||
"sudo service postgresql start",
|
||||
"createdb myapp_dev || true",
|
||||
"npm run db:migrate",
|
||||
|
||||
"# Start background services",
|
||||
"redis-server --daemonize yes",
|
||||
"npm run workers:start &"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Use `|| true`** for commands that might fail but shouldn't stop setup:
|
||||
```json
|
||||
["createdb myapp_dev || true"]
|
||||
```
|
||||
|
||||
2. **Chain related commands** with `&&`:
|
||||
```json
|
||||
["cd frontend && npm install && npm run build"]
|
||||
```
|
||||
|
||||
3. **Add comments** for clarity:
|
||||
```json
|
||||
["# Install Python dependencies", "pip install -r requirements.txt"]
|
||||
```
|
||||
|
||||
4. **Test commands** in a regular container first:
|
||||
```bash
|
||||
docker run -it claude-code-sandbox:latest bash
|
||||
# Test your commands here
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
- Commands are run sequentially
|
||||
- If a command fails (non-zero exit code), subsequent commands still run
|
||||
- Failed commands show an error message but don't stop the container
|
||||
- To stop on first error, add `"set -e"` as the first command
|
||||
|
||||
## Working Directory
|
||||
|
||||
All commands run in `/workspace` (your project root) as the `claude` user.
|
||||
|
||||
## Environment Variables
|
||||
|
||||
Commands have access to:
|
||||
- All environment variables from your config
|
||||
- Standard container environment
|
||||
- `HOME=/home/claude`
|
||||
- `USER=claude`
|
||||
|
||||
## Limitations
|
||||
|
||||
- Commands run synchronously (one at a time)
|
||||
- Long-running commands will delay container startup
|
||||
- Background processes should be daemonized
|
||||
- Output is prefixed with `>` for clarity
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Command Not Found
|
||||
Ensure the tool is installed in the Docker image or install it in your setup commands:
|
||||
```json
|
||||
{
|
||||
"setupCommands": [
|
||||
"sudo apt-get update && sudo apt-get install -y <package>"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Permission Denied
|
||||
The `claude` user has passwordless sudo access. Prefix commands with `sudo` if needed:
|
||||
```json
|
||||
{
|
||||
"setupCommands": [
|
||||
"sudo systemctl start postgresql"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Command Hangs
|
||||
Ensure commands don't wait for user input. Use flags like `-y` or `--yes`:
|
||||
```json
|
||||
{
|
||||
"setupCommands": [
|
||||
"sudo apt-get install -y package-name"
|
||||
]
|
||||
}
|
||||
```
|
|
@ -10,6 +10,7 @@ const DEFAULT_CONFIG: SandboxConfig = {
|
|||
autoCreatePR: true,
|
||||
autoStartClaude: true,
|
||||
claudeConfigPath: path.join(os.homedir(), '.claude.json'),
|
||||
setupCommands: [], // Example: ["npm install", "pip install -r requirements.txt"]
|
||||
allowedTools: ['*'], // All tools allowed in sandbox
|
||||
// maxThinkingTokens: 100000,
|
||||
// bashTimeout: 600000, // 10 minutes
|
||||
|
|
|
@ -348,7 +348,7 @@ exec claude --dangerously-skip-permissions' > /start-claude.sh && \\
|
|||
|
||||
// First, set up the git branch and create startup script
|
||||
try {
|
||||
console.log(chalk.green("Setting up git branch and startup script..."));
|
||||
console.log(chalk.gray("Setting up git branch and startup script..."));
|
||||
|
||||
// Create different startup scripts based on autoStartClaude setting
|
||||
const startupScript = this.config.autoStartClaude
|
||||
|
@ -410,6 +410,59 @@ exec /bin/bash`;
|
|||
});
|
||||
|
||||
console.log(chalk.green("✓ Container setup completed"));
|
||||
|
||||
// Execute custom setup commands if provided
|
||||
if (this.config.setupCommands && this.config.setupCommands.length > 0) {
|
||||
console.log(chalk.blue("Running custom setup commands..."));
|
||||
|
||||
for (const command of this.config.setupCommands) {
|
||||
console.log(chalk.gray(` Running: ${command}`));
|
||||
|
||||
const cmdExec = await container.exec({
|
||||
Cmd: ["/bin/bash", "-c", command],
|
||||
AttachStdout: true,
|
||||
AttachStderr: true,
|
||||
WorkingDir: "/workspace",
|
||||
User: "claude",
|
||||
});
|
||||
|
||||
const cmdStream = await cmdExec.start({});
|
||||
|
||||
// Wait for command to complete
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
let hasError = false;
|
||||
|
||||
cmdStream.on("data", (chunk) => {
|
||||
process.stdout.write(chalk.gray(" > ") + chunk.toString());
|
||||
});
|
||||
|
||||
cmdStream.on("end", async () => {
|
||||
// Check exit code
|
||||
try {
|
||||
const info = await cmdExec.inspect();
|
||||
if (info.ExitCode !== 0) {
|
||||
console.error(chalk.red(` ✗ Command failed with exit code ${info.ExitCode}`));
|
||||
hasError = true;
|
||||
} else {
|
||||
console.log(chalk.green(` ✓ Command completed successfully`));
|
||||
}
|
||||
} catch (e) {
|
||||
// Ignore inspection errors
|
||||
}
|
||||
|
||||
if (hasError && this.config.setupCommands?.includes("set -e")) {
|
||||
reject(new Error(`Setup command failed: ${command}`));
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
|
||||
cmdStream.on("error", reject);
|
||||
});
|
||||
}
|
||||
|
||||
console.log(chalk.green("✓ All setup commands completed"));
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(chalk.red("Setup failed:"), error);
|
||||
throw error;
|
||||
|
|
|
@ -7,6 +7,7 @@ export interface SandboxConfig {
|
|||
autoCreatePR?: boolean;
|
||||
autoStartClaude?: boolean;
|
||||
claudeConfigPath?: string;
|
||||
setupCommands?: string[];
|
||||
environment?: Record<string, string>;
|
||||
volumes?: string[];
|
||||
allowedTools?: string[];
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue