Skip to content

CG-10667: Add local codegen server daemon #332

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Feb 24, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 64 additions & 0 deletions Dockerfile-runner
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
FROM ghcr.io/astral-sh/uv:python3.13-bookworm-slim

# Set environment variables to prevent interactive prompts during installation
ENV NVM_DIR=/root/.nvm \
NODE_VERSION=18.17.0 \
DEBIAN_FRONTEND=noninteractive \
NODE_OPTIONS="--max-old-space-size=8192" \
PYTHONUNBUFFERED=1 \
COREPACK_ENABLE_DOWNLOAD_PROMPT=0 \
PYTHONPATH="/usr/local/lib/python3.13/site-packages" \
IS_SANDBOX=True \
HATCH_BUILD_HOOKS_ENABLE=1
# Update packages lists and install git and curl
RUN apt-get update && apt-get install -y \
git \
curl \
gcc \
build-essential \
python3-dev \
# Cleanup apt cache to reduce image size
&& rm -rf /var/lib/apt/lists/*

# Install nvm and Node.js
SHELL ["/bin/bash", "-c"]
RUN curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.5/install.sh | bash \
&& source $NVM_DIR/nvm.sh \
&& nvm install $NODE_VERSION \
&& nvm use default \
&& npm install -g yarn pnpm \
&& corepack enable \
&& corepack prepare yarn@stable --activate \
&& corepack prepare pnpm@latest --activate \
&& uv pip install --system uvicorn[standard]

# Add node and npm to PATH
ENV PATH=$NVM_DIR/versions/node/v$NODE_VERSION/bin:$PATH
RUN node --version \
&& corepack --version \
&& npm --version \
&& yarn --version \
&& pnpm --version \
&& python --version

# Install codegen from source instead of PyPI
WORKDIR /codegen-sdk
COPY . .

# Install dependencies and build codegen with entry points
RUN --mount=type=cache,target=/root/.cache/uv \
uv venv && source .venv/bin/activate \
&& uv sync --frozen --no-dev --all-extras \
&& uv pip install --system -e . --no-deps \
&& uv pip install --system .

RUN codegen --version

# Create a non-root user for local development + debugging
RUN useradd -m -s /bin/bash user
USER root
RUN chown -R user:user /home/user
USER user

WORKDIR /app
ENTRYPOINT ["/bin/bash", "-c"]
3 changes: 3 additions & 0 deletions src/codegen/cli/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
from codegen.cli.commands.reset.main import reset_command
from codegen.cli.commands.run.main import run_command
from codegen.cli.commands.run_on_pr.main import run_on_pr_command
from codegen.cli.commands.start.main import start_command
from codegen.cli.commands.style_debug.main import style_debug_command
from codegen.cli.commands.update.main import update_command

Expand Down Expand Up @@ -47,6 +48,8 @@ def main():
main.add_command(update_command)
main.add_command(config_command)
main.add_command(lsp_command)
main.add_command(start_command)


if __name__ == "__main__":
main()
74 changes: 74 additions & 0 deletions src/codegen/cli/commands/start/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import subprocess
from importlib.metadata import version
from pathlib import Path

import click
import rich
from rich.box import ROUNDED
from rich.panel import Panel

from codegen.configs.models.secrets import SecretsConfig
from codegen.git.schemas.repo_config import RepoConfig


@click.command(name="start")
@click.option("--platform", "-t", type=click.Choice(["linux/amd64", "linux/arm64", "linux/amd64,linux/arm64"]), default="linux/amd64,linux/arm64", help="Target platform(s) for the Docker image")
@click.option("--port", "-p", type=int, default=8000)
@click.option("--detached", "-d", is_flag=True, default=False, help="Starts up the server as detached background process")
def start_command(port: int, platform: str, detached: bool):
"""Starts a local codegen server"""
codegen_version = version("codegen")
rich.print(f"[bold green]Codegen version:[/bold green] {codegen_version}")
codegen_root = Path(__file__).parent.parent.parent.parent.parent.parent

try:
rich.print("[bold blue]Building Docker image...[/bold blue]")
_build_docker_image(codegen_root, platform)
rich.print("[bold blue]Starting Docker container...[/bold blue]")
_run_docker_container(port, platform, detached)

Check failure on line 28 in src/codegen/cli/commands/start/main.py

View workflow job for this annotation

GitHub Actions / mypy

error: Too many arguments for "_run_docker_container" [call-arg]

Check failure on line 28 in src/codegen/cli/commands/start/main.py

View workflow job for this annotation

GitHub Actions / mypy

error: Argument 2 to "_run_docker_container" has incompatible type "str"; expected "bool" [arg-type]
rich.print(Panel(f"[green]Server started successfully![/green]\nAccess the server at: [bold]http://0.0.0.0:{port}[/bold]", box=ROUNDED, title="Codegen Server"))
except subprocess.CalledProcessError as e:
rich.print(f"[bold red]Error:[/bold red] Failed to {e.cmd[0]} Docker container")
raise click.Abort()
except Exception as e:
rich.print(f"[bold red]Error:[/bold red] {e!s}")
raise click.Abort()


def _build_docker_image(codegen_root: Path, platform: str):
build_cmd = [
"docker",
"buildx",
"build",
"--platform",
platform,
"-f",
str(codegen_root / "Dockerfile-runner"),
"-t",
"codegen-runner",
"--load",
str(codegen_root),
]
rich.print(f"build_cmd: {str.join(' ', build_cmd)}")
subprocess.run(build_cmd, check=True)


def _run_docker_container(port: int, detached: bool):
repo_path = Path.cwd().resolve()
repo_config = RepoConfig.from_repo_path(repo_path)

Check failure on line 58 in src/codegen/cli/commands/start/main.py

View workflow job for this annotation

GitHub Actions / mypy

error: Argument 1 to "from_repo_path" of "RepoConfig" has incompatible type "Path"; expected "str" [arg-type]
container_repo_path = f"/app/git/{repo_config.name}"
envvars = {
"REPOSITORY_LANGUAGE": repo_config.language.value,
"REPOSITORY_OWNER": repo_config.organization_name,
"REPOSITORY_PATH": container_repo_path,
"GITHUB_TOKEN": SecretsConfig().github_token,
}
envvars_args = [arg for k, v in envvars.items() for arg in ("--env", f"{k}={v}")]

mount_args = ["-v", f"{repo_path}:{container_repo_path}"]
run_mode = "-d" if detached else "-it"
entry_point = f"uv run --frozen uvicorn codegen.runner.sandbox.server:app --host 0.0.0.0 --port {port}"
run_cmd = ["docker", "run", run_mode, "-p", f"8000:{port}", *mount_args, *envvars_args, "codegen-runner", entry_point]

rich.print(f"run_cmd: {str.join(' ', run_cmd)}")
subprocess.run(run_cmd, check=True)
Loading