Output formats

--format selects how findings are printed: human (default), json, github, or sarif.

pgsafe migration.sql                  # human-readable (default)
pgsafe --format json migration.sql    # machine-readable
pgsafe --format github migration.sql  # GitHub Actions annotations
pgsafe --format sarif migration.sql   # SARIF 2.1.0, for GitHub code scanning

JSON

The --format json output is a versioned envelope:

{
  "schema_version": 2,
  "files": [
    {
      "file": "migration.sql",
      "findings": [
        { "rule_id": "add-index-non-concurrent", "severity": "error", ... }
      ]
    }
  ]
}

If a file cannot be parsed, its "findings" array is empty and an "error" key is added to that file’s object. Other files in the same run are still reported normally.

Pipe it into jq to filter:

pgsafe --format json migration.sql | jq '.files[].findings[] | select(.severity == "error")'

The fix object

Some findings carry an optional fix object describing an unambiguous mechanical remediation — for example, adding CONCURRENTLY to a CREATE INDEX. Advisory findings (warning-only outcomes such as DROP TABLE or RENAME) never carry one.

{
  "rule_id": "add-index-non-concurrent",
  "severity": "error",
  "message": "CREATE INDEX without CONCURRENTLY takes an AccessExclusiveLock ...",
  "fix": {
    "title": "Add CONCURRENTLY",
    "edits": [
      { "start": 12, "end": 12, "replacement": " CONCURRENTLY" }
    ]
  }
}

start and end are absolute UTF-8 byte offsets into the submitted SQL string. start == end means a pure insertion (no bytes are removed). The edits array is in ascending offset order and the ranges never overlap. Because each edit’s offsets reference the original SQL, a consumer can apply them in reverse (last to first) without adjusting any offsets, or apply them in forward order while tracking the cumulative length change.

The in-browser playground surfaces a Fix button on any finding that includes a fix object; clicking it rewrites the editor content in place.

To apply a fix from the command line, see Usage.

SARIF (GitHub code scanning)

--format sarif emits SARIF 2.1.0, for upload to GitHub code scanning:

pgsafe --format sarif db/migrate/*.sql > pgsafe.sarif

Findings (including -- pgsafe:ignore-suppressed ones, marked dismissed via SARIF suppressions) become SARIF results; a file that fails to parse becomes a tool-execution notification instead of a result. A findings run (exit 1) and a parse error (exit 2) both still write valid SARIF; a configuration or I/O error (exit 2) writes none.

To upload this into GitHub code scanning from a workflow, see CI & GitHub Action.

Severity & gating

Each rule is error or warning:

--fail-on controls which severities fail the run: warning (default — any finding fails), error (only errors fail; warnings print but exit 0), or never (report-only). Parse and I/O errors always exit 2, regardless of --fail-on. See the exit codes for gating.