Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions .github/scripts/retry.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#!/usr/bin/env bash
# Runs a command and retries it on failure, to ride out transient network
# flakes (interrupted maven downloads, brief mirror outages, and the like).
#
# retry.sh <command> [args...]
#
# RETRY_ATTEMPTS (default 3) sets the total number of attempts; the pause
# between attempts grows with each failure.
set -uo pipefail

attempts="${RETRY_ATTEMPTS:-3}"
code=0
for ((i = 1; i <= attempts; i++)); do
"$@"
code=$?
[ "$code" -eq 0 ] && exit 0
if ((i < attempts)); then
delay=$((i * 15))
echo "::warning::Attempt ${i}/${attempts} of '$*' failed with exit code ${code}; retrying in ${delay}s"
sleep "$delay"
fi
done
echo "::error::All ${attempts} attempts of '$*' failed"
exit "$code"
22 changes: 19 additions & 3 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,17 @@ jobs:
java-version: ${{ matrix.java }}
distribution: 'microsoft'

- name: setup gradle with dependency caching
# Caches Gradle dependencies between runs (written on the default
# branch, read everywhere), so a flaky maven rarely has to be hit.
uses: gradle/actions/setup-gradle@v6

- name: make gradle wrapper executable
run: chmod +x ./gradlew

- name: build :${{ matrix.mc }}:build
run: ./gradlew :${{ matrix.mc }}:build --stacktrace
# retry.sh reruns the build on transient failures (flaky maven downloads).
run: bash .github/scripts/retry.sh ./gradlew :${{ matrix.mc }}:build --stacktrace

- name: upload jar
uses: actions/upload-artifact@v7
Expand Down Expand Up @@ -97,6 +103,9 @@ jobs:
java-version: ${{ matrix.java }}
distribution: 'microsoft'

- name: setup gradle with dependency caching
uses: gradle/actions/setup-gradle@v6

- name: make gradle wrapper executable
run: chmod +x ./gradlew

Expand All @@ -109,7 +118,11 @@ jobs:
run: bash .github/scripts/generate-test-world.sh ${{ matrix.mc }}

- name: launch minecraft ${{ matrix.mc }} with mod
run: ./gradlew :launchtest:runProductionClientGameTest -PtestMcVersion=${{ matrix.mc }} --stacktrace
# Two attempts (not the default three): a real game crash fails both,
# and a second full launch still fits the 40-minute job timeout.
env:
RETRY_ATTEMPTS: '2'
run: bash .github/scripts/retry.sh ./gradlew :launchtest:runProductionClientGameTest -PtestMcVersion=${{ matrix.mc }} --stacktrace

- name: check survival world screenshot was taken
run: test -n "$(find launchtest/run/screenshots -name '*betterhud-survival-world.png' -print -quit 2>/dev/null)"
Expand Down Expand Up @@ -228,11 +241,14 @@ jobs:
25
distribution: 'microsoft'

- name: setup gradle with dependency caching
uses: gradle/actions/setup-gradle@v6

- name: make gradle wrapper executable
run: chmod +x ./gradlew

- name: build all variants and write the upload guide
run: ./gradlew modrinthBundle --stacktrace "-Porg.gradle.java.installations.paths=$JAVA_HOME_21_X64,$JAVA_HOME_25_X64"
run: bash .github/scripts/retry.sh ./gradlew modrinthBundle --stacktrace "-Porg.gradle.java.installations.paths=$JAVA_HOME_21_X64,$JAVA_HOME_25_X64"

- name: upload modrinth bundle
uses: actions/upload-artifact@v7
Expand Down
7 changes: 6 additions & 1 deletion .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -113,11 +113,16 @@ jobs:
java-version: ${{ matrix.java }}
distribution: 'microsoft'

- name: setup gradle with dependency caching
# Caches Gradle dependencies between runs; combined with retry.sh below
# this keeps transient maven download failures from killing a release.
uses: gradle/actions/setup-gradle@v6

- name: make gradle wrapper executable
run: chmod +x ./gradlew

- name: build :${{ matrix.mc }}:build
run: ./gradlew :${{ matrix.mc }}:build --stacktrace
run: bash .github/scripts/retry.sh ./gradlew :${{ matrix.mc }}:build --stacktrace

