NikGapps 3.95__tar.gz → 3.96__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (94) hide show
  1. {nikgapps-3.95 → nikgapps-3.96}/NikGapps/helper/assets/config_versions.json +3 -2
  2. {nikgapps-3.95 → nikgapps-3.96}/NikGapps/overlay_control.py +15 -0
  3. {nikgapps-3.95 → nikgapps-3.96/NikGapps.egg-info}/PKG-INFO +1 -1
  4. {nikgapps-3.95/NikGapps.egg-info → nikgapps-3.96}/PKG-INFO +1 -1
  5. {nikgapps-3.95 → nikgapps-3.96}/nikgapps_package_pipeline/cli.py +19 -1
  6. {nikgapps-3.95 → nikgapps-3.96}/nikgapps_package_pipeline/pipeline.py +32 -48
  7. {nikgapps-3.95 → nikgapps-3.96}/nikgapps_package_pipeline/upgrade.py +75 -22
  8. {nikgapps-3.95 → nikgapps-3.96}/setup.py +1 -1
  9. {nikgapps-3.95 → nikgapps-3.96}/tests_package_pipeline/test_consumer.py +11 -16
  10. {nikgapps-3.95 → nikgapps-3.96}/tests_package_pipeline/test_overlay_control.py +13 -1
  11. {nikgapps-3.95 → nikgapps-3.96}/tests_package_pipeline/test_pipeline.py +12 -7
  12. {nikgapps-3.95 → nikgapps-3.96}/tests_package_pipeline/test_upgrade.py +38 -1
  13. {nikgapps-3.95 → nikgapps-3.96}/LICENSE +0 -0
  14. {nikgapps-3.95 → nikgapps-3.96}/MANIFEST.in +0 -0
  15. {nikgapps-3.95 → nikgapps-3.96}/NikGapps/__init__.py +0 -0
  16. {nikgapps-3.95 → nikgapps-3.96}/NikGapps/build/Build.py +0 -0
  17. {nikgapps-3.95 → nikgapps-3.96}/NikGapps/build/NikGappsManager.py +0 -0
  18. {nikgapps-3.95 → nikgapps-3.96}/NikGapps/build/NikGappsOverlays.py +0 -0
  19. {nikgapps-3.95 → nikgapps-3.96}/NikGapps/build/PackageConstants.py +0 -0
  20. {nikgapps-3.95 → nikgapps-3.96}/NikGapps/build/Release.py +0 -0
  21. {nikgapps-3.95 → nikgapps-3.96}/NikGapps/build/__init__.py +0 -0
  22. {nikgapps-3.95 → nikgapps-3.96}/NikGapps/build_config.py +0 -0
  23. {nikgapps-3.95 → nikgapps-3.96}/NikGapps/cache.py +0 -0
  24. {nikgapps-3.95 → nikgapps-3.96}/NikGapps/config/NikGappsConfig.py +0 -0
  25. {nikgapps-3.95 → nikgapps-3.96}/NikGapps/config/__init__.py +0 -0
  26. {nikgapps-3.95 → nikgapps-3.96}/NikGapps/config_upload.py +0 -0
  27. {nikgapps-3.95 → nikgapps-3.96}/NikGapps/copy_repos.py +0 -0
  28. {nikgapps-3.95 → nikgapps-3.96}/NikGapps/helper/AppSet.py +0 -0
  29. {nikgapps-3.95 → nikgapps-3.96}/NikGapps/helper/Args.py +0 -0
  30. {nikgapps-3.95 → nikgapps-3.96}/NikGapps/helper/Assets.py +0 -0
  31. {nikgapps-3.95 → nikgapps-3.96}/NikGapps/helper/Config.py +0 -0
  32. {nikgapps-3.95 → nikgapps-3.96}/NikGapps/helper/ConfigObj.py +0 -0
  33. {nikgapps-3.95 → nikgapps-3.96}/NikGapps/helper/Package.py +0 -0
  34. {nikgapps-3.95 → nikgapps-3.96}/NikGapps/helper/__init__.py +0 -0
  35. {nikgapps-3.95 → nikgapps-3.96}/NikGapps/helper/assets/addon.sh +0 -0
  36. {nikgapps-3.95 → nikgapps-3.96}/NikGapps/helper/assets/appsets.json +0 -0
  37. {nikgapps-3.95 → nikgapps-3.96}/NikGapps/helper/assets/changelogs.yaml +0 -0
  38. {nikgapps-3.95 → nikgapps-3.96}/NikGapps/helper/assets/customize.sh +0 -0
  39. {nikgapps-3.95 → nikgapps-3.96}/NikGapps/helper/assets/debloater.config +0 -0
  40. {nikgapps-3.95 → nikgapps-3.96}/NikGapps/helper/assets/functions.sh +0 -0
  41. {nikgapps-3.95 → nikgapps-3.96}/NikGapps/helper/assets/gofile.sh +0 -0
  42. {nikgapps-3.95 → nikgapps-3.96}/NikGapps/helper/assets/header.sh +0 -0
  43. {nikgapps-3.95 → nikgapps-3.96}/NikGapps/helper/assets/installer.sh +0 -0
  44. {nikgapps-3.95 → nikgapps-3.96}/NikGapps/helper/assets/magisk-update-binary.sh +0 -0
  45. {nikgapps-3.95 → nikgapps-3.96}/NikGapps/helper/assets/module.prop +0 -0
  46. {nikgapps-3.95 → nikgapps-3.96}/NikGapps/helper/assets/mount.sh +0 -0
  47. {nikgapps-3.95 → nikgapps-3.96}/NikGapps/helper/assets/mtg_mount.sh +0 -0
  48. {nikgapps-3.95 → nikgapps-3.96}/NikGapps/helper/assets/nikgapps.config +0 -0
  49. {nikgapps-3.95 → nikgapps-3.96}/NikGapps/helper/assets/nikgapps_functions.sh +0 -0
  50. {nikgapps-3.95 → nikgapps-3.96}/NikGapps/helper/assets/packages.json +0 -0
  51. {nikgapps-3.95 → nikgapps-3.96}/NikGapps/helper/assets/uninstaller.sh +0 -0
  52. {nikgapps-3.95 → nikgapps-3.96}/NikGapps/helper/assets/unmount.sh +0 -0
  53. {nikgapps-3.95 → nikgapps-3.96}/NikGapps/helper/compression/CompOps.py +0 -0
  54. {nikgapps-3.95 → nikgapps-3.96}/NikGapps/helper/compression/Export.py +0 -0
  55. {nikgapps-3.95 → nikgapps-3.96}/NikGapps/helper/compression/__init__.py +0 -0
  56. {nikgapps-3.95 → nikgapps-3.96}/NikGapps/helper/git/TestGit.py +0 -0
  57. {nikgapps-3.95 → nikgapps-3.96}/NikGapps/helper/git/__init__.py +0 -0
  58. {nikgapps-3.95 → nikgapps-3.96}/NikGapps/helper/overlay/ApkMetaInfo.py +0 -0
  59. {nikgapps-3.95 → nikgapps-3.96}/NikGapps/helper/overlay/Bool.py +0 -0
  60. {nikgapps-3.95 → nikgapps-3.96}/NikGapps/helper/overlay/Library.py +0 -0
  61. {nikgapps-3.95 → nikgapps-3.96}/NikGapps/helper/overlay/Manifest.py +0 -0
  62. {nikgapps-3.95 → nikgapps-3.96}/NikGapps/helper/overlay/Overlay.py +0 -0
  63. {nikgapps-3.95 → nikgapps-3.96}/NikGapps/helper/overlay/Resources.py +0 -0
  64. {nikgapps-3.95 → nikgapps-3.96}/NikGapps/helper/overlay/String.py +0 -0
  65. {nikgapps-3.95 → nikgapps-3.96}/NikGapps/helper/overlay/StringArray.py +0 -0
  66. {nikgapps-3.95 → nikgapps-3.96}/NikGapps/helper/overlay/__init__.py +0 -0
  67. {nikgapps-3.95 → nikgapps-3.96}/NikGapps/main.py +0 -0
  68. {nikgapps-3.95 → nikgapps-3.96}/NikGapps/test.py +0 -0
  69. {nikgapps-3.95 → nikgapps-3.96}/NikGapps.egg-info/SOURCES.txt +0 -0
  70. {nikgapps-3.95 → nikgapps-3.96}/NikGapps.egg-info/dependency_links.txt +0 -0
  71. {nikgapps-3.95 → nikgapps-3.96}/NikGapps.egg-info/entry_points.txt +0 -0
  72. {nikgapps-3.95 → nikgapps-3.96}/NikGapps.egg-info/requires.txt +0 -0
  73. {nikgapps-3.95 → nikgapps-3.96}/NikGapps.egg-info/top_level.txt +0 -0
  74. {nikgapps-3.95 → nikgapps-3.96}/README.md +0 -0
  75. {nikgapps-3.95 → nikgapps-3.96}/nikgapps_package_pipeline/__init__.py +0 -0
  76. {nikgapps-3.95 → nikgapps-3.96}/nikgapps_package_pipeline/__main__.py +0 -0
  77. {nikgapps-3.95 → nikgapps-3.96}/nikgapps_package_pipeline/apk.py +0 -0
  78. {nikgapps-3.95 → nikgapps-3.96}/nikgapps_package_pipeline/archive.py +0 -0
  79. {nikgapps-3.95 → nikgapps-3.96}/nikgapps_package_pipeline/compat.py +0 -0
  80. {nikgapps-3.95 → nikgapps-3.96}/nikgapps_package_pipeline/config.py +0 -0
  81. {nikgapps-3.95 → nikgapps-3.96}/nikgapps_package_pipeline/consumer.py +0 -0
  82. {nikgapps-3.95 → nikgapps-3.96}/nikgapps_package_pipeline/errors.py +0 -0
  83. {nikgapps-3.95 → nikgapps-3.96}/nikgapps_package_pipeline/gitlab.py +0 -0
  84. {nikgapps-3.95 → nikgapps-3.96}/nikgapps_package_pipeline/models.py +0 -0
  85. {nikgapps-3.95 → nikgapps-3.96}/nikgapps_package_pipeline/scanner.py +0 -0
  86. {nikgapps-3.95 → nikgapps-3.96}/nikgapps_package_pipeline/util.py +0 -0
  87. {nikgapps-3.95 → nikgapps-3.96}/nikgapps_registry/__init__.py +0 -0
  88. {nikgapps-3.95 → nikgapps-3.96}/nikgapps_registry/__main__.py +0 -0
  89. {nikgapps-3.95 → nikgapps-3.96}/nikgapps_registry/cli.py +0 -0
  90. {nikgapps-3.95 → nikgapps-3.96}/nikgapps_registry/service.py +0 -0
  91. {nikgapps-3.95 → nikgapps-3.96}/setup.cfg +0 -0
  92. {nikgapps-3.95 → nikgapps-3.96}/tests_package_pipeline/__init__.py +0 -0
  93. {nikgapps-3.95 → nikgapps-3.96}/tests_registry/__init__.py +0 -0
  94. {nikgapps-3.95 → nikgapps-3.96}/tests_registry/test_registry.py +0 -0
