2222 "rc" : "library-rc.zip" ,
2323 "release" : "library-latest.zip" ,
2424}
25+ V2_FULL_PACKAGE_BASENAME = "library"
2526CATALOG_FILE_NAMES = (
2627 "catalog_en.json" ,
2728 "catalog_zh.json" ,
@@ -115,6 +116,14 @@ def summarize_versions(edition_list: list[dict]) -> list[str]:
115116 return versions
116117
117118
119+ def v2_full_package_names (channel : str , dataset_version : str ) -> tuple [str , str ]:
120+ return (f"{ V2_FULL_PACKAGE_BASENAME } -{ dataset_version } .zip" , f"{ V2_FULL_PACKAGE_BASENAME } -{ channel } .zip" )
121+
122+
123+ def v2_app_package_names (dataset_version : str ) -> tuple [str , str ]:
124+ return (f"{ dataset_version } .zip" , "latest.zip" )
125+
126+
118127def sha256_file (path : Path ) -> str :
119128 digest = hashlib .sha256 ()
120129 with path .open ("rb" ) as handle :
@@ -172,19 +181,39 @@ def current_app_fingerprint(app_dir: Path) -> str:
172181 return digest .hexdigest ()
173182
174183
184+ def build_app_package_entry (app_name : str , dataset_version : str ) -> dict :
185+ versioned_name , latest_name = v2_app_package_names (dataset_version )
186+ app_base = f"apps/{ app_name } "
187+ return {
188+ "versioned" : f"{ app_base } /{ versioned_name } " ,
189+ "latest" : f"{ app_base } /{ latest_name } " ,
190+ }
191+
192+
193+ def build_app_checksum_entry (app_name : str , dataset_version : str ) -> dict :
194+ package_entry = build_app_package_entry (app_name , dataset_version )
195+ return {
196+ "versioned" : f"{ package_entry ['versioned' ]} .sha256" ,
197+ "latest" : f"{ package_entry ['latest' ]} .sha256" ,
198+ }
199+
200+
175201def build_apps_index (dataset_version : str , channel : str , generated_at : str ) -> dict :
176202 apps = []
177203 for app_dir in sorted (path for path in APPS_DIR .iterdir () if path .is_dir ()):
178204 variables = load_variables_json (app_dir )
205+ app_name = app_dir .name
179206 apps .append (
180207 {
181- "app" : app_dir . name ,
182- "name" : variables .get ("name" , app_dir . name ),
183- "trademark" : variables .get ("trademark" , variables .get ("name" , app_dir . name )),
208+ "app" : app_name ,
209+ "name" : variables .get ("name" , app_name ),
210+ "trademark" : variables .get ("trademark" , variables .get ("name" , app_name )),
184211 "release" : variables .get ("release" ),
185212 "versions" : summarize_versions (variables .get ("edition" , [])),
186- "path" : f"apps/{ app_dir . name } " ,
213+ "path" : f"apps/{ app_name } " ,
187214 "hash" : current_app_fingerprint (app_dir ),
215+ "package" : build_app_package_entry (app_name , dataset_version ),
216+ "checksum" : build_app_checksum_entry (app_name , dataset_version ),
188217 }
189218 )
190219
@@ -260,7 +289,7 @@ def build_apps_delta(
260289def build_library_manifest (
261290 dataset_version : str ,
262291 channel : str ,
263- package_name : str ,
292+ full_package_names : dict ,
264293 apps_index_name : str ,
265294 apps_delta_name : str ,
266295 checksum_names : dict ,
@@ -270,9 +299,11 @@ def build_library_manifest(
270299 "schemaVersion" : "1" ,
271300 "datasetVersion" : dataset_version ,
272301 "channel" : channel ,
273- "libraryPackage " : package_name ,
302+ "fullPackage " : full_package_names ,
274303 "appsIndex" : apps_index_name ,
275304 "appsDelta" : apps_delta_name ,
305+ "appPackagesBase" : "apps/" ,
306+ "supportsPartialUpdate" : True ,
276307 "checksum" : checksum_names ,
277308 "generatedAt" : generated_at ,
278309 }
@@ -373,7 +404,8 @@ def validate_catalog_artifacts(output_dir: Path, manifest: dict) -> None:
373404
374405def validate_library_artifacts (output_dir : Path , manifest : dict ) -> None :
375406 for name in (
376- manifest ["libraryPackage" ],
407+ manifest ["fullPackage" ]["versioned" ],
408+ manifest ["fullPackage" ]["latest" ],
377409 manifest ["appsIndex" ],
378410 manifest ["appsDelta" ],
379411 "manifest.json" ,
@@ -390,6 +422,14 @@ def validate_library_artifacts(output_dir: Path, manifest: dict) -> None:
390422 if key not in apps_delta or not isinstance (apps_delta [key ], list ):
391423 raise SystemExit (f"invalid apps delta structure: { key } " )
392424
425+ apps_index = json .loads ((output_dir / manifest ["appsIndex" ]).read_text (encoding = "utf-8" ))
426+ for entry in apps_index .get ("apps" , []):
427+ package = entry .get ("package" ) or {}
428+ checksum = entry .get ("checksum" ) or {}
429+ for path in (package .get ("versioned" ), package .get ("latest" ), checksum .get ("versioned" ), checksum .get ("latest" )):
430+ if not path or not (output_dir / path ).exists ():
431+ raise SystemExit (f"missing app package artifact: { entry .get ('app' )} -> { path } " )
432+
393433
394434def validate_appstore_artifacts (appstore_dir : Path ) -> None :
395435 required_paths = (
@@ -506,12 +546,36 @@ def build_v2_appstore_artifacts(
506546
507547 apps_index_name = f"apps-index-{ dataset_version } .json"
508548 apps_delta_name = f"apps-delta-{ from_version } -to-{ dataset_version } .json"
549+ full_dir = library_dir / "full"
550+ apps_packages_dir = library_dir / "apps"
551+ full_dir .mkdir (parents = True , exist_ok = True )
552+ apps_packages_dir .mkdir (parents = True , exist_ok = True )
553+
554+ full_versioned_name , full_latest_name = v2_full_package_names (channel , dataset_version )
509555
510556 with tempfile .TemporaryDirectory () as tmp_dir_name :
511557 tmp_dir = Path (tmp_dir_name )
512558 package_dir = tmp_dir / PACKAGE_ROOT_NAME
513559 copy_package_contents (package_dir , packaged_library_json )
514- create_zip_from_directory (package_dir , library_dir / package_name )
560+ create_zip_from_directory (package_dir , full_dir / full_versioned_name )
561+
562+ shutil .copy2 (full_dir / full_versioned_name , full_dir / full_latest_name )
563+
564+ for app_dir in sorted (path for path in APPS_DIR .iterdir () if path .is_dir ()):
565+ app_name = app_dir .name
566+ app_output_dir = apps_packages_dir / app_name
567+ app_output_dir .mkdir (parents = True , exist_ok = True )
568+ app_versioned_name , app_latest_name = v2_app_package_names (dataset_version )
569+
570+ with tempfile .TemporaryDirectory () as tmp_dir_name :
571+ tmp_dir = Path (tmp_dir_name )
572+ app_package_root = tmp_dir / app_name
573+ shutil .copytree (app_dir , app_package_root )
574+ create_zip_from_directory (app_package_root , app_output_dir / app_versioned_name )
575+
576+ shutil .copy2 (app_output_dir / app_versioned_name , app_output_dir / app_latest_name )
577+ write_checksum_file (app_output_dir / app_versioned_name )
578+ write_checksum_file (app_output_dir / app_latest_name )
515579
516580 apps_index = build_apps_index (dataset_version , channel , generated_at )
517581 apps_delta = build_apps_delta (
@@ -526,15 +590,19 @@ def build_v2_appstore_artifacts(
526590 write_json (library_dir / apps_delta_name , apps_delta )
527591
528592 library_checksums = {
529- "libraryPackage" : write_checksum_file (library_dir / package_name ),
593+ "fullPackageVersioned" : f"full/{ write_checksum_file (full_dir / full_versioned_name )} " ,
594+ "fullPackageLatest" : f"full/{ write_checksum_file (full_dir / full_latest_name )} " ,
530595 "appsIndex" : write_checksum_file (library_dir / apps_index_name ),
531596 "appsDelta" : write_checksum_file (library_dir / apps_delta_name ),
532597 }
533598
534599 library_manifest = build_library_manifest (
535600 dataset_version = dataset_version ,
536601 channel = channel ,
537- package_name = package_name ,
602+ full_package_names = {
603+ "versioned" : f"full/{ full_versioned_name } " ,
604+ "latest" : f"full/{ full_latest_name } " ,
605+ },
538606 apps_index_name = apps_index_name ,
539607 apps_delta_name = apps_delta_name ,
540608 checksum_names = library_checksums ,
@@ -558,7 +626,11 @@ def build_v2_appstore_artifacts(
558626 },
559627 "library" : {
560628 "manifest" : "library/manifest.json" ,
561- "libraryPackage" : package_name ,
629+ "fullPackage" : {
630+ "versioned" : f"full/{ full_versioned_name } " ,
631+ "latest" : f"full/{ full_latest_name } " ,
632+ },
633+ "appPackagesBase" : "apps/" ,
562634 "appsIndex" : apps_index_name ,
563635 "appsDelta" : apps_delta_name ,
564636 "checksum" : library_checksums ,
0 commit comments