This is an automated email from the ASF dual-hosted git repository.

paulk-asert pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/groovy.git


The following commit(s) were added to refs/heads/master by this push:
     new f597292f28 improved error message for some cases when .groovy/grapes 
cache is corrupt
f597292f28 is described below

commit f597292f28f9d4de2eafd49bca2b514fcc756caa
Author: Paul King <[email protected]>
AuthorDate: Sun May 10 19:49:37 2026 +1000

    improved error message for some cases when .groovy/grapes cache is corrupt
---
 .../main/groovy/groovy/grape/ivy/GrapeIvy.groovy   | 66 +++++++++++++++++++++-
 1 file changed, 64 insertions(+), 2 deletions(-)

diff --git 
a/subprojects/groovy-grape-ivy/src/main/groovy/groovy/grape/ivy/GrapeIvy.groovy 
b/subprojects/groovy-grape-ivy/src/main/groovy/groovy/grape/ivy/GrapeIvy.groovy
index 23bf7198df..06f7c9218a 100644
--- 
a/subprojects/groovy-grape-ivy/src/main/groovy/groovy/grape/ivy/GrapeIvy.groovy
+++ 
b/subprojects/groovy-grape-ivy/src/main/groovy/groovy/grape/ivy/GrapeIvy.groovy
@@ -42,6 +42,7 @@ import org.apache.ivy.core.module.id.ModuleId
 import org.apache.ivy.core.module.id.ModuleRevisionId
 import org.apache.ivy.core.report.ArtifactDownloadReport
 import org.apache.ivy.core.report.ResolveReport
+import org.apache.ivy.core.resolve.IvyNode
 import org.apache.ivy.core.resolve.ResolveOptions
 import org.apache.ivy.core.settings.IvySettings
 import org.apache.ivy.plugins.matcher.ExactPatternMatcher
@@ -102,7 +103,8 @@ class GrapeIvy implements GrapeEngine {
         Message.setDefaultLogger(new PlatformLoggingMessageLogger())
 
         settings = new IvySettings()
-        settings.setVariable('user.home.url', new 
File(System.getProperty('user.home')).toURI().toURL() as String)
+        def url = new File(System.getProperty('user.home')).toURI().toURL() as 
String
+        settings.setVariable('user.home.url', url.endsWith("/") ? url[0..-2] : 
url)
         File grapeConfig = getLocalGrapeConfig()
         if (grapeConfig.exists()) {
             try {
@@ -451,7 +453,7 @@ class GrapeIvy implements GrapeEngine {
         }
 
         if (report.hasError()) {
-            throw new RuntimeException("Error grabbing Grapes -- 
${report.getAllProblemMessages()}")
+            throw new RuntimeException("Error grabbing Grapes -- 
${report.getAllProblemMessages()}${diagnoseHalfPopulatedLocalM2(report)}")
         }
         if (report.getDownloadSize() && reportDownloads) {
             System.err.println("Downloaded ${report.getDownloadSize() >> 10} 
Kbytes in ${report.getDownloadTime()}ms:\n  
${report.getAllArtifactsReports()*.toString().join('\n  ')}")
@@ -466,6 +468,66 @@ class GrapeIvy implements GrapeEngine {
         report
     }
 
+    /**
+     * When a download fails, check whether the local Maven cache has the POM 
but
+     * not the primary artifact for any failed dependency. Ivy binds artifact
+     * downloads to the resolver that resolved the descriptor, so a localm2 
with
+     * only the POM blocks the chain from falling through to Maven Central.
+     */
+    private static String diagnoseHalfPopulatedLocalM2(ResolveReport report) {
+        File m2root = new File(System.getProperty('user.home'), 
'.m2/repository')
+        if (!m2root.isDirectory()) return ''
+        StringBuilder hint = new StringBuilder()
+        Set<String> seen = new LinkedHashSet<String>()
+        // Artifact-level failures ("download failed: g#a;v!a.jar") — primary 
half-population.
+        for (ArtifactDownloadReport adr : report.getFailedArtifactsReports()) {
+            String classifier = (String) 
adr.getArtifact().getExtraAttribute('classifier')
+            appendHintForCoord(adr.getArtifact().getModuleRevisionId(),
+                    adr.getArtifact().getName(),
+                    adr.getArtifact().getExt() ?: 'jar',
+                    classifier, m2root, hint, seen)
+        }
+        // Dependency-level failures ("unresolved dependency: g#a;v not 
found") — typical when
+        // the JAR-only-in-localm2 case combines with a transient Central 
descriptor lookup miss.
+        for (IvyNode node : report.getUnresolvedDependencies()) {
+            appendHintForCoord(node.getId(), node.getId().getName(), 'jar', 
null, m2root, hint, seen)
+        }
+        return hint.toString()
+    }
+
+    private static void appendHintForCoord(ModuleRevisionId mrid, String 
artName, String ext,
+                                           String classifier, File m2root, 
StringBuilder hint,
+                                           Set<String> seen) {
+        String org = mrid.getOrganisation()
+        String mod = mrid.getName()
+        String rev = mrid.getRevision()
+        String coord = "${org}:${mod}:${rev}".toString()
+        if (!seen.add(coord)) return
+        File dir = new File(m2root, "${org.replace('.', '/')}/${mod}/${rev}")
+        if (!dir.isDirectory()) return
+        File pom = new File(dir, "${mod}-${rev}.pom")
+        String suffix = classifier ? "-${classifier}" : ''
+        File primary = new File(dir, "${artName}-${rev}${suffix}.${ext}")
+        if (pom.exists() && !primary.exists()) {
+            File grapeIvyXml = new File(System.getProperty('user.home'),
+                    ".groovy/grapes/${org}/${mod}/ivy-${rev}.xml")
+            hint.append('\nHint: ').append(coord)
+                .append(' has a POM but no ').append(ext).append(' in your 
local Maven cache.\n')
+                .append('Either run:  mvn dependency:get 
-Dartifact=').append(coord).append('\n')
+                .append('or remove these so Grape can fetch from Maven 
Central:\n')
+                .append('  ').append(dir).append('\n')
+                .append('  ').append(grapeIvyXml).append(' (and ivy-')
+                .append(rev).append('.xml.original, 
ivydata-').append(rev).append('.properties)')
+        } else if (primary.exists() && !pom.exists()) {
+            hint.append('\nHint: ').append(coord)
+                .append(' has a ').append(ext).append(' but no POM in your 
local Maven cache.\n')
+                .append('Ivy needs the POM to resolve the descriptor; without 
it the chain falls through ')
+                .append('to Maven Central and any transient Central failure 
surfaces as "not found".\n')
+                .append('Either run:  mvn dependency:get 
-Dartifact=').append(coord).append('\n')
+                .append('or remove ').append(dir).append(' to force a clean 
fetch.')
+        }
+    }
+
     private addIvyListener() {
         ivyInstance.eventManager.addIvyListener { ivyEvent ->
             switch (ivyEvent) {

Reply via email to