@@ -6,5 +6,6 @@
6
6
  "13": "37",
7
7
  "14": "37",
8
8
  "15": "37",
9
- "16": "38"
10
- }
9
+ "16": "38",
10
+ "17": "39"
11
+ }
@@ -12,8 +12,22 @@ from niklibrary.git.Git import Git
12
12
  from NikGapps.helper.overlay.Overlay import Overlay
13
13
 
14
14
 
15
+ ANDROID_PLATFORM_VERSIONS = {
16
+ # niklibrary 0.28 predates Android 17. Keep the compatibility entry here
17
+ # until the same mapping is available in the shared library.
18
+ "17": {"sdk": "37", "code": "CinnamonBun"},
19
+ }
20
+
21
+
22
+ def register_android_platform(android_version):
23
+ version = str(android_version)
24
+ if version not in Statics.android_versions and version in ANDROID_PLATFORM_VERSIONS:
25
+ Statics.android_versions[version] = ANDROID_PLATFORM_VERSIONS[version]
26
+
27
+
15
28
  def build_local_overlays(android_version, source_dir, output_dir):
16
29
  """Generate and compile overlays locally without cloning or pushing repos."""
30
+ register_android_platform(android_version)
17
31
  source_dir = os.path.abspath(source_dir)
18
32
  output_dir = os.path.abspath(output_dir)
