diff --git a/DOCKER.md b/DOCKER.md new file mode 100644 index 0000000..04f8ca6 --- /dev/null +++ b/DOCKER.md @@ -0,0 +1,161 @@ +# Docker Setup for Serena (Experimental) + +⚠️ **EXPERIMENTAL FEATURE**: The Docker setup for Serena is currently experimental and has several limitations. Please read this entire document before using Docker with Serena. + +## Overview + +Docker support allows you to run Serena in an isolated container environment, which provides better security isolation for the shell tool and consistent dependencies across different systems. + +## Benefits + +- **Safer shell tool execution**: Commands run in an isolated container environment +- **Consistent dependencies**: No need to manage language servers and dependencies on your host system +- **Cross-platform support**: Works consistently across Windows, macOS, and Linux + +## Important Limitations and Caveats + +### 1. Configuration File Conflicts + +⚠️ **Critical**: Docker uses a separate configuration file (`serena_config.docker.yml`) to avoid path conflicts. When running in Docker: +- Container paths will be stored in the configuration (e.g., `/workspaces/serena/...`) +- These paths are incompatible with non-Docker usage +- After using Docker, you cannot directly switch back to non-Docker usage without manual configuration adjustment + +### 2. Project Activation Limitations + +- **Only mounted directories work**: Projects must be mounted as volumes to be accessible +- Projects outside the mounted directories cannot be activated or accessed +- Default setup only mounts the current directory + +### 3. GUI Window Disabled + +- The GUI log window option is automatically disabled in Docker environments +- Use the web dashboard instead (see below) + +### 4. Dashboard Port Configuration + +The web dashboard runs on port 24282 (0x5EDA) by default. You can configure this using environment variables: + +```bash +# Use default ports +docker-compose up serena + +# Use custom ports +SERENA_DASHBOARD_PORT=8080 docker-compose up serena +``` + +⚠️ **Note**: If the local port is occupied, you'll need to specify a different port using the environment variable. + +### 5. Line Ending Issues on Windows + +⚠️ **Windows Users**: Be aware of potential line ending inconsistencies: +- Files edited within the Docker container may use Unix line endings (LF) +- Your Windows system may expect Windows line endings (CRLF) +- This can cause issues with version control and text editors +- Configure your Git settings appropriately: `git config core.autocrlf true` + +## Quick Start + +### Using Docker Compose (Recommended) + +1. **Production mode** (for using Serena as MCP server): + ```bash + docker-compose up serena + ``` + +2. **Development mode** (with source code mounted): + ```bash + docker-compose up serena-dev + ``` + +### Using Docker directly + +```bash +# Build the image +docker build -t serena . + +# Run with current directory mounted +docker run -it --rm \ + -v "$(pwd)":/workspace \ + -p 9121:9121 \ + -p 24282:24282 \ + -e SERENA_DOCKER=1 \ + serena +``` + +## Accessing the Dashboard + +Once running, access the web dashboard at: +- Default: http://localhost:24282/dashboard +- Custom port: http://localhost:${SERENA_DASHBOARD_PORT}/dashboard + +## Volume Mounting + +To work with projects, you must mount them as volumes: + +```yaml +# In compose.yaml +volumes: + - ./my-project:/workspace/my-project + - /path/to/another/project:/workspace/another-project +``` + +## Environment Variables + +- `SERENA_DOCKER=1`: Set automatically to indicate Docker environment +- `SERENA_PORT`: MCP server port (default: 9121) +- `SERENA_DASHBOARD_PORT`: Web dashboard port (default: 24282) + +## Troubleshooting + +### Port Already in Use + +If you see "port already in use" errors: +```bash +# Check what's using the port +lsof -i :24282 # macOS/Linux +netstat -ano | findstr :24282 # Windows + +# Use a different port +SERENA_DASHBOARD_PORT=8080 docker-compose up serena +``` + +### Configuration Issues + +If you need to reset Docker configuration: +```bash +# Remove Docker-specific config +rm serena_config.docker.yml + +# Serena will auto-generate a new one on next run +``` + +### Project Access Issues + +Ensure projects are properly mounted: +- Check volume mounts in `docker-compose.yaml` +- Use absolute paths for external projects +- Verify permissions on mounted directories + +## Migration Path + +To switch between Docker and non-Docker usage: + +1. **Docker to Non-Docker**: + - Manually edit project paths in `serena_config.yml` + - Change container paths to host paths + - Or use separate config files for each environment + +2. **Non-Docker to Docker**: + - Projects will be re-registered with container paths + - Original config remains unchanged + +## Future Improvements + +We're working on: +- Automatic config migration between environments +- Better project path handling +- Dynamic port allocation +- Windows line-ending handling + +For updates and issues, please check the [GitHub repository](https://github.com/AbanteAI/serena). \ No newline at end of file diff --git a/README.md b/README.md index fd07825..9b832ad 100644 --- a/README.md +++ b/README.md @@ -256,7 +256,9 @@ which will let you open the JSON file `claude_desktop_config.json`. Add the foll } ``` -#### Docker Installation +#### Docker Installation (Experimental) + +⚠️ **EXPERIMENTAL**: Docker support is currently experimental with several limitations. Please read the [Docker documentation](DOCKER.md) for important caveats before using. Alternatively, you can run Serena using Docker: @@ -271,7 +273,12 @@ Alternatively, you can run Serena using Docker: } ``` -Replace `/path/to/your/projects` with the absolute path to your projects directory. The Docker approach has the advantage of not requiring local installation of dependencies. +Replace `/path/to/your/projects` with the absolute path to your projects directory. The Docker approach provides: +- Better security isolation for shell command execution +- No need to install language servers and dependencies locally +- Consistent environment across different systems + +See the [Docker documentation](DOCKER.md) for detailed setup instructions, configuration options, and known limitations. If you are using paths containing backslashes for paths on Windows (note that you can also just use forward slashes), be sure to escape them correctly (`\\`). diff --git a/compose.yaml b/compose.yaml index 17348bb..bb8f683 100644 --- a/compose.yaml +++ b/compose.yaml @@ -6,8 +6,10 @@ services: dockerfile: Dockerfile target: production ports: - - "9121:9121" - - "24282:24282" + - "${SERENA_PORT:-9121}:9121" # MCP server port + - "${SERENA_DASHBOARD_PORT:-24282}:24282" # Dashboard port (default 0x5EDA = 24282) + environment: + - SERENA_DOCKER=1 command: - "uv run --directory . serena-mcp-server --transport sse --port 9121 --host 0.0.0.0" @@ -19,7 +21,12 @@ services: target: development tty: true stdin_open: true + environment: + - SERENA_DOCKER=1 volumes: - .:/workspaces/serena + ports: + - "${SERENA_PORT:-9121}:9121" # MCP server port + - "${SERENA_DASHBOARD_PORT:-24282}:24282" # Dashboard port command: - "uv run --directory . serena-mcp-server" diff --git a/src/serena/agent.py b/src/serena/agent.py index cde3bc1..c6021a1 100644 --- a/src/serena/agent.py +++ b/src/serena/agent.py @@ -102,6 +102,19 @@ def get_serena_managed_dir(project_root: str | Path) -> str: return os.path.join(project_root, SERENA_MANAGED_DIR_NAME) +def is_running_in_docker() -> bool: + """Check if we're running inside a Docker container.""" + # Check for Docker-specific files + if os.path.exists('/.dockerenv'): + return True + # Check cgroup for docker references + try: + with open('/proc/self/cgroup', 'r') as f: + return 'docker' in f.read() + except: + return False + + @dataclass class ProjectConfig(ToStringMixin): project_name: str @@ -315,6 +328,7 @@ class SerenaConfig(SerenaConfigBase): loaded_commented_yaml: CommentedMap CONFIG_FILE = "serena_config.yml" + CONFIG_FILE_DOCKER = "serena_config.docker.yml" @classmethod def autogenerate(cls) -> None: @@ -328,7 +342,8 @@ class SerenaConfig(SerenaConfigBase): @classmethod def get_config_file_path(cls) -> str: - return os.path.join(REPO_ROOT, cls.CONFIG_FILE) + config_file = cls.CONFIG_FILE_DOCKER if is_running_in_docker() else cls.CONFIG_FILE + return os.path.join(REPO_ROOT, config_file) @classmethod def from_config_file(cls, generate_if_missing: bool = True) -> "SerenaConfig": @@ -370,7 +385,11 @@ class SerenaConfig(SerenaConfigBase): project = Project.load(path) instance.projects.append(project) - instance.gui_log_window_enabled = loaded_commented_yaml.get("gui_log_window", False) + # Force disable GUI in Docker environment + if is_running_in_docker(): + instance.gui_log_window_enabled = False + else: + instance.gui_log_window_enabled = loaded_commented_yaml.get("gui_log_window", False) instance.log_level = loaded_commented_yaml.get("log_level", loaded_commented_yaml.get("gui_log_level", logging.INFO)) instance.web_dashboard = loaded_commented_yaml.get("web_dashboard", True) instance.tool_timeout = loaded_commented_yaml.get("tool_timeout", DEFAULT_TOOL_TIMEOUT)