CI/CD Integration

1. Overview

CRA Evidence integrates directly into your build pipeline so that every build or release automatically records its SBOM, vulnerability scan results, and CRA compliance status.

The typical flow is:

  1. Build your software and generate an SBOM (CycloneDX JSON recommended).
  2. Run craevidence upload-sbom to upload the SBOM. Products and versions are auto-created by default. CI metadata (commit SHA, branch, pipeline ID) is captured automatically.
  3. Optionally use --scan --fail-on <severity> to gate the build on vulnerability findings, or use wait-ready in a subsequent step to gate on full CRA readiness.
  4. After release, use craevidence release to advance the version lifecycle state.

The CLI reads CRA_EVIDENCE_API_KEY and CRA_EVIDENCE_URL from the environment. No wrapper action or plugin is required for any CI system.


2. Setup

Create an API Key

In CRA Evidence, go to Settings > API Keys and create a key with at least sbom:write scope. Copy the key — it is shown only once.

Environment Variables

Variable Required Default Description
CRA_EVIDENCE_API_KEY Yes API key for authentication
CRA_EVIDENCE_URL No https://app.craevidence.com Override for self-hosted or staging instances

Store CRA_EVIDENCE_API_KEY as a secret in your CI system. Never hardcode it in pipeline files or commit it to source control.

Common mistake: The variable is CRA_EVIDENCE_API_KEY, not CRA_API_KEY. The URL variable is CRA_EVIDENCE_URL, not CRA_EVIDENCE_BASE_URL. The default URL is https://app.craevidence.com, not api.craevidence.com.

Install the CLI

pip install craevidence

Or use the Docker image if you prefer not to install Python in your CI environment:

docker run --rm \
  -e CRA_EVIDENCE_API_KEY="$CRA_EVIDENCE_API_KEY" \
  -v "$(pwd)":/workspace \
  craevidence/cli:latest \
  upload-sbom --product my-product --version 1.2.3 --file /workspace/sbom.json

3. GitHub Actions

Store your API key as a repository secret named CRA_EVIDENCE_API_KEY.

Standard Release Pipeline

name: CRA Compliance

on:
  release:
    types: [published]

jobs:
  sbom-upload:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Install Syft
        run: curl -sSfL https://raw.githubusercontent.com/anchore/syft/main/install.sh | sh -s -- -b /usr/local/bin

      - name: Generate SBOM
        run: syft . -o cyclonedx-json > sbom.json

      - name: Install CRA Evidence CLI
        run: pip install craevidence

      - name: Upload SBOM and scan
        env:
          CRA_EVIDENCE_API_KEY: ${{ secrets.CRA_EVIDENCE_API_KEY }}
        run: |
          craevidence upload-sbom \
            --product ${{ github.event.repository.name }} \
            --version ${{ github.ref_name }} \
            --file sbom.json \
            --scan \
            --fail-on high \
            --environment production

      - name: Mark version as released
        env:
          CRA_EVIDENCE_API_KEY: ${{ secrets.CRA_EVIDENCE_API_KEY }}
        run: |
          craevidence release \
            --product ${{ github.event.repository.name }} \
            --version ${{ github.ref_name }} \
            --state released

The CLI auto-detects GITHUB_SHA, GITHUB_REF_NAME, GITHUB_RUN_ID, and GITHUB_REPOSITORY. You do not need to pass --commit or --branch manually.

Development Branch Builds

on:
  push:
    branches: [main, develop]

jobs:
  sbom-upload:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: syft . -o cyclonedx-json > sbom.json
      - name: Upload SBOM
        env:
          CRA_EVIDENCE_API_KEY: ${{ secrets.CRA_EVIDENCE_API_KEY }}
        run: |
          craevidence upload-sbom \
            --product ${{ github.event.repository.name }} \
            --version ${{ github.sha }} \
            --file sbom.json \
            --environment development

4. GitLab CI

Store your API key as a CI/CD variable named CRA_EVIDENCE_API_KEY (masked, protected).

stages:
  - build
  - compliance
  - release

generate-sbom:
  stage: build
  image: anchore/syft:latest
  script:
    - syft . -o cyclonedx-json > sbom.json
  artifacts:
    paths: [sbom.json]

