This is the multi-page printable view of this section. Click here to print.

Return to the regular view of this page.

Ecosystem Guides

For each language, what to prepare for the Lifecycle scan, and the Tier 1 job that prepares it.

What these guides are

Under the Pull Request CI & Secrets standard, a Tier 1 job (no secrets) prepares a scan input, and a separate Tier 2 job (with secrets) hands it to Sonatype Lifecycle. Which files Lifecycle can accurately evaluate depends on the language. These guides record, for each one:

  • What Lifecycle reads, and so what the scan input must contain.
  • A prepare-scan-input job to paste into your ci.yaml (see Reference Workflows).
  • What accuracy you give up compared with scanning a full build, so that nobody is surprised.

The Tier 2 workflow is the same for every language. Only the preparation differs.

Two kinds of ecosystem

KindExamplesWhat the scan input isAccuracy
ManifestGo, Rust, PHP, Ruby, Python, .NET, RA lockfile or a dependency list, produced by the language’s own toolingGood, if the lockfile or list pins exact versions. Transitive dependencies depend on what the file contains.
BinaryJava, npm with node_modules, DockerThe built artifact, or the installed dependenciesBest. Lifecycle fingerprints the files themselves, which also finds modified or repackaged dependencies.

Binary inputs are large. Allow for that in the artifact limits (see Reference Workflows).

The guides

EcosystemUsed byGuide
GoNancy, Cheque, Terraform providers, API clientsGo
Java (Maven, Gradle)Sherlock Trunks, Nexus CasC Plugin, API clientsJava
JavaScript (npm, Yarn)AuditJS, API clientsJavaScript
PythonJake, API clientsPython
.NET (NuGet)No current Project.NET
Rust (Cargo)Cargo PantsRust
Ruby (Bundler)ChelseaRuby
PHP (Composer)BachPHP
R (CRAN)OysterRR
C/C++ (Conan)No current ProjectC/C++
Docker imagesContainer image ProjectsDocker

Terraform providers are written in Go, so their Lifecycle scan is the Go guide.

Not yet covered: Dart and Flutter, Swift, Objective-C, Conda, RPM, and Hugging Face. Sonatype supports these. If your Project needs one of them, or uses a tool or ecosystem not listed at all, see Your tool or ecosystem is not covered.

Adding a guide

Each guide follows the same shape so they can be compared: what Lifecycle reads, the prepare-scan-input job, what you give up, and a link to the Sonatype page it came from. Quote Sonatype’s documentation, and say “not documented” where it is silent.

1 - .NET

Preparing the Lifecycle scan input for NuGet projects.

What Lifecycle reads

Sonatype’s documentation says Lifecycle scans .nupkg files and PE binaries (.dll, .exe and others), as a binary scan, and project files (.csproj, packages.config) as a manifest scan. In manifest mode “only components with an exact version specified are evaluated”, floating versions such as 3.6.* are not supported, and transitive dependencies are not included.

Running dotnet restore is not required, but it is recommended for a more complete analysis. Restoring into a directory (dotnet restore --packages packages) gives a folder of packages to scan.

Tier 1 job

  prepare-scan-input:
    name: Prepare Lifecycle scan input
    runs-on: ubuntu-latest
    permissions:
      contents: read
    steps:
      - name: Checkout
        uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
        with:
          persist-credentials: false

      - name: Set up .NET
        uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0
        with:
          global-json-file: global.json

      - name: Restore packages
        run: |
          mkdir scan-input
          dotnet restore --packages scan-input/packages
          find . -name '*.csproj' -not -path './scan-input/*' -exec cp --parents {} scan-input/ \;

      - name: Upload scan input
        uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
        with:
          name: lifecycle-scan-input
          path: scan-input/
          if-no-files-found: error
          retention-days: 3

dotnet restore can run MSBuild targets from the project, which is code from the Pull Request. That is acceptable in Tier 1 and is why it must not run in Tier 2.

Tier 2

Unchanged from the reference workflow.

Trusted-ref evaluation

Restore and scan the same way against the real checkout, at stage: build on main and stage: release for a release. Scanning the published output (dotnet publish) is the closest equivalent of the Java “deployed artifact” advice, but Sonatype’s page does not state this for .NET.

What you give up

  • Manifest mode skips transitive dependencies. Restoring into a package folder avoids this.
  • Floating versions are not evaluated. Pin exact versions.
  • .msi files are not supported.
  • Advisory only at PR time, as for every ecosystem.

2 - C/C++

Preparing the Lifecycle scan input for Conan-based C and C++ projects.

What Lifecycle reads

Sonatype’s documentation says it analyses files named conanfile.txt, conanfile.py or conaninfo.txt, taking dependencies “with an exact version” from the requires and build_requires sections. It describes a scan of “a directory or subdirectories containing” those files. It does not describe a lockfile or a binary scan, and does not say that conan install is needed first.

Note that the Conan support is for the dependencies declared in those files. C and C++ code that is vendored into a repository, or fetched some other way, is not covered.

Tier 1 job

  prepare-scan-input:
    name: Prepare Lifecycle scan input
    runs-on: ubuntu-latest
    permissions:
      contents: read
    steps:
      - name: Checkout
        uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
        with:
          persist-credentials: false

      - name: Gather scan input
        run: |
          mkdir scan-input
          for f in conanfile.txt conanfile.py conaninfo.txt; do
            [ -f "$f" ] && cp "$f" scan-input/
          done
          test -n "$(ls -A scan-input)"

      - name: Upload scan input
        uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
        with:
          name: lifecycle-scan-input
          path: scan-input/
          if-no-files-found: error
          retention-days: 3

This job runs nothing from the Pull Request. The final test makes the job fail if none of the files exist, because an empty directory would upload nothing.

Tier 2

Unchanged from the reference workflow.

Trusted-ref evaluation

Scan the real Conan files at stage: build on main and stage: release for a release.

What you give up

  • conanfile.py is code. Lifecycle reads it as text and does not run it, but be aware that it is a Python file and that reviewers should treat changes to it as code changes.
  • Exact versions only. Version ranges are not evaluated.
  • No binary scan is documented.
  • Advisory only at PR time, as for every ecosystem.

3 - Docker

Preparing the Lifecycle scan input for container images.

What Lifecycle reads

Docker is a binary ecosystem. Sonatype’s documentation describes the scan target as an image saved to a tar archive with docker save, which the CLI then accepts directly: for example docker save webgoat/webgoat-7.1:latest > webgoat-7.1.tar. It says the Sonatype CLI “relies on the Docker daemon to package the image as an archive file”.

That is helpful for this standard. If the Tier 1 job builds the image and saves the tar, the Tier 2 job has no need for a Docker daemon, because the CLI is given a file. It also means the Tier 2 job never pulls an image from a registry, so it never needs registry credentials.

Tier 1 job

  prepare-scan-input:
    name: Prepare Lifecycle scan input
    runs-on: ubuntu-latest
    permissions:
      contents: read
    steps:
      - name: Checkout
        uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
        with:
          persist-credentials: false

      - name: Build image
        run: docker build --tag scan-candidate:pr .

      - name: Save image
        run: |
          mkdir scan-input
          docker save scan-candidate:pr --output scan-input/image.tar

      - name: Upload scan input
        uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
        with:
          name: lifecycle-scan-input
          path: scan-input/
          if-no-files-found: error
          retention-days: 3

A docker build runs the Dockerfile’s instructions, which is code from the Pull Request. That is acceptable in Tier 1.

Tier 2

Two changes from the reference workflow:

  • Set the repository variable LIFECYCLE_SCAN_TARGETS to scan-input/image.tar, so that the scan target is the single archive.
  • Raise LIFECYCLE_SCAN_MAX_BYTES. Images are routinely hundreds of megabytes to gigabytes, and the default of 512 MB will reject many.

An image archive is large and contains whole filesystems, so also see the remaining exposure.

Trusted-ref evaluation

Build the image from the real checkout, save it, and scan the archive at stage: build on main and stage: release for a release. A Project that publishes the image should scan the same image it publishes.

