This commit is contained in:
@@ -1278,7 +1278,22 @@ def _write_builder_state(
|
|||||||
|
|
||||||
|
|
||||||
def _source_bundle_hash(bundle: bytes) -> str:
|
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(
|
def _record_sandbox_result(
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
import hashlib
|
import gzip
|
||||||
import io
|
import io
|
||||||
import json
|
import json
|
||||||
import tarfile
|
import tarfile
|
||||||
@@ -22,6 +22,7 @@ from agent_builder.tools import (
|
|||||||
_replace_workspace_files,
|
_replace_workspace_files,
|
||||||
_render_a2a_init_template,
|
_render_a2a_init_template,
|
||||||
_render_skill_md,
|
_render_skill_md,
|
||||||
|
_source_bundle_hash,
|
||||||
_tarball_workspace_dir,
|
_tarball_workspace_dir,
|
||||||
ToolContext,
|
ToolContext,
|
||||||
build_tools,
|
build_tools,
|
||||||
@@ -382,9 +383,9 @@ class TemplateInitTests(unittest.TestCase):
|
|||||||
}
|
}
|
||||||
for rel, content in files.items():
|
for rel, content in files.items():
|
||||||
workspace.write_bytes(prefix + rel, content.encode("utf-8"))
|
workspace.write_bytes(prefix + rel, content.encode("utf-8"))
|
||||||
source_hash = hashlib.sha256(
|
source_hash = _source_bundle_hash(
|
||||||
_tarball_workspace_dir(_WorkspaceObjectStore(workspace), prefix)
|
_tarball_workspace_dir(_WorkspaceObjectStore(workspace), prefix)
|
||||||
).hexdigest()
|
)
|
||||||
workspace.write_bytes(
|
workspace.write_bytes(
|
||||||
prefix + _BUILDER_STATE_FILE,
|
prefix + _BUILDER_STATE_FILE,
|
||||||
json.dumps({
|
json.dumps({
|
||||||
@@ -478,6 +479,27 @@ class TemplateInitTests(unittest.TestCase):
|
|||||||
self.assertEqual(result["error"], "sandbox_not_passed")
|
self.assertEqual(result["error"], "sandbox_not_passed")
|
||||||
self.assertEqual(_FakeAsyncClient.posts, [])
|
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:
|
def test_cp_compose_meta_agent_posts_manifest_and_records_state(self) -> None:
|
||||||
workspace = _FakeWorkspace()
|
workspace = _FakeWorkspace()
|
||||||
tools = build_tools(
|
tools = build_tools(
|
||||||
|
|||||||
Reference in New Issue
Block a user