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 799693c5df test additional user agent workarounds
799693c5df is described below

commit 799693c5df9020bdc2be8b5abf0ce554dd8e6e38
Author: Paul King <[email protected]>
AuthorDate: Mon May 11 19:31:29 2026 +1000

    test additional user agent workarounds
---
 .../main/groovy/org.apache.groovy-tested.gradle    | 33 ++++++++++++++--------
 .../main/groovy/groovy/grape/ivy/GrapeIvy.groovy   | 29 +++++++++++--------
 .../groovy/grape/ivy/defaultGrapeConfig.xml        |  3 +-
 3 files changed, 41 insertions(+), 24 deletions(-)

diff --git a/build-logic/src/main/groovy/org.apache.groovy-tested.gradle 
b/build-logic/src/main/groovy/org.apache.groovy-tested.gradle
index e21fefdc67..3e65a10a3f 100644
--- a/build-logic/src/main/groovy/org.apache.groovy-tested.gradle
+++ b/build-logic/src/main/groovy/org.apache.groovy-tested.gradle
@@ -62,24 +62,33 @@ def grapeBridgeCache = 
(findProperty('groovy.grape.bridge-cache') ?:
 tasks.withType(Test).configureEach {
     def fs = objects.newInstance(TestServices).fileSystemOperations
     def grapeDirectory = new File(temporaryDir, '.groovy')
-    // GROOVY-12005 / 2026-05: Maven Central (Fastly) returns HTTP 404 with a 
1-byte
-    // body for requests bearing the JDK URLConnection default User-Agent 
("Java/<version>"),
-    // which Ivy's BasicURLHandler relies on. We must set http.agent as a JVM 
argument
-    // here rather than via systemProperty / a static initializer in GrapeIvy: 
by the
-    // time GrapeIvy's class init runs (lazily on first @Grab), the test JVM 
has already
-    // loaded HttpURLConnection and its agent is locked in. Passing it as 
`-Dhttp.agent`
-    // on the JVM command line guarantees it's set before any class is 
initialized.
-    // Tests pass with literally any non-"Java/*" value; see also the matching 
static
-    // initializer in GrapeIvy for CLI / standalone-script coverage.
     def options = ['-ea', "-Xms${groovyJUnit_ms}", "-Xmx${groovyJUnit_mx}",
-                   '-Duser.language=en', '-Duser.country=US',
-                   '-Dhttp.agent=Apache-Ivy_Groovy-Grape']
+                   '-Duser.language=en', '-Duser.country=US']
     jvmArgs(*options)
+    // GROOVY-12005 / 2026-05: Maven Central (Fastly) returns HTTP 404 with a
+    // 1-byte body for requests bearing the JDK URLConnection default 
User-Agent
+    // ("Java/<version>"), which Ivy 2.5.3's BasicURLHandler relies on. We 
adopt
+    // a Maven-shaped UA — Maven Resolver's HTTP transport sends this and the 
CDN
+    // doesn't 404 it. The Maven version string is decorative; the CDN's filter
+    // is on the bare "Java/<version>" default, not on Maven's specific 
version.
+    //
+    // http.agent must reach the test JVM as `-Dhttp.agent=...` on the command
+    // line: by the time GrapeIvy's matching static initializer runs (lazily on
+    // first @Grab), HttpURLConnection's userAgent static is already populated
+    // and our value would arrive too late. Using `systemProperty` rather than
+    // raw `jvmArgs` preserves the space-containing value as a single argument.
+    systemProperty 'http.agent',
+        "Apache-Maven/3.9.14 (Java ${System.getProperty('java.version')}; 
${System.getProperty('os.name')} ${System.getProperty('os.version')})"
     systemProperty 'groovy.force.illegal.access', 
findProperty('groovy.force.illegal.access')
     def testdb = System.properties['groovy.testdb.props']
     if (testdb) {
         systemProperty 'groovy.testdb.props', testdb
     }
