Search

Search the docs, blog, and marketplace.

Operations

CI integration

Drop-in recipes for GitHub Actions, GitLab, Jenkins, Azure DevOps, and CircleCI.

Last updated July 30, 2026View source (v0.21.1)

Drop-in recipes for running Bomly in CI. For Bomly's own CI configuration see dev-docs/CI.md in the repository.

The pattern is the same everywhere: install Bomly, run bomly scan with --audit --fail-on <severity>, upload SBOM and SARIF artifacts, and let exit code 2 fail the build on policy violations. See Exit codes.

Install strategy

Prefer package-manager installs where the runner image supports them. Otherwise use the verified install script or a pinned GitHub Release archive. Pin versions in CI rather than relying on latest.

curl -fsSL https://bomly.dev/install.sh | BOMLY_VERSION=v0.14.2 sh

GitHub Actions

name: Bomly

on:
  pull_request:
  push:
    branches: [main]

jobs:
  bomly:
    runs-on: ubuntu-latest
    permissions:
      security-events: write
      contents: read
    steps:
      - uses: actions/checkout@v4

      - name: Install Bomly
        run: |
          curl -fsSL https://bomly.dev/install.sh | BOMLY_VERSION=v0.14.2 sh

      - name: Scan
        run: |
          bomly scan --enrich --audit --fail-on high \
            --format sarif \
            -o spdx=sbom.spdx.json \
            -o cyclonedx=sbom.cdx.json \
            > bomly.sarif

      - name: Upload SARIF
        if: success() || failure()
        uses: github/codeql-action/upload-sarif@v3
        with:
          sarif_file: bomly.sarif

      - name: Upload SBOMs
        if: success() || failure()
        uses: actions/upload-artifact@v4
        with:
          name: sbom
          path: sbom.*.json

if: success() || failure() ensures SARIF and SBOM uploads run even when the scan exits 2 on policy violation.

Diff against the base branch on PRs

      - name: Diff against main
        if: github.event_name == 'pull_request'
        run: |
          git fetch origin ${{ github.base_ref }}:base
          bomly diff --base base --head HEAD \
            --enrich --audit --fail-on high \
            --json > bomly-diff.json

The diff audits only the packages the PR touched, so findings on untouched packages never fail the job. Within that scope, introduced and persisted high findings both gate — persisted means a changed package still ships a known issue at its new version.

Cache matcher data

      - name: Cache Bomly matcher data
        uses: actions/cache@v4
        with:
          path: ~/.bomly/cache
          key: bomly-${{ runner.os }}-${{ hashFiles('**/go.sum', '**/package-lock.json', '**/pom.xml') }}
          restore-keys: bomly-${{ runner.os }}-

Cuts cold-start enrichment time from minutes to seconds. Cache TTLs are listed in Matchers.

Turnkey PR reviews with Bomly Guard

The recipes above call the CLI directly. For GitHub pull requests, the Bomly Guard action wraps the same bomly diff --enrich --audit flow into a single step.

name: Bomly Guard

on:
  pull_request:

permissions:
  contents: read
  pull-requests: write
  security-events: write

jobs:
  guard:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - uses: bomly-dev/bomly-guard@v1
        with:
          fail-on: high
          deny-licenses: GPL-3.0-only
          comment-summary-in-pr: on-failure

The action's inputs map onto the CLI policy flags. See Bomly Guard for the full input and output reference.

The action installs the Bomly CLI — not project package managers. Add ecosystem setup steps (e.g. actions/setup-node, actions/setup-java) before Bomly Guard when your project uses build-tool-backed detectors or install-first. See Package Manager Setup.

GitLab CI

