taler-deployment

Deployment scripts and configuration files
Log | Files | Refs | README

commit 3d0f8e6be1ebe7ad5a3aaa5d2cb6343852b5db4e
parent 057181985eec94a8d896c30197627082b85aed97
Author: Florian Dold <dold@taler.net>
Date:   Sun, 23 Aug 2026 12:40:19 +0200

packaging/ng: remove stale local package builds

Diffstat:
Mpackaging/ng/buildscripts/generic | 44++++++++++++++++++++++++++++++++++++--------
Apackaging/ng/testing/test_buildscript_generic.py | 60++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 96 insertions(+), 8 deletions(-)

diff --git a/packaging/ng/buildscripts/generic b/packaging/ng/buildscripts/generic @@ -13,8 +13,9 @@ import sys import subprocess import glob import shutil -import shlex from email.utils import formatdate +from pathlib import Path + def run_cmd(cmd, shell=False, cwd=None, env=None): """Helper to run commands and exit on failure (mimicking set -e).""" @@ -28,10 +29,28 @@ def run_cmd(cmd, shell=False, cwd=None, env=None): subprocess.check_call(cmd, shell=shell, cwd=cwd, env=command_env) + def get_output(cmd, shell=False, cwd=None): """Helper to get command output as string.""" return subprocess.check_output(cmd, shell=shell, cwd=cwd, text=True).strip() + +def remove_stale_packages(pkgdir): + """Remove package artifacts not referenced by a current-build manifest.""" + pkgdir = Path(pkgdir) + current_packages = set() + for manifest in pkgdir.glob("*.built.current"): + current_packages.update(manifest.read_text().split()) + + for artifact in sorted(pkgdir.iterdir()): + if artifact.suffix not in (".deb", ".ddeb"): + continue + if artifact.name in current_packages: + continue + print(f"Removing stale local package {artifact.name}") + artifact.unlink() + + def get_tag_debver(tag): """Get a debian version string from a git tag""" if tag.startswith("v"): @@ -179,13 +198,9 @@ def main(): for f in ddeb_files: shutil.copy(f, "/pkgdir/") - # Save built filenames - built_current_path = f"/pkgdir/{PACKAGE}@{ARCH}.built.current" - with open(built_current_path, 'w') as f: - for deb in deb_files: - f.write(os.path.basename(deb) + "\n") - for ddeb in ddeb_files: - f.write(os.path.basename(ddeb) + "\n") + built_artifacts = { + os.path.basename(artifact) for artifact in deb_files + ddeb_files + } # 6. Testing # Re-scan @@ -240,6 +255,19 @@ def main(): print(f"Installed binary {fname} has linker issue") sys.exit(2) + # Record the successful build before removing packages that are no longer + # current for any component or architecture. + built_current_path = f"/pkgdir/{PACKAGE}@{ARCH}.built.current" + with open(built_current_path, "w") as f: + for artifact in sorted(built_artifacts): + f.write(artifact + "\n") + + remove_stale_packages("/pkgdir") + + # Ensure the local repository metadata does not refer to removed packages. + os.chdir("/pkgdir") + run_cmd("dpkg-scanpackages . | xz - > /pkgdir/Packages.xz", shell=True) + # Finalize tag with open(f"/pkgdir/{PACKAGE}@{ARCH}.built.tag", "w") as f: f.write(TAG + "\n") diff --git a/packaging/ng/testing/test_buildscript_generic.py b/packaging/ng/testing/test_buildscript_generic.py @@ -0,0 +1,60 @@ +#!/usr/bin/env python3 +# This file is in the public domain. + +import importlib.machinery +import importlib.util +import tempfile +import unittest +from pathlib import Path + +SCRIPT_PATH = Path(__file__).parents[1] / "buildscripts" / "generic" +LOADER = importlib.machinery.SourceFileLoader("buildscript_generic", str(SCRIPT_PATH)) +SPEC = importlib.util.spec_from_loader(LOADER.name, LOADER) +GENERIC = importlib.util.module_from_spec(SPEC) +LOADER.exec_module(GENERIC) + + +class RemoveStalePackagesTests(unittest.TestCase): + def test_removes_only_unreferenced_package_artifacts(self): + with tempfile.TemporaryDirectory() as tmp: + pkgdir = Path(tmp) + current = { + "component_2.0_amd64.deb", + "component-dbgsym_2.0_amd64.ddeb", + "other_1.0_arm64.deb", + } + (pkgdir / "component@amd64.built.current").write_text( + "component_2.0_amd64.deb\n" "component-dbgsym_2.0_amd64.ddeb\n" + ) + (pkgdir / "other@arm64.built.current").write_text("other_1.0_arm64.deb\n") + + stale = { + "component_1.0_amd64.deb", + "component-dbgsym_1.0_amd64.ddeb", + "orphan_0.1_all.deb", + } + for name in current | stale: + (pkgdir / name).touch() + (pkgdir / "Packages.xz").touch() + (pkgdir / "build.changes").touch() + + GENERIC.remove_stale_packages(pkgdir) + + self.assertTrue(all((pkgdir / name).exists() for name in current)) + self.assertTrue(all(not (pkgdir / name).exists() for name in stale)) + self.assertTrue((pkgdir / "Packages.xz").exists()) + self.assertTrue((pkgdir / "build.changes").exists()) + + def test_removes_all_packages_when_no_manifest_references_them(self): + with tempfile.TemporaryDirectory() as tmp: + pkgdir = Path(tmp) + (pkgdir / "orphan_1.0_all.deb").touch() + (pkgdir / "orphan-dbgsym_1.0_all.ddeb").touch() + + GENERIC.remove_stale_packages(pkgdir) + + self.assertEqual([], list(pkgdir.iterdir())) + + +if __name__ == "__main__": + unittest.main()