From 1bb61b0ae49810bb3a0462108d1fab2ec59e59a7 Mon Sep 17 00:00:00 2001 From: Mish <10400064+mishushakov@users.noreply.github.com> Date: Mon, 3 Nov 2025 13:03:31 +0100 Subject: [PATCH 01/31] initial commit --- Makefile | 2 +- template/Dockerfile | 74 -------------------- template/build_docker.py | 5 ++ template/build_prod.py | 13 ++++ template/build_test.py | 13 ++++ template/e2b.Dockerfile | 1 - template/e2b.toml | 18 ----- template/requirements-dev.txt | 1 + template/start-up.sh | 6 +- template/template.py | 124 ++++++++++++++++++++++++++++++++++ template/test.Dockerfile | 63 ----------------- 11 files changed, 160 insertions(+), 160 deletions(-) delete mode 100644 template/Dockerfile create mode 100644 template/build_docker.py create mode 100644 template/build_prod.py create mode 100644 template/build_test.py delete mode 100644 template/e2b.Dockerfile delete mode 100644 template/e2b.toml create mode 100644 template/requirements-dev.txt mode change 100644 => 100755 template/start-up.sh create mode 100644 template/template.py delete mode 100644 template/test.Dockerfile diff --git a/Makefile b/Makefile index db252514..99efa3b8 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ start-template-server: - docker run --rm -e E2B_LOCAL=true -p 49999:49999 -it $$(docker build . -q -f ./template/test.Dockerfile) + docker run --rm -e E2B_LOCAL=true -p 49999:49999 -it $$(python template/build_docker.py | docker build -q ./template -f -) kill-template-server: docker kill $(shell docker ps --filter expose=49999 --format {{.ID}}) diff --git a/template/Dockerfile b/template/Dockerfile deleted file mode 100644 index 814c14c0..00000000 --- a/template/Dockerfile +++ /dev/null @@ -1,74 +0,0 @@ -FROM python:3.12 - -RUN DEBIAN_FRONTEND=noninteractive apt-get update && apt-get install -y --no-install-recommends \ - build-essential curl git util-linux jq sudo fonts-noto-cjk r-base - -# Install Node.js 20.x from NodeSource -RUN curl -fsSL https://deb.nodesource.com/setup_20.x | bash - && \ - apt-get install -y nodejs - -ENV PIP_DEFAULT_TIMEOUT=100 \ - PIP_DISABLE_PIP_VERSION_CHECK=1 \ - PIP_NO_CACHE_DIR=1 \ - JUPYTER_CONFIG_PATH="/root/.jupyter" \ - IPYTHON_CONFIG_PATH="/root/.ipython" \ - SERVER_PATH="/root/.server" \ - JAVA_HOME=/opt/java/openjdk - -# Install Jupyter -COPY ./requirements.txt requirements.txt -RUN pip install --no-cache-dir -r requirements.txt && ipython kernel install --name "python3" --user - -# R Kernel -RUN R -e "install.packages('IRkernel', repos='https://cloud.r-project.org')" -RUN R -e "IRkernel::installspec(user = FALSE, name = 'r', displayname = 'R')" - -# Javascript Kernel -RUN npm install -g --unsafe-perm git+https://github.com/e2b-dev/ijavascript.git -RUN ijsinstall --install=global - -# Deno Kernel -COPY --from=denoland/deno:bin-2.0.4 /deno /usr/bin/deno -RUN chmod +x /usr/bin/deno -RUN deno jupyter --unstable --install -COPY ./deno.json /root/.local/share/jupyter/kernels/deno/kernel.json - -# Bash Kernel -RUN pip install bash_kernel -RUN python -m bash_kernel.install - -# Create separate virtual environment for server -RUN python -m venv $SERVER_PATH/.venv - -# Copy server and its requirements -RUN mkdir -p $SERVER_PATH/ -COPY ./server/requirements.txt $SERVER_PATH -RUN $SERVER_PATH/.venv/bin/pip install --no-cache-dir -r $SERVER_PATH/requirements.txt -COPY ./server $SERVER_PATH - -# Copy matplotlibrc -COPY matplotlibrc /root/.config/matplotlib/.matplotlibrc - -# Copy Jupyter configuration -COPY ./start-up.sh $JUPYTER_CONFIG_PATH/ -RUN chmod +x $JUPYTER_CONFIG_PATH/start-up.sh - -COPY ./jupyter_server_config.py $JUPYTER_CONFIG_PATH/ - -RUN mkdir -p $IPYTHON_CONFIG_PATH/profile_default -COPY ipython_kernel_config.py $IPYTHON_CONFIG_PATH/profile_default/ - -RUN mkdir -p $IPYTHON_CONFIG_PATH/profile_default/startup -COPY startup_scripts/* $IPYTHON_CONFIG_PATH/profile_default/startup - - -COPY --from=eclipse-temurin:11-jdk $JAVA_HOME $JAVA_HOME -RUN ln -s ${JAVA_HOME}/bin/java /usr/bin/java - -# Java Kernel -RUN wget https://github.com/SpencerPark/IJava/releases/download/v1.3.0/ijava-1.3.0.zip && \ - unzip ijava-1.3.0.zip && \ - python install.py --sys-prefix - -# Setup entrypoint for local development -ENTRYPOINT $JUPYTER_CONFIG_PATH/start-up.sh diff --git a/template/build_docker.py b/template/build_docker.py new file mode 100644 index 00000000..17c17098 --- /dev/null +++ b/template/build_docker.py @@ -0,0 +1,5 @@ +from template import make_template +from e2b import Template + +tmp = make_template(kernels=["python", "javascript"]) +print(Template.to_dockerfile(tmp)) diff --git a/template/build_prod.py b/template/build_prod.py new file mode 100644 index 00000000..28ebf483 --- /dev/null +++ b/template/build_prod.py @@ -0,0 +1,13 @@ +from dotenv import load_dotenv +from e2b_template import Template +from template import make_template + +load_dotenv() + +Template.build( + make_template(), + alias="code-interpreter", + cpu_count=1, + memory_mb=1024, + on_build_logs=lambda log_entry: print(log_entry), +) diff --git a/template/build_test.py b/template/build_test.py new file mode 100644 index 00000000..e87cbd53 --- /dev/null +++ b/template/build_test.py @@ -0,0 +1,13 @@ +from dotenv import load_dotenv +from e2b_template import Template +from template import make_template + +load_dotenv() + +Template.build( + make_template(kernels=["python", "javascript"]), + alias="code-interpreter-dev", + cpu_count=1, + memory_mb=1024, + on_build_logs=lambda log_entry: print(log_entry), +) diff --git a/template/e2b.Dockerfile b/template/e2b.Dockerfile deleted file mode 100644 index 47fb7712..00000000 --- a/template/e2b.Dockerfile +++ /dev/null @@ -1 +0,0 @@ -FROM e2bdev/code-interpreter:latest diff --git a/template/e2b.toml b/template/e2b.toml deleted file mode 100644 index 1eb93ae1..00000000 --- a/template/e2b.toml +++ /dev/null @@ -1,18 +0,0 @@ -# This is a config for E2B sandbox template. -# You can use template ID (nlhz8vlwyupq845jsdg9) or template name (code-interpreter-v1) to create a sandbox: - -# Python SDK -# from e2b import Sandbox, AsyncSandbox -# sandbox = Sandbox.create("code-interpreter-v1") # Sync sandbox -# sandbox = await AsyncSandbox.create("code-interpreter-v1") # Async sandbox - -# JS SDK -# import { Sandbox } from 'e2b' -# const sandbox = await Sandbox.create('code-interpreter-v1') - -team_id = "460355b3-4f64-48f9-9a16-4442817f79f5" -memory_mb = 1_024 -start_cmd = "/root/.jupyter/start-up.sh" -dockerfile = "e2b.Dockerfile" -template_name = "code-interpreter-v1" -template_id = "nlhz8vlwyupq845jsdg9" diff --git a/template/requirements-dev.txt b/template/requirements-dev.txt new file mode 100644 index 00000000..ac40efa3 --- /dev/null +++ b/template/requirements-dev.txt @@ -0,0 +1 @@ +e2b==2.6.0 \ No newline at end of file diff --git a/template/start-up.sh b/template/start-up.sh old mode 100644 new mode 100755 index 5d6223ff..18695f6d --- a/template/start-up.sh +++ b/template/start-up.sh @@ -13,10 +13,10 @@ function start_jupyter_server() { response=$(curl -s -o /dev/null -w "%{http_code}" "http://localhost:8888/api/status") done - cd /root/.server/ - /root/.server/.venv/bin/uvicorn main:app --host 0.0.0.0 --port 49999 --workers 1 --no-access-log --no-use-colors --timeout-keep-alive 640 + cd .server/ + .venv/bin/uvicorn main:app --host 0.0.0.0 --port 49999 --workers 1 --no-access-log --no-use-colors --timeout-keep-alive 640 } echo "Starting Code Interpreter server..." start_jupyter_server & -MATPLOTLIBRC=/root/.config/matplotlib/.matplotlibrc jupyter server --IdentityProvider.token="" >/dev/null 2>&1 +MATPLOTLIBRC=.config/matplotlib/.matplotlibrc jupyter server --IdentityProvider.token="" >/dev/null 2>&1 diff --git a/template/template.py b/template/template.py new file mode 100644 index 00000000..9040fd27 --- /dev/null +++ b/template/template.py @@ -0,0 +1,124 @@ +from e2b import Template, wait_for_port + + +def make_template( + kernels: list[str] = ["python", "r", "javascript", "deno", "bash", "java"], +): + # Start with base template + template = ( + Template() + .from_image("python:3.12") + .set_envs( + { + "PIP_DEFAULT_TIMEOUT": "100", + "PIP_DISABLE_PIP_VERSION_CHECK": "1", + "PIP_NO_CACHE_DIR": "1", + "JUPYTER_CONFIG_PATH": ".jupyter", + "IPYTHON_CONFIG_PATH": ".ipython", + "SERVER_PATH": ".server", + "R_VERSION": "4.4.2", + "R_HOME": "/opt/R/4.4.2", + "JAVA_HOME": "/opt/java/openjdk", + "DENO_INSTALL": "$HOME/.deno", + } + ) + .apt_install( + [ + "build-essential", + "curl", + "git", + "util-linux", + "jq", + "sudo", + "fonts-noto-cjk", + ] + ) + .run_cmd("curl -fsSL https://deb.nodesource.com/setup_20.x | bash -") + .apt_install("nodejs") + .copy("requirements.txt", "requirements.txt") + .pip_install("--no-cache-dir -r requirements.txt") + ) + + if "python" in kernels: + template = template.run_cmd("ipython kernel install --name 'python3' --user") + + # Install R Kernel if requested + if "r" in kernels: + template = ( + template.run_cmd( + "curl -O https://cdn.rstudio.com/r/debian-12/pkgs/r-4.4.2_1_amd64.deb" + ) + .apt_install("./r-4.4.2_1_amd64.deb") + .make_symlink("/opt/R/4.4.2/bin/R", "/usr/bin/R") + .run_cmd( + [ + "R -e \"install.packages('IRkernel', repos='https://cloud.r-project.org')\"", + "R -e \"IRkernel::installspec(user = FALSE, name = 'r', displayname = 'R')\"", + ] + ) + ) + + # Install JavaScript Kernel if requested + if "javascript" in kernels: + template = template.npm_install( + "--unsafe-perm git+https://github.com/e2b-dev/ijavascript.git", + g=True, + ).run_cmd("ijsinstall --install=global") + + # Install Deno Kernel if requested + if "deno" in kernels: + template = template.run_cmd( + [ + "curl -fsSL https://deno.land/x/install/install.sh | sh", + "PATH=$HOME/.deno/bin:$PATH", + "deno jupyter --unstable --install", + ] + ) + template = template.copy( + "deno.json", ".local/share/jupyter/kernels/deno/kernel.json" + ) + + # Install Bash Kernel if requested + if "bash" in kernels: + template = template.pip_install("bash_kernel").run_cmd( + "python -m bash_kernel.install" + ) + + # Install Java and Java Kernel if requested + if "java" in kernels: + template = template.apt_install("default-jdk") + template = template.run_cmd( + [ + "wget https://github.com/SpencerPark/IJava/releases/download/v1.3.0/ijava-1.3.0.zip", + "unzip ijava-1.3.0.zip", + "python install.py --sys-prefix", + ] + ) + + # Common setup steps (always run) + template = ( + template + # Create server virtual environment + .copy("server", ".server") + .run_cmd( + [ + "python -m venv .server/.venv", + ". .server/.venv/bin/activate", + ] + ) + # Copy and install server requirements + .run_cmd( + ".server/.venv/bin/pip install --no-cache-dir -r .server/requirements.txt" + ) + # Copy configuration files + .copy("matplotlibrc", ".config/matplotlib/.matplotlibrc") + .copy("start-up.sh", ".jupyter/start-up.sh", mode=0o755, user="root") + .run_cmd("chmod +x .jupyter/start-up.sh") + .copy("jupyter_server_config.py", ".jupyter/") + .make_dir(".ipython/profile_default/startup") + .copy("ipython_kernel_config.py", ".ipython/profile_default/") + .copy("startup_scripts", ".ipython/profile_default/startup") + .set_start_cmd(".jupyter/start-up.sh", wait_for_port(49999)) + ) + + return template diff --git a/template/test.Dockerfile b/template/test.Dockerfile deleted file mode 100644 index 14eb0005..00000000 --- a/template/test.Dockerfile +++ /dev/null @@ -1,63 +0,0 @@ -FROM python:3.12 - -ENV JAVA_HOME=/opt/java/openjdk -COPY --from=eclipse-temurin:11-jdk $JAVA_HOME $JAVA_HOME -ENV PATH="${JAVA_HOME}/bin:${PATH}" - -RUN DEBIAN_FRONTEND=noninteractive apt-get update && apt-get install -y --no-install-recommends \ - build-essential curl git util-linux jq sudo fonts-noto-cjk - -# Install Node.js 20.x from NodeSource -RUN curl -fsSL https://deb.nodesource.com/setup_20.x | bash - && \ - apt-get install -y nodejs - -ENV PIP_DEFAULT_TIMEOUT=100 \ - PIP_DISABLE_PIP_VERSION_CHECK=1 \ - PIP_NO_CACHE_DIR=1 \ - JUPYTER_CONFIG_PATH="/root/.jupyter" \ - IPYTHON_CONFIG_PATH="/root/.ipython" \ - SERVER_PATH="/root/.server" - -# Install Jupyter -COPY ./template/requirements.txt requirements.txt -RUN pip install --no-cache-dir -r requirements.txt && ipython kernel install --name "python3" --user - -# Javascript Kernel -RUN npm install -g --unsafe-perm git+https://github.com/e2b-dev/ijavascript.git -RUN ijsinstall --install=global - -# Deno Kernel -COPY --from=denoland/deno:bin-2.0.4 /deno /usr/bin/deno -RUN chmod +x /usr/bin/deno -RUN deno jupyter --unstable --install -COPY ./template/deno.json /root/.local/share/jupyter/kernels/deno/kernel.json - -# Create separate virtual environment for server -RUN python -m venv $SERVER_PATH/.venv - -# Copy server and its requirements -RUN mkdir -p $SERVER_PATH/ -COPY ./template/server/requirements.txt $SERVER_PATH -RUN $SERVER_PATH/.venv/bin/pip install --no-cache-dir -r $SERVER_PATH/requirements.txt -COPY ./template/server $SERVER_PATH - -# Copy matplotlibrc -COPY ./template/matplotlibrc /root/.config/matplotlib/matplotlibrc - -# Copy Jupyter configuration -COPY ./template/start-up.sh $JUPYTER_CONFIG_PATH/ -RUN chmod +x $JUPYTER_CONFIG_PATH/start-up.sh - -COPY ./template/jupyter_server_config.py $JUPYTER_CONFIG_PATH/ - -RUN mkdir -p $IPYTHON_CONFIG_PATH/profile_default -COPY ./template/ipython_kernel_config.py $IPYTHON_CONFIG_PATH/profile_default/ - -RUN mkdir -p $IPYTHON_CONFIG_PATH/profile_default/startup -COPY ./template/startup_scripts/* $IPYTHON_CONFIG_PATH/profile_default/startup - -# Setup entrypoint for local development -WORKDIR /home/user -COPY ./chart_data_extractor ./chart_data_extractor -RUN pip install -e ./chart_data_extractor -ENTRYPOINT $JUPYTER_CONFIG_PATH/start-up.sh From 3b9ce192bc48c93b6b93f9742f6506d1d677ef5b Mon Sep 17 00:00:00 2001 From: Mish <10400064+mishushakov@users.noreply.github.com> Date: Mon, 3 Nov 2025 13:08:07 +0100 Subject: [PATCH 02/31] cursor suggestions --- template/build_prod.py | 2 +- template/build_test.py | 2 +- template/template.py | 7 +------ 3 files changed, 3 insertions(+), 8 deletions(-) diff --git a/template/build_prod.py b/template/build_prod.py index 28ebf483..b853fc4a 100644 --- a/template/build_prod.py +++ b/template/build_prod.py @@ -1,5 +1,5 @@ from dotenv import load_dotenv -from e2b_template import Template +from e2b import Template from template import make_template load_dotenv() diff --git a/template/build_test.py b/template/build_test.py index e87cbd53..7174215b 100644 --- a/template/build_test.py +++ b/template/build_test.py @@ -1,5 +1,5 @@ from dotenv import load_dotenv -from e2b_template import Template +from e2b import Template from template import make_template load_dotenv() diff --git a/template/template.py b/template/template.py index 9040fd27..a3f32523 100644 --- a/template/template.py +++ b/template/template.py @@ -100,12 +100,7 @@ def make_template( template # Create server virtual environment .copy("server", ".server") - .run_cmd( - [ - "python -m venv .server/.venv", - ". .server/.venv/bin/activate", - ] - ) + .run_cmd("python -m venv .server/.venv") # Copy and install server requirements .run_cmd( ".server/.venv/bin/pip install --no-cache-dir -r .server/requirements.txt" From 856b37fcfa47e385cff58ab189095902da48e428 Mon Sep 17 00:00:00 2001 From: Mish <10400064+mishushakov@users.noreply.github.com> Date: Mon, 3 Nov 2025 13:58:44 +0100 Subject: [PATCH 03/31] updated --- template/build_prod.py | 10 +++++----- template/build_test.py | 6 +++--- template/template.py | 21 +++++++++++---------- 3 files changed, 19 insertions(+), 18 deletions(-) diff --git a/template/build_prod.py b/template/build_prod.py index b853fc4a..c923edba 100644 --- a/template/build_prod.py +++ b/template/build_prod.py @@ -1,13 +1,13 @@ from dotenv import load_dotenv -from e2b import Template +from e2b import Template, default_build_logger from template import make_template load_dotenv() Template.build( - make_template(), + make_template(set_user_workdir=True), alias="code-interpreter", - cpu_count=1, - memory_mb=1024, - on_build_logs=lambda log_entry: print(log_entry), + cpu_count=2, + memory_mb=2048, + on_build_logs=default_build_logger(), ) diff --git a/template/build_test.py b/template/build_test.py index 7174215b..2e228785 100644 --- a/template/build_test.py +++ b/template/build_test.py @@ -1,13 +1,13 @@ from dotenv import load_dotenv -from e2b import Template +from e2b import Template, default_build_logger from template import make_template load_dotenv() Template.build( - make_template(kernels=["python", "javascript"]), + make_template(kernels=["python", "javascript"], set_user_workdir=True), alias="code-interpreter-dev", cpu_count=1, memory_mb=1024, - on_build_logs=lambda log_entry: print(log_entry), + on_build_logs=default_build_logger(min_level="debug"), ) diff --git a/template/template.py b/template/template.py index a3f32523..5e2e608f 100644 --- a/template/template.py +++ b/template/template.py @@ -1,13 +1,16 @@ -from e2b import Template, wait_for_port +from e2b import Template, wait_for_port, wait_for_url def make_template( kernels: list[str] = ["python", "r", "javascript", "deno", "bash", "java"], + set_user_workdir: bool = False, ): # Start with base template template = ( Template() .from_image("python:3.12") + .set_user("root") + .set_workdir("/") .set_envs( { "PIP_DEFAULT_TIMEOUT": "100", @@ -19,7 +22,7 @@ def make_template( "R_VERSION": "4.4.2", "R_HOME": "/opt/R/4.4.2", "JAVA_HOME": "/opt/java/openjdk", - "DENO_INSTALL": "$HOME/.deno", + "DENO_INSTALL": "/opt/deno", } ) .apt_install( @@ -45,11 +48,7 @@ def make_template( # Install R Kernel if requested if "r" in kernels: template = ( - template.run_cmd( - "curl -O https://cdn.rstudio.com/r/debian-12/pkgs/r-4.4.2_1_amd64.deb" - ) - .apt_install("./r-4.4.2_1_amd64.deb") - .make_symlink("/opt/R/4.4.2/bin/R", "/usr/bin/R") + template.apt_install("r-base") .run_cmd( [ "R -e \"install.packages('IRkernel', repos='https://cloud.r-project.org')\"", @@ -70,7 +69,7 @@ def make_template( template = template.run_cmd( [ "curl -fsSL https://deno.land/x/install/install.sh | sh", - "PATH=$HOME/.deno/bin:$PATH", + "PATH=/opt/deno/bin:$PATH", "deno jupyter --unstable --install", ] ) @@ -113,7 +112,9 @@ def make_template( .make_dir(".ipython/profile_default/startup") .copy("ipython_kernel_config.py", ".ipython/profile_default/") .copy("startup_scripts", ".ipython/profile_default/startup") - .set_start_cmd(".jupyter/start-up.sh", wait_for_port(49999)) ) - return template + if set_user_workdir: + template = template.set_user("user").set_workdir("/home/user") + + return template.set_start_cmd(".jupyter/start-up.sh", wait_for_url("http://localhost:49999/health")) From 4a1a7bc0b16265213183bf405ab4ce22e55fa709 Mon Sep 17 00:00:00 2001 From: Mish <10400064+mishushakov@users.noreply.github.com> Date: Mon, 3 Nov 2025 15:06:53 +0100 Subject: [PATCH 04/31] fix template --- template/start-up.sh | 2 +- template/template.py | 15 +++++++++------ 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/template/start-up.sh b/template/start-up.sh index 18695f6d..81375084 100755 --- a/template/start-up.sh +++ b/template/start-up.sh @@ -13,7 +13,7 @@ function start_jupyter_server() { response=$(curl -s -o /dev/null -w "%{http_code}" "http://localhost:8888/api/status") done - cd .server/ + cd /.server/ .venv/bin/uvicorn main:app --host 0.0.0.0 --port 49999 --workers 1 --no-access-log --no-use-colors --timeout-keep-alive 640 } diff --git a/template/template.py b/template/template.py index 5e2e608f..2ccdbef8 100644 --- a/template/template.py +++ b/template/template.py @@ -104,9 +104,15 @@ def make_template( .run_cmd( ".server/.venv/bin/pip install --no-cache-dir -r .server/requirements.txt" ) - # Copy configuration files - .copy("matplotlibrc", ".config/matplotlib/.matplotlibrc") - .copy("start-up.sh", ".jupyter/start-up.sh", mode=0o755, user="root") + ) + + if set_user_workdir: + template = template.set_user("user").set_workdir("/home/user") + + # Copy configuration files + template = ( + template.copy("matplotlibrc", ".config/matplotlib/.matplotlibrc") + .copy("start-up.sh", ".jupyter/start-up.sh") .run_cmd("chmod +x .jupyter/start-up.sh") .copy("jupyter_server_config.py", ".jupyter/") .make_dir(".ipython/profile_default/startup") @@ -114,7 +120,4 @@ def make_template( .copy("startup_scripts", ".ipython/profile_default/startup") ) - if set_user_workdir: - template = template.set_user("user").set_workdir("/home/user") - return template.set_start_cmd(".jupyter/start-up.sh", wait_for_url("http://localhost:49999/health")) From 3bb1ac5af2edb19a9659bc3b0422bdbf3a5fa070 Mon Sep 17 00:00:00 2001 From: Mish <10400064+mishushakov@users.noreply.github.com> Date: Mon, 3 Nov 2025 15:11:34 +0100 Subject: [PATCH 05/31] fmt --- template/template.py | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/template/template.py b/template/template.py index 2ccdbef8..56ec90c8 100644 --- a/template/template.py +++ b/template/template.py @@ -47,14 +47,11 @@ def make_template( # Install R Kernel if requested if "r" in kernels: - template = ( - template.apt_install("r-base") - .run_cmd( - [ - "R -e \"install.packages('IRkernel', repos='https://cloud.r-project.org')\"", - "R -e \"IRkernel::installspec(user = FALSE, name = 'r', displayname = 'R')\"", - ] - ) + template = template.apt_install("r-base").run_cmd( + [ + "R -e \"install.packages('IRkernel', repos='https://cloud.r-project.org')\"", + "R -e \"IRkernel::installspec(user = FALSE, name = 'r', displayname = 'R')\"", + ] ) # Install JavaScript Kernel if requested @@ -120,4 +117,6 @@ def make_template( .copy("startup_scripts", ".ipython/profile_default/startup") ) - return template.set_start_cmd(".jupyter/start-up.sh", wait_for_url("http://localhost:49999/health")) + return template.set_start_cmd( + ".jupyter/start-up.sh", wait_for_url("http://localhost:49999/health") + ) From 748968ac7e515b1b087ece35298b1b2a86254535 Mon Sep 17 00:00:00 2001 From: Mish <10400064+mishushakov@users.noreply.github.com> Date: Mon, 3 Nov 2025 16:41:11 +0100 Subject: [PATCH 06/31] removed unused env vars --- template/template.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/template/template.py b/template/template.py index 56ec90c8..490f136f 100644 --- a/template/template.py +++ b/template/template.py @@ -19,8 +19,6 @@ def make_template( "JUPYTER_CONFIG_PATH": ".jupyter", "IPYTHON_CONFIG_PATH": ".ipython", "SERVER_PATH": ".server", - "R_VERSION": "4.4.2", - "R_HOME": "/opt/R/4.4.2", "JAVA_HOME": "/opt/java/openjdk", "DENO_INSTALL": "/opt/deno", } @@ -66,7 +64,7 @@ def make_template( template = template.run_cmd( [ "curl -fsSL https://deno.land/x/install/install.sh | sh", - "PATH=/opt/deno/bin:$PATH", + "PATH=$DENO_INSTALL/bin:$PATH", "deno jupyter --unstable --install", ] ) From ef6597acee4852234c351d436ac09e451d363186 Mon Sep 17 00:00:00 2001 From: Mish <10400064+mishushakov@users.noreply.github.com> Date: Mon, 3 Nov 2025 16:51:30 +0100 Subject: [PATCH 07/31] updated ci --- .github/workflows/build_test_template.yml | 23 +++++++++++++++-------- .github/workflows/pull_request.yml | 2 +- template/build_ci.py | 14 ++++++++++++++ 3 files changed, 30 insertions(+), 9 deletions(-) create mode 100644 template/build_ci.py diff --git a/.github/workflows/build_test_template.yml b/.github/workflows/build_test_template.yml index e8d6edb2..b3852fe6 100644 --- a/.github/workflows/build_test_template.yml +++ b/.github/workflows/build_test_template.yml @@ -3,7 +3,7 @@ name: Build Template on: workflow_call: secrets: - E2B_TESTS_ACCESS_TOKEN: + E2B_API_KEY: required: true inputs: E2B_DOMAIN: @@ -34,20 +34,27 @@ jobs: echo "Version: $VERSION" sed -i "s/e2b_charts/e2b_charts==${VERSION}/g" requirements.txt - - name: Install E2B CLI - run: npm install -g @e2b/cli + - uses: actions/setup-python@v6 + with: + python-version: '3.13' + + - name: Install development dependencies + working-directory: ./template + run: pip install -r requirements-dev.txt - name: Build E2B template id: build-template + working-directory: ./template run: | - rm -f e2b.toml - e2b template build --memory-mb 1024 -c "/root/.jupyter/start-up.sh" -d "Dockerfile" - TEMPLATE_ID=$(grep "template_id" e2b.toml | cut -d '"' -f 2) + BUILD_OUTPUT=$(python build_ci.py 2>&1) + echo "$BUILD_OUTPUT" + TEMPLATE_ID=$(echo "$BUILD_OUTPUT" | grep "Template created with ID:" | sed 's/.*Template created with ID: \([^,]*\).*/\1/') + BUILD_ID=$(echo "$BUILD_OUTPUT" | grep -A 1 "Build ID:" | tail -n 1 | xargs) echo "Captured Template ID: $TEMPLATE_ID" + echo "Captured Build ID: $BUILD_ID" echo "template_id=$TEMPLATE_ID" >> $GITHUB_OUTPUT - working-directory: ./template env: - E2B_ACCESS_TOKEN: ${{ secrets.E2B_TESTS_ACCESS_TOKEN }} + E2B_API_KEY: ${{ secrets.E2B_API_KEY }} E2B_DOMAIN: ${{ inputs.E2B_DOMAIN }} - name: Output template ID diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index 5ea47773..7ed194c9 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -17,7 +17,7 @@ jobs: build-template: uses: ./.github/workflows/build_test_template.yml secrets: - E2B_TESTS_ACCESS_TOKEN: ${{ secrets.E2B_TESTS_ACCESS_TOKEN }} + E2B_API_KEY: ${{ secrets.E2B_API_KEY }} with: E2B_DOMAIN: ${{ vars.E2B_DOMAIN }} js-sdk: diff --git a/template/build_ci.py b/template/build_ci.py new file mode 100644 index 00000000..7b26721d --- /dev/null +++ b/template/build_ci.py @@ -0,0 +1,14 @@ +from dotenv import load_dotenv +from e2b import Template, default_build_logger +from template import make_template +from uuid import uuid4 + +load_dotenv() + +Template.build( + make_template(set_user_workdir=True), + alias="code-interpreter-ci-" + str(uuid4()), + cpu_count=2, + memory_mb=2048, + on_build_logs=default_build_logger(), +) From be9b0474649def9909f12c768427e3e39debbc89 Mon Sep 17 00:00:00 2001 From: Mish <10400064+mishushakov@users.noreply.github.com> Date: Mon, 3 Nov 2025 16:53:28 +0100 Subject: [PATCH 08/31] python build ci test --- .github/workflows/build_test_template.yml | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build_test_template.yml b/.github/workflows/build_test_template.yml index b3852fe6..e2694b86 100644 --- a/.github/workflows/build_test_template.yml +++ b/.github/workflows/build_test_template.yml @@ -46,13 +46,14 @@ jobs: id: build-template working-directory: ./template run: | - BUILD_OUTPUT=$(python build_ci.py 2>&1) - echo "$BUILD_OUTPUT" - TEMPLATE_ID=$(echo "$BUILD_OUTPUT" | grep "Template created with ID:" | sed 's/.*Template created with ID: \([^,]*\).*/\1/') - BUILD_ID=$(echo "$BUILD_OUTPUT" | grep -A 1 "Build ID:" | tail -n 1 | xargs) - echo "Captured Template ID: $TEMPLATE_ID" - echo "Captured Build ID: $BUILD_ID" - echo "template_id=$TEMPLATE_ID" >> $GITHUB_OUTPUT + python build_ci.py + # BUILD_OUTPUT=$(python build_ci.py 2>&1) + # echo "$BUILD_OUTPUT" + # TEMPLATE_ID=$(echo "$BUILD_OUTPUT" | grep "Template created with ID:" | sed 's/.*Template created with ID: \([^,]*\).*/\1/') + # BUILD_ID=$(echo "$BUILD_OUTPUT" | grep -A 1 "Build ID:" | tail -n 1 | xargs) + # echo "Captured Template ID: $TEMPLATE_ID" + # echo "Captured Build ID: $BUILD_ID" + # echo "template_id=$TEMPLATE_ID" >> $GITHUB_OUTPUT env: E2B_API_KEY: ${{ secrets.E2B_API_KEY }} E2B_DOMAIN: ${{ inputs.E2B_DOMAIN }} From cf7a9952dc0905da6aa659ca0293a3865dcb6ae1 Mon Sep 17 00:00:00 2001 From: Mish <10400064+mishushakov@users.noreply.github.com> Date: Mon, 3 Nov 2025 16:55:09 +0100 Subject: [PATCH 09/31] dependencies --- template/build_ci.py | 1 - template/requirements-dev.txt | 3 ++- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/template/build_ci.py b/template/build_ci.py index 7b26721d..8711fdb0 100644 --- a/template/build_ci.py +++ b/template/build_ci.py @@ -1,4 +1,3 @@ -from dotenv import load_dotenv from e2b import Template, default_build_logger from template import make_template from uuid import uuid4 diff --git a/template/requirements-dev.txt b/template/requirements-dev.txt index ac40efa3..16a0115f 100644 --- a/template/requirements-dev.txt +++ b/template/requirements-dev.txt @@ -1 +1,2 @@ -e2b==2.6.0 \ No newline at end of file +e2b==2.6.0 +python-dotenv==1.2.1 \ No newline at end of file From af0c3c0a915ca93c16196652387d24c6372871e1 Mon Sep 17 00:00:00 2001 From: Mish <10400064+mishushakov@users.noreply.github.com> Date: Mon, 3 Nov 2025 16:55:41 +0100 Subject: [PATCH 10/31] removed dotenv from ci --- template/build_ci.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/template/build_ci.py b/template/build_ci.py index 8711fdb0..8b2f2ed8 100644 --- a/template/build_ci.py +++ b/template/build_ci.py @@ -2,8 +2,6 @@ from template import make_template from uuid import uuid4 -load_dotenv() - Template.build( make_template(set_user_workdir=True), alias="code-interpreter-ci-" + str(uuid4()), From a6446d0a248445be27893b003764912c7f04c1c8 Mon Sep 17 00:00:00 2001 From: Mish <10400064+mishushakov@users.noreply.github.com> Date: Mon, 3 Nov 2025 17:01:28 +0100 Subject: [PATCH 11/31] add capture for template id, build id --- .github/workflows/build_test_template.yml | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/.github/workflows/build_test_template.yml b/.github/workflows/build_test_template.yml index e2694b86..10979424 100644 --- a/.github/workflows/build_test_template.yml +++ b/.github/workflows/build_test_template.yml @@ -13,6 +13,9 @@ on: template_id: description: "The ID of the built template" value: ${{ jobs.build.outputs.template_id }} + build_id: + description: "The ID of the build" + value: ${{ jobs.build.outputs.build_id }} permissions: contents: read @@ -23,6 +26,7 @@ jobs: runs-on: ubuntu-latest outputs: template_id: ${{ steps.build-template.outputs.template_id }} + build_id: ${{ steps.build-template.outputs.build_id }} steps: - name: Checkout repository uses: actions/checkout@v4 @@ -46,18 +50,18 @@ jobs: id: build-template working-directory: ./template run: | - python build_ci.py - # BUILD_OUTPUT=$(python build_ci.py 2>&1) - # echo "$BUILD_OUTPUT" - # TEMPLATE_ID=$(echo "$BUILD_OUTPUT" | grep "Template created with ID:" | sed 's/.*Template created with ID: \([^,]*\).*/\1/') - # BUILD_ID=$(echo "$BUILD_OUTPUT" | grep -A 1 "Build ID:" | tail -n 1 | xargs) - # echo "Captured Template ID: $TEMPLATE_ID" - # echo "Captured Build ID: $BUILD_ID" - # echo "template_id=$TEMPLATE_ID" >> $GITHUB_OUTPUT + python build_ci.py 2>&1 | tee build_output.log + TEMPLATE_ID=$(grep "Template created with ID:" build_output.log | sed 's/.*Template created with ID: \([^,]*\).*/\1/') + BUILD_ID=$(grep -A 1 "Build ID:" build_output.log | tail -n 1 | xargs) + echo "Captured Template ID: $TEMPLATE_ID" + echo "Captured Build ID: $BUILD_ID" + echo "template_id=$TEMPLATE_ID" >> $GITHUB_OUTPUT + echo "build_id=$BUILD_ID" >> $GITHUB_OUTPUT env: E2B_API_KEY: ${{ secrets.E2B_API_KEY }} E2B_DOMAIN: ${{ inputs.E2B_DOMAIN }} - - name: Output template ID + - name: Output template and build IDs run: | echo "Template ID from step output: ${{ steps.build-template.outputs.template_id }}" + echo "Build ID from step output: ${{ steps.build-template.outputs.build_id }}" From 66eb9fb4c2d0400a702dfc578b91500b6bed2a7c Mon Sep 17 00:00:00 2001 From: Mish <10400064+mishushakov@users.noreply.github.com> Date: Mon, 3 Nov 2025 17:30:40 +0100 Subject: [PATCH 12/31] fixes java test, template name --- python/tests/sync/test_default_kernels.py | 2 +- template/build_prod.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/python/tests/sync/test_default_kernels.py b/python/tests/sync/test_default_kernels.py index 8dfd4bf9..eba627d8 100644 --- a/python/tests/sync/test_default_kernels.py +++ b/python/tests/sync/test_default_kernels.py @@ -17,7 +17,7 @@ def test_r_kernel(sandbox: Sandbox): @pytest.mark.skip_debug() def test_java_kernel(sandbox: Sandbox): execution = sandbox.run_code('System.out.println("Hello, World!")', language="java") - assert execution.logs.stdout[0] == "Hello, World!" + assert execution.logs.stdout[0] == "Hello, World!\n" def test_js_esm_imports(sandbox: Sandbox): diff --git a/template/build_prod.py b/template/build_prod.py index c923edba..d88c447d 100644 --- a/template/build_prod.py +++ b/template/build_prod.py @@ -6,7 +6,7 @@ Template.build( make_template(set_user_workdir=True), - alias="code-interpreter", + alias="code-interpreter-v1", cpu_count=2, memory_mb=2048, on_build_logs=default_build_logger(), From 8691060b4aa71a5126b3d713baeda64829a4ac4c Mon Sep 17 00:00:00 2001 From: Mish <10400064+mishushakov@users.noreply.github.com> Date: Mon, 3 Nov 2025 18:14:19 +0100 Subject: [PATCH 13/31] lint --- .github/workflows/pull_request.yml | 19 ++++++++++--------- template/template.py | 2 +- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index 7ed194c9..9b57ef84 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -44,14 +44,15 @@ jobs: with: E2B_DOMAIN: ${{ vars.E2B_DOMAIN }} E2B_TESTS_TEMPLATE: ${{ needs.build-template.outputs.template_id }} - cleanup-build-template: - uses: ./.github/workflows/cleanup_build_template.yml - needs: [build-template, js-sdk, python-sdk, performance-tests] - if: always() && !contains(needs.build-template.result, 'failure') && !contains(needs.build-template.result, 'cancelled') - secrets: - E2B_TESTS_ACCESS_TOKEN: ${{ secrets.E2B_TESTS_ACCESS_TOKEN }} - with: - E2B_DOMAIN: ${{ vars.E2B_DOMAIN }} - E2B_TESTS_TEMPLATE: ${{ needs.build-template.outputs.template_id }} + # Commented out because else we won't be hitting caches + # cleanup-build-template: + # uses: ./.github/workflows/cleanup_build_template.yml + # needs: [build-template, js-sdk, python-sdk, performance-tests] + # if: always() && !contains(needs.build-template.result, 'failure') && !contains(needs.build-template.result, 'cancelled') + # secrets: + # E2B_TESTS_ACCESS_TOKEN: ${{ secrets.E2B_TESTS_ACCESS_TOKEN }} + # with: + # E2B_DOMAIN: ${{ vars.E2B_DOMAIN }} + # E2B_TESTS_TEMPLATE: ${{ needs.build-template.outputs.template_id }} charts-tests: uses: ./.github/workflows/charts_tests.yml diff --git a/template/template.py b/template/template.py index 490f136f..d20798ec 100644 --- a/template/template.py +++ b/template/template.py @@ -1,4 +1,4 @@ -from e2b import Template, wait_for_port, wait_for_url +from e2b import Template, wait_for_url def make_template( From aac47987c907d799dc1be722df3be8be3d798972 Mon Sep 17 00:00:00 2001 From: Mish <10400064+mishushakov@users.noreply.github.com> Date: Mon, 3 Nov 2025 23:52:50 +0100 Subject: [PATCH 14/31] added changeset --- .changeset/empty-wasps-count.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/empty-wasps-count.md diff --git a/.changeset/empty-wasps-count.md b/.changeset/empty-wasps-count.md new file mode 100644 index 00000000..019c88f4 --- /dev/null +++ b/.changeset/empty-wasps-count.md @@ -0,0 +1,5 @@ +--- +'@e2b/code-interpreter-template': minor +--- + +updated template to new SDK From c2e9f10b41053d156e0bae7d4cd757aa3e7dee1a Mon Sep 17 00:00:00 2001 From: Mish <10400064+mishushakov@users.noreply.github.com> Date: Mon, 3 Nov 2025 23:55:10 +0100 Subject: [PATCH 15/31] readded cleanup --- .github/workflows/pull_request.yml | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index 9b57ef84..7ed194c9 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -44,15 +44,14 @@ jobs: with: E2B_DOMAIN: ${{ vars.E2B_DOMAIN }} E2B_TESTS_TEMPLATE: ${{ needs.build-template.outputs.template_id }} - # Commented out because else we won't be hitting caches - # cleanup-build-template: - # uses: ./.github/workflows/cleanup_build_template.yml - # needs: [build-template, js-sdk, python-sdk, performance-tests] - # if: always() && !contains(needs.build-template.result, 'failure') && !contains(needs.build-template.result, 'cancelled') - # secrets: - # E2B_TESTS_ACCESS_TOKEN: ${{ secrets.E2B_TESTS_ACCESS_TOKEN }} - # with: - # E2B_DOMAIN: ${{ vars.E2B_DOMAIN }} - # E2B_TESTS_TEMPLATE: ${{ needs.build-template.outputs.template_id }} + cleanup-build-template: + uses: ./.github/workflows/cleanup_build_template.yml + needs: [build-template, js-sdk, python-sdk, performance-tests] + if: always() && !contains(needs.build-template.result, 'failure') && !contains(needs.build-template.result, 'cancelled') + secrets: + E2B_TESTS_ACCESS_TOKEN: ${{ secrets.E2B_TESTS_ACCESS_TOKEN }} + with: + E2B_DOMAIN: ${{ vars.E2B_DOMAIN }} + E2B_TESTS_TEMPLATE: ${{ needs.build-template.outputs.template_id }} charts-tests: uses: ./.github/workflows/charts_tests.yml From 46e40271ea91126960f7b469dbeafa8785facc7e Mon Sep 17 00:00:00 2001 From: Mish <10400064+mishushakov@users.noreply.github.com> Date: Tue, 4 Nov 2025 00:00:09 +0100 Subject: [PATCH 16/31] updated release CI --- .github/workflows/release.yml | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3ef0ed82..18a7a261 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -200,13 +200,13 @@ jobs: sed -i "s/e2b_charts/e2b_charts==${VERSION}/g" requirements.txt - - name: Build Docker image - uses: docker/build-push-action@v6 - with: - context: ./template - push: true - platforms: linux/amd64 - tags: ${{ secrets.DOCKERHUB_USERNAME }}/code-interpreter:latest + - name: Build and push to DockerHub + working-directory: ./template + run: | + poetry run python build_docker.py | docker buildx build \ + --platform linux/amd64 \ + --push \ + --tag ${{ secrets.DOCKERHUB_USERNAME }}/desktop:latest -f - . build-template: name: Build E2B template @@ -219,14 +219,22 @@ jobs: steps: - name: Checkout repository uses: actions/checkout@v4 - - name: Install E2B CLI - run: npm install -g @e2b/cli + - uses: actions/setup-python@v6 + with: + python-version: '3.13' - - name: Build e2b - run: e2b template build + - name: Install development dependencies working-directory: ./template + run: pip install -r requirements-dev.txt + + - name: Build E2B template + id: build-template + working-directory: ./template + run: | + python build_prod.py env: - E2B_ACCESS_TOKEN: ${{ secrets.E2B_ACCESS_TOKEN }} + E2B_API_KEY: ${{ secrets.E2B_API_KEY }} + E2B_DOMAIN: ${{ vars.E2B_DOMAIN }} python-tests: name: Python Tests From cc9872056bc89022eb79db1f5926bbd993dc9dfd Mon Sep 17 00:00:00 2001 From: Mish <10400064+mishushakov@users.noreply.github.com> Date: Tue, 4 Nov 2025 00:00:30 +0100 Subject: [PATCH 17/31] updated dockerhub push tag --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 18a7a261..7077fb17 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -206,7 +206,7 @@ jobs: poetry run python build_docker.py | docker buildx build \ --platform linux/amd64 \ --push \ - --tag ${{ secrets.DOCKERHUB_USERNAME }}/desktop:latest -f - . + --tag ${{ secrets.DOCKERHUB_USERNAME }}/code-interpreter:latest -f - . build-template: name: Build E2B template From 293571668373ca68eb8b5a31b12033ffdaf8599e Mon Sep 17 00:00:00 2001 From: Mish <10400064+mishushakov@users.noreply.github.com> Date: Tue, 4 Nov 2025 00:31:05 +0100 Subject: [PATCH 18/31] updated readme --- template/README.md | 74 +++++++++++++++++++++++++++------------------- 1 file changed, 43 insertions(+), 31 deletions(-) diff --git a/template/README.md b/template/README.md index c1103acd..d74d92c3 100644 --- a/template/README.md +++ b/template/README.md @@ -1,45 +1,57 @@ # Using custom sandbox with Code Interpreter SDK -If you want to customize the Code Interprerter sandbox (e.g.: add a preinstalled package) you can do that by using a [custom sandbox template](https://e2b.dev/docs/sandbox-template). - +If you want to customize the Code Interprerter sandbox (e.g.: add a preinstalled package) you can do that by creating a [custom sandbox template](https://e2b.dev/docs/template/quickstart). ## Step-by-step guide -1. Create custom sandbox by following [this guide](https://e2b.dev/docs/sandbox-template) -2. Use prebuilt [E2B Code Interpreter image](https://hub.docker.com/r/e2bdev/code-interpreter) by replacing the `FROM` command in your `e2b.Dockerfile` with following +1. Install E2B SDK + +``` +pip install e2b dotenv +``` + +2. Create a custom sandbox template: + +**template.py** + +```python +from e2b import Template + +template = Template().from_template("code-interpreter-v1") +``` + +3. Create a build script: - ```Dockerfile - FROM e2bdev/code-interpreter:latest - ``` +**build.py** -3. Copy [`start-up.sh`](./start-up.sh) to the same directory where's your `e2b.toml` +```python +from dotenv import load_dotenv +from .template import template +from e2b import Template, default_build_logger -4. Run the following in the same directory where's your `e2b.toml` - ```sh - e2b template build -c "/root/.jupyter/start-up.sh" - ``` +load_dotenv() -5. Use your custom sandbox with Code Interpreter SDK +Template.build( + template, + alias="code-interpreter-custom", + cpu_count=2, + memory_mb=2048, + on_build_logs=default_build_logger(), +) +``` - **Python** - ```python - from e2b_code_interpreter import Sandbox - sandbox = Sandbox.create(template="your-custom-sandbox-name") - execution = sandbox.run_code("print('hello')") - sandbox.kill() +3. Build the template: - # Or you can use `with` which handles closing the sandbox for you - with Sandbox.create(template="your-custom-sandbox-name") as sandbox: - execution = sandbox.run_code("print('hello')") - ``` - +``` +python build.py +``` - **JavaScript/TypeScript** +4. Use the custom template: - ```js - import {Sandbox} from '@e2b/code-interpreter' +```python +from e2b import Sandbox -const sandbox = await Sandbox.create({template: 'your-custom-sandbox-name'}) -const execution = await sandbox.runCode('print("hello")') -await sandbox.kill() - ``` +sbx = Sandbox.create(template="code-interpreter-custom") +execution = sbx.run_code("print('Hello, World!')") +print(execution.logs.stdout) +``` From 80aed03a6dc259f945bf7b8ffe87a101f9a8e3e7 Mon Sep 17 00:00:00 2001 From: Mish <10400064+mishushakov@users.noreply.github.com> Date: Tue, 4 Nov 2025 00:35:06 +0100 Subject: [PATCH 19/31] remove poetry from template build ci --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 7077fb17..06a97a21 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -203,7 +203,7 @@ jobs: - name: Build and push to DockerHub working-directory: ./template run: | - poetry run python build_docker.py | docker buildx build \ + python build_docker.py | docker buildx build \ --platform linux/amd64 \ --push \ --tag ${{ secrets.DOCKERHUB_USERNAME }}/code-interpreter:latest -f - . From 7673c9f73567ecb2eed9911c51f89c7cf8ed802d Mon Sep 17 00:00:00 2001 From: Mish <10400064+mishushakov@users.noreply.github.com> Date: Tue, 4 Nov 2025 15:26:13 +0100 Subject: [PATCH 20/31] generate template alias before test run --- .github/workflows/build_test_template.yml | 14 +++----------- template/build_ci.py | 4 ++-- 2 files changed, 5 insertions(+), 13 deletions(-) diff --git a/.github/workflows/build_test_template.yml b/.github/workflows/build_test_template.yml index 10979424..0aadfce7 100644 --- a/.github/workflows/build_test_template.yml +++ b/.github/workflows/build_test_template.yml @@ -50,18 +50,10 @@ jobs: id: build-template working-directory: ./template run: | - python build_ci.py 2>&1 | tee build_output.log - TEMPLATE_ID=$(grep "Template created with ID:" build_output.log | sed 's/.*Template created with ID: \([^,]*\).*/\1/') - BUILD_ID=$(grep -A 1 "Build ID:" build_output.log | tail -n 1 | xargs) - echo "Captured Template ID: $TEMPLATE_ID" - echo "Captured Build ID: $BUILD_ID" + TEMPLATE_ID=$(uuidgen) + echo "Building template with Template ID: $TEMPLATE_ID" + python build_ci.py echo "template_id=$TEMPLATE_ID" >> $GITHUB_OUTPUT - echo "build_id=$BUILD_ID" >> $GITHUB_OUTPUT env: E2B_API_KEY: ${{ secrets.E2B_API_KEY }} E2B_DOMAIN: ${{ inputs.E2B_DOMAIN }} - - - name: Output template and build IDs - run: | - echo "Template ID from step output: ${{ steps.build-template.outputs.template_id }}" - echo "Build ID from step output: ${{ steps.build-template.outputs.build_id }}" diff --git a/template/build_ci.py b/template/build_ci.py index 8b2f2ed8..7455836d 100644 --- a/template/build_ci.py +++ b/template/build_ci.py @@ -1,10 +1,10 @@ +import os from e2b import Template, default_build_logger from template import make_template -from uuid import uuid4 Template.build( make_template(set_user_workdir=True), - alias="code-interpreter-ci-" + str(uuid4()), + alias=os.environ["E2B_TESTS_TEMPLATE"], cpu_count=2, memory_mb=2048, on_build_logs=default_build_logger(), From 29d10b696dd7d67f4e6bb88b76e02839b0177d5b Mon Sep 17 00:00:00 2001 From: Mish <10400064+mishushakov@users.noreply.github.com> Date: Tue, 4 Nov 2025 15:29:00 +0100 Subject: [PATCH 21/31] updated workflow --- .github/workflows/build_test_template.yml | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build_test_template.yml b/.github/workflows/build_test_template.yml index 0aadfce7..b43a4d2f 100644 --- a/.github/workflows/build_test_template.yml +++ b/.github/workflows/build_test_template.yml @@ -46,14 +46,18 @@ jobs: working-directory: ./template run: pip install -r requirements-dev.txt + - name: Generate Template ID + id: generate-template-id + run: | + E2B_TESTS_TEMPLATE=$(uuidgen) + echo "template_id=$E2B_TESTS_TEMPLATE" >> $GITHUB_OUTPUT + - name: Build E2B template id: build-template working-directory: ./template run: | - TEMPLATE_ID=$(uuidgen) - echo "Building template with Template ID: $TEMPLATE_ID" python build_ci.py - echo "template_id=$TEMPLATE_ID" >> $GITHUB_OUTPUT env: E2B_API_KEY: ${{ secrets.E2B_API_KEY }} E2B_DOMAIN: ${{ inputs.E2B_DOMAIN }} + E2B_TESTS_TEMPLATE: ${{ steps.generate-template-id.outputs.template_id }} From 131da7cffe19902d51386740952548c458c7a67f Mon Sep 17 00:00:00 2001 From: Mish <10400064+mishushakov@users.noreply.github.com> Date: Tue, 4 Nov 2025 15:31:22 +0100 Subject: [PATCH 22/31] updated generated template alias for clarity --- .github/workflows/build_test_template.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build_test_template.yml b/.github/workflows/build_test_template.yml index b43a4d2f..35193050 100644 --- a/.github/workflows/build_test_template.yml +++ b/.github/workflows/build_test_template.yml @@ -49,7 +49,7 @@ jobs: - name: Generate Template ID id: generate-template-id run: | - E2B_TESTS_TEMPLATE=$(uuidgen) + E2B_TESTS_TEMPLATE=e2b-code-interpreter-ci-$(uuidgen) echo "template_id=$E2B_TESTS_TEMPLATE" >> $GITHUB_OUTPUT - name: Build E2B template From 9e78917628fef57cea088df1200a4e2848557947 Mon Sep 17 00:00:00 2001 From: Mish <10400064+mishushakov@users.noreply.github.com> Date: Tue, 4 Nov 2025 15:31:55 +0100 Subject: [PATCH 23/31] updated output --- .github/workflows/build_test_template.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build_test_template.yml b/.github/workflows/build_test_template.yml index 35193050..434bde32 100644 --- a/.github/workflows/build_test_template.yml +++ b/.github/workflows/build_test_template.yml @@ -50,6 +50,7 @@ jobs: id: generate-template-id run: | E2B_TESTS_TEMPLATE=e2b-code-interpreter-ci-$(uuidgen) + echo "Generated Template ID: $E2B_TESTS_TEMPLATE" echo "template_id=$E2B_TESTS_TEMPLATE" >> $GITHUB_OUTPUT - name: Build E2B template From 2bbcf8838065b6f1a5f27375ce4a3afc77a544c8 Mon Sep 17 00:00:00 2001 From: Mish <10400064+mishushakov@users.noreply.github.com> Date: Tue, 4 Nov 2025 15:36:45 +0100 Subject: [PATCH 24/31] removed unused build_id output --- .github/workflows/build_test_template.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/build_test_template.yml b/.github/workflows/build_test_template.yml index 434bde32..f7fe4221 100644 --- a/.github/workflows/build_test_template.yml +++ b/.github/workflows/build_test_template.yml @@ -13,9 +13,6 @@ on: template_id: description: "The ID of the built template" value: ${{ jobs.build.outputs.template_id }} - build_id: - description: "The ID of the build" - value: ${{ jobs.build.outputs.build_id }} permissions: contents: read From 36529ce6c853e2421db405896f2dcdc5cb7a78b7 Mon Sep 17 00:00:00 2001 From: Mish <10400064+mishushakov@users.noreply.github.com> Date: Tue, 4 Nov 2025 16:42:40 +0100 Subject: [PATCH 25/31] corrected outputs --- .github/workflows/build_test_template.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/build_test_template.yml b/.github/workflows/build_test_template.yml index f7fe4221..3f39bc41 100644 --- a/.github/workflows/build_test_template.yml +++ b/.github/workflows/build_test_template.yml @@ -22,8 +22,7 @@ jobs: name: Build E2B Template runs-on: ubuntu-latest outputs: - template_id: ${{ steps.build-template.outputs.template_id }} - build_id: ${{ steps.build-template.outputs.build_id }} + template_id: ${{ steps.generate-template-id.outputs.template_id }} steps: - name: Checkout repository uses: actions/checkout@v4 From 13de536546cf53be9d1337a124a8236fac5e2af0 Mon Sep 17 00:00:00 2001 From: Mish <10400064+mishushakov@users.noreply.github.com> Date: Wed, 5 Nov 2025 11:17:49 +0100 Subject: [PATCH 26/31] pin r, java, deno versions --- template/template.py | 38 ++++++++++++++++++++++++++------------ 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/template/template.py b/template/template.py index d20798ec..62de0a76 100644 --- a/template/template.py +++ b/template/template.py @@ -19,8 +19,11 @@ def make_template( "JUPYTER_CONFIG_PATH": ".jupyter", "IPYTHON_CONFIG_PATH": ".ipython", "SERVER_PATH": ".server", - "JAVA_HOME": "/opt/java/openjdk", + "JAVA_VERSION": "21", + "JAVA_HOME": "/usr/lib/jvm/java-${JAVA_VERSION}-openjdk-amd64", "DENO_INSTALL": "/opt/deno", + "DENO_VERSION": "v2.4.0", + "R_VERSION": "4.5.*", } ) .apt_install( @@ -32,6 +35,10 @@ def make_template( "jq", "sudo", "fonts-noto-cjk", + "dirmngr", + "gnupg", + "apt-transport-https", + "ca-certificates", ] ) .run_cmd("curl -fsSL https://deb.nodesource.com/setup_20.x | bash -") @@ -45,11 +52,21 @@ def make_template( # Install R Kernel if requested if "r" in kernels: - template = template.apt_install("r-base").run_cmd( - [ - "R -e \"install.packages('IRkernel', repos='https://cloud.r-project.org')\"", - "R -e \"IRkernel::installspec(user = FALSE, name = 'r', displayname = 'R')\"", - ] + template = ( + template.run_cmd( + [ + "sudo gpg --keyserver keyserver.ubuntu.com --recv-key 95C0FAF38DB3CCAD0C080A7BDC78B2DDEABC47B7", + "sudo gpg --armor --export 95C0FAF38DB3CCAD0C080A7BDC78B2DDEABC47B7 | sudo tee /etc/apt/trusted.gpg.d/cran_debian_key.asc", + 'echo "deb https://cloud.r-project.org/bin/linux/debian trixie-cran40/" | sudo tee /etc/apt/sources.list.d/cran.list', + ] + ) + .apt_install("r-base=${R_VERSION} r-base-dev") + .run_cmd( + [ + "R -e \"install.packages('IRkernel', repos='https://cloud.r-project.org')\"", + "R -e \"IRkernel::installspec(user = FALSE, name = 'r', displayname = 'R')\"", + ] + ) ) # Install JavaScript Kernel if requested @@ -63,14 +80,11 @@ def make_template( if "deno" in kernels: template = template.run_cmd( [ - "curl -fsSL https://deno.land/x/install/install.sh | sh", + "curl -fsSL https://deno.land/install.sh | sh -s ${DENO_VERSION}", "PATH=$DENO_INSTALL/bin:$PATH", "deno jupyter --unstable --install", ] - ) - template = template.copy( - "deno.json", ".local/share/jupyter/kernels/deno/kernel.json" - ) + ).copy("deno.json", ".local/share/jupyter/kernels/deno/kernel.json") # Install Bash Kernel if requested if "bash" in kernels: @@ -80,9 +94,9 @@ def make_template( # Install Java and Java Kernel if requested if "java" in kernels: - template = template.apt_install("default-jdk") template = template.run_cmd( [ + "apt-get install -y --no-install-recommends openjdk-${JAVA_VERSION}-jdk", "wget https://github.com/SpencerPark/IJava/releases/download/v1.3.0/ijava-1.3.0.zip", "unzip ijava-1.3.0.zip", "python install.py --sys-prefix", From 33a5c22d523d6557fd30a8f58ecd7db8f57a81cb Mon Sep 17 00:00:00 2001 From: Mish <10400064+mishushakov@users.noreply.github.com> Date: Wed, 5 Nov 2025 16:17:15 +0100 Subject: [PATCH 27/31] install java from source, keep java 11 --- python/tests/sync/test_default_kernels.py | 2 +- template/template.py | 15 ++++++++------- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/python/tests/sync/test_default_kernels.py b/python/tests/sync/test_default_kernels.py index eba627d8..8dfd4bf9 100644 --- a/python/tests/sync/test_default_kernels.py +++ b/python/tests/sync/test_default_kernels.py @@ -17,7 +17,7 @@ def test_r_kernel(sandbox: Sandbox): @pytest.mark.skip_debug() def test_java_kernel(sandbox: Sandbox): execution = sandbox.run_code('System.out.println("Hello, World!")', language="java") - assert execution.logs.stdout[0] == "Hello, World!\n" + assert execution.logs.stdout[0] == "Hello, World!" def test_js_esm_imports(sandbox: Sandbox): diff --git a/template/template.py b/template/template.py index 62de0a76..8c8df6a5 100644 --- a/template/template.py +++ b/template/template.py @@ -19,8 +19,9 @@ def make_template( "JUPYTER_CONFIG_PATH": ".jupyter", "IPYTHON_CONFIG_PATH": ".ipython", "SERVER_PATH": ".server", - "JAVA_VERSION": "21", + "JAVA_VERSION": "11", "JAVA_HOME": "/usr/lib/jvm/java-${JAVA_VERSION}-openjdk-amd64", + "IJAVA_VERSION": "1.3.0", "DENO_INSTALL": "/opt/deno", "DENO_VERSION": "v2.4.0", "R_VERSION": "4.5.*", @@ -35,9 +36,6 @@ def make_template( "jq", "sudo", "fonts-noto-cjk", - "dirmngr", - "gnupg", - "apt-transport-https", "ca-certificates", ] ) @@ -96,9 +94,12 @@ def make_template( if "java" in kernels: template = template.run_cmd( [ - "apt-get install -y --no-install-recommends openjdk-${JAVA_VERSION}-jdk", - "wget https://github.com/SpencerPark/IJava/releases/download/v1.3.0/ijava-1.3.0.zip", - "unzip ijava-1.3.0.zip", + "mkdir -p /usr/lib/jvm", + "curl -fsSL https://download.java.net/java/ga/jdk${JAVA_VERSION}/openjdk-${JAVA_VERSION}_linux-x64_bin.tar.gz | tar -xz -C /usr/lib/jvm", + "update-alternatives --install /usr/bin/java java /usr/lib/jvm/jdk-${JAVA_VERSION}/bin/java 1", + "update-alternatives --install /usr/bin/javac javac /usr/lib/jvm/jdk-${JAVA_VERSION}/bin/javac 1", + "wget https://github.com/SpencerPark/IJava/releases/download/v${IJAVA_VERSION}/ijava-${IJAVA_VERSION}.zip", + "unzip ijava-${IJAVA_VERSION}.zip", "python install.py --sys-prefix", ] ) From 7e8d74ad0bf6d1d3651d39fd9307e8076ab906eb Mon Sep 17 00:00:00 2001 From: Mish <10400064+mishushakov@users.noreply.github.com> Date: Wed, 5 Nov 2025 16:20:32 +0100 Subject: [PATCH 28/31] make sure python & js kernels always installed --- template/template.py | 1 + 1 file changed, 1 insertion(+) diff --git a/template/template.py b/template/template.py index 8c8df6a5..0114f5a0 100644 --- a/template/template.py +++ b/template/template.py @@ -5,6 +5,7 @@ def make_template( kernels: list[str] = ["python", "r", "javascript", "deno", "bash", "java"], set_user_workdir: bool = False, ): + kernels = ["python", "javascript"] + kernels # Start with base template template = ( Template() From 101ab77345bfac2e659bb9e0f71486d2f4295839 Mon Sep 17 00:00:00 2001 From: Mish <10400064+mishushakov@users.noreply.github.com> Date: Wed, 5 Nov 2025 16:24:09 +0100 Subject: [PATCH 29/31] fixes java home --- template/template.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/template/template.py b/template/template.py index 0114f5a0..d0e3951b 100644 --- a/template/template.py +++ b/template/template.py @@ -21,7 +21,7 @@ def make_template( "IPYTHON_CONFIG_PATH": ".ipython", "SERVER_PATH": ".server", "JAVA_VERSION": "11", - "JAVA_HOME": "/usr/lib/jvm/java-${JAVA_VERSION}-openjdk-amd64", + "JAVA_HOME": "/usr/lib/jvm/jdk-${JAVA_VERSION}", "IJAVA_VERSION": "1.3.0", "DENO_INSTALL": "/opt/deno", "DENO_VERSION": "v2.4.0", From e97ad192952401fb2cf8bd508dd67054bee6dcef Mon Sep 17 00:00:00 2001 From: Mish <10400064+mishushakov@users.noreply.github.com> Date: Thu, 6 Nov 2025 11:31:11 +0100 Subject: [PATCH 30/31] make kernels set --- template/template.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/template/template.py b/template/template.py index d0e3951b..36234506 100644 --- a/template/template.py +++ b/template/template.py @@ -5,7 +5,7 @@ def make_template( kernels: list[str] = ["python", "r", "javascript", "deno", "bash", "java"], set_user_workdir: bool = False, ): - kernels = ["python", "javascript"] + kernels + enabled_kernels = set(["python", "javascript"] + kernels) # Start with base template template = ( Template() @@ -46,11 +46,11 @@ def make_template( .pip_install("--no-cache-dir -r requirements.txt") ) - if "python" in kernels: + if "python" in enabled_kernels: template = template.run_cmd("ipython kernel install --name 'python3' --user") # Install R Kernel if requested - if "r" in kernels: + if "r" in enabled_kernels: template = ( template.run_cmd( [ @@ -69,14 +69,14 @@ def make_template( ) # Install JavaScript Kernel if requested - if "javascript" in kernels: + if "javascript" in enabled_kernels: template = template.npm_install( "--unsafe-perm git+https://github.com/e2b-dev/ijavascript.git", g=True, ).run_cmd("ijsinstall --install=global") # Install Deno Kernel if requested - if "deno" in kernels: + if "deno" in enabled_kernels: template = template.run_cmd( [ "curl -fsSL https://deno.land/install.sh | sh -s ${DENO_VERSION}", @@ -86,13 +86,13 @@ def make_template( ).copy("deno.json", ".local/share/jupyter/kernels/deno/kernel.json") # Install Bash Kernel if requested - if "bash" in kernels: + if "bash" in enabled_kernels: template = template.pip_install("bash_kernel").run_cmd( "python -m bash_kernel.install" ) # Install Java and Java Kernel if requested - if "java" in kernels: + if "java" in enabled_kernels: template = template.run_cmd( [ "mkdir -p /usr/lib/jvm", From ea461503b3d1ada7ffe4a210db84aa2d04c7cb24 Mon Sep 17 00:00:00 2001 From: Mish Ushakov <10400064+mishushakov@users.noreply.github.com> Date: Thu, 6 Nov 2025 11:36:05 +0100 Subject: [PATCH 31/31] fixed typo --- template/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/template/README.md b/template/README.md index d74d92c3..19039fc4 100644 --- a/template/README.md +++ b/template/README.md @@ -1,6 +1,6 @@ # Using custom sandbox with Code Interpreter SDK -If you want to customize the Code Interprerter sandbox (e.g.: add a preinstalled package) you can do that by creating a [custom sandbox template](https://e2b.dev/docs/template/quickstart). +If you want to customize the Code Interpreter sandbox (e.g.: add a preinstalled package) you can do that by creating a [custom sandbox template](https://e2b.dev/docs/template/quickstart). ## Step-by-step guide