NikGapps 3.94__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.
- {nikgapps-3.94 → nikgapps-3.96}/NikGapps/build/Build.py +25 -0
- {nikgapps-3.94 → nikgapps-3.96}/NikGapps/build_config.py +10 -3
- {nikgapps-3.94 → nikgapps-3.96}/NikGapps/helper/Args.py +10 -0
- {nikgapps-3.94 → nikgapps-3.96}/NikGapps/helper/Config.py +18 -0
- {nikgapps-3.94 → nikgapps-3.96}/NikGapps/helper/assets/config_versions.json +3 -2
- {nikgapps-3.94 → nikgapps-3.96}/NikGapps/main.py +41 -7
- {nikgapps-3.94 → nikgapps-3.96}/NikGapps/overlay_control.py +53 -1
- {nikgapps-3.94 → nikgapps-3.96/NikGapps.egg-info}/PKG-INFO +1 -1
- {nikgapps-3.94 → nikgapps-3.96}/NikGapps.egg-info/SOURCES.txt +27 -1
- {nikgapps-3.94 → nikgapps-3.96}/NikGapps.egg-info/entry_points.txt +1 -0
- nikgapps-3.96/NikGapps.egg-info/top_level.txt +5 -0
- {nikgapps-3.94/NikGapps.egg-info → nikgapps-3.96}/PKG-INFO +1 -1
- nikgapps-3.96/nikgapps_package_pipeline/__init__.py +3 -0
- nikgapps-3.96/nikgapps_package_pipeline/__main__.py +3 -0
- nikgapps-3.96/nikgapps_package_pipeline/apk.py +136 -0
- nikgapps-3.96/nikgapps_package_pipeline/archive.py +176 -0
- nikgapps-3.96/nikgapps_package_pipeline/cli.py +369 -0
- nikgapps-3.96/nikgapps_package_pipeline/compat.py +44 -0
- nikgapps-3.96/nikgapps_package_pipeline/config.py +118 -0
- nikgapps-3.96/nikgapps_package_pipeline/consumer.py +318 -0
- nikgapps-3.96/nikgapps_package_pipeline/errors.py +2 -0
- nikgapps-3.96/nikgapps_package_pipeline/gitlab.py +267 -0
- nikgapps-3.96/nikgapps_package_pipeline/models.py +102 -0
- nikgapps-3.96/nikgapps_package_pipeline/pipeline.py +673 -0
- nikgapps-3.96/nikgapps_package_pipeline/scanner.py +103 -0
- nikgapps-3.96/nikgapps_package_pipeline/upgrade.py +247 -0
- nikgapps-3.96/nikgapps_package_pipeline/util.py +83 -0
- nikgapps-3.96/nikgapps_registry/__init__.py +5 -0
- nikgapps-3.96/nikgapps_registry/__main__.py +3 -0
- nikgapps-3.96/nikgapps_registry/cli.py +98 -0
- nikgapps-3.96/nikgapps_registry/service.py +197 -0
- {nikgapps-3.94 → nikgapps-3.96}/setup.py +3 -2
- nikgapps-3.96/tests_package_pipeline/__init__.py +1 -0
- nikgapps-3.96/tests_package_pipeline/test_consumer.py +153 -0
- nikgapps-3.96/tests_package_pipeline/test_overlay_control.py +63 -0
- nikgapps-3.96/tests_package_pipeline/test_pipeline.py +509 -0
- nikgapps-3.96/tests_package_pipeline/test_upgrade.py +101 -0
- nikgapps-3.96/tests_registry/__init__.py +0 -0
- nikgapps-3.96/tests_registry/test_registry.py +60 -0
- nikgapps-3.94/NikGapps.egg-info/top_level.txt +0 -1
- {nikgapps-3.94 → nikgapps-3.96}/LICENSE +0 -0
- {nikgapps-3.94 → nikgapps-3.96}/MANIFEST.in +0 -0
- {nikgapps-3.94 → nikgapps-3.96}/NikGapps/__init__.py +0 -0
- {nikgapps-3.94 → nikgapps-3.96}/NikGapps/build/NikGappsManager.py +0 -0
- {nikgapps-3.94 → nikgapps-3.96}/NikGapps/build/NikGappsOverlays.py +0 -0
- {nikgapps-3.94 → nikgapps-3.96}/NikGapps/build/PackageConstants.py +0 -0
- {nikgapps-3.94 → nikgapps-3.96}/NikGapps/build/Release.py +0 -0
- {nikgapps-3.94 → nikgapps-3.96}/NikGapps/build/__init__.py +0 -0
- {nikgapps-3.94 → nikgapps-3.96}/NikGapps/cache.py +0 -0
- {nikgapps-3.94 → nikgapps-3.96}/NikGapps/config/NikGappsConfig.py +0 -0
- {nikgapps-3.94 → nikgapps-3.96}/NikGapps/config/__init__.py +0 -0
- {nikgapps-3.94 → nikgapps-3.96}/NikGapps/config_upload.py +0 -0
- {nikgapps-3.94 → nikgapps-3.96}/NikGapps/copy_repos.py +0 -0
- {nikgapps-3.94 → nikgapps-3.96}/NikGapps/helper/AppSet.py +0 -0
- {nikgapps-3.94 → nikgapps-3.96}/NikGapps/helper/Assets.py +0 -0
- {nikgapps-3.94 → nikgapps-3.96}/NikGapps/helper/ConfigObj.py +0 -0
- {nikgapps-3.94 → nikgapps-3.96}/NikGapps/helper/Package.py +0 -0
- {nikgapps-3.94 → nikgapps-3.96}/NikGapps/helper/__init__.py +0 -0
- {nikgapps-3.94 → nikgapps-3.96}/NikGapps/helper/assets/addon.sh +0 -0
- {nikgapps-3.94 → nikgapps-3.96}/NikGapps/helper/assets/appsets.json +0 -0
- {nikgapps-3.94 → nikgapps-3.96}/NikGapps/helper/assets/changelogs.yaml +0 -0
- {nikgapps-3.94 → nikgapps-3.96}/NikGapps/helper/assets/customize.sh +0 -0
- {nikgapps-3.94 → nikgapps-3.96}/NikGapps/helper/assets/debloater.config +0 -0
- {nikgapps-3.94 → nikgapps-3.96}/NikGapps/helper/assets/functions.sh +0 -0
- {nikgapps-3.94 → nikgapps-3.96}/NikGapps/helper/assets/gofile.sh +0 -0
- {nikgapps-3.94 → nikgapps-3.96}/NikGapps/helper/assets/header.sh +0 -0
- {nikgapps-3.94 → nikgapps-3.96}/NikGapps/helper/assets/installer.sh +0 -0
- {nikgapps-3.94 → nikgapps-3.96}/NikGapps/helper/assets/magisk-update-binary.sh +0 -0
- {nikgapps-3.94 → nikgapps-3.96}/NikGapps/helper/assets/module.prop +0 -0
- {nikgapps-3.94 → nikgapps-3.96}/NikGapps/helper/assets/mount.sh +0 -0
- {nikgapps-3.94 → nikgapps-3.96}/NikGapps/helper/assets/mtg_mount.sh +0 -0
- {nikgapps-3.94 → nikgapps-3.96}/NikGapps/helper/assets/nikgapps.config +0 -0
- {nikgapps-3.94 → nikgapps-3.96}/NikGapps/helper/assets/nikgapps_functions.sh +0 -0
- {nikgapps-3.94 → nikgapps-3.96}/NikGapps/helper/assets/packages.json +0 -0
- {nikgapps-3.94 → nikgapps-3.96}/NikGapps/helper/assets/uninstaller.sh +0 -0
- {nikgapps-3.94 → nikgapps-3.96}/NikGapps/helper/assets/unmount.sh +0 -0
- {nikgapps-3.94 → nikgapps-3.96}/NikGapps/helper/compression/CompOps.py +0 -0
- {nikgapps-3.94 → nikgapps-3.96}/NikGapps/helper/compression/Export.py +0 -0
- {nikgapps-3.94 → nikgapps-3.96}/NikGapps/helper/compression/__init__.py +0 -0
- {nikgapps-3.94 → nikgapps-3.96}/NikGapps/helper/git/TestGit.py +0 -0
- {nikgapps-3.94 → nikgapps-3.96}/NikGapps/helper/git/__init__.py +0 -0
- {nikgapps-3.94 → nikgapps-3.96}/NikGapps/helper/overlay/ApkMetaInfo.py +0 -0
- {nikgapps-3.94 → nikgapps-3.96}/NikGapps/helper/overlay/Bool.py +0 -0
- {nikgapps-3.94 → nikgapps-3.96}/NikGapps/helper/overlay/Library.py +0 -0
- {nikgapps-3.94 → nikgapps-3.96}/NikGapps/helper/overlay/Manifest.py +0 -0
- {nikgapps-3.94 → nikgapps-3.96}/NikGapps/helper/overlay/Overlay.py +0 -0
- {nikgapps-3.94 → nikgapps-3.96}/NikGapps/helper/overlay/Resources.py +0 -0
- {nikgapps-3.94 → nikgapps-3.96}/NikGapps/helper/overlay/String.py +0 -0
- {nikgapps-3.94 → nikgapps-3.96}/NikGapps/helper/overlay/StringArray.py +0 -0
- {nikgapps-3.94 → nikgapps-3.96}/NikGapps/helper/overlay/__init__.py +0 -0
- {nikgapps-3.94 → nikgapps-3.96}/NikGapps/test.py +0 -0
- {nikgapps-3.94 → nikgapps-3.96}/NikGapps.egg-info/dependency_links.txt +0 -0
- {nikgapps-3.94 → nikgapps-3.96}/NikGapps.egg-info/requires.txt +0 -0
- {nikgapps-3.94 → nikgapps-3.96}/README.md +0 -0
- {nikgapps-3.94 → nikgapps-3.96}/setup.cfg +0 -0
|
@@ -22,6 +22,31 @@ class Build:
|
|
|
22
22
|
if cached:
|
|
23
23
|
return app_set_build_list
|
|
24
24
|
building = T()
|
|
25
|
+
if Config.PACKAGE_SOURCE == "registry":
|
|
26
|
+
print("[Registry] Package source mode is enabled; skipping the legacy APK repository")
|
|
27
|
+
from nikgapps_package_pipeline.consumer import (
|
|
28
|
+
CatalogPackageSource,
|
|
29
|
+
channel_overrides_from_env,
|
|
30
|
+
)
|
|
31
|
+
registry_source = CatalogPackageSource(
|
|
32
|
+
Config.PACKAGE_CATALOG_URL,
|
|
33
|
+
Config.PACKAGE_APPSETS_URL,
|
|
34
|
+
Path(Config.PACKAGE_CACHE),
|
|
35
|
+
channel=Config.PACKAGE_CHANNEL,
|
|
36
|
+
channel_overrides=channel_overrides_from_env(Config.PACKAGE_CHANNEL_OVERRIDES),
|
|
37
|
+
token=os.environ.get("GITLAB_TOKEN"),
|
|
38
|
+
)
|
|
39
|
+
Config.APK_SOURCE = str(registry_source.prepare(
|
|
40
|
+
app_set_build_list,
|
|
41
|
+
android_version,
|
|
42
|
+
getattr(Config, "PACKAGE_ARCH", "arm64"),
|
|
43
|
+
))
|
|
44
|
+
print(f"[Registry] Handing prepared source to the existing builder: {Config.APK_SOURCE}")
|
|
45
|
+
print("[Registry] Final NikGapps ZIP assembly remains unchanged")
|
|
46
|
+
if Config.OVERLAY_SOURCE is None:
|
|
47
|
+
overlay_path = Path(Config.APK_SOURCE) / ".overlays"
|
|
48
|
+
overlay_path.mkdir(parents=True, exist_ok=True)
|
|
49
|
+
Config.OVERLAY_SOURCE = str(overlay_path)
|
|
25
50
|
source_directory = Config.APK_SOURCE
|
|
26
51
|
print(f"Building from {source_directory}")
|
|
27
52
|
cmd = Cmd()
|
|
@@ -26,6 +26,11 @@ def build_config():
|
|
|
26
26
|
if len(args.get_android_versions()) > 0:
|
|
27
27
|
android_versions = args.get_android_versions()
|
|
28
28
|
Config.UPLOAD_FILES = args.upload
|
|
29
|
+
if args.package_source:
|
|
30
|
+
Config.PACKAGE_SOURCE = args.package_source
|
|
31
|
+
if args.package_channel:
|
|
32
|
+
Config.PACKAGE_CHANNEL = args.package_channel
|
|
33
|
+
Config.PACKAGE_ARCH = args.arch
|
|
29
34
|
print("---------------------------------------")
|
|
30
35
|
print("Android Versions to build: " + str(android_versions))
|
|
31
36
|
print("---------------------------------------")
|
|
@@ -48,10 +53,12 @@ def build_config():
|
|
|
48
53
|
path = config_name
|
|
49
54
|
F.write_string_in_lf_file(str_data=config_string, file_path=path)
|
|
50
55
|
for android_version in android_versions:
|
|
51
|
-
|
|
52
|
-
|
|
56
|
+
if Config.PACKAGE_SOURCE != "registry":
|
|
57
|
+
apk_repo = GitOp.clone_apk_source(android_version, args.arch, release_type=Config.RELEASE_TYPE)
|
|
58
|
+
Config.APK_SOURCE = apk_repo.working_tree_dir
|
|
53
59
|
overlay_repo = GitOp.clone_overlay_repo(android_version)
|
|
54
|
-
|
|
60
|
+
if overlay_repo is not None:
|
|
61
|
+
Config.OVERLAY_SOURCE = overlay_repo.working_tree_dir
|
|
55
62
|
config_obj = NikGappsConfig(android_version=android_version, config_path=path)
|
|
56
63
|
print(f"Setting up package list from {config_name}...")
|
|
57
64
|
config_obj.config_package_list = Build.build_from_directory(app_set_build_list=config_obj.config_package_list,
|
|
@@ -39,6 +39,14 @@ class Args:
|
|
|
39
39
|
'-r', '--releaseType', help="It is the release type for which we need to build the gapps",
|
|
40
40
|
default="stable", type=str)
|
|
41
41
|
self.parser.add_argument('-P', '--packageList', help="List of packages to build", type=str)
|
|
42
|
+
self.parser.add_argument(
|
|
43
|
+
'--packageSource', choices=['git', 'registry'],
|
|
44
|
+
help="Use the legacy Git source or the package registry catalog"
|
|
45
|
+
)
|
|
46
|
+
self.parser.add_argument(
|
|
47
|
+
'--packageChannel', choices=['stable', 'beta', 'canary'],
|
|
48
|
+
help="Default registry channel for selected packages"
|
|
49
|
+
)
|
|
42
50
|
|
|
43
51
|
args = self.parser.parse_args()
|
|
44
52
|
|
|
@@ -60,6 +68,8 @@ class Args:
|
|
|
60
68
|
self.release_type = args.releaseType
|
|
61
69
|
self.ssh_clone = args.sshClone
|
|
62
70
|
self.send_zip_device = args.sendToDevice
|
|
71
|
+
self.package_source = args.packageSource
|
|
72
|
+
self.package_channel = args.packageChannel
|
|
63
73
|
# self.oems = args.oems
|
|
64
74
|
|
|
65
75
|
def get_package_list(self):
|
|
@@ -53,6 +53,24 @@ CACHED_SOURCE = None
|
|
|
53
53
|
APK_SOURCE = None
|
|
54
54
|
OVERLAY_SOURCE = None
|
|
55
55
|
|
|
56
|
+
# Package source. "git" preserves the existing clone behavior; "registry"
|
|
57
|
+
# resolves AppSets through the published package catalog and verified cache.
|
|
58
|
+
PACKAGE_SOURCE = os.environ.get("PACKAGE_SOURCE", "git").lower()
|
|
59
|
+
PACKAGE_CHANNEL = os.environ.get("PACKAGE_CHANNEL", RELEASE_TYPE).lower()
|
|
60
|
+
PACKAGE_CATALOG_URL = os.environ.get(
|
|
61
|
+
"PACKAGE_CATALOG_URL",
|
|
62
|
+
"https://gitlab.com/nikgapps/nikgapps-package-catalog/-/raw/main/catalog.json"
|
|
63
|
+
)
|
|
64
|
+
PACKAGE_APPSETS_URL = os.environ.get(
|
|
65
|
+
"PACKAGE_APPSETS_URL",
|
|
66
|
+
"https://gitlab.com/nikgapps/nikgapps-package-catalog/-/raw/main/appsets.json"
|
|
67
|
+
)
|
|
68
|
+
PACKAGE_CACHE = os.environ.get(
|
|
69
|
+
"PACKAGE_CACHE",
|
|
70
|
+
os.path.abspath(os.path.join(os.getcwd(), ".nikgapps-package-cache"))
|
|
71
|
+
)
|
|
72
|
+
PACKAGE_CHANNEL_OVERRIDES = os.environ.get("PACKAGE_CHANNEL_OVERRIDES", "{}")
|
|
73
|
+
|
|
56
74
|
# Enabling this will enable the feature of building NikGapps using config file
|
|
57
75
|
BUILD_CONFIG = True
|
|
58
76
|
BUILD_EXCLUSIVE = (RELEASE_TYPE.lower().__eq__("stable"))
|
|
@@ -21,6 +21,11 @@ def main():
|
|
|
21
21
|
package_list = Config.BUILD_PACKAGE_LIST
|
|
22
22
|
Config.UPLOAD_FILES = args.upload
|
|
23
23
|
Config.USE_CACHED_APKS = args.use_cached_apks
|
|
24
|
+
if args.package_source:
|
|
25
|
+
Config.PACKAGE_SOURCE = args.package_source
|
|
26
|
+
if args.package_channel:
|
|
27
|
+
Config.PACKAGE_CHANNEL = args.package_channel
|
|
28
|
+
Config.PACKAGE_ARCH = args.arch
|
|
24
29
|
if Config.USE_CACHED_APKS:
|
|
25
30
|
print("Using Cached Apks")
|
|
26
31
|
Modes.DEFAULT = Modes.TAR_XZ if args.tar else Modes.ZIP
|
|
@@ -39,13 +44,17 @@ def main():
|
|
|
39
44
|
Config.TARGET_ANDROID_VERSION = android_version
|
|
40
45
|
# clone the apk repo if it doesn't exist
|
|
41
46
|
if args.enable_git_clone:
|
|
42
|
-
if Config.
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
47
|
+
if Config.PACKAGE_SOURCE != "registry":
|
|
48
|
+
if Config.USE_CACHED_APKS:
|
|
49
|
+
cached_repo = GitOp.clone_apk_source(android_version, release_type=Config.RELEASE_TYPE,
|
|
50
|
+
cached=True)
|
|
51
|
+
Config.CACHED_SOURCE = cached_repo.working_tree_dir
|
|
52
|
+
else:
|
|
53
|
+
apk_repo = GitOp.clone_apk_source(android_version, args.arch, release_type=Config.RELEASE_TYPE)
|
|
54
|
+
Config.APK_SOURCE = apk_repo.working_tree_dir
|
|
55
|
+
# Overlays are not catalog artifacts yet. Keep the established
|
|
56
|
+
# overlay source so registry-backed builds retain output parity.
|
|
57
|
+
if not Config.USE_CACHED_APKS:
|
|
49
58
|
overlay_repo = GitOp.clone_overlay_repo(android_version)
|
|
50
59
|
if overlay_repo is not None:
|
|
51
60
|
Config.OVERLAY_SOURCE = overlay_repo.working_tree_dir
|
|
@@ -53,6 +62,31 @@ def main():
|
|
|
53
62
|
Release.zip(package_list, android_version, args.arch, args.sign)
|
|
54
63
|
if Config.RELEASE_TYPE and Config.ENVIRONMENT_TYPE == "production":
|
|
55
64
|
GitOp.mark_a_release(android_version, Config.RELEASE_TYPE)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
|
|
56
90
|
if args.release:
|
|
57
91
|
if args.update_website:
|
|
58
92
|
website_repo = TestGit(GitStatics.website_repo_dir)
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import argparse
|
|
1
2
|
import os
|
|
2
3
|
from pathlib import Path
|
|
3
4
|
|
|
@@ -11,11 +12,49 @@ from niklibrary.git.Git import Git
|
|
|
11
12
|
from NikGapps.helper.overlay.Overlay import Overlay
|
|
12
13
|
|
|
13
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
|
+
|
|
28
|
+
def build_local_overlays(android_version, source_dir, output_dir):
|
|
29
|
+
"""Generate and compile overlays locally without cloning or pushing repos."""
|
|
30
|
+
register_android_platform(android_version)
|
|
31
|
+
source_dir = os.path.abspath(source_dir)
|
|
32
|
+
output_dir = os.path.abspath(output_dir)
|
|
33
|
+
os.makedirs(source_dir, exist_ok=True)
|
|
34
|
+
os.makedirs(output_dir, exist_ok=True)
|
|
35
|
+
for overlay in NikGappsOverlays.get_overlay(android_version=android_version):
|
|
36
|
+
overlay.build_apk_source(source_dir)
|
|
37
|
+
built = []
|
|
38
|
+
for folder in sorted(Path(source_dir).iterdir()):
|
|
39
|
+
if not folder.is_dir() or not F.file_exists(os.path.join(str(folder), "apktool.yml")):
|
|
40
|
+
continue
|
|
41
|
+
overlay_path = Cmd().build_overlay(folder_name=str(folder))
|
|
42
|
+
if not overlay_path:
|
|
43
|
+
raise RuntimeError(f"Failed to build Android {android_version} overlay: {folder.name}")
|
|
44
|
+
destination = os.path.join(output_dir, folder.name, f"{folder.name}.apk")
|
|
45
|
+
F.copy_file(overlay_path, destination)
|
|
46
|
+
F.remove_dir(os.path.join(str(folder), "dist"))
|
|
47
|
+
F.remove_dir(os.path.join(str(folder), "build"))
|
|
48
|
+
built.append(destination)
|
|
49
|
+
return built
|
|
50
|
+
|
|
51
|
+
|
|
14
52
|
def overlay_control():
|
|
15
53
|
args = Args()
|
|
16
54
|
android_versions = args.get_android_versions()
|
|
17
55
|
|
|
18
56
|
for android_version in android_versions:
|
|
57
|
+
register_android_platform(android_version)
|
|
19
58
|
repo_name = Statics.get_overlay_source_repo(android_version)
|
|
20
59
|
repo_dir = Statics.get_overlay_source_directory(android_version)
|
|
21
60
|
branch = "main"
|
|
@@ -64,4 +103,17 @@ def overlay_control():
|
|
|
64
103
|
|
|
65
104
|
|
|
66
105
|
if __name__ == "__main__":
|
|
67
|
-
|
|
106
|
+
parser = argparse.ArgumentParser(description="Build NikGapps resource overlays")
|
|
107
|
+
parser.add_argument("--android-version")
|
|
108
|
+
parser.add_argument("--source-dir")
|
|
109
|
+
parser.add_argument("--output-dir")
|
|
110
|
+
options = parser.parse_args()
|
|
111
|
+
if options.android_version:
|
|
112
|
+
if not options.source_dir or not options.output_dir:
|
|
113
|
+
parser.error("--source-dir and --output-dir are required in local mode")
|
|
114
|
+
paths = build_local_overlays(
|
|
115
|
+
options.android_version, options.source_dir, options.output_dir
|
|
116
|
+
)
|
|
117
|
+
print(f"Built {len(paths)} overlays for Android {options.android_version}")
|
|
118
|
+
else:
|
|
119
|
+
overlay_control()
|
|
@@ -63,4 +63,30 @@ NikGapps/helper/overlay/Overlay.py
|
|
|
63
63
|
NikGapps/helper/overlay/Resources.py
|
|
64
64
|
NikGapps/helper/overlay/String.py
|
|
65
65
|
NikGapps/helper/overlay/StringArray.py
|
|
66
|
-
NikGapps/helper/overlay/__init__.py
|
|
66
|
+
NikGapps/helper/overlay/__init__.py
|
|
67
|
+
nikgapps_package_pipeline/__init__.py
|
|
68
|
+
nikgapps_package_pipeline/__main__.py
|
|
69
|
+
nikgapps_package_pipeline/apk.py
|
|
70
|
+
nikgapps_package_pipeline/archive.py
|
|
71
|
+
nikgapps_package_pipeline/cli.py
|
|
72
|
+
nikgapps_package_pipeline/compat.py
|
|
73
|
+
nikgapps_package_pipeline/config.py
|
|
74
|
+
nikgapps_package_pipeline/consumer.py
|
|
75
|
+
nikgapps_package_pipeline/errors.py
|
|
76
|
+
nikgapps_package_pipeline/gitlab.py
|
|
77
|
+
nikgapps_package_pipeline/models.py
|
|
78
|
+
nikgapps_package_pipeline/pipeline.py
|
|
79
|
+
nikgapps_package_pipeline/scanner.py
|
|
80
|
+
nikgapps_package_pipeline/upgrade.py
|
|
81
|
+
nikgapps_package_pipeline/util.py
|
|
82
|
+
nikgapps_registry/__init__.py
|
|
83
|
+
nikgapps_registry/__main__.py
|
|
84
|
+
nikgapps_registry/cli.py
|
|
85
|
+
nikgapps_registry/service.py
|
|
86
|
+
tests_package_pipeline/__init__.py
|
|
87
|
+
tests_package_pipeline/test_consumer.py
|
|
88
|
+
tests_package_pipeline/test_overlay_control.py
|
|
89
|
+
tests_package_pipeline/test_pipeline.py
|
|
90
|
+
tests_package_pipeline/test_upgrade.py
|
|
91
|
+
tests_registry/__init__.py
|
|
92
|
+
tests_registry/test_registry.py
|
|
@@ -3,5 +3,6 @@ build = NikGapps.build_config:build_config
|
|
|
3
3
|
cache = NikGapps.cache:cache
|
|
4
4
|
copy_repos = NikGapps.copy_repos:copy_repos
|
|
5
5
|
nikgapps = NikGapps.main:main
|
|
6
|
+
nikgapps-registry = nikgapps_registry.cli:main
|
|
6
7
|
nikgapps_config_upload = NikGapps.config_upload:config_upload
|
|
7
8
|
nikgapps_overlay = NikGapps.overlay_control:overlay_control
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import re
|
|
4
|
+
import subprocess
|
|
5
|
+
import os
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
from .errors import PipelineError
|
|
9
|
+
from .models import ApkMetadata
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class ApkInspector:
|
|
13
|
+
"""APK metadata boundary; injectable for tests and alternate tooling."""
|
|
14
|
+
|
|
15
|
+
def inspect(self, apk: Path) -> ApkMetadata:
|
|
16
|
+
raise NotImplementedError
|
|
17
|
+
|
|
18
|
+
def privileged_permissions(self, apk: Path) -> list[str]:
|
|
19
|
+
"""Return permissions requested/declared by a privileged APK."""
|
|
20
|
+
return []
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class Aapt2Inspector(ApkInspector):
|
|
24
|
+
def __init__(self, executable: str = "aapt2") -> None:
|
|
25
|
+
self.executable = executable
|
|
26
|
+
|
|
27
|
+
def inspect(self, apk: Path) -> ApkMetadata:
|
|
28
|
+
badging = self._dump(["dump", "badging", str(apk)], apk)
|
|
29
|
+
if not re.search(r"^sdkVersion:'\d+'", badging, re.MULTILINE):
|
|
30
|
+
manifest = self._dump(
|
|
31
|
+
["dump", "xmltree", str(apk), "--file", "AndroidManifest.xml"],
|
|
32
|
+
apk,
|
|
33
|
+
)
|
|
34
|
+
uses_sdk = re.search(
|
|
35
|
+
r"E: uses-sdk\b(?P<body>.*?)(?=\n\s*E: |\Z)",
|
|
36
|
+
manifest,
|
|
37
|
+
re.DOTALL,
|
|
38
|
+
)
|
|
39
|
+
minimum = re.search(
|
|
40
|
+
r"minSdkVersion[^=]*=(?:\(type [^)]+\))?(\d+)",
|
|
41
|
+
uses_sdk.group("body") if uses_sdk else "",
|
|
42
|
+
)
|
|
43
|
+
if not minimum:
|
|
44
|
+
raise PipelineError(
|
|
45
|
+
f"Missing minimum SDK in both badging and manifest output for {apk}"
|
|
46
|
+
)
|
|
47
|
+
badging += f"\nsdkVersion:'{minimum.group(1)}'\n"
|
|
48
|
+
return self.parse(badging, apk)
|
|
49
|
+
|
|
50
|
+
def privileged_permissions(self, apk: Path) -> list[str]:
|
|
51
|
+
output = self._dump(["dump", "permissions", str(apk)], apk)
|
|
52
|
+
permissions: set[str] = set()
|
|
53
|
+
for line in output.splitlines():
|
|
54
|
+
stripped = line.strip()
|
|
55
|
+
requested = re.match(r"uses-permission(?:-sdk-\d+)?:\s+name='([^']+)'", stripped)
|
|
56
|
+
declared = re.match(r"permission:\s+(?:name=)?'?([^'\s]+)'?", stripped)
|
|
57
|
+
match = requested or declared
|
|
58
|
+
if match:
|
|
59
|
+
permissions.add(match.group(1))
|
|
60
|
+
return sorted(permissions)
|
|
61
|
+
|
|
62
|
+
def _dump(self, arguments: list[str], apk: Path) -> str:
|
|
63
|
+
try:
|
|
64
|
+
result = subprocess.run(
|
|
65
|
+
[self.executable, *arguments],
|
|
66
|
+
check=True,
|
|
67
|
+
capture_output=True,
|
|
68
|
+
)
|
|
69
|
+
except FileNotFoundError as exc:
|
|
70
|
+
raise PipelineError(
|
|
71
|
+
f"{self.executable!r} was not found. Install Android build-tools "
|
|
72
|
+
"or pass --aapt2 with its full path."
|
|
73
|
+
) from exc
|
|
74
|
+
except subprocess.CalledProcessError as exc:
|
|
75
|
+
raw_detail = exc.stderr or exc.stdout or b""
|
|
76
|
+
detail = raw_detail.decode("utf-8", errors="replace").strip()
|
|
77
|
+
raise PipelineError(f"aapt2 failed for {apk}: {detail}") from exc
|
|
78
|
+
return result.stdout.decode("utf-8", errors="replace")
|
|
79
|
+
|
|
80
|
+
@staticmethod
|
|
81
|
+
def parse(text: str, apk: Path | None = None) -> ApkMetadata:
|
|
82
|
+
def required(pattern: str, name: str) -> str:
|
|
83
|
+
match = re.search(pattern, text, re.MULTILINE)
|
|
84
|
+
if not match:
|
|
85
|
+
raise PipelineError(f"Missing {name} in aapt2 output for {apk or 'APK'}")
|
|
86
|
+
return match.group(1)
|
|
87
|
+
|
|
88
|
+
return ApkMetadata(
|
|
89
|
+
package_name=required(r"^package: name='([^']+)'", "package name"),
|
|
90
|
+
version_name=required(r"^package:.*versionName='([^']+)'", "version name"),
|
|
91
|
+
version_code=int(required(r"^package:.*versionCode='(\d+)'", "version code")),
|
|
92
|
+
min_api=int(required(r"^sdkVersion:'(\d+)'", "minimum SDK")),
|
|
93
|
+
target_api=int(required(r"^targetSdkVersion:'(\d+)'", "target SDK")),
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def find_aapt2() -> str:
|
|
98
|
+
"""Find aapt2 in PATH or a conventional local Android SDK."""
|
|
99
|
+
import shutil
|
|
100
|
+
|
|
101
|
+
on_path = shutil.which("aapt2")
|
|
102
|
+
if on_path:
|
|
103
|
+
return on_path
|
|
104
|
+
project_root = Path(__file__).resolve().parent.parent
|
|
105
|
+
bundled = (
|
|
106
|
+
project_root.parent / "nikassets" / "nikassets" / "helper" / "assets"
|
|
107
|
+
/ "bin" / ("Windows" if os.name == "nt" else "Linux")
|
|
108
|
+
/ ("aapt2.exe" if os.name == "nt" else "aapt2")
|
|
109
|
+
)
|
|
110
|
+
if bundled.is_file():
|
|
111
|
+
return str(bundled)
|
|
112
|
+
sdk_roots = [
|
|
113
|
+
os.environ.get("ANDROID_SDK_ROOT"),
|
|
114
|
+
os.environ.get("ANDROID_HOME"),
|
|
115
|
+
str(Path.home() / "AppData" / "Local" / "Android" / "Sdk"),
|
|
116
|
+
]
|
|
117
|
+
for raw_root in sdk_roots:
|
|
118
|
+
if not raw_root:
|
|
119
|
+
continue
|
|
120
|
+
build_tools = Path(raw_root) / "build-tools"
|
|
121
|
+
try:
|
|
122
|
+
exists = build_tools.is_dir()
|
|
123
|
+
except OSError:
|
|
124
|
+
continue
|
|
125
|
+
if not exists:
|
|
126
|
+
continue
|
|
127
|
+
try:
|
|
128
|
+
candidates = sorted(
|
|
129
|
+
build_tools.glob("*/aapt2.exe" if os.name == "nt" else "*/aapt2"),
|
|
130
|
+
reverse=True,
|
|
131
|
+
)
|
|
132
|
+
except OSError:
|
|
133
|
+
continue
|
|
134
|
+
if candidates:
|
|
135
|
+
return str(candidates[0])
|
|
136
|
+
return "aapt2"
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import zipfile
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
from .errors import PipelineError
|
|
8
|
+
from .models import PayloadFile
|
|
9
|
+
from .util import json_data, safe_path
|
|
10
|
+
|
|
11
|
+
ZIP_TIME = (1980, 1, 1, 0, 0, 0)
|
|
12
|
+
ALREADY_COMPRESSED = {
|
|
13
|
+
".apk", ".zip", ".jar", ".gz", ".png", ".jpg", ".jpeg", ".webp",
|
|
14
|
+
".mp3", ".mp4", ".so", ".tflite",
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def _write_entry(archive: zipfile.ZipFile, name: str, data: bytes, compress: int) -> None:
|
|
19
|
+
safe_path(name)
|
|
20
|
+
info = zipfile.ZipInfo(name, ZIP_TIME)
|
|
21
|
+
info.create_system = 3
|
|
22
|
+
info.external_attr = 0o100644 << 16
|
|
23
|
+
info.compress_type = compress
|
|
24
|
+
archive.writestr(info, data, compress_type=compress, compresslevel=9)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def write_package_zip(
|
|
28
|
+
destination: Path,
|
|
29
|
+
files: list[PayloadFile],
|
|
30
|
+
manifest: dict[str, Any],
|
|
31
|
+
*,
|
|
32
|
+
pretty: bool = True,
|
|
33
|
+
) -> None:
|
|
34
|
+
if any(item.path in {"package.json", "installer.sh", "uninstaller.sh"} for item in files):
|
|
35
|
+
raise PipelineError("Payload collides with reserved package.json")
|
|
36
|
+
destination.parent.mkdir(parents=True, exist_ok=True)
|
|
37
|
+
with zipfile.ZipFile(destination, "w") as archive:
|
|
38
|
+
for item in sorted(files, key=lambda value: value.path):
|
|
39
|
+
compression = (
|
|
40
|
+
zipfile.ZIP_STORED
|
|
41
|
+
if item.source.suffix.casefold() in ALREADY_COMPRESSED
|
|
42
|
+
else zipfile.ZIP_DEFLATED
|
|
43
|
+
)
|
|
44
|
+
_write_entry(archive, encode_builder_path(item.path), item.source.read_bytes(), compression)
|
|
45
|
+
for generated in sorted(manifest.get("generatedFiles", []), key=lambda value: value["path"]):
|
|
46
|
+
_write_entry(
|
|
47
|
+
archive,
|
|
48
|
+
encode_builder_path(generated["path"]),
|
|
49
|
+
generated["content"].encode("utf-8"),
|
|
50
|
+
zipfile.ZIP_DEFLATED,
|
|
51
|
+
)
|
|
52
|
+
_write_entry(archive, "installer.sh", package_installer(manifest).encode(), zipfile.ZIP_DEFLATED)
|
|
53
|
+
_write_entry(archive, "uninstaller.sh", package_uninstaller(manifest).encode(), zipfile.ZIP_DEFLATED)
|
|
54
|
+
_write_entry(archive, "package.json", json_data(manifest, pretty), zipfile.ZIP_DEFLATED)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def encode_builder_path(path: str) -> str:
|
|
58
|
+
"""Encode an install path using the nested-package format used by NikGapps."""
|
|
59
|
+
safe_path(path)
|
|
60
|
+
parts = path.split("/")
|
|
61
|
+
return "___" + "___".join(parts[:-1]) + "/" + parts[-1]
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def _lines(values: list[str]) -> str:
|
|
65
|
+
return "\n".join(values)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def _legacy_asset(name: str) -> str:
|
|
69
|
+
path = Path(__file__).resolve().parent.parent / "NikGapps" / "helper" / "assets" / name
|
|
70
|
+
try:
|
|
71
|
+
return path.read_text(encoding="utf-8")
|
|
72
|
+
except OSError as exc:
|
|
73
|
+
raise PipelineError(f"Cannot read legacy package template {path}: {exc}") from exc
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def package_installer(manifest: dict[str, Any]) -> str:
|
|
77
|
+
install = manifest["install"]
|
|
78
|
+
apk = manifest.get("apk") or {}
|
|
79
|
+
file_list = [item["archivePath"] for item in manifest["files"]]
|
|
80
|
+
extra = install.get("additionalInstallerScript") or ""
|
|
81
|
+
extra_block = f"{extra}\n" if extra else ""
|
|
82
|
+
validation = install.get("validationScript") or "find_install_mode"
|
|
83
|
+
remove_files = [value for value in install["removeFiles"] if value.strip()]
|
|
84
|
+
remove_overlays = [value for value in install["removeOverlays"] if value.strip()]
|
|
85
|
+
remove_files_block = "".join(f"{value}\n" for value in remove_files)
|
|
86
|
+
remove_overlays_block = "".join(f"{value}\n" for value in remove_overlays)
|
|
87
|
+
file_list_block = "".join(f"{value}\n" for value in file_list)
|
|
88
|
+
folders: list[str] = []
|
|
89
|
+
for row in manifest["files"]:
|
|
90
|
+
parts = row["path"].split("/")[:-1]
|
|
91
|
+
for index in range(1, len(parts) + 1):
|
|
92
|
+
folder = "/".join(parts[:index])
|
|
93
|
+
if parts and parts[0] not in {"system", "vendor", "product", "system_ext", "overlay"} and folder not in folders:
|
|
94
|
+
folders.append(folder)
|
|
95
|
+
make_dirs = "".join(f' make_dir "{folder}"\n' for folder in folders)
|
|
96
|
+
return _legacy_asset("installer.sh") + f'''# Initialize the variables
|
|
97
|
+
default_partition="{manifest["defaultPartition"]}"
|
|
98
|
+
clean_flash_only="{str(install["cleanFlashOnly"]).lower()}"
|
|
99
|
+
product_prefix=$(find_product_prefix "$install_partition")
|
|
100
|
+
title="{install["title"]}"
|
|
101
|
+
package_title="{install["packageTitle"]}"
|
|
102
|
+
pkg_size="{install["payloadSize"]}"
|
|
103
|
+
package_name="{manifest.get("packageName") or ""}"
|
|
104
|
+
packagePath=install{install["packageTitle"]}Files
|
|
105
|
+
deleteFilesPath=delete{install["packageTitle"]}Files
|
|
106
|
+
propFilePath=$(get_prop_file_path $package_title)
|
|
107
|
+
|
|
108
|
+
remove_aosp_apps_from_rom="
|
|
109
|
+
{remove_files_block}"
|
|
110
|
+
|
|
111
|
+
delete_overlays="
|
|
112
|
+
{remove_overlays_block}"
|
|
113
|
+
|
|
114
|
+
file_list="
|
|
115
|
+
{file_list_block}"
|
|
116
|
+
|
|
117
|
+
remove_overlays() {{
|
|
118
|
+
for i in $delete_overlays; do
|
|
119
|
+
delete_overlays "$i" "$propFilePath" "$package_title"
|
|
120
|
+
done
|
|
121
|
+
}}
|
|
122
|
+
|
|
123
|
+
remove_existing_package() {{
|
|
124
|
+
# remove the existing folder for clean install of {install["packageTitle"]}
|
|
125
|
+
delete_package "{install["title"]}" "$package_title"
|
|
126
|
+
}}
|
|
127
|
+
|
|
128
|
+
remove_aosp_apps() {{
|
|
129
|
+
# Delete the folders that we want to remove with installing {install["packageTitle"]}
|
|
130
|
+
for i in $remove_aosp_apps_from_rom; do
|
|
131
|
+
RemoveAospAppsFromRom "$i" "$propFilePath" "$package_title"
|
|
132
|
+
done
|
|
133
|
+
}}
|
|
134
|
+
|
|
135
|
+
install_package() {{
|
|
136
|
+
remove_existing_package
|
|
137
|
+
remove_aosp_apps
|
|
138
|
+
remove_overlays
|
|
139
|
+
# Create folders and set the permissions
|
|
140
|
+
{make_dirs}
|
|
141
|
+
delete_prop_lines "$propFilePath"
|
|
142
|
+
|
|
143
|
+
# Copy the files and set the permissions
|
|
144
|
+
for i in $file_list; do
|
|
145
|
+
install_file "$i"
|
|
146
|
+
done
|
|
147
|
+
|
|
148
|
+
{extra_block} chmod 755 "$COMMONDIR/addon.sh";
|
|
149
|
+
update_prop "$propFilePath" "install" "$propFilePath" "{install["packageTitle"]}"
|
|
150
|
+
. $COMMONDIR/addon.sh "{install["packageTitle"]}" "$propFilePath" "{install["addonIndex"]}"
|
|
151
|
+
copy_file "$propFilePath" "$logDir/addonfiles/$package_title.prop"
|
|
152
|
+
}}
|
|
153
|
+
|
|
154
|
+
{validation}
|
|
155
|
+
|
|
156
|
+
'''
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def package_uninstaller(manifest: dict[str, Any]) -> str:
|
|
160
|
+
install = manifest["install"]
|
|
161
|
+
file_list = [item["archivePath"] for item in manifest["files"]]
|
|
162
|
+
return _legacy_asset("uninstaller.sh") + f'''
|
|
163
|
+
|
|
164
|
+
# Initialize the variables
|
|
165
|
+
uninstall_addon=$1
|
|
166
|
+
clean_flash_only="{str(install["cleanFlashOnly"]).lower()}"
|
|
167
|
+
title="{install["title"]}"
|
|
168
|
+
package_title="{install["packageTitle"]}"
|
|
169
|
+
package_name="{manifest.get("packageName") or ""}"
|
|
170
|
+
|
|
171
|
+
file_list="
|
|
172
|
+
{_lines(file_list)}
|
|
173
|
+
"
|
|
174
|
+
|
|
175
|
+
uninstall_package
|
|
176
|
+
'''
|