Running BrassCoders on Every Commit
Installing BrassCoders doesn't make it watch your commits — it's a command-line scanner you invoke, not a background service. This guide covers the two ways to get a scan on every commit: a CI step on push, and a git pre-commit hook. It also explains why there's no auto-run mode, and how the exit code turns a scan into a gate.
A common expectation is that adding BrassCoders to a project means it starts running on its own. It doesn't. BrassCoders is a single-invocation command-line tool: it runs when something runs it, writes .brass/*.yaml, and exits. Getting a scan on every commit is a deliberate setup step — and there are exactly two places to wire it in.
This guide sets up both. Both use the same command, brasscoders --offline scan, which runs the 12-scanner pass (Bandit, Pylint, Pyre/Pysa, Semgrep, ast-grep, detect-secrets, plus six custom detectors), makes zero outbound network calls, and exits non-zero on CRITICAL findings. For the broader gating strategy — pre-commit plus CI plus a branch-protection merge gate with an audit trail — see the CI/CD Code Gates guide. This page is the focused setup for the on-every-commit cadence itself.
Why It Isn't Automatic
BrassCoders is a command you invoke — it runs when a command runs it, and nothing else, because it has no background daemon or file watcher. BrassCoders removed its watch command and its monitoring module in version 2.0.9: a resident watcher added a second, degraded code path without earning its keep. A single-invocation CLI is predictable — same command, same input, same output, then it's gone. So "on every commit" is never automatic; it's whatever cadence you wire up.
That design is why the two mechanisms below exist. Neither is a BrassCoders feature you toggle — they're standard git and CI hook points that run any command on a trigger. BrassCoders is just the command they run.
Mechanism 1: A CI Step on Push
A CI step running BrassCoders on every push is the enforced, central way to scan every commit — it runs on the server, on every contributor's push, and no developer can skip it. The scan exits non-zero on CRITICAL findings, which fails the job and blocks the merge when paired with branch protection.
GitHub Actions:
name: BrassCoders Scan
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
brasscoders:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.12'
- name: Install BrassCoders
run: pip install brasscoders
- name: Run BrassCoders scan
run: brasscoders --offline scan .
- name: Upload .brass artifact
if: always()
uses: actions/upload-artifact@v4
with:
name: brasscoders-findings
path: .brass/
retention-days: 90
The if: always() uploads the .brass/ directory even when the scan fails, so reviewers can see what was found — not just that something was. Pin a version (pip install brasscoders==2.0.12) when you need reproducible CI across an audit period.
GitLab CI is the same shape:
brasscoders:
image: python:3.12
script:
- pip install brasscoders
- brasscoders --offline scan .
artifacts:
when: always
paths:
- .brass/
expire_in: 90 days Mechanism 2: A Git Pre-Commit Hook
A git pre-commit hook runs BrassCoders on the developer's machine before the commit is created, so a CRITICAL finding blocks the commit and bad code never enters git history. It's the fastest feedback loop — seconds, local, no network — and it catches issues at the lowest cost, before anything reaches the repository.
Using the pre-commit framework, add BrassCoders as a local hook:
# pip install pre-commit brasscoders
# .pre-commit-config.yaml
repos:
- repo: local
hooks:
- id: brasscoders
name: BrassCoders scan
entry: brasscoders --offline scan
language: system
pass_filenames: false
stages: [pre-commit]
# then install the hook
pre-commit install pass_filenames: false runs BrassCoders against the full project directory rather than individual staged files — necessary because BrassCoders does project-level analysis (taint flows, cross-file secret patterns, import resolution) that needs the whole tree. Pre-commit hooks can be bypassed with git commit --no-verify, which is why the CI step above is the enforced backstop for anything that skips the hook.
Which One to Pick
Most teams run both: the pre-commit hook catches issues cheapest — nothing is in the repository yet — and the CI step is the enforced backstop that can't be bypassed with git commit --no-verify. If you run only one, run CI: it's the gate no developer can skip, it runs on external contributors' code too, and its output becomes the audit record.
How the Exit Code Gates the Build
BrassCoders gates a commit through its exit code: it exits 1 when it finds any CRITICAL issue and 0 when it doesn't, and that exit code is what a CI job or a pre-commit hook reads to fail the build or block the commit. Non-critical findings (HIGH, MEDIUM, LOW, INFO) are written to .brass/ for review but don't change the exit code.
That's the whole contract. Everything else — which findings to fix, which are false positives — happens after the gate, when a developer hands .brass/ai_instructions.yaml to Claude Code or Cursor for triage. The gate's only job is to stop a CRITICAL finding from shipping silently.
Frequently Asked Questions
Does BrassCoders run automatically on every commit?
No. BrassCoders is a command-line scanner you invoke with brasscoders scan. It has no background daemon or file watcher, so it runs only when a CI step or a git hook runs it. Scanning on every commit becomes true once you wire BrassCoders into CI on push or a git pre-commit hook.
Does BrassCoders have a watch or daemon mode?
No. BrassCoders removed its watch command and monitoring module in version 2.0.9. BrassCoders is a single-invocation CLI by design: it scans, writes YAML to .brass/, and exits. Continuous background scanning is not a feature it offers — you get on-every-commit cadence from CI or a pre-commit hook instead.
Should I use CI, a pre-commit hook, or both?
Both, if you can. The pre-commit hook is the fast local catch — it runs on the developer machine before git commit completes, so bad code never enters history. CI is the enforced backstop — it runs on every push on the server and can't be skipped with git commit --no-verify. If you pick one, pick CI: it's the gate no developer can bypass.
What makes the build or commit fail?
BrassCoders exits with code 1 when it finds any CRITICAL issue and code 0 when it doesn't. A CI job or a pre-commit hook reads that exit code to fail the build or block the commit. Non-critical findings (HIGH, MEDIUM, LOW, INFO) are written to .brass/ for review but do not affect the exit code.
Can the pre-commit hook run without network access?
Yes. brasscoders --offline scan makes zero outbound network calls and refuses any attempt to make one, which is what you want in a git hook: no timeouts, no network dependency, no accidental data egress. The OSS core is offline by default; the flag makes the guarantee explicit.