DEV Community

CopperSunDev
CopperSunDev

Posted on Originally published at coppersun.dev

Add BrassCoders to GitLab CI: Full .gitlab-ci.yml Configuration

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'
Enter fullscreen mode Exit fullscreen mode

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'
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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.

Top comments (0)