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>