schema.yml files into ODCS YAML and applying it with armor contract apply.
One-line pipeline
contract apply reads from stdin when passed -f -. If the adapter maps zero models, it refuses to emit (non-zero exit) so the pipeline fails clean instead of clobbering your live config with an empty contract.
Prefer a two-step flow for anything non-trivial:
What the adapter reads
Runningarmor migrate-from dbt <project> walks the project root recursively and parses every schema.yml / schema.yaml file. It skips:
dbt_packages/anddbt_modules/(vendored third-party packages, not your models).node_modules/(front-end tooling that sometimes coexists with dbt repos).- Any hidden directory (a path segment starting with
.).
dbt_project.yml. The adapter does not call dbt compile or dbt parse, so you do not need a live target or a working profiles.yml. Static file scan only.
Mapping table
| dbt input | ODCS output | Notes |
|---|---|---|
models[].name | schema[].name | Table identity. |
models[].columns[].name | schema[].properties[].name | Column identity. |
models[].columns[].data_type | schema[].properties[].physicalType | Only emitted if present in the dbt file. |
models[].columns[].description | schema[].properties[].description | Native ODCS. |
models[].description | schema[].description | Native ODCS. |
columns[].tests[].not_null | validity rule, rule_type=not_null | AA extension. |
columns[].tests[].unique | validity rule, rule_type=unique | AA extension. |
columns[].tests[].accepted_values.values | validity rule, rule_type=allowed_values | Needs a non-empty values list. |
models[].config.freshness | freshness schedule (hours) | Best-effort: error_after preferred, falls back to warn_after. minute / hour / day periods convert to hours. |
What gets skipped (and why)
| Input shape | Reason | Resolution |
|---|---|---|
dbt_utils.* tests (e.g. dbt_utils.accepted_range) | No 1:1 map to an AA validity rule | Re-author as a custom SQL check in AnomalyArmor, or add to the request queue for adapter coverage. |
dbt_expectations.* tests | Too many variants to map safely | Same as above. |
relationships tests | Reference integrity is not modeled by AA validity rules | Track as a custom SQL check. |
Any test with a namespaced name (contains .) | Conservative skip | Adapter errs on the side of warning over silent mistranslation. |
accepted_values with empty or missing values | Would emit an empty rule | Fix the test in dbt, re-run. |
freshness with a non-minute/hour/day period | Cannot normalize to hours | Normalize in dbt or edit the emitted YAML directly. |
dbt meta, tags, owner | No round-trip design yet | Not a data-quality signal; track with AA tags or leave in dbt. |
mapped_count is zero, the adapter exits non-zero and writes nothing to stdout.
Flags
| Flag | Purpose |
|---|---|
--output, -o | Write YAML to a file instead of stdout. Pass - or omit for stdout. |
--name | Override the contract name field. Defaults to the project directory name. |
Validate before applying
contract validate runs the document against the ODCS v3.1.0 JSON Schema. Parse or schema errors print with file path, YAML path, and line number so editors and CI can surface them inline. No DB connection required beyond auth.
One dbt project, many assets
Todaycontract apply is asset-scoped (one asset per call). If your dbt project covers multiple warehouse tables:
- Translate once:
armor migrate-from dbt ./proj -o full.yaml. - Split by table or re-export per-asset from AnomalyArmor to get the target asset scoping, then apply each file against its asset.
See also
- Data Contracts (ODCS export/import)
- Migrate from Great Expectations
- anomalyarmor.ai/migrate for Soda users (ODCS export is native in Soda)
