Add BrassCoders to GitLab CI: Full .gitlab-ci.yml Configuration
Add BrassCoders to your GitLab CI pipeline in under 10 minutes. Full .gitlab-ci.yml config, artifact upload, merge request gates, and Paid plan license key setup.
Running brasscoders scan . locally catches bugs before you commit. Running it in GitLab CI catches bugs before your teammates merge them. The two aren’t redundant — a local scan is voluntary, a CI gate is not.
This guide wires BrassCoders into GitLab CI with a complete .gitlab-ci.yml configuration, uploads .brass/ as a downloadable artifact on every pipeline run, and shows how to make the scan block a merge request when it fails.
The Full .gitlab-ci.yml Configuration
BrassCoders runs 12 static-analysis scanners — Bandit, Pylint, Pyre/Pysa, Semgrep, ast-grep, detect-secrets, and six custom detectors for secrets, privacy/PII, phantom API calls, performance, content moderation, and JavaScript/TypeScript — and writes the union of their findings to .brass/ as YAML.
Here’s a complete, working job definition you can drop into an existing .gitlab-ci.yml or use as your starting point:
brasscoders-scan:
stage: test
image: python:3.12-slim
before_script:
- pip install brasscoders
script:
- brasscoders scan .
artifacts:
paths:
- .brass/
expire_in: 90 days
when: always
rules:
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
- if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
Each section does specific work:
image: python:3.12-slim — pulls a minimal Python 3.12 image from Docker Hub. BrassCoders requires Python 3.10+; the slim variant keeps the image pull fast. You can substitute python:3.11-slim or any image with Python 3.10+ available.
before_script — installs BrassCoders before the scan step runs. To pin a specific release (recommended for reproducibility), use pip install brasscoders==2.0.8 instead.
script — runs the scan against the project root. Point it at a subdirectory if your Python source lives elsewhere: brasscoders scan src/.
artifacts — the paths block tells GitLab to capture the .brass/ directory and make it downloadable from the pipeline’s job detail page. when: always uploads the artifact even when the job fails — critical for debugging a gate that tripped. expire_in: 90 days retains findings long enough for sprint-level review; see the audit section below for compliance-driven adjustments.
rules — the job runs on two triggers: merge requests and pushes to the default branch. This matches the pattern GitLab recommends in its CI/CD pipeline configuration reference. Merge request pipelines get their own artifact, distinct from the main-branch artifact, so reviewers can compare findings before and after a merge.
Adding the BrassCoders Paid License Key
BrassCoders Paid’s AI-powered enrichment pass reduces a typical scan’s 1500+ raw findings to roughly 300 actionable ones, ranked against a project signature derived from your README and manifest — and you activate it in GitLab CI by setting one environment variable.
In GitLab, go to your project’s Settings → CI/CD → Variables and click Add variable. Set the key to BRASS_LICENSE_KEY, paste your license key as the value, check Mask variable (so it never appears in job logs), and save. That variable is now available to every job in the project’s pipelines.
No changes to the job’s script block are needed. BrassCoders detects the BRASS_LICENSE_KEY environment variable automatically and routes the scan through the enrichment pipeline. The full updated job looks like this:
brasscoders-scan:
stage: test
image: python:3.12-slim
before_script:
- pip install brasscoders
script:
- brasscoders scan .
artifacts:
paths:
- .brass/
expire_in: 90 days
when: always
rules:
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
- if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
The only change from the OSS configuration is what happens at runtime: BrassCoders validates the key, runs the 12 local scanners, then sends already-redacted findings to the BrassCoders gateway for the deduplication and ranking step. Raw source code never leaves the runner. The .brass/ artifact at the end of a Paid scan contains enriched YAML — the same files, smaller finding sets, ranked by relevance to your project.
If the license key is absent or invalid, BrassCoders falls back to the full OSS scan and writes a status note in the artifact. The job doesn’t fail on key absence — it runs the local scanners and keeps going.
Making It a Merge Request Gate
BrassCoders exits with code 1 when any CRITICAL-severity finding is detected, and GitLab CI fails the job on a non-zero exit — but failing the job doesn’t block the merge button by default. That requires one setting change.
Go to your project’s Settings → Merge requests → Merge checks and enable Pipelines must succeed. Once that’s on, a merge request with a failed brasscoders-scan job cannot be merged. The merge button grays out with a note that pipeline checks haven’t passed.
The allow_failure: false default in GitLab CI means you don’t need to add that key explicitly. Every job fails the pipeline by default unless you opt out with allow_failure: true. The job definition above already behaves as a hard gate.
When the gate trips, here’s what the team sees: the merge request pipeline shows a red status for brasscoders-scan, the merge button is unavailable, and the .brass/ artifact is still uploaded (because of when: always). A reviewer downloads the artifact from the job detail page, opens detailed_analysis.yaml, and reads the CRITICAL findings with their file paths and evidence. Fix the findings, push the branch, and the pipeline re-runs.
That sequence is intentional. The artifact travels with the failure — you don’t need to re-run the scan locally to understand what the gate found.
Storing the Artifacts for Audit
BrassCoders’s detailed_analysis.yaml records the git commit hash, the BrassCoders version, and the per-scanner status alongside every finding, which means each artifact is a point-in-time snapshot you can trace back to a specific build.
The expire_in: 90 days setting in the base configuration keeps findings available for 90 days after the pipeline runs. For teams with SOC 2 or internal audit requirements, extend that to a year:
artifacts:
paths:
- .brass/
expire_in: 1 year
when: always
GitLab’s artifacts documentation covers the full range of accepted formats (1 week, 3 months, 1 year, never). The never option keeps artifacts indefinitely; use it only if your GitLab storage plan can absorb the growth.
For compliance-driven teams, security_report.yaml inside .brass/ is the artifact to reference. It isolates findings from Bandit, Pysa/Pyre, Semgrep, and detect-secrets — the scanners most directly tied to security controls — and excludes style and performance findings. It’s a shorter document for auditors who don’t need the full scanner output.
The commit hash in each artifact means you can reproduce any historical scan. Check out the commit, install the same BrassCoders version recorded in detailed_analysis.yaml, run brasscoders scan ., and you’ll get the same findings. That reproducibility is what makes the artifact meaningful as an audit record.
Install BrassCoders with pip install brasscoders and follow the instructions at coppersun.dev/install to activate the Paid plan or get started with the OSS core. The scan runs locally, the artifact uploads to GitLab, and the merge gate holds until critical findings are resolved.
Frequently Asked Questions
Does the BrassCoders GitLab CI job require a license key?
No. The OSS core runs with no account and no license key. Install brasscoders from PyPI, run brasscoders scan, and the job completes without outbound network calls. BrassCoders Paid ($12/month) is optional — it adds AI-powered semantic deduplication and does require a BRASS_LICENSE_KEY variable stored as a masked CI/CD variable in your GitLab project settings.
What exit code does BrassCoders return when it finds critical issues?
BrassCoders exits with code 1 when any CRITICAL-severity finding is detected. GitLab CI treats a non-zero exit as job failure. When 'Pipelines must succeed' is enabled in merge request settings, a failed brasscoders-scan job blocks the merge button.
What files are in the .brass/ artifact?
The .brass/ artifact contains ai_instructions.yaml (severity-ranked findings formatted for Claude Code or Cursor), detailed_analysis.yaml (every finding with file path, line number, scanner name, and commit hash), and security_report.yaml (a security-only view covering Bandit, Pysa, Semgrep, and detect-secrets findings).
Does BrassCoders send code to any external service during the scan?
No. The OSS core makes zero outbound network calls by default. Nothing leaves the GitLab runner. BrassCoders Paid sends already-redacted findings to the BrassCoders gateway for enrichment — never raw source code.
Which Python version should the GitLab CI job use?
BrassCoders requires Python 3.10 or higher. The example job uses python:3.12-slim, which is a safe choice. Python 3.10 and 3.11 also work.
How long should I keep .brass/ artifacts for compliance purposes?
BrassCoders's detailed_analysis.yaml records the commit hash and scanner version alongside every finding, which means each artifact is traceable to a specific build. For SOC 2 or internal audit use, set expire_in: 1 year. The default in the example config is 90 days, which covers most sprint-level review windows.