Skip to content

Commit d2b32e9

Browse files
authored
improve(docs): blueprint build with context added to examples (#751)
1 parent fa16c1d commit d2b32e9

File tree

3 files changed

+169
-0
lines changed

3 files changed

+169
-0
lines changed

EXAMPLES.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,40 @@ Runnable examples live in [`examples/`](./examples).
77

88
## Table of Contents
99

10+
- [Blueprint with Build Context](#blueprint-with-build-context)
1011
- [Devbox From Blueprint (Run Command, Shutdown)](#devbox-from-blueprint-lifecycle)
1112
- [MCP Hub + Claude Code + GitHub](#mcp-github-tools)
1213

14+
<a id="blueprint-with-build-context"></a>
15+
## Blueprint with Build Context
16+
17+
**Use case:** Create a blueprint using the object store to provide docker build context files, then verify files are copied into the image. Uses the async SDK.
18+
19+
**Tags:** `blueprint`, `object-store`, `build-context`, `devbox`, `cleanup`, `async`
20+
21+
### Workflow
22+
- Create a temporary directory with sample application files
23+
- Upload the directory to object storage as build context
24+
- Create a blueprint with a Dockerfile that copies the context files
25+
- Create a devbox from the blueprint
26+
- Verify the files were copied into the image
27+
- Shutdown devbox and delete blueprint and storage object
28+
29+
### Prerequisites
30+
- `RUNLOOP_API_KEY`
31+
32+
### Run
33+
```sh
34+
uv run python -m examples.blueprint_with_build_context
35+
```
36+
37+
### Test
38+
```sh
39+
uv run pytest -m smoketest tests/smoketests/examples/
40+
```
41+
42+
**Source:** [`examples/blueprint_with_build_context.py`](./examples/blueprint_with_build_context.py)
43+
1344
<a id="devbox-from-blueprint-lifecycle"></a>
1445
## Devbox From Blueprint (Run Command, Shutdown)
1546

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
#!/usr/bin/env -S uv run python
2+
"""
3+
---
4+
title: Blueprint with Build Context
5+
slug: blueprint-with-build-context
6+
use_case: Create a blueprint using the object store to provide docker build context files, then verify files are copied into the image. Uses the async SDK.
7+
workflow:
8+
- Create a temporary directory with sample application files
9+
- Upload the directory to object storage as build context
10+
- Create a blueprint with a Dockerfile that copies the context files
11+
- Create a devbox from the blueprint
12+
- Verify the files were copied into the image
13+
- Shutdown devbox and delete blueprint and storage object
14+
tags:
15+
- blueprint
16+
- object-store
17+
- build-context
18+
- devbox
19+
- cleanup
20+
- async
21+
prerequisites:
22+
- RUNLOOP_API_KEY
23+
run: uv run python -m examples.blueprint_with_build_context
24+
test: uv run pytest -m smoketest tests/smoketests/examples/
25+
---
26+
"""
27+
28+
from __future__ import annotations
29+
30+
import tempfile
31+
from pathlib import Path
32+
from datetime import timedelta
33+
34+
from runloop_api_client import AsyncRunloopSDK
35+
from runloop_api_client.lib.polling import PollingConfig
36+
37+
from ._harness import run_as_cli, unique_name, wrap_recipe
38+
from .example_types import ExampleCheck, RecipeOutput, RecipeContext
39+
40+
# building can take time: make sure to set a long blueprint build timeout
41+
BLUEPRINT_POLL_TIMEOUT_S = 10 * 60
42+
BLUEPRINT_POLL_MAX_ATTEMPTS = 600
43+
44+
# configure object storage ttl for the build context
45+
BUILD_CONTEXT_TTL = timedelta(hours=1)
46+
47+
48+
async def recipe(ctx: RecipeContext) -> RecipeOutput:
49+
"""Create a blueprint with build context from object storage, then verify files in a devbox."""
50+
cleanup = ctx.cleanup
51+
52+
sdk = AsyncRunloopSDK()
53+
54+
# setup: create a temporary directory with sample application files to use as build context
55+
with tempfile.TemporaryDirectory() as tmp_dir:
56+
tmp_path = Path(tmp_dir)
57+
(tmp_path / "app.py").write_text('print("Hello from app")')
58+
(tmp_path / "config.txt").write_text("key=value")
59+
60+
# upload the build context to object storage
61+
storage_obj = await sdk.storage_object.upload_from_dir(
62+
tmp_path,
63+
name=unique_name("example-build-context"),
64+
ttl=BUILD_CONTEXT_TTL,
65+
)
66+
cleanup.add(f"storage_object:{storage_obj.id}", storage_obj.delete)
67+
68+
# create a blueprint with the build context
69+
blueprint = await sdk.blueprint.create(
70+
name=unique_name("example-blueprint-context"),
71+
dockerfile="FROM ubuntu:22.04\nWORKDIR /app\nCOPY . .",
72+
build_context=storage_obj.as_build_context(),
73+
polling_config=PollingConfig(
74+
timeout_seconds=BLUEPRINT_POLL_TIMEOUT_S,
75+
max_attempts=BLUEPRINT_POLL_MAX_ATTEMPTS,
76+
),
77+
)
78+
cleanup.add(f"blueprint:{blueprint.id}", blueprint.delete)
79+
80+
devbox = await blueprint.create_devbox(
81+
name=unique_name("example-devbox"),
82+
launch_parameters={
83+
"resource_size_request": "X_SMALL",
84+
"keep_alive_time_seconds": 60 * 5,
85+
},
86+
)
87+
cleanup.add(f"devbox:{devbox.id}", devbox.shutdown)
88+
89+
app_result = await devbox.cmd.exec("cat /app/app.py")
90+
app_stdout = await app_result.stdout()
91+
92+
config_result = await devbox.cmd.exec("cat /app/config.txt")
93+
config_stdout = await config_result.stdout()
94+
95+
return RecipeOutput(
96+
resources_created=[
97+
f"storage_object:{storage_obj.id}",
98+
f"blueprint:{blueprint.id}",
99+
f"devbox:{devbox.id}",
100+
],
101+
checks=[
102+
ExampleCheck(
103+
name="app.py exists and readable",
104+
passed=app_result.exit_code == 0,
105+
details=f"exitCode={app_result.exit_code}",
106+
),
107+
ExampleCheck(
108+
name="app.py contains expected content",
109+
passed='print("Hello from app")' in app_stdout,
110+
details=app_stdout.strip(),
111+
),
112+
ExampleCheck(
113+
name="config.txt exists and readable",
114+
passed=config_result.exit_code == 0,
115+
details=f"exitCode={config_result.exit_code}",
116+
),
117+
ExampleCheck(
118+
name="config.txt contains expected content",
119+
passed="key=value" in config_stdout,
120+
details=config_stdout.strip(),
121+
),
122+
],
123+
)
124+
125+
126+
run_blueprint_with_build_context_example = wrap_recipe(recipe)
127+
128+
129+
if __name__ == "__main__":
130+
run_as_cli(run_blueprint_with_build_context_example)

examples/registry.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,19 @@
99

1010
from .example_types import ExampleResult
1111
from .mcp_github_tools import run_mcp_github_tools_example
12+
from .blueprint_with_build_context import run_blueprint_with_build_context_example
1213
from .devbox_from_blueprint_lifecycle import run_devbox_from_blueprint_lifecycle_example
1314

1415
ExampleRegistryEntry = dict[str, Any]
1516

1617
example_registry: list[ExampleRegistryEntry] = [
18+
{
19+
"slug": "blueprint-with-build-context",
20+
"title": "Blueprint with Build Context",
21+
"file_name": "blueprint_with_build_context.py",
22+
"required_env": ["RUNLOOP_API_KEY"],
23+
"run": run_blueprint_with_build_context_example,
24+
},
1725
{
1826
"slug": "devbox-from-blueprint-lifecycle",
1927
"title": "Devbox From Blueprint (Run Command, Shutdown)",

0 commit comments

Comments
 (0)