Skip to content

fix(manifest): gradle --facts works with configuration cache + adds --configs/--ignore-unresolved (REA-484)#1338

Merged
Jeppe Fredsgaard Blaabjerg (jfblaa) merged 6 commits into
v1.xfrom
jfblaa/rea-484-socket-manifest-gradle-facts-configuration-cache-compat-sbt
May 28, 2026
Merged

fix(manifest): gradle --facts works with configuration cache + adds --configs/--ignore-unresolved (REA-484)#1338
Jeppe Fredsgaard Blaabjerg (jfblaa) merged 6 commits into
v1.xfrom
jfblaa/rea-484-socket-manifest-gradle-facts-configuration-cache-compat-sbt

Conversation

@jfblaa
Copy link
Copy Markdown
Contributor

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

Summary

  • socket manifest gradle --facts (and kotlin --facts) now works on Gradle builds with the configuration cache enabled (default on Gradle 9, common in modern monorepos). Previously failed with Task.project at execution time errors. Fix: pass -Dorg.gradle.configuration-cache=false to the gradle invocation (silently ignored on pre-6.6 Gradle, so backward compat is preserved) and hoist findProperty/projectDir reads to configuration time as defensive cleanup.
  • Adds sbt-parity flags --configs and --ignore-unresolved to the gradle/kotlin commands. --configs takes comma-separated case-sensitive glob patterns (* and ? wildcards) matched against Gradle configuration names, e.g. *CompileClasspath,*RuntimeClasspath to skip tooling configs like annotationProcessor. --ignore-unresolved opts out of the new default of failing the run when a dep can't resolve. Both honored from socket.json and via socket scan create --auto-manifest.
  • socket manifest scala --facts --configs also gains glob support so the same syntax works on both commands. Bare names (no wildcards) keep working as exact-name filters — no breaking change to existing sbt usages.

Closes REA-484.

Test plan

  • Local: unit tests pass for gradle/kotlin/scala command suites, lint clean, tsc clean, help-text snapshots updated.
  • Local end-to-end against OpenTelemetry's Java SDK (Gradle 9.5.1 with org.gradle.configuration-cache=true and parallel CC forced on):
    • Default --facts: 419 components, BUILD SUCCESSFUL, no CC errors.
    • --configs=*CompileClasspath,*RuntimeClasspath: 335 components, 0 tooling-tagged.
    • --configs=runtimeClasspath (exact, no wildcards): 215 components — only the standalone configs match.
    • --configs=*test*: 345 components, all dev-tagged.
    • --configs=DoesNotExist: clean no resolvable dependencies message.
  • Local end-to-end against a Gradle fixture with an unresolvable dependency:
    • Default: BUILD FAILED, exit 1, no file emitted, could not resolve 1 dependency(ies) summary.
    • --ignore-unresolved: BUILD SUCCESSFUL, exit 0, file emitted with the unresolvable as a direct entry.
  • Local end-to-end against an sbt 1.10 fixture:
    • Default: 20 components (5-name DefaultConfs unchanged).
    • --configs=*test*: 20 components (matches test scope).
    • --configs=*Test* (capital T): 0 components / skip — confirms case-sensitivity.
    • --configs=compile: 2 components — backward-compat exact name match.
  • CI green.

…-configs/--ignore-unresolved (REA-484)

The init script reached into `Task.project` from a `doLast` action, which
Gradle 7+ forbids when the configuration cache is on. Disable the cache for
the facts run via `-Dorg.gradle.configuration-cache=false` (silently ignored
on pre-6.6 Gradle, so we stay compatible with old versions) and hoist
`findProperty`/`projectDir` reads to configuration time as defensive cleanup.

Also bring `socket manifest gradle --facts` (and its `kotlin` alias) up to
sbt-parity on resolution knobs: `--configs=<comma-separated suffixes>` for
case-insensitive configuration-name filtering, and `--ignore-unresolved` to
opt out of the new default of failing the run when a dep can't resolve.
Both options also honored from `socket.json` and via the auto-manifest path.

Verified end-to-end on OpenTelemetry's Java SDK (CC + parallel CC, Gradle
9.5.1): default emits 419 components, `--configs=compileClasspath,
runtimeClasspath` drops to 335 with 0 tooling-tagged. Unresolved-dep fixture
fails with exit 1 by default; `--ignore-unresolved` flips to exit 0 with
the unresolvable surfaced as a `direct` entry.
…ad of suffix match

Suffix matching was arbitrary — `--configs=compileClasspath` silently
matched every variant (testCompileClasspath, jvmMainCompileClasspath, ...).
Switch to explicit glob patterns (`*` and `?` wildcards), case-insensitive.
The old suffix behavior is now expressed as `*CompileClasspath`; bare names
match exactly.

Verified on OpenTelemetry's Java SDK: `*CompileClasspath,*RuntimeClasspath`
preserves the 335-component / 0-tooling outcome; bare `runtimeClasspath`
narrows to 215 (the standalone configs only); `*test*` matches every
test-related config (345 components, all dev-tagged).
When a user types a glob, they almost always mean what they typed. Silent
lower-casing makes `*compileclasspath` and `*CompileClasspath` behave the
same — convenient until it isn't, and surprising when matching a custom
config whose canonical name has unusual casing. Drop the case-insensitive
flag.
@mtorp
Copy link
Copy Markdown
Contributor

