Skip to content
Open
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
6 changes: 6 additions & 0 deletions .changeset/true-hands-type.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@e2b/python-sdk': minor
'e2b': minor
---

keep Docker WORKDIR and USER if specified
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,5 @@
.run_cmd("pip install --upgrade pip && pip install -r requirements.txt")
.copy("app.py", ".")
.set_user("appuser")
.set_user("user")
.set_workdir("/home/user")
.set_start_cmd("sudo gunicorn --bind 0.0.0.0:8000 app:application", "sleep 20")
)
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,5 @@
.run_cmd("pip install --upgrade pip && pip install -r requirements.txt")
.copy("app.py", ".")
.set_user("appuser")
.set_user("user")
.set_workdir("/home/user")
.set_start_cmd("sudo gunicorn --bind 0.0.0.0:8000 app:application", "sleep 20")
)
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,4 @@ export const template = Template()
.runCmd('pip install --upgrade pip && pip install -r requirements.txt')
.copy('app.py', '.')
.setUser('appuser')
.setUser('user')
.setWorkdir('/home/user')
.setStartCmd('sudo gunicorn --bind 0.0.0.0:8000 app:application', 'sleep 20')
.setStartCmd('sudo gunicorn --bind 0.0.0.0:8000 app:application', 'sleep 20')
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,5 @@
.set_workdir("/app")
.copy("server.js", ".")
.set_user("user")
.set_workdir("/home/user")
.set_start_cmd("sudo node server.js", "curl -f http://localhost:3000/health || exit 1")
)
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,5 @@
.set_workdir("/app")
.copy("server.js", ".")
.set_user("user")
.set_workdir("/home/user")
.set_start_cmd("sudo node server.js", "curl -f http://localhost:3000/health || exit 1")
)
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,4 @@ export const template = Template()
.setWorkdir('/app')
.copy('server.js', '.')
.setUser('user')
.setWorkdir('/home/user')
.setStartCmd('sudo node server.js', 'curl -f http://localhost:3000/health || exit 1')
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,4 @@
})
.set_workdir("/app")
.set_user("user")
.set_workdir("/home/user")
)
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,4 @@
})
.set_workdir("/app")
.set_user("user")
.set_workdir("/home/user")
)
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,4 @@ export const template = Template()
'SINGLE_VAR': 'single_value',
})
.setWorkdir('/app')
.setUser('user')
.setWorkdir('/home/user')
.setUser('user')
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,5 @@
"PYTHONUNBUFFERED": "1",
})
.set_user("user")
.set_workdir("/home/user")
.set_start_cmd("sudo node server.js", "sleep 20")
)
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,5 @@
"PYTHONUNBUFFERED": "1",
})
.set_user("user")
.set_workdir("/home/user")
.set_start_cmd("sudo node server.js", "sleep 20")
)
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,4 @@ export const template = Template()
'PYTHONUNBUFFERED': '1',
})
.setUser('user')
.setWorkdir('/home/user')
.setStartCmd('sudo node server.js', 'sleep 20')
12 changes: 10 additions & 2 deletions packages/js-sdk/src/template/dockerfileParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,8 @@ export function parseDockerfile(
const fromInstruction = fromInstructions[0]
const argumentsData = fromInstruction.getArguments()
let baseImage = 'e2bdev/base' // default fallback
let userChanged = false
let workdirChanged = false
if (argumentsData && argumentsData.length > 0) {
baseImage = argumentsData[0].getValue()
}
Expand Down Expand Up @@ -114,10 +116,12 @@ export function parseDockerfile(

case 'WORKDIR':
handleWorkdirInstruction(instruction, templateBuilder)
workdirChanged = true
break

case 'USER':
handleUserInstruction(instruction, templateBuilder)
userChanged = true
break

case 'ENV':
Expand Down Expand Up @@ -145,8 +149,12 @@ export function parseDockerfile(
}

// Set the user and workdir to the E2B defaults
templateBuilder.setUser('user')
templateBuilder.setWorkdir('/home/user')
if (!userChanged) {
templateBuilder.setUser('user')
}
if (!workdirChanged) {
templateBuilder.setWorkdir('/home/user')
}

return {
baseImage,
Expand Down
55 changes: 54 additions & 1 deletion packages/js-sdk/tests/template/methods/fromMethods.test.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { buildTemplateTest } from '../../setup'
import { Template } from '../../../src'
import { InstructionType } from '../../../src/template/types'
import path from 'node:path'
import fs from 'node:fs'
import { afterAll, beforeAll } from 'vitest'
import { afterAll, beforeAll, assert } from 'vitest'

const fileContextPath = path.join(__dirname, 'dockerfile-context')

Expand Down Expand Up @@ -93,3 +94,55 @@ RUN npm install`
const template = Template({ fileContextPath }).fromDockerfile(dockerfile)
await buildTemplate(template, { skipCache: true })
})

buildTemplateTest('fromDockerfile with default user and workdir', () => {
const dockerfile = 'FROM node:24'
const template = Template({ fileContextPath }).fromDockerfile(dockerfile)

assert.equal(
// @ts-expect-error - instructions is not a property of TemplateBuilder
template.instructions[template.instructions.length - 2].type,
InstructionType.USER
)
assert.equal(
// @ts-expect-error - instructions is not a property of TemplateBuilder
template.instructions[template.instructions.length - 2].args[0],
'user'
)
assert.equal(
// @ts-expect-error - instructions is not a property of TemplateBuilder
template.instructions[template.instructions.length - 1].type,
InstructionType.WORKDIR
)
assert.equal(
// @ts-expect-error - instructions is not a property of TemplateBuilder
template.instructions[template.instructions.length - 1].args[0],
'/home/user'
)
})

buildTemplateTest('fromDockerfile with custom user and workdir', () => {
const dockerfile = 'FROM node:24\nUSER mish\nWORKDIR /home/mish'
const template = Template({ fileContextPath }).fromDockerfile(dockerfile)

assert.equal(
// @ts-expect-error - instructions is not a property of TemplateBuilder
template.instructions[template.instructions.length - 2].type,
InstructionType.USER
)
assert.equal(
// @ts-expect-error - instructions is not a property of TemplateBuilder
template.instructions[template.instructions.length - 2].args[0],
'mish'
)
assert.equal(
// @ts-expect-error - instructions is not a property of TemplateBuilder
template.instructions[template.instructions.length - 1].type,
InstructionType.WORKDIR
)
assert.equal(
// @ts-expect-error - instructions is not a property of TemplateBuilder
template.instructions[template.instructions.length - 1].args[0],
'/home/mish'
)
})
11 changes: 9 additions & 2 deletions packages/python-sdk/e2b/template/dockerfile_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,9 @@ def parse_dockerfile(
if " as " in base_image.lower():
base_image = base_image.split(" as ")[0].strip()

user_changed = False
workdir_changed = False

# Set the user and workdir to the Docker defaults
template_builder.set_user("root")
template_builder.set_workdir("/")
Expand All @@ -119,8 +122,10 @@ def parse_dockerfile(
_handle_copy_instruction(value, template_builder)
elif instruction == "WORKDIR":
_handle_workdir_instruction(value, template_builder)
workdir_changed = True
elif instruction == "USER":
_handle_user_instruction(value, template_builder)
user_changed = True
elif instruction in ["ENV", "ARG"]:
_handle_env_instruction(value, instruction, template_builder)
elif instruction in ["CMD", "ENTRYPOINT"]:
Expand All @@ -130,8 +135,10 @@ def parse_dockerfile(
continue

# Set the user and workdir to the E2B defaults
template_builder.set_user("user")
template_builder.set_workdir("/home/user")
if not user_changed:
template_builder.set_user("user")
if not workdir_changed:
template_builder.set_workdir("/home/user")

return base_image

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import pytest

from e2b import AsyncTemplate
from e2b.template.types import InstructionType


@pytest.fixture(scope="module")
Expand Down Expand Up @@ -111,3 +112,31 @@ async def test_from_dockerfile(async_build, setup_dockerfile_context):
file_context_path=setup_dockerfile_context
).from_dockerfile(dockerfile)
await async_build(template, skip_cache=True)


@pytest.mark.skip_debug()
async def test_from_dockerfile_with_default_user_and_workdir(setup_dockerfile_context):
dockerfile = "FROM node:24"

template = AsyncTemplate(
file_context_path=setup_dockerfile_context
).from_dockerfile(dockerfile)

assert template._template._instructions[-2]["type"] == InstructionType.USER
assert template._template._instructions[-2]["args"][0] == "user"
assert template._template._instructions[-1]["type"] == InstructionType.WORKDIR
assert template._template._instructions[-1]["args"][0] == "/home/user"


@pytest.mark.skip_debug()
async def test_from_dockerfile_with_custom_user_and_workdir(setup_dockerfile_context):
dockerfile = "FROM node:24\nUSER mish\nWORKDIR /home/mish"

template = AsyncTemplate(
file_context_path=setup_dockerfile_context
).from_dockerfile(dockerfile)

assert template._template._instructions[-2]["type"] == InstructionType.USER
assert template._template._instructions[-2]["args"][0] == "mish"
assert template._template._instructions[-1]["type"] == InstructionType.WORKDIR
assert template._template._instructions[-1]["args"][0] == "/home/mish"
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import pytest

from e2b import Template
from e2b.template.types import InstructionType


@pytest.fixture(scope="module")
Expand Down Expand Up @@ -112,3 +113,31 @@ def test_from_dockerfile(build, setup_dockerfile_context):
dockerfile
)
build(template, skip_cache=True)


@pytest.mark.skip_debug()
def test_from_dockerfile_with_default_user_and_workdir(setup_dockerfile_context):
dockerfile = "FROM node:24"

template = Template(file_context_path=setup_dockerfile_context).from_dockerfile(
dockerfile
)

assert template._template._instructions[-2]["type"] == InstructionType.USER
assert template._template._instructions[-2]["args"][0] == "user"
assert template._template._instructions[-1]["type"] == InstructionType.WORKDIR
assert template._template._instructions[-1]["args"][0] == "/home/user"


@pytest.mark.skip_debug()
def test_from_dockerfile_with_custom_user_and_workdir(setup_dockerfile_context):
dockerfile = "FROM node:24\nUSER mish\nWORKDIR /home/mish"

template = Template(file_context_path=setup_dockerfile_context).from_dockerfile(
dockerfile
)

assert template._template._instructions[-2]["type"] == InstructionType.USER
assert template._template._instructions[-2]["args"][0] == "mish"
assert template._template._instructions[-1]["type"] == InstructionType.WORKDIR
assert template._template._instructions[-1]["args"][0] == "/home/mish"
Loading