GitHub Actions has a native action, so there is no container syntax to write. It is also the only runtime that currently supports OIDC trusted publishing and build provenance attestation, both of which depend on GitHub-issued identity tokens.
Minimal example
name: Generate SBOM
on:
push:
branches: [main]
pull_request:
jobs:
sbom:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: sbomify/sbomify-action@v26.8.0
env:
LOCK_FILE: package-lock.json
OUTPUT_FILE: sbom.cdx.json
ENRICH: true
UPLOAD: false
- uses: actions/upload-artifact@v7
with:
name: sbom
path: sbom.cdx.jsonNo account required. Swap the lockfile for whichever one your project uses.
Uploading without a token
Prefer OIDC trusted publishing over a long-lived secret. Create the trusted publisher binding in sbomify first - Component, Settings, Trusted Publishing - or the exchange returns 403.
permissions:
contents: read
id-token: write
jobs:
sbom:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: sbomify/sbomify-action@v26.8.0
env:
COMPONENT_ID: your-component-id
LOCK_FILE: requirements.txt
AUGMENT: true
ENRICH: trueThe id-token: write permission is what makes this work. If TOKEN is also set it takes precedence and OIDC is skipped, so remove it to force tokenless publishing.
Full details in publishing.
With a token instead
- uses: sbomify/sbomify-action@v26.8.0
env:
TOKEN: ${{ secrets.SBOMIFY_TOKEN }}
COMPONENT_ID: your-component-id
LOCK_FILE: requirements.txt
AUGMENT: true
ENRICH: trueAttestation
Sign the SBOM in the same job that produced it:
permissions:
contents: read
id-token: write
attestations: write
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: sbomify/sbomify-action@v26.8.0
env:
LOCK_FILE: Cargo.lock
OUTPUT_FILE: sbom.cdx.json
COMPONENT_NAME: my-app
COMPONENT_VERSION: ${{ github.ref_name }}
ENRICH: true
UPLOAD: false
- uses: actions/attest-build-provenance@v4
with:
subject-path: sbom.cdx.jsonactions/attest-build-provenance is not available on private or internal repositories on Free, Pro or Team plans, or on GitHub Enterprise Server at all. Check the availability table before adding it, or sign with cosign instead.
Container images
- uses: sbomify/sbomify-action@v26.8.0
env:
DOCKER_IMAGE: ghcr.io/${{ github.repository }}:${{ github.sha }}
OUTPUT_FILE: container-sbom.cdx.json
COMPONENT_NAME: ${{ github.repository }}
COMPONENT_VERSION: ${{ github.sha }}
ENRICH: true
UPLOAD: falseThe image must be pullable from the runner, so push it or load it into the local daemon first. Chainguard base images are detected automatically and their published SBOM is reused.
Caching
Worth doing - it avoids re-downloading the license database on every run, and reduces exposure to rate limits.
- uses: actions/cache@v6
with:
path: .sbomify-cache
key: sbomify-${{ runner.os }}
- uses: sbomify/sbomify-action@v26.8.0
env:
SBOMIFY_CACHE_DIR: ${{ github.workspace }}/.sbomify-cache
SYFT_CACHE_DIR: ${{ github.workspace }}/.sbomify-cache/syft
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
LOCK_FILE: requirements.txt
ENRICH: true
UPLOAD: falseSetting GITHUB_TOKEN here is strongly recommended even on GitHub Actions - it raises the license database download limit from 60 to 5,000 requests per hour.
Monorepos
The workflow-level working-directory: setting does not affect this action, because it runs in a container. Use the working-dir input:
- uses: sbomify/sbomify-action@v26.8.0
with:
working-dir: packages/my-app
env:
LOCK_FILE: package-lock.json
OUTPUT_FILE: sbom.cdx.json
ENRICH: true
UPLOAD: falseFor several components, use a matrix - see advanced usage.
Tagging releases
- uses: sbomify/sbomify-action@v26.8.0
env:
COMPONENT_ID: your-component-id
LOCK_FILE: requirements.txt
COMPONENT_VERSION: ${{ github.ref_name }}
PRODUCT_RELEASE: '["your-product-id:${{ github.ref_name }}"]'
AUGMENT: true
ENRICH: trueRun this on tag pushes rather than every commit. See product releases.
VCS detection
Automatic. Repository URL, commit SHA and branch or tag are read from the environment and added to the SBOM, including on GitHub Enterprise Server. Nothing to configure.
Pull requests from forks
Secrets are not exposed to fork pull requests, so uploads will fail. Verify generation without uploading:
- uses: sbomify/sbomify-action@v26.8.0
env:
LOCK_FILE: requirements.txt
OUTPUT_FILE: sbom.cdx.json
ENRICH: true
UPLOAD: ${{ github.event.pull_request.head.repo.fork && 'false' || 'true' }}Version pinning
sbomify/sbomify-action@v26.8.0 is readable and fine for most projects. For production, pin to a full 40-character commit SHA - the only reference GitHub treats as immutable. The setup wizard does this automatically.
Let the wizard write it
The wizard generates a complete workflow, including SHA pins and matrix entries per lockfile:
docker run --rm -it \
-v "$(pwd):/github/workspace" \
-w /github/workspace \
ghcr.io/sbomify/sbomify-action \
sbomify-action wizardIt writes .github/workflows/sboms.yml and will never overwrite a workflow you wrote yourself. See the quick start.
Next steps
- Configuration reference - every option
- Publishing - OIDC, releases, Dependency Track
- Advanced - attestation, audit trail, troubleshooting