upload-sbom:
  stage: compliance
  image: python:3.12-slim
  needs: [generate-sbom]
  script:
    - pip install craevidence
    - |
      craevidence upload-sbom \
        --product "$CI_PROJECT_NAME" \
        --version "$CI_COMMIT_TAG" \
        --file sbom.json \
        --scan \
        --fail-on high \
        --environment production
  rules:
    - if: $CI_COMMIT_TAG

mark-released:
  stage: release
  image: python:3.12-slim
  needs: [upload-sbom]
  script:
    - pip install craevidence
    - |
      craevidence release \
        --product "$CI_PROJECT_NAME" \
        --version "$CI_COMMIT_TAG" \
        --state released
  rules:
    - if: $CI_COMMIT_TAG

The CLI auto-detects CI_COMMIT_SHA, CI_COMMIT_REF_NAME, CI_PIPELINE_ID, and CI_PROJECT_PATH from the GitLab environment.


5. Other CI Systems

Jenkins

The CLI detects JENKINS_URL and reads GIT_COMMIT, GIT_BRANCH (strips the origin/ prefix automatically), BUILD_ID, and GIT_URL.

pipeline {
    agent any
    environment {
        CRA_EVIDENCE_API_KEY = credentials('cra-evidence-api-key')
    }
    stages {
        stage('SBOM Upload') {
            steps {
                sh '''
                    pip install craevidence
                    syft . -o cyclonedx-json > sbom.json
                    craevidence upload-sbom \
                      --product my-product \
                      --version ${BUILD_TAG} \
                      --file sbom.json \
                      --scan \
                      --fail-on high
                '''
            }
        }
    }
}

Azure DevOps

The CLI detects TF_BUILD=True and reads BUILD_SOURCEVERSION, BUILD_SOURCEBRANCH (strips refs/heads/ prefix), BUILD_BUILDID, and BUILD_REPOSITORY_NAME.

- task: Bash@3
  displayName: Upload SBOM
  env:
    CRA_EVIDENCE_API_KEY: $(CRA_EVIDENCE_API_KEY)
  inputs:
    targetType: inline
    script: |
      pip install craevidence
      craevidence upload-sbom \
        --product $(Build.DefinitionName) \
        --version $(Build.BuildNumber) \
        --file sbom.json \
        --scan \
        --fail-on high

CircleCI

The CLI detects CIRCLECI=true and reads CIRCLE_SHA1, CIRCLE_BRANCH, CIRCLE_BUILD_NUM, and CIRCLE_PROJECT_REPONAME.

- run:
    name: Upload SBOM
    command: |
      pip install craevidence
      craevidence upload-sbom \
        --product my-product \
        --version $CIRCLE_TAG \
        --file sbom.json \
        --scan --fail-on high
    environment:
      CRA_EVIDENCE_API_KEY: $CRA_EVIDENCE_API_KEY

Bitbucket Pipelines

The CLI detects BITBUCKET_BUILD_NUMBER and reads BITBUCKET_COMMIT, BITBUCKET_BRANCH, BITBUCKET_BUILD_NUMBER, and BITBUCKET_REPO_FULL_NAME.

- step:
    name: Upload SBOM
    script:
      - pip install craevidence
      - |
        craevidence upload-sbom \
          --product my-product \
          --version $BITBUCKET_TAG \
          --file sbom.json \
          --scan --fail-on high

6. CI Auto-Detection

When you run upload-sbom or upload-hbom inside a CI environment, the CLI automatically reads and records CI metadata without any flags.

GitHub Actions

Detected when GITHUB_ACTIONS=true

Field Variable
Commit GITHUB_SHA
Branch GITHUB_REF_NAME
Pipeline ID GITHUB_RUN_ID
Repository GITHUB_REPOSITORY

GitLab CI

Detected when GITLAB_CI=true

Field Variable
Commit CI_COMMIT_SHA
Branch CI_COMMIT_BRANCH / CI_COMMIT_REF_NAME
Pipeline ID CI_PIPELINE_ID
Repository CI_PROJECT_PATH

Jenkins

Detected when JENKINS_URL is set

Field Variable
Commit GIT_COMMIT
Branch GIT_BRANCH (strips origin/ prefix)
Pipeline ID BUILD_ID
Repository GIT_URL

Azure DevOps

Detected when TF_BUILD=True

Field Variable
Commit BUILD_SOURCEVERSION
Branch BUILD_SOURCEBRANCH (strips refs/heads/ prefix)
Pipeline ID BUILD_BUILDID
Repository BUILD_REPOSITORY_NAME

CircleCI

Detected when CIRCLECI=true

Field Variable
Commit CIRCLE_SHA1
Branch CIRCLE_BRANCH
Pipeline ID CIRCLE_BUILD_NUM
Repository CIRCLE_PROJECT_REPONAME

Bitbucket Pipelines

Detected when BITBUCKET_BUILD_NUMBER is set

Field Variable
Commit BITBUCKET_COMMIT
Branch BITBUCKET_BRANCH
Pipeline ID BITBUCKET_BUILD_NUMBER
Repository BITBUCKET_REPO_FULL_NAME

Unsupported CI Systems

For CI platforms not listed above, set these variables manually before running the CLI:

Field Variable
Commit CRA_COMMIT_SHA
Branch CRA_BRANCH
Pipeline ID CRA_PIPELINE_ID
Repository CRA_REPOSITORY

Overriding Auto-Detection

Override any auto-detected value with explicit flags: --commit, --branch, --pipeline-id, --repository. Disable detection entirely with --no-ci-detect.


7. Quality Gates

Gate on Vulnerability Severity

Add --scan --fail-on <severity> to fail the build immediately if the uploaded SBOM contains vulnerabilities at or above the specified severity. This works on both upload-sbom and scan commands.

# Fail if any critical vulnerabilities are found
craevidence upload-sbom --product my-product --version 1.2.3 --file sbom.json \
  --scan --fail-on critical

# Fail if any high or critical vulnerabilities are found
craevidence upload-sbom --product my-product --version 1.2.3 --file sbom.json \
  --scan --fail-on high

# Fail if any medium, high, or critical vulnerabilities are found
craevidence upload-sbom --product my-product --version 1.2.3 --file sbom.json \
  --scan --fail-on medium

# Fail if any low or higher vulnerabilities are found
craevidence upload-sbom --product my-product --version 1.2.3 --file sbom.json \
  --scan --fail-on low

--fail-on is cumulative: high catches both high and critical, medium catches medium, high, and critical, and so on. Default is none (no gating).

The scan runs synchronously with the upload request. If scan results are not yet available at upload time (the scan is still queued), --fail-on will not trigger and the command exits 0. Use the two-stage pattern below when you need a guaranteed gate.

Gate on CRA Readiness (two-stage CI pattern)

The status command supports --fail-on to gate on full CRA readiness. When --fail-on is set to any value other than none, the command raises CRANonCompliantError (exit 20) if cra_status != "ready". This enables a two-stage CI pattern:

  • Stage 1 (upload-sbom --scan --fail-on high): vulnerability gate — fails with exit 10/11/12/13 if vulnerabilities exceed the threshold.
  • Stage 2 (status --fail-on high): CRA gate — fails with exit 20 if the overall CRA status is not ready (e.g., required documents are missing).
# Stage 1: Upload and fail on high vulnerabilities (exit 11 if threshold exceeded)
craevidence upload-sbom \
  --product my-product \
  --version 1.2.3 \
  --file sbom.json \
  --scan \
  --fail-on high

# Stage 2: Gate on CRA readiness (exit 20 if cra_status != "ready")
craevidence status \
  --product my-product \
  --version 1.2.3 \
  --fail-on high

For pipelines where the scan runs asynchronously after upload, use wait-ready before the status gate to ensure the scan has completed:

# Step 1: Upload (scan triggered asynchronously)
craevidence upload-sbom \
  --product my-product \
  --version 1.2.3 \
  --file sbom.json \
  --scan

# Step 2: Wait until scan completes and CRA status resolves
# Polls every 10s for up to 5 minutes. Exits 0 on ready, 1 on timeout.
craevidence wait-ready \
  --product my-product \
  --version 1.2.3 \
  --timeout 300 \
  --interval 10

# Step 3: Assert CRA readiness (exits 20 if not ready)
craevidence status \
  --product my-product \
  --version 1.2.3 \
  --fail-on high

In GitHub Actions with two jobs:

jobs:
  upload:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Upload SBOM and vulnerability gate
        env:
          CRA_EVIDENCE_API_KEY: ${{ secrets.CRA_EVIDENCE_API_KEY }}
        run: |
          craevidence upload-sbom \
            --product ${{ github.event.repository.name }} \
            --version ${{ github.ref_name }} \
            --file sbom.json \
            --scan \
            --fail-on high

  cra-gate:
    needs: upload
    runs-on: ubuntu-latest
    steps:
      - name: Wait for scan and assert CRA readiness
        env:
          CRA_EVIDENCE_API_KEY: ${{ secrets.CRA_EVIDENCE_API_KEY }}
        run: |
          craevidence wait-ready \
            --product ${{ github.event.repository.name }} \
            --version ${{ github.ref_name }} \
            --timeout 300
          craevidence status \
            --product ${{ github.event.repository.name }} \
            --version ${{ github.ref_name }} \
            --fail-on high

The two CRA status values are ready and incomplete. There is no partial status.

You can also trigger a standalone scan (without re-uploading) using the scan command:

# Trigger scan and fail on high severity (separate from upload)
craevidence scan --product my-product --version 1.2.3 --fail-on high

8. API-Only Integration

If you cannot install the CLI (restricted environment, custom toolchain), use the REST API directly. All CLI commands map to API endpoints.

Upload an SBOM

curl -X POST "https://app.craevidence.com/ci/scan" \
  -H "Authorization: Bearer $CRA_EVIDENCE_API_KEY" \
  -F "product=my-product" \
  -F "version=1.2.3" \
  -F "scan=true" \
  -F "file=@sbom.json"

Note: The CI endpoint uses multipart form (-F), not JSON body.

Check Status

curl "https://app.craevidence.com/api/v1/products/my-product/versions/1.2.3/status" \
  -H "Authorization: Bearer $CRA_EVIDENCE_API_KEY"

The response includes cra_status (ready or incomplete), scan_state, and vulnerability_summary with counts by severity.

Set Release State

curl -X POST "https://app.craevidence.com/ci/release-state" \
  -H "Authorization: Bearer $CRA_EVIDENCE_API_KEY" \
  -F "product=my-product" \
  -F "version=1.2.3" \
  -F "state=released"

Valid states: draft, pending_review, approved, released, deprecated, end_of_life.

For scripted polling (waiting for readiness without the CLI), poll the status endpoint on an interval and check cra_status == "ready".


9. Exit Codes in CI Context

Code Exception Meaning Recommended CI action
0 Success Proceed
1 CRAEvidenceError General/unexpected failure Check logs for details.
2 AuthenticationError Invalid or expired API key Rotate API key in CRA Evidence settings, update secret.
3 APIError Server returned an error Check Request ID in output and contact support if persistent.
4 ValidationError Invalid input (bad flag values, schema mismatch) Fix the command invocation. This is a caller error.
5 FileNotFoundError SBOM or HBOM file path does not exist Verify the SBOM generation step ran and produced the file.
6 ConfigurationError Missing API key or malformed URL Verify CRA_EVIDENCE_API_KEY is set and CRA_EVIDENCE_URL is valid.
10 VulnerabilityThresholdExceeded Critical vulnerabilities found (--fail-on critical) Review vulnerabilities in CRA Evidence dashboard. Do not release.
11 VulnerabilityThresholdExceeded High vulnerabilities found (--fail-on high) Review and triage high/critical findings before release.
12 VulnerabilityThresholdExceeded Medium vulnerabilities found (--fail-on medium) Review medium/high/critical findings.
13 VulnerabilityThresholdExceeded Low vulnerabilities found (--fail-on low) Review low/medium/high/critical findings.
20 CRANonCompliantError cra_status != "ready" when status --fail-on is used CRA requirements not yet met. Check status output for missing documents or open vulnerabilities.
130 Interrupted (Ctrl-C or SIGINT) Pipeline was cancelled. No action needed.

Last updated February 27, 2026
Was this page helpful?
Thanks for your feedback!

Help us improve. What was missing or unclear?