139 lines
5.1 KiB
Python
139 lines
5.1 KiB
Python
# 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")
|