Use canonical builder source hash
All checks were successful
build / build (push) Successful in 13s

This commit is contained in:
robert
2026-06-08 09:41:31 -03:00
parent 4d1dcfb2df
commit e511295023
2 changed files with 41 additions and 4 deletions

View File

@@ -1278,7 +1278,22 @@ def _write_builder_state(
def _source_bundle_hash(bundle: bytes) -> str:
return hashlib.sha256(bundle).hexdigest()
digest = hashlib.sha256()
with tarfile.open(fileobj=io.BytesIO(bundle), mode="r:gz") as tf:
members = sorted(
(member for member in tf.getmembers() if member.isfile()),
key=lambda member: member.name,
)
for member in members:
extracted = tf.extractfile(member)
body = extracted.read() if extracted is not None else b""
digest.update(member.name.encode("utf-8"))
digest.update(b"\0")
digest.update(str(len(body)).encode("ascii"))
digest.update(b"\0")
digest.update(body)
digest.update(b"\0")
return digest.hexdigest()
def _record_sandbox_result(

View File

@@ -1,7 +1,7 @@
from __future__ import annotations
import asyncio
import hashlib
import gzip
import io
import json
import tarfile
@@ -22,6 +22,7 @@ from agent_builder.tools import (
_replace_workspace_files,
_render_a2a_init_template,
_render_skill_md,
_source_bundle_hash,
_tarball_workspace_dir,
ToolContext,
build_tools,
@@ -382,9 +383,9 @@ class TemplateInitTests(unittest.TestCase):
}
for rel, content in files.items():
workspace.write_bytes(prefix + rel, content.encode("utf-8"))
source_hash = hashlib.sha256(
source_hash = _source_bundle_hash(
_tarball_workspace_dir(_WorkspaceObjectStore(workspace), prefix)
).hexdigest()
)
workspace.write_bytes(
prefix + _BUILDER_STATE_FILE,
json.dumps({
@@ -478,6 +479,27 @@ class TemplateInitTests(unittest.TestCase):
self.assertEqual(result["error"], "sandbox_not_passed")
self.assertEqual(_FakeAsyncClient.posts, [])
def test_source_bundle_hash_ignores_archive_metadata(self) -> None:
def bundle_with_mtime(mtime: int) -> bytes:
raw = io.BytesIO()
with tarfile.open(fileobj=raw, mode="w") as tf:
body = b"print('ok')\n"
info = tarfile.TarInfo(name="agent.py")
info.size = len(body)
info.mode = 0o644
info.mtime = mtime
tf.addfile(info, io.BytesIO(body))
compressed = io.BytesIO()
with gzip.GzipFile(fileobj=compressed, mode="wb", mtime=mtime) as gz:
gz.write(raw.getvalue())
return compressed.getvalue()
first = bundle_with_mtime(1)
second = bundle_with_mtime(2)
self.assertNotEqual(first, second)
self.assertEqual(_source_bundle_hash(first), _source_bundle_hash(second))
def test_cp_compose_meta_agent_posts_manifest_and_records_state(self) -> None:
workspace = _FakeWorkspace()
tools = build_tools(