19
33
  os.makedirs(source_dir, exist_ok=True)
@@ -40,6 +54,7 @@ def overlay_control():
40
54
  android_versions = args.get_android_versions()
41
55
 
42
56
  for android_version in android_versions:
57
+ register_android_platform(android_version)
43
58
  repo_name = Statics.get_overlay_source_repo(android_version)
44
59
  repo_dir = Statics.get_overlay_source_directory(android_version)
45
60
  branch = "main"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: NikGapps
3
- Version: 3.95
3
+ Version: 3.96
4
4
  Summary: A short description of your project
5
5
  Home-page: https://github.com/nikgapps/project
6
6
  Author: Nikhil Menghani
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: NikGapps
3
- Version: 3.95
3
+ Version: 3.96
4
4
  Summary: A short description of your project
5
5
  Home-page: https://github.com/nikgapps/project
6
6
  Author: Nikhil Menghani
@@ -168,6 +168,11 @@ def build_parser() -> argparse.ArgumentParser:
168
168
  upgrade.add_argument("--baseline-firmware", type=Path, required=True)
169
169
  upgrade.add_argument("--target-firmware", type=Path, required=True)
170
170
  upgrade.add_argument("--output", type=Path, required=True)
171
+ upgrade.add_argument("--carry-forward-missing", action="store_true",
172
+ help="copy the complete template first, replace everything available from the target image, and audit retained files")
173
+ upgrade.add_argument("--android-version", help="build overlays for this Android version after upgrading")
174
+ upgrade.add_argument("--overlay-source", type=Path, help="generated overlay source workspace")
175
+ upgrade.add_argument("--overlay-output", type=Path, help="compiled overlay APK output")
171
176
  return parser
172
177
 
173
178
 
@@ -336,9 +341,22 @@ def main(argv: Sequence[str] | None = None) -> int:
336
341
  if args.command == "reset-registry":
337
342
  return _reset_registry(args)
338
343
  if args.command == "upgrade":
