261 lines
12 KiB
YAML
261 lines
12 KiB
YAML
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).
|
|
#
|
|
# 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:
|
|
inputs:
|
|
release_tag:
|
|
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:
|
|
if: ${{ github.event_name == 'workflow_dispatch' }}
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 90
|
|
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@v6
|
|
with:
|
|
cache-cleanup: never
|
|
cache-provider: ${{ github.server_url == 'https://github.com' && 'basic' || 'external' }}
|
|
|
|
# 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/wrapper
|
|
.gradle/wrapper-neo
|
|
key: gradle-wrapper-${{ runner.os }}-${{ hashFiles('gradle/wrapper/gradle-wrapper.properties', 'gradle/wrapper/GradleWrapperNeo.java') }}
|
|
|
|
- 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: |
|
|
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 -Dprism.order=sw
|
|
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:
|
|
# 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") 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(
|
|
"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")
|
|
|
|
def put(name: str, data: bytes):
|
|
url = upload_root + "/" + urllib.parse.quote(name, safe="")
|
|
# Gitea refuses to overwrite a generic package file (HTTP 409), so the previous copy of this one
|
|
# file is deleted right before it is uploaded again (keeps the gap without a file as short as possible).
|
|
delete = urllib.request.Request(url, method="DELETE")
|
|
delete.add_header("Authorization", "token " + token)
|
|
delete.add_header("User-Agent", "tdlm-hmcl-publish")
|
|
try:
|
|
with urllib.request.urlopen(delete, timeout=300) as response:
|
|
response.read()
|
|
except urllib.error.HTTPError as exc:
|
|
if exc.code != 404:
|
|
raise SystemExit("HTTP %s deleting the old %s: %s" % (exc.code, name, exc.read()[:300]))
|
|
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 }}
|
|
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 }}
|
|
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") 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(
|
|
"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")
|
|
|
|
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 fileApi default: http://159.75.5.225:19527/tdlm-client\n"
|
|
"(Pack must publish/mirror the same layout; see docs/TDLM_CONSUME.md).\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
|