Skip to content

feat(bundle): a registry image with its build context beside it, and bundle init --image-ref - #405

Open
earakely-scale wants to merge 2 commits into
mainfrom
feat/bundle-image-refs
Open

earakely-scale wants to merge 2 commits into
mainfrom
feat/bundle-image-refs

Conversation

@earakely-scale

@earakely-scale earakely-scale commented Oct 30, 2026 •

Copy link
Copy Markdown
Collaborator

What

A bundle can name an image in a registry and keep its build context beside it:

image = { ref = "<registry>/<repo>:<tag>@sha256:<digest>", dockerfile = "Dockerfile" }

An image folder can also hold a Dockerfile beside the ref in its artifact.toml, which it used to refuse. A run pulls the image by its digest and never builds it. The context is there to read or rebuild, and a deploy builds from it only when it can't pull the image, as it already does for a published agent image.

agent-env bundle init can write images this way. --image-ref ID=REF (or image_refs= on import_task and import_eval) writes the stored image ID as the registry image REF, with the part of its stored context its Dockerfile reads beside it.

  • Version pinning: it skips pinning the Dockerfile to the stored image's versions, since the run pulls the image, so the import never reads the stored tarball.
  • No context: an image with no context, or one whose context can't be written, is written as the ref alone.
  • Refusals: a ref that doesn't name its registry or pin a digest, or that names an image the import writes none of, fails the import.

The point is delivering a bundle the way Harbor is delivered: the export pushes each image to a private registry the recipient is granted, and the bundle names it by digest.

How

  • Writing: put_ref with a context but no digest now pins through the registry, then records the name and the digest apart, the shape a published image has. Existing callers all pass a digest, so they're unchanged.
  • Ledger: it covers the context's files, so editing the context rewrites the image. Rewriting keeps the earlier pin while the ref is the same.
  • Reporting: the import lists such an image as "pulled as REF, its build context beside it". Its README says pulled images are pulled by digest, and names their registries among what a machine needs. The machine-needs bullets now sit inside that list instead of after the images paragraph.

Testing

  • Unit tests cover resolve, the write, the ledger and pin reuse, the dry-run line, the import (with its refusals and the CLI), and an import followed by a run writing the image at the given digest without building it.
  • End to end on local Docker 29, with the echo-mcp example and a local registry:2 standing in for the delivery registry:
    1. Ran it into the store and pushed its image.
    2. Ran bundle init --image-ref.
    3. Deleted the image from Docker.
    4. agent-env run from an empty state root pulled the image by its digest, built nothing, and the verifier passed.

🤖 Generated with Claude Code

RetriggerView in GreptileConfidence Score: 5/5

The PR appears safe to merge; all four previous findings are addressed.

Summary

Adds registry-backed bundle images with optional build contexts and bundle init --image-ref.

  • Registry image entries can keep their Dockerfile context beside them.
  • bundle init can write stored images as digest-pinned registry references.

Diagram

%%{init: {'theme': 'neutral'}}%%
flowchart LR
  A[Stored image] --> B{Supplied image ref?}
  B -->|Yes| C[Write digest-pinned ref]
  C --> D{Context writable?}
  D -->|Yes| E[Keep context beside ref]
  D -->|No| F[Keep ref alone]
  E --> G[Run uses registry image]
  F --> G
  B -->|No| H[Existing import behavior]
Loading

Reviews (2) · Last reviewed commit: "fix(bundle): leave a Dockerfile a pulled..." · Reviewed by Greptile

…bundle init --image-ref

An image key can be `{ ref = "<registry>/<repo>:<tag>@sha256:…", dockerfile = "Dockerfile" }`,
and an image folder can hold a Dockerfile beside its ref. A run pulls the image by its digest and
never builds it; the context is kept for reading or rebuilding, which a deploy does only when it
can't pull. put_ref given a context and no digest pins through the registry and records name and
digest apart, as a published image does; the ledger covers the context's files.

`agent-env bundle init --image-ref ID=REF` (image_refs= on import_task / import_eval) writes the
stored image ID as that digest-pinned registry image with its pruned context beside it, skipping
version pinning, and refuses a ref that isn't pinned or names no image the import writes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Comment thread src/agent_env/bundle/_images.py Outdated
Comment thread src/agent_env/bundle/importer.py
Comment thread src/agent_env/bundle/importer.py Outdated
Comment thread src/agent_env/bundle/_import_report.py Outdated
…efuse what can't be written

- A Dockerfile an image given a ref shares with another image is no longer pinned to the other
  image's versions, since a deploy that can't pull the ref'd image rebuilds it from that file.
- A context in a store this config can't read no longer aborts the import: its download says why,
  and an image given a ref is written as the ref alone.
- A given ref must pin a whole sha256 digest (64 hex digits).
- The README gives the build paragraph only when an image was written from its context, and the
  pulled one whenever an image is pulled, ref-only ones included.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant