Ecosystem Guides
For each language, what to prepare for the Lifecycle scan, and the Tier 1 job that prepares it.
DRAFT. These guides are drawn from Sonatype’s published analysis documentation. The snippets have not been run end to end, and where Sonatype’s documentation is silent the guide says so, rather than guessing.
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
| Kind | Examples | What the scan input is | Accuracy |
|---|
| Manifest | Go, Rust, PHP, Ruby, Python, .NET, R | A lockfile or a dependency list, produced by the language’s own tooling | Good, if the lockfile or list pins exact versions. Transitive dependencies depend on what the file contains. |
| Binary | Java, npm with node_modules, Docker | The built artifact, or the installed dependencies | Best. 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
| Ecosystem | Used by | Guide |
|---|
| Go | Nancy, Cheque, Terraform providers, API clients | Go |
| Java (Maven, Gradle) | Sherlock Trunks, Nexus CasC Plugin, API clients | Java |
| JavaScript (npm, Yarn) | AuditJS, API clients | JavaScript |
| Python | Jake, API clients | Python |
| .NET (NuGet) | No current Project | .NET |
| Rust (Cargo) | Cargo Pants | Rust |
| Ruby (Bundler) | Chelsea | Ruby |
| PHP (Composer) | Bach | PHP |
| R (CRAN) | OysterR | R |
| C/C++ (Conan) | No current Project | C/C++ |
| Docker images | Container image Projects | Docker |
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.
DRAFT. Not yet run end to end. No current community Project uses .NET, so this guide is the least exercised. Source: Sonatype’s NuGet Application Analysis.
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.
DRAFT. Not yet run end to end. No current community Project uses Conan, so this guide is the least exercised. Source: Sonatype’s C/C++ Application Analysis.
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.
DRAFT. Not yet run end to end. Container images are the largest scan input, and the one with the most open questions. Source: Sonatype’s Docker Image Analysis.
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.
To confirm with Sonatype: the exact package pattern the go list command should use. We use ./... so the whole module is covered. A Project with several modules (a go.work workspace) needs a go list per module.
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-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.
DRAFT. Not yet run end to end. Source: Sonatype’s Java Application Analysis. Gradle guidance is thinner than Maven, and this guide says where.
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/
Not documented: Sonatype’s Java page names build/libs only indirectly and gives no Gradle equivalent of Maven’s dependency:copy-dependencies. For a Gradle project that produces a thin jar, the dependencies are not in build/libs. Until Sonatype documents a recommended approach, a Gradle Project has two choices, and should say which it uses:
- Build a fat jar (for example with the Shadow plugin) and scan that.
- Scan
build.gradle as a manifest, accepting the loss of transitive dependencies. Sonatype’s documentation lists the supported scopes (api, apiElements, compileClasspath, implementation, compileOnly, compileOnlyApi, compile) and says only Groovy build.gradle files are supported, so a Kotlin DSL build is not covered.
The Gradle plugin Project in this community (scan-gradle-plugin) is itself a Gradle plugin and its Pull Requests are a good test case for deciding this.
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 input | Mode | Accuracy |
|---|
package.json plus a lockfile (package-lock.json, npm-shrinkwrap.json, yarn.lock, pnpm-lock.yaml) | Manifest matching | Good. Evaluates dependencies from metadata only. |
package.json, the lockfile and node_modules | Binary (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.
Install scripts run here. npm install runs package install scripts. In Tier 1 that is acceptable, because Tier 1 has no secrets. It is the reason this step must never move into Tier 2.
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 input | Notes |
|---|
requirements.txt | Must use == with a version and no wildcards. Variable ranges and environment ranges are ignored. |
poetry.lock | Parsed automatically. For Poetry 1.5.1 and later, development dependencies are excluded when pyproject.toml is present and discoverable. |
Pipfile.lock | Needs IQ CLI 2.0 or later. Only dependencies under the default key are parsed. |
.whl and .tar.gz files | Binary 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.
Building runs setup code. pip download of a source distribution, and pip wheel, can execute a package’s build scripts. That is acceptable in Tier 1, which has no secrets, and is the reason this must not happen in Tier 2.
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.
DRAFT. Not yet run end to end, and the thinnest guide: Sonatype’s documentation for R is brief. Source: Sonatype’s R (CRAN) Application Analysis.
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
Not documented, and not yet verified: Sonatype’s page names the file and the command that produces it but not the exact column layout the scanner expects. The write.table line above is our guess at a sensible export and must be checked against a real scan before this guide is relied on.
OysterR is the Project this applies to. It has a DESCRIPTION file and no lockfile, so it would need this approach.
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
Not documented: Sonatype’s Ruby page does not say whether the directory has to be named vendor/bundle to be recognised. This snippet renames it to vendor-bundle to avoid a path clash, which has not been tested. Keep the original name if the scan does not pick it up.
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.