From 02463120c6080cc735d2a6f986760da8d304289d Mon Sep 17 00:00:00 2001 From: Hendrik Brombeer <20863417+hbrombeer@users.noreply.github.com> Date: Sat, 17 Oct 2026 11:17:17 +0200 Subject: [PATCH 1/2] ci: resolve Gradle dependencies through the runners' Maven mirror 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. --- .github/actions/maven-mirror/action.yml | 42 ++++++++++++++ .../grounds-maven-mirror.init.gradle.kts | 55 +++++++++++++++++++ .../workflows/docker-gradle-build-push.yml | 10 ++++ .github/workflows/gradle-ci.yml | 3 + .github/workflows/gradle-publish.yml | 3 + 5 files changed, 113 insertions(+) create mode 100644 .github/actions/maven-mirror/action.yml create mode 100644 .github/actions/maven-mirror/grounds-maven-mirror.init.gradle.kts diff --git a/.github/actions/maven-mirror/action.yml b/.github/actions/maven-mirror/action.yml new file mode 100644 index 0000000..59ef3ab --- /dev/null +++ b/.github/actions/maven-mirror/action.yml @@ -0,0 +1,42 @@ +name: Maven mirror +description: > + Points Gradle at the runners' Maven mirror (Maven Central, the Gradle Plugin + Portal and PaperMC) by installing a Gradle init script. Uses the runner's + MAVEN_MIRROR_URL, which only the self-hosted runners set. Without it, or when + the mirror does not answer, the action warns and changes nothing, so Gradle + resolves from upstream exactly as before. + +outputs: + init-script: + description: > + Path of the installed init script, empty when the mirror is not used. + Pass it to Docker builds as the BuildKit secret `gradle_init`. + value: ${{ steps.install.outputs.init-script }} + +runs: + using: composite + steps: + - name: Install Maven mirror init script + id: install + shell: bash + env: + TEMPLATE: ${{ github.action_path }}/grounds-maven-mirror.init.gradle.kts + run: | + set -euo pipefail + url="${MAVEN_MIRROR_URL:-}" + url="${url%/}" + if [ -z "$url" ]; then + echo "::warning::MAVEN_MIRROR_URL is not set on this runner, resolving from upstream" + exit 0 + fi + probe="$url/releases/org/jetbrains/kotlin/kotlin-stdlib/maven-metadata.xml" + status="$(curl -s -o /dev/null -m 3 -w '%{http_code}' "$probe" || true)" + if [ "$status" != "200" ]; then + echo "::warning::Maven mirror $url unreachable (HTTP ${status:-none}), resolving from upstream" + exit 0 + fi + script="$HOME/.gradle/init.d/grounds-maven-mirror.init.gradle.kts" + mkdir -p "$(dirname "$script")" + sed "s|@MIRROR_URL@|$url|" "$TEMPLATE" > "$script" + echo "init-script=$script" >> "$GITHUB_OUTPUT" + echo "Using Maven mirror $url" diff --git a/.github/actions/maven-mirror/grounds-maven-mirror.init.gradle.kts b/.github/actions/maven-mirror/grounds-maven-mirror.init.gradle.kts new file mode 100644 index 0000000..b106b24 --- /dev/null +++ b/.github/actions/maven-mirror/grounds-maven-mirror.init.gradle.kts @@ -0,0 +1,55 @@ +// Points the public upstream repositories at the CI runners' Maven mirror. +// Installed by the groundsgg/.github maven-mirror action on self-hosted runners only. +// GitHub Packages, mavenLocal() and every other repository stay as the build declares them. +// +// The mirror is plain HTTP inside the cluster, so only the rewritten repositories get +// isAllowInsecureProtocol. + +val mirror = "@MIRROR_URL@".trimEnd('/') +val pluginPortal = "https://plugins.gradle.org/m2" +val rewrites = mapOf( + "https://repo.maven.apache.org/maven2" to "$mirror/releases", + "https://repo1.maven.org/maven2" to "$mirror/releases", + pluginPortal to "$mirror/gradle-plugins", + "https://repo.papermc.io/repository/maven-public" to "$mirror/papermc", +) + +fun RepositoryHandler.useMirror() { + whenObjectAdded { + val repo = this as? MavenArtifactRepository ?: return@whenObjectAdded + val upstream = repo.url.toString().trimEnd('/') + val target = rewrites[upstream] ?: return@whenObjectAdded + if (upstream == pluginPortal) { + // gradlePluginPortal() is a repository Gradle builds itself; swap it for a plain + // maven repository rather than mutating it. Adding here keeps its place in the list. + remove(repo) + maven { + name = "groundsMavenMirrorGradlePlugins" + url = uri(target) + isAllowInsecureProtocol = true + } + } else { + repo.url = uri(target) + repo.isAllowInsecureProtocol = true + } + } +} + +println("Using Maven mirror $mirror") + +beforeSettings { + buildscript.repositories.useMirror() + pluginManagement.repositories.useMirror() + dependencyResolutionManagement.repositories.useMirror() +} + +settingsEvaluated { + // Without any pluginManagement repositories Gradle falls back to an implicit plugin portal + // that never enters this list; declare it so it gets swapped for the mirror. + if (pluginManagement.repositories.isEmpty()) pluginManagement.repositories.gradlePluginPortal() +} + +allprojects { + buildscript.repositories.useMirror() + repositories.useMirror() +} diff --git a/.github/workflows/docker-gradle-build-push.yml b/.github/workflows/docker-gradle-build-push.yml index 3f76081..95760de 100644 --- a/.github/workflows/docker-gradle-build-push.yml +++ b/.github/workflows/docker-gradle-build-push.yml @@ -80,6 +80,14 @@ jobs: echo "github.token=${{ secrets.PACKAGES_TOKEN || secrets.GITHUB_TOKEN }}" } > ~/.gradle/gradle.properties + # Installs the init script on the runner, for the `./gradlew -q properties` + # below, and hands the same file to the image builds as the BuildKit secret + # `gradle_init`. A Dockerfile opts in by mounting it on its Gradle RUN: + # --mount=type=secret,id=gradle_init,target=/root/.gradle/init.d/grounds-maven-mirror.init.gradle.kts,required=false + - name: Use the Maven mirror + id: mirror + uses: groundsgg/.github/.github/actions/maven-mirror@feat/maven-mirror + - name: Read Gradle project version id: gradle_version run: | @@ -186,6 +194,7 @@ jobs: GITHUB_USER=${{ github.actor }} secrets: | github_token=${{ secrets.PACKAGES_TOKEN || secrets.GITHUB_TOKEN }} + secret-files: ${{ steps.mirror.outputs.init-script && format('gradle_init={0}', steps.mirror.outputs.init-script) || '' }} - name: Build and push image if: github.event_name != 'pull_request' @@ -203,6 +212,7 @@ jobs: GITHUB_USER=${{ github.actor }} secrets: | github_token=${{ secrets.PACKAGES_TOKEN || secrets.GITHUB_TOKEN }} + secret-files: ${{ steps.mirror.outputs.init-script && format('gradle_init={0}', steps.mirror.outputs.init-script) || '' }} - name: Generate SBOM if: github.event_name != 'pull_request' diff --git a/.github/workflows/gradle-ci.yml b/.github/workflows/gradle-ci.yml index bc94e26..64034f3 100644 --- a/.github/workflows/gradle-ci.yml +++ b/.github/workflows/gradle-ci.yml @@ -68,6 +68,9 @@ jobs: with: cache-disabled: ${{ runner.environment != 'github-hosted' }} + - name: Use the Maven mirror + uses: groundsgg/.github/.github/actions/maven-mirror@feat/maven-mirror + - name: Configure GitHub Packages credentials run: | mkdir -p ~/.gradle diff --git a/.github/workflows/gradle-publish.yml b/.github/workflows/gradle-publish.yml index 51158ef..855d353 100644 --- a/.github/workflows/gradle-publish.yml +++ b/.github/workflows/gradle-publish.yml @@ -68,6 +68,9 @@ jobs: with: cache-disabled: ${{ runner.environment != 'github-hosted' }} + - name: Use the Maven mirror + uses: groundsgg/.github/.github/actions/maven-mirror@feat/maven-mirror + - name: Configure GitHub Packages credentials for resolution run: | mkdir -p ~/.gradle From 5a29e9a62916e3cbb3f77acb754c61041de25d35 Mon Sep 17 00:00:00 2001 From: Hendrik Brombeer <20863417+hbrombeer@users.noreply.github.com> Date: Sat, 17 Oct 2026 11:29:51 +0200 Subject: [PATCH 2/2] ci: reference the maven-mirror action at main 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. --- .github/workflows/docker-gradle-build-push.yml | 2 +- .github/workflows/gradle-ci.yml | 2 +- .github/workflows/gradle-publish.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/docker-gradle-build-push.yml b/.github/workflows/docker-gradle-build-push.yml index 95760de..8e59f1d 100644 --- a/.github/workflows/docker-gradle-build-push.yml +++ b/.github/workflows/docker-gradle-build-push.yml @@ -86,7 +86,7 @@ jobs: # --mount=type=secret,id=gradle_init,target=/root/.gradle/init.d/grounds-maven-mirror.init.gradle.kts,required=false - name: Use the Maven mirror id: mirror - uses: groundsgg/.github/.github/actions/maven-mirror@feat/maven-mirror + uses: groundsgg/.github/.github/actions/maven-mirror@main - name: Read Gradle project version id: gradle_version diff --git a/.github/workflows/gradle-ci.yml b/.github/workflows/gradle-ci.yml index 64034f3..b4fe9b3 100644 --- a/.github/workflows/gradle-ci.yml +++ b/.github/workflows/gradle-ci.yml @@ -69,7 +69,7 @@ jobs: cache-disabled: ${{ runner.environment != 'github-hosted' }} - name: Use the Maven mirror - uses: groundsgg/.github/.github/actions/maven-mirror@feat/maven-mirror + uses: groundsgg/.github/.github/actions/maven-mirror@main - name: Configure GitHub Packages credentials run: | diff --git a/.github/workflows/gradle-publish.yml b/.github/workflows/gradle-publish.yml index 855d353..3740421 100644 --- a/.github/workflows/gradle-publish.yml +++ b/.github/workflows/gradle-publish.yml @@ -69,7 +69,7 @@ jobs: cache-disabled: ${{ runner.environment != 'github-hosted' }} - name: Use the Maven mirror - uses: groundsgg/.github/.github/actions/maven-mirror@feat/maven-mirror + uses: groundsgg/.github/.github/actions/maven-mirror@main - name: Configure GitHub Packages credentials for resolution run: |