2026-10-23 19:12:16 +08:00
|
|
|
"""离线核对发布清单、开发素材及布局;不访问远端。"""
|
|
|
|
|
|
|
|
|
|
import hashlib
|
|
|
|
|
import json
|
|
|
|
|
from pathlib import Path
|
2026-10-26 18:53:41 +08:00
|
|
|
import sys
|
2026-10-23 19:12:16 +08:00
|
|
|
from zipfile import ZipFile
|
|
|
|
|
|
2026-10-26 18:53:41 +08:00
|
|
|
ROOT = Path(__file__).resolve().parents[1]
|
|
|
|
|
sys.path.insert(0, str(ROOT / 'src'))
|
|
|
|
|
from farm_core.catalogue_release import parse_manifest
|
|
|
|
|
from farm_render.catalogue_bundle import ROOTS
|
|
|
|
|
|
2026-10-23 19:12:16 +08:00
|
|
|
|
|
|
|
|
def check(root):
|
|
|
|
|
data = Path(root) / 'src/farm_render/data'
|
|
|
|
|
manifest = json.loads((data / 'asset-manifest.json').read_bytes())
|
|
|
|
|
metadata = json.loads((data / 'metadata.json').read_bytes())
|
|
|
|
|
bundle = data / 'assets.zip'
|
|
|
|
|
if (manifest['schema'] != 1
|
|
|
|
|
or bundle.stat().st_size != manifest['size_bytes']
|
|
|
|
|
or hashlib.sha256(bundle.read_bytes()).hexdigest()
|
|
|
|
|
!= manifest['sha256']):
|
|
|
|
|
raise ValueError('Asset manifest and development ZIP differ')
|
|
|
|
|
with ZipFile(bundle) as archive:
|
|
|
|
|
if set(archive.namelist()) != set(metadata['images']):
|
|
|
|
|
raise ValueError('Asset manifest and layout differ')
|
2026-10-26 18:53:41 +08:00
|
|
|
bootstrap = parse_manifest((Path(root) / 'src/farm_core/data/'
|
|
|
|
|
'catalogue-manifest.json').read_bytes())
|
|
|
|
|
if manifest.get('catalogue_release') != bootstrap['release']:
|
|
|
|
|
raise ValueError('Bootstrap and asset release differ')
|
|
|
|
|
for name, base in ROOTS.items():
|
|
|
|
|
source = name.lower()
|
|
|
|
|
prefix = base + '/releases/' + bootstrap['release'] + '/assets.zip'
|
2026-10-23 19:12:16 +08:00
|
|
|
if manifest['sources'][source] != prefix:
|
|
|
|
|
raise ValueError('Asset source must use service repository')
|
|
|
|
|
print('Verified external asset manifest and development ZIP')
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
|
check(Path(__file__).resolve().parents[1])
|