From 9026ec5ef208bb4617530abd59ae6bb851ad604c Mon Sep 17 00:00:00 2001 From: Aaron Yordanyan Date: Fri, 6 Jun 2025 17:15:16 +0400 Subject: [PATCH 1/4] Add Docker support with GitHub Actions workflow and updated configurations - Introduced `.github/workflows/docker.yml` for building and pushing Docker images. - Updated `Dockerfile` to split stages for `base`, `development`, and `production`. - Added `compose.yaml` for Docker Compose setups. - Enhanced `README.md` with Docker usage instructions. --- .github/workflows/docker.yml | 68 ++++++++++++++++++++++++++++++++++++ Dockerfile | 30 ++++++++++++---- README.md | 29 ++++++++++++--- compose.yaml | 22 ++++++++++++ 4 files changed, 138 insertions(+), 11 deletions(-) create mode 100644 .github/workflows/docker.yml create mode 100644 compose.yaml diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml new file mode 100644 index 0000000..7e15e4c --- /dev/null +++ b/.github/workflows/docker.yml @@ -0,0 +1,68 @@ +name: Build and Push Docker Images + +on: + push: + branches: [ main ] + tags: [ 'v*' ] + pull_request: + branches: [ main ] + +env: + REGISTRY: ghcr.io + IMAGE_NAME: ${{ github.repository }} + +jobs: + build-and-push: + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log in to Container Registry + if: github.event_name != 'pull_request' + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract metadata + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + tags: | + type=ref,event=branch + type=ref,event=pr + type=semver,pattern={{version}} + type=semver,pattern={{major}}.{{minor}} + type=raw,value=latest,enable={{is_default_branch}} + + - name: Build and push production image + uses: docker/build-push-action@v5 + with: + context: . + target: production + push: ${{ github.event_name != 'pull_request' }} + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max + + - name: Build and push development image + uses: docker/build-push-action@v5 + with: + context: . + target: development + push: ${{ github.event_name != 'pull_request' }} + tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:dev + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 4521867..d88d4e2 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ -# Use the official Python image for the base image. -FROM python:3.11-slim +# Base stage with common dependencies +FROM python:3.11-slim AS base SHELL ["/bin/bash", "-c"] # Set environment variables to make Python print directly to the terminal and avoid .pyc files. @@ -21,18 +21,18 @@ RUN python3 -m pip install --no-cache-dir pipx \ # Add local bin to the path ENV PATH="${PATH}:/root/.local/bin" - # Install the latest version of uv RUN curl -LsSf https://astral.sh/uv/install.sh | sh # Set the working directory WORKDIR /workspaces/serena -# Copy required files into the image -COPY pyproject.toml /workspaces/serena/ -COPY README.md /workspaces/serena/ +# Development target +FROM base AS development +# Copy all files for development +COPY . /workspaces/serena/ -# Create virtual environment and install dependencies +# Create virtual environment and install dependencies with dev extras RUN uv venv RUN . .venv/bin/activate RUN uv pip install --all-extras -r pyproject.toml -e . @@ -41,3 +41,19 @@ ENV PATH="/workspaces/serena/.venv/bin:${PATH}" # Entrypoint to ensure environment is activated ENTRYPOINT ["/bin/bash", "-c", "source .venv/bin/activate && $0 $@"] +# Production target +FROM base AS production +# Copy only necessary files for production +COPY pyproject.toml /workspaces/serena/ +COPY README.md /workspaces/serena/ +COPY src/ /workspaces/serena/src/ + +# Create virtual environment and install dependencies (production only) +RUN uv venv +RUN . .venv/bin/activate +RUN uv pip install -r pyproject.toml -e . +ENV PATH="/workspaces/serena/.venv/bin:${PATH}" + +# Entrypoint to ensure environment is activated +ENTRYPOINT ["/bin/bash", "-c", "source .venv/bin/activate && $0 $@"] + diff --git a/README.md b/README.md index 6d48de7..fd07825 100644 --- a/README.md +++ b/README.md @@ -243,6 +243,8 @@ Configure the MCP server in your client. For [Claude Desktop](https://claude.ai/download) (available for Windows and macOS), go to File / Settings / Developer / MCP Servers / Edit Config, which will let you open the JSON file `claude_desktop_config.json`. Add the following (with adjusted paths) to enable Serena: +#### Local Installation + ```json { "mcpServers": { @@ -254,6 +256,23 @@ which will let you open the JSON file `claude_desktop_config.json`. Add the foll } ``` +#### Docker Installation + +Alternatively, you can run Serena using Docker: + +```json +{ + "mcpServers": { + "serena": { + "command": "docker", + "args": ["run", "--rm", "-i", "--network", "host", "-v", "/path/to/your/projects:/workspaces/projects", "ghcr.io/oraios/serena:latest", "serena-mcp-server", "--transport", "stdio"] + } + } +} +``` + +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. + 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 (`\\`). @@ -265,16 +284,18 @@ That's it! Save the config and then restart Claude Desktop. You are ready for ac uv run serena-mcp-server --help ``` -ℹ️ You can use Serena without cloning or configuring it explicitly by +ℹ️ You can use Serena without cloning or configuring it explicitly by using the Docker image above or: +```json { "mcpServers": { "serena": { - "command": "/abs/path/to/uv", - "args": ["run", "--directory", "/abs/path/to/serena", "serena-mcp-server"] + "command": "uvx", + "args": ["--from", "git+https://github.com/oraios/serena", "serena-mcp-server"] } } } +``` #### Troubleshooting @@ -399,7 +420,7 @@ Here's how it works (see also [Agno's documentation](https://docs.agno.com/intro 3. Copy `.env.example` to `.env` and fill in the API keys for the provider(s) you intend to use. -5. Start the agno agent app with +4. Start the agno agent app with ```shell uv run python scripts/agno_agent.py ``` diff --git a/compose.yaml b/compose.yaml new file mode 100644 index 0000000..5150442 --- /dev/null +++ b/compose.yaml @@ -0,0 +1,22 @@ +services: + serena: + image: serena:latest + build: + context: ./ + dockerfile: Dockerfile + target: production + tty: true + stdin_open: true + + serena-dev: + image: serena:dev + build: + context: ./ + dockerfile: Dockerfile + target: development + tty: true + stdin_open: true + volumes: + - .:/workspaces/serena + command: + - "uv run --directory . serena-mcp-server" From d9d40e89b545dfcba94155e2ee7e37526c75266f Mon Sep 17 00:00:00 2001 From: Aaron Yordanyan Date: Fri, 6 Jun 2025 17:37:05 +0400 Subject: [PATCH 2/4] Expose necessary ports and update host configuration for Dockerized services --- compose.yaml | 7 +++++-- src/serena/dashboard.py | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/compose.yaml b/compose.yaml index 5150442..17348bb 100644 --- a/compose.yaml +++ b/compose.yaml @@ -5,8 +5,11 @@ services: context: ./ dockerfile: Dockerfile target: production - tty: true - stdin_open: true + ports: + - "9121:9121" + - "24282:24282" + command: + - "uv run --directory . serena-mcp-server --transport sse --port 9121 --host 0.0.0.0" serena-dev: image: serena:dev diff --git a/src/serena/dashboard.py b/src/serena/dashboard.py index 4b0419a..622bef1 100644 --- a/src/serena/dashboard.py +++ b/src/serena/dashboard.py @@ -109,7 +109,7 @@ class SerenaDashboardAPI: raise RuntimeError(f"No free ports found starting from {start_port}") - def run(self, host: str = "127.0.0.1", port: int = 0x5EDA) -> int: + def run(self, host: str = "0.0.0.0", port: int = 0x5EDA) -> int: """ Runs the dashboard on the given host and port and returns the port number. """ From e8fc6d4fa772bf95e7175ef656fcbac9479f20ce Mon Sep 17 00:00:00 2001 From: Aaron Yordanyan Date: Mon, 9 Jun 2025 10:09:30 +0400 Subject: [PATCH 3/4] Add experimental Docker support and update configuration handling - Introduced `DOCKER.md` with detailed setup instructions and limitations. - Enhanced `compose.yaml` with dynamic port and environment variable support. - Updated `agent.py` to detect Docker environments and disable GUI features. - Modified configuration paths to support both Docker and non-Docker setups. - Marked Docker support as experimental in the documentation. --- DOCKER.md | 161 ++++++++++++++++++++++++++++++++++++++++++++ README.md | 11 ++- compose.yaml | 11 ++- src/serena/agent.py | 23 ++++++- 4 files changed, 200 insertions(+), 6 deletions(-) create mode 100644 DOCKER.md 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) From 04011230081cfb54cd3b652d709273b8de837d9f Mon Sep 17 00:00:00 2001 From: Aaron Yordanyan Date: Sat, 14 Jun 2025 10:19:12 +0400 Subject: [PATCH 4/4] Refine Docker configuration handling and documentation - Updated exception handling in `agent.py` for better error specificity (catching `FileNotFoundError`). - Added contextual comment for `CONFIG_FILE_DOCKER` in `agent.py`. - Updated `DOCKER.md` to reflect the new GitHub repository link. --- DOCKER.md | 2 +- src/serena/agent.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/DOCKER.md b/DOCKER.md index 04f8ca6..45e0f5c 100644 --- a/DOCKER.md +++ b/DOCKER.md @@ -158,4 +158,4 @@ We're working on: - 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 +For updates and issues, please check the [GitHub repository](https://github.com/diazoxide/serena). diff --git a/src/serena/agent.py b/src/serena/agent.py index c6021a1..512a8fe 100644 --- a/src/serena/agent.py +++ b/src/serena/agent.py @@ -111,7 +111,7 @@ def is_running_in_docker() -> bool: try: with open('/proc/self/cgroup', 'r') as f: return 'docker' in f.read() - except: + except FileNotFoundError: return False @@ -328,7 +328,7 @@ class SerenaConfig(SerenaConfigBase): loaded_commented_yaml: CommentedMap CONFIG_FILE = "serena_config.yml" - CONFIG_FILE_DOCKER = "serena_config.docker.yml" + CONFIG_FILE_DOCKER = "serena_config.docker.yml" # Docker-specific config file; auto-generated if missing, mounted via docker-compose for user customization @classmethod def autogenerate(cls) -> None: