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:
- Build your software and generate an SBOM (CycloneDX JSON recommended).
- Run
craevidence upload-sbomto upload the SBOM. Products and versions are auto-created by default. CI metadata (commit SHA, branch, pipeline ID) is captured automatically. - Optionally use
--scan --fail-on <severity>to gate the build on vulnerability findings, or usewait-readyin a subsequent step to gate on full CRA readiness. - After release, use
craevidence releaseto 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 notready(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. |
Related Documentation
- API Keys — creating and scoping CI/CD tokens
- CLI Reference — all commands and flags
- SBOM Guide — SBOM generation tools and best practices
Help us improve. What was missing or unclear?