From 1ac6a518e6393d5c80a83578678e0efe032965b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mario=20Andr=C3=A9s=20P=C3=A9rez=20Batanero?= Date: Mon, 22 Dec 2025 20:37:54 +0100 Subject: [PATCH] asu: build: rework default packages management Use the .packageinfo file included in imagebuilder bundles as information source for checks about which default packages can be included without conflicting with others included in the full package list provided by ASU clients. Keep the previous "absolute" mode of operation where default packages not included in the user request are removed, and improve the "additional" mode where default packages are included unless they conflict with others requested by the user. Flip the logic and default value of the "diff_packages" flag to True to activate the "additional" mode by default, while still allowing users to select the "absolute" mode if needed. (Allow old ASU clients use this) Fixes: https://github.com/openwrt/openwrt/issues/19749 Fixes: all issues related with changes in default packages lists Fixes: ath10 fixes Fixes: fitblk inclusion Fixes: ... --- asu/build.py | 50 ++++++++++++++++++++++++++++++++++++++------ asu/build_request.py | 4 ++-- 2 files changed, 46 insertions(+), 8 deletions(-) diff --git a/asu/build.py b/asu/build.py index bacd4327..3d9ddbba 100644 --- a/asu/build.py +++ b/asu/build.py @@ -191,7 +191,8 @@ def _build(build_request: BuildRequest, job=None): report_error(job, "Could not set up ImageBuilder") returncode, job.meta["stdout"], job.meta["stderr"] = run_cmd( - container, ["make", "info"] + container, ["make", "info"], + copy=[".packageinfo", bin_dir], ) job.meta["imagebuilder_status"] = "validate_revision" @@ -206,12 +207,11 @@ def _build(build_request: BuildRequest, job=None): f"Received incorrect version {version_code} (requested {requested})", ) - default_packages = set( + target_default_packages = set( re.search(r"Default Packages: (.*)\n", job.meta["stdout"]).group(1).split() - ) - log.debug(f"Default packages: {default_packages}") + ) - profile_packages = set( + profile_default_packages = set( re.search( r"{}:\n .+\n Packages: (.*?)\n".format(build_request.profile), job.meta["stdout"], @@ -221,13 +221,51 @@ def _build(build_request: BuildRequest, job=None): .split() ) + default_packages = target_default_packages | profile_default_packages + log.debug(f"Default packages: {default_packages}") + apply_package_changes(build_request) build_cmd_packages = build_request.packages if build_request.diff_packages: + # additional mode - keep default packages which not conflict with user requested packages + # Get information about package conflicts and provides from .packageinfo + packages_db: dict[str, dict[str, list[str]]] = {} + packages_with_conflicts: set[str] = set() + with open(str(bin_dir / ".packageinfo"), 'r') as file: + pack_blocks = re.findall( + r"Package: (.*?)\n.*?\nConflicts: (.*?)\n.*?\nProvides: (.*?)\n", + file.read(), + re.DOTALL, + ) + for p_name, p_conflicts, p_provides in pack_blocks: + packages_db.update({p_name: {"conflicts": p_conflicts.split(), "provides": p_provides.split()}}) + + # Do not include default packages with potential conflicts (conflicts or provides) + for package in default_packages: + for package_user in set(build_request.packages) - default_packages: + if package in packages_db and package_user in packages_db: + if package in packages_db[package_user]["provides"]: + log.debug(f"{package}\tNOT INCLUDED (Provided by: {package_user})") + packages_with_conflicts.add(package) + if set(packages_db[package]["provides"]) & set(packages_db[package_user]["provides"]): + log.debug(f"{package}\tNOT INCLUDED (Common provides with: {package_user})") + packages_with_conflicts.add(package) + if package in packages_db[package_user]["conflicts"] or package_user in packages_db[package]["conflicts"]: + log.debug(f"{package}\tNOT INCLUDED (Conflict with: {package_user})") + packages_with_conflicts.add(package) + + for p in packages_with_conflicts: + build_cmd_packages.remove(p) + build_cmd_packages.insert(0, f"-{p}") + + log.debug(f"List of packages removing default packages with conflicts: {build_cmd_packages}") + + else: + # absolute mode - remove default packages not in requested list build_cmd_packages: list[str] = diff_packages( - build_request.packages, default_packages | profile_packages + build_request.packages, default_packages ) log.debug(f"Diffed packages: {build_cmd_packages}") diff --git a/asu/build_request.py b/asu/build_request.py index 1fc18437..24c12292 100644 --- a/asu/build_request.py +++ b/asu/build_request.py @@ -98,7 +98,7 @@ class BuildRequest(BaseModel): Field( description=""" This parameter determines if requested packages are seen as - *additional* or *absolute*. If set to `true` the packages are + *additional* or *absolute*. If set to `false` the packages are seen as *absolute* and all default packages outside the requested packages are removed. \n\n It is possible to brick devices when requesting an incomplete list with this parameter @@ -106,7 +106,7 @@ class BuildRequest(BaseModel): packages. """.strip(), ), - ] = False + ] = True defaults: Annotated[ str | None, Field(