From 1de77b277d99066327aeed3061a3498a7c618645 Mon Sep 17 00:00:00 2001 From: Edgar Arakelyan Date: Fri, 30 Oct 2026 10:00:34 -0700 Subject: [PATCH] fix(bundle): task_images lists every image, however it's stored task_images ran the import's walk with no refs, so an image the import can write only as a given reference, such as a tarball under a bare name, was refused as unwritable and the listing raised. Those are exactly the images an export loads and pushes, then names by reference. The listing now names every image without judging how it's stored; it still raises on the import's other problems. Co-Authored-By: Claude Opus 5.5 --- src/agent_env/bundle/importer.py | 5 ++++- tst/unit/bundle/importer_image_refs_test.py | 14 ++++++++++++-- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/src/agent_env/bundle/importer.py b/src/agent_env/bundle/importer.py index e5d1e776..731c9c18 100644 --- a/src/agent_env/bundle/importer.py +++ b/src/agent_env/bundle/importer.py @@ -93,7 +93,8 @@ def task_images(task_id: str, *, version: int | None = None, include_scopes: Collection[FieldScope] = ()) -> tuple[DockerImageArtifact, ...]: """The images ``import_task`` writes for the stored task ``task_id``, at ``version`` or its latest, each once in the order it reaches them, found without downloading anything: what an export pushes to a registry before importing the - task with ``image_refs`` naming them there. Raises BundleError when the import would fail on what it reads.""" + task with ``image_refs`` naming them there. Every image is listed, however it's stored, since each is then given a + reference; raises BundleError when the import would fail on anything else it reads.""" importer = _Importer(None, include_scopes) importer.add_task(Task.get(task_id, version)) if importer.problems: @@ -284,6 +285,8 @@ def image(self, out: _EntityOut, artifact: DockerImageArtifact, key: str) -> str registry ref, else a pinned store image. Given a registry reference for it, that reference, its tag pinned to the digest the registry serves now when it gives none, with the context kept beside it when it has one.""" self.images_named.setdefault((artifact.id, artifact.version), artifact) + if self.dest is None: # only listed, never written: each image is given a reference before an import is + return {"ref": artifact.image_name} ref = self.image_refs.get(artifact.id) if ref is not None: self.refs_used.add(artifact.id) diff --git a/tst/unit/bundle/importer_image_refs_test.py b/tst/unit/bundle/importer_image_refs_test.py index b4c5b0ac..a55758a4 100644 --- a/tst/unit/bundle/importer_image_refs_test.py +++ b/tst/unit/bundle/importer_image_refs_test.py @@ -212,11 +212,21 @@ def test_the_images_a_task_writes_are_listed_once_in_order_without_downloading_a refs["crm-image"], refs["crm-image"], refs["mail-image"]] -def test_listing_a_task_the_import_cant_write_fails_before_any_push_would_start(home): +def test_an_image_the_import_writes_only_with_a_ref_is_listed_and_takes_one(home): + """A tarball under a bare name is what an export loads and pushes, so it's listed, not refused.""" _stored(get_artifact_store().put_document(DockerImageArtifact( id="crm-image", description="d", image_name="crm-image:v1", tar_gz_s3_url="file:///nowhere/crm.tar.gz"))) + (image,) = task_images("t") + imported = import_task("t", home / "t", image_refs={image.id: REF}) + + assert (image.id, _toml(imported.root, "envs/crm/env.toml")["image"]) == ("crm-image", {"ref": REF}) + + +def test_listing_a_task_the_import_cant_write_fails_before_any_push_would_start(home): + Task.put(id="t", steps=[build_step(dict(DEPLOY))]) # its env isn't in the store + with pytest.raises(BundleError) as caught: task_images("t") - assert any("stored only as a docker save tarball" in problem for problem in caught.value.problems) + assert any("crm" in problem for problem in caught.value.problems)