Usage
Run linting rules against contract specs and surface violations before they reach a snapshot or release.
Command
Section titled “Command”contractual lint [--contract <name>] [--format <fmt>]How it works
Section titled “How it works”contractual lint iterates over every contract registered in contractual.yaml and runs the appropriate linter for its type. Violations are reported with file path, rule name, severity, and line number. The command exits with code 1 if any error-level violations are found.
Options
Section titled “Options”| Flag | Description | Default |
|---|---|---|
--contract <name> | Run lint only for the named contract. Repeat to target multiple contracts. | All contracts |
--format <fmt> | Output format. One of text, json. | text |
Examples
Section titled “Examples”Lint all contracts
Section titled “Lint all contracts”$ contractual lint Linting contracts... payments-api (openapi) ✖ [error] oas3-api-servers No servers defined api/openapi.yaml:1 ✔ user-schema (json-schema) No violations 1 contract(s) passed, 1 contract(s) have errors.
Lint a single contract
Section titled “Lint a single contract”$ contractual lint --contract payments-api Linting payments-api... ✖ [error] oas3-api-servers No servers defined api/openapi.yaml:1 ✖ [warn] operation-tags Operation "GET /charges" has no tags api/openapi.yaml:42 2 violation(s) found (1 error, 1 warning).
JSON output for CI reporting
Section titled “JSON output for CI reporting”$ contractual lint --format json
{
"contracts": [
{
"name": "payments-api",
"type": "openapi",
"violations": [
{
"rule": "oas3-api-servers",
"severity": "error",
"message": "No servers defined",
"path": "api/openapi.yaml",
"line": 1
}
]
},
{
"name": "user-schema",
"type": "json-schema",
"violations": []
}
],
"summary": { "passed": 1, "failed": 1, "total": 2 }
} Exit codes
Section titled “Exit codes”| Code | Meaning |
|---|---|
0 | All contracts passed linting with no error-level violations. |
1 | One or more error-level lint violations were found. |
2 | Configuration error, for example contractual.yaml not found or a contract name passed to --contract does not exist. |
3 | Tool error, the configured linter binary was not found or exited unexpectedly. |
Custom linters
Section titled “Custom linters”Override the default linter for a contract using the lint field. The value is a shell command. Use {spec} as a placeholder. Contractual replaces it with the absolute path to the spec file before running the command.
contracts: - name: orders-api type: openapi path: ./specs/orders.openapi.yaml lint: "spectral lint {spec} --format json"The command runs in the directory containing contractual.yaml. Standard output is captured and parsed. Standard error is displayed in the CLI output if the command fails.
Placeholder reference
Section titled “Placeholder reference”| Placeholder | Replaced with |
|---|---|
{spec} | Absolute path to the current spec file |
{name} | The contract’s name value |
Output format expectations
Section titled “Output format expectations”Contractual runs the custom command and inspects two things:
Exit code: zero means pass, non-zero means failure. This is the primary signal.
Standard output: Contractual attempts to parse the output to extract structured information. For the best results, use --format json with tools that support it. Contractual understands the JSON output formats of:
- Spectral (
spectral lint --format json) - Vacuum (
vacuum lint --format json)
If a command produces output in an unrecognized format, Contractual falls back to treating the raw output as a text message.
Disabling linting
Section titled “Disabling linting”Set lint: false to skip linting entirely for a contract.
contracts: - name: order-schema type: json-schema path: ./schemas/order.schema.json lint: false # Schema is validated elsewhere in CISetting a field to false is different from omitting it. Omitting uses the built-in default. false explicitly opts out.
Examples
Section titled “Examples”Spectral with a custom ruleset
Section titled “Spectral with a custom ruleset”contracts: - name: orders-api type: openapi path: ./specs/orders.openapi.yaml lint: "spectral lint {spec} --ruleset .spectral.yaml --format json"The .spectral.yaml ruleset applies alongside Contractual’s breaking change detection. Spectral handles style and compliance. Contractual handles versioning and diffs.
Vacuum
Section titled “Vacuum”Vacuum is a fast Spectral-compatible linter:
contracts: - name: orders-api type: openapi path: ./specs/orders.openapi.yaml lint: "vacuum lint {spec} --format json"Custom Python validation script
Section titled “Custom Python validation script”For contracts with a custom validation script:
contracts: - name: order-schema type: json-schema path: ./schemas/order.schema.json lint: "python scripts/validate_schema.py {spec}"The script must exit with code 0 on success and a non-zero code on failure:
import sysimport json
spec_path = sys.argv[1]
with open(spec_path) as f: schema = json.load(f)
errors = []
# Example: enforce that all properties have descriptionsfor prop_name, prop_def in schema.get("properties", {}).items(): if "description" not in prop_def: errors.append(f"Property '{prop_name}' is missing a description")
if errors: for error in errors: print(f"ERROR: {error}") sys.exit(1)
print("OK")sys.exit(0)CI integration
Section titled “CI integration”GitHub Action
Section titled “GitHub Action”The GitHub Action runs linting automatically:
- uses: contractual-dev/action@v1 with: mode: pr-check github-token: ${{ secrets.GITHUB_TOKEN }}Manual CI
Section titled “Manual CI”npm install -g @contractual/cli
contractual lint --format json > lint-report.json
if [ $? -eq 1 ]; then echo "Lint violations found" exit 1fiNext steps
Section titled “Next steps”- Overview: What linting catches and when to use it
- Breaking Change Detection: Detect breaking changes after linting passes