Merge pull request #157 from diazoxide/main

Add Docker support with GitHub Actions workflow and updated Readme.md
This commit is contained in:
Michael Panchenko
2025-06-15 11:45:01 +02:00
committed by GitHub
7 changed files with 338 additions and 14 deletions
+68
View File
@@ -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
+161
View File
@@ -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/diazoxide/serena).
+23 -7
View File
@@ -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 $@"]
+32 -4
View File
@@ -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,30 @@ which will let you open the JSON file `claude_desktop_config.json`. Add the foll
}
```
#### 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:
```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 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 (`\\`).
@@ -265,16 +291,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 +427,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
```
+32
View File
@@ -0,0 +1,32 @@
services:
serena:
image: serena:latest
build:
context: ./
dockerfile: Dockerfile
target: production
ports:
- "${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"
serena-dev:
image: serena:dev
build:
context: ./
dockerfile: Dockerfile
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"
+21 -2
View File
@@ -103,6 +103,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 FileNotFoundError:
return False
@dataclass
class ProjectConfig(ToStringMixin):
project_name: str
@@ -316,6 +329,7 @@ class SerenaConfig(SerenaConfigBase):
loaded_commented_yaml: CommentedMap
CONFIG_FILE = "serena_config.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:
@@ -329,7 +343,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":
@@ -371,7 +386,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)
+1 -1
View File
@@ -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.
"""