Observe

putnami observe explains a change from what a CI job already produced. It runs at the end of the job, after your build and your tests, and reads the repository, the Git history and the reports the job wrote. It needs no Putnami workspace and changes nothing in your pipeline.

To add it to a pipeline, follow Observe a change in your CI.

@putnami/observe is experimental: it promises no compatibility yet.

What it answers

Question How it answers
Which commit was built, by which CI run? It reads the CI's environment: the code host, the repository's numeric id, the commit, the run and the pull or merge request.
What is the change measured against? The pull request base, the merge request diff base, or on a branch push the commit before the push. It deepens a shallow clone itself.
Which files changed? The Git diff between the base and the commit.
Which parts of the repository changed, and which depend on them? It reads the repository's build and workspace files to find its components and the edges between them. A component is changed when one of its files changed, dependent when it depends on a changed component, and unaffected otherwise.
Which tests ran, and did they pass? It reads the test reports and places each test case on its component.
How well do the tests cover the change? It reads the coverage reports, places each covered file on its component, and computes the coverage of the changed lines.
Which dependencies moved? It reads each changed lockfile at the base and at the commit, and lists the packages added, removed, upgraded or downgraded.
Do those moves fix or bring known vulnerabilities, or change licences? It reads the software bill of materials (SBOM, the list of a build's packages with their licences and known vulnerabilities) the job wrote, at the base and at the commit.

What it writes

Output Where
A text summary The job log.
One JSON document Standard output, with --output=json.
The change evidence document A file, with --evidence <path>. It follows the protocol/evidence v1 schema; keep it as a CI artifact.
A commit status and one summary note The pull or merge request, with --publish. The next run updates the same note.
An upload to Putnami Cloud With --upload and a PUTNAMI_CLOUD_TOKEN.

It exits 0 whatever it observes, so a failed test or a missing report never fails the job. It exits 2 on a usage error, and 1 only when it cannot read the job context or write its output. A missing or unreadable report, an unreachable base or a failed post is a diagnostic in the summary.

What it does not do

  • It runs no build tool, package manager, test or SBOM tool. It reads files.
  • It queries no vulnerability database: vulnerabilities come from the SBOM your job wrote.
  • It does not decide whether a change may merge. It records evidence.

Coverage

Every tool below is read from files. A tool that is not listed is not read.

CI providers

CI What it reads --publish posts with
GitHub Actions GITHUB_* variables and the event payload GITHUB_TOKEN, with statuses: write and pull-requests: write
GitLab CI, GitLab.com and self-managed CI_* variables PUTNAMI_GITLAB_TOKEN, a project access token with the api scope and the Developer role
Any other CI Nothing: name the base with --base Nothing

Git 2.29 or later is required.

Repository tools

A detector reads one tool's files and names components. When two detectors name one directory, an orchestrator's graph wins over a workspace, and a workspace over a single manifest. When no detector finds a component, the whole repository is one component.

Tool Languages A component is Edges come from Graph export, read when present Page
Nx Any A project The graph export, else project.json, nx.json and package.json dependencies nx graph --file=graph.json Nx
moon 1.x and 2.x Any A project The graph export, else moon.yml dependsOn, task deps and package.json dependencies moon project-graph --json > moon-graph.json, or --moon-graph moon
Pants Python and any language Pants builds A directory of BUILD targets The graph export, with the dependencies Pants infers from imports pants peek :: > pants-graph.json, or --pants-graph Pants
Rush JavaScript, TypeScript A project of rush.json package.json dependencies on another project None Rush
npm, pnpm, yarn and bun workspaces, Turborepo, Lerna JavaScript, TypeScript A workspace member workspace:, file:, link: and portal: dependencies, and versions a member satisfies None Node workspaces
Composer PHP A directory with a composer.json Packages a path repository provides None Composer
Go modules and go.work Go A module go.mod requires of another module of the repository, and local replace targets None Go
uv workspaces, Poetry, pyproject.toml Python A project with a [project] or [tool.poetry] table Workspace and path sources None Python
Gradle, Android included Java, Kotlin A project the settings include project(":x") and projects.x dependencies None Gradle and Maven
Maven 3 and 4 Java, Kotlin A module A dependency on another module None Gradle and Maven
Swift Package Manager Swift A package and each of its targets Target and local package dependencies swift package describe --type json > swiftpm.json, in each package iOS and Swift
Xcode 15 and later Swift, Objective-C The directory of an .xcodeproj The local Swift packages the project references None iOS and Swift
Dart, Flutter, pub workspaces, melos Dart A package path: dependencies, and inside a workspace a dependency on another of its packages None Dart and Flutter
.NET SDK projects and solutions C#, F#, Visual Basic A .csproj, .fsproj or .vbproj ProjectReference None .NET
Bundler, packwerk, gems Ruby, Rails A pack, a gem or an application A pack's dependencies, Gemfile path: gems, gemspec dependencies on a local gem None Ruby and Rails
Cargo Rust A crate Path dependencies, and workspace dependencies with a path None Rust
Bazel, Bzlmod or WORKSPACE Any A package with a BUILD file The query export bazel query 'deps(//...)' --output=streamed_jsonproto > bazel-graph.json, or --bazel-graph Bazel

Without its graph export, Pants and Bazel components have no edges, and the summary names the command that writes the export.

Test reports

Format Written by
JUnit XML Jest with jest-junit, Vitest, node:test, PHPUnit --log-junit, gotestsum, go-junit-report, pytest --junitxml, pants test --test-report, bazel test, Gradle, Android, Maven Surefire and Failsafe, cargo-nextest, xcbeautify --report junit, fastlane scan, swift test --xunit-output, junitreport's tojunit for Dart and Flutter, rspec_junit_formatter, minitest-reporters, dotnet test --logger junit
TRX dotnet test --logger trx, with xUnit, NUnit or MSTest

Coverage reports

Format Written by
LCOV Jest, Vitest, c8, bun, flutter test --coverage, cargo-llvm-cov --lcov, llvm-cov export -format=lcov, bazel coverage --combined_report=lcov, simplecov-lcov
Cobertura XML pytest-cov --cov-report=xml, Pants --coverage-py-report=xml, Coverlet, cargo-llvm-cov --cobertura, xcresultparser, slather, simplecov-cobertura, Jest, Vitest, c8, PHPUnit --coverage-cobertura
Clover XML PHPUnit --coverage-clover, Jest, Vitest
JaCoCo XML The JaCoCo Maven plugin, the Gradle jacoco plugin, Kover, Android unit test coverage
Go cover profile go test -coverprofile

putnami observe finds the reports by their content, anywhere in the checkout except .git, node_modules, vendor and build outputs that hold no report. --junit and --coverage name them instead. It reads no .xcresult bundle: convert it to JUnit and Cobertura first. See Reports for what each format gives.

Lockfiles

Lockfile Ecosystem
package-lock.json, pnpm-lock.yaml, yarn.lock npm
Rush's common/config/rush/pnpm-lock.yaml, npm-shrinkwrap.json and yarn.lock, and subspace lockfiles npm
composer.lock composer
go.mod go
uv.lock, poetry.lock, and the Pants lockfiles [python.resolves] names pypi
gradle.lockfile, settings-gradle.lockfile, gradle/libs.versions.toml maven
Package.resolved swift
Podfile.lock cocoapods
pubspec.lock pub
packages.lock.json, else Directory.Packages.props nuget
Gemfile.lock gem
Cargo.lock cargo

Maven has no lockfile, and bun.lock is not read, so their dependency changes do not show. A dependency ecosystem can differ from the component's: a Gradle project is a gradle component whose dependencies are maven packages.

Software bills of materials

Format Written by
CycloneDX 1.4, 1.5 and 1.6, JSON osv-scanner, Trivy, Syft, cyclonedx-maven-plugin
SPDX 2.3, JSON Syft, osv-scanner. Most SPDX writers record no vulnerability: use CycloneDX for them.

--sbom and --base-sbom name the documents of the commit and of the base. Without --sbom, putnami observe reads the SBOM it finds in the checkout. When it finds several, it reads none and says so.

Reference

Page What it covers
What putnami observe reads Every flag, how each CI and the base are read, report discovery, the evidence document, upload and publishing
How putnami observe explains a change Components, tests and coverage, changed-line coverage, dependency changes, licences and vulnerabilities
Reports What each test, coverage and SBOM format gives, and why a report is rejected
One page per repository tool The pages in the repository tools table