feat(forgejo): add package prune script with retention depth 3
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s

List and optionally delete package versions beyond the newest three,
protecting production Helm image tags. Adds Make targets and unit tests
for ACTIVITY-WP-0020.
This commit is contained in:
tegwick 2026-07-12 11:35:04 +02:00
parent 2806f2cdda
commit 715631dedd
5 changed files with 564 additions and 1 deletions

View file

@ -0,0 +1,67 @@
from __future__ import annotations
import importlib.util
import sys
import unittest
from pathlib import Path
from unittest import mock
REPO_DIR = Path(__file__).resolve().parents[1]
SPEC = importlib.util.spec_from_file_location(
"forgejo_package_prune", REPO_DIR / "scripts/forgejo_package_prune.py"
)
prune = importlib.util.module_from_spec(SPEC)
assert SPEC.loader is not None
sys.modules[SPEC.name] = prune
SPEC.loader.exec_module(prune)
class ForgejoPackagePruneTests(unittest.TestCase):
def test_collect_protected_versions_from_helm_values(self) -> None:
import tempfile
with tempfile.TemporaryDirectory() as tmpdir:
root = Path(tmpdir)
helm = root / "helm"
helm.mkdir()
(helm / "vergabe-teilnahme-values.yaml").write_text(
"""
image:
repository: forgejo.coulomb.social/coulomb/vergabe-teilnahme
tag: "064d295"
""".lstrip(),
encoding="utf-8",
)
protected = prune.collect_protected_versions(root)
self.assertIn(("container", "vergabe-teilnahme", "064d295"), protected)
def test_build_delete_plans_respects_depth_and_protection(self) -> None:
protected = {("container", "vergabe-teilnahme", "old-prod")}
versions = [
{"version": "v4", "created_at": "2026-07-12T10:00:00Z"},
{"version": "v3", "created_at": "2026-07-11T10:00:00Z"},
{"version": "v2", "created_at": "2026-07-10T10:00:00Z"},
{"version": "v1", "created_at": "2026-07-09T10:00:00Z"},
{"version": "old-prod", "created_at": "2026-07-08T10:00:00Z"},
]
with mock.patch.object(prune, "list_packages", return_value=[{"name": "vergabe-teilnahme"}]):
with mock.patch.object(prune, "list_versions", return_value=versions):
plans, errors = prune.build_delete_plans(
base_url="https://forgejo.example",
token="token",
owner="coulomb",
package_types=["container"],
max_versions=3,
protected=protected,
)
self.assertEqual(errors, [])
deletable = {(p.name, p.version) for p in plans if not p.protected}
protected_plans = {(p.name, p.version) for p in plans if p.protected}
self.assertEqual(deletable, {("vergabe-teilnahme", "v1")})
self.assertEqual(protected_plans, {("vergabe-teilnahme", "old-prod")})
if __name__ == "__main__":
unittest.main()