From c775681ba349057f87aeb9f5464632c7b4a992ea Mon Sep 17 00:00:00 2001 From: Achilleas Koutsou Date: Wed, 13 Dec 2023 20:53:15 +0100 Subject: [PATCH] test: use osbuild commit for build test filtering Record the osbuild commit ID from the Schutzfile in the build info when a build is successful. If a commit ID isn't specified, record it as "RELEASE" to signify that the osbuild version is the one in the distro repos. If the osbuild version and commit ID don't match, schedule the config for a build. This makes sure that changes in osbuild which don't affect the manifest get retested. This can catch bugs that weren't caught in osbuild and is useful for testing internal osbuild functionality changes. --- test/scripts/build-image | 27 ++++++---- test/scripts/imgtestlib.py | 89 +++++++++++++++++++++++---------- test/scripts/setup-osbuild-repo | 2 +- 3 files changed, 80 insertions(+), 38 deletions(-) diff --git a/test/scripts/build-image b/test/scripts/build-image index 65b0500e69..bbce3fdc22 100755 --- a/test/scripts/build-image +++ b/test/scripts/build-image @@ -23,7 +23,7 @@ def main(): print(f"👷 Building image {distro}/{image_type} using config {config_path}") # print the config for logging - with open(config_path, "r") as config_file: + with open(config_path, "r", encoding="utf-8") as config_file: config = json.load(config_file) print(json.dumps(config, indent=2)) config_name = config["name"] @@ -43,23 +43,30 @@ def main(): arch = os.uname().machine build_dir = os.path.join("build", testlib.gen_build_name(distro, arch, image_type, config_name)) manifest_path = os.path.join(build_dir, "manifest.json") - with open(manifest_path, "r") as manifest_fp: + with open(manifest_path, "r", encoding="utf-8") as manifest_fp: manifest_data = json.load(manifest_fp) manifest_id = testlib.get_manifest_id(manifest_data) osbuild_ver, _ = testlib.runcmd(["osbuild", "--version"]) + osrelease = read_osrelease() + distro_version = osrelease["ID"] + "-" + osrelease["VERSION_ID"] + osbuild_commit = testlib.get_osbuild_commit(distro_version) + if osbuild_commit is None: + osbuild_commit = "RELEASE" + build_info = { - "distro": distro, - "arch": arch, - "image-type": image_type, - "config": config_name, - "manifest-checksum": manifest_id, - "obuild-version": osbuild_ver.decode(), - "commit": os.environ.get("CI_COMMIT_SHA", "N/A") + "distro": distro, + "arch": arch, + "image-type": image_type, + "config": config_name, + "manifest-checksum": manifest_id, + "obuild-version": osbuild_ver.decode(), + "osbuild-commit": osbuild_commit, + "commit": os.environ.get("CI_COMMIT_SHA", "N/A") } info_file_path = os.path.join(build_dir, "info.json") - with open(info_file_path, "w") as info_fp: + with open(info_file_path, "w", encoding="utf-8") as info_fp: json.dump(build_info, info_fp, indent=2) diff --git a/test/scripts/imgtestlib.py b/test/scripts/imgtestlib.py index 6c5bfd49ab..8202bbbef6 100644 --- a/test/scripts/imgtestlib.py +++ b/test/scripts/imgtestlib.py @@ -174,6 +174,58 @@ def read_manifests(path): return manifests +def check_for_build(manifest_fname, build_info_path, osbuild_ver, osbuild_commit, errors): + # rebuild if matching build info is not found + if not os.path.exists(build_info_path): + return True + + try: + with open(build_info_path, encoding="utf-8") as build_info_fp: + dl_config = json.load(build_info_fp) + except json.JSONDecodeError as jd: + errors.append(( + f"failed to parse {build_info_path}\n" + f"{jd.msg}\n" + "Scheduling config for rebuild\n" + )) + + # check if osbuild version matches + config_osbuild_commit = dl_config.get("osbuild-commit", None) + config_osbuild_ver = dl_config.get("osbuild-version", None) + if config_osbuild_commit is None: + # TODO: remove this check when all the build infos get updated + print("osbuild commit ID not found in build info. Scheduling config for rebuild.") + return True + + osbuild_id = f"{osbuild_ver}:{osbuild_commit}" + config_osbuild_id = f"{config_osbuild_ver}:{config_osbuild_commit}" + + if osbuild_id != config_osbuild_id: + print(f"🖼️ Manifest {manifest_fname} was built with {config_osbuild_id}") + print(f" Testing {osbuild_id}") + return True + + commit = dl_config["commit"] + pr = dl_config.get("pr") + url = f"https://github.com/osbuild/images/commit/{commit}" + print(f"🖼️ Manifest {manifest_fname} was successfully built in commit {commit}\n {url}") + if pr: + print(f" PR-{pr}: https://github.com/osbuild/images/pull/{pr}") + + image_type = dl_config["image-type"] + if image_type not in CAN_BOOT_TEST: + print(f" Boot testing for {image_type} is not yet supported") + return False + + # boot testing supported: check if it's been tested, otherwise queue it for rebuild and boot + if dl_config.get("boot-success", False): + print(" This image was successfully boot tested") + return False + + # default to build + return True + + def filter_builds(manifests, skip_ostree_pull=True): """ Returns a list of build requests for the manifests that have no matching config in the test build cache. @@ -187,6 +239,14 @@ def filter_builds(manifests, skip_ostree_pull=True): errors = [] + osrelease = read_osrelease() + distro_version = osrelease["ID"] + "-" + osrelease["VERSION_ID"] + osbuild_commit = get_osbuild_commit(distro_version) + if osbuild_commit is None: + osbuild_commit = "RELEASE" + osbuild_ver, _ = runcmd(["osbuild", "--version"]) + osbuild_ver = osbuild_ver.decode() + for manifest_fname, data in manifests.items(): manifest_id = data["id"] data = data.get("data") @@ -209,33 +269,8 @@ def filter_builds(manifests, skip_ostree_pull=True): dl_config_dir = os.path.join(dl_path, distro, arch) build_info_path = os.path.join(dl_config_dir, manifest_id, "info.json") - # check if the id_fname exists in the synced directory - if os.path.exists(build_info_path): - try: - with open(build_info_path) as build_info_fp: - dl_config = json.load(build_info_fp) - commit = dl_config["commit"] - pr = dl_config.get("pr") - url = f"https://github.com/osbuild/images/commit/{commit}" - print(f"🖼️ Manifest {manifest_fname} was successfully built in commit {commit}\n {url}") - if pr: - print(f" PR-{pr}: https://github.com/osbuild/images/pull/{pr}") - if image_type not in CAN_BOOT_TEST: - print(f" Boot testing for {image_type} is not yet supported") - continue - # boot testing supported: check if it's been tested, otherwise queue it for rebuild and boot - if dl_config.get("boot-success", False): - print(" This image was successfully boot tested") - continue - except json.JSONDecodeError as jd: - errors.append(( - f"failed to parse {build_info_path}\n" - f"{jd.msg}\n" - "Scheduling config for rebuild\n" - f"Config: {distro}/{arch}/{image_type}/{config_name}\n" - )) - - build_requests.append(build_request) + if check_for_build(manifest_fname, build_info_path, osbuild_ver, osbuild_commit, errors): + build_requests.append(build_request) print("✅ Config filtering done!\n") if errors: diff --git a/test/scripts/setup-osbuild-repo b/test/scripts/setup-osbuild-repo index a22c17858c..5e86ff3f9c 100755 --- a/test/scripts/setup-osbuild-repo +++ b/test/scripts/setup-osbuild-repo @@ -22,7 +22,7 @@ def write_repo(commit, distro_version): arch = os.uname().machine repo_path = f"osbuild/{distro_version}/{arch}/{commit}" print(f"Setting up dnf repository for {commit} ({repo_path})") - with open("/etc/yum.repos.d/osbuild.repo", "w", endoding="utf-8") as repofile: + with open("/etc/yum.repos.d/osbuild.repo", "w", encoding="utf-8") as repofile: repofile.write(REPO_TEMPLATE.format(commit=commit, repo_path=repo_path))