What problem does this solve?
CI/CD defined in .github/ is effectively invisible to the graph. When a repo's .github/workflows/*.yml is not .cbmignored, CBM does index it — but only via the generic grammar_yaml.c path, which emits one top-level-key Variable node per file (e.g. a jobs node). No jobs, steps, action references, or job-dependency edges exist.
Concrete misses on real repos:
- No CI impact analysis. "What breaks if I change this composite action / rename this reusable workflow / remove this secret?" is unanswerable — no edges from a calling workflow to the action/workflow it
uses:.
- No reusable-workflow graph.
uses: ./.github/workflows/_test.yml (this repo does exactly this: _build.yml, _lint.yml, _test.yml, _security.yml are called as reusable workflows) produces no cross-file edge.
- No job DAG.
needs: [build, lint] ordering is dropped, so deploy-gating logic is untraceable.
- Composite/local actions (
.github/actions/**/action.yml) aren't modeled as callable units.
Evidence: on an indexed repo, .github/workflows/terraform.yaml yields exactly one node — jobs (label Variable) — and nothing else.
Proposed solution
Add a dedicated GHA extractor mirroring the existing K8s/Kustomize path (internal/cbm/extract_k8s.c, shipped for #86):
- New
CBM_LANG_GHA in lang_specs.{c,h}, detected by path:
**/.github/workflows/*.{yml,yaml} -> workflow
**/.github/actions/**/action.{yml,yaml} and repo-root action.{yml,yaml} -> action definition
- New
internal/cbm/extract_gha.c, dispatched from cbm.c alongside the existing if (lang == K8S || KUSTOMIZE) cbm_extract_k8s() (~line 595). Reuse the vendored YAML tree-sitter grammar and extract_k8s.c's get_scalar_text / flow_node-unwrap helpers.
- Nodes:
Workflow (per file) -> Job (per jobs.<id>) -> Step (per steps[]).
- Edges:
uses: ./.github/workflows/x.yml -> IMPORTS/CALLS to the local reusable Workflow (cross-file).
uses: owner/repo/...@ref -> external action node (mirrors existing external-dep representation).
uses: ./.github/actions/foo -> edge to local composite-action node.
needs: [a, b] -> Job->Job dependency edge.
- optional:
secrets: / with: inputs as Fields for config tracing.
Public OSS test beds (rich reusable workflows + composite actions):
cli/cli — many workflows + composite actions.
grafana/grafana — heavy reusable-workflow (uses: ./.github/workflows/...).
actions/checkout — canonical action.yml (node action).
home-assistant/core — large job DAGs with needs:.
DeusData/codebase-memory-mcp (this repo) — _build/_lint/_test/_security/_smoke/_soak.yml reusable-workflow pattern.
Alternatives considered
- Generic YAML only (status quo): loses all structure; a workflow = one opaque
Variable. No CI impact analysis.
- Treat
uses: as a string via search_code: grep-findable but no edges, so trace_path/impact queries can't traverse CI.
- External GHA linters (actionlint): validate syntax, do not populate a cross-file/cross-repo code graph.
Confirmations
What problem does this solve?
CI/CD defined in
.github/is effectively invisible to the graph. When a repo's.github/workflows/*.ymlis not.cbmignored, CBM does index it — but only via the genericgrammar_yaml.cpath, which emits one top-level-keyVariablenode per file (e.g. ajobsnode). No jobs, steps, action references, or job-dependency edges exist.Concrete misses on real repos:
uses:.uses: ./.github/workflows/_test.yml(this repo does exactly this:_build.yml,_lint.yml,_test.yml,_security.ymlare called as reusable workflows) produces no cross-file edge.needs: [build, lint]ordering is dropped, so deploy-gating logic is untraceable..github/actions/**/action.yml) aren't modeled as callable units.Evidence: on an indexed repo,
.github/workflows/terraform.yamlyields exactly one node —jobs(labelVariable) — and nothing else.Proposed solution
Add a dedicated GHA extractor mirroring the existing K8s/Kustomize path (
internal/cbm/extract_k8s.c, shipped for #86):CBM_LANG_GHAinlang_specs.{c,h}, detected by path:**/.github/workflows/*.{yml,yaml}-> workflow**/.github/actions/**/action.{yml,yaml}and repo-rootaction.{yml,yaml}-> action definitioninternal/cbm/extract_gha.c, dispatched fromcbm.calongside the existingif (lang == K8S || KUSTOMIZE) cbm_extract_k8s()(~line 595). Reuse the vendored YAML tree-sitter grammar andextract_k8s.c'sget_scalar_text/ flow_node-unwrap helpers.Workflow(per file) ->Job(perjobs.<id>) ->Step(persteps[]).uses: ./.github/workflows/x.yml->IMPORTS/CALLSto the local reusableWorkflow(cross-file).uses: owner/repo/...@ref-> external action node (mirrors existing external-dep representation).uses: ./.github/actions/foo-> edge to local composite-action node.needs: [a, b]->Job->Jobdependency edge.secrets:/with:inputs asFields for config tracing.Public OSS test beds (rich reusable workflows + composite actions):
cli/cli— many workflows + composite actions.grafana/grafana— heavy reusable-workflow (uses: ./.github/workflows/...).actions/checkout— canonicalaction.yml(node action).home-assistant/core— large job DAGs withneeds:.DeusData/codebase-memory-mcp(this repo) —_build/_lint/_test/_security/_smoke/_soak.ymlreusable-workflow pattern.Alternatives considered
Variable. No CI impact analysis.uses:as a string via search_code: grep-findable but no edges, sotrace_path/impact queries can't traverse CI.Confirmations