@@ -124,6 +124,15 @@ def v2_full_package_names(channel: str, dataset_version: str) -> tuple[str, str]
124124APP_PACKAGE_NAME = "latest.zip"
125125
126126
127+ def _hash_content (* contents : str ) -> str :
128+ """Derive a short content-addressed version from one or more strings."""
129+ digest = hashlib .sha256 ()
130+ for c in contents :
131+ digest .update (c .encode ("utf-8" ))
132+ digest .update (b"\0 " )
133+ return digest .hexdigest ()[:16 ]
134+
135+
127136def sha256_file (path : Path ) -> str :
128137 digest = hashlib .sha256 ()
129138 with path .open ("rb" ) as handle :
@@ -316,16 +325,21 @@ def build_catalog_manifest(dataset_version: str, checksum_names: dict, generated
316325 }
317326
318327
319- def build_appstore_manifest (dataset_version : str , channel : str , generated_at : str ) -> dict :
328+ def build_appstore_manifest (
329+ dataset_version : str , channel : str , generated_at : str ,
330+ catalog_dsv : str , library_dsv : str ,
331+ ) -> dict :
320332 return {
321333 "schemaVersion" : "1" ,
322334 "datasetVersion" : dataset_version ,
323335 "channel" : channel ,
324336 "catalog" : {
325337 "manifest" : "catalog/manifest.json" ,
338+ "datasetVersion" : catalog_dsv ,
326339 },
327340 "library" : {
328341 "manifest" : "library/manifest.json" ,
342+ "datasetVersion" : library_dsv ,
329343 },
330344 "generatedAt" : generated_at ,
331345 }
@@ -537,13 +551,14 @@ def build_v2_appstore_artifacts(
537551 elif file_name == "product_zh.json" :
538552 catalog_checksums ["productZh" ] = write_checksum_file (destination )
539553
540- catalog_manifest = build_catalog_manifest (dataset_version , catalog_checksums , generated_at )
554+ catalog_checksum_values = "," .join (f"{ k } ={ v } " for k , v in sorted (catalog_checksums .items ()))
555+ catalog_dsv = _hash_content (catalog_checksum_values )
556+
557+ catalog_manifest = build_catalog_manifest (catalog_dsv , catalog_checksums , generated_at )
541558 write_json (catalog_dir / "manifest.json" , catalog_manifest )
542559 write_checksum_file (catalog_dir / "manifest.json" )
543560 validate_catalog_artifacts (catalog_dir , catalog_manifest )
544561
545- apps_index_name = f"apps-index-{ dataset_version } .json"
546- apps_delta_name = f"apps-delta-{ from_version } -to-{ dataset_version } .json"
547562 full_dir = library_dir / "full"
548563 apps_packages_dir = library_dir / "apps"
549564 full_dir .mkdir (parents = True , exist_ok = True )
@@ -560,16 +575,22 @@ def build_v2_appstore_artifacts(
560575 shutil .copy2 (full_dir / full_versioned_name , full_dir / full_latest_name )
561576
562577 # ── library – compute index & delta BEFORE per-app zips ──
563- apps_index = build_apps_index (dataset_version , channel , generated_at )
578+ apps_index = build_apps_index (catalog_dsv , channel , generated_at )
579+ serialized_index = json .dumps (apps_index , sort_keys = True , ensure_ascii = False )
580+ library_dsv = _hash_content (serialized_index )
581+
564582 apps_delta = build_apps_delta (
565583 apps_index = apps_index ,
566584 from_ref = from_ref ,
567585 from_version = from_version ,
568- to_version = dataset_version ,
586+ to_version = library_dsv ,
569587 channel = channel ,
570588 generated_at = generated_at ,
571589 )
572590
591+ apps_index_name = f"apps-index-{ library_dsv } .json"
592+ apps_delta_name = f"apps-delta-{ from_version } -to-{ library_dsv } .json"
593+
573594 # Determine which apps actually need (re)built packages.
574595 # --all-apps (first-publish seed) or from_ref=None both mean "build everything".
575596 if all_apps or from_ref is None :
@@ -606,7 +627,7 @@ def build_v2_appstore_artifacts(
606627 }
607628
608629 library_manifest = build_library_manifest (
609- dataset_version = dataset_version ,
630+ dataset_version = library_dsv ,
610631 channel = channel ,
611632 full_package_names = {
612633 "versioned" : f"full/{ full_versioned_name } " ,
@@ -621,7 +642,8 @@ def build_v2_appstore_artifacts(
621642 write_checksum_file (library_dir / "manifest.json" )
622643 validate_library_artifacts (library_dir , library_manifest , changed_app_names )
623644
624- appstore_manifest = build_appstore_manifest (dataset_version , channel , generated_at )
645+ appstore_dsv = _hash_content (catalog_dsv , library_dsv )
646+ appstore_manifest = build_appstore_manifest (appstore_dsv , channel , generated_at , catalog_dsv , library_dsv )
625647 write_json (manifests_dir / "appstore-manifest.json" , appstore_manifest )
626648 appstore_manifest_checksum = write_checksum_file (manifests_dir / "appstore-manifest.json" )
627649 validate_appstore_artifacts (output_dir )
@@ -632,6 +654,7 @@ def build_v2_appstore_artifacts(
632654 "manifest" : "catalog/manifest.json" ,
633655 "files" : list (CATALOG_FILE_NAMES ),
634656 "checksum" : catalog_checksums ,
657+ "datasetVersion" : catalog_dsv ,
635658 },
636659 "library" : {
637660 "manifest" : "library/manifest.json" ,
@@ -643,10 +666,12 @@ def build_v2_appstore_artifacts(
643666 "appsIndex" : apps_index_name ,
644667 "appsDelta" : apps_delta_name ,
645668 "checksum" : library_checksums ,
669+ "datasetVersion" : library_dsv ,
646670 },
647671 "appstore" : {
648672 "manifest" : "manifests/appstore-manifest.json" ,
649673 "checksum" : appstore_manifest_checksum ,
674+ "datasetVersion" : appstore_dsv ,
650675 },
651676 }
652677
0 commit comments