From 8d010b4bd52d5fa6542fef7e0d6a5170e25f2c2b Mon Sep 17 00:00:00 2001 From: a-gave Date: Sun, 5 Jan 2025 20:48:20 +0100 Subject: [PATCH] build: allow to define additional configs --- asu/build.py | 32 ++++++++++++++++++++++++++++++++ asu/build_request.py | 21 +++++++++++++++++++++ asu/config.py | 8 ++++++++ asu/routers/api.py | 10 ++++++++++ asu/util.py | 13 +++++++++++++ 5 files changed, 84 insertions(+) diff --git a/asu/build.py b/asu/build.py index a1c9e386..ef126ae3 100644 --- a/asu/build.py +++ b/asu/build.py @@ -150,6 +150,23 @@ def build(build_request: BuildRequest, job=None): }, ) + if build_request.configs: + log.debug("Found extra configs") + configs = "" + for config in build_request.configs: + configs += f"{config}\n" + + (bin_dir / ".config_local").write_text(configs) + + mounts.append( + { + "type": "bind", + "source": str(bin_dir / ".config_local"), + "target": "/builder/.config_local", + "read_only": True, + } + ) + log.debug("Mounts: %s", mounts) container = podman.containers.create( @@ -243,6 +260,21 @@ def build(build_request: BuildRequest, job=None): packages_hash: str = get_packages_hash(manifest.keys()) log.debug(f"Packages Hash: {packages_hash}") + if build_request.configs: + log.info("Appling local configs") + returncode, job.meta["stdout"], job.meta["stderr"] = run_cmd( + container, + [ + "sh", + "-c", + ( + "for config in $(grep '#' .config_local | awk '{print $2}'); do sed -i 's/'$config'.*//' .config ; done; cat .config_local >> .config" + ), + ], + ) + if returncode: + report_error(job, "Could not apply local configs") + job.meta["build_cmd"] = [ "make", "image", diff --git a/asu/build_request.py b/asu/build_request.py index 0700440e..b904ec13 100644 --- a/asu/build_request.py +++ b/asu/build_request.py @@ -6,6 +6,9 @@ STRING_PATTERN = r"^[\w.,-]*$" TARGET_PATTERN = r"^[\w]*/[\w]*$" PKG_VERSION_PATTERN = r"^[\w.,~-]*$" +CONFIG_PATTERN = ( + r"^(# CONFIG_[\w_.-]+ is not set|CONFIG_[\w_.-]+=([\w_.-]+$|\"[^\"][\w_.-]+\"))$" +) class BuildRequest(BaseModel): @@ -158,3 +161,21 @@ class BuildRequest(BaseModel): """.strip(), ), ] = None + configs: Annotated[ + list[Annotated[str, Field(pattern=CONFIG_PATTERN)]], + Field( + examples=[ + [ + "CONFIG_VERSION_DIST=MyRouterOS", + "CONFIG_VERSION_NUMBER=1.6", + "CONFIG_TARGET_ROOTFS_TARGZ=y", + "CONFIG_TARGET_ROOTFS_JFFS2=y", + "# CONFIG_TARGET_ROOTFS_SQUASHFS is not set", + ] + ], + description=""" + List of configs, only the few ones not related to kernel/packages + as they will not be recompiled. + """.strip(), + ), + ] = [] diff --git a/asu/config.py b/asu/config.py index ce84867f..83cf09b9 100644 --- a/asu/config.py +++ b/asu/config.py @@ -82,6 +82,14 @@ class Settings(BaseSettings): server_stats: str = "/stats" log_level: str = "INFO" squid_cache: bool = False + configs_allowed: list = [] + # configs_allowed: list = [ + # 'CONFIG_VERSION_DIST', + # 'CONFIG_VERSION_NUMBER', + # 'CONFIG_TARGET_ROOTFS_TARGZ', + # 'CONFIG_TARGET_ROOTFS_JFFS2', + # 'CONFIG_TARGET_ROOTFS_SQUASHFS' + # ] settings = Settings() diff --git a/asu/routers/api.py b/asu/routers/api.py index 120eaf3b..c9f09bdd 100644 --- a/asu/routers/api.py +++ b/asu/routers/api.py @@ -17,6 +17,10 @@ get_request_hash, ) +import re + +configs_extract = r"^# (CONFIG_[\w_.-]) is not set|(CONFIG_[\w_.-]+)" + router = APIRouter() @@ -71,6 +75,12 @@ def validate_request(build_request: BuildRequest) -> tuple[dict, int]: if build_request.distro not in get_distros(): return validation_failure(f"Unsupported distro: {build_request.distro}") + if build_request.configs: + for config in build_request.configs: + config_key = re.search(configs_extract, config) + if config_key[0] not in settings.configs_allowed: + return validation_failure("Illegal config requested: {config_key[0]}") + branch = get_branch(build_request.version)["name"] r = get_redis_client() diff --git a/asu/util.py b/asu/util.py index 644822e1..3a57fdcf 100644 --- a/asu/util.py +++ b/asu/util.py @@ -97,6 +97,18 @@ def get_file_hash(path: str) -> str: return h.hexdigest() +def get_configs_hash(configs: list) -> str: + """Return sha256sum of configs list + Duplicate configs are automatically removed and the list is sorted to be + reproducible + Args: + configs (list): list of configs + Returns: + str: hash of `req` + """ + return get_str_hash(" ".join(sorted(list(set(configs))))) + + def get_manifest_hash(manifest: dict[str, str]) -> str: """Return sha256sum of package manifest @@ -132,6 +144,7 @@ def get_request_hash(build_request: BuildRequest) -> str: build_request.target, build_request.profile.replace(",", "_"), get_packages_hash(build_request.packages), + get_configs_hash(build_request.configs), get_manifest_hash(build_request.packages_versions), str(build_request.diff_packages), "", # build_request.filesystem