Skip to contents

Purpose

This vignette documents how to configure an R package repository to generate a Software Bill of Materials (SBOM) as part of continuous integration (CI) using:

  • renv for reproducible dependency locking (renv.lock)
  • GitHub Actions for CI
  • Anchore SBOM Action (Syft-based) to generate an SBOM file (SPDX JSON by default)
  • reproducibleai to standardize configuration (use_sbom()) and to write additional environment metadata (write_sbom_env())

This workflow is designed to be:

  • repeatable (same steps across repos)
  • auditable (SBOM bundle includes renv.lock + environment manifest)
  • scalable (multi-language projects can share the SBOM generation approach later)

What gets produced

On every pull request and on merges to main, the workflow will generate an artifact bundle under:

  • artifacts/sbom/

Typically including:

  • sbom.spdx.json (SBOM generated by the Anchore action)
  • <Package>_env_sha-<shortsha>.txt (environment manifest generated by reproducibleai)
  • renv.lock (copied into the bundle to document the dependency lock)

Artifacts are uploaded with a target retention of 120 days, subject to GitHub org/repo policy.

Prerequisites

GitHub Actions

Your repository must have GitHub Actions enabled.

renv (required for SBOM)

SBOM generation in this workflow requires renv.lock.

If your repo does not yet use renv, initialize it from the package root:

install.packages("renv")
renv::init()

Commit the resulting files:

  • renv.lock
  • renv/activate.R

Do not commit renv/library/.

Step-by-step: enable SBOM in an R package repo

From the package root (folder containing DESCRIPTION):

1) Install reproducibleai and snapshot

Install reproducibleai using your standard method (for your environment):

pak::pak("MVR-GIS/reproducibleai")

then:

renv::snapshot()

This ensures reproducibleai is recorded in renv.lock so CI can restore it.

2) Scaffold SBOM workflow

Run once:

reproducibleai::use_sbom()

This will:

  • write .github/workflows/sbom-r.yml
  • add /artifacts/ to .gitignore
  • add ^artifacts$ to .Rbuildignore

Commit these changes.

3) Verify in CI

Merge these commits or open a pull request and confirm:

  • the sbom-r workflow runs successfully
  • the workflow run has an uploaded artifact containing artifacts/sbom/
  • the bundle includes sbom.spdx.json, renv.lock, and an env manifest file

Troubleshooting

renv::restore() fails compiling packages (common with sf stacks)

If your package depends on sf, terra, units, xml2, etc., CI may fail due to missing system libraries/headers on Ubuntu runners.

The default workflow written by use_sbom() installs a baseline set of Ubuntu packages (GDAL/PROJ/GEOS/udunits, etc.), but you may need to extend it depending on your dependency graph.

Look for errors like:

  • fatal error: ...: No such file or directory
  • configure: error: ... not found

Then add the corresponding Ubuntu apt-get package(s) to the workflow.

SBOM format (SPDX vs CycloneDX)

The Anchore SBOM Action defaults to SPDX JSON output (format: spdx-json). CycloneDX can be adopted later if/when required.

For stronger supply-chain controls, pin third-party actions to a full commit SHA rather than a moving tag like @v0. After validating the workflow, replace:

  • uses: anchore/sbom-action@v0

with a specific commit SHA.

Appendix: what write_sbom_env() records

The environment manifest includes:

  • Package name + version (from DESCRIPTION)
  • git SHA (from GITHUB_SHA in CI when available)
  • timestamp (UTC)
  • R version + platform details
  • sha256 hash of renv.lock

This provides a lightweight audit trail linking the SBOM file to the exact locked dependency specification used by the repository.