dataform-sqlx-lint
A convention linter for Dataform .sqlx
files. SQL linters (sqlfluff) check the SQL body; dataform compile checks
syntax. Neither sees the config-block and project conventions that keep a
Dataform repo healthy — this tool does.
Zero dependencies (Python ≥ 3.11 standard library only). Designed for pre-commit.
pip install dataform-sqlx-lint
Rules
| Code | Default | Checks |
|---|---|---|
| E001 | on | config {} block present and balanced |
| E002 | on | non-empty columns: {} documentation on tables/views/incrementals/declarations |
| E003 | on | schema: must not hardcode an environment suffix (_prod/_dev by default) — --schema-suffix appends it, so a literal doubles up (looker_prod_prod) |
| E004 | on | name: matching the filename is redundant (declarations exempt) |
| E005 | opt-in | operations/assertions must not set schema: (hasOutput: true operations exempt — schema+name define ${self()}) |
| E006 | on | hardcoded `project.dataset.table` paths instead of ${ref()} — these silently break Dataform's dependency graph |
| E007 | on* | configurable per-directory naming/type policies (*no-op until policies are configured) |
| W008 | opt-in | post_operations {} placed before the main SELECT (style preference; Dataform accepts either) |
| E010 | on | every determinable output column appears in columns: {} — parses the main SELECT conservatively (unparseable expressions are skipped, never false-flagged) and follows select * through a single plain ${ref()} into the upstream file |
| E011 | on* | FOREIGN KEY written by hand in post_operations under configured paths — for projects that declare keys once in a map and generate both the DDL and the referential assertions from it (*no-op until foreign_key_paths is configured) |
Why E010 matters: columns: {} is what Dataform writes to BigQuery column
descriptions — the metadata data catalogs, BI tools, and AI/conversational
analytics agents read. Partial blocks leave silent gaps.
Usage
dataform-sqlx-lint definitions/output/my_table.sqlx [...]
# exit 0 = clean or warnings only; 1 = errors
Run from the repo root so --definitions-root (default ./definitions) can
index ${ref()} targets for E010's star-resolution.
pre-commit
repos:
- repo: https://github.com/acuantia/dataform-sqlx-lint
rev: v0.1.0
hooks:
- id: dataform-sqlx-lint
Configuration
.sqlx-lint.toml in the repo root, or a [tool.sqlx-lint] table in
pyproject.toml (the standalone file wins). All keys optional:
schema_suffixes = ["_prod", "_dev"] # E003 suffix list ([] disables)
documented_types = ["table", "view", "incremental", "declaration"] # E002
coverage_paths = ["definitions/output/"] # E010 scope; empty = everywhere
enable = ["E005", "W008"] # switch on opt-in rules
disable = ["E004"] # switch off default rules
foreign_key_paths = ["definitions/output/"] # E011 scope; empty = rule off
foreign_key_hint = "includes/keys.js" # named in E011's message
[[dir_policies]] # E007 (repeatable)
path_contains = "definitions/output/looker/"
require_prefix = "looker_"
require_types = ["table", "incremental"]
severity = "error" # or "warning"
See examples/acuantia.sqlx-lint.toml for a complete real-world config.
Suppressing findings
from `proj.raw_api.events` -- sqlx-lint: disable=E006 (declaration repoints at cutover)
or file-wide, anywhere in the file:
-- sqlx-lint: disable-file=E006
Suppress with a reason, sparingly — the convention is usually the fix.
Design notes
- Conservative by construction: the SQL projection parser only claims
column names it can determine (aliases, simple identifiers, resolvable
select *); anything ambiguous is skipped, so E010 never false-flags. - Declarations are exempt from E003/E004 deliberately: raw source datasets
legitimately carry environment-suffixed names, and Dataform requires
name:on declarations. - Rule codes are stable; gaps in the numbering are historical.
Agent Skill
skills/dataform-sqlx-lint/ contains an
Agent Skill — instructions that teach AI coding
agents (Claude Code, Gemini CLI, Cursor, or any tool supporting the open
skills format) to run this linter on every .sqlx file they create or
modify. Install it by copying the folder into your agent's skills directory —
or paste one of the ready-made prompts in
skills/dataform-sqlx-lint/INSTALL_PROMPTS.md
and let the agent install itself.
Development
python3 -m venv .venv && .venv/bin/pip install -e . pytest
.venv/bin/pytest # 59 tests
License
MIT — see LICENSE.
Metadata
Release files for dataform-sqlx-lint 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| dataform_sqlx_lint-0.2.0.tar.gz | 16.9 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| dataform_sqlx_lint-0.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 29.6 kB
Release files / dataform_sqlx_lint-0.2.0.tar.gz
| Download URL | dataform_sqlx_lint-0.2.0.tar.gz |
|---|---|
| Size | 16.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
d0d44ba76742eb6ed0c93f7d42dcdfd8f5e369225bb95ba841b797a9a31c7af2
|
|
BLAKE2b-256 checksum How to use checksums |
cb39672d4a382c91521ef88b0ac5d066c9ad4bd3889135a6c2be42dd146153d1
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.7
|
Release files / dataform_sqlx_lint-0.2.0-py3-none-any.whl
| Download URL | dataform_sqlx_lint-0.2.0-py3-none-any.whl |
|---|---|
| Size | 12.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
cfc134f0af3cc79b634b013cb65bb7be7cb3331e8c17f9030331aa812859b6dd
|
|
BLAKE2b-256 checksum How to use checksums |
db2351ee31bc7687cad42179605df4abfdbdc25d37cc985048725de1156873ba
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.7
|