What you give up

  • Time and storage. An image artifact is expensive to upload and download. Keep retention-days short, and consider building a smaller image for the scan if your Dockerfile has a multi-stage build with a test stage.
  • Base image drift. The result reflects the base image as pulled at build time, which can change between runs.
  • Platforms. docker save of a multi-platform build saves one platform. Choose the one you ship, or scan each.
  • Advisory only at PR time, as for every ecosystem. In particular, a Dockerfile change that adds a layer is exactly what a hostile Pull Request might try, and the image contents are what get scanned, which is a mitigating factor.

4 - Go

Preparing the Lifecycle scan input for Go modules, including Terraform providers.

What Lifecycle reads

Go is a manifest ecosystem. Lifecycle matches Go components by module path and version. Sonatype’s documentation says:

  • A valid go.mod is required for Go module analysis.
  • The recommended scan target is a go.list file generated with go list.
  • go.sum is also supported, but “may include dependencies that are not used by the application”, so it can report more than your build actually uses.
  • The Go toolchain does not need to be present when Lifecycle scans the file.

Why go.list and not go.sum

go.sum records every module version your module graph has ever needed, including ones the build does not use. go.list is produced from what the compiler would actually link, so it matches your real dependencies. The tradeoff is that producing it needs the Go toolchain and module downloads, which is why it happens in Tier 1.

Tier 1 job

  prepare-scan-input:
    name: Prepare Lifecycle scan input
    runs-on: ubuntu-latest
    permissions:
      contents: read
    steps:
      - name: Checkout
        uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
        with:
          persist-credentials: false

      - name: Set up Go
        uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
        with:
          go-version-file: go.mod

      - name: Generate go.list
        run: |
          mkdir scan-input
          go list -f '{{define "M"}}{{.Path}} {{.Version}}{{end}}{{with .Module}}
          {{if not .Main}}{{if .Replace}}{{template "M" .Replace}}
          {{else}}{{template "M" .}}{{end}}{{end}}{{end}}' -compiled -deps ./... | sort -u > scan-input/go.list
          cp go.mod scan-input/go.mod

      - name: Upload scan input
        uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
        with:
          name: lifecycle-scan-input
          path: scan-input/
          if-no-files-found: error
          retention-days: 3

The go list template is Sonatype’s, verbatim. Two things are ours: the ./... package pattern, which the Sonatype page does not show (its command ends at -deps), and copying go.mod next to the list, because the page says a valid go.mod is required.

Tier 2

No change from the reference workflow. The default scan-targets of scan-input finds go.list and go.mod.

Trusted-ref evaluation

Scan the real checkout. Run the same go list step, and use scan-targets: 'go.list go.mod' or '.'.

What you give up

  • Replace directives and local modules. The template substitutes a replace target’s path and version, which is what you want for a module replacement. A replace pointing at a local directory has no version, so it cannot be matched. Reviewers should treat new replace directives in a Pull Request as a signal to look closely.
  • Versions Lifecycle cannot match. Sonatype’s documentation lists these: +incompatible versions “may not be identified and can appear as Unknown”; pseudo-versions have “partial support”; arbitrary pre-release versions “are not supported”.
  • Build tags. -compiled -deps follows the build configuration the job runs with. Code behind other build tags or other operating systems is not listed. If you ship for several platforms, run go list for each (GOOS, GOARCH) and combine the results with sort -u.
  • Advisory only at PR time. As for every ecosystem, the contributor produces this file, so the Pull Request result is advisory. See What a PR-time result means.

Terraform providers

terraform-provider-sonatyperepo and terraform-provider-sonatypeiq are Go modules, so this guide applies unchanged. The provider’s docs/ and example Terraform files are not dependencies and are not scanned.

5 - Java

Preparing the Lifecycle scan input for Maven and Gradle projects.

What Lifecycle reads

Java is a binary ecosystem. Sonatype’s documentation says Lifecycle scans packaged archives (.ear, .war, .jar, .zip, .tar.gz) and post-build artifacts, using Advanced Binary Fingerprinting, and that “for the best results” the post-build artifact you plan to deploy should be the scan target.

It can also read pom.xml and build.gradle, but that is a manifest scan with a real limit: “transitive dependencies are not automatically resolved and, therefore not included”. It needs the group, artifact and exact version of each direct dependency.

So for an accurate Pull Request scan, the scan input should be the built output. That means the build runs in Tier 1, where running a build is safe.

Tier 1 job: Maven

  prepare-scan-input:
    name: Prepare Lifecycle scan input
    runs-on: ubuntu-latest
    permissions:
      contents: read
    steps:
      - name: Checkout
        uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
        with:
          persist-credentials: false

      - name: Set up Java
        uses: actions/setup-java@de7274f081f381c8f8158605e0321c36c376e2e6 # v6.0.1
        with:
          distribution: temurin
          java-version-file: .java-version # or set java-version: explicitly
          cache: maven

      - name: Build
        run: ./mvnw --batch-mode -DskipTests package

      - name: Gather scan input
        run: |
          mkdir scan-input
          cp -r target/*.jar scan-input/ 2>/dev/null || true
          cp -r target/*.war scan-input/ 2>/dev/null || true
          # A thin jar does not contain its dependencies. Copy them alongside.
          ./mvnw --batch-mode dependency:copy-dependencies -DoutputDirectory=scan-input/dependencies

      - name: Upload scan input
        uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
        with:
          name: lifecycle-scan-input
          path: scan-input/
          if-no-files-found: error
          retention-days: 3

Notes:

  • mvn dependency:copy-dependencies is the command Sonatype’s documentation gives for thin jars.
  • An uber or shaded jar already contains its dependencies, so the dependency:copy-dependencies step is not needed. Sonatype’s documentation says to “call the index goal of the Maven plugin at the time of building the uber/shaded jar”. Add that to the build step if you produce one.
  • -DskipTests is for speed here. Your tests run in the build-and-test job.
  • Use the project’s own wrapper (./mvnw), so the Maven version is the one the Project uses. A Maven wrapper downloads Maven, which is acceptable in Tier 1 and is why this must not run in Tier 2.

Tier 1 job: Gradle

      - name: Build
        run: ./gradlew --no-daemon assemble

      - name: Gather scan input
        run: |
          mkdir scan-input
          cp -r build/libs/. scan-input/

Tier 2

Mostly unchanged from the reference workflow. Two adjustments:

  • Binary inputs are larger than lockfiles. Raise LIFECYCLE_SCAN_MAX_BYTES if the default of 512 MB is too small, and keep it as low as is workable.

Trusted-ref evaluation

Build the project, then scan the same output (scan-targets: 'target' or 'build/libs') at stage: build on main and stage: release for a release. Do not scan ., because the source tree is a weaker target than the deployed artifact.

What you give up

  • Test and provided scope. dependency:copy-dependencies copies test-scope dependencies by default. To scan only what ships, add -DincludeScope=runtime. Whether to include test dependencies is a policy decision for the Project.
  • A build that fails. If the Pull Request does not compile, there is no artifact, so the Pull Request gets no Lifecycle result until it builds. This is consistent with Tier 2 only running after Tier 1 succeeds.
  • Advisory only at PR time. A Pull Request could build an artifact that omits a dependency. Maintainers reviewing pom.xml and build.gradle changes is the control for that, and the trusted-ref evaluation is the backstop.

Nexus plugin Projects

Nexus Repository plugins (for example the CasC plugin) are built as bundles that run inside Nexus Repository. The bundle is the deployed artifact, so scan it. The Nexus platform itself is provided by the host at runtime and is not in the bundle. Whether it should appear in the scan is a policy decision for the Project.

6 - JavaScript

Preparing the Lifecycle scan input for npm and Yarn projects.

What Lifecycle reads

JavaScript can be scanned two ways, and the choice sets the accuracy:

Scan inputModeAccuracy
package.json plus a lockfile (package-lock.json, npm-shrinkwrap.json, yarn.lock, pnpm-lock.yaml)Manifest matchingGood. Evaluates dependencies from metadata only.
package.json, the lockfile and node_modulesBinary (ABF)Best. Sonatype recommends this for “the most precise results”.

Sonatype’s documentation says that when JavaScript files are included, ABF is used and the JavaScript files are the primary identification source, and that “manifest entries without corresponding JavaScript files are ignored”. package.json must come with a package-lock.json.

The binary mode needs node_modules from “a clean production flag build”.

Which should a Project choose

  • Manifest is small, fast and cheap to move between jobs. Choose it as the default for Pull Requests.
  • Binary matters when you ship or bundle node_modules, or need to detect modified copies of packages. It is large. Choose it for the trusted-ref evaluation and, if your Project wants it, for Pull Requests too.

Tier 1 job: manifest

  prepare-scan-input:
    name: Prepare Lifecycle scan input
    runs-on: ubuntu-latest
    permissions:
      contents: read
    steps:
      - name: Checkout
        uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
        with:
          persist-credentials: false

      - name: Gather scan input
        run: |
          mkdir scan-input
          cp package.json scan-input/
          # Use whichever lockfile your Project has.
          cp package-lock.json scan-input/

      - name: Upload scan input
        uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
        with:
          name: lifecycle-scan-input
          path: scan-input/
          if-no-files-found: error
          retention-days: 3

This job does not run npm, so it runs nothing from the Pull Request at all. It is the simplest and safest way to prepare a scan input.

For a monorepo or workspace, copy each package’s package.json into a matching subdirectory of scan-input, so that each lockfile stays next to its manifest.

Tier 1 job: binary

      - name: Set up Node
        uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0
        with:
          node-version-file: .nvmrc

      - name: Install production dependencies
        run: npm install --production
        # For Yarn: yarn install --prod

      - name: Gather scan input
        run: |
          mkdir scan-input
          cp package.json package-lock.json scan-input/
          cp -r node_modules scan-input/node_modules

npm install --production is the command Sonatype’s preparation page gives, as is yarn install --prod. Use these exactly and not npm ci, until Sonatype says the others are equivalent.

Tier 2

Unchanged from the reference workflow for the manifest mode. For the binary mode, node_modules is large and has many files. Raise LIFECYCLE_SCAN_MAX_BYTES and LIFECYCLE_SCAN_MAX_FILES for this Project, and note that the artifact upload and download will be slow.

Trusted-ref evaluation

Run npm install --production on the real checkout, then scan package.json, the lockfile and node_modules together, at stage: build on main and stage: release for a release.

What you give up

  • Manifest mode cannot detect a package whose files were altered, and relies on the lockfile being honest. A lockfile that a Pull Request changed deserves a reviewer’s attention.
  • Development dependencies. The production flag means they are excluded in binary mode. If your policy covers build-time dependencies, scan them separately.
  • Name matching sunset. Sonatype’s documentation notes that matching unmatched JavaScript files by name ended on 2025-09-10. Only files that match known hashes are counted.
  • Advisory only at PR time, as for every ecosystem.

7 - PHP

Preparing the Lifecycle scan input for Composer projects.

What Lifecycle reads

PHP is a manifest ecosystem and the documentation describes one file only: “files named composer.lock (a JSON file generated by PHP composer) will be analyzed”. It does not scan composer.json or the vendor/ directory. Sonatype recommends generating and committing composer.lock before scanning, so the Project must commit composer.lock.

Tier 1 job

  prepare-scan-input:
    name: Prepare Lifecycle scan input
    runs-on: ubuntu-latest
    permissions:
      contents: read
    steps:
      - name: Checkout
        uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
        with:
          persist-credentials: false

      - name: Gather scan input
        run: |
          mkdir scan-input
          cp composer.lock scan-input/

      - name: Upload scan input
        uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
        with:
          name: lifecycle-scan-input
          path: scan-input/
          if-no-files-found: error
          retention-days: 3

This job runs nothing from the Pull Request, and does not need composer install.

Tier 2

Unchanged from the reference workflow.

Trusted-ref evaluation

Scan the real composer.lock at stage: build on main and stage: release for a release.

What you give up

  • Only top-level packages. Sonatype’s documentation says “only top-level dependencies (specified under the ‘packages’ group) are evaluated”. Development dependencies, which Composer records under packages-dev, are not.
  • No binary scan is documented for PHP.
  • Advisory only at PR time, as for every ecosystem. A Pull Request that edits composer.lock is changing the thing being scanned.

8 - Python

Preparing the Lifecycle scan input for Python projects using pip or Poetry.

What Lifecycle reads

Python can be scanned from manifests or from binaries:

Scan inputNotes
requirements.txtMust use == with a version and no wildcards. Variable ranges and environment ranges are ignored.
poetry.lockParsed automatically. For Poetry 1.5.1 and later, development dependencies are excluded when pyproject.toml is present and discoverable.
Pipfile.lockNeeds IQ CLI 2.0 or later. Only dependencies under the default key are parsed.
.whl and .tar.gz filesBinary scan of the actual packages.

Sonatype’s recommendation for the best results is to first create a virtual environment, resolve dependencies with pip install -r requirements.txt, and use pip download or pip wheel to gather the packages as the scan target.

Tier 1 job: Poetry lockfile (manifest)

This is the simplest option and fits a Project that already commits poetry.lock, such as Jake.

  prepare-scan-input:
    name: Prepare Lifecycle scan input
    runs-on: ubuntu-latest
    permissions:
      contents: read
    steps:
      - name: Checkout
        uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
        with:
          persist-credentials: false

      - name: Gather scan input
        run: |
          mkdir scan-input
          cp poetry.lock pyproject.toml scan-input/

      - name: Upload scan input
        uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
        with:
          name: lifecycle-scan-input
          path: scan-input/
          if-no-files-found: error
          retention-days: 3

Include pyproject.toml so that Lifecycle can exclude development dependencies. This job runs nothing from the Pull Request.

Tier 1 job: pip (binary)

      - name: Set up Python
        uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
        with:
          python-version-file: .python-version # or set python-version: explicitly

      - name: Download packages
        run: |
          python -m venv "$RUNNER_TEMP/venv"
          . "$RUNNER_TEMP/venv/bin/activate"
          mkdir scan-input
          pip download -r requirements.txt -d scan-input

This follows Sonatype’s guidance. Use pip wheel -r requirements.txt -w scan-input instead if you want wheels built from source distributions.

Tier 2

Unchanged from the reference workflow. Binary inputs are larger, so check the limits.

Trusted-ref evaluation

Scan the real poetry.lock and pyproject.toml, or the downloaded packages, at stage: build on main and stage: release for a release.

What you give up

  • Unpinned requirements. A requirements.txt with ranges is skipped for those lines. Pin versions (for example with pip freeze, or a tool such as pip-compile) before scanning.
  • A single requirements file. Sonatype’s documentation says other variants of requirements files are ignored. If you split them (requirements-dev.txt), only the one named exactly is read.
  • Advisory only at PR time, as for every ecosystem. A Pull Request that edits poetry.lock is changing the thing being scanned, and a reviewer should look at it.

Not documented

Sonatype’s page does not state what site-packages scans look like, and does not describe a poetry export flow. If a Project needs either, ask Sonatype before relying on it.

9 - R

Preparing the Lifecycle scan input for R (CRAN) packages.

What Lifecycle reads

The only input Sonatype’s documentation describes is a file named cran-installed.packages, “exported via installed.packages() command in CRAN console”. It does not describe scanning DESCRIPTION, renv.lock or package tarballs. Matching is by package name and version.

That means the scan input has to be generated from an R installation that has the package’s dependencies installed. The Project’s own repository does not contain it.

Tier 1 job

  prepare-scan-input:
    name: Prepare Lifecycle scan input
    runs-on: ubuntu-latest
    permissions:
      contents: read
    steps:
      - name: Checkout
        uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
        with:
          persist-credentials: false

      - name: Set up R
        uses: r-lib/actions/setup-r@f9a764fea8d5c63df6ef9a5c7795bf7deb5d7e05 # v2.14.0
        with:
          use-public-rspm: true

      - name: Install dependencies and export package list
        run: |
          mkdir scan-input
          Rscript -e 'install.packages("remotes"); remotes::install_deps(dependencies = TRUE)'
          Rscript -e 'write.table(installed.packages()[, c("Package", "Version")], file = "scan-input/cran-installed.packages", sep = "\t", quote = FALSE, row.names = FALSE)'

      - name: Upload scan input
        uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
        with:
          name: lifecycle-scan-input
          path: scan-input/
          if-no-files-found: error
          retention-days: 3

Tier 2

Unchanged from the reference workflow.

Trusted-ref evaluation

Generate the same file from the real checkout and scan it at stage: build on main and stage: release for a release.

What you give up

  • Only what is installed. The scan covers what the Tier 1 job installed, which depends on the repositories and versions available at that moment, so two runs can differ.
  • Name and version only. There is no binary fingerprinting for R in Sonatype’s documentation.
  • Installing packages runs their code (compilation, .onLoad hooks in some cases). That is acceptable in Tier 1 and is why this must not run in Tier 2.
  • Advisory only at PR time, as for every ecosystem.

10 - Ruby

Preparing the Lifecycle scan input for Bundler projects.

What Lifecycle reads

Ruby is a manifest ecosystem, with a binary option. Sonatype’s documentation says it scans Gemfile.lock, .gem files and vendor/bundle. For the lockfile, “components from sections GIT, GEM and PATH and with an exact version will be analyzed”. For the best results it recommends scanning “after either installing the packages from a clean environment or storing the gems in a local cache”.

The Project must commit Gemfile.lock. Gem projects (libraries) often do not, and then there is nothing to scan.

Tier 1 job: lockfile

  prepare-scan-input:
    name: Prepare Lifecycle scan input
    runs-on: ubuntu-latest
    permissions:
      contents: read
    steps:
      - name: Checkout
        uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
        with:
          persist-credentials: false

      - name: Gather scan input
        run: |
          mkdir scan-input
          cp Gemfile.lock scan-input/

      - name: Upload scan input
        uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
        with:
          name: lifecycle-scan-input
          path: scan-input/
          if-no-files-found: error
          retention-days: 3

This job runs nothing from the Pull Request.

Tier 1 job: installed gems (binary)

Add this to scan the gems themselves. It runs bundle, which may execute native extension builds, so it belongs in Tier 1.

      - name: Install gems into a local path
        run: |
          bundle config set --local path vendor/bundle
          bundle install
          mkdir scan-input
          cp Gemfile.lock scan-input/
          cp -r vendor/bundle scan-input/vendor-bundle

Tier 2

Unchanged from the reference workflow.

Trusted-ref evaluation

Scan the real Gemfile.lock, or the installed gems, at stage: build on main and stage: release for a release.

What you give up

  • Only exact versions are analysed, so a lockfile entry without one is skipped.
  • The Gemfile is not read. The lockfile is the truth, which means a Pull Request that edits it is changing the thing being scanned, and a reviewer should look at it.
  • Advisory only at PR time, as for every ecosystem.

11 - Rust

Preparing the Lifecycle scan input for Cargo projects.

What Lifecycle reads

Rust is a manifest ecosystem, and the only file Sonatype’s documentation describes is Cargo.lock: “the Cargo coordinate-based matching feature provides the ability to scan and evaluate Rust dependencies found in the Cargo.lock file”. The name and version of each package entry are evaluated. The documentation does not mention Cargo.toml or built binaries.

Because the lockfile is the input, the Project must commit Cargo.lock. Library crates often do not. A Project that does not commit it has nothing to scan, and should either start committing it or generate it in Tier 1 with cargo generate-lockfile (which resolves to the newest versions, not the ones the Project tested against, and is therefore a weaker signal).

Tier 1 job

  prepare-scan-input:
    name: Prepare Lifecycle scan input
    runs-on: ubuntu-latest
    permissions:
      contents: read
    steps:
      - name: Checkout
        uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
        with:
          persist-credentials: false

      - name: Gather scan input
        run: |
          mkdir scan-input
          cp Cargo.lock scan-input/

      - name: Upload scan input
        uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
        with:
          name: lifecycle-scan-input
          path: scan-input/
          if-no-files-found: error
          retention-days: 3

This job runs nothing from the Pull Request. For a workspace with several Cargo.lock files, copy each into its own subdirectory.

Tier 2

Unchanged from the reference workflow.

Trusted-ref evaluation

Scan the real Cargo.lock at stage: build on main and stage: release for a release.

What you give up

  • No binary scan is documented. The result is only as honest as Cargo.lock. A Pull Request that edits the lockfile is changing the thing being scanned, and a reviewer should look at it.
  • Path and git dependencies may not match published crates. Sonatype’s page does not say how they are handled.
  • Advisory only at PR time, as for every ecosystem.