feat(manifest): unify Gradle config-level resolution transparency (REA-519)#1398
Merged
Jeppe Fredsgaard Blaabjerg (jfblaa) merged 4 commits intoJul 2, 2026
Conversation
…ings Gradle phrases a zero-compatible-variant failure several ways depending on version and whether consumer attributes were supplied. The classifier only matched "no matching variant"/"no variants of", so "unable to find a matching variant" and "no compatible variant" fell through to the generic "other" category. Severity was unaffected (both blocking), but the report rendered under the wrong header; recognize the alternate phrasings so these surface under the accurate "No compatible variant" heading.
A whole Gradle configuration whose resolution throws (as opposed to a single unresolved dependency within it) was previously caught and logged to stdout but never surfaced: it still counted as "scanned", so the user was never told we could not scan it. Record such configs as a new `unscannable` fact and classify them by the same cause rules as per-dependency failures: variant ambiguity stays lenient (a one-line notice), every other cause is fail-closed (blocking, overridable by --ignore-unresolved). The build script still exits cleanly and the TS layer owns the reporting; a config-level failure no longer trips the crashed-build path, which stays reserved for a build that produced nothing at all. Also drop the stale "--include-configs default excludes AGP instrumented-test classpaths" help text: the native-facts rewrite removed that exclusion, and AGP AndroidTest configs resolve like any other.
Martin Torp (mtorp)
approved these changes
Jul 2, 2026
Martin Torp (mtorp)
left a comment
Contributor
There was a problem hiding this comment.
LGTM — traced the unscannable pipeline end-to-end (init script → records → assemble → render → exit policy), and typecheck + the render/help-snapshot tests pass locally. Two cosmetic nits inline, neither blocking.
This was referenced Jul 2, 2026
- Don't lead the failure summary with a blank line when there are only blocking unscannable configs and no per-dependency failures (avoids a dangling marker under logger.fail); added a regression assertion. - Correct the config-throw comments: the path fires on config-LEVEL failures (resolution-rule throws, artifact-transform failures, broken repos), not AGP *AndroidTest* configs, which resolve cleanly per the validation.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What & why
Follow-up to REA-516. Two Gradle resolution paths sat in tension with the "reveal what we couldn't resolve" policy:
scanned, so the user was never told we couldn't scan it.--include-configshelp still advertised a default AGP-instrumented-test exclusion that the native-facts rewrite had already removed.Changes
REA-519 — config-level transparency
try/catchnow records the config as a newunscannablefact (build-tool's deepest-cause message) instead of swallowing it. The build script still exits cleanly; the TS layer owns reporting.unscannableconfigs are classified by the same cause rules as per-dependency failures: variant ambiguity stays lenient (a one-line notice), every other cause is fail-closed (blocking, overridable by--ignore-unresolved).--verbose.REA-516 — classifier clarification (first commit)
unable to find a matching variant,no compatible variant) so they render under the accurate "No compatible variant" header instead of falling through to "Other". Severity unchanged (both blocking).Empirical validation
Validated on real throwaway Gradle projects (AGP 8.13.2 / Gradle 9.2.1 / JDK 21):
*AndroidTest*configs resolve cleanly from the init script (41 nodes/config, no ambiguity, no throws) — the hypothesized "init-script can't supply consumer attributes → ambiguity" does not occur, because AGP sets the attributes on the configuration itself. Removing the name-based exclusion was safe.lenientConfigurationswallows all dependency-graph failures (not-found, capability conflict, version conflict, variant ambiguity) into per-dep records. Theunscannablepath therefore fires only on non-graph exceptions (resolution-rule throw, artifact-transform failure, broken repo config, …) — a rare but real safety net.unscannablepipeline was exercised end-to-end (one config throwing, others resolving): default → exit 1 + no facts written;--ignore-unresolved→ exit 0 + partial facts written.Testing
pnpm check:tscclean; targeted unit tests green (render/assemble/gradle/kotlin help snapshots).socket manifest gradle --factsagainst the test projects.Note
Medium Risk
Changes exit-code and facts-write behavior when Gradle configurations throw during resolution (partial SBOM vs hard fail), which affects manifest/scan workflows on Android and complex Gradle builds; logic is covered by new render tests and mirrors existing per-dep failure policy.
Overview
Gradle manifest facts generation now records whole-configuration resolution failures instead of swallowing them in the init script. Thrown configs emit
unscannablerecords through the line protocol; the TypeScript layer classifies each message with the same rules as per-dependency failures (variant ambiguity → non-blocking notice; other causes → blocking, honor--ignore-unresolved). Reports distinguish “could not scan N configuration(s)” from unresolved deps, omit unscannable configs from “Resolution succeeded in”, and include them in--verboseoutput. Crashed-build detection no longer treats unscannable-only outcomes as a total build failure.--include-configshelp formanifest gradle/manifest kotlinno longer claims a default AGP instrumented-test exclusion (default is every resolvable configuration). Gradle’s “unable to find a matching variant” / “no compatible variant” wording maps to the existing no-matching-variant blocking category.Reviewed by Cursor Bugbot for commit 0c65404. Configure here.