bomly:
  image: ubuntu:24.04
  stage: test
  before_script:
    - apt-get update && apt-get install -y curl ca-certificates
    - curl -fsSL https://bomly.dev/install.sh | BOMLY_VERSION=v0.14.2 sh
  script:
    - |
      bomly scan --enrich --audit --fail-on high \
        --format text \
        -o spdx=sbom.spdx.json \
        -o cyclonedx=sbom.cdx.json
  artifacts:
    when: always
    paths:
      - sbom.spdx.json
      - sbom.cdx.json
    reports:
      cyclonedx: sbom.cdx.json
  cache:
    key:
      files:
        - "**/go.sum"
        - "**/package-lock.json"
        - "**/pom.xml"
    paths:
      - .cache/bomly

GitLab natively renders CycloneDX SBOMs through reports:cyclonedx. To point Bomly's cache at the GitLab cache, export XDG_CACHE_HOME or configure matcher-specific cache directories in ~/.bomly/config.yaml.

Jenkins

pipeline {
  agent any
  stages {
    stage('Bomly') {
      steps {
        sh '''
          curl -fsSL https://bomly.dev/install.sh | BOMLY_VERSION=v0.14.2 sh
          bomly scan --enrich --audit --fail-on high \
            --format sarif \
            -o spdx=sbom.spdx.json \
            -o cyclonedx=sbom.cdx.json \
            > bomly.sarif
        '''
      }
      post {
        always {
          archiveArtifacts artifacts: 'bomly.sarif, sbom.*.json', fingerprint: true
          recordIssues tools: [sarif(pattern: 'bomly.sarif')]
        }
      }
    }
  }
}

recordIssues from the Warnings Next Generation plugin ingests SARIF and surfaces findings on the build page.

Azure DevOps

steps:
- script: |
    curl -fsSL https://bomly.dev/install.sh | BOMLY_VERSION=v0.14.2 sh
    bomly scan --enrich --audit --fail-on high --format sarif > bomly.sarif
  displayName: 'Bomly scan'

- task: PublishBuildArtifacts@1
  condition: succeededOrFailed()
  inputs:
    pathToPublish: bomly.sarif
    artifactName: bomly-sarif

The free SARIF SAST Scans Tab extension renders results on the build page.

CircleCI

version: 2.1
jobs:
  bomly:
    docker:
      - image: cimg/base:stable
    steps:
      - checkout
      - restore_cache:
          keys:
            - bomly-cache-v1-{{ checksum "go.sum" }}
            - bomly-cache-v1-
      - run:
          name: Install and scan
          command: |
            curl -fsSL https://bomly.dev/install.sh | BOMLY_VERSION=v0.14.2 sh
            bomly scan --enrich --audit --fail-on high \
              --format text \
              -o spdx=sbom.spdx.json \
              -o cyclonedx=sbom.cdx.json
      - save_cache:
          key: bomly-cache-v1-{{ checksum "go.sum" }}
          paths:
            - ~/.bomly/cache
      - store_artifacts:
          path: sbom.spdx.json
      - store_artifacts:
          path: sbom.cdx.json

Pre-commit hook

For local enforcement:

# .pre-commit-config.yaml
- repo: local
  hooks:
    - id: bomly
      name: bomly scan
      entry: bomly scan --audit --fail-on critical --format text
      language: system
      pass_filenames: false
      stages: [pre-push]

Tune --fail-on to taste. pre-push keeps commits fast and only runs on push.

Recommendations

  • Pin the Bomly version in CI. Use a tagged release URL or package-manager version, not latest.
  • Cache ~/.bomly/cache across runs. Matcher TTLs make this safe.
  • Always upload the SBOM even when the scan fails. The SBOM is a release artifact in its own right.
  • Use bomly diff on PRs so pre-existing findings on untouched packages don't penalize the job — only the packages the PR changes are audited.
  • Pre-warm enrichment on main with a scheduled nightly run so PR jobs start with a warm cache.

See also

  • Exit codes - what each CI exit means
  • Output formats - SARIF, JSON, SBOM details
  • Auditors - --fail-on
  • dev-docs/CI.md (in the repository) - Bomly's own internal CI configuration