The --ignore-unresolved flag description is misleading:

With --facts: skip dependencies that fail to resolve instead of failing the run

(at src/commands/manifest/cmd-manifest-gradle.mts:43-45 and the kotlin twin at cmd-manifest-kotlin.mts:48-50)

Per the test plan and socket-facts.init.gradle:261-278, unresolved deps are still emitted as direct entries — their declared selector coords are upserted into nodes and directIds regardless of the flag. --ignore-unresolved only controls whether the build aborts; nothing is actually "skipped" from the output.

That mismatch will trip users who pass the flag expecting unresolved entries to be omitted from .socket.facts.json. Suggested rewording:

With --facts: warn on unresolved dependencies instead of failing the run (they're still emitted as direct entries with whatever coordinates were declared)

Same edit applies to the help-text body in cmd-manifest-gradle.mts:87 ("--ignore-unresolved to skip dependencies that fail to resolve") and to the matching test snapshots.

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.

LGTM — the configuration-cache fix is the right approach (good rationale in the inline comments at convert-gradle-to-facts.mts:50-67), defensive hoisting of project reads in socket-facts.init.gradle:287-303 is a nice belt-and-suspenders, and parity with sbt --facts is clean. Left an inline comment about the misleading --ignore-unresolved description; happy to land as-is and follow up, or address pre-merge — your call.

Mirror the gradle command's `--configs` semantics so a user who learned
one wildcard syntax gets the same behavior on the other. The sbt plugin
replaces its prior set-membership check with a glob matcher (`*` and `?`,
case-sensitive); bare names — the existing comma-separated-names form —
keep working unchanged because no wildcards = literal-name match.

Verified end-to-end on a fresh sbt 1.10 fixture: default emits 20
components (5-name DefaultConfs unchanged), `*test*` matches `test`
giving the same 20, capitalized `*Test*` matches nothing (skips with
"no resolvable dependencies") confirming case-sensitivity, and bare
`compile` resolves only the compile-scope deps (2 components).
The flag was described as "skip dependencies that fail to resolve", but
in gradle/kotlin the implementation still emits unresolved deps as
`direct` entries with their declared coordinates — the flag only controls
whether the build aborts. Reword to match what actually happens, on
mtorp's PR feedback.

The sbt variant is reworded too (its `isEmittable` filter does drop
unresolved deps from the output, so the wording there reflects that).
@jfblaa
Copy link
Copy Markdown
Contributor Author

Good catch — fixed in 3de29a4.

Reworded the --ignore-unresolved flag description (and the corresponding help-body line) on gradle/kotlin to match what actually happens: unresolved deps are still emitted as direct entries with their declared coordinates; the flag only flips whether the run aborts vs. warns. Used your suggested wording.

While I was in there I also tightened the sbt variant — its isEmittable filter does drop unresolved deps from the output, so the sbt description now says "(unresolved deps are not emitted to the facts file)" rather than implying the same gradle behavior. The two are accurate to their respective implementations now.

Snapshots regenerated for all three commands.

The gradle init script was upserting unresolved deps into the components
output with their selector-only coordinates (no classifier, no ext,
possibly empty version), so a `.socket.facts.json` from `--ignore-
unresolved` carried half-formed entries that downstream tools (coana's
`mvn dependency:get`) would attempt to resolve against Maven Central
and 404 on. The sbt plugin's `isEmittable` filter already drops these
the same way — this aligns gradle to match.

`--ignore-unresolved` now means the same thing on both commands:
unresolved deps drive the abort-vs-warn decision but never reach the
emitted facts file. Flag descriptions and help text reworded to match.
Per mtorp's PR feedback.
@jfblaa
Copy link
Copy Markdown
Contributor Author

Spotted a related divergence while clarifying the wording: gradle was actually upserting unresolved deps into .socket.facts.json as direct entries (selector-only coords — no classifier, no ext, possibly empty version), while sbt's isEmittable filter dropped them from the output entirely. Same flag, different observable effect on the resulting file.

Fixed in 72ebc30 by aligning gradle to sbt's behavior — unresolved deps drive the abort/warn decision but no longer leak into the emitted components. The flag descriptions and help text are reworded accordingly, and --ignore-unresolved now means the same thing on both commands: "the run continues, only resolved deps end up in the output."

Verified on the unresolvable-dep fixture: default still fails with exit 1; --ignore-unresolved exits 0 with only the resolved commons-lang3 in the output (not the phantom com.example.does.not.exist entry that previously sneaked through). opentelemetry-java is still 419 components — no happy-path regression.

@jfblaa Jeppe Fredsgaard Blaabjerg (jfblaa) merged commit c1987a9 into v1.x May 28, 2026
12 checks passed
@jfblaa Jeppe Fredsgaard Blaabjerg (jfblaa) deleted the jfblaa/rea-484-socket-manifest-gradle-facts-configuration-cache-compat-sbt branch May 28, 2026 12:46
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