Skip to content

Commit e8576a5

Browse files
authored
Add .fromBunImage and .bunInstall to the SDK (#993)
<!-- CURSOR_SUMMARY --> > [!NOTE] > Adds `fromBunImage` and `bunInstall` to JS/Python SDKs, and extends `npmInstall` with a `dev` flag, with tests across sync/async suites. > > - **Template APIs**: > - Add `fromBunImage` to start from `oven/bun:<variant>` in `packages/js-sdk/src/template/index.ts` and `packages/python-sdk/e2b/template/main.py`. > - Add `bunInstall(packages, { g, dev })` in both SDKs. > - Extend `npmInstall` to accept `{ dev: boolean }` in both SDKs. > - **Types/Docs**: > - Update `packages/js-sdk/src/template/types.ts` to declare `fromBunImage`, `bunInstall`, and `npmInstall` `dev` option. > - **Tests**: > - Add tests for `fromBunImage` and `bunInstall` and update `npmInstall` (global/dev) in JS (`packages/js-sdk/tests/...`) and Python sync/async (`packages/python-sdk/tests/...`). > - **Changeset**: > - Minor releases for `@e2b/python-sdk` and `e2b` (`.changeset/silly-lions-buy.md`). > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit b6461d0. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY -->
1 parent 70487f0 commit e8576a5

File tree

13 files changed

+268
-9
lines changed

13 files changed

+268
-9
lines changed

.changeset/silly-lions-buy.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
'@e2b/python-sdk': minor
3+
'e2b': minor
4+
---
5+
6+
add from bun image and bun install

packages/js-sdk/src/template/index.ts

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,10 @@ export class TemplateBase
138138
return this.fromImage(`node:${variant}`)
139139
}
140140

141+
fromBunImage(variant: string = 'latest'): TemplateBuilder {
142+
return this.fromImage(`oven/bun:${variant}`)
143+
}
144+
141145
fromBaseImage(): TemplateBuilder {
142146
return this.fromImage(this.defaultBaseImage)
143147
}
@@ -448,7 +452,7 @@ export class TemplateBase
448452

449453
npmInstall(
450454
packages?: string | string[],
451-
options?: { g?: boolean }
455+
options?: { g?: boolean; dev?: boolean }
452456
): TemplateBuilder {
453457
const args = ['npm', 'install']
454458
const packageList = packages
@@ -459,6 +463,36 @@ export class TemplateBase
459463
if (options?.g) {
460464
args.push('-g')
461465
}
466+
if (options?.dev) {
467+
args.push('--save-dev')
468+
}
469+
if (packageList) {
470+
args.push(...packageList)
471+
}
472+
473+
return this.runInNewStackTraceContext(() =>
474+
this.runCmd(args.join(' '), {
475+
user: options?.g ? 'root' : undefined,
476+
})
477+
)
478+
}
479+
480+
bunInstall(
481+
packages?: string | string[],
482+
options?: { g?: boolean; dev?: boolean }
483+
): TemplateBuilder {
484+
const args = ['bun', 'install']
485+
const packageList = packages
486+
? Array.isArray(packages)
487+
? packages
488+
: [packages]
489+
: undefined
490+
if (options?.g) {
491+
args.push('-g')
492+
}
493+
if (options?.dev) {
494+
args.push('--dev')
495+
}
462496
if (packageList) {
463497
args.push(...packageList)
464498
}

packages/js-sdk/src/template/types.ts

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,17 @@ export interface TemplateFromImage {
150150
*/
151151
fromNodeImage(variant?: string): TemplateBuilder
152152

153+
/**
154+
* Start from a Bun-based Docker image.
155+
* @param variant Bun variant (default: 'latest')
156+
*
157+
* @example
158+
* ```ts
159+
* Template().fromBunImage('1.3')
160+
* ```
161+
*/
162+
fromBunImage(variant?: string): TemplateBuilder
163+
153164
/**
154165
* Start from E2B's default base image (e2bdev/base:latest).
155166
*
@@ -454,13 +465,32 @@ export interface TemplateBuilder {
454465
* ```ts
455466
* template.npmInstall('express')
456467
* template.npmInstall(['lodash', 'axios'])
457-
* template.npmInstall('typescript', { g: true })
468+
* template.npmInstall('tsx', { g: true })
469+
* template.npmInstall('typescript', { dev: true })
458470
* template.npmInstall() // Installs from package.json
459471
* ```
460472
*/
461473
npmInstall(
462474
packages?: string | string[],
463-
options?: { g?: boolean }
475+
options?: { g?: boolean; dev?: boolean }
476+
): TemplateBuilder
477+
/**
478+
* Install Bun packages using bun.
479+
* @param packages Package name(s) or undefined for package.json
480+
* @param options Install options
481+
*
482+
* @example
483+
* ```ts
484+
* template.bunInstall('express')
485+
* template.bunInstall(['lodash', 'axios'])
486+
* template.bunInstall('tsx', { g: true })
487+
* template.bunInstall('typescript', { dev: true })
488+
* template.bunInstall() // Installs from package.json
489+
* ```
490+
*/
491+
bunInstall(
492+
packages?: string | string[],
493+
options?: { g?: boolean; dev?: boolean }
464494
): TemplateBuilder
465495

466496
/**
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { Template } from '../../../src'
2+
import { buildTemplateTest } from '../../setup'
3+
4+
buildTemplateTest('bun install', async ({ buildTemplate }) => {
5+
const template = Template()
6+
.fromBunImage('1.3')
7+
.bunInstall(['lodash', 'axios'])
8+
9+
await buildTemplate(template)
10+
})
11+
12+
buildTemplateTest('bun install global', async ({ buildTemplate }) => {
13+
const template = Template()
14+
.fromBunImage('1.3')
15+
.bunInstall(['tsx'], { g: true })
16+
17+
await buildTemplate(template)
18+
})
19+
20+
buildTemplateTest('bun install dev', async ({ buildTemplate }) => {
21+
const template = Template()
22+
.fromBunImage('1.3')
23+
.bunInstall(['typescript'], { dev: true })
24+
25+
await buildTemplate(template)
26+
})

packages/js-sdk/tests/template/methods/fromMethods.test.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,16 @@ buildTemplateTest('fromNodeImage with variant', async ({ buildTemplate }) => {
6464
await buildTemplate(template)
6565
})
6666

67+
buildTemplateTest('fromBunImage', async ({ buildTemplate }) => {
68+
const template = Template().fromBunImage()
69+
await buildTemplate(template)
70+
})
71+
72+
buildTemplateTest('fromBunImage with variant', async ({ buildTemplate }) => {
73+
const template = Template().fromBunImage('1.3')
74+
await buildTemplate(template)
75+
})
76+
6777
buildTemplateTest('fromImage', async ({ buildTemplate }) => {
6878
const template = Template().fromImage('ubuntu:22.04')
6979
await buildTemplate(template)

packages/js-sdk/tests/template/methods/npmInstall.test.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,25 @@ import { Template } from '../../../src'
22
import { buildTemplateTest } from '../../setup'
33

44
buildTemplateTest('npm install', async ({ buildTemplate }) => {
5-
const template = Template().fromNodeImage('24').npmInstall(['lodash', 'ms'])
5+
const template = Template()
6+
.fromNodeImage('24')
7+
.npmInstall(['lodash', 'axios'])
68

79
await buildTemplate(template)
810
})
911

1012
buildTemplateTest('npm install global', async ({ buildTemplate }) => {
1113
const template = Template()
1214
.fromNodeImage('24')
13-
.npmInstall(['lodash', 'ms'], { g: true })
15+
.npmInstall(['tsx'], { g: true })
16+
17+
await buildTemplate(template)
18+
})
19+
20+
buildTemplateTest('npm install dev', async ({ buildTemplate }) => {
21+
const template = Template()
22+
.fromNodeImage('24')
23+
.npmInstall(['typescript'], { dev: true })
1424

1525
await buildTemplate(template)
1626
})

packages/python-sdk/e2b/template/main.py

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -365,12 +365,14 @@ def npm_install(
365365
self,
366366
packages: Optional[Union[str, List[str]]] = None,
367367
g: Optional[bool] = False,
368+
dev: Optional[bool] = False,
368369
) -> "TemplateBuilder":
369370
"""
370371
Install Node.js packages using npm.
371372
372373
:param packages: Package name(s) to install. If None, installs from package.json
373374
:param g: Install packages globally
375+
:param dev: Install packages as dev dependencies
374376
375377
:return: `TemplateBuilder` class
376378
@@ -388,6 +390,47 @@ def npm_install(
388390
args = ["npm", "install"]
389391
if g:
390392
args.append("-g")
393+
if dev:
394+
args.append("--save-dev")
395+
if packages:
396+
args.extend(packages)
397+
398+
return self._template._run_in_new_stack_trace_context(
399+
lambda: self.run_cmd(" ".join(args), user="root" if g else None)
400+
)
401+
402+
def bun_install(
403+
self,
404+
packages: Optional[Union[str, List[str]]] = None,
405+
g: Optional[bool] = False,
406+
dev: Optional[bool] = False,
407+
) -> "TemplateBuilder":
408+
"""
409+
Install Bun packages using bun.
410+
411+
:param packages: Package name(s) to install. If None, installs from package.json
412+
:param g: Install packages globally
413+
:param dev: Install packages as dev dependencies
414+
415+
:return: `TemplateBuilder` class
416+
417+
Example
418+
```python
419+
template.bun_install('express')
420+
template.bun_install(['lodash', 'axios'])
421+
template.bun_install('tsx', g=True)
422+
template.bun_install('typescript', dev=True)
423+
template.bun_install() // Installs from package.json
424+
```
425+
"""
426+
if isinstance(packages, str):
427+
packages = [packages]
428+
429+
args = ["bun", "install"]
430+
if g:
431+
args.append("-g")
432+
if dev:
433+
args.append("--dev")
391434
if packages:
392435
args.extend(packages)
393436

@@ -924,6 +967,18 @@ def from_node_image(self, variant: str = "lts") -> TemplateBuilder:
924967
lambda: self.from_image(f"node:{variant}")
925968
)
926969

970+
def from_bun_image(self, variant: str = "latest") -> TemplateBuilder:
971+
"""
972+
Start template from a Bun base image.
973+
974+
:param variant: Bun image variant (default: 'latest')
975+
976+
:return: `TemplateBuilder` class
977+
"""
978+
return self._run_in_new_stack_trace_context(
979+
lambda: self.from_image(f"oven/bun:{variant}")
980+
)
981+
927982
def from_base_image(self) -> TemplateBuilder:
928983
"""
929984
Start template from the E2B base image (e2bdev/base:latest).
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import pytest
2+
3+
from e2b import AsyncTemplate
4+
5+
6+
@pytest.mark.skip_debug()
7+
async def test_bun_install(async_build):
8+
template = AsyncTemplate().from_bun_image("1.3").bun_install(["lodash", "axios"])
9+
10+
await async_build(template)
11+
12+
13+
@pytest.mark.skip_debug()
14+
async def test_bun_install_global(async_build):
15+
template = AsyncTemplate().from_bun_image("1.3").bun_install(["tsx"], g=True)
16+
17+
await async_build(template)
18+
19+
20+
@pytest.mark.skip_debug()
21+
async def test_bun_install_dev(async_build):
22+
template = (
23+
AsyncTemplate().from_bun_image("1.3").bun_install(["typescript"], dev=True)
24+
)
25+
26+
await async_build(template)

packages/python-sdk/tests/async/template_async/methods/test_from_methods.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,18 @@ async def test_from_node_image(async_build):
7070
await async_build(template)
7171

7272

73+
@pytest.mark.skip_debug()
74+
async def test_from_bun_image(async_build):
75+
template = AsyncTemplate().from_bun_image()
76+
await async_build(template)
77+
78+
79+
@pytest.mark.skip_debug()
80+
async def test_from_bun_image_with_variant(async_build):
81+
template = AsyncTemplate().from_bun_image("1.3")
82+
await async_build(template)
83+
84+
7385
@pytest.mark.skip_debug()
7486
async def test_from_node_image_with_variant(async_build):
7587
template = AsyncTemplate().from_node_image("24")

packages/python-sdk/tests/async/template_async/methods/test_npm_install.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,22 @@
55

66
@pytest.mark.skip_debug()
77
async def test_npm_install(async_build):
8-
template = AsyncTemplate().from_node_image("24").npm_install(["lodash", "ms"])
8+
template = AsyncTemplate().from_node_image("24").npm_install(["lodash", "axios"])
99

1010
await async_build(template)
1111

1212

1313
@pytest.mark.skip_debug()
1414
async def test_npm_install_global(async_build):
15+
template = AsyncTemplate().from_node_image("24").npm_install(["tsx"], g=True)
16+
17+
await async_build(template)
18+
19+
20+
@pytest.mark.skip_debug()
21+
async def test_npm_install_dev(async_build):
1522
template = (
16-
AsyncTemplate().from_node_image("24").npm_install(["lodash", "ms"], g=True)
23+
AsyncTemplate().from_node_image("24").npm_install(["typescript"], dev=True)
1724
)
1825

1926
await async_build(template)

0 commit comments

Comments
 (0)