344
+ overlay_values = (args.android_version, args.overlay_source, args.overlay_output)
345
+ if any(overlay_values) and not all(overlay_values):
346
+ raise PipelineError("--android-version, --overlay-source, and --overlay-output must be supplied together")
347
+ overlays = []
348
+ if all(overlay_values):
349
+ from NikGapps.overlay_control import build_local_overlays
350
+ overlays = build_local_overlays(args.android_version, args.overlay_source, args.overlay_output)
339
351
  report = StableTreeUpgrader().upgrade(
340
- args.template, args.baseline_firmware, args.target_firmware, args.output
352
+ args.template, args.baseline_firmware, args.target_firmware, args.output,
353
+ carry_forward_missing=args.carry_forward_missing,
341
354
  )
355
+ if overlays:
356
+ report.built_overlays = [str(Path(path).resolve().relative_to(args.overlay_output.resolve())).replace("\\", "/")
357
+ for path in overlays]
358
+ (args.output / "upgrade-report.json").write_text(
359
+ json.dumps(report.as_dict(), indent=2) + "\n", encoding="utf-8")
342
360
  print(json.dumps(report.as_dict(), indent=2))
343
361
  return 0
344
362
  return _migrate(args)
@@ -377,13 +377,12 @@ class MigrationPipeline:
377
377
  "with its required support files"
378
378
  )
379
379
  gms.dependencies = [
380
- {"id": "gms_core_support_common"},
381
380
  {
382
- "id": "gms_core_support_standard",
381
+ "id": "gms_core_extra_files",
383
382
  "when": {"appSet": "core"},
384
383
  },
385
384
  {
386
- "id": "gms_core_support_go",
385
+ "id": "gms_core_extra_files_go",
387
386
  "when": {"appSet": "core_go"},
388
387
  },
389
388
  ]
@@ -433,40 +432,28 @@ class MigrationPipeline:
433
432
  normal_source = sources["Core/ExtraFiles"]
434
433
  go_source = sources["CoreGo/ExtraFilesGo"]
435
434
  scanner = LegacyRepositoryScanner()
436
- normal = {item.path: item for item in scanner.payload(normal_source)}
437
- go = {item.path: item for item in scanner.payload(go_source)}
438
- common_paths = {
439
- path for path in normal.keys() & go.keys()
440
- if normal[path].sha256 == go[path].sha256
441
- }
442
435
  groups = [
443
436
  (
444
- "gms_core_support_common",
445
- "GmsCore common support files",
446
- [normal[path] for path in sorted(common_paths)],
447
- ["Core/ExtraFiles", "CoreGo/ExtraFilesGo"],
448
- ["Core", "CoreGo"],
449
- ),
450
- (
451
- "gms_core_support_standard",
452
- "GmsCore standard support files",
453
- [normal[path] for path in sorted(normal.keys() - common_paths)],
437
+ "gms_core_extra_files",
438
+ "ExtraFiles",
439
+ scanner.payload(normal_source),
454
440
  ["Core/ExtraFiles"],
455
441
  ["Core"],
442
+ normal_source,
456
443
  ),
457
444
  (
458
- "gms_core_support_go",
459
- "GmsCore Go support files",
460
- [go[path] for path in sorted(go.keys() - common_paths)],
445
+ "gms_core_extra_files_go",
446
+ "ExtraFilesGo",
447
+ scanner.payload(go_source),
461
448
  ["CoreGo/ExtraFilesGo"],
462
449
  ["CoreGo"],
450
+ go_source,
463
451
  ),
464
452
  ]
465
453
  result: list[BuiltPackage] = []
466
- for package_id, name, files, source_names, appset_names in groups:
454
+ for package_id, name, files, source_names, appset_names, support_source in groups:
467
455
  if not files:
468
456
  continue
469
- support_source = normal_source if "go" not in package_id else go_source
470
457
  payload_digest = content_digest((item.path, item.sha256) for item in files)
471
458
  manifest = _manifest(package_id, support_source, files, None, None, [],
472
459
  payload_digest, config)
@@ -588,32 +575,29 @@ class MigrationPipeline:
588
575
  }
589
576
  except (OSError, json.JSONDecodeError) as exc:
590
577
  raise PipelineError(f"Cannot merge existing AppSets {appsets_path}: {exc}") from exc
591
- generated_appsets = [
592
- {
578
+ generated_appsets = []
579
+ for name, package_ids in sorted(appsets.items()):
580
+ selected = list(dict.fromkeys(package_ids))
581
+ resolved = MigrationPipeline._resolved_packages(name, selected, packages)
582
+ legacy_names = {}
583
+ for package_id in resolved:
584
+ legacy_names[package_id] = next(
585
+ (
586
+ membership.split("/", 1)[1]
587
+ for package in packages
588
+ if package.package_id == package_id
589
+ for membership in package.source_names
590
+ if membership.split("/", 1)[0] == name
591
+ ),
592
+ package_id,
593
+ )
594
+ generated_appsets.append({
593
595
  "id": stable_id(name),
594
596
  "name": name,
595
- "packages": list(dict.fromkeys(package_ids)),
596
- "resolvedPackages": MigrationPipeline._resolved_packages(
597
- name,
598
- list(dict.fromkeys(package_ids)),
599
- packages,
600
- ),
601
- "legacyPackageNames": {
602
- package_id: next(
603
- (
604
- membership.split("/", 1)[1]
605
- for package in packages
606
- if package.package_id == package_id
607
- for membership in package.source_names
608
- if membership.split("/", 1)[0] == name
609
- ),
610
- package_id,
611
- )
612
- for package_id in dict.fromkeys(package_ids)
613
- },
614
- }
615
- for name, package_ids in sorted(appsets.items())
616
- ]
597
+ "packages": selected,
598
+ "resolvedPackages": resolved,
599
+ "legacyPackageNames": legacy_names,
600
+ })
617
601
  existing_appsets.update({item["id"]: item for item in generated_appsets})
