@@ -63,6 +63,25 @@ def _get_chart_root(path: Path) -> Path | None:
6363 return None
6464
6565
66+ def _collect_chart_roots (paths : list [Path ]) -> set [Path ]:
67+ roots : set [Path ] = set ()
68+ for p in paths :
69+ root = _get_chart_root (p )
70+ if root :
71+ roots .add (root )
72+ return roots
73+
74+
75+ def _is_in_chart (candidate : Path , chart_roots : set [Path ]) -> bool :
76+ for chart_root in chart_roots :
77+ try :
78+ if candidate .is_relative_to (chart_root ):
79+ return True
80+ except (ValueError , TypeError ):
81+ continue
82+ return False
83+
84+
6685class HelmEdgeBuilder (EdgeBuilder ):
6786 weight = 0.70
6887 reverse_weight_factor = 0.45
@@ -79,34 +98,20 @@ def discover_related_files(
7998 if not templates and not values :
8099 return []
81100
82- chart_roots : set [Path ] = set ()
83- for t in templates :
84- root = _get_chart_root (t )
85- if root :
86- chart_roots .add (root )
87- for v in values :
88- root = _get_chart_root (v )
89- if root :
90- chart_roots .add (root )
91-
101+ chart_roots = _collect_chart_roots (templates + values )
92102 if not chart_roots :
93103 return []
94104
95- discovered : list [Path ] = []
96105 changed_set = set (changed_files )
106+ discovered : list [Path ] = []
97107
98108 for candidate in all_candidate_files :
99109 if candidate in changed_set :
100110 continue
101-
102- for chart_root in chart_roots :
103- try :
104- if candidate .is_relative_to (chart_root ):
105- if _is_helm_template (candidate ) or _is_helm_values (candidate ) or _is_chart_yaml (candidate ):
106- discovered .append (candidate )
107- break
108- except (ValueError , TypeError ):
109- continue
111+ if not (_is_helm_template (candidate ) or _is_helm_values (candidate ) or _is_chart_yaml (candidate )):
112+ continue
113+ if _is_in_chart (candidate , chart_roots ):
114+ discovered .append (candidate )
110115
111116 return discovered
112117
@@ -119,50 +124,82 @@ def build(self, fragments: list[Fragment], repo_root: Path | None = None) -> Edg
119124 return {}
120125
121126 edges : EdgeDict = {}
127+ values_idx = self ._build_values_index (values_files )
128+ define_defs = self ._build_define_index (templates )
122129
123- values_keys_by_chart : dict [Path , dict [str , list [FragmentId ]]] = defaultdict (lambda : defaultdict (list ))
130+ for tmpl in templates :
131+ self ._add_template_edges (tmpl , values_idx , define_defs , chart_files , edges )
132+
133+ return edges
134+
135+ def _build_values_index (self , values_files : list [Fragment ]) -> dict [Path , dict [str , list [FragmentId ]]]:
136+ idx : dict [Path , dict [str , list [FragmentId ]]] = defaultdict (lambda : defaultdict (list ))
124137 for vf in values_files :
125138 chart_root = _get_chart_root (vf .path ) or vf .path .parent
126- keys = _extract_yaml_keys (vf .content )
127- for key in keys :
128- values_keys_by_chart [ chart_root ][ key ]. append ( vf . id )
139+ for key in _extract_yaml_keys (vf .content ):
140+ idx [ chart_root ][ key ]. append ( vf . id )
141+ return idx
129142
130- define_defs : dict [str , list [FragmentId ]] = defaultdict (list )
143+ def _build_define_index (self , templates : list [Fragment ]) -> dict [str , list [FragmentId ]]:
144+ defs : dict [str , list [FragmentId ]] = defaultdict (list )
131145 for tmpl in templates :
132146 for match in _HELM_DEFINE_RE .finditer (tmpl .content ):
133- define_name = match .group (1 )
134- define_defs [define_name ].append (tmpl .id )
135-
136- for tmpl in templates :
137- chart_root = _get_chart_root (tmpl .path ) or tmpl .path .parent .parent
138- values_keys = values_keys_by_chart .get (chart_root , {})
139-
140- for match in _HELM_VALUES_RE .finditer (tmpl .content ):
141- value_path = match .group (1 )
142- parts = value_path .split ("." )
143-
144- for i in range (len (parts ), 0 , - 1 ):
145- partial = "." .join (parts [:i ])
146- for values_id in values_keys .get (partial , []):
147- self .add_edge (edges , tmpl .id , values_id , self .weight )
148- break
149- else :
150- continue
151- break
152-
153- if parts [0 ] in values_keys :
154- for values_id in values_keys [parts [0 ]]:
155- self .add_edge (edges , tmpl .id , values_id , self .weight * 0.8 )
156-
157- for match in _HELM_INCLUDE_RE .finditer (tmpl .content ):
158- include_name = match .group (1 )
159- for def_id in define_defs .get (include_name , []):
160- if def_id != tmpl .id :
161- self .add_edge (edges , tmpl .id , def_id , self .weight * 0.9 )
162-
163- for cf in chart_files :
164- cf_chart_root = _get_chart_root (cf .path ) or cf .path .parent
165- if cf_chart_root == chart_root :
166- self .add_edge (edges , tmpl .id , cf .id , self .weight * 0.5 )
147+ defs [match .group (1 )].append (tmpl .id )
148+ return defs
167149
168- return edges
150+ def _add_template_edges (
151+ self ,
152+ tmpl : Fragment ,
153+ values_idx : dict [Path , dict [str , list [FragmentId ]]],
154+ define_defs : dict [str , list [FragmentId ]],
155+ chart_files : list [Fragment ],
156+ edges : EdgeDict ,
157+ ) -> None :
158+ chart_root = _get_chart_root (tmpl .path ) or tmpl .path .parent .parent
159+ values_keys = values_idx .get (chart_root , {})
160+
161+ self ._add_values_ref_edges (tmpl , values_keys , edges )
162+ self ._add_include_edges (tmpl , define_defs , edges )
163+ self ._add_chart_file_edges (tmpl , chart_root , chart_files , edges )
164+
165+ def _add_values_ref_edges (self , tmpl : Fragment , values_keys : dict [str , list [FragmentId ]], edges : EdgeDict ) -> None :
166+ for match in _HELM_VALUES_RE .finditer (tmpl .content ):
167+ parts = match .group (1 ).split ("." )
168+ self ._link_longest_match (tmpl .id , parts , values_keys , edges )
169+ self ._link_root_key (tmpl .id , parts [0 ], values_keys , edges )
170+
171+ def _link_longest_match (
172+ self ,
173+ tmpl_id : FragmentId ,
174+ parts : list [str ],
175+ values_keys : dict [str , list [FragmentId ]],
176+ edges : EdgeDict ,
177+ ) -> None :
178+ for i in range (len (parts ), 0 , - 1 ):
179+ partial = "." .join (parts [:i ])
180+ values_ids = values_keys .get (partial , [])
181+ if values_ids :
182+ self .add_edge (edges , tmpl_id , values_ids [0 ], self .weight )
183+ return
184+
185+ def _link_root_key (
186+ self ,
187+ tmpl_id : FragmentId ,
188+ root_key : str ,
189+ values_keys : dict [str , list [FragmentId ]],
190+ edges : EdgeDict ,
191+ ) -> None :
192+ for values_id in values_keys .get (root_key , []):
193+ self .add_edge (edges , tmpl_id , values_id , self .weight * 0.8 )
194+
195+ def _add_include_edges (self , tmpl : Fragment , define_defs : dict [str , list [FragmentId ]], edges : EdgeDict ) -> None :
196+ for match in _HELM_INCLUDE_RE .finditer (tmpl .content ):
197+ for def_id in define_defs .get (match .group (1 ), []):
198+ if def_id != tmpl .id :
199+ self .add_edge (edges , tmpl .id , def_id , self .weight * 0.9 )
200+
201+ def _add_chart_file_edges (self , tmpl : Fragment , chart_root : Path , chart_files : list [Fragment ], edges : EdgeDict ) -> None :
202+ for cf in chart_files :
203+ cf_root = _get_chart_root (cf .path ) or cf .path .parent
204+ if cf_root == chart_root :
205+ self .add_edge (edges , tmpl .id , cf .id , self .weight * 0.5 )
0 commit comments