Skip to content

ci: resolve Gradle dependencies through the runners' Maven mirror - #36

Merged
hbrombeer merged 2 commits into
mainfrom
feat/maven-mirror
Oct 17, 2026
Merged

hbrombeer merged 2 commits into
mainfrom
feat/maven-mirror

Conversation

@hbrombeer

Copy link
Copy Markdown
Member

Pull Request

Description

Maven Central answers hbr1's IP with 429, so builds on the self-hosted runners fail at plugin resolution (Could not GET https://repo.maven.apache.org/.../kotlin-stdlib-2.2.20.pom ... 429). The runner pods now set MAVEN_MIRROR_URL to a Reposilite pull-through cache in the runners' cluster, with three repos: /releases, /gradle-plugins and /papermc.

  • New composite action .github/actions/maven-mirror. If MAVEN_MIRROR_URL is set and a 3 s probe of $URL/releases/org/jetbrains/kotlin/kotlin-stdlib/maven-metadata.xml returns 200, it installs ~/.gradle/init.d/grounds-maven-mirror.init.gradle.kts and outputs its path as init-script. Otherwise it writes a ::warning:: and changes nothing, so hosted runners, or a run while the mirror is down, behave as before.
  • What the init script rewrites. Maven Central (mavenCentral(), repo.maven.apache.org, repo1.maven.org) goes to /releases, repo.papermc.io/repository/maven-public to /papermc, and gradlePluginPortal() is replaced by a plain maven { } pointing at /gradle-plugins. Only those repos get isAllowInsecureProtocol, because the mirror is plain HTTP inside the cluster. GitHub Packages, mavenLocal() and every other repo are left alone.
    • It hooks settings buildscript, pluginManagement and dependencyResolutionManagement repositories, plus every project's buildscript and repositories.
    • When a build declares no pluginManagement repositories, Gradle normally falls back to an implicit plugin portal. The script declares that portal explicitly so it also goes through the mirror.
    • It prints Using Maven mirror <url>.
  • gradle-ci.yml and gradle-publish.yml run the action right after "Set up Gradle".
  • docker-gradle-build-push.yml runs it before ./gradlew -q properties. Both build-push steps pass the script as the BuildKit secret gradle_init, and only when the action produced it. A Dockerfile opts in with one line on its Gradle RUN:
    --mount=type=secret,id=gradle_init,target=/root/.gradle/init.d/grounds-maven-mirror.init.gradle.kts,required=false \
    With required=false, local builds and builds on hosted runners still work.

The workflows reference the action at @main, so it only resolves once this PR is merged. The pilot ran against @feat/maven-mirror (first commit).

Type of Change

  • 🐛 Bug fix
  • ✨ New feature
  • 💥 Breaking change
  • ♻️ Refactoring
  • 📚 Documentation
  • 🔧 Chore

Related Issues

  • Pilot: groundsgg/game-survivalgames#11

Testing

  • Unit tests pass (tests/test_docker_workflow.py)
  • Manual testing completed
  • New tests added for new functionality

Local, Gradle 9.2.1, run against the mirror with --refresh-dependencies --info:

  • Plugins (foojay settings plugin, Kotlin JVM, shadow) resolved through /gradle-plugins.
  • mavenCentral() resolved through /releases, and paper-api through /papermc.
  • A buildSrc with kotlin-dsl and no pluginManagement block went through the mirror via the implicit portal.
  • The log has no requests to repo.maven.apache.org or plugins.gradle.org.

Pilot in game-survivalgames#11 on grounds-runners. Both runs passed:

  • Java Build logged Using Maven mirror http://maven-mirror.arc-runners.svc.cluster.local:8080 in the action and in both Gradle invocations.
  • In Docker Build, the image's Gradle RUN logged #15 44.76 Using Maven mirror http://maven-mirror.arc-runners.svc.cluster.local:8080, then BUILD SUCCESSFUL. Its caches were cold (it downloaded the Gradle distribution), so the cluster DNS name resolves inside BuildKit RUN steps under DinD with the docker-container driver. No ClusterIP workaround is needed.

Checklist

  • I have performed a self-review of my own code
  • Tests have been added/updated and pass (if needed)
  • Documentation has been updated (if needed)

Maven Central answers hbr1's IP with 429 and fails builds at plugin
resolution. The self-hosted runners now advertise a pull-through mirror
in MAVEN_MIRROR_URL.

A new maven-mirror composite action probes that mirror and, when it
answers, installs a Gradle init script that points Maven Central, the
Gradle Plugin Portal and PaperMC at it. Without the variable (hosted
runners) or with the mirror down, it warns and changes nothing.

gradle-ci, gradle-publish and docker-gradle-build-push run it before
Gradle. The Docker workflow also passes the script to the image builds
as the BuildKit secret gradle_init, which Dockerfiles opt in to with a
secret mount on their Gradle RUN.
The pilot ran the action from the feat/maven-mirror branch. Point the
reusable workflows at main, the same way they pin update-openapi-snapshot,
so the action resolves once this lands.
@hbrombeer
hbrombeer merged commit fdf6ec8 into main Oct 17, 2026
3 checks passed
@hbrombeer
hbrombeer deleted the feat/maven-mirror branch October 17, 2026 09:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant