Skip to content

feat(manifest): --facts mode emits Socket facts JSON for sbt/Scala projects (REA-474)#1334

Merged
Jeppe Fredsgaard Blaabjerg (jfblaa) merged 9 commits into
v1.xfrom
jfblaa/rea-474-sbtscala-socket-facts-sbom-socket-manifest-scala-facts
May 27, 2026
Merged

feat(manifest): --facts mode emits Socket facts JSON for sbt/Scala projects (REA-474)#1334
Jeppe Fredsgaard Blaabjerg (jfblaa) merged 9 commits into
v1.xfrom
jfblaa/rea-474-sbtscala-socket-facts-sbom-socket-manifest-scala-facts

Conversation

@jfblaa
Copy link
Copy Markdown
Contributor

@jfblaa Jeppe Fredsgaard Blaabjerg (jfblaa) commented May 27, 2026

What

Adds socket manifest scala --facts: emit a single .socket.facts.json dependency-graph SBOM from an sbt build (mirroring manifest gradle --facts, REA-442), for use as pregenerated input to scan create Tier 1 reachability instead of generating pom.xml.

Closes REA-474.

How

  • Source-only sbt AutoPlugin shipped in dist/, dropped into an isolated -Dsbt.global.base plugins dir and run via sbt -Dsbt.global.base=… socketFacts. No plugin install, no project changes, never touches the user's ~/.sbt. Compiled by the sbt meta-build, so a single Ivy-based path spans sbt 0.13 → 1.x (no reflection).
  • Metadata-only resolution via Ivy ResolveOptions.setDownload(false) — no artifact jars are downloaded (don't consume client bandwidth). Resolution is per-project, so divergent per-module versions are both reported; intra-build project deps are omitted; one component is emitted per resolved module.
  • Config-scoped for speed + signal: resolves only the real dependency configs (compile/runtime/test/provided/optional) by default, skipping the Scala toolchain, sbt-native-packager configs (debian/docker/universal/…), -internal duplicates and docs/pom/sources. On lichess/lila (~70 modules): ~48s warm, no downloads.

Options

  • --configs=compile,test,… — choose which sbt configurations to resolve.
  • --ignore-unresolved — skip dependencies that fail to resolve instead of failing the run (default: fail).
  • Both also read from socket.json (defaults.manifest.sbt.{configs,ignoreUnresolved}). The --facts toggle is exposed in the manifest setup wizard, and scan create --auto-manifest honors defaults.manifest.sbt.facts.

Notes

  • Why Ivy (not update/updateFull): updateFull resolves every configuration (can't be scoped) so it's slower on packager-heavy builds and downloads jars; Ivy's setConfs resolves only what we want and stays metadata-only. Coursier's resolve-without-fetch isn't usable here — sbt ships coursier shaded and scalac can't compile against the shaded types (sbt#6651), and it doesn't exist on 0.13. Ivy resolves the same versions coursier does for real deps and respects dependencyOverrides (verified on lila).
  • sbt 0.13 requires an older JDK (Java 8/11) — its TrapExit calls setSecurityManager, which JDK 18+ rejects; the CLI surfaces this with a clear hint.

Validation

sbt 0.13.18 / 1.9.9 / 1.10.0; Scala 2.11 / 2.13 / 3; single + multi-module; version eviction/overrides; crash-on-unresolved + --ignore-unresolved; --configs capturing custom (IntegrationTest) configs. pnpm check (lint + tsc) and the unit suite pass.


Note

Medium Risk
Runs external sbt with a global plugin and depends on correct Ivy resolution metadata for SBOM accuracy; scope is manifest generation only, not auth or data stores.

Overview
Adds socket manifest scala --facts to emit a build-root .socket.facts.json SBOM from sbt (metadata-only Ivy resolution, no pom.xml), aligned with existing Gradle facts mode for Tier 1 reachability / scan create --reach.

A new SocketFactsPlugin Scala source is bundled into dist/ and injected via an isolated -Dsbt.global.base (no changes to the project or ~/.sbt). convert-sbt-to-facts.mts stages the plugin, runs socketFacts, and surfaces failures (including a JDK hint for legacy sbt on JDK 18+). CLI flags --configs and --ignore-unresolved map to JVM properties; defaults can live in socket.json.

scan create --auto-manifest, manifest setup, help/tests, and the changelog are updated so sbt auto-manifest can prefer facts over makePom when configured.

Reviewed by Cursor Bugbot for commit 160208a. Configure here.

…s (REA-474)

`socket manifest scala --facts` emits a single .socket.facts.json describing an
sbt build's resolved dependency graph (mirroring `manifest gradle --facts`), for
use as pregenerated SBOM input to `scan create` Tier 1 reachability instead of
generating pom.xml files.

Delivered as a source-only sbt AutoPlugin dropped into an isolated
-Dsbt.global.base plugins dir: no plugin install, no project changes, never
touches the user's ~/.sbt. Compiled by the sbt meta-build so a single Ivy-based
path spans sbt 0.13 through 1.x. The dependency graph is read from Ivy
resolution metadata only (setDownload(false)) so no artifact jars are
downloaded, and resolution is per-project so divergent per-module versions are
both reported. Also exposed in the `manifest setup` wizard and honored by
`scan create --auto-manifest`.
…/--ignore-unresolved (REA-474)

Resolve only the project's real dependency configurations by default
(compile/runtime/test/provided/optional) instead of every configuration. On
large multi-module builds (e.g. sbt-native-packager projects) this avoids
resolving packager/toolchain/internal configs entirely — a major speedup with
no jar downloads, and cleaner output. Add `--configs` to choose configurations
and `--ignore-unresolved` to skip unresolvable dependencies instead of failing
the run (default: fail). Drop the temporary toolchain-tag mechanism, which the
default config set makes unnecessary, and skip Ivy metadata revalidation for
faster warm-cache resolution.
…ut --facts (REA-474)

Those options only affect --facts; the pom path (`sbt makePom`) has no
equivalent. Warn on an explicitly-passed flag in pom mode rather than silently
ignoring it. socket.json defaults don't trigger the warning — only a flag
actually present on the command line.
…la-socket-facts-sbom-socket-manifest-scala-facts
Bump version to 1.1.105 and move the `socket manifest scala --facts` entry
out of [Unreleased] into a dated 1.1.105 changelog section.
…facts

Drop the sbt-specific implementation asides (pom round-trip, metadata-only,
version range) so the entry mirrors the existing gradle --facts wording.
The `.socket.facts.json` SBOM is consumed by any `socket scan create`, not
just `--reach` / Tier 1 reachability scans. Reword both the gradle (1.1.98)
and sbt (1.1.105) entries to drop the reachability-specific framing.
Copy link
Copy Markdown
Contributor

@mtorp Martin Torp (mtorp) left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work. The Ivy-based, source-only plugin approach is well-chosen, the cross-version (sbt 0.13 ↔ 1.x) story holds up, and the comments are doing real load-bearing work. Approving — left four small inline notes (none blocking) on places where the sbt path quietly diverges from the existing Gradle facts mode in user-visible or code-cleanliness ways.

Comment thread src/commands/manifest/generate_auto_manifest.mts
Comment thread src/commands/manifest/cmd-manifest-scala.mts
Comment thread src/commands/manifest/socket-facts.plugin.scala Outdated
Comment thread src/commands/manifest/cmd-manifest-scala.mts Outdated
- generate_auto_manifest: keep the shared sbt arg bag to fields both handlers
  accept; add facts-only (configs/ignoreUnresolved) and pom-only (out) per
  branch so convertSbtToMaven is no longer spread properties it doesn't take.
- cmd-manifest-scala: warn when --out/--stdout are passed with --facts (the
  facts file is always written to the build root), mirroring the existing
  --configs/--ignore-unresolved guard.
- socket-facts.plugin: key intra-build project filtering on the full GAV
  (org:name:version) via a shared gavKey helper, so an external dep that only
  shares an org:name with a build project is still emitted.
- cmd-manifest-scala: sort the --configs default list in the help text to
  match DefaultConfs ordering in the plugin.
Resolve version collision: origin/v1.x released 1.1.105 (coana 15.3.11), so
this branch's sbt --facts feature moves to 1.1.106. CHANGELOG keeps both
sections; package.json bumped to 1.1.106.
auto-merge was automatically disabled May 27, 2026 18:08

Pull request was closed

@jfblaa Jeppe Fredsgaard Blaabjerg (jfblaa) merged commit 8be8afb into v1.x May 27, 2026
18 of 23 checks passed
@jfblaa Jeppe Fredsgaard Blaabjerg (jfblaa) deleted the jfblaa/rea-474-sbtscala-socket-facts-sbom-socket-manifest-scala-facts branch May 27, 2026 18:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants