mirror of
https://github.com/tiennm99/serena.git
synced 2026-09-03 10:23:43 +00:00
Merge pull request #157 from diazoxide/main
Add Docker support with GitHub Actions workflow and updated Readme.md
This commit is contained in:
@@ -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
|
||||
@@ -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
@@ -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 $@"]
|
||||
|
||||
|
||||
@@ -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
|
||||
```
|
||||
|
||||
@@ -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
@@ -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)
|
||||
|
||||
@@ -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.
|
||||
"""
|
||||
|
||||
Reference in New Issue
Block a user