618
602
  appsets_path.write_bytes(json_data({
619
603
  "schemaVersion": 1,
@@ -9,11 +9,24 @@ from typing import Any
9
9
  from .errors import PipelineError
10
10
 
11
11
 
12
+ def _is_profile(path: Path) -> bool:
13
+ return path.suffix.casefold() == ".prof"
14
+
15
+
16
+ def _ignore_profiles(_directory: str, names: list[str]) -> set[str]:
17
+ return {name for name in names if Path(name).suffix.casefold() == ".prof"}
18
+
19
+
12
20
  @dataclass
13
21
  class UpgradeReport:
14
22
  upgraded_packages: list[str] = field(default_factory=list)
15
23
  file_only_packages: list[str] = field(default_factory=list)
16
24
  unavailable_packages: list[str] = field(default_factory=list)
25
+ carried_forward_packages: list[str] = field(default_factory=list)
26
+ updated_dependencies: list[str] = field(default_factory=list)
27
+ carried_forward_files: list[str] = field(default_factory=list)
28
+ ambiguous_dependencies: list[str] = field(default_factory=list)
29
+ built_overlays: list[str] = field(default_factory=list)
17
30
  copied_files: int = 0
18
31
 
19
32
  def as_dict(self) -> dict[str, Any]:
@@ -22,6 +35,11 @@ class UpgradeReport:
22
35
  "upgradedPackages": sorted(self.upgraded_packages),
23
36
  "fileOnlyPackages": sorted(self.file_only_packages),
24
37
  "unavailablePackages": sorted(self.unavailable_packages),
38
+ "carriedForwardPackages": sorted(self.carried_forward_packages),
39
+ "updatedDependencies": sorted(self.updated_dependencies),
40
+ "carriedForwardFiles": sorted(self.carried_forward_files),
41
+ "ambiguousDependencies": sorted(self.ambiguous_dependencies),
42
+ "builtOverlays": sorted(self.built_overlays),
25
43
  "copiedFiles": self.copied_files,
26
44
  }
27
45
 
@@ -95,7 +113,8 @@ def _copy_firmware_directory(
95
113
  source_directory = source_apk.parent
96
114
  count = 0
97
115
  for source in sorted(source_directory.rglob("*")):
98
- if not source.is_file() or any(part.endswith("_extracted") for part in source.parts):
116
+ if (not source.is_file() or _is_profile(source)
117
+ or any(part.endswith("_extracted") for part in source.parts)):
99
118
  continue
100
119
  source_location = Path(location).parent / source.relative_to(source_directory)
101
120
  destination = package_output / encode_builder_path(source_location.as_posix())
@@ -114,6 +133,8 @@ class StableTreeUpgrader:
114
133
  baseline_firmware: Path,
115
134
  target_firmware: Path,
116
135
  output: Path,
136
+ *,
137
+ carry_forward_missing: bool = False,
117
138
  ) -> UpgradeReport:
118
139
  template_root = template_root.resolve()
119
140
  baseline_firmware = baseline_firmware.resolve()
@@ -128,6 +149,18 @@ class StableTreeUpgrader:
128
149
 
129
150
  baseline = _load_inventory(baseline_firmware)
130
151
  target = _load_inventory(target_firmware)
152
+ target_files: dict[str, list[Path]] = {}
153
+ for candidate in sorted(target_firmware.rglob("*")):
154
+ if not candidate.is_file() or candidate.name in {"mustang.json", "all_files.txt"}:
155
+ continue
156
+ if any(part.endswith("_extracted") or part == ".git" for part in candidate.parts):
157
+ continue
158
+ relative = candidate.relative_to(target_firmware).as_posix()
159
+ try:
160
+ builder_path = encode_builder_path(relative).as_posix()
161
+ except PipelineError:
162
+ continue
163
+ target_files.setdefault(builder_path, []).append(candidate)
131
164
  report = UpgradeReport()
132
165
  output.mkdir(parents=True)
133
166
  for metadata_name in (".gitattributes", "README.md"):
@@ -141,10 +174,18 @@ class StableTreeUpgrader:
141
174
  destination = output / appset.name / package.name
142
175
  # Preserve the complete AppSet/package skeleton even when a
143
176
  # package is not shipped by the target firmware image.
144
- destination.mkdir(parents=True, exist_ok=True)
177
+ if carry_forward_missing:
178
+ shutil.copytree(package, destination, ignore=_ignore_profiles)
179
+ else:
180
+ destination.mkdir(parents=True, exist_ok=True)
145
181
  template_apks = sorted(package.rglob("*.apk"))
182
+ template_dependencies = sorted(
183
+ path for path in package.rglob("*")
184
+ if path.is_file() and path.suffix.casefold() not in {".apk", ".prof"}
185
+ )
146
186
  copied = 0
147
187
  seen_targets: set[str] = set()
188
+ replaced_apk_parents: set[Path] = set()
148
189
  for template_apk in template_apks:
149
190
  match = _record_for_template(template_apk, package, baseline)
150
191
  if not match:
@@ -157,36 +198,48 @@ class StableTreeUpgrader:
157
198
  if location in seen_targets:
158
199
  continue
159
200
  seen_targets.add(location)
201
+ if carry_forward_missing:
202
+ old_parent = destination / template_apk.parent.relative_to(package)
203
+ if old_parent not in replaced_apk_parents:
204
+ shutil.rmtree(old_parent)
205
+ replaced_apk_parents.add(old_parent)
160
206
  copied += _copy_firmware_directory(target_firmware, target_record, destination)
161
207
 
208
+ dependency_copied = 0
209
+ for template_file in template_dependencies:
210
+ relative = template_file.relative_to(package)
211
+ builder_path = relative.as_posix()
212
+ matches = target_files.get(builder_path, [])
213
+ audit_path = f"{membership}/{builder_path}"
214
+ if len(matches) == 1:
215
+ destination_file = destination / relative
216
+ destination_file.parent.mkdir(parents=True, exist_ok=True)
217
+ shutil.copy2(matches[0], destination_file)
218
+ dependency_copied += 1
219
+ report.copied_files += 1
220
+ report.updated_dependencies.append(audit_path)
221
+ elif len(matches) > 1:
222
+ report.ambiguous_dependencies.append(audit_path)
223
+ if carry_forward_missing and destination.joinpath(relative).is_file():
224
+ report.carried_forward_files.append(audit_path)
225
+ elif carry_forward_missing and destination.joinpath(relative).is_file():
226
+ report.carried_forward_files.append(audit_path)
227
+
162
228
  if copied:
163
229
  report.upgraded_packages.append(membership)
164
230
  report.copied_files += copied
231
+ if carry_forward_missing and any(
232
+ destination.joinpath(apk.relative_to(package)).is_file() for apk in template_apks
233
+ ):
234
+ report.carried_forward_packages.append(membership)
165
235
  continue
166
236
 
167
- # File-only packages are retained only when the same path is
168
- # present in Android 17. Nothing is ever copied from Android 16.
169
- file_only_copied = 0
170
- for template_file in sorted(package.rglob("*")):
171
- if not template_file.is_file() or template_file.suffix == ".apk":
172
- continue
173
- suffix = decode_builder_path(template_file.relative_to(package))
174
- matches = [
175
- candidate for candidate in target_firmware.rglob(template_file.name)
176
- if candidate.is_file()
177
- and candidate.as_posix().endswith("/" + suffix)
178
- and "_extracted" not in candidate.as_posix()
179
- ]
180
- if len(matches) == 1:
181
- destination_file = destination / template_file.relative_to(package)
182
- destination_file.parent.mkdir(parents=True, exist_ok=True)
183
- shutil.copy2(matches[0], destination_file)
184
- report.copied_files += 1
185
- file_only_copied += 1
186
- if file_only_copied:
237
+ if dependency_copied:
187
238
  report.file_only_packages.append(membership)
188
239
  else:
189
240
  report.unavailable_packages.append(membership)
241
+ if carry_forward_missing and any(destination.rglob("*")):
242
+ report.carried_forward_packages.append(membership)
190
243
 
191
244
  (output / "upgrade-report.json").write_text(
192
245
  json.dumps(report.as_dict(), indent=2) + "\n", encoding="utf-8"
@@ -2,7 +2,7 @@ from setuptools import setup, find_packages
2
2
 
3
3
  setup(
4
4
  name="NikGapps",
5
- version="3.95",
5
+ version="3.96",
6
6
  packages=find_packages(),
7
7
  include_package_data=True,
8
8
  package_data={
@@ -57,21 +57,17 @@ class ConsumerTests(unittest.TestCase):
57
57
  "gms_core": artifact(
58
58
  root, "gms", "priv-app/Gms/Gms.apk", b"apk"
59
59
  ),
60
- "gms_core_support_common": artifact(
61
- root, "common", "etc/permissions/common.xml", b"common"
60
+ "gms_core_extra_files": artifact(
61
+ root, "extra", "etc/permissions/common.xml", b"common"
62
62
  ),
63
- "gms_core_support_standard": artifact(
64
- root, "standard", "etc/sysconfig/variant.xml", b"standard"
65
- ),
66
- "gms_core_support_go": artifact(
63
+ "gms_core_extra_files_go": artifact(
67
64
  root, "go", "etc/sysconfig/go.xml", b"go"
68
65
  ),
69
66
  }
70
67
  memberships = {
71
68
  "gms_core": ["Core/GmsCore", "CoreGo/GmsCore"],
72
- "gms_core_support_common": ["Core/ExtraFiles", "CoreGo/ExtraFilesGo"],
73
- "gms_core_support_standard": ["Core/ExtraFiles"],
74
- "gms_core_support_go": ["CoreGo/ExtraFilesGo"],
69
+ "gms_core_extra_files": ["Core/ExtraFiles"],
70
+ "gms_core_extra_files_go": ["CoreGo/ExtraFilesGo"],
75
71
  }
76
72
  packages = []
77
73
  for package_id, artifact_data in artifacts.items():
@@ -83,7 +79,7 @@ class ConsumerTests(unittest.TestCase):
83
79
  "versions": {
84
80
  key: {
85
81
  "android": {"minApi": 29, "maxApi": None},
86
- "architectures": [] if "support" in package_id else ["arm64-v8a"],
82
+ "architectures": [] if "extra_files" in package_id else ["arm64-v8a"],
87
83
  "artifact": artifact_data,
88
84
  }
89
85
  },
@@ -98,10 +94,12 @@ class ConsumerTests(unittest.TestCase):
98
94
  "packages": ["gms_core"],
99
95
  "resolvedPackages": [
100
96
  "gms_core",
101
- "gms_core_support_common",
102
- "gms_core_support_standard",
97
+ "gms_core_extra_files",
103
98
  ],
104
- "legacyPackageNames": {"gms_core": "GmsCore"},
99
+ "legacyPackageNames": {
100
+ "gms_core": "GmsCore",
101
+ "gms_core_extra_files": "ExtraFiles",
102
+ },
105
103
  }]
106
104
  }), encoding="utf-8")
107
105
  requested = [SimpleNamespace(
@@ -120,9 +118,6 @@ class ConsumerTests(unittest.TestCase):
120
118
  self.assertTrue(
121
119
  (source / "Core" / "ExtraFiles" / "___etc___permissions" / "common.xml").is_file()
122
120
  )
123
- self.assertTrue(
124
- (source / "Core" / "ExtraFiles" / "___etc___sysconfig" / "variant.xml").is_file()
125
- )
126
121
  self.assertFalse((source / "CoreGo").exists())
127
122
 
128
123
  def test_checksum_mismatch_is_rejected(self) -> None:
@@ -10,7 +10,8 @@ sys.path.insert(0, str(Path(__file__).resolve().parents[2] / "niklibrary"))
10
10
  sys.path.insert(0, str(Path(__file__).resolve().parents[2] / "nikassets"))
11
11
 
12
12
  try:
13
- from NikGapps.overlay_control import build_local_overlays
13
+ from NikGapps.overlay_control import build_local_overlays, register_android_platform
14
+ from niklibrary.helper.Statics import Statics
14
15
  except ModuleNotFoundError:
15
16
  build_local_overlays = None
16
17
 
@@ -26,6 +27,17 @@ class FakeOverlay:
26
27
 
27
28
  @unittest.skipIf(build_local_overlays is None, "optional NikGapps build dependencies unavailable")
28
29
  class OverlayControlTests(unittest.TestCase):
30
+ def test_android_17_platform_compatibility(self) -> None:
31
+ original = Statics.android_versions.pop("17", None)
32
+ try:
33
+ register_android_platform("17")
34
+ self.assertEqual(Statics.get_android_sdk("17"), "37")
35
+ self.assertEqual(Statics.get_android_code("17"), "CinnamonBun")
36
+ finally:
37
+ Statics.android_versions.pop("17", None)
38
+ if original is not None:
39
+ Statics.android_versions["17"] = original
40
+
29
41
  def test_android_17_local_overlay_output(self) -> None:
30
42
  with tempfile.TemporaryDirectory() as temporary:
31
43
  root = Path(temporary)
@@ -293,9 +293,8 @@ class MigrationTests(unittest.TestCase):
293
293
  "example",
294
294
  "shared",
295
295
  "gms_core",
296
- "gms_core_support_common",
297
- "gms_core_support_standard",
298
- "gms_core_support_go",
296
+ "gms_core_extra_files",
297
+ "gms_core_extra_files_go",
299
298
  },
300
299
  )
301
300
  shared = next(item for item in packages if item.package_id == "shared")
@@ -306,10 +305,16 @@ class MigrationTests(unittest.TestCase):
306
305
  )
