Skip to content

Usage

Run linting rules against contract specs and surface violations before they reach a snapshot or release.

Terminal window
contractual lint [--contract <name>] [--format <fmt>]

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.


FlagDescriptionDefault
--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

terminal
$ 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.
terminal
$ 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).
terminal
$ 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 }
}

CodeMeaning
0All contracts passed linting with no error-level violations.
1One or more error-level lint violations were found.
2Configuration error, for example contractual.yaml not found or a contract name passed to --contract does not exist.
3Tool error, the configured linter binary was not found or exited unexpectedly.

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.

contractual.yaml
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.

PlaceholderReplaced with
{spec}Absolute path to the current spec file
{name}The contract’s name value

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.


Set lint: false to skip linting entirely for a contract.

contractual.yaml
contracts:
- name: order-schema
type: json-schema
path: ./schemas/order.schema.json
lint: false # Schema is validated elsewhere in CI

Setting a field to false is different from omitting it. Omitting uses the built-in default. false explicitly opts out.


contractual.yaml
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 is a fast Spectral-compatible linter:

contractual.yaml
contracts:
- name: orders-api
type: openapi
path: ./specs/orders.openapi.yaml
lint: "vacuum lint {spec} --format json"

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:

scripts/validate_schema.py
import sys
import json
spec_path = sys.argv[1]
with open(spec_path) as f:
schema = json.load(f)
errors = []
# Example: enforce that all properties have descriptions
for 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)

The GitHub Action runs linting automatically:

- uses: contractual-dev/action@v1
with:
mode: pr-check
github-token: ${{ secrets.GITHUB_TOKEN }}
Terminal window
npm install -g @contractual/cli
contractual lint --format json > lint-report.json
if [ $? -eq 1 ]; then
echo "Lint violations found"
exit 1
fi