CI Integration

Sawmill can gate your CI pipeline on log quality. Use --check to exit non-zero when unwaived messages exceed a severity threshold.

Check mode

$ sawmill build.log --check

In check mode, sawmill:

  1. Parses the log file using the auto-detected (or --plugin) plugin.

  2. Loads waivers from .sawmill/waivers.toml (or --waivers).

  3. Counts unwaived messages at or above the failure threshold.

  4. Exits with code 1 if any are found, 0 otherwise.

Failure threshold

--fail-on sets the minimum severity that causes a failure:

$ sawmill build.log --check --fail-on warning

If --fail-on is omitted, the default threshold is the second-lowest severity level from the plugin. For a plugin with levels note (0) / warning (1) / error (2) / fatal (3), the default is warning (level 1) — meaning notes pass, but warnings and above fail.

Waivers in CI

Waived messages are excluded from the failure count:

$ sawmill build.log --check --waivers .sawmill/waivers.toml

If --waivers is not specified, sawmill auto-discovers .sawmill/waivers.toml (or sawmill/waivers.toml) in the current directory.

See Waivers for the waiver file format.

Suppressions do NOT affect CI

Suppressions are display-only. If you combine --suppress or --suppress-id with --check, sawmill emits a warning to stderr:

Warning: --suppress/--suppress-id affects display only, not CI pass/fail.
Use --waivers for CI acceptance.

Suppressed messages are still counted toward CI pass/fail. Use waivers to formally accept messages.

JSON reports

--report writes a JSON summary to a file:

$ sawmill build.log --check --report report.json

The report structure:

{
  "metadata": {
    "log_file": "build.log",
    "plugin": "vivado",
    "timestamp": "2026-01-15T10:30:00+00:00",
    "fail_on_level": 1
  },
  "exit_code": 0,
  "summary": {
    "total": 100,
    "suppressed": 5,
    "waived": 10,
    "by_severity": {"error": 3, "warning": 20, "note": 77},
    "waived_by_severity": {"error": 1, "warning": 5, "note": 4}
  },
  "issues": [
    {
      "message_id": "Vivado 12-3523",
      "severity": "warning",
      "content": "Attempt to change...",
      "line": 42,
      "raw_text": "WARNING: [Vivado 12-3523] ..."
    }
  ],
  "waived": [
    {
      "message_id": "Synth 8-6157",
      "severity": "warning",
      "content": "...",
      "line": 100,
      "waiver_message_id": "Synth 8-6157",
      "waiver_reason": "Expected in this design"
    }
  ],
  "suppressed": [
    {
      "message_id": "Common 17-55",
      "severity": "note",
      "content": "...",
      "line": 5,
      "raw_text": "INFO: [Common 17-55] ..."
    }
  ],
  "unused_waivers": [
    {
      "message_id": "DRC RTSTAT-10",
      "reason": "No longer triggered"
    }
  ]
}

Auditing

Show waived messages in the output:

$ sawmill build.log --show-waived

Report unused waivers (stale waivers that no longer match any messages):

$ sawmill build.log --report-unused

CI pipeline examples

GitHub Actions

- name: Check log quality
  run: |
    pip install sawmill sawmill-plugin-vivado
    sawmill build.log --check --fail-on warning --report report.json

- name: Upload report
  if: always()
  uses: actions/upload-artifact@v4
  with:
    name: sawmill-report
    path: report.json

GitLab CI

check_logs:
  script:
    - pip install sawmill sawmill-plugin-vivado
    - sawmill build.log --check --fail-on warning --report report.json
  artifacts:
    when: always
    paths:
      - report.json

Makefile

check-logs:
     sawmill build.log --check --fail-on warning --report report.json