Repository navigation
feat(modal): build an image that is only a build context, with from_dockerfile - #137
Conversation
|
@greptileai review |
| of ``out`` is left out, as nothing there is the build's. Raises ValueError on anything else, which no context | ||
| written by ``BuildContext.write`` holds: a hard link, a device, a name that leads out of ``out``.""" | ||
| with tarfile.open(archive, "r:gz") as tar: | ||
| tar.extractall(out, filter=_context_member) |
There was a problem hiding this comment.
Modal builds fail on early Python 3.11
On Python 3.11.0–3.11.3, extract() uses tar filters that those releases lack. The package allows those versions, so preparing a context-only image on Modal fails before the build starts. Require a compatible Python patch release or use an extraction path that works on every supported version.
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/agent_env/utils/build_context.py
Line: 107
Comment:
**Modal builds fail on early Python 3.11**
On Python 3.11.0–3.11.3, `extract()` uses tar filters that those releases lack. The package allows those versions, so preparing a context-only image on Modal fails before the build starts. Require a compatible Python patch release or use an extraction path that works on every supported version.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.There was a problem hiding this comment.
Fixed in 1387697: the package now requires Python 3.11.4 (pyproject, uv.lock, and the README/CONTRIBUTING/AGENTS floor), the release that added tarfile's extraction filters as the fix for CVE-2007-4559. Every CI job and consumer runs 3.11-latest or 3.12, so this only rules out 3.11.0–3.11.3.
|
@greptileai review |
A chain's deadline cancels a create still waiting on its sandbox, and the CancelledError skipped the cleanup that terminates it, so the sandbox ran until its timeout. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
SandboxProvider.prepare_image(image) readies an image before a sandbox is created from its name; it does nothing by default. A chain remembers the image and prepares each provider just before trying it, outside the deadline on creating the sandbox, so a slow build doesn't count against it, and a failed prepare falls through to the next provider like a failed create. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…d reads build_context.extract unpacks a context's tar.gz: its files, folders and links, leaving out a link that leads out of the folder, which nothing in the build can use, and refusing what no context holds, such as a hard link, a device or a name leading out. ignore_file, now public, names the ignore file docker build reads, so a build elsewhere applies the same one. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ockerfile ModalSandboxProvider.prepare_image downloads a context-only image's build context, unpacks it, and builds it with modal.Image.from_dockerfile and the context's own ignore file, then create_container runs the built image instead of pulling its name. The built image's id is kept per Modal token, app and source digest for the life of the process, and one build of each runs at a time, so deploys of the same sources share a build; a failed build is built again by the next deploy, and create_container builds an image it was told of on a miss. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Modal now builds such an image, so the guards that refused it there are lifted: an agent on Modal or on a chain holding it, Modal's gateway and its MCP servers, and a lone server on Modal each prepare the image before creating anything, and the gateway builds all of its images before its first container. A container on the local provider, a lone server on any other provider, and an agent attached to an existing container sandbox still refuse it, and preflight agrees, naming a provider that builds it. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…s a build context is unpacked with Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…than failing every deploy of it Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
1387697 to
6fb059b
Compare
|
@greptileai review |
…ing its sandbox does Every caller and a chain pass it, so a provider can attribute the build to the deploy it's for. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…uild makes of them, and name the build log on failure Modal's builder fetches only a URL for ADD, and its context parser can't read a JSON-form COPY. The build reads a copy of the Dockerfile, beside the context so a COPY of the context still copies it as written, in which a JSON-form COPY or ADD whose paths hold no whitespace is in shell form, and an ADD of files and folders in the context, none of them a tar archive, is the COPY it is. A failed build names the Modal build log that has its cause. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… the command that shows the log Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
@greptileai review |
An image that is only a build context (
put_context) could run only on VM sandboxes, which build it themselves (#133). Modal containers refused it, and so did any chain holding Modal, such asbeta_scale,modal.With this PR, Modal builds such an image itself, with
modal.Image.from_dockerfile.prepare_imagehook: a newSandboxProvider.prepare_image(image), which does nothing by default. Callers invoke it before asking a provider for a sandbox or container: the agent deploy, a lone server on Modal, and Modal's gateway, which builds all its images before creating anything. Modal's version:source_digest, so concurrent deploys of one image share one build.create_containeruses the built image, rebuilding on a cache miss. Everything else is pulled as before.create_*: a chain forwards its kwargs to every provider, and a plugin provider without**kwargswould raise a TypeError on every deploy.build_context.extractaccepts only files, folders and links. It refuses hard links and devices, and skips links whose target leaves the folder, so even an older Modal that follows links can't upload anything from outside the context.by_name_problem()'s wording are updated to match.CancelledErrorskipped the cleanup and the sandbox ran until its TTL.No pre-check for Dockerfile features Modal doesn't reproduce; Modal is left to try. Two forms it refuses are written as what
docker buildmakes of them, in a copy of the Dockerfile kept beside the context (so aCOPYof the context still copies the original): a JSON-formCOPY/ADDwhose paths hold no whitespace becomes shell form, and anADDof files or folders in the context, none a tar archive, becomesCOPY(Modal'sADDfetches only a URL). What Modal still builds differently from a VM: a link to a file is copied as the file; links to folders, dangling links, links that leave the context, and empty folders are dropped.Tests
create_containerrebuilding on a miss;from_dockerfile,build.aioandfrom_idagainst the installed Modal.Live (Modal dev workspace,
@localids only)itemsserver as a lone servermodal,modal_vmSince review
Chaos battery (12 cases, Modal dev workspace,
@localids, a dedicated Modal App):--sandbox modaland onbeta_scale,modal;It found three things, fixed here:
prepare_imagenow takes the deploy's attribution (every caller and the chain pass it), so a provider can attribute the build as it does the sandbox. The sdk's per-project App wrapping covers it in agentenvhub-sdk 0.33.8. Verified live: the build and the container both go in<app>-<project_id>.COPYfailed in Modal's context parser.ADDwas refused by Modal's builder. Both now build, through the Dockerfile copy above.Found but not part of this change: modal_vm's VMs have no buildx, so a VM build using a BuildKit-only flag (
COPY --chmod,RUN --mount) fails there; beta_scale's have it.Rebased onto feat(bundle): write a built image as its build context when its deploys build it in their VMs #136. The bundle form decision now counts Modal as a provider that builds these images: any provider that overrides
prepare_image, alongside those that create VMs. A bundle-built agent, gateway server or lone server onmodal, or onbeta_scale,modal(the Scale default chain), is written as a build context. A lone server on any other provider is still refused. The dry-run line and ledger reason no longer say only VMs build a context.A built image Modal no longer holds is built again, once. The process cache never forgot an image id, so if Modal dropped a built image, every later deploy of it would fail until the process restarted. A create that fails with Modal's
NotFoundErrorfor an image built from a context now rebuilds it and retries once. Concurrent deploys waiting on the same stale id share one rebuild. Verified live by planting a missing id in the cache: the next deploy rebuilt and ran in 3 s.Checked against the hub and worker's Modal client,
1.5.4.dev20: the provider tests pass with it, and a real build from a fresh context deployed in 23 s.🤖 Generated with Claude Code


Confidence Score: 5/5
The PR appears safe to merge; no new actionable issue was established, and the previous findings are closed.Fix with agent prompt
Summary
Modal can now build images saved only as Docker build contexts before creating containers, while provider chains prepare each provider only when they try it. Bundle planning also recognizes Modal as a builder, and context extraction keeps links from reaching outside the build folder.
Diagram
%%{init: {'theme': 'neutral'}}%% flowchart LR A[Context-only image] --> B[Prepare image] B --> C{Provider} C -->|Modal| D[Build image on Modal] C -->|VM| E[Build image in VM] D --> F[Create sandbox] E --> FReviews (5) · Last reviewed commit: "fix(modal): drop the build-log pointer, ..." · Reviewed by Greptile