MCPcopy Create free account
hub / github.com/Graphify-Labs/graphify / extract_terraform

Function extract_terraform

graphify/extract.py:15729–15899  ·  view source on GitHub ↗

Extract Terraform/HCL blocks and the references between them via tree-sitter. Nodes: resources, data sources, modules, variables, outputs, providers, and locals. Edges: `contains` (file -> block), `references` (block -> the blocks it interpolates, e.g. `aws_instance.web` -> `var.region`

(path: Path)

Source from the content-addressed store, hash-verified

15727
15728
15729def extract_terraform(path: Path) -> dict:
15730 """Extract Terraform/HCL blocks and the references between them via tree-sitter.
15731
15732 Nodes: resources, data sources, modules, variables, outputs, providers, and
15733 locals. Edges: `contains` (file -> block), `references` (block -> the blocks
15734 it interpolates, e.g. `aws_instance.web` -> `var.region`), and `depends_on`
15735 (explicit dependency edges).
15736
15737 Node IDs are scoped by the parent directory, not the file stem, because
15738 Terraform resources are module(directory)-scoped: a resource defined in
15739 main.tf is referenced from other .tf files in the same directory. Directory
15740 scoping lets those cross-file references resolve when per-file extractions
15741 are merged (stem scoping would split a definition from its references).
15742 """
15743 try:
15744 import tree_sitter_hcl as tshcl
15745 from tree_sitter import Language, Parser
15746 except ImportError:
15747 return {"nodes": [], "edges": [], "error": "tree_sitter_hcl not installed. Run: pip install tree-sitter-hcl"}
15748
15749 try:
15750 language = Language(tshcl.language())
15751 parser = Parser(language)
15752 source = path.read_bytes()
15753 tree = parser.parse(source)
15754 root = tree.root_node
15755 except Exception as e:
15756 return {"nodes": [], "edges": [], "error": str(e)}
15757
15758 str_path = str(path)
15759 file_nid = _make_id(str_path)
15760 scope = path.parent.name or "tf"
15761
15762 nodes: list[dict] = [{"id": file_nid, "label": path.name, "file_type": "code",
15763 "source_file": str_path, "source_location": None}]
15764 edges: list[dict] = []
15765 seen_ids: set[str] = {file_nid}
15766 seen_edges: set[tuple[str, str, str]] = set()
15767
15768 def _read(n) -> str:
15769 return source[n.start_byte:n.end_byte].decode("utf-8", errors="replace")
15770
15771 def _label_text(n) -> str:
15772 return _read(n).strip().strip('"')
15773
15774 def _add_node(address: str, label: str, line: int) -> str:
15775 nid = _make_id(scope, address)
15776 if nid not in seen_ids:
15777 seen_ids.add(nid)
15778 nodes.append({"id": nid, "label": label, "file_type": "code",
15779 "source_file": str_path, "source_location": f"L{line}"})
15780 edges.append({"source": file_nid, "target": nid, "relation": "contains",
15781 "confidence": "EXTRACTED", "source_file": str_path,
15782 "source_location": f"L{line}", "weight": 1.0})
15783 return nid
15784
15785 def _add_edge(src: str, address: str, relation: str, line: int) -> None:
15786 tgt = _make_id(scope, address)

Calls 6

_make_idFunction · 0.90
_block_partsFunction · 0.85
_body_ofFunction · 0.85
_readFunction · 0.85
_collect_refsFunction · 0.85
_add_nodeFunction · 0.70