@@ -217,35 +217,56 @@ def detect_ui(repo: str) -> bool:
217217 return False
218218
219219
220+ # Operations-file taxonomy, kept as named signal sets so the surface is documented in ONE place and
221+ # widening coverage is a data edit, not new control flow. Two literal kinds, matched by _ops_category:
222+ # _OPS_PATH_FILES — full repo-relative path (root-anchored configs like .circleci/config.yml)
223+ # _OPS_BASE_FILES — exact basename, at any depth (Jenkinsfile, Procfile, ...)
224+ # The remaining signals are shape-based (prefix/suffix/path-segment) and live in the predicates below.
225+ _YAML_EXT = (".yml" , ".yaml" )
226+ _OPS_PATH_FILES = {
227+ "ci_cd" : frozenset ((".gitlab-ci.yml" , ".circleci/config.yml" , ".travis.yml" ,
228+ "azure-pipelines.yml" , "bitbucket-pipelines.yml" )),
229+ }
230+ _OPS_BASE_FILES = {
231+ "ci_cd" : frozenset (("jenkinsfile" ,)),
232+ "containers" : frozenset (("kustomization.yaml" , "chart.yaml" )),
233+ "deploy" : frozenset (("procfile" , "fly.toml" , "vercel.json" , "netlify.toml" ,
234+ "render.yaml" , "serverless.yml" , "app.yaml" )),
235+ }
236+
237+
238+ def _is_ci_cd (low , base ):
239+ return (base in _OPS_BASE_FILES ["ci_cd" ]
240+ or low in _OPS_PATH_FILES ["ci_cd" ]
241+ or (low .startswith (".github/workflows/" ) and low .endswith (_YAML_EXT )))
242+
243+
244+ def _is_containers (low , base ):
245+ return (base == "dockerfile" or base .startswith ("dockerfile." )
246+ or base in _OPS_BASE_FILES ["containers" ]
247+ or low .endswith ((".tf" , ".tfvars" ))
248+ or ((base .startswith ("docker-compose" ) or base .startswith ("compose." )) and low .endswith (_YAML_EXT ))
249+ # k8s: filename/dir heuristic — it cannot see manifest content.
250+ or low .startswith ("k8s/" ) or "/k8s/" in low )
251+
252+
253+ def _is_deploy (low , base ):
254+ return (base in _OPS_BASE_FILES ["deploy" ]
255+ or (base .startswith ("deploy" ) and base .endswith (".sh" )))
256+
257+
258+ # Evaluated in order; the first category whose predicate matches wins.
259+ _OPS_RULES = (("ci_cd" , _is_ci_cd ), ("containers" , _is_containers ), ("deploy" , _is_deploy ))
260+
261+
220262def _ops_category (rel : str ):
221- """Classify a repo-relative path as an operations file, or None. Deterministic signal set;
222- k8s detection is a filename/dir heuristic (documented as such — it cannot see manifest content) ."""
263+ """Classify a repo-relative path into an operations category (ci_cd | containers | deploy),
264+ or None. Signal surface lives in the _OPS_* sets and the _is_* predicates above ."""
223265 low = rel .lower ()
224266 base = low .rsplit ("/" , 1 )[- 1 ]
225- # --- CI/CD ---
226- if low .startswith (".github/workflows/" ) and low .endswith ((".yml" , ".yaml" )):
227- return "ci_cd"
228- if low in (".gitlab-ci.yml" , ".circleci/config.yml" , ".travis.yml" ,
229- "azure-pipelines.yml" , "bitbucket-pipelines.yml" ):
230- return "ci_cd"
231- if base == "jenkinsfile" :
232- return "ci_cd"
233- # --- containers / infra ---
234- if base == "dockerfile" or base .startswith ("dockerfile." ):
235- return "containers"
236- if (base .startswith ("docker-compose" ) or base .startswith ("compose." )) \
237- and low .endswith ((".yml" , ".yaml" )):
238- return "containers"
239- if low .endswith ((".tf" , ".tfvars" )):
240- return "containers"
241- if base in ("kustomization.yaml" , "chart.yaml" ) or low .startswith ("k8s/" ) or "/k8s/" in low :
242- return "containers"
243- # --- deploy / PaaS ---
244- if base in ("procfile" , "fly.toml" , "vercel.json" , "netlify.toml" ,
245- "render.yaml" , "serverless.yml" , "app.yaml" ):
246- return "deploy"
247- if base .startswith ("deploy" ) and base .endswith (".sh" ):
248- return "deploy"
267+ for category , matches in _OPS_RULES :
268+ if matches (low , base ):
269+ return category
249270 return None
250271
251272
0 commit comments