Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions edge0-infer/tools/assets/asset_common.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,13 @@ def snapshot(root: Path) -> dict[str, str]:
return {str(p.relative_to(root)): sha256(p) for p in sorted(root.rglob('*')) if p.is_file()}


def verify_source(source: Path, lock: dict, names=None) -> None:
def verify_source(source: Path, lock: dict, names=None, *, digests: dict[str, str] | None = None) -> None:
for name in names if names is not None else lock['files']:
path = source / name
if not path.is_file():
raise ValueError(f'missing source file: {name}')
if sha256(path) != lock['files'][name]:
digest = sha256(path) if digests is None else digests.get(name)
if digest != lock['files'][name]:
raise ValueError(f'source digest mismatch: {name}')


Expand Down
2 changes: 1 addition & 1 deletion edge0-infer/tools/assets/edge0_35b/make_assets.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,8 @@ def make_assets(source: Path, out: Path, lock: dict | None = None, converter: Pa
if partial.exists() or partial.is_symlink():
raise ValueError(f'an earlier run left {partial}; delete it and rerun')
lock = lock if lock is not None else read_json(Path(__file__).with_name('source.lock.json'))
verify_source(source, lock)
before = snapshot(source)
verify_source(source, lock, digests=before)
partial.mkdir(parents=True, exist_ok=False)
try:
_convert(source, out, partial, lock, converter, before)
Expand Down
23 changes: 23 additions & 0 deletions edge0-infer/tools/assets/tests/test_make_assets.py
Original file line number Diff line number Diff line change
Expand Up @@ -167,3 +167,26 @@ def test_repository_converter_is_recorded_relative(tmp_path):
partial = tmp_path / 'out.partial'
assert maker.recorded_path(maker.CONVERTER, partial) == 'tools/assets/edge0_35b/edge0_convert/convert_mlx_to_gguf.py'
assert maker.recorded_path(partial / 'staging/edge0-35b', partial) == '<partial>/staging/edge0-35b'


def test_source_snapshot_is_reused_for_lock_verification(setup, monkeypatch):
import asset_common
from collections import Counter

source, out, lock, converter, _ = setup
original = asset_common.sha256
reads = Counter()

def counted(path):
path = Path(path)
if path.parent == source:
reads[path.name] += 1
return original(path)

monkeypatch.setattr(asset_common, 'sha256', counted)
with pytest.raises(maker.MakeError):
maker.make_assets(source, out, lock, converter)
# One digest supplies both the initial snapshot and lock verification;
# the finally block independently verifies that the converter changed nothing.
assert dict(reads) == {name: 2 for name in lock['files']}
assert not out.exists()