hcl_path_equals
Query a structured document with a JSONPath expression and assert every match deep-equals the supplied value.
Semantics:
- Multiple matches — every match must equal the expected value.
- Zero matches — counts as a violation (the key the rule is enforcing doesn’t exist).
- Unparseable files — one violation per file (not silently skipped).
XML mapping applies to every XML surface: xml_path_*, json_schema_passes format: xml, and the xml: extract used by cross_file / file_graph / registry_paths_resolve. XML is mapped to the queryable tree with the xmltodict-style convention so the JSONPath reads like the XML — the document is { <root-element>: … } ($.Project…, $.project…); attributes are @name keys (['@Version'], in bracket notation, since .@Version is not valid JSONPath); a leaf element collapses to its text (<TargetFramework>net8.0</TargetFramework> → "net8.0"); namespaces flatten to the local name (Maven’s default pom.xml namespace just works). Two properties of XML’s data model to keep in mind:
- Every XML leaf value is a string. Quote expected values (
equals: "4.0.0", notequals: 4.0.0), reach for*_path_matches, and in ajson_schema_passesschema type XML fields asstringwith apattern—type: integer/boolean/numberalways fail against XML (every leaf is a string);type: array/objectadditionally depend on cardinality (next point). - Cardinality is data-dependent. A single
<dependency>is an object (a string for a leaf element); two or more become an array. A[*]wildcard therefore does not normalize cardinality —dependency[*]reads nothing for one element but every element for many, and a leaf query flips the same way (an attribute such as MSBuild’sCondition=also turns a would-be leaf string into a{ "@Condition": …, "#text": … }object). When a rule must handle both one and many, use recursive descent:$.Project.ItemGroup.ProjectReference..['@Include']reads the@Includeof one or many. Scope the..under a parent element so it does not over-match a same-named key elsewhere.
Full rationale and edge cases: docs/design/v0.10/xml_path.md.
Options
Section titled “Options”| Option | Type | Required | Default | Description |
|---|---|---|---|---|
equals | any value | yes | Expected value. Any JSON type (string, number, boolean, null, array, object). | |
if_present | boolean | false | When true, a query returning zero matches is silently OK - only real matches that fail the op produce violations. | |
path | string | yes | JSONPath expression rooted at $. Supports dot-access ($.foo.bar), array index ($.deps[0]), wildcards ($.deps[*]), filters, and every other RFC 9535 construct. |
Plus the common paths, level, id, and when fields. This table is generated from the JSON Schema; option types and defaults are authoritative.
Example
Section titled “Example”A Terraform instance on the wrong count
Section titled “A Terraform instance on the wrong count”The rule fires on this repository:
main.tfresource "aws_instance" "web" { ami = "ami-123" count = 2}With this .alint.yml:
version: 1rules: - id: single-instance kind: hcl_path_equals paths: "main.tf" path: "$.resource.aws_instance.web.count" equals: 1 level: erroralint check reports:
--- main.tf -------------------------------------------------------------------- x error single-instance value at path does not equal expected: expected 1, got 2
Summary (1 violation): x 1 error 0 passing * 1 failingA Terraform instance on the canonical count
Section titled “A Terraform instance on the canonical count”This repository is compliant:
main.tfresource "aws_instance" "web" { ami = "ami-123" count = 1}With this .alint.yml:
version: 1rules: - id: single-instance kind: hcl_path_equals paths: "main.tf" path: "$.resource.aws_instance.web.count" equals: 1 level: erroralint check reports:
v All 1 rule(s) passed.