commit:     afc779b8a445cf95f1ea907bb1ac651cf9440815
Author:     Arthur Zamarin <arthurzam <AT> gentoo <DOT> org>
AuthorDate: Wed Jun 21 18:20:57 2023 +0000
Commit:     Arthur Zamarin <arthurzam <AT> gentoo <DOT> org>
CommitDate: Sat Jun 24 05:49:32 2023 +0000
URL:        
https://gitweb.gentoo.org/proj/pkgcore/pkgcheck.git/commit/?id=afc779b8

ProfilesCheck: for unknown packages, mention last match removed

https://github.com/pkgcore/pkgcheck/issues/588
Signed-off-by: Arthur Zamarin <arthurzam <AT> gentoo.org>

 src/pkgcheck/checks/profiles.py | 48 +++++++++++++++++++++++++++++++++++------
 1 file changed, 42 insertions(+), 6 deletions(-)

diff --git a/src/pkgcheck/checks/profiles.py b/src/pkgcheck/checks/profiles.py
index 3420e471..b9108f83 100644
--- a/src/pkgcheck/checks/profiles.py
+++ b/src/pkgcheck/checks/profiles.py
@@ -1,5 +1,6 @@
 """Various profile-related checks."""
 
+from datetime import datetime
 import os
 from collections import defaultdict
 from typing import Iterable
@@ -16,6 +17,21 @@ from .. import addons, base, results, sources
 from . import Check, RepoCheck
 
 
+class OutdatedProfilePackage(results.ProfilesResult, results.Warning):
+    """Profile files includes package entry that doesn't exist in the repo
+    for a mentioned period of time."""
+
+    def __init__(self, path, atom, age):
+        super().__init__()
+        self.path = path
+        self.atom = str(atom)
+        self.age = float(age)
+
+    @property
+    def desc(self):
+        return f"{self.path!r}: outdated package entry: {self.atom!r}, last 
match removed {self.age} years ago"
+
+
 class UnknownProfilePackage(results.ProfilesResult, results.Warning):
     """Profile files includes package entry that doesn't exist in the repo."""
 
@@ -190,9 +206,10 @@ class ProfilesCheck(Check):
     """Scan repo profiles for unknown flags/packages."""
 
     _source = sources.ProfilesRepoSource
-    required_addons = (addons.UseAddon, addons.KeywordsAddon)
+    required_addons = (addons.UseAddon, addons.KeywordsAddon, 
addons.git.GitAddon)
     known_results = frozenset(
         {
+            OutdatedProfilePackage,
             UnknownProfilePackage,
             UnmatchedProfilePackageUnmask,
             UnknownProfilePackageUse,
@@ -210,12 +227,20 @@ class ProfilesCheck(Check):
     # mapping between known files and verification methods
     known_files = {}
 
-    def __init__(self, *args, use_addon: addons.UseAddon, keywords_addon: 
addons.KeywordsAddon):
+    def __init__(
+        self,
+        *args,
+        use_addon: addons.UseAddon,
+        keywords_addon: addons.KeywordsAddon,
+        git_addon: addons.git.GitAddon,
+    ):
         super().__init__(*args)
         repo = self.options.target_repo
         self.keywords = keywords_addon
         self.search_repo = self.options.search_repo
         self.profiles_dir = repo.config.profiles_base
+        self.today = datetime.today()
+        self.existence_repo = git_addon.cached_repo(addons.git.GitRemovedRepo)
         self.use_expand_groups = {
             use.upper(): frozenset({val.removeprefix(f"{use}_") for val, _desc 
in vals})
             for use, vals in repo.config.use_expand_desc.items()
@@ -229,6 +254,17 @@ class ProfilesCheck(Check):
             | use_addon.global_iuse_implicit
         )
 
+    def _report_unknown_atom(self, path, atom):
+        if not isinstance(atom, atom_cls):
+            atom = atom_cls(atom)
+        if matches := self.existence_repo.match(atom):
+            removal = max(x.time for x in matches)
+            removal = datetime.fromtimestamp(removal)
+            years = (self.today - removal).days / 365.2425
+            return OutdatedProfilePackage(path, atom, round(years, 2))
+        else:
+            return UnknownProfilePackage(path, atom)
+
     @verify_files(("parent", "parents"), ("eapi", "eapi"))
     def _pull_attr(self, *args):
         """Verification only needs to pull the profile attr."""
@@ -279,7 +315,7 @@ class ProfilesCheck(Check):
     def _pkg_atoms(self, filename, node, vals):
         for x in iflatten_instance(vals, atom_cls):
             if not isinstance(x, bool) and not self.search_repo.match(x):
-                yield UnknownProfilePackage(pjoin(node.name, filename), x)
+                yield self._report_unknown_atom(pjoin(node.name, filename), x)
 
     @verify_files(
         ("package.mask", "masks"),
@@ -292,10 +328,10 @@ class ProfilesCheck(Check):
         unmasked, masked = vals
         for x in masked:
             if not self.search_repo.match(x):
-                yield UnknownProfilePackage(pjoin(node.name, filename), x)
+                yield self._report_unknown_atom(pjoin(node.name, filename), x)
         for x in unmasked:
             if not self.search_repo.match(x):
-                yield UnknownProfilePackage(pjoin(node.name, filename), x)
+                yield self._report_unknown_atom(pjoin(node.name, filename), x)
             elif x not in all_masked:
                 yield UnmatchedProfilePackageUnmask(pjoin(node.name, 
filename), x)
 
@@ -324,7 +360,7 @@ class ProfilesCheck(Check):
                             pjoin(node.name, filename), a, unknown_enabled
                         )
                 else:
-                    yield UnknownProfilePackage(pjoin(node.name, filename), a)
+                    yield self._report_unknown_atom(pjoin(node.name, 
filename), a)
 
     @verify_files(("make.defaults", "make_defaults"))
     def _make_defaults(self, filename: str, node: sources.ProfileNode, vals: 
dict[str, str]):

Reply via email to