From 3cbab39d163f95b86504d5d9023f9a121203eab1 Mon Sep 17 00:00:00 2001 From: layue13 Date: Sun, 8 Nov 2026 19:54:52 +0000 Subject: [PATCH 1/9] ci: publish HMCL.exe/jar to Gitea Release and tdlm-hmcl/latest Add a TDLMPack-oriented publish workflow (nest API, same pattern as TDLMPack pack.yml) plus docs for the exact consume URLs. Pack builds still do not Maven-resolve the launcher. --- .github/workflows/publish-tdlm.yml | 201 +++++++++++++++++++++++++++++ docs/TDLM_CONSUME.md | 46 +++++++ 2 files changed, 247 insertions(+) create mode 100644 .github/workflows/publish-tdlm.yml create mode 100644 docs/TDLM_CONSUME.md diff --git a/.github/workflows/publish-tdlm.yml b/.github/workflows/publish-tdlm.yml new file mode 100644 index 0000000000..ecffc1336c --- /dev/null +++ b/.github/workflows/publish-tdlm.yml @@ -0,0 +1,201 @@ +name: Publish TDLM launcher + +# Builds HMCL.exe / HMCL.jar and publishes them for TDLMPack / players: +# 1. Gitea Release assets (versioned download page) +# 2. Gitea generic package tdlm-hmcl/latest (stable curl URLs) +# +# Mirrors TDLM/TDLMPack's pack.yml publish style (nest API host; Cloudflare +# in front of git.potatolab.uk blocks Python urllib for large uploads). + +on: + workflow_dispatch: + inputs: + release_tag: + description: Release tag (also used as the release title). Example: 2026.11.08 or v3.dev-abc1234 + required: true + type: string + +concurrency: + group: ${{ github.workflow }} + cancel-in-progress: false + +jobs: + publish: + runs-on: ubuntu-latest + timeout-minutes: 90 + steps: + - uses: actions/checkout@v4 + + - name: Set up JDK 17 + JavaFX + uses: actions/setup-java@v4 + with: + distribution: 'liberica' + java-version: '17' + java-package: 'jdk+fx' + + - name: Setup Gradle + uses: gradle/actions/setup-gradle@v4 + with: + cache-cleanup: never + + - name: Cache Gradle distribution + uses: actions/cache@v4 + with: + path: | + ~/.gradle/wrapper + .gradle/wrapper-neo + key: gradle-dist-${{ hashFiles('gradle/wrapper/gradle-wrapper.properties', 'gradle/wrapper/GradleWrapperNeo.java') }} + + - name: Install Xvfb + run: | + 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; } + + - name: Build executables + run: xvfb-run --auto-servernum ./gradlew :HMCL:makeExecutables --no-daemon --parallel + env: + JAVA_TOOL_OPTIONS: -Djava.awt.headless=true + VERSION_TYPE: unofficial + MICROSOFT_AUTH_ID: ${{ secrets.MICROSOFT_AUTH_ID }} + CURSEFORGE_API_KEY: ${{ secrets.CURSEFORGE_API_KEY }} + + - name: Stage stable launcher names + run: | + set -euo pipefail + mkdir -p build/tdlm-hmcl + jar="$(ls -1 HMCL/build/libs/HMCL-*.jar | grep -v '\.sha' | head -n1)" + exe="$(ls -1 HMCL/build/libs/HMCL-*.exe | grep -v '\.sha' | head -n1)" + test -n "$jar" && test -n "$exe" + cp -f "$jar" build/tdlm-hmcl/HMCL.jar + cp -f "$exe" build/tdlm-hmcl/HMCL.exe + # Prefer Gradle-produced checksums when present; otherwise compute. + if [ -f "${jar}.sha256" ]; then cp -f "${jar}.sha256" build/tdlm-hmcl/HMCL.jar.sha256 + else sha256sum build/tdlm-hmcl/HMCL.jar | awk '{print $1}' > build/tdlm-hmcl/HMCL.jar.sha256 + fi + if [ -f "${exe}.sha256" ]; then cp -f "${exe}.sha256" build/tdlm-hmcl/HMCL.exe.sha256 + else sha256sum build/tdlm-hmcl/HMCL.exe | awk '{print $1}' > build/tdlm-hmcl/HMCL.exe.sha256 + fi + echo "STAGED_JAR=$jar" >> "$GITHUB_ENV" + echo "STAGED_EXE=$exe" >> "$GITHUB_ENV" + ls -la build/tdlm-hmcl + + # Stable player/CI channel: overwritten in place under tdlm-hmcl/latest. + - name: Publish generic package tdlm-hmcl/latest + env: + TDLM_PACK_TOKEN: ${{ secrets.TDLM_PACK_TOKEN }} + GITEA_PACKAGE_ROOT: https://git.nest.potatolab.uk:8443/api/packages/TDLM/generic/tdlm-hmcl/latest + run: | + python3 - <<'PY' + import os, pathlib, urllib.error, urllib.parse, urllib.request + token = os.environ.get("TDLM_PACK_TOKEN", "") + if not token: + raise SystemExit("TDLM_PACK_TOKEN is required to publish tdlm-hmcl (same org secret as TDLMPack)") + upload_root = os.environ["GITEA_PACKAGE_ROOT"].rstrip("/") + root = pathlib.Path("build/tdlm-hmcl") + + def put(name: str, data: bytes): + url = upload_root + "/" + urllib.parse.quote(name, safe="") + request = urllib.request.Request(url, data=data, method="PUT") + request.add_header("Authorization", "token " + token) + request.add_header("Content-Type", "application/octet-stream") + request.add_header("User-Agent", "tdlm-hmcl-publish") + try: + with urllib.request.urlopen(request, timeout=1800) as response: + response.read() + except urllib.error.HTTPError as exc: + raise SystemExit("HTTP %s uploading %s: %s" % (exc.code, name, exc.read()[:500])) + + for path in sorted(root.iterdir()): + if path.is_file(): + put(path.name, path.read_bytes()) + print("PACKAGE " + path.name) + print("generic=" + upload_root) + PY + + # Gitea release (versioned). Nest API avoids Cloudflare blocking large urllib uploads. + - name: Publish Gitea release + env: + RELEASE_TAG: ${{ inputs.release_tag }} + TDLM_PACK_TOKEN: ${{ secrets.TDLM_PACK_TOKEN }} + GITEA_API_URL: https://git.nest.potatolab.uk:8443/api/v1 + GITEA_PACKAGE_ROOT: https://git.nest.potatolab.uk:8443/api/packages/TDLM/generic/tdlm-hmcl/latest + TARGET_COMMIT: ${{ github.sha }} + run: | + python3 - <<'PY' + import json, os, urllib.error, urllib.parse, urllib.request + tag = os.environ["RELEASE_TAG"].strip() + token = os.environ.get("TDLM_PACK_TOKEN", "") + api = os.environ["GITEA_API_URL"].rstrip("/") + "/repos/TDLM/HMCL" + package_root = os.environ["GITEA_PACKAGE_ROOT"].rstrip("/") + target = os.environ.get("TARGET_COMMIT", "main").strip() or "main" + if not token: + raise SystemExit("TDLM_PACK_TOKEN is required to publish the release") + if not tag: + raise SystemExit("release_tag is empty") + + def call(method, path, data=None, content_type="application/json", timeout=90): + request = urllib.request.Request(api + path, data=data, method=method) + request.add_header("Authorization", "token " + token) + request.add_header("Accept", "application/json") + request.add_header("User-Agent", "tdlm-hmcl-release") + if data is not None and content_type: + request.add_header("Content-Type", content_type) + try: + with urllib.request.urlopen(request, timeout=timeout) as response: + body = response.read() + return json.loads(body) if body else {} + except urllib.error.HTTPError as exc: + if exc.code == 404: + return None + detail = exc.read()[:500] + raise SystemExit("HTTP %s for %s: %s" % (exc.code, path, detail)) + + body = ( + "TDLM/HMCL launcher binaries for players and TDLMPack.\n\n" + "Stable generic (overwrite in place):\n" + "- %s/HMCL.exe\n" + "- %s/HMCL.jar\n\n" + "TDLMPack does not Maven-resolve this launcher; point the pack README / install docs at these URLs " + "instead of hand-copying exe/jar.\n" + "Client pack updates stay on tdlm-client/latest (unchanged).\n" + ) % (package_root, package_root) + + quoted = urllib.parse.quote(tag, safe="") + release = call("GET", "/releases/tags/" + quoted) + if release and not release.get("draft"): + raise SystemExit("Release tag already exists: " + tag) + if not release: + release = call("POST", "/releases", json.dumps({ + "tag_name": tag, + "name": tag, + "target_commitish": target, + "draft": True, + "prerelease": False, + "body": body, + }).encode("utf-8")) + release_id = release["id"] + + staged = [ + ("HMCL.exe", "build/tdlm-hmcl/HMCL.exe"), + ("HMCL.jar", "build/tdlm-hmcl/HMCL.jar"), + ("HMCL.exe.sha256", "build/tdlm-hmcl/HMCL.exe.sha256"), + ("HMCL.jar.sha256", "build/tdlm-hmcl/HMCL.jar.sha256"), + ] + for name, path in staged: + if not os.path.exists(path): + raise SystemExit("missing " + path) + for old in call("GET", "/releases/%s/assets" % release_id) or []: + if old.get("name") == name: + call("DELETE", "/releases/%s/assets/%s" % (release_id, old["id"])) + with open(path, "rb") as asset: + call( + "POST", + "/releases/%s/assets?name=%s" % (release_id, urllib.parse.quote(name)), + asset.read(), + "application/octet-stream", + 1800, + ) + print("RELEASE asset " + name) + call("PATCH", "/releases/%s" % release_id, json.dumps({"draft": False}).encode("utf-8")) + print(release.get("html_url", "Release created")) + PY diff --git a/docs/TDLM_CONSUME.md b/docs/TDLM_CONSUME.md new file mode 100644 index 0000000000..6d440e29f2 --- /dev/null +++ b/docs/TDLM_CONSUME.md @@ -0,0 +1,46 @@ +# TDLMPack consume path for TDLM/HMCL + +TDLMPack does **not** pull this launcher via Maven, a version pin, or a pack-build download step. +The pack build only publishes the HMCL Server Modpack client (`server-manifest.json` + `overrides/`) +to Gitea generic `tdlm-client/latest`. Players install the launcher separately. + +## After this repo’s publish workflow + +Run **Actions → Publish TDLM launcher → Run workflow** with a new `release_tag`. +Requires repo secret `TDLM_PACK_TOKEN` (same org token TDLMPack uses for package/release uploads). + +### 1. Stable generic URLs (preferred for README / curl / CI) + +Overwrite-in-place, same host style as the client fileApi: + +```text +https://git.nest.potatolab.uk:8443/api/packages/TDLM/generic/tdlm-hmcl/latest/HMCL.exe +https://git.nest.potatolab.uk:8443/api/packages/TDLM/generic/tdlm-hmcl/latest/HMCL.jar +``` + +Checksums (when published): `HMCL.exe.sha256`, `HMCL.jar.sha256` next to those names. + +### 2. Gitea Release (versioned page) + +```text +https://git.potatolab.uk/TDLM/HMCL/releases/latest +``` + +Assets on each tag: `HMCL.exe`, `HMCL.jar`, plus `.sha256` files. +(Use the nest API host for scripted uploads; public download links on `git.potatolab.uk` are fine for browsers.) + +### 3. What TDLMPack should change + +In `TDLM/TDLMPack` README §启动, replace the repo-only “安装 TDLM/HMCL” step with one of the URLs above. +**Do not** add HMCL as a Gradle/Maven dependency of the pack — pack CI stays pack-only. + +### 4. Unchanged: client pack → launcher data + +HMCL still reads the pack fileApi (default): + +```text +https://git.nest.potatolab.uk:8443/api/packages/TDLM/generic/tdlm-client/latest/server-manifest.json +``` + +Override: `-Dhmcl.client.fileApi=…` or jar attribute `hmcl.client.fileApi`. +That path is pack content for the already-installed launcher, not how the pack obtains `HMCL.exe`. -- 2.47.3 From 64345731051aac1038723696f6716bafda7928f5 Mon Sep 17 00:00:00 2001 From: layue13 Date: Sun, 8 Nov 2026 20:02:47 +0000 Subject: [PATCH 2/9] fix: point DEFAULT_CLIENT_FILE_API at packages.dandanton.com CDN Use https://packages.dandanton.com/tdlm-client/latest (same relative layout as Pack's tdlm-client/latest). Document TDLM_CLIENT_FILE_API alignment and the Pack publish/mirror caveat. --- .github/workflows/publish-tdlm.yml | 3 ++- .../java/org/jackhuang/hmcl/Metadata.java | 4 +-- docs/TDLM_CONSUME.md | 27 ++++++++++++++----- 3 files changed, 24 insertions(+), 10 deletions(-) diff --git a/.github/workflows/publish-tdlm.yml b/.github/workflows/publish-tdlm.yml index ecffc1336c..ecceb483b6 100644 --- a/.github/workflows/publish-tdlm.yml +++ b/.github/workflows/publish-tdlm.yml @@ -157,7 +157,8 @@ jobs: "- %s/HMCL.jar\n\n" "TDLMPack does not Maven-resolve this launcher; point the pack README / install docs at these URLs " "instead of hand-copying exe/jar.\n" - "Client pack updates stay on tdlm-client/latest (unchanged).\n" + "Client pack fileApi default: https://packages.dandanton.com/tdlm-client/latest\n" + "(Pack must publish/mirror the same layout; see docs/TDLM_CONSUME.md).\n" ) % (package_root, package_root) quoted = urllib.parse.quote(tag, safe="") diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/Metadata.java b/HMCL/src/main/java/org/jackhuang/hmcl/Metadata.java index a1cb3d4438..5032e7a82a 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/Metadata.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/Metadata.java @@ -141,9 +141,9 @@ public final class Metadata { return null; } - /// Default client Server Modpack fileApi published by TDLMPack (Gitea generic package `latest`). + /// Default client Server Modpack fileApi (CDN mirror of Pack's `tdlm-client/latest` layout). public static final String DEFAULT_CLIENT_FILE_API = - "https://git.nest.potatolab.uk:8443/api/packages/TDLM/generic/tdlm-client/latest"; + "https://packages.dandanton.com/tdlm-client/latest"; /// Stable client update root (`server-manifest.json` and `overrides/` live under this URL). /// diff --git a/docs/TDLM_CONSUME.md b/docs/TDLM_CONSUME.md index 6d440e29f2..f275c076fe 100644 --- a/docs/TDLM_CONSUME.md +++ b/docs/TDLM_CONSUME.md @@ -1,8 +1,8 @@ # TDLMPack consume path for TDLM/HMCL TDLMPack does **not** pull this launcher via Maven, a version pin, or a pack-build download step. -The pack build only publishes the HMCL Server Modpack client (`server-manifest.json` + `overrides/`) -to Gitea generic `tdlm-client/latest`. Players install the launcher separately. +The pack build publishes the HMCL Server Modpack client (`server-manifest.json` + `overrides/`) +under the `tdlm-client/latest` layout. Players install the launcher separately. ## After this repo’s publish workflow @@ -34,13 +34,26 @@ Assets on each tag: `HMCL.exe`, `HMCL.jar`, plus `.sha256` files. In `TDLM/TDLMPack` README §启动, replace the repo-only “安装 TDLM/HMCL” step with one of the URLs above. **Do not** add HMCL as a Gradle/Maven dependency of the pack — pack CI stays pack-only. -### 4. Unchanged: client pack → launcher data +### 4. Client pack fileApi (CDN) -HMCL still reads the pack fileApi (default): +HMCL default (`Metadata.DEFAULT_CLIENT_FILE_API`): ```text -https://git.nest.potatolab.uk:8443/api/packages/TDLM/generic/tdlm-client/latest/server-manifest.json +https://packages.dandanton.com/tdlm-client/latest ``` -Override: `-Dhmcl.client.fileApi=…` or jar attribute `hmcl.client.fileApi`. -That path is pack content for the already-installed launcher, not how the pack obtains `HMCL.exe`. +First install / updates fetch `{fileApi}/server-manifest.json` (and `overrides/**` under that root). +Override at runtime: `-Dhmcl.client.fileApi=…` or jar attribute `hmcl.client.fileApi`. + +**TDLMPack must use the same URL** so `server-manifest.json` embeds it: + +```text +TDLM_CLIENT_FILE_API=https://packages.dandanton.com/tdlm-client/latest +``` + +Set that as the Gitea Actions **variable** on `TDLM/TDLMPack` (pack.yml already passes `vars.TDLM_CLIENT_FILE_API`). + +**Publish caveat:** Pack’s current workflow PUTs the client tree to whatever `TDLM_CLIENT_FILE_API` is (upload root = fileApi). Today that defaults to Gitea generic +`https://git.nest.potatolab.uk:8443/api/packages/TDLM/generic/tdlm-client/latest`. +If you only change the HMCL default to the CDN and leave Pack publishing to Gitea, players will 404 unless you **mirror/sync** Gitea → `packages.dandanton.com/tdlm-client/latest`. +If you set `TDLM_CLIENT_FILE_API` to the CDN URL, Pack CI must be able to PUT to that CDN (or you must split “upload origin” from “public fileApi” in Pack — not implemented today). Do not point HMCL at the CDN without one of those two ops paths. -- 2.47.3 From 9de2d8eeefa2efde6f981c73a7632a7629a24f9e Mon Sep 17 00:00:00 2001 From: layue13 Date: Sun, 8 Nov 2026 20:25:50 +0000 Subject: [PATCH 3/9] ci: align publish auth with TDLMPack / org secret pattern Keep secrets.TDLM_PACK_TOKEN (same name as pack.yml); document that it is currently a TDLMPack repo secret while MAVEN_* are org-level. Fall back to github.token so release can proceed after merge without a new HMCL-only secret when the pack token is promoted or unavailable. --- .github/workflows/publish-tdlm.yml | 25 +++++++++++++++++++++---- docs/TDLM_CONSUME.md | 14 +++++++++++++- 2 files changed, 34 insertions(+), 5 deletions(-) diff --git a/.github/workflows/publish-tdlm.yml b/.github/workflows/publish-tdlm.yml index ecceb483b6..1c7a05531e 100644 --- a/.github/workflows/publish-tdlm.yml +++ b/.github/workflows/publish-tdlm.yml @@ -6,6 +6,13 @@ name: Publish TDLM launcher # # Mirrors TDLM/TDLMPack's pack.yml publish style (nest API host; Cloudflare # in front of git.potatolab.uk blocks Python urllib for large uploads). +# +# Auth (same name as TDLMPack pack.yml): +# secrets.TDLM_PACK_TOKEN — Gitea API token with package + release write. +# Prefer an *org* secret (like MAVEN_USER/MAVEN_PASSWORD) so HMCL inherits it; +# today it exists only as a TDLMPack *repo* secret unless promoted. +# Falls back to github.token when TDLM_PACK_TOKEN is unset (same-repo release; +# org generic packages may still need the pack token). on: workflow_dispatch: @@ -82,14 +89,20 @@ jobs: # Stable player/CI channel: overwritten in place under tdlm-hmcl/latest. - name: Publish generic package tdlm-hmcl/latest env: + # Same secret name as TDLM/TDLMPack pack.yml (org or repo). Not MAVEN_*. TDLM_PACK_TOKEN: ${{ secrets.TDLM_PACK_TOKEN }} + GITHUB_TOKEN: ${{ github.token }} GITEA_PACKAGE_ROOT: https://git.nest.potatolab.uk:8443/api/packages/TDLM/generic/tdlm-hmcl/latest run: | python3 - <<'PY' import os, pathlib, urllib.error, urllib.parse, urllib.request - token = os.environ.get("TDLM_PACK_TOKEN", "") + token = (os.environ.get("TDLM_PACK_TOKEN") or os.environ.get("GITHUB_TOKEN") or "").strip() + token_src = "TDLM_PACK_TOKEN" if os.environ.get("TDLM_PACK_TOKEN", "").strip() else "github.token" if not token: - raise SystemExit("TDLM_PACK_TOKEN is required to publish tdlm-hmcl (same org secret as TDLMPack)") + raise SystemExit( + "Need secrets.TDLM_PACK_TOKEN (promote TDLMPack's repo secret to org, like MAVEN_*) " + "or a usable github.token to publish tdlm-hmcl") + print("auth=" + token_src) upload_root = os.environ["GITEA_PACKAGE_ROOT"].rstrip("/") root = pathlib.Path("build/tdlm-hmcl") @@ -117,6 +130,7 @@ jobs: env: RELEASE_TAG: ${{ inputs.release_tag }} TDLM_PACK_TOKEN: ${{ secrets.TDLM_PACK_TOKEN }} + GITHUB_TOKEN: ${{ github.token }} GITEA_API_URL: https://git.nest.potatolab.uk:8443/api/v1 GITEA_PACKAGE_ROOT: https://git.nest.potatolab.uk:8443/api/packages/TDLM/generic/tdlm-hmcl/latest TARGET_COMMIT: ${{ github.sha }} @@ -124,12 +138,15 @@ jobs: python3 - <<'PY' import json, os, urllib.error, urllib.parse, urllib.request tag = os.environ["RELEASE_TAG"].strip() - token = os.environ.get("TDLM_PACK_TOKEN", "") + token = (os.environ.get("TDLM_PACK_TOKEN") or os.environ.get("GITHUB_TOKEN") or "").strip() + token_src = "TDLM_PACK_TOKEN" if os.environ.get("TDLM_PACK_TOKEN", "").strip() else "github.token" api = os.environ["GITEA_API_URL"].rstrip("/") + "/repos/TDLM/HMCL" package_root = os.environ["GITEA_PACKAGE_ROOT"].rstrip("/") target = os.environ.get("TARGET_COMMIT", "main").strip() or "main" if not token: - raise SystemExit("TDLM_PACK_TOKEN is required to publish the release") + raise SystemExit( + "Need secrets.TDLM_PACK_TOKEN (org-preferred, same as TDLMPack) or github.token") + print("auth=" + token_src) if not tag: raise SystemExit("release_tag is empty") diff --git a/docs/TDLM_CONSUME.md b/docs/TDLM_CONSUME.md index f275c076fe..d3ec69220e 100644 --- a/docs/TDLM_CONSUME.md +++ b/docs/TDLM_CONSUME.md @@ -7,7 +7,19 @@ under the `tdlm-client/latest` layout. Players install the launcher separately. ## After this repo’s publish workflow Run **Actions → Publish TDLM launcher → Run workflow** with a new `release_tag`. -Requires repo secret `TDLM_PACK_TOKEN` (same org token TDLMPack uses for package/release uploads). + +### Secrets (sibling pattern) + +| Name | Where it lives today | Used by | +|---|---|---| +| `MAVEN_USER` / `MAVEN_PASSWORD` | **Org** secrets | Stratum / mod Maven publish — **not** used for launcher binaries | +| `TDLM_PACK_TOKEN` | **TDLMPack repo** secret only (not org) | TDLMPack `pack.yml` Gitea package + release uploads | +| `github.token` | Actions runtime | HMCL publish fallback when `TDLM_PACK_TOKEN` is unset | + +HMCL `publish-tdlm.yml` uses the **same name** as TDLMPack: `secrets.TDLM_PACK_TOKEN`, then falls back to `github.token`. +Org Maven secrets cannot publish Gitea generic packages / releases. + +**Recommended:** promote `TDLM_PACK_TOKEN` to an **org** secret (like `MAVEN_*`) so HMCL inherits it with no new repo secret. Until then, either copy it onto `TDLM/HMCL` or rely on `github.token` (same-repo release usually works; org `tdlm-hmcl` package write may still need the pack token). ### 1. Stable generic URLs (preferred for README / curl / CI) -- 2.47.3 From 7f3b7cfe8aacd978418676e106178a79184ce232 Mon Sep 17 00:00:00 2001 From: layue13 Date: Mon, 9 Nov 2026 17:09:01 +0000 Subject: [PATCH 4/9] ci: align publish-tdlm action pins with gradle.yml Use checkout/setup-java v6 and gradle-actions v5 so act resolves the same nest-mirrored action tags as Java CI. --- .github/workflows/publish-tdlm.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/publish-tdlm.yml b/.github/workflows/publish-tdlm.yml index 1c7a05531e..ffa9e9551b 100644 --- a/.github/workflows/publish-tdlm.yml +++ b/.github/workflows/publish-tdlm.yml @@ -31,17 +31,17 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 90 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: Set up JDK 17 + JavaFX - uses: actions/setup-java@v4 + uses: actions/setup-java@v6 with: distribution: 'liberica' java-version: '17' java-package: 'jdk+fx' - name: Setup Gradle - uses: gradle/actions/setup-gradle@v4 + uses: actions/gradle-actions/setup-gradle@v5 with: cache-cleanup: never -- 2.47.3 From 0f29fafc0a455e97e2c13aa67e7dc5be1b8af620 Mon Sep 17 00:00:00 2001 From: layue13 Date: Mon, 9 Nov 2026 17:12:51 +0000 Subject: [PATCH 5/9] ci: skip publish-tdlm job unless workflow_dispatch Push events were recording empty failed runs (0 jobs) for this dispatch-only workflow. Guard the job so push is a clean skip. --- .github/workflows/publish-tdlm.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/publish-tdlm.yml b/.github/workflows/publish-tdlm.yml index ffa9e9551b..db14e48cb0 100644 --- a/.github/workflows/publish-tdlm.yml +++ b/.github/workflows/publish-tdlm.yml @@ -28,6 +28,8 @@ concurrency: jobs: publish: + # Gitea may record a push run for this file; only workflow_dispatch should publish. + if: ${{ github.event_name == 'workflow_dispatch' }} runs-on: ubuntu-latest timeout-minutes: 90 steps: -- 2.47.3 From 93bb547a9eca6b2069e880451fd0b09f68334472 Mon Sep 17 00:00:00 2001 From: layue13 Date: Mon, 9 Nov 2026 17:18:22 +0000 Subject: [PATCH 6/9] ci: add publish-tdlm push no-op to avoid empty failed runs Gitea was marking publish-tdlm.yml push events as failure with 0 jobs. Add an explicit push path filter + noop job; keep publish on dispatch. --- .github/workflows/publish-tdlm.yml | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/.github/workflows/publish-tdlm.yml b/.github/workflows/publish-tdlm.yml index db14e48cb0..dcd603c5fe 100644 --- a/.github/workflows/publish-tdlm.yml +++ b/.github/workflows/publish-tdlm.yml @@ -21,14 +21,24 @@ on: description: Release tag (also used as the release title). Example: 2026.11.08 or v3.dev-abc1234 required: true type: string + # Gitea records a push run whenever this file changes; without a push job the run + # fails with 0 jobs. Keep a no-op so the check is green; real publish stays dispatch-only. + push: + paths: + - '.github/workflows/publish-tdlm.yml' concurrency: group: ${{ github.workflow }} cancel-in-progress: false jobs: + noop: + if: ${{ github.event_name == 'push' }} + runs-on: ubuntu-latest + steps: + - run: echo "publish-tdlm is workflow_dispatch-only; push no-op ok" + publish: - # Gitea may record a push run for this file; only workflow_dispatch should publish. if: ${{ github.event_name == 'workflow_dispatch' }} runs-on: ubuntu-latest timeout-minutes: 90 -- 2.47.3 From aace7932e0c55f71facc82707503711d8636ede5 Mon Sep 17 00:00:00 2001 From: layue13 Date: Mon, 9 Nov 2026 17:21:25 +0000 Subject: [PATCH 7/9] ci: quote publish-tdlm workflow_dispatch description Unquoted "Example: ..." broke YAML parsing, so Gitea recorded push runs with 0 jobs as failure. --- .github/workflows/publish-tdlm.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/publish-tdlm.yml b/.github/workflows/publish-tdlm.yml index dcd603c5fe..61c241bb2a 100644 --- a/.github/workflows/publish-tdlm.yml +++ b/.github/workflows/publish-tdlm.yml @@ -18,7 +18,7 @@ on: workflow_dispatch: inputs: release_tag: - description: Release tag (also used as the release title). Example: 2026.11.08 or v3.dev-abc1234 + description: "Release tag (also used as the release title). Example: 2026.11.08 or v3.dev-abc1234" required: true type: string # Gitea records a push run whenever this file changes; without a push job the run -- 2.47.3 From 327c0ea9401b548db8e31419e3a9cc745dc96a8f Mon Sep 17 00:00:00 2001 From: layue13 Date: Mon, 9 Nov 2026 19:42:50 +0000 Subject: [PATCH 8/9] =?UTF-8?q?ci:=20fix=20HMCL:test=20hang=20=E2=80=94=20?= =?UTF-8?q?install=20GTK=20for=20JavaFX=20under=20xvfb?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/publish-tdlm.yml | 34 ++++++++++++++++++++++++------ 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/.github/workflows/publish-tdlm.yml b/.github/workflows/publish-tdlm.yml index 61c241bb2a..1c42e52e8c 100644 --- a/.github/workflows/publish-tdlm.yml +++ b/.github/workflows/publish-tdlm.yml @@ -45,35 +45,55 @@ jobs: steps: - uses: actions/checkout@v6 + # Same caching pattern as gradle.yml / TDLM ForestryMC+IC2 setup-build. - name: Set up JDK 17 + JavaFX uses: actions/setup-java@v6 with: distribution: 'liberica' java-version: '17' java-package: 'jdk+fx' + cache-jdk: true - name: Setup Gradle - uses: actions/gradle-actions/setup-gradle@v5 + uses: actions/gradle-actions/setup-gradle@v6 with: cache-cleanup: never + cache-provider: ${{ github.server_url == 'https://github.com' && 'basic' || 'external' }} - - name: Cache Gradle distribution + - 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') }} + 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 }}- - - name: Install Xvfb + - 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: | - 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 + 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 + - name: Build executables run: xvfb-run --auto-servernum ./gradlew :HMCL:makeExecutables --no-daemon --parallel env: - JAVA_TOOL_OPTIONS: -Djava.awt.headless=true + JAVA_TOOL_OPTIONS: -Djava.awt.headless=true -Dprism.order=sw VERSION_TYPE: unofficial MICROSOFT_AUTH_ID: ${{ secrets.MICROSOFT_AUTH_ID }} CURSEFORGE_API_KEY: ${{ secrets.CURSEFORGE_API_KEY }} -- 2.47.3 From 95111d2f8486e6e8355141578d7c1102be0e8ada Mon Sep 17 00:00:00 2001 From: layue13 Date: Mon, 9 Nov 2026 20:37:44 +0000 Subject: [PATCH 9/9] ci: stop caching entire ~/.gradle/caches (was ~4GB) --- .github/workflows/publish-tdlm.yml | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/.github/workflows/publish-tdlm.yml b/.github/workflows/publish-tdlm.yml index 1c42e52e8c..d186be33bc 100644 --- a/.github/workflows/publish-tdlm.yml +++ b/.github/workflows/publish-tdlm.yml @@ -60,16 +60,14 @@ jobs: cache-cleanup: never cache-provider: ${{ github.server_url == 'https://github.com' && 'basic' || 'external' }} - - name: Cache Gradle home + # Wrapper only — do not cache ~/.gradle/caches (org siblings don't; ~4GB killed Docker). + - name: Cache Gradle wrapper uses: actions/cache@v4 with: path: | - ~/.gradle/caches ~/.gradle/wrapper .gradle/wrapper-neo - 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 }}- + key: gradle-wrapper-${{ runner.os }}-${{ hashFiles('gradle/wrapper/gradle-wrapper.properties', 'gradle/wrapper/GradleWrapperNeo.java') }} - name: Cache apt packages uses: actions/cache@v4 -- 2.47.3