DEV Community

CopperSunDev
CopperSunDev

Posted on Originally published at coppersun.dev

How to Run BrassCoders on Every Commit (and Why It Isn't Automatic)

The most common misconception about BrassCoders is that installing it means it starts watching your commits. It doesn't. BrassCoders is a command-line tool: it runs when something runs it, and nothing else. Getting a scan on every commit is a deliberate setup step, and there are exactly two ways to do it.

BrassCoders Runs When You Run It

BrassCoders is a command-line scanner you invoke with brasscoders scan, and it has no background daemon or file watcher — it never runs on its own. Installing the package with pip install brasscoders puts a CLI on your PATH. That CLI scans a directory when called, writes structured YAML to .brass/, and exits. Nothing stays resident.

This is a design choice, not a missing feature. BrassCoders removed its watch command and its monitoring module in version 2.0.9 — a resident watcher added complexity and 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.

The Two Ways to Run It on Every Commit

BrassCoders runs on every commit through one of two mechanisms — a CI step that runs on push, or a git pre-commit hook — and picking between them is really a question of where you want the gate. A CI step is enforced and central: it runs on the server, on every push, and no developer can skip it. A pre-commit hook is local and fast: it runs on the developer's machine before the commit is even created, so bad code never enters git history.

Most teams that care about this run both. The two mechanisms don't conflict, and because BrassCoders is deterministic, they never disagree about what's in the code.

Wire It Into CI on Every Push

A CI step is the enforced way to run BrassCoders on every push: the pipeline checks out the code, runs brasscoders scan, and fails the job when the scan exits non-zero. The scan runs as an ordinary build step after checkout. Because BrassCoders exits with code 1 on any CRITICAL finding, a failing scan fails the job, and branch-protection rules can require that job to pass before a merge is allowed.

Copy-paste configurations for both major platforms are already written up: Add BrassCoders to GitHub Actions and Add BrassCoders to GitLab CI. Both walk through the workflow file, uploading .brass/ as a build artifact, and configuring the check as a required gate.

Or Run It as a Pre-Commit Hook

A git pre-commit hook runs BrassCoders on the developer's machine before each commit is created, so a CRITICAL finding aborts the commit locally instead of failing a job minutes later in CI. The hook catches problems at the earliest possible point: the bug never reaches the branch, the PR, or the reviewer. For a local hook, brasscoders --offline scan is the right call — it refuses every outbound network request, so the hook has no network dependency, no timeouts, and no risk of data egress from a developer's laptop.

The full five-line setup is in Pre-Commit Hook That Stops AI-Coder Bugs, and the offline behavior is covered in Can I Run BrassCoders Offline?.

Run Both for a Real Gate

BrassCoders is deterministic, so running it as both a pre-commit hook and a CI step produces identical findings — the hook gives fast local feedback, and the CI step is the gate no one can bypass. A pre-commit hook is a convenience for the developer, and a developer can always skip it with git commit --no-verify. That's fine: the hook exists to save time, not to enforce policy. The CI step is what enforces policy, because branch protection runs on the server where --no-verify has no effect.

The pattern most teams settle on: the pre-commit hook catches the obvious problems in seconds, and the CI step guarantees nothing critical merges even if a hook was skipped. Two layers, one deterministic scanner, identical results.

How the Scan Becomes a Gate

BrassCoders turns a scan into a gate by exiting with code 1 whenever it detects a CRITICAL finding — a non-zero exit fails a CI step or aborts a commit, and a clean scan exits 0. There's no separate flag to configure and no severity threshold to tune for the default gate: the exit code is the contract. CRITICAL findings in the OSS core come primarily from Bandit (SQL injection, shell injection, unsafe deserialization), Pyre/Pysa (taint flows from user input to sensitive sinks), and the secrets detector (hardcoded credentials and API keys matching known formats).

The scan either passes or it fails. There's no degraded middle state where some findings get checked and others get skipped, and with --offline the scan can't be quietly skipped by a network problem either. That all-or-nothing behavior is what makes the exit code trustworthy as a merge gate.

Install it and run your first scan in under two minutes:

pip install brasscoders
brasscoders --offline scan /path/to/your/project
Enter fullscreen mode Exit fullscreen mode

Then pick a mechanism — CI on push, a pre-commit hook, or both — and the scan runs on every commit from there. BrassCoders needs Python 3.10 or newer, and the OSS core is Apache 2.0 licensed with no account required.

Top comments (0)