# SPDX-License-Identifier: GPL-3.0-or-later """Remove generated package files and reset isolated build environments.""" import shutil import subprocess from pathlib import Path import project as p def remove(path): if path.is_symlink() or path.is_file(): path.unlink() elif path.exists(): shutil.rmtree(path) def clean_apt_cache(): """Remove cached APT downloads while retaining logs and build artifacts.""" cache = p.WORK / "apt-cache" if cache.is_symlink(): raise ValueError("APT cache directory must not be a symlink") remove(cache) print("APT cache cleared") def has_generated_files(module, accepted=False): """Report whether cleanup can remove any generated files for this project.""" entry = p.packages()[module] recipe, version = p.recipe(module) prepared = p.WORK / "prepared" / (module + ".json") target = p.WORK / "sources" / (entry["source"] + "-" + version) if prepared.exists() or target.exists() or target.is_symlink(): return True for path in (p.WORK / "sources").glob(entry["source"] + "_*"): if path.is_file() and not (".orig.tar." in path.name or ".orig-" in path.name): return True state = p.WORK / "state" / (module + ".json") if state.exists(): if accepted: return True keep = set(p.read(state)["artifacts"]) else: if (p.WORK / "input/modules" / (module + ".json")).exists(): return True keep = set() control = p.fields((recipe / "control").read_text()) names = {entry["source"], *(paragraph["Package"] for paragraph in control[1:])} results = p.WORK / "results" for name in names: if any( not path.name.endswith(".build") and path.name not in keep for path in results.glob(name + "_*") ): return True if any(path.name not in keep for path in results.glob(name + "-dbgsym_*.deb")): return True return False def clean(module, accepted=False): entry = p.packages()[module] recipe, version = p.recipe(module) state_path = p.WORK / "state" / (module + ".json") state = p.read(state_path) if state_path.exists() else None results = p.WORK / "results" if results.is_symlink(): raise ValueError("Results directory must not be a symlink") artifacts = list(state["artifacts"]) if state else [] if any(Path(name).name != name for name in artifacts): raise ValueError("Invalid artifact filename in build record: " + module) if accepted and (p.WORK / "repository/conf/distributions").exists(): if not (p.WORK / "controller/etc/debian_version").exists(): raise ValueError( "Run setup before removing packages from the local repository." ) subprocess.run( [ "sudo", "bash", str(p.ROOT / "tools/lib/controller-enter"), "builder", "reprepro", "--basedir", "/work/repository", "removesrc", p.read(p.ROOT / "config/release.json")["build_suite"], entry["source"], ], check=True, ) prepared = p.WORK / "prepared" / (module + ".json") targets = {p.WORK / "sources" / (entry["source"] + "-" + version)} if prepared.exists(): relative = Path(p.read(prepared)["target"]) if ( relative.is_absolute() or len(relative.parts) != 2 or relative.parts[0] != "sources" ): raise ValueError("Invalid prepared source path: " + str(relative)) targets.add(p.WORK / relative) source_root = p.WORK / "sources" for target in sorted(targets): if target.parent != source_root or source_root.is_symlink(): raise ValueError("Invalid source directory: " + str(target)) remove(target) remove(prepared) for path in source_root.glob(entry["source"] + "_*"): if path.is_file() and not (".orig.tar." in path.name or ".orig-" in path.name): remove(path) keep = set() if accepted else set(artifacts) control = p.fields((recipe / "control").read_text()) names = {entry["source"], *(paragraph["Package"] for paragraph in control[1:])} for name in sorted(names): for path in results.glob(name + "_*"): if path.name.endswith(".build") or path.name in keep: continue remove(path) for path in results.glob(name + "-dbgsym_*.deb"): if path.name not in keep: remove(path) if accepted: for name in artifacts: if not name.endswith(".build"): remove(results / name) remove(state_path) remove(p.WORK / "records/package-library-split-audit.json") if accepted or not state_path.exists(): remove(p.WORK / "input/modules" / (module + ".json")) if accepted: print( module + ": generated sources, accepted artifacts and build records cleaned" ) else: print(module + ": generated sources and unaccepted results cleaned")