diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 465efd58f..fdcb850e1 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -1,6 +1,10 @@ { "name": "serena Project", "dockerFile": "../Dockerfile", + "build": { + "dockerfile": "../Dockerfile", + "target": "dev" + }, "workspaceFolder": "/workspaces/serena", "settings": { "terminal.integrated.shell.linux": "/bin/bash", diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 5513b734b..cf9e42596 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -50,6 +50,7 @@ jobs: uses: docker/build-push-action@v5 with: context: . + target: prod platforms: linux/amd64,linux/arm64 push: ${{ github.event_name != 'pull_request' }} tags: ${{ steps.meta.outputs.tags }} diff --git a/DOCKER.md b/DOCKER.md index bb77040cd..d7f341054 100644 --- a/DOCKER.md +++ b/DOCKER.md @@ -16,12 +16,12 @@ Docker support allows you to run Serena in an isolated container environment, wh ### Configuration -Serena's configuration and log files are stored in the container in `/workspaces/serena/config/`. +Serena's configuration and log files are stored in the container in `/home/serena/.serena`. Any local configuration you may have for Serena will not apply; the container uses its own separate configuration. You can mount a local configuration/data directory to persist settings across container restarts (which will also contain session log files). -Simply mount your local directory to `/workspaces/serena/config` in the container. +Simply mount your local directory to `/home/serena/.serena` in the container. Initially, be sure to add a `serena_config.yml` file to the mounted directory which applies the following special settings for Docker usage: ``` @@ -45,8 +45,11 @@ Set other configuration options as needed. The default Docker image does not include dependencies for languages that require explicit system-level installations. + Only languages that install their requirements on the fly will work out of the box. +A basic example of using Serena for your own toolchain can be seen in `examples/docker/ + ### Dashboard Port Configuration The web dashboard runs on port 24282 (0x5EDA) by default. You can configure this using environment variables: @@ -73,17 +76,29 @@ SERENA_DASHBOARD_PORT=8080 docker-compose up serena ### Using Docker Compose (Recommended) -1. **Production mode** (for using Serena as MCP server): +#### Using Serena with other projects + +For developing your own project with Serena you may use the following command. + +By default this will mount the local directory that the compose file is started in. + ```bash docker-compose up serena ``` -2. **Development mode** (with source code mounted): +To override this behavior pass in the `PROJECT_DIR` environment variable at the CLI or by modifying the compose file. + ```bash - docker-compose up serena-dev + PROJECT_DIR="../reponame" docker-compose up serena ``` -Note: Edit the `compose.yaml` file to customize volume mounts for your projects. +#### For Serena Development + +To develop the Serena codebase you can run the following command + + ```bash + docker-compose -f compose-dev.yaml up + ``` ### Building the Docker Image Manually @@ -96,50 +111,35 @@ docker run -it --rm \ -v "$(pwd)":/workspace \ -p 9121:9121 \ -p 24282:24282 \ - -e SERENA_DOCKER=1 \ serena ``` -### Using Docker Compose with Merge Compose files - -To use Docker Compose with merge files, you can create a `compose.override.yml` file to customize the configuration: - -```yaml -services: - serena: - # To work with projects, you must mount them as volumes: - volumes: - - ./my-project:/workspace/my-project - - /path/to/another/project:/workspace/another-project - # Add the context for the IDE assistant option: - command: - - "uv run --directory . serena-mcp-server --transport sse --port 9121 --host 0.0.0.0 --context claude-code" -``` - -See the [Docker Merge Compose files documentation](https://docs.docker.com/compose/how-tos/multiple-compose-files/merge/) for more details on using merge files. - ## Accessing the Dashboard Once running, access the web dashboard at: -- Default: http://localhost:24282/dashboard -- Custom port: http://localhost:${SERENA_DASHBOARD_PORT}/dashboard + +http://localhost:24282/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 -``` + ```yaml + # In compose.yaml + volumes: + - ./my-project:/workspace/ + ``` + + ```bash + # Using the environment variable + PROJECT_DIR="../reponame" docker-compose up serena + ``` ## 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) +- `SERENA_PORT`: MCP server port (default: `9121`) +- `SERENA_DASHBOARD_PORT`: Web dashboard port (default: `24282`) +- `PROJECT_DIR`: The local project directory to mount into the container (default: `./`) - `INTELEPHENSE_LICENSE_KEY`: License key for Intelephense PHP LSP premium features (optional) ## Troubleshooting @@ -156,19 +156,9 @@ netstat -ano | findstr :24282 # Windows 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` +- Check volume mounts in `compose.yaml` - Use absolute paths for external projects - Verify permissions on mounted directories diff --git a/Dockerfile b/Dockerfile index 1dccaf402..321d1f28a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,77 +1,81 @@ -# Base stage with common dependencies +FROM node:22.22-slim AS node +FROM rust:1.94-slim AS rust +FROM ghcr.io/astral-sh/uv:0.11.7 AS uv + FROM python:3.11-slim AS base -SHELL ["/bin/bash", "-c"] +ENV PYTHONUNBUFFERED=1 \ + PYTHONDONTWRITEBYTECODE=1 \ + SERENA_HOME=/home/serena/.serena + +COPY ./src/serena/resources/serena_config.template.yml /home/serena/.serena/serena_config.yml + +RUN sed -i 's/^gui_log_window: .*/gui_log_window: False/' "$SERENA_HOME/serena_config.yml" && \ + sed -i 's/^web_dashboard_listen_address: .*/web_dashboard_listen_address: 0.0.0.0/' "$SERENA_HOME/serena_config.yml" && \ + sed -i 's/^web_dashboard_open_on_launch: .*/web_dashboard_open_on_launch: False/' "$SERENA_HOME/serena_config.yml" + +RUN useradd --create-home --shell /usr/sbin/nologin serena \ + && mkdir -p "/workspace" \ + && chown -R serena:serena "/workspace" \ + && chown -R serena:serena /home/serena + +FROM base AS builder + +WORKDIR /build + +COPY --from=uv /uv /uvx /bin/ + +COPY pyproject.toml README.md uv.lock ./ +COPY src ./src + +RUN uv build + +FROM base AS prod -# Set environment variables to make Python print directly to the terminal and avoid .pyc files. -ENV PYTHONUNBUFFERED=1 -ENV PYTHONDONTWRITEBYTECODE=1 +WORKDIR /workspace + +COPY --from=builder /build/dist/*.whl /tmp/ + +RUN pip install --no-cache-dir /tmp/*.whl \ + && rm -f /tmp/*.whl + +USER serena + +EXPOSE 9121 24282 + +ENTRYPOINT ["serena"] +CMD ["start-mcp-server", "--transport", "stdio", "--project", "./"] + +FROM base AS dev +SHELL ["/bin/bash", "-c"] -# Install system dependencies required for package manager and build tools. -# sudo, wget, zip needed for some assistants, like junie RUN apt-get update && apt-get install -y --no-install-recommends \ curl \ build-essential \ git \ ssh \ - sudo \ wget \ zip \ unzip \ - git \ + sed \ && rm -rf /var/lib/apt/lists/* -# Install pipx. -RUN python3 -m pip install --no-cache-dir pipx \ - && pipx ensurepath - -# Install nodejs -ENV NVM_VERSION=0.40.3 -ENV NODE_VERSION=22.18.0 -RUN curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v${NVM_VERSION}/install.sh | bash -# standard location -ENV NVM_DIR=/root/.nvm -RUN . "$NVM_DIR/nvm.sh" && nvm install ${NODE_VERSION} -RUN . "$NVM_DIR/nvm.sh" && nvm use v${NODE_VERSION} -RUN . "$NVM_DIR/nvm.sh" && nvm alias default v${NODE_VERSION} -ENV PATH="${NVM_DIR}/versions/node/v${NODE_VERSION}/bin/:${PATH}" - -# 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 - -# Install Rust and rustup for rust-analyzer support (minimal profile) -ENV RUSTUP_HOME=/usr/local/rustup -ENV CARGO_HOME=/usr/local/cargo -ENV PATH="${CARGO_HOME}/bin:${PATH}" -RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y \ - --default-toolchain stable \ - --profile minimal \ - && rustup component add rust-analyzer - -# Set the working directory +# NVM can be re-added if switching node version is common while testing/developing serena +COPY --from=node /usr/local /usr/local +COPY --from=uv /uv /uvx /bin/ +COPY --from=rust /usr/local/cargo /usr/local/cargo +COPY --from=rust /usr/local/rustup /usr/local/rustup + +ENV CARGO_HOME=/usr/local/cargo \ + RUSTUP_HOME=/usr/local/rustup \ + PATH="/usr/local/cargo/bin:${PATH}" + WORKDIR /workspaces/serena -# Copy all files for development COPY . /workspaces/serena/ -# Install sed -RUN apt-get update && apt-get install -y sed - -# Create Serena configuration -ENV SERENA_HOME=/workspaces/serena/config -RUN mkdir -p $SERENA_HOME -RUN cp src/serena/resources/serena_config.template.yml $SERENA_HOME/serena_config.yml -RUN sed -i 's/^gui_log_window: .*/gui_log_window: False/' $SERENA_HOME/serena_config.yml -RUN sed -i 's/^web_dashboard_listen_address: .*/web_dashboard_listen_address: 0.0.0.0/' $SERENA_HOME/serena_config.yml -RUN sed -i 's/^web_dashboard_open_on_launch: .*/web_dashboard_open_on_launch: False/' $SERENA_HOME/serena_config.yml - -# Create virtual environment and install dependencies -RUN uv venv -RUN . .venv/bin/activate -RUN uv pip install -r pyproject.toml -e . +RUN uv sync + ENV PATH="/workspaces/serena/.venv/bin:${PATH}" -# Entrypoint to ensure environment is activated -ENTRYPOINT ["/bin/bash", "-c", "source .venv/bin/activate && $0 $@"] +ENTRYPOINT ["serena"] +CMD ["start-mcp-server", "--transport", "stdio", "--project", "./"] diff --git a/compose-dev.yaml b/compose-dev.yaml new file mode 100644 index 000000000..867282a1b --- /dev/null +++ b/compose-dev.yaml @@ -0,0 +1,9 @@ +include: + - compose.yaml + +services: + serena: + pull_policy: build + build: + context: . + target: dev diff --git a/compose.yaml b/compose.yaml index d7c8becd8..b1e126c48 100644 --- a/compose.yaml +++ b/compose.yaml @@ -1,21 +1,23 @@ services: serena: image: serena:latest - # To work with projects, you must mount them into /workspace/ in the container: - # volumes: - # - ./my-project:/workspace/my-project - # - /path/to/another/project:/workspace/another-project + pull_policy: always build: - context: ./ - dockerfile: Dockerfile - target: production + context: . + target: prod 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" - # Alternatively add further arguments, e.g. a context - # - "uv run --directory . serena-mcp-server --transport sse --port 9121 --host 0.0.0.0 --context ide" - + volumes: + - ${PROJECT_DIR:-.}:/workspace/ + # Default command can be overridden below + # command: + # - start-mcp-server + # - --transport + # - streamable-http + # - --host + # - 0.0.0.0 + # - --port + # - "9121" + # - --context + # - vscode diff --git a/examples/docker/Dockerfile b/examples/docker/Dockerfile new file mode 100644 index 000000000..df322b6a8 --- /dev/null +++ b/examples/docker/Dockerfile @@ -0,0 +1,14 @@ +FROM ghcr.io/opentofu/opentofu:minimal AS tofu + +FROM node:22.22-slim AS node + +FROM serena:latest + +USER root + +COPY --from=node /usr/local /usr/local +COPY --from=tofu /usr/local/bin/tofu /usr/local/bin/tofu + +RUN ln -s /usr/local/bin/tofu /usr/local/bin/terraform + +USER serena \ No newline at end of file diff --git a/examples/docker/README.md b/examples/docker/README.md new file mode 100644 index 000000000..80e90435f --- /dev/null +++ b/examples/docker/README.md @@ -0,0 +1,7 @@ +# Example: Build a Serena image for Terraform/OpenTofu & Ansible + +This directory demonstrates building a Serena images with the necessary dependencies for Terraform/OpenTofu and Ansible. + +The `Dockerfile` bases itself on the latest serena image, then layers the necessary dependencies on the top. + +The `compose.yaml` then builds the image and mounts the local directory as a project. \ No newline at end of file diff --git a/examples/docker/compose.yaml b/examples/docker/compose.yaml new file mode 100644 index 000000000..8ec9388e1 --- /dev/null +++ b/examples/docker/compose.yaml @@ -0,0 +1,23 @@ +services: + serena: + image: serena-infra:latest + volumes: + - ${PROJECT_DIR:-.}:/workspace/ + pull_policy: build + build: + context: ./ + dockerfile: Dockerfile + ports: + - "${SERENA_PORT:-9121}:9121" # MCP server port + - "${SERENA_DASHBOARD_PORT:-24282}:24282" # Dashboard port (default 0x5EDA = 24282) + # HTTP Server setup + command: + - start-mcp-server + - --transport + - streamable-http + - --host + - 0.0.0.0 + - --port + - "9121" + - --context + - vscode \ No newline at end of file