+    // Forward StrictLocalM2Resolver opt-in to test JVMs. Accepts -P or -D.
+    def strictLocalM2 = findProperty('groovy.grape.strict-localm2') ?: 
System.properties['groovy.grape.strict-localm2']
+    if (strictLocalM2) {
+        systemProperty 'groovy.grape.strict-localm2', strictLocalM2
+    }
     def headless = System.properties['java.awt.headless']
     if (headless == 'true') {
         systemProperty 'java.awt.headless', 'true'
@@ -105,7 +114,7 @@ tasks.withType(Test).configureEach {
         println "Using ${executable} to run tests"
     }
 
-    forkEvery = 50
+    forkEvery = 40
     maxParallelForks = sharedConfiguration.isRunningOnCI ? 1 : 
(Runtime.runtime.availableProcessors().intdiv(2) ?: 1)
     scanForTestClasses = true
     ignoreFailures = false
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 52828b5a2c..4d952722de 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
@@ -69,21 +69,28 @@ class GrapeIvy implements GrapeEngine {
     static {
         // GROOVY-12005 / 2026-05: Maven Central (Fastly) currently returns 
HTTP 404
         // with a 1-byte body for requests whose User-Agent is the JDK 
URLConnection
-        // default "Java/<version>". Any other value (curl/x.y, 
Apache-Ivy/..., etc.)
-        // gets the expected HTTP 200. Ivy 2.5.3 uses java.net.URLConnection 
under
-        // the hood, which honours -Dhttp.agent. We set it here so direct 
grape use
-        // (CLI `grape install`, `groovy [email protected]`, embedded 
usage)
-        // gets a working agent — those paths load GrapeIvy before any other 
class
-        // touches the network, so this static init runs early enough.
+        // default "Java/<version>". Maven's own requests are not blocked, so 
we
+        // adopt a Maven-shaped User-Agent — that's the value Maven Resolver's 
HTTP
+        // transport sends, and it passes the CDN cleanly. Ivy 2.5.3's
+        // BasicURLHandler uses java.net.URLConnection, which honours 
-Dhttp.agent.
+        //
+        // This static initializer covers direct grape use (CLI `grape 
install`,
+        // `groovy [email protected]`, embedded usage) — those paths 
load
+        // GrapeIvy before any other class touches the network, so it runs 
early
+        // enough that HttpURLConnection's userAgent static init reads our 
value.
         //
         // For test JVMs the same property MUST be set on the JVM command line 
—
-        // see the matching -Dhttp.agent in 
build-logic/.../org.apache.groovy-tested.gradle —
-        // because by the time GrapeIvy is class-loaded for the first @Grab in 
a test,
-        // the agent has already been cached deep in the JDK's HTTP machinery.
+        // see the matching `systemProperty 'http.agent', ...` in
+        // build-logic/.../org.apache.groovy-tested.gradle — because by the 
time
+        // GrapeIvy is class-loaded for the first @Grab in a test, 
HttpURLConnection
+        // has already been initialised and its userAgent is locked in.
         //
-        // Respect any value the user has already configured.
+        // Respect any value the user has already configured (e.g. 
-Dhttp.agent=…).
         if (System.getProperty('http.agent') == null) {
-            System.setProperty('http.agent', 'Apache-Ivy_Groovy-Grape')
+            // Maven version is decorative — the CDN's filter is on the bare
+            // "Java/<version>" default, not on a specific Maven version 
string.
+            String ua = "Apache-Maven/3.9.14 (Java 
${System.getProperty('java.version')}; ${System.getProperty('os.name')} 
${System.getProperty('os.version')})"
+            System.setProperty('http.agent', ua)
         }
     }
 
diff --git 
a/subprojects/groovy-grape-ivy/src/main/resources/groovy/grape/ivy/defaultGrapeConfig.xml
 
b/subprojects/groovy-grape-ivy/src/main/resources/groovy/grape/ivy/defaultGrapeConfig.xml
index 4fb56af2f3..7fc507514e 100644
--- 
a/subprojects/groovy-grape-ivy/src/main/resources/groovy/grape/ivy/defaultGrapeConfig.xml
+++ 
b/subprojects/groovy-grape-ivy/src/main/resources/groovy/grape/ivy/defaultGrapeConfig.xml
@@ -21,13 +21,14 @@
 <ivysettings>
   <settings defaultResolver="downloadGrapes"/>
   <caches lockStrategy="artifact-lock-nio"/>
+  <typedef name="strict-localm2" 
classname="groovy.grape.ivy.StrictLocalM2Resolver"/>
   <resolvers>
     <chain name="downloadGrapes" returnFirst="true">
       <filesystem name="cachedGrapes">
         <ivy 
pattern="${user.home}/.groovy/grapes/[organisation]/[module]/ivy-[revision].xml"/>
         <artifact 
pattern="${user.home}/.groovy/grapes/[organisation]/[module]/[type]s/[artifact]-[revision](-[classifier]).[ext]"/>
       </filesystem>
-      <ibiblio name="localm2" root="${user.home.url}/.m2/repository/" 
checkmodified="true" changingPattern=".*" changingMatcher="regexp" 
m2compatible="true"/>
+      <strict-localm2 name="localm2" root="${user.home.url}/.m2/repository/" 
checkmodified="true" changingPattern=".*" changingMatcher="regexp" 
m2compatible="true"/>
       <!-- TODO: add 'endorsed groovy extensions' resolver here -->
       <ibiblio name="ibiblio" m2compatible="true"/>
     </chain>

Reply via email to