- name: publish to modrinth and curseforge
uses: Kir-Antipov/mc-publish@v3.3
Expand Down
8 changes: 6 additions & 2 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ repositories {
maven { url 'https://maven.terraformersmc.com/releases/' }
}

// Adds the Modrinth maven and modmenuDependency(), a fallback for when the
// TerraformersMC maven is unreachable.
apply from: "${rootDir}/gradle/modrinth-fallback.gradle"

// MC 26+ uses fabric-rendering-v1's transitive deps but rejects ASM coming from
// annotation processors.
if (!isLegacy) {
Expand All @@ -52,14 +56,14 @@ dependencies {

modImplementation "net.fabricmc:fabric-loader:${project.loader_version}"
modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_api_version}"
modImplementation "com.terraformersmc:modmenu:${project.modmenu_version}"
modImplementation modmenuDependency(project.modmenu_version)
modImplementation("me.shedaniel.cloth:cloth-config-fabric:${project.cloth_config_version}") {
exclude(group: 'net.fabricmc.fabric-api')
}
} else {
implementation "net.fabricmc:fabric-loader:${project.loader_version}"
implementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_api_version}"
implementation "com.terraformersmc:modmenu:${project.modmenu_version}"
implementation modmenuDependency(project.modmenu_version)
implementation("me.shedaniel.cloth:cloth-config-fabric:${project.cloth_config_version}") {
exclude(group: 'net.fabricmc.fabric-api')
}
Expand Down
41 changes: 41 additions & 0 deletions gradle/modrinth-fallback.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// Fallback for the TerraformersMC maven, which intermittently fails downloads
// in CI ("Premature end of chunk coded message body"). The Modrinth maven
// serves the exact same Mod Menu release jars under maven.modrinth coordinates
// with identical version strings, so when the TerraformersMC coordinate cannot
// be resolved the build switches to the Modrinth mirror instead of failing.
//
// Apply after the project's own repositories block, then declare Mod Menu via
// modmenuDependency("<version>").

repositories {
exclusiveContent {
forRepository {
maven {
name = 'Modrinth'
url = 'https://api.modrinth.com/maven'
}
}
// Only maven.modrinth artifacts come from here, and nowhere else — the
// other repositories are never queried for the fallback coordinates.
filter {
includeGroup 'maven.modrinth'
}
}
}

ext.modmenuDependency = { String version ->
def primary = "com.terraformersmc:modmenu:${version}"
try {
// Probe the primary coordinate with a detached configuration. Release
// versions are cached forever, so once downloaded (or restored from the
// CI Gradle cache) this touches no network at all.
def probe = configurations.detachedConfiguration(dependencies.create(primary))
probe.transitive = false
probe.resolve()
return primary
} catch (Exception e) {
logger.warn("Could not resolve ${primary} from the TerraformersMC maven " +
"(${e.message}); falling back to maven.modrinth:modmenu:${version}")
return "maven.modrinth:modmenu:${version}"
}
}
6 changes: 5 additions & 1 deletion launchtest/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,10 @@ repositories {
maven { url 'https://maven.terraformersmc.com/releases/' }
}

// Adds the Modrinth maven and modmenuDependency(), a fallback for when the
// TerraformersMC maven is unreachable.
apply from: "${rootDir}/gradle/modrinth-fallback.gradle"

// Matches the variant builds: MC 26+ rejects ASM coming from annotation processors.
if (!isLegacy) {
configurations.configureEach {
Expand Down Expand Up @@ -107,7 +111,7 @@ dependencies {
// mavens; transitive deps are excluded because each fat jar already nests
// everything it needs via jar-in-jar.
productionRuntimeMods("net.fabricmc.fabric-api:fabric-api:${entry.fabricApi}") { transitive = false }
productionRuntimeMods("com.terraformersmc:modmenu:${entry.modmenu}") { transitive = false }
productionRuntimeMods(modmenuDependency(entry.modmenu)) { transitive = false }
productionRuntimeMods("me.shedaniel.cloth:cloth-config-fabric:${entry.cloth}") { transitive = false }

if (entry.clientGametest) {
Expand Down