ci: align Gradle/JDK caching with TDLM org workflows #16
No files matched your search
@@ -21,65 +21,95 @@ jobs:
|
||||
build:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 90
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
# Same pattern as TDLM ForestryMC/IndustrialCraft2 setup-build: cache the JDK toolchain.
|
||||
- name: Set up JDK 17
|
||||
uses: actions/setup-java@v6
|
||||
with:
|
||||
distribution: 'liberica'
|
||||
java-version: '17'
|
||||
java-package: 'jdk+fx'
|
||||
cache-jdk: true
|
||||
|
||||
# Nest mirrors github.com/gradle/actions as actions/gradle-actions (also as gradle/actions).
|
||||
# v5 rejects cache-provider; v6 supports it. Default on v6 is "enhanced" (commercial) —
|
||||
# on Gitea use external and cache Gradle home with actions/cache below (org IC2/Forestry pattern).
|
||||
- name: Setup Gradle
|
||||
# Nest hosts this under actions/gradle-actions (mirror of github.com/gradle/actions).
|
||||
# uses: gradle/actions/... makes act clone git.nest…/gradle/actions which is not seeded.
|
||||
uses: actions/gradle-actions/setup-gradle@v5
|
||||
uses: actions/gradle-actions/setup-gradle@v6
|
||||
with:
|
||||
cache-cleanup: never
|
||||
# Only main writes the dependency cache; pull requests read it.
|
||||
cache-read-only: ${{ github.ref != 'refs/heads/main' }}
|
||||
# The repository's wrapper downloads Gradle itself, so cache that download too.
|
||||
- name: Cache Gradle distribution
|
||||
cache-provider: ${{ github.server_url == 'https://github.com' && 'basic' || 'external' }}
|
||||
|
||||
# Cache wrapper + dependency/build caches (covers what setup-gradle skips under external).
|
||||
- name: Cache Gradle home
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.gradle/caches
|
||||
~/.gradle/wrapper
|
||||
.gradle/wrapper-neo
|
||||
key: gradle-dist-${{ hashFiles('gradle/wrapper/gradle-wrapper.properties', 'gradle/wrapper/GradleWrapperNeo.java') }}
|
||||
- name: Install Xvfb
|
||||
key: gradle-home-${{ runner.os }}-${{ hashFiles('gradle/wrapper/gradle-wrapper.properties', 'gradle/wrapper/GradleWrapperNeo.java', '**/libs.versions.toml', '**/*.gradle.kts', '**/*.gradle') }}
|
||||
restore-keys: |
|
||||
gradle-home-${{ runner.os }}-
|
||||
|
||||
# Xvfb alone is not enough: JavaFX Glass needs GTK/X11 libs or Platform.startup fails with
|
||||
# GtkPlatformFactory "Internal Error" and :HMCL:test can hang (looks like a checkstyle stall).
|
||||
- name: Cache apt packages
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/apt-cache
|
||||
key: apt-javafx-xvfb-${{ runner.os }}-${{ runner.arch }}-1
|
||||
|
||||
- name: Install Xvfb and JavaFX GTK deps
|
||||
run: |
|
||||
# Installing is the slowest setup step, so skip it when the runner already has xvfb, and
|
||||
# only refresh the package index when a plain install fails.
|
||||
command -v xvfb-run >/dev/null || sudo apt-get install -y --no-install-recommends xvfb \
|
||||
|| { sudo apt-get update && sudo apt-get install -y --no-install-recommends xvfb; }
|
||||
pkgs='xvfb libgtk-3-0 libxtst6 libxxf86vm1 libgl1 libxi6 libx11-6 libxext6 libxrender1'
|
||||
sudo mkdir -p /var/cache/apt/archives/partial
|
||||
if [ -d "${HOME}/apt-cache" ] && [ -n "$(ls -A "${HOME}/apt-cache" 2>/dev/null)" ]; then
|
||||
sudo cp -a "${HOME}/apt-cache/." /var/cache/apt/archives/
|
||||
fi
|
||||
# Prefer cached debs; refresh the index only when install cannot resolve packages.
|
||||
sudo apt-get install -y --no-install-recommends $pkgs \
|
||||
|| { sudo apt-get update && sudo apt-get install -y --no-install-recommends $pkgs; }
|
||||
mkdir -p "${HOME}/apt-cache"
|
||||
cp -a /var/cache/apt/archives/*.deb "${HOME}/apt-cache/" 2>/dev/null || true
|
||||
|
||||
# One Gradle run builds, tests and runs the code and translation checks, so the
|
||||
# project is configured and compiled once instead of in two workflows.
|
||||
- name: Build and check with Gradle
|
||||
run: xvfb-run --auto-servernum ./gradlew build checkstyle checkTranslations --no-daemon --parallel
|
||||
env:
|
||||
JAVA_TOOL_OPTIONS: -Djava.awt.headless=true
|
||||
JAVA_TOOL_OPTIONS: -Djava.awt.headless=true -Dprism.order=sw
|
||||
MICROSOFT_AUTH_ID: ${{ secrets.MICROSOFT_AUTH_ID }}
|
||||
CURSEFORGE_API_KEY: ${{ secrets.CURSEFORGE_API_KEY }}
|
||||
|
||||
- name: Get short SHA
|
||||
run: echo "SHORT_SHA=${GITHUB_SHA::7}" >> $GITHUB_ENV
|
||||
|
||||
- name: Upload JAR
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: HMCL-${{ env.SHORT_SHA }}-jar
|
||||
path: HMCL/build/libs/HMCL-*.jar
|
||||
archive: false
|
||||
|
||||
- name: Upload EXE
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: HMCL-${{ env.SHORT_SHA }}-exe
|
||||
path: HMCL/build/libs/HMCL-*.exe
|
||||
archive: false
|
||||
|
||||
- name: Upload SH
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: HMCL-${{ env.SHORT_SHA }}-sh
|
||||
path: HMCL/build/libs/HMCL-*.sh
|
||||
archive: false
|
||||
|
||||
- name: Upload DEB
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
|
||||
@@ -10,6 +10,9 @@ import org.jetbrains.annotations.NotNullByDefault;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
/// Owns the shared JavaFX toolkit for the lifetime of the test process.
|
||||
@NotNullByDefault
|
||||
@@ -27,11 +30,36 @@ public final class JavaFXLauncher {
|
||||
System.setProperty("hmcl.scroll.speed", "1");
|
||||
try {
|
||||
initializeAnimationPolicy();
|
||||
Platform.startup(() -> {
|
||||
});
|
||||
started = true;
|
||||
} catch (IllegalStateException e) {
|
||||
started = true;
|
||||
// Platform.startup can hang when GTK/Glass is broken (minimal CI images with only xvfb).
|
||||
// Bound class-init so :HMCL:test cannot stall the whole Gradle build.
|
||||
CountDownLatch done = new CountDownLatch(1);
|
||||
AtomicReference<Throwable> error = new AtomicReference<>();
|
||||
Thread t = new Thread(() -> {
|
||||
try {
|
||||
Platform.startup(() -> {
|
||||
});
|
||||
} catch (Throwable e) {
|
||||
error.set(e);
|
||||
} finally {
|
||||
done.countDown();
|
||||
}
|
||||
}, "hmcl-javafx-startup");
|
||||
t.setDaemon(true);
|
||||
t.start();
|
||||
if (!done.await(45, TimeUnit.SECONDS)) {
|
||||
System.err.println("JavaFX Platform.startup timed out; JavaFX tests will be skipped");
|
||||
} else {
|
||||
Throwable e = error.get();
|
||||
if (e == null) {
|
||||
started = true;
|
||||
} else if (e instanceof IllegalStateException) {
|
||||
started = true;
|
||||
} else {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
@@ -2,6 +2,10 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later WITH AdditionRef-HMCL
|
||||
package org.jackhuang.hmcl;
|
||||
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
public final class JavaFXLauncher {
|
||||
|
||||
private JavaFXLauncher() {
|
||||
@@ -10,13 +14,37 @@ public final class JavaFXLauncher {
|
||||
private static boolean started = false;
|
||||
|
||||
static {
|
||||
// init JavaFX Toolkit
|
||||
// init JavaFX Toolkit — never block class init forever if GTK/Glass hangs under CI.
|
||||
try {
|
||||
javafx.application.Platform.startup(() -> {
|
||||
});
|
||||
started = true;
|
||||
} catch (IllegalStateException e) {
|
||||
started = true;
|
||||
CountDownLatch done = new CountDownLatch(1);
|
||||
AtomicReference<Throwable> error = new AtomicReference<>();
|
||||
Thread t = new Thread(() -> {
|
||||
try {
|
||||
javafx.application.Platform.startup(() -> {
|
||||
});
|
||||
} catch (Throwable e) {
|
||||
error.set(e);
|
||||
} finally {
|
||||
done.countDown();
|
||||
}
|
||||
}, "hmcl-javafx-startup");
|
||||
t.setDaemon(true);
|
||||
t.start();
|
||||
if (!done.await(45, TimeUnit.SECONDS)) {
|
||||
System.err.println("JavaFX Platform.startup timed out; JavaFX tests will be skipped");
|
||||
} else {
|
||||
Throwable e = error.get();
|
||||
if (e == null) {
|
||||
started = true;
|
||||
} else if (e instanceof IllegalStateException) {
|
||||
// Toolkit already running in this JVM.
|
||||
started = true;
|
||||
} else {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user