Skip to content

Commit 1aaff8b

Browse files
committed
fix(manifest): report the Gradle failures the facts init script swallowed
Dependency-verification failures, failed project dependencies, modules whose artifacts cannot be listed and jars that cannot be fetched all reached the facts as healthy or vanished silently. They are now reported, named by the version Gradle attempted, and an unfetched jar is marked so its component gets no targets.
1 parent bff5259 commit 1aaff8b

14 files changed

Lines changed: 473 additions & 58 deletions

‎CHANGELOG.md‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
1515
- Maven dependencies with missing or invalid POMs are reported in every module that uses them, `system`-scope jars reach reachability analysis, and a failing dependency no longer fails scopes that `excludeConfigs` leaves out or the build when it lost a version conflict.
1616
- Reachability scans that hit unresolved JVM dependencies now point out that ignoring them also needs `--reach-continue-on-install-errors`.
1717
- A Gradle dependency whose variant is ambiguous in every configuration of its project now fails the run instead of quietly dropping out of the scan.
18+
- Gradle dependencies that fail dependency verification, or whose jars cannot be fetched, are reported instead of passed on as healthy.
19+
- Failed Gradle project dependencies are reported instead of silently dropping everything they pull in, and failures name the version that actually failed.
1820

1921
## [1.10.0](https://github.com/SocketDev/socket-cli/releases/tag/v1.10.0) - 2026-10-22
2022

‎src/commands/manifest/scripts/resolution-report-gradle.mts‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,11 @@ export function classifyGradleFailure(detail: string): FailureCategory {
2121
) {
2222
return 'no-matching-variant'
2323
}
24-
if (t.includes('cannot choose between')) {
24+
// The artifact-level form: several transforms produce the requested variant.
25+
if (
26+
t.includes('cannot choose between') ||
27+
t.includes('found multiple transformation chains')
28+
) {
2529
return 'variant-ambiguity'
2630
}
2731
if (

‎src/commands/manifest/scripts/resolution-report-render.test.mts‎

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,21 @@ describe('resolution failure classification', () => {
4949
expect(r.nonBlockingNotice).toContain('ambiguous variant')
5050
})
5151

52+
it('classifies an ambiguous Gradle artifact transform as variant ambiguity', () => {
53+
const r = renderResolutionErrorReport(
54+
[
55+
f(
56+
'com.example:amb:1.0',
57+
'Found multiple transformation chains that produce a variant of com.example:amb:1.0',
58+
),
59+
],
60+
['runtimeClasspath'],
61+
'gradle',
62+
)
63+
expect(r.hasBlockingFailures).toBe(false)
64+
expect(r.nonBlockingNotice).toContain('ambiguous variant')
65+
})
66+
5267
it("classifies Gradle's 'unable to find a matching variant' phrasing as blocking no-matching-variant", () => {
5368
const r = renderResolutionErrorReport(
5469
[

‎src/commands/manifest/scripts/socket-facts.init.gradle‎

Lines changed: 202 additions & 49 deletions
Large diffs are not rendered by default.

‎src/commands/manifest/scripts/test/gradle-compat/.gitignore‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,9 @@ project/.socket.facts.json
66
project/.gradle/
77
project/build/
88
.populate-for.txt
9+
failures/localrepo/
10+
failures/records.tsv
11+
failures/.gradle/
12+
failures/build/
13+
failures/lib/build/
14+
failures/app/build/
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// Kept out of the root project: on some old Gradle, a project dependency on a missing configuration
2+
// fails resolving the declaring project's other configurations too.
3+
configurations {
4+
projectFail
5+
}
6+
dependencies {
7+
projectFail project(path: ':lib', configuration: 'nonexistent')
8+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
// Hermetic fixture for ../smoke-test-failures.sh: each configuration fails to resolve in one way,
2+
// against a generated local Maven repo (see ../../make-stub-repo.sh).
3+
allprojects {
4+
repositories {
5+
maven { url rootProject.file('localrepo').toURI() }
6+
}
7+
}
8+
9+
configurations {
10+
forced
11+
shadowing
12+
verified
13+
walkAborted
14+
quiet
15+
}
16+
configurations.forced.resolutionStrategy.force 'demo:c:9.9'
17+
18+
dependencies {
19+
forced 'demo:a:1.0'
20+
// Shares the group:name of project :lib, but is an external module.
21+
shadowing 'demo:lib:7.7'
22+
verified 'demo:a:1.0'
23+
verified 'demo:tampered:1.0'
24+
walkAborted 'demo:a:1.0'
25+
walkAborted 'demo:boom:1.0'
26+
quiet 'demo:quiet:1.0'
27+
}
28+
29+
// Artifact transforms and dependency verification exist only on newer Gradle.
30+
if (org.gradle.util.GradleVersion.current() >= org.gradle.util.GradleVersion.version('6.2')) {
31+
apply from: 'transforms.gradle'
32+
}
33+
34+
// No build throws from the walk's own reads on demand, so make two of them throw for one module
35+
// each: boom's children, and quiet's file with an empty message.
36+
tasks.matching { it.name == 'socketFactsCollect' }.all { t ->
37+
t.doFirst {
38+
def boom = configurations.walkAborted.resolvedConfiguration.firstLevelModuleDependencies.find { it.moduleName == 'boom' }
39+
def depMeta = boom.getClass().metaClass
40+
def children = depMeta.getMetaMethod('getChildren', [] as Object[])
41+
depMeta.getChildren = { ->
42+
if (delegate.moduleName == 'boom') {
43+
throw new IllegalStateException('walk aborted by the fixture')
44+
}
45+
children.invoke(delegate)
46+
}
47+
def quiet = configurations.quiet.resolvedConfiguration.resolvedArtifacts.find { it.name == 'quiet' }
48+
def artifactMeta = quiet.getClass().metaClass
49+
def file = artifactMeta.getMetaMethod('getFile', [] as Object[])
50+
artifactMeta.getFile = { ->
51+
if (delegate.name == 'quiet') {
52+
throw new RuntimeException('')
53+
}
54+
file.invoke(delegate)
55+
}
56+
}
57+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!-- tampered's jar never matches its recorded checksum; every other artifact is trusted. -->
3+
<verification-metadata xmlns="https://schema.gradle.org/dependency-verification" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="https://schema.gradle.org/dependency-verification https://schema.gradle.org/dependency-verification/dependency-verification-1.0.xsd">
4+
<configuration>
5+
<verify-metadata>false</verify-metadata>
6+
<verify-signatures>false</verify-signatures>
7+
<trusted-artifacts>
8+
<trust group="demo" name="^(?!tampered$).*$" regex="true"/>
9+
</trusted-artifacts>
10+
</configuration>
11+
<components>
12+
<component group="demo" name="tampered" version="1.0">
13+
<artifact name="tampered-1.0.jar">
14+
<sha256 value="0000000000000000000000000000000000000000000000000000000000000000"/>
15+
</artifact>
16+
</component>
17+
</components>
18+
</verification-metadata>
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
apply plugin: 'java'
2+
group = 'demo'
3+
version = '3.0'
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
rootProject.name = 'gradle-compat-failures'
2+
include 'lib', 'app'

0 commit comments

Comments
 (0)