55from collections import defaultdict
66from functools import partial
77from itertools import chain
8+ from urllib .parse import urlencode
89
910from pkgcheck import const as pkgcheck_const
1011from pkgcheck .addons import ArchesAddon , init_addon
1314from pkgcheck .scripts import argparse_actions
1415from pkgcore .ebuild .atom import atom
1516from pkgcore .ebuild .ebuild_src import package
17+ from pkgcore .ebuild .errors import MalformedAtom
1618from pkgcore .ebuild .misc import sort_keywords
1719from pkgcore .repository import multiplex
1820from pkgcore .restrictions import boolean , packages , values
@@ -127,6 +129,16 @@ def _get_suggested_keywords(repo, pkg: package):
127129 return frozenset ({x for x in match_keywords if "-" not in x })
128130
129131
132+ def parse_atom (pkg : str ):
133+ try :
134+ return atom (pkg )
135+ except MalformedAtom as exc :
136+ try :
137+ return atom (f"={ pkg } " )
138+ except MalformedAtom :
139+ raise exc
140+
141+
130142class GraphNode :
131143 __slots__ = ("pkgs" , "edges" , "bugno" )
132144
@@ -267,7 +279,7 @@ def _find_dependencies(self, pkg: package, keywords: set[str]):
267279 combined = boolean .AndRestriction (* set ().union (* problems .values ()))
268280 match = self .find_best_match (combined , pkgset )
269281 yield match , set (problems .keys ())
270- except ValueError :
282+ except ( ValueError , IndexError ) :
271283 results : dict [package , set [str ]] = defaultdict (set )
272284 for keyword , deps in problems .items ():
273285 match = self .find_best_match (deps , pkgset )
@@ -310,6 +322,8 @@ def output_dot(self, dot_file):
310322 dot .write ("\t rankdir=LR;\n " )
311323 for node in self .nodes :
312324 node_text = "\\ n" .join (node .lines ())
325+ if node .bugno is not None :
326+ node_text += f"\\ nbug #{ node .bugno } "
313327 dot .write (f'\t { node .dot_edge } [label="{ node_text } "];\n ' )
314328 for other in node .edges :
315329 dot .write (f"\t { node .dot_edge } -> { other .dot_edge } ;\n " )
@@ -385,6 +399,46 @@ def merge_new_keywords_children(self):
385399 found_someone = True
386400 break
387401
402+ def scan_existing_bugs (self , api_key : str ):
403+ params = urlencode (
404+ {
405+ "Bugzilla_api_key" : api_key ,
406+ "include_fields" : "id,cf_stabilisation_atoms" ,
407+ "component" : "Stabilization" ,
408+ "resolution" : "---" ,
409+ "f1" : "cf_stabilisation_atoms" ,
410+ "o1" : "anywords" ,
411+ "v1" : {pkg [0 ].unversioned_atom for node in self .nodes for pkg in node .pkgs },
412+ },
413+ doseq = True ,
414+ )
415+ request = urllib .Request (
416+ url = "https://bugs.gentoo.org/rest/bug?" + params ,
417+ method = "GET" ,
418+ headers = {
419+ "Content-Type" : "application/json" ,
420+ "Accept" : "application/json" ,
421+ },
422+ )
423+ with urllib .urlopen (request , timeout = 30 ) as response :
424+ reply = json .loads (response .read ().decode ("utf-8" ))
425+ for bug in reply ["bugs" ]:
426+ bug_atoms = (
427+ parse_atom (line .split (" " , 1 )[0 ]).unversioned_atom
428+ for line in map (str .strip , bug ["cf_stabilisation_atoms" ].splitlines ())
429+ if line
430+ )
431+ bug_match = boolean .OrRestriction (* bug_atoms )
432+ for node in self .nodes :
433+ if node .bugno is None and all (bug_match .match (pkg [0 ]) for pkg in node .pkgs ):
434+ node .bugno = bug ["id" ]
435+ self .out .write (
436+ self .out .fg ("yellow" ),
437+ f"Found https://bugs.gentoo.org/{ node .bugno } for node { node } " ,
438+ self .out .reset ,
439+ )
440+ break
441+
388442 def file_bugs (self , api_key : str , auto_cc_arches : frozenset [str ]):
389443 def observe (node : GraphNode ):
390444 self .out .write (
@@ -411,6 +465,9 @@ def main(options, out: Formatter, err: Formatter):
411465 for node in d .nodes :
412466 node .cleanup_keywords (search_repo )
413467
468+ if userquery ("Check for open bugs matching current graph?" , out , err , default_answer = False ):
469+ d .scan_existing_bugs (options .api_key )
470+
414471 if options .dot is not None :
415472 d .output_dot (options .dot )
416473 out .write (out .fg ("green" ), f"Dot file written to { options .dot } " , out .reset )
0 commit comments