307
306
  support = next(
308
307
  item for item in packages
309
- if item.package_id == "gms_core_support_common"
308
+ if item.package_id == "gms_core_extra_files"
310
309
  )
311
310
  self.assertIsNone(support.primary_apk)
312
311
  self.assertTrue(support.internal)
312
+ with zipfile.ZipFile(support.artifact_path) as support_archive:
313
+ support_installer = support_archive.read("installer.sh").decode()
314
+ support_uninstaller = support_archive.read("uninstaller.sh").decode()
315
+ self.assertIn("com.google.android.maps.xml", support_installer)
316
+ self.assertIn("uninstall_package() {", support_uninstaller)
317
+ self.assertIn("___etc___permissions/common.xml", support_uninstaller)
313
318
  self.assertRegex(
314
319
  support.version_key,
315
320
  r"^content-[0-9a-f]{12}-arm64-v8a$",
@@ -344,9 +349,9 @@ class MigrationTests(unittest.TestCase):
344
349
  appsets = json.loads((output / "metadata" / "appsets.json").read_text())
345
350
  core_go = next(item for item in appsets["appSets"] if item["name"] == "CoreGo")
346
351
  self.assertEqual(core_go["packages"], ["gms_core", "shared"])
347
- self.assertIn("gms_core_support_common", core_go["resolvedPackages"])
348
- self.assertIn("gms_core_support_go", core_go["resolvedPackages"])
349
- self.assertNotIn("gms_core_support_standard", core_go["resolvedPackages"])
352
+ self.assertIn("gms_core_extra_files_go", core_go["resolvedPackages"])
353
+ self.assertNotIn("gms_core_extra_files", core_go["resolvedPackages"])
354
+ self.assertEqual(core_go["legacyPackageNames"]["gms_core_extra_files_go"], "ExtraFilesGo")
350
355
  compatible = Path(temporary) / "compatible"
351
356
  LegacyCompatibilityExporter().export(packages, compatible)
352
357
  self.assertTrue(
@@ -43,7 +43,7 @@ class StableTreeUpgradeTests(unittest.TestCase):
43
43
 
44
44
  upgraded = output / "Set" / "App" / "___priv-app___AppNew_17"
45
45
  self.assertEqual((upgraded / "AppNew_17.apk").read_bytes(), b"new")
46
- self.assertEqual((upgraded / "AppNew_17.apk.prof").read_bytes(), b"profile")
46
+ self.assertFalse((upgraded / "AppNew_17.apk.prof").exists())
47
47
  self.assertEqual(report.upgraded_packages, ["Set/App"])
48
48
  self.assertFalse((output / "Set" / "App" / "___priv-app___AppOld").exists())
49
49
 
@@ -59,6 +59,43 @@ class StableTreeUpgradeTests(unittest.TestCase):
59
59
  root / "template", root / "old", root / "new", root / "output"
60
60
  )
61
61
 
62
+ def test_complete_upgrade_replaces_dependencies_and_audits_carry_forward(self) -> None:
63
+ with tempfile.TemporaryDirectory() as temporary:
64
+ root = Path(temporary)
65
+ template = root / "16_stable"
66
+ old = root / "16_mustang"
67
+ new = root / "17_mustang"
68
+ output = root / "17_stable"
69
+ old_location = "product/priv-app/AppOld/AppOld.apk"
70
+ new_location = "product/priv-app/AppNew/AppNew.apk"
71
+ self.inventory(old, "com.example", old_location)
72
+ self.inventory(new, "com.example", new_location)
73
+ (old / old_location).parent.mkdir(parents=True)
74
+ (old / old_location).write_bytes(b"old")
75
+ (new / new_location).parent.mkdir(parents=True)
76
+ (new / new_location).write_bytes(b"new")
77
+ (new / "product/etc/permissions/updated.xml").parent.mkdir(parents=True)
78
+ (new / "product/etc/permissions/updated.xml").write_bytes(b"17")
79
+ package = template / "Set" / "App"
80
+ (package / "___priv-app___AppOld").mkdir(parents=True)
81
+ (package / "___priv-app___AppOld/AppOld.apk").write_bytes(b"template")
82
+ (package / "___etc___permissions").mkdir(parents=True)
83
+ (package / "___etc___permissions/updated.xml").write_bytes(b"16")
84
+ (package / "___etc___permissions/retained.xml").write_bytes(b"retain")
85
+ (package / "___priv-app___AppOld/AppOld.apk.prof").write_bytes(b"old profile")
86
+
87
+ report = StableTreeUpgrader().upgrade(
88
+ template, old, new, output, carry_forward_missing=True)
89
+
90
+ upgraded = output / "Set/App"
91
+ self.assertFalse((upgraded / "___priv-app___AppOld").exists())
92
+ self.assertEqual((upgraded / "___priv-app___AppNew/AppNew.apk").read_bytes(), b"new")
93
+ self.assertEqual((upgraded / "___etc___permissions/updated.xml").read_bytes(), b"17")
94
+ self.assertEqual((upgraded / "___etc___permissions/retained.xml").read_bytes(), b"retain")
95
+ self.assertFalse(any(upgraded.rglob("*.prof")))
96
+ self.assertIn("Set/App/___etc___permissions/updated.xml", report.updated_dependencies)
97
+ self.assertIn("Set/App/___etc___permissions/retained.xml", report.carried_forward_files)
98
+
62
99
 
63
100
  if __name__ == "__main__":
64
101
  unittest.main()
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes