Skip to the content.

CI/CD And SARIF

Local hooks are the first gate. CI is the independent gate that still runs when a developer or agent bypasses local enforcement.

coding-ethos emits SARIF 2.1.0 from the same normalized diagnostics used by hook and lint-capture output. SARIF results include stable policy IDs, source tool codes, ETHOS principle IDs, skill IDs, file and line locations, remediation advice, CEL provenance when applicable, policy coverage, and audit properties.

For product uses beyond CI upload, including MCP remediation, finding deduplication, audit bundles, and editor diagnostics, see SARIF_USES.md and SARIF_EDITOR_INTEGRATION.md.

Output Contract

Use --sarif on lint result paths:

bin/coding-ethos-run policy-lint --sarif --scope files --files path/to/file.py
bin/coding-ethos-run policy-lint --sarif --replay .coding-ethos/lint-runs/<trace>.json

Captured managed tools can also emit SARIF:

bin/coding-ethos-run policy-lint --managed-capture-tool ruff --sarif -- check path/to/file.py

Sandboxed managed capture is opt-in while the Bubblewrap profile is hardened:

bin/coding-ethos-run policy-lint --managed-capture-tool ruff --sandbox-mode required --sarif -- check path/to/file.py

When sandboxing is requested, SARIF records the selected backend, profile, declared capabilities, and backend denials under runs[].properties.sandbox. Pathless sandbox denials remain run-level evidence for code-scanning compatibility and are preserved in .coding-ethos lint traces.

SARIF is intentionally not supported for explanatory or aggregate analysis commands such as --explain and --analyze-log; those are operator summaries, not per-result diagnostic reports.

Code Scanning Metadata

The SARIF encoder follows the current GitHub code-scanning subset and the OASIS SARIF 2.1.0 tracking model:

Do not emit SARIF fields that are not grounded in actual evidence. In particular, fixes, codeFlows, relatedLocations, baseline state, and CWE taxa should only be added when the underlying policy or tool result supplies that information explicitly.

Reusable GitHub Workflow

Consumer GitHub and GitLab SARIF gates are generated by default through the normal managed config sync path:

generated_config:
  ci:
    github_actions:
      enabled: true
      sandbox_mode: required
    gitlab:
      enabled: true
      sandbox_mode: required

make sync-tool-configs writes the enabled CI files and records them in .code-ethos/tool-config-hashes.json; make check-tool-configs detects drift. Set either enabled value to false only for a deliberate repo exception. CI files intentionally do not have a separate sync command.

Generated CI defaults sandbox_mode to required. Local developers can use advisory auto or disabled off mode for unsupported platforms, but CI is the place where high-risk managed tool classes should fail closed if the sandbox backend is unavailable.

The reusable workflow at .github/workflows/coding-ethos-sarif.yml builds the managed runtime, runs the configured project gate, emits SARIF, uploads it to GitHub code scanning, and preserves SARIF plus .coding-ethos traces as workflow artifacts. It defaults to workflow_call only. The repo-level CI workflow should call it once and own concurrency, required jobs, actionlint, package validation, and artifact attestations. Set generated_config.ci.github_actions.standalone_triggers: true only when a repo intentionally wants the generated SARIF workflow to run directly on pull_request, push, and workflow_dispatch.

The generated workflow emits and uploads SARIF with an explicit generated_config.ci.github_actions.sarif_category value. The default is policy, which keeps GitHub code-scanning configuration identity stable when a repo calls the reusable workflow from .github/workflows/ci.yml. Repos that already have an established GitHub code-scanning stream can set this to the existing category, for example .github/workflows/ci.yml:policy. The linter also writes that category into SARIF runAutomationDetails.id using the SARIF category/ form; this avoids GitHub treating the uploaded file as a different analysis stream from the same workflow.

The SARIF job scopes file analysis to the event’s changed files. Pull requests diff against the target branch, push events diff against the pushed commit range, and explicit CODING_ETHOS_FILES input remains available for controlled reruns. If CI cannot prove a safe event diff, it emits an empty SARIF result instead of falling back to a whole-repo scan; broad historical audits should be run through an explicit audit workflow or local operator command.

name: coding-ethos

on:
  pull_request:
  push:
    branches: [main]

jobs:
  actionlint:
    runs-on: ubuntu-latest
    timeout-minutes: 10
    steps:
      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
      - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c
        with:
          go-version-file: coding-ethos/go/go.mod
      - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405
        with:
          python-version: "3.13"
      - uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78
      - run: make -C coding-ethos build
      - run: >-
          coding-ethos/bin/coding-ethos-run policy-tool actionlint
          .github/workflows/ci.yml
          .github/workflows/coding-ethos-sarif.yml

  policy:
    uses: ./coding-ethos/.github/workflows/coding-ethos-sarif.yml
    permissions:
      actions: read
      contents: read
      security-events: write

If the repository does not check out coding-ethos as a submodule, replace the make -C coding-ethos paths with the repository-local install path used by the project.

Distribution jobs should run independently of the test matrix, validate built metadata with uvx twine check dist/*, upload dist/* as artifacts, and use GitHub artifact attestations when publishing build outputs. The build job needs attestations: write and id-token: write; ordinary test and policy jobs should keep narrower contents: read permissions.

Reusable GitLab CI Template

The reusable GitLab template at ci/gitlab/coding-ethos-sarif.yml exposes a hidden .coding_ethos_sarif job. Consumers include it, extend it, and override variables only when their checkout layout differs.

include:
  - local: coding-ethos/ci/gitlab/coding-ethos-sarif.yml

coding_ethos:
  extends: .coding_ethos_sarif
  image: golang:1.24

GitLab runners should use the same compiled bundle and managed toolchain as local hooks, then keep SARIF as a job artifact. The generated GitLab config now uses policy/test/build stages, interruptible: true, job timeouts, artifact expiry, and optional test/build/package-check commands from generated_config.ci.gitlab.*. Projects can wire the SARIF artifact into their own merge-request annotation or security-reporting layer as needed. GitLab is actively adding first-class SARIF ingestion, but the durable contract is still stable identifiers, ordered locations, and consistent severity mapping. Like the GitHub workflow, the GitLab job scopes merge requests to the target branch diff, push pipelines to $CI_COMMIT_BEFORE_SHA..$CI_COMMIT_SHA, and uses an empty SARIF result when no safe diff